misc/mu4sim/mu4sim.c- Source code ソースコード - XEiJ - STUDIO KAMADA
misc/mu4sim/mu4sim.c

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>

    //ゲート1個あたりの遅延時間。100ps
    #define GATE_DELAY 100.0e-12

    //EXREQがアサートされたらDMA転送が行われたことにしてEXACKをアサートする
    //ネゲートも同様
    #define EXACK_DELAY (10.0 / 10000000.0)

    //ノードの種類
    //  PORT ポート
    //  NODE ノード、入力ピン、出力ピン
    //  IOPIN 入出力ピン
    //  REG レジスタ
    #define PORT 0
    #define NODE 1
    #define IOPIN 2
    #define REG 3

    //ノードの数の上限
    #define NODE_LIMIT 10000

    //スパイク検出
    #define SPIKE_DETECTION

    //イベントの種類
    #define CVAL 0
    #define DVAL 1
    #define IVAL 2
    #define OVAL 3
    #define VAL 4

    //イベントキューの長さ
    #define EVENT_LIMIT 10000

    //ポートアドレス
    #define DATA_PORT    0xecc080
    #define COMMAND_PORT 0xecc090
    #define STATUS_PORT  0xecc0a0
    #define OPN3L1_PORT  0xecc0c0
    #define OPN3L2_PORT  0xecc0c8

    //ノード
    typedef struct struct_node {
      char *name;  //名前
      struct struct_node **cdown;  //下流のノードの配列。NULL終端。down->ccctが使う
      struct struct_node **ddown;  //down->dcctが使う
      struct struct_node **idown;  //down->icctが使う
      struct struct_node **ndown;  //down->ncctが使う
      struct struct_node **odown;  //down->occtが使う
      void (*task) (double);  //追加の処理
      _Bool initial_val;  //valの初期値
      _Bool val;  //内部から参照したときの値
      _Bool initialized;  //少なくとも1回は値を確定させた
      int type;  //種類。PORT,NODE,IOPIN,REG
      union {
        //NODE ノード、入力ピン、出力ピン
        struct {
          _Bool (*ncct) ();  //回路
          int ndpt;  //回路の深さ
          //_Bool nval;  //回路の値
          //val = nval
        };
        //IOPIN 入出力ピン
        struct {
          _Bool (*dcct) ();  //方向回路
          int ddpt;  //方向回路の深さ
          _Bool dval;  //方向回路の値
          _Bool (*icct) ();  //入力回路
          int idpt;  //入力回路の深さ
          _Bool ival;  //入力回路の値
          _Bool (*occt) ();  //出力回路
          int odpt;  //出力回路の深さ
          _Bool oval;  //出力回路の値
          _Bool out;  //出力値。dval && oval。ovalのままではデータバスのワイヤードORができない
          //val = dval ? oval : ival  入力値
        };
        //REG レジスタ
        struct {
          _Bool (*ccct) ();  //クロック回路
          int cdpt;  //クロック回路の深さ
          _Bool cval;  //クロック回路の値
          _Bool (*rcct) ();  //レジスタ回路
          int rdpt;  //レジスタ回路の深さ
          //_Bool rval;  //レジスタ回路の値
          //val = rval
    #ifdef SPIKE_DETECTION
          double prev_time;  //前回の変化時刻
    #endif
        };
      };
    } node_t;

    node_t node_array[NODE_LIMIT];  //すべのノードの配列
    int node_count;  //ノードの数

    //ポート
    node_t *new_port (char *name, _Bool val) {
      if (node_count == NODE_LIMIT) {
        fprintf (stderr, "node overflow\n");
        exit (1);
      }
      node_t *node = &node_array[node_count++];
      node->name = name;
      node->cdown = NULL;
      node->ddown = NULL;
      node->idown = NULL;
      node->ndown = NULL;
      node->odown = NULL;
      node->task = NULL;
      node->initial_val = val;
      node->val = val;
      node->initialized = 0;
      node->type = PORT;
      return node;
    }
    //ノード、入力ピン、出力ピン
    node_t *new_node (char *name, _Bool val,
                      _Bool (*ncct) (), int ndpt) {
      node_t *node = new_port (name, val);
      node->type = NODE;
      node->ncct = ncct;
      node->ndpt = ndpt;
      return node;
    }
    //入出力ピン
    node_t *new_iopin (char *name, _Bool val,
                       _Bool (*dcct) (), int ddpt,
                       _Bool (*icct) (), int idpt,
                       _Bool (*occt) (), int odpt) {
      node_t *node = new_port (name, val);
      node->type = IOPIN;
      node->dcct = dcct;
      node->ddpt = ddpt;
      node->dval = 0;
      node->icct = icct;
      node->idpt = idpt;
      node->ival = 0;
      node->occt = occt;
      node->odpt = odpt;
      node->oval = 0;
      node->out = 0;
      return node;
    }
    //レジスタ
    node_t *new_reg (char *name, _Bool val,
                     _Bool (*ccct) (), int cdpt,
                     _Bool (*rcct) (), int rdpt) {
      node_t *node = new_port (name, val);
      node->type = REG;
      node->ccct = ccct;
      node->cdpt = cdpt;
      node->cval = 0;
      node->rcct = rcct;
      node->rdpt = rdpt;
    #ifdef SPIKE_DETECTION
      node->prev_time = -1L;
    #endif
      return node;
    }

    //イベント
    //  値が変化するとき
    //    値を更新する
    //    直下の(このノードを回路に含む)すべてのノードについて
    //      回路を計算する
    //      遅延時間後にイベントを予約する
    //    追加の処理
    //  順序に注意
    //    遅延時間前のノードの入力値を使用して回路を計算する
    //    遅延時間後にノードの出力値を更新する
    //    それを下流のノードの遅延時間前のノードの入力値として使用する
    typedef struct {
      double time;  //時刻
      node_t *node;  //ノード
      int type;  //種類。CVAL,DVAL,IVAL,OVAL,VAL
      _Bool val;  //値
    } event_t;

    event_t event_queue[EVENT_LIMIT];  //イベントキュー
    double event_time;  //最後に実行したイベントの時刻
    int event_offset;  //先頭のイベントの位置
    int event_count;  //イベントの数

    _Bool log_add_event = 0;
    event_t *add_event (double time, node_t *node, int type, _Bool val);

    _Bool log_execute = 0;
    _Bool log_x68kbus = 0;
    void execute ();

    _Bool log_dump = 0;

    void dump_event () {
      for (int i = 0; i < event_count; i++) {
        event_t *event = &event_queue[event_offset + i];
        printf ("[%d] %16lg %s %s %d\n",
                i,
                event->time,
                event->node->name,
                event->type == CVAL ? "cval" :
                event->type == DVAL ? "dval" :
                event->type == IVAL ? "ival" :
                event->type == OVAL ? "oval" :
                event->type == VAL ? "val" :
                "???",
                event->val);
      }
    }

    #include "mu4sim.h"

    int get_Axx () {
      return ((x_A23->val << 23) |
              (x_A22->val << 22) |
              (x_A21->val << 21) |
              (x_A20->val << 20) |
              (x_A19->val << 19) |
              (x_A18->val << 18) |
              (x_A17->val << 17) |
              (x_A16->val << 16) |
              (x_A15->val << 15) |
              (x_A14->val << 14) |
              (x_A13->val << 13) |
              (x_A12->val << 12) |
              (x_A11->val << 11) |
              (x_A10->val << 10) |
              (x_A9->val << 9) |
              (x_A8->val << 8) |
              (x_A7->val << 7) |
              (x_A6->val << 6) |
              (x_A5->val << 5) |
              (x_A4->val << 4) |
              (x_A3->val << 3));
    }

    int get_Dxx () {
      return ((x_D15r->val << 15) |
              (x_D14r->val << 14) |
              (x_D13r->val << 13) |
              (x_D12r->val << 12) |
              (x_D11r->val << 11) |
              (x_D10r->val << 10) |
              (x_D9r->val << 9) |
              (x_D8r->val << 8) |
              (x_D7r->val << 7) |
              (x_D6r->val << 6) |
              (x_D5r->val << 5) |
              (x_D4r->val << 4) |
              (x_D3r->val << 3) |
              (x_D2r->val << 2) |
              (x_D1r->val << 1) |
              (x_D0r->val << 0));
    }
    int get_Dxxw () {
      return ((x_D15w->val << 15) |
              (x_D14w->val << 14) |
              (x_D13w->val << 13) |
              (x_D12w->val << 12) |
              (x_D11w->val << 11) |
              (x_D10w->val << 10) |
              (x_D9w->val << 9) |
              (x_D8w->val << 8) |
              (x_D7w->val << 7) |
              (x_D6w->val << 6) |
              (x_D5w->val << 5) |
              (x_D4w->val << 4) |
              (x_D3w->val << 3) |
              (x_D2w->val << 2) |
              (x_D1w->val << 1) |
              (x_D0w->val << 0));
    }

    #define x68kbus_count 70
    node_t *x68kbus_node_array[x68kbus_count];
    _Bool x68kbus_val_array[x68kbus_count];

    void init_x68kbus () {
      {
        int i = 0;
        x68kbus_node_array[i++] = x_10M;
        x68kbus_node_array[i++] = x_10M_;
        x68kbus_node_array[i++] = x_20M;
        x68kbus_node_array[i++] = x_HSYNC;
        x68kbus_node_array[i++] = x_VSYNC;
        x68kbus_node_array[i++] = x_VDISP;
        x68kbus_node_array[i++] = x_R_W_;
        x68kbus_node_array[i++] = x_A23;
        x68kbus_node_array[i++] = x_A22;
        x68kbus_node_array[i++] = x_A21;
        x68kbus_node_array[i++] = x_A20;
        x68kbus_node_array[i++] = x_A19;
        x68kbus_node_array[i++] = x_A18;
        x68kbus_node_array[i++] = x_A17;
        x68kbus_node_array[i++] = x_A16;
        x68kbus_node_array[i++] = x_A15;
        x68kbus_node_array[i++] = x_A14;
        x68kbus_node_array[i++] = x_A13;
        x68kbus_node_array[i++] = x_A12;
        x68kbus_node_array[i++] = x_A11;
        x68kbus_node_array[i++] = x_A10;
        x68kbus_node_array[i++] = x_A9;
        x68kbus_node_array[i++] = x_A8;
        x68kbus_node_array[i++] = x_A7;
        x68kbus_node_array[i++] = x_A6;
        x68kbus_node_array[i++] = x_A5;
        x68kbus_node_array[i++] = x_A4;
        x68kbus_node_array[i++] = x_A3;
        x68kbus_node_array[i++] = x_AS;
        x68kbus_node_array[i++] = x_D15r;
        x68kbus_node_array[i++] = x_D14r;
        x68kbus_node_array[i++] = x_D13r;
        x68kbus_node_array[i++] = x_D12r;
        x68kbus_node_array[i++] = x_D11r;
        x68kbus_node_array[i++] = x_D10r;
        x68kbus_node_array[i++] = x_D9r;
        x68kbus_node_array[i++] = x_D8r;
        x68kbus_node_array[i++] = x_D7r;
        x68kbus_node_array[i++] = x_D6r;
        x68kbus_node_array[i++] = x_D5r;
        x68kbus_node_array[i++] = x_D4r;
        x68kbus_node_array[i++] = x_D3r;
        x68kbus_node_array[i++] = x_D2r;
        x68kbus_node_array[i++] = x_D1r;
        x68kbus_node_array[i++] = x_D0r;
        x68kbus_node_array[i++] = x_D15w;
        x68kbus_node_array[i++] = x_D14w;
        x68kbus_node_array[i++] = x_D13w;
        x68kbus_node_array[i++] = x_D12w;
        x68kbus_node_array[i++] = x_D11w;
        x68kbus_node_array[i++] = x_D10w;
        x68kbus_node_array[i++] = x_D9w;
        x68kbus_node_array[i++] = x_D8w;
        x68kbus_node_array[i++] = x_D7w;
        x68kbus_node_array[i++] = x_D6w;
        x68kbus_node_array[i++] = x_D5w;
        x68kbus_node_array[i++] = x_D4w;
        x68kbus_node_array[i++] = x_D3w;
        x68kbus_node_array[i++] = x_D2w;
        x68kbus_node_array[i++] = x_D1w;
        x68kbus_node_array[i++] = x_D0w;
        x68kbus_node_array[i++] = x_UDS;
        x68kbus_node_array[i++] = x_LDS;
        x68kbus_node_array[i++] = x_DTACK;
        x68kbus_node_array[i++] = x_EXPCL;
        x68kbus_node_array[i++] = x_EXREQ;
        x68kbus_node_array[i++] = x_IACK2;
        x68kbus_node_array[i++] = x_IACK4;
        x68kbus_node_array[i++] = x_EXACK;
        x68kbus_node_array[i++] = x_EXRESET;
        if (i != x68kbus_count) {
          fprintf (stderr, "error\n");
          exit (1);
        }
      }
      for (int i = 0; i < x68kbus_count; i++) {
        x68kbus_val_array[i] = 0;
      }
    }
    _Bool x68kbus_changed () {
      _Bool changed = 0;
      for (int i = 0; i < x68kbus_count; i++) {
        if (x68kbus_val_array[i] != x68kbus_node_array[i]->val) {
          x68kbus_val_array[i] = x68kbus_node_array[i]->val;
          changed = 1;
        }
      }
      return changed;
    }
    void print_x68kbus_header () {
        printf ("                                                                     E\n");
        printf ("                                                                     X\n");
        printf ("                      H V V                            D  E E  I I E R\n");
        printf ("                 1    S S D  R                         T  X X  A A X E\n");
        printf ("               1 0 2  Y Y I  _                     U L A  P R  C C A S\n");
        printf ("   time us     0 M 0  N N S  W  addr  A    data    D D C  C E  K K C E\n");
        printf ("               M _ M  C C P  _        S   r    w   S S K  L Q  2 4 K T\n");
        printf ("----------------------------------------------------------------------\n");
        //       ddddddddddddd  b b b  b b b  b xxxxxx b  xxxx xxxx b b b  b b  b b b b
    }
    void print_x68kbus () {
      printf ("%13.6lf  %d %d %d  %d %d %d  %d %06X %d  %04X %04X %d %d %d  %d %d  %d %d %d %d\n",
              1000000.0 * event_time,
              x_10M->val, x_10M_->val, x_20M->val,
              x_HSYNC->val, x_VSYNC->val, x_VDISP->val,
              x_R_W_->val, get_Axx (), x_AS->val,
              get_Dxx (), get_Dxxw (), x_UDS->val, x_LDS->val, x_DTACK->val,
              x_EXPCL->val, x_EXREQ->val,
              x_IACK2->val, x_IACK4->val, x_EXACK->val, x_EXRESET->val
              );
    }

    //イベントを追加する
    event_t *add_event (double time, node_t *node, int type, _Bool val) {
      if (log_add_event) {
        printf ("add_event %16lg %s %s %d\n",
                time,
                node->name,
                type == CVAL ? "cval" :
                type == DVAL ? "dval" :
                type == IVAL ? "ival" :
                type == OVAL ? "oval" :
                type == VAL ? "val" :
                "???",
                val);
      }
      if (event_count == EVENT_LIMIT) {  //キューが満杯
        fprintf (stderr, "event queue overflow\n");
        exit (1);
      }
      //配列の末尾に接していたら先頭に接するようにずらす
      if (event_offset + event_count == EVENT_LIMIT) {  //配列の末尾に接している
    #if 0
        memmove (&event_queue[0],  //dst
                 &event_queue[event_offset],  //src
                 sizeof (event_t) * event_count);  //先頭に接するようにずらす
    #else
        for (int i = 0; i < event_count; i++) {
          event_queue[i] = event_queue[event_offset + i];
        }
    #endif
        //先頭を巻き戻す
        event_offset = 0;
      }
      //バイナリサーチで挿入位置を決める
      int l = 0;  //左端。含む
      int r = event_count;  //右端。含まない
      while (l < r) {
        int m = (l + r) >> 1;  //中央
        if (event_queue[event_offset + m].time <= time) {  //中央と同じか大きい。同じときは右側に挿入する
          l = m + 1;  //右半分に絞り込む
        } else {
          r = m;  //左半分に絞り込む
        }
      }
      //挿入位置から右を右に1つずらす
      event_t *event = &event_queue[event_offset + l];  //挿入位置
      if (l < event_count) {  //ずらす要素がある
    #if 0
        memmove (event + 1,  //dst
                 event,  //src
                 sizeof (event_t) * (event_count - l));  //右に1つずらす
    #else
        for (int i = event_count; l < i; i--) {
          event_queue[event_offset + i] = event_queue[event_offset + i - 1];
        }
    #endif
      }
      //挿入位置に書き込む
      event->time = time;
      event->node = node;
      event->type = type;
      event->val = val;
      //要素を増やす
      event_count++;
      if (log_dump) {
        dump_event ();
      }
      return event;
    }

    //先頭のイベントと同じ時刻のイベントを実行する
    void execute () {
      if (event_count == 0) {  //イベントがない
        fprintf (stderr, "no event\n");
        exit (1);
      }
      event_time = event_queue[event_offset].time;
      while (event_count != 0 &&
             event_queue[event_offset].time == event_time) {  //同じ時刻のイベントがある
        //event_offsetとevent_countは毎回変化することに注意
        //イベントを取り出す
        event_t *event = &event_queue[event_offset];  //イベント
        double time = event->time;
        node_t *node = event->node;
        int type = event->type;
        _Bool val = event->val;
        if (log_execute) {
          printf ("execute %16lg %s %s %d\n",
                  time,
                  node->name,
                  type == CVAL ? "cval" :
                  type == DVAL ? "dval" :
                  type == IVAL ? "ival" :
                  type == OVAL ? "oval" :
                  type == VAL ? "val" :
                  "???",
                  val);
        }
        //イベントを取り除く
        event_offset++;
        event_count--;
        if (log_dump) {
          dump_event ();
        }
        //イベントの種類ごとの処理
        if (type == CVAL) {  //REGのccctの結果をcdpt後にcvalに書き込むイベント
          if (node->initialized &&  //値が確定していて
              node->cval == val) {  //cvalが変化しないとき
            continue;  //何もしない
          }
          _Bool edge = !node->cval && val;  //立ち上がりエッジ
          node->cval = val;  //cvalに書き込む
    #ifdef SPIKE_DETECTION
          if (node->prev_time == time) {  //前回の変化時刻と同じ
            printf ("spike detected %s\n", node->name);
            fflush (stdout);
          }
          node->prev_time = time;
    #endif
          if (edge) {  //cvalが立ち上がったとき
            add_event (time + GATE_DELAY * node->rdpt, node, VAL, (node->rcct) ());  //rcctを評価する
          }
          continue;  //valに書き込まない
        }
        if (type == DVAL) {  //IOPINのdcctの結果をddpt後にdvalに書き込むイベント
          if (node->initialized &&  //値が確定していて
              node->dval == val) {  //dvalが変化しないとき
            continue;  //何もしない
          }
          node->dval = val;  //dvalに書き込む
          node->out = node->dval && node->oval;
    #if 1
          val = node->dval ? node->oval : node->ival;
    #else
          add_event (time + GATE_DELAY, node, VAL, node->dval ? node->oval : node->ival);
          continue;
    #endif
        } else if (type == IVAL) {  //IOPINのicctの結果をidpt後にivalに書き込むイベント
          if (node->initialized &&  //値が確定していて
              node->ival == val) {  //ivalが変化しないとき
            continue;  //何もしない
          }
          node->ival = val;  //ivalに書き込む
    #if 1
          val = node->dval ? node->oval : node->ival;
    #else
          add_event (time + GATE_DELAY, node, VAL, node->dval ? node->oval : node->ival);
          continue;
    #endif
        } else if (type == OVAL) {  //IOPINのocctの結果をodpt後にovalに書き込むイベント
          if (node->initialized &&  //値が確定していて
              node->oval == val) {  //ovalが変化しないとき
            continue;  //何もしない
          }
          node->oval = val;  //ovalに書き込む
          node->out = node->dval && node->oval;
    #if 1
          val = node->dval ? node->oval : node->ival;
    #else
          add_event (time + GATE_DELAY, node, VAL, node->dval ? node->oval : node->ival);
          continue;
    #endif
        }
        //valに書き込むイベント
        if (node->initialized &&  //値が確定していて
            node->val == val) {  //valが変化しないとき
          continue;  //何もしない
        }
        node->val = val;  //valに書き込む
        node->initialized = 1;  //値が確定した
        //下流のノードのイベントを作る
        if (node->cdown != NULL) {
          for (node_t **handle = node->cdown; *handle != NULL; handle++) {
            node_t *down = *handle;
            add_event (time + GATE_DELAY * down->cdpt, down, CVAL, down->ccct ());  //ccctを評価する
          }
        }
        if (node->ddown != NULL) {
          for (node_t **handle = node->ddown; *handle != NULL; handle++) {
            node_t *down = *handle;
            add_event (time + GATE_DELAY * down->ddpt, down, DVAL, down->dcct ());  //dcctを評価する
          }
        }
        if (node->idown != NULL) {
          for (node_t **handle = node->idown; *handle != NULL; handle++) {
            node_t *down = *handle;
            add_event (time + GATE_DELAY * down->idpt, down, IVAL, down->icct ());  //icctを評価する
          }
        }
        if (node->ndown != NULL) {
          for (node_t **handle = node->ndown; *handle != NULL; handle++) {
            node_t *down = *handle;
            add_event (time + GATE_DELAY * down->ndpt, down, VAL, down->ncct ());  //ncctを評価する
          }
        }
        if (node->odown != NULL) {
          for (node_t **handle = node->odown; *handle != NULL; handle++) {
            node_t *down = *handle;
            add_event (time + GATE_DELAY * down->odpt, down, OVAL, down->occt ());  //occtを評価する
          }
        }
        //追加の処理を呼び出す
        if (node->task != NULL) {
          node->task (time);
        }
      }
      if (log_x68kbus && x68kbus_changed ()) {
        print_x68kbus ();
      }
    }

    //taskがない(クロックではない)ノードのイベントがなくなるまでイベントを実行する
    void wait_all () {
    #if 0
      printf ("wait_all start\n");
      dump_event ();
    #endif
      for (;;) {
        _Bool cont = 0;
        for (int i = 0; i < event_count; i++) {
          if (event_queue[event_offset + i].node->task == NULL) {  //taskがないノードのイベントがあれば
            cont = 1;  //続ける
            break;
          }
        }
        if (!cont) {  //taskがないノードのイベントが1つもなければ
          break;  //終わり
        }
        execute ();
      }
    #if 0
      printf ("wait_all end\n");
      dump_event ();
    #endif
    }

    //指定された時刻になるまでイベントを実行する
    void wait_time (double time) {
      while (event_time <= time) {
        execute ();
      }
    }

    //指定された時間が経過するまでイベントを実行する
    void wait_length (double length) {
      wait_length (event_time + length);
    }

    //指定された信号が1になるまでイベントを実行する
    void wait_1 (node_t *node) {
      while (node->val == 0) {
        execute ();
      }
    }

    //指定された信号が0になるまでイベントを実行する
    void wait_0 (node_t *node) {
      while (node->val != 0) {
        execute ();
      }
    }

    //指定された信号の立ち上がりまでイベントを実行する
    void wait_rising_edge (node_t *node) {
      _Bool val;
      do {
        val = node->val;
        execute ();
      } while (val || !node->val);
    }
    void wait_rising_edge_n (node_t *node, int n) {
      for (int i = 0; i < n; i++) {
        wait_rising_edge (node);
      }
    }

    //指定された信号の立ち下がりまでイベントを実行する
    void wait_falling_edge (node_t *node) {
      _Bool val;
      do {
        val = node->val;
        execute ();
      } while (!val || node->val);
    }
    void wait_falling_edge_n (node_t *node, int n) {
      for (int i = 0; i < n; i++) {
        wait_falling_edge (node);
      }
    }

    //指定された信号の立ち上がりまたは立ち下がりまでイベントを実行する
    void wait_edge (node_t *node) {
      _Bool val = node->val;
      do {
        execute ();
      } while (val == node->val);
    }
    void wait_edge_2 (node_t *node1, node_t *node2) {
      _Bool val1 = node1->val;
      _Bool val2 = node2->val;
      do {
        execute ();
      } while (val1 == node1->val &&
               val2 == node2->val);
    }
    void wait_edge_3 (node_t *node1, node_t *node2, node_t *node3) {
      _Bool val1 = node1->val;
      _Bool val2 = node2->val;
      _Bool val3 = node3->val;
      do {
        execute ();
      } while (val1 == node1->val &&
               val2 == node2->val &&
               val3 == node3->val);
    }
    void wait_edge_4 (node_t *node1, node_t *node2, node_t *node3, node_t *node4) {
      _Bool val1 = node1->val;
      _Bool val2 = node2->val;
      _Bool val3 = node3->val;
      _Bool val4 = node4->val;
      do {
        execute ();
      } while (val1 == node1->val &&
               val2 == node2->val &&
               val3 == node3->val &&
               val4 == node4->val);
    }
    void wait_edge_5 (node_t *node1, node_t *node2, node_t *node3, node_t *node4, node_t *node5) {
      _Bool val1 = node1->val;
      _Bool val2 = node2->val;
      _Bool val3 = node3->val;
      _Bool val4 = node4->val;
      _Bool val5 = node5->val;
      do {
        execute ();
      } while (val1 == node1->val &&
               val2 == node2->val &&
               val3 == node3->val &&
               val4 == node4->val &&
               val5 == node5->val);
    }

    //クロック
    //  拡張スロットのHSYNCとVSYNCはX68000初代だけ負論理でそれ以外は正論理
    //  ここでは正論理とする
    //  HSYNCとVSYNCは同時に立ち上がる
    //  768x512 dotclock=69.5519MHz/2
    //  HT=138 HS=15 HB=18 HD=96 HF=9
    //  VT=568 VS=6 VB=35 VD=512 VF=15
    //  VDISPを追加する
    //  VDISPは水平フロントポーチの分だけ早く立ち上がる
    //         <--VS--><-VB-><---VD---><-VF->
    //  VSYNC  111111110000000000000000000000
    //  VDISP  000000000000001111111111000000
    //         <--HS--><-HB-><---HD---><-HF->
    //  HSYNC  111111110000000000000000000000
    //         000000000000000000000000111111
    //  VDISP  111111111111111111111111000000
    //20M
    int x_20M_count = 0;
    void x_20M_task (double time) {
      x_20M_count++;
      double half_cycle = 1.0 / (20000000.0 * 2.0);
      add_event (time + half_cycle, x_20M, VAL, !x_20M->val);
    }
    //HSYNC
    int x_HSYNC_count = 0;
    void x_HSYNC_task (double time) {
      x_HSYNC_count++;
      double half_cycle = (x_HSYNC->val == 0 ?
                           (double) (8 * (18 + 96 + 9)) / (69551900.0 / 2.0) :
                           (double) (8 * 15) / (69551900.0 / 2.0));  //水平同期パルス幅
      add_event (time + half_cycle, x_HSYNC, VAL, !x_HSYNC->val);
    }
    //VDISP
    int x_VDISP_count = 0;
    void x_VDISP_task (double time) {
      x_VDISP_count++;
      double half_cycle = (x_VDISP->val == 0 ?
                           (double) (8 * 138 * (15 + 6 + 35)) / (69551900.0 / 2.0) :  //垂直帰線期間
                           (double) (8 * 138 * 512) / (69551900.0 / 2.0));  //垂直表示期間
      add_event (time + half_cycle, x_VDISP, VAL, !x_VDISP->val);
    }
    //VSYNC
    int x_VSYNC_count = 0;
    void x_VSYNC_task (double time) {
      x_VSYNC_count++;
      double half_cycle = (x_VSYNC->val == 0 ?
                           (double) (8 * 138 * (35 + 512 + 15)) / (69551900.0 / 2.0) :
                           (double) (8 * 138 * 6) / (69551900.0 / 2.0));  //垂直同期パルス幅
      add_event (time + half_cycle, x_VSYNC, VAL, !x_VSYNC->val);
    }
    //X1
    int o_X1_count = 0;
    void o_X1_task (double time) {
      o_X1_count++;
      double half_cycle = 1.0 / (12288000.0 * 2.0);
      add_event (time + half_cycle, o_X1, VAL, !o_X1->val);
    }
    //X2
    int o_X2_count = 0;
    void o_X2_task (double time) {
      o_X2_count++;
      double half_cycle = 1.0 / (16934400.0 * 2.0);
      add_event (time + half_cycle, o_X2, VAL, !o_X2->val);
    }
    //X3
    int o_X3_count = 0;
    void o_X3_task (double time) {
      o_X3_count++;
      double half_cycle = 1.0 / (18432000.0 * 2.0);
      add_event (time + half_cycle, o_X3, VAL, !o_X3->val);
    }
    //クロックを開始する
    void start_clocks () {
      //20M
      x_20M->val = 1;
      x_20M->task = x_20M_task;
      x_20M_task (0L);
      //HSYNC
      x_HSYNC->val = 1;
      x_HSYNC->task = x_HSYNC_task;
      x_HSYNC_task (0L);
      //VDISP
      x_VDISP->val = 0;
      x_VDISP->task = x_VDISP_task;
      double delay = (double) (8 * (138 * (6 + 35) - 9)) / (69551900.0 / 2.0);  //VS+VB-HF
      add_event (delay, x_VDISP, VAL, !x_VDISP->val);
      //VSYNC
      x_VSYNC->val = 1;
      x_VSYNC->task = x_VSYNC_task;
      x_VSYNC_task (0L);
      //X1
      o_X1->val = 1;
      o_X1->task = o_X1_task;
      o_X1_task (0L);
      //X2
      o_X2->val = 1;
      o_X2->task = o_X2_task;
      o_X2_task (0L);
      //X3
      o_X3->val = 1;
      o_X3->task = o_X3_task;
      o_X3_task (0L);
    }

    //Axx
    void set_Axx (int val) {
      add_event (event_time + GATE_DELAY, x_A10, VAL, (val >> 10) & 1);
      add_event (event_time + GATE_DELAY, x_A11, VAL, (val >> 11) & 1);
      add_event (event_time + GATE_DELAY, x_A12, VAL, (val >> 12) & 1);
      add_event (event_time + GATE_DELAY, x_A13, VAL, (val >> 13) & 1);
      add_event (event_time + GATE_DELAY, x_A14, VAL, (val >> 14) & 1);
      add_event (event_time + GATE_DELAY, x_A15, VAL, (val >> 15) & 1);
      add_event (event_time + GATE_DELAY, x_A16, VAL, (val >> 16) & 1);
      add_event (event_time + GATE_DELAY, x_A17, VAL, (val >> 17) & 1);
      add_event (event_time + GATE_DELAY, x_A18, VAL, (val >> 18) & 1);
      add_event (event_time + GATE_DELAY, x_A19, VAL, (val >> 19) & 1);
      add_event (event_time + GATE_DELAY, x_A20, VAL, (val >> 20) & 1);
      add_event (event_time + GATE_DELAY, x_A21, VAL, (val >> 21) & 1);
      add_event (event_time + GATE_DELAY, x_A22, VAL, (val >> 22) & 1);
      add_event (event_time + GATE_DELAY, x_A23, VAL, (val >> 23) & 1);
      add_event (event_time + GATE_DELAY, x_A3, VAL, (val >> 3) & 1);
      add_event (event_time + GATE_DELAY, x_A4, VAL, (val >> 4) & 1);
      add_event (event_time + GATE_DELAY, x_A5, VAL, (val >> 5) & 1);
      add_event (event_time + GATE_DELAY, x_A6, VAL, (val >> 6) & 1);
      add_event (event_time + GATE_DELAY, x_A7, VAL, (val >> 7) & 1);
      add_event (event_time + GATE_DELAY, x_A8, VAL, (val >> 8) & 1);
      add_event (event_time + GATE_DELAY, x_A9, VAL, (val >> 9) & 1);
    }

    //AS
    void set_AS (_Bool val) {
      add_event (event_time + GATE_DELAY, x_AS, VAL, val);
    }

    //Dxx
    void set_Dxx (int val) {
      add_event (event_time + GATE_DELAY, x_D15w, VAL, (val >> 15) & 1);
      add_event (event_time + GATE_DELAY, x_D14w, VAL, (val >> 14) & 1);
      add_event (event_time + GATE_DELAY, x_D13w, VAL, (val >> 13) & 1);
      add_event (event_time + GATE_DELAY, x_D12w, VAL, (val >> 12) & 1);
      add_event (event_time + GATE_DELAY, x_D11w, VAL, (val >> 11) & 1);
      add_event (event_time + GATE_DELAY, x_D10w, VAL, (val >> 10) & 1);
      add_event (event_time + GATE_DELAY, x_D9w, VAL, (val >> 9) & 1);
      add_event (event_time + GATE_DELAY, x_D8w, VAL, (val >> 8) & 1);
      add_event (event_time + GATE_DELAY, x_D7w, VAL, (val >> 7) & 1);
      add_event (event_time + GATE_DELAY, x_D6w, VAL, (val >> 6) & 1);
      add_event (event_time + GATE_DELAY, x_D5w, VAL, (val >> 5) & 1);
      add_event (event_time + GATE_DELAY, x_D4w, VAL, (val >> 4) & 1);
      add_event (event_time + GATE_DELAY, x_D3w, VAL, (val >> 3) & 1);
      add_event (event_time + GATE_DELAY, x_D2w, VAL, (val >> 2) & 1);
      add_event (event_time + GATE_DELAY, x_D1w, VAL, (val >> 1) & 1);
      add_event (event_time + GATE_DELAY, x_D0w, VAL, (val >> 0) & 1);
    }

    //EXACK
    void set_EXACK (_Bool val) {
      add_event (event_time + GATE_DELAY, x_EXACK, VAL, val);
    }

    //EXRESET
    void set_EXRESET (_Bool val) {
      add_event (event_time + GATE_DELAY, x_EXRESET, VAL, val);
    }

    //IACK2
    void set_IACK2 (_Bool val) {
      add_event (event_time + GATE_DELAY, x_IACK2, VAL, val);
    }

    //IACK4
    void set_IACK4 (_Bool val) {
      add_event (event_time + GATE_DELAY, x_IACK4, VAL, val);
    }

    //LDS
    void set_LDS (_Bool val) {
      add_event (event_time + GATE_DELAY, x_LDS, VAL, val);
    }

    //R_W_
    void set_R_W_ (_Bool val) {
      add_event (event_time + GATE_DELAY, x_R_W_, VAL, val);
    }

    //UDS
    void set_UDS (_Bool val) {
      add_event (event_time + GATE_DELAY, x_UDS, VAL, val);
    }

    void write_byte (int a, int d) {
      wait_rising_edge_n (x_10M, 2);
      set_Axx (a);
      set_AS (1);
      set_R_W_ (0);
      if ((a & 1) == 0) {
        set_Dxx (d << 8);
        set_UDS (1);
        set_LDS (0);
      } else {
        set_Dxx (d & 0xff);
        set_UDS (0);
        set_LDS (1);
      }
      wait_1 (x_DTACK);
      wait_rising_edge_n (x_10M, 2);
      set_UDS (0);
      set_LDS (0);
      set_AS (0);
      wait_0 (x_DTACK);
      wait_rising_edge_n (x_10M, 2);
    }

    void write_word (int a, int d) {
      wait_rising_edge_n (x_10M, 2);
      set_Axx (a);
      set_AS (1);
      set_R_W_ (0);
      set_Dxx (d);
      set_UDS (1);
      set_LDS (1);
      wait_1 (x_DTACK);
      wait_rising_edge_n (x_10M, 2);
      set_UDS (0);
      set_LDS (0);
      set_AS (0);
      wait_0 (x_DTACK);
      wait_rising_edge_n (x_10M, 2);
    }

    int read_byte (int a) {
      wait_rising_edge_n (x_10M, 2);
      set_R_W_ (1);
      set_Axx (a);
      set_AS (1);
      if ((a & 1) == 0) {
        set_UDS (1);
        set_LDS (0);
      } else {
        set_UDS (0);
        set_LDS (1);
      }
      wait_1 (x_DTACK);
      wait_rising_edge_n (x_10M, 2);
      int d = ((a & 1) == 0 ?
               get_Dxx () >> 8 :
               get_Dxx () & 0xff);
      set_UDS (0);
      set_LDS (0);
      set_AS (0);
      wait_0 (x_DTACK);
      wait_rising_edge_n (x_10M, 2);
      return d;
    }

    int read_word (int a) {
      wait_rising_edge_n (x_10M, 2);
      set_R_W_ (1);
      set_Axx (a);
      set_AS (1);
      set_UDS (1);
      set_LDS (1);
      wait_1 (x_DTACK);
      wait_rising_edge_n (x_10M, 2);
      int d = get_Dxx ();
      set_UDS (0);
      set_LDS (0);
      set_AS (0);
      wait_0 (x_DTACK);
      wait_rising_edge_n (x_10M, 2);
      return d;
    }

    //全体を初期化する
    void reset_all () {
      //イベントを止める
      event_time = 0L;
      event_offset = 0;
      event_count = 0;
      //ノードを初期化する
      //  同じ状態から何度も試せるようにする
      for (int i = 0; i < node_count; i++) {
        node_t *node = &node_array[i];
        node->task = NULL;
        node->val = node->initial_val;
        node->initialized = 0;
        if (node->type == PORT) {
        } else if (node->type == NODE) {
        } else if (node->type == IOPIN) {
          node->dval = 0;
          node->ival = 0;
          node->oval = 0;
          node->out = 0;
        } else if (node->type == REG) {
          node->cval = 0;
    #ifdef SPIKE_DETECTION
          node->prev_time = -1L;
    #endif
        }
      }
      //すべてのポートに0を入力するイベントを作成する
      //  イベント駆動型の回路シミュレータは運用前にすべての小回路を少なくとも1回評価しなければならない
      input_zeros ();
      //イベントキューが空になるまでイベントを実行する
      while (event_count != 0) {
        execute ();
      }
      event_time = 0L;
      event_offset = 0;
      event_count = 0;
    }

    //リセットボタンを押す
    void push_reset_button () {
      wait_rising_edge (x_10M);
      set_EXRESET (1);
      wait_rising_edge_n (x_10M, 200);  //20us
      set_EXRESET (0);
      set_EXACK (0);
      wait_rising_edge_n (x_10M, 2);
    }

    int get_a_Dxx () {
      return (a_D12->oval << 12 |
              a_D11->oval << 11 |
              a_D10->oval << 10 |
              a_D9->oval << 9 |
              a_D8->oval << 8 |
              a_D7->oval << 7 |
              a_D6->oval << 6 |
              a_D5->oval << 5 |
              a_D4->oval << 4 |
              a_D3->oval << 3 |
              a_D2->oval << 2 |
              a_D1->oval << 1 |
              a_D0->oval << 0);
    }

    //リセット値
    void reset_value () {
      printf ("reset value\n");
      reset_all ();
      start_clocks ();
      push_reset_button ();
      printf ("data=0x%04x\n", read_word (DATA_PORT));
      printf ("command=0x%04x\n", read_word (COMMAND_PORT));
      printf ("status=0x%04x\n", read_word (STATUS_PORT));
      printf ("\n");
    }

    //M256タイミング
    void m256_timing () {
      printf ("m256 timing\n");
      int commands[] = {
        //          fmic cc  s
        0x0fd11,  //00100010001 m256 16k mono
        0x0fd13,  //00100010011 m256 16k stereo
        0x0fd21,  //00100100001 m256 22k mono
        0x0fd23,  //00100100011 m256 22k stereo
        0x0fd31,  //00100110001 m256 24k mono
        0x0fd33,  //00100110011 m256 24k stereo
        0x0fd91,  //00110010001 m256 32k mono
        0x0fd93,  //00110010011 m256 32k stereo
        0x0fda1,  //00110100001 m256 44k mono
        0x0fda3,  //00110100011 m256 44k stereo
        0x0fdb1,  //00110110001 m256 48k mono
        0x0fdb3,  //00110110011 m256 48k stereo
        0x1fd11,  //10100010001 force m256 16k mono
        0x1fd13,  //10100010011 force m256 16k stereo
        0x1fd21,  //10100100001 force m256 22k mono
        0x1fd23,  //10100100011 force m256 22k stereo
        0x1fd31,  //10100110001 force m256 24k mono
        0x1fd33,  //10100110011 force m256 24k stereo
        0x1fd91,  //10110010001 force m256 32k mono
        0x1fd93,  //10110010011 force m256 32k stereo
        0x1fda1,  //10110100001 force m256 44k mono
        0x1fda3,  //10110100011 force m256 44k stereo
        0x1fdb1,  //10110110001 force m256 48k mono
        0x1fdb3,  //10110110011 force m256 48k stereo
      };
      char *title[] = {
        "m256 16k mono",
        "m256 16k stereo",
        "m256 22k mono",
        "m256 22k stereo",
        "m256 24k mono",
        "m256 24k stereo",
        "m256 32k mono",
        "m256 32k stereo",
        "m256 44k mono",
        "m256 44k stereo",
        "m256 48k mono",
        "m256 48k stereo",
        "force m256 16k mono",
        "force m256 16k stereo",
        "force m256 22k mono",
        "force m256 22k stereo",
        "force m256 24k mono",
        "force m256 24k stereo",
        "force m256 32k mono",
        "force m256 32k stereo",
        "force m256 44k mono",
        "force m256 44k stereo",
        "force m256 48k mono",
        "force m256 48k stereo",
      };
      for (int test = 0; test < (int) (sizeof (commands) / sizeof (commands[0])); test++) {
        int command = commands[test];
        reset_all ();
        start_clocks ();
        push_reset_button ();
        wait_rising_edge (x_VDISP);
        printf ("command=0x%04x %s\n", command & 65535, title[test]);
        write_word (COMMAND_PORT, command);
        int *o_count = ((command & 0x30) == 0x10 ? &o_X1_count :  //16000/32000
                        (command & 0x10) == 0x00 ? &o_X2_count :  //22050/44100
                        &o_X3_count);  //24000/48000
        int o_base = *o_count;
        double time_base = event_time;
        const int maximum_rows = 16;
        double time_min[maximum_rows], time_avg[maximum_rows], time_max[maximum_rows];
        double o_min[maximum_rows], o_avg[maximum_rows], o_max[maximum_rows];
        const int hsynccol = 0;
        const int hdispcol = 1;
        const int pclcol = 2;
        const int reqcol = 3;
        char *label[maximum_rows][4];
        for (int i = 0; i < maximum_rows; i++) {
          time_min[i] = 1e+12;
          time_avg[i] = 0.0;
          time_max[i] = 0.0;
          o_min[i] = 1e+12;
          o_avg[i] = 0.0;
          o_max[i] = 0.0;
          label[i][hsynccol] = "";
          label[i][hdispcol] = "";
          label[i][pclcol] = "";
          label[i][reqcol] = "";
        }
        int major = -5;
        int minor = 0;
        const int count = 100;
        const int label_major = 1;  //0と1に違いがないことを確認済み
        for (;;) {
          _Bool hsyncval = x_HSYNC->val;
          _Bool pclval = x_EXPCL->val;
          _Bool reqval = x_EXREQ->val;
          do {
            execute ();
          } while (hsyncval == x_HSYNC->val &&
                   pclval == x_EXPCL->val &&
                   reqval == x_EXREQ->val);
          if ((command & 0x10000) == 0 &&  //forceでない
              reqval != x_EXREQ->val) {  //EXREQが変化した
            add_event (event_time + EXACK_DELAY, x_EXACK, VAL, x_EXREQ->val);
          }
          if (!hsyncval && x_HSYNC->val) {  //HSYNC立ち上がり
            if (major == label_major) {
              label[minor][hsynccol] = hsyncval == x_HSYNC->val ? "" : hsyncval ? "H->L" : "L->H";
              label[minor][pclcol] = pclval == x_EXPCL->val ? "" : pclval ? "L->H" : "H->L";
              label[minor][reqcol] = reqval == x_EXREQ->val ? "" : reqval ? "L->H" : "H->L";
            }
            if (0 <= major) {
              double t = event_time - time_base;
              if (t < time_min[minor]) {
                time_min[minor] = t;
              }
              if (time_max[minor] < t) {
                time_max[minor] = t;
              }
              time_avg[minor] += t;
              t = (double) (*o_count - o_base);
              if (t < o_min[minor]) {
                o_min[minor] = t;
              }
              if (o_max[minor] < t) {
                o_max[minor] = t;
              }
              o_avg[minor] += t;
            }
            o_base = *o_count;
            time_base = event_time;
            major++;
            minor = 0;
            if (count <= major) {
              break;
            }
          }
          {
            if (major == label_major) {
              label[minor][hsynccol] = hsyncval == x_HSYNC->val ? "" : hsyncval ? "H->L" : "L->H";
              label[minor][pclcol] = pclval == x_EXPCL->val ? "" : pclval ? "L->H" : "H->L";
              label[minor][reqcol] = reqval == x_EXREQ->val ? "" : reqval ? "L->H" : "H->L";
            }
            if (0 <= major) {
              double t = event_time - time_base;
              if (t < time_min[minor]) {
                time_min[minor] = t;
              }
              if (time_max[minor] < t) {
                time_max[minor] = t;
              }
              time_avg[minor] += t;
              t = (double) (*o_count - o_base);
              if (t < o_min[minor]) {
                o_min[minor] = t;
              }
              if (o_max[minor] < t) {
                o_max[minor] = t;
              }
              o_avg[minor] += t;
            }
          }
          minor++;
        }  //for
        //有効な行数を確認する
        int effective_rows = maximum_rows;
        while (1 < effective_rows && time_avg[effective_rows - 1] == 0.0) {
          effective_rows--;
        }
        //avgをcountで割る
        for (int i = 0; i < effective_rows; i++) {
          time_avg[i] /= (double) count;
          o_avg[i] /= (double) count;
        }
        //オシレータの周波数
        double o_freq = (o_count == &o_X1_count ? 12288000.0 :
                         o_count == &o_X2_count ? 16934400.0 :
                         18432000.0);
        //HDISPを挿入する
        double hdisp_time[] = {
          (8.0 * (15.0 + 18.0 + 96.0 * 0.0 / 3.0)) / (69551900.0 / 2.0),
          (8.0 * (15.0 + 18.0 + 96.0 * 1.0 / 3.0)) / (69551900.0 / 2.0),
          (8.0 * (15.0 + 18.0 + 96.0 * 2.0 / 3.0)) / (69551900.0 / 2.0),
          (8.0 * (15.0 + 18.0 + 96.0 * 3.0 / 3.0)) / (69551900.0 / 2.0),
        };
        char *hdisp_label[] = {
          " 0/3",
          " 1/3",
          " 2/3",
          " 3/3",
        };
        effective_rows += 4;
        for (int i = effective_rows - 1, j = 4; 0 <= i; i--) {
          if (j == 0 || hdisp_time[j - 1] < time_avg[i - j]) {
            time_min[i] = time_min[i - j];
            time_avg[i] = time_avg[i - j];
            time_max[i] = time_max[i - j];
            o_min[i] = o_min[i - j];
            o_avg[i] = o_avg[i - j];
            o_max[i] = o_max[i - j];
            label[i][0] = label[i - j][hsynccol];
            label[i][1] = label[i - j][hdispcol];
            label[i][2] = label[i - j][pclcol];
            label[i][3] = label[i - j][reqcol];
          } else {
            j--;
            time_min[i] = -1.0;
            time_avg[i] = hdisp_time[j];
            time_max[i] = -1.0;
            o_min[i] = -1.0;
            o_avg[i] = hdisp_time[j] * (o_freq * 2.0);
            o_max[i] = -1.0;
            label[i][hsynccol] = "";
            label[i][hdispcol] = hdisp_label[j];
            label[i][pclcol] = "";
            label[i][reqcol] = "";
          }
        }
        //範囲を:で表す
        for (int i = 0; i < effective_rows; i++) {
          if (strcmp (label[i][hsynccol], "") == 0) {
            for (int j = i - 1; 0 <= j; j--) {
              if (strcmp (label[j][hsynccol], "H->L") == 0) {
                break;
              }
              if (strcmp (label[j][hsynccol], "L->H") == 0) {
                label[i][hsynccol] = "  :";
                break;
              }
            }
            for (int j = i + 1; j < effective_rows; j++) {
              if (strcmp (label[j][hsynccol], "L->H") == 0) {
                break;
              }
              if (strcmp (label[j][hsynccol], "H->L") == 0) {
                label[i][hsynccol] = "  :";
                break;
              }
            }
          }
          if (strcmp (label[i][hdispcol], "") == 0) {
            for (int j = i - 1; 0 <= j; j--) {
              if (strcmp (label[j][hdispcol], " 3/3") == 0) {
                break;
              }
              if (strcmp (label[j][hdispcol], " 0/3") == 0 ||
                  strcmp (label[j][hdispcol], " 1/3") == 0 ||
                  strcmp (label[j][hdispcol], " 2/3") == 0) {
                label[i][hdispcol] = "  :";
                break;
              }
            }
            for (int j = i + 1; j < effective_rows; j++) {
              if (strcmp (label[j][hdispcol], " 0/3") == 0) {
                break;
              }
              if (strcmp (label[j][hdispcol], " 1/3") == 0 ||
                  strcmp (label[j][hdispcol], " 2/3") == 0 ||
                  strcmp (label[j][hdispcol], " 3/3") == 0) {
                label[i][hdispcol] = "  :";
                break;
              }
            }
          }
          if (strcmp (label[i][pclcol], "") == 0) {
            for (int j = i - 1; 0 <= j; j--) {
              if (strcmp (label[j][pclcol], "L->H") == 0) {
                break;
              }
              if (strcmp (label[j][pclcol], "H->L") == 0) {
                label[i][pclcol] = "  :";
                break;
              }
            }
            for (int j = i + 1; j < effective_rows; j++) {
              if (strcmp (label[j][pclcol], "H->L") == 0) {
                break;
              }
              if (strcmp (label[j][pclcol], "L->H") == 0) {
                label[i][pclcol] = "  :";
                break;
              }
            }
          }
          if (strcmp (label[i][reqcol], "") == 0) {
            for (int j = i - 1; 0 <= j; j--) {
              if (strcmp (label[j][reqcol], "L->H") == 0) {
                break;
              }
              if (strcmp (label[j][reqcol], "H->L") == 0) {
                label[i][reqcol] = "  :";
                break;
              }
            }
            for (int j = i + 1; j < effective_rows; j++) {
              if (strcmp (label[j][reqcol], "H->L") == 0) {
                break;
              }
              if (strcmp (label[j][reqcol], "L->H") == 0) {
                label[i][reqcol] = "  :";
                break;
              }
            }
          }
        }
        //出力する
        printf ("+---------------------+------------------------+-------+-------+-------+-------+\n");
        printf ("|       time us       |    %7.4lfMHz cycle    |       |       | _____ | _____ |\n",
                o_freq / 1000000.0);
        printf ("|  min    avg    max  |   min     avg     max  | HSYNC | HDISP | EXPCL | EXREQ |\n");
        printf ("+---------------------+------------------------+-------+-------+-------+-------+\n");
        for (int i = 0; i < effective_rows; i++) {
          if (time_min[i] < 0.0) {
            printf ("| %-5s  %5.2lf  %-5s",
                    "  -",
                    1000000.0 * time_avg[i],
                    "  -");
          } else {
            printf ("| %5.2lf  %5.2lf  %5.2lf",
                    1000000.0 * time_min[i],
                    1000000.0 * time_avg[i],
                    1000000.0 * time_max[i]);
          }
          if (o_min[i] < 0.0) {
            printf (" | %-6s  %6.2lf  %-6s",
                    "   -",
                    0.5 * o_avg[i],
                    "   -");
          } else {
            printf (" | %6.2lf  %6.2lf  %6.2lf",
                    0.5 * o_min[i],
                    0.5 * o_avg[i],
                    0.5 * o_max[i]);
          }
          printf (" | %-5s | %-5s | %-5s | %-5s |\n",
                  label[i][hsynccol],
                  label[i][hdispcol],
                  label[i][pclcol],
                  label[i][reqcol]);
        }
        printf ("+---------------------+------------------------+-------+-------+-------+-------+\n");
      }
      printf ("\n");
    }

    //出力タイミング
    void out_timing () {
      printf ("out timing\n");
      int commands[] = {
        //         mic cc  s
        0xfe5d,  //1001011101 ackig 16k mono
        0xfe5f,  //1001011111 ackig 16k stereo
        0xfe6d,  //1001101101 ackig 22k mono
        0xfe6f,  //1001101111 ackig 22k stereo
        0xfe7d,  //1001111101 ackig 24k mono
        0xfe7f,  //1001111111 ackig 24k stereo
        0xfedd,  //1011011101 ackig 32k mono
        0xfedf,  //1011011111 ackig 32k stereo
        0xfeed,  //1011101101 ackig 44k mono
        0xfeef,  //1011101111 ackig 44k stereo
        0xfefd,  //1011111101 ackig 48k mono
        0xfeff,  //1011111111 ackig 48k stereo
        0xff5d,  //1101011101 16k mono
        0xff5f,  //1101011111 16k stereo
        0xff6d,  //1101101101 22k mono
        0xff6f,  //1101101111 22k stereo
        0xff7d,  //1101111101 24k mono
        0xff7f,  //1101111111 24k stereo
        0xffdd,  //1111011101 32k mono
        0xffdf,  //1111011111 32k stereo
        0xffed,  //1111101101 44k mono
        0xffef,  //1111101111 44k stereo
        0xfffd,  //1111111101 48k mono
        0xffff,  //1111111111 48k stereo
      };
      char *title[] = {
        "ackig 16k mono",
        "ackig 16k stereo",
        "ackig 22k mono",
        "ackig 22k stereo",
        "ackig 24k mono",
        "ackig 24k stereo",
        "ackig 32k mono",
        "ackig 32k stereo",
        "ackig 44k mono",
        "ackig 44k stereo",
        "ackig 48k mono",
        "ackig 48k stereo",
        "16k mono",
        "16k stereo",
        "22k mono",
        "22k stereo",
        "24k mono",
        "24k stereo",
        "32k mono",
        "32k stereo",
        "44k mono",
        "44k stereo",
        "48k mono",
        "48k stereo",
      };
      for (int test = 0; test < (int) (sizeof (commands) / sizeof (commands[0])); test++) {
        int command = commands[test];
        reset_all ();
        start_clocks ();
        push_reset_button ();
        printf ("command=0x%04x %s\n", command & 65535, title[test]);
        write_word (COMMAND_PORT, command);
        int *o_count = ((command & 0x30) == 0x10 ? &o_X1_count :  //16000/32000
                        (command & 0x10) == 0x00 ? &o_X2_count :  //22050/44100
                        &o_X3_count);  //24000/48000
        int o_base = *o_count;
        double time_base = event_time;
        const int maximum_rows = 16;
        double time_min[maximum_rows], time_avg[maximum_rows], time_max[maximum_rows];
        double o_min[maximum_rows], o_avg[maximum_rows], o_max[maximum_rows];
        const int pclcol = 0;
        const int reqcol = 1;
        const int lrckcol = 2;
        char *label[maximum_rows][3];
        for (int i = 0; i < maximum_rows; i++) {
          time_min[i] = 1e+12;
          time_avg[i] = 0.0;
          time_max[i] = 0.0;
          o_min[i] = 1e+12;
          o_avg[i] = 0.0;
          o_max[i] = 0.0;
          label[i][pclcol] = "";
          label[i][reqcol] = "";
          label[i][lrckcol] = "";
        }
        int major = -5;
        int minor = 0;
        const int count = 100;
        for (;;) {
          _Bool pclval = x_EXPCL->val;
          _Bool reqval = x_EXREQ->val;
          _Bool lrckval = a_LRCK->val;
          do {
            execute ();
          } while (pclval == x_EXPCL->val &&
                   reqval == x_EXREQ->val &&
                   lrckval == a_LRCK->val);
          if ((command & 0x10000) == 0 &&  //forceでない
              reqval != x_EXREQ->val) {  //EXREQが変化した
            add_event (event_time + EXACK_DELAY, x_EXACK, VAL, x_EXREQ->val);
          }
          if (!pclval && x_EXPCL->val) {  //PCL立ち上がり
            if (major == 0) {
              label[minor][pclcol] = pclval == x_EXPCL->val ? "" : pclval ? "L->H" : "H->L";
              label[minor][reqcol] = reqval == x_EXREQ->val ? "" : reqval ? "L->H" : "H->L";
              label[minor][lrckcol] = lrckval == a_LRCK->val ? "" : lrckval ? "H->L" : "L->H";
            }
            if (0 <= major) {
              double t = event_time - time_base;
              if (t < time_min[minor]) {
                time_min[minor] = t;
              }
              if (time_max[minor] < t) {
                time_max[minor] = t;
              }
              time_avg[minor] += t;
              t = (double) (*o_count - o_base);
              if (t < o_min[minor]) {
                o_min[minor] = t;
              }
              if (o_max[minor] < t) {
                o_max[minor] = t;
              }
              o_avg[minor] += t;
            }
            o_base = *o_count;
            time_base = event_time;
            major++;
            minor = 0;
            if (count <= major) {
              break;
            }
          }
          {
            if (major == 0) {
              label[minor][pclcol] = pclval == x_EXPCL->val ? "" : pclval ? "L->H" : "H->L";
              label[minor][reqcol] = reqval == x_EXREQ->val ? "" : reqval ? "L->H" : "H->L";
              label[minor][lrckcol] = lrckval == a_LRCK->val ? "" : lrckval ? "H->L" : "L->H";
            }
            if (0 <= major) {
              double t = event_time - time_base;
              if (t < time_min[minor]) {
                time_min[minor] = t;
              }
              if (time_max[minor] < t) {
                time_max[minor] = t;
              }
              time_avg[minor] += t;
              t = (double) (*o_count - o_base);
              if (t < o_min[minor]) {
                o_min[minor] = t;
              }
              if (o_max[minor] < t) {
                o_max[minor] = t;
              }
              o_avg[minor] += t;
            }
          }
          minor++;
        }  //for
        //有効な行数を確認する
        int effective_rows = maximum_rows;
        while (1 < effective_rows && time_avg[effective_rows - 1] == 0.0) {
          effective_rows--;
        }
        //avgをcountで割る
        for (int i = 0; i < effective_rows; i++) {
          time_avg[i] /= (double) count;
          o_avg[i] /= (double) count;
        }
        //オシレータの周波数
        double o_freq = (o_count == &o_X1_count ? 12288000.0 :
                         o_count == &o_X2_count ? 16934400.0 :
                         18432000.0);
        //範囲を:で表す
        for (int i = 0; i < effective_rows; i++) {
          if (strcmp (label[i][pclcol], "") == 0) {
            for (int j = i - 1; 0 <= j; j--) {
              if (strcmp (label[j][pclcol], "L->H") == 0) {
                break;
              }
              if (strcmp (label[j][pclcol], "H->L") == 0) {
                label[i][pclcol] = "  :";
                break;
              }
            }
            for (int j = i + 1; j < effective_rows; j++) {
              if (strcmp (label[j][pclcol], "H->L") == 0) {
                break;
              }
              if (strcmp (label[j][pclcol], "L->H") == 0) {
                label[i][pclcol] = "  :";
                break;
              }
            }
          }
          if (strcmp (label[i][reqcol], "") == 0) {
            for (int j = i - 1; 0 <= j; j--) {
              if (strcmp (label[j][reqcol], "L->H") == 0) {
                break;
              }
              if (strcmp (label[j][reqcol], "H->L") == 0) {
                label[i][reqcol] = "  :";
                break;
              }
            }
            for (int j = i + 1; j < effective_rows; j++) {
              if (strcmp (label[j][reqcol], "H->L") == 0) {
                break;
              }
              if (strcmp (label[j][reqcol], "L->H") == 0) {
                label[i][reqcol] = "  :";
                break;
              }
            }
          }
          if (strcmp (label[i][lrckcol], "") == 0) {
            for (int j = i - 1; 0 <= j; j--) {
              if (strcmp (label[j][lrckcol], "H->L") == 0) {
                break;
              }
              if (strcmp (label[j][lrckcol], "L->H") == 0) {
                label[i][lrckcol] = "  :";
                break;
              }
            }
            for (int j = i + 1; j < effective_rows; j++) {
              if (strcmp (label[j][lrckcol], "L->H") == 0) {
                break;
              }
              if (strcmp (label[j][lrckcol], "H->L") == 0) {
                label[i][lrckcol] = "  :";
                break;
              }
            }
          }
        }
        //出力する
        printf ("+---------------------+------------------------+-------+-------+-------+\n");
        printf ("|       time us       |    %7.4lfMHz cycle    | _____ | _____ |       |\n",
                o_freq / 1000000.0);
        printf ("|  min    avg    max  |   min     avg     max  | EXPCL | EXREQ | LRCK  |\n");
        printf ("+---------------------+------------------------+-------+-------+-------+\n");
        for (int i = 0; i < effective_rows; i++) {
          if (time_min[i] < 0.0) {
            printf ("| %-5s  %5.2lf  %-5s",
                    "  -",
                    1000000.0 * time_avg[i],
                    "  -");
          } else {
            printf ("| %5.2lf  %5.2lf  %5.2lf",
                    1000000.0 * time_min[i],
                    1000000.0 * time_avg[i],
                    1000000.0 * time_max[i]);
          }
          if (o_min[i] < 0.0) {
            printf (" | %-6s  %6.2lf  %-6s",
                    "   -",
                    0.5 * o_avg[i],
                    "   -");
          } else {
            printf (" | %6.2lf  %6.2lf  %6.2lf",
                    0.5 * o_min[i],
                    0.5 * o_avg[i],
                    0.5 * o_max[i]);
          }
          printf (" | %-5s | %-5s | %-5s |\n",
                  label[i][pclcol],
                  label[i][reqcol],
                  label[i][lrckcol]);
        }
        printf ("+---------------------+------------------------+-------+-------+-------+\n");
      }
      printf ("\n");
    }

    int main () {
    #if 0
      log_add_event = 1;
      log_execute = 1;
      log_dump = 1;
      log_x68kbus = 1;
    #endif
      create_nodes ();
      init_x68kbus ();
      reset_value ();
      m256_timing ();
      out_timing ();
      return 0;
    }

STUDIO KAMADA
Copyright © 1999-2026 Makoto Kamada