misc/mu4sim/mu4sim.pl

use integer;
use strict;
use utf8;
use warnings;

my $DELAY_n384FS = 0;  #n384FSの遅延。0~3

#回路の要素
my $ATOM = 0;
my $NOT = 1;
my $AND = 2;
my $EOR = 3;
my $IOR = 4;

#ノードの種類
my $PORT = 0;
my $NODE = 1;
my $IOPIN = 2;
my $REG = 3;

#ノードと回路の追加
#  名前の先頭1文字はグループ
#    (接続される可能性のある)異なるグループには異なる文字を割り当てること
#    a  Artemis
#    d  Delay
#    j  JP2,JP3
#    l  Lunar
#    m  YMF288 MASTER
#    o  X1,X2,X3
#    s  YMF288 SLAVE
#    t  TC9245F
#    x  X68KBUS
#    ノードの値を参照するとき
#      参照先がIOPINかつ参照元と参照先のグループが異なるとき
#        outを参照する
#      さもなくば
#        valを参照する
my $ADDITION = {

  #artemis
  a_A10 => { ncct => [$ATOM, 'x_A10'] },  #artemis 125 I/O80 [A10] X68KBUS B15 AB10
  a_A11 => { ncct => [$ATOM, 'x_A11'] },  #artemis 123 I/O78 [A11] X68KBUS B16 AB11
  a_A12 => { ncct => [$ATOM, 'x_A12'] },  #artemis 121 I/O76 [A12] X68KBUS B17 AB12
  a_A13 => { ncct => [$ATOM, 'x_A13'] },  #artemis 119 I/O74 [A13] X68KBUS B18 AB13
  a_A14 => { ncct => [$ATOM, 'x_A14'] },  #artemis 117 I/O72 [A14] X68KBUS B19 AB14
  a_A15 => { ncct => [$ATOM, 'x_A15'] },  #artemis 44 I/O22 [A15] X68KBUS B20 AB15
  a_A16 => { ncct => [$ATOM, 'x_A16'] },  #artemis 108 I/O70 [A16] X68KBUS B22 AB16
  a_A17 => { ncct => [$ATOM, 'x_A17'] },  #artemis 107 I/O69 [A17] X68KBUS B23 AB17
  a_A18 => { ncct => [$ATOM, 'x_A18'] },  #artemis 105 I/O67 [A18] X68KBUS B24 AB18
  a_A19 => { ncct => [$ATOM, 'x_A19'] },  #artemis 103 I/O65 [A19] X68KBUS B25 AB19
  a_A20 => { ncct => [$ATOM, 'x_A20'] },  #artemis 101 I/O63 [A20] X68KBUS B26 AB20
  a_A21 => { ncct => [$ATOM, 'x_A21'] },  #artemis 99 I/O61 [A21] X68KBUS B27 AB21
  a_A22 => { ncct => [$ATOM, 'x_A22'] },  #artemis 96 I/O59 [A22] X68KBUS B28 AB22
  a_A23 => { ncct => [$ATOM, 'x_A23'] },  #artemis 94 I/O57 [A23] X68KBUS B29 AB23
  a_A3 => { ncct => [$ATOM, 'x_A3'] },  #artemis 12 I/O94 [A3] X68KBUS B7 AB3
  a_A4 => { ncct => [$ATOM, 'x_A4'] },  #artemis 10 I/O92 [A4] X68KBUS B8 AB4
  a_A5 => { ncct => [$ATOM, 'x_A5'] },  #artemis 8 I/O90 [A5] X68KBUS B9 AB5
  a_A6 => { ncct => [$ATOM, 'x_A6'] },  #artemis 6 I/O88 [A6] X68KBUS B10 AB6
  a_A7 => { ncct => [$ATOM, 'x_A7'] },  #artemis 4 I/O86 [A7] X68KBUS B12 AB7
  a_A8 => { ncct => [$ATOM, 'x_A8'] },  #artemis 2 I/O84 [A8] X68KBUS B13 AB8
  a_A9 => { ncct => [$ATOM, 'x_A9'] },  #artemis 127 I/O82 [A9] X68KBUS B14 AB9
  a_AS => { ncct => [$ATOM, 'x_AS'] },  #artemis 100 I/O62 [AS_] X68KBUS A27 AS_
  a_CLK10M_ => { ncct => [$ATOM, 'x_10M_'] },  #artemis 15 Y0 [10M_] X68KBUS B3 10M_
  a_CLK12M => { ncct => [$ATOM, 'o_X1'] },  #artemis 54 I/O26 [12M] 12.288MHz OSC X1 OUT
  a_CLK16M => { ncct => [$ATOM, 'o_X2'] },  #artemis 53 I/O25 [16M] 16.9344MHz OSC X2 OUT
  a_CLK18M => { ncct => [$ATOM, 'o_X3'] },  #artemis 52 I/O24 [18M] 18.432MHz OSC X3 OUT
  a_CLK20M => { ncct => [$ATOM, 'x_20M'] },  #artemis 80 Y2 [20M] X68KBUS A2 20M
  a_D0 => { icct => [$ATOM, 'x_D0w'] },  #artemis 25 I/O4 [D0] X68KBUS A4 DB0
  a_D1 => { icct => [$ATOM, 'x_D1w'] },  #artemis 24 I/O3 [D1] X68KBUS A5 DB1
  a_D10 => { icct => [$ATOM, 'x_D10w'] },  #artemis 126 I/O81 [D10] X68KBUS A15 DB10
  a_D11 => { icct => [$ATOM, 'x_D11w'] },  #artemis 124 I/O79 [D11] X68KBUS A16 DB11
  a_D12 => { icct => [$ATOM, 'x_D12w'] },  #artemis 122 I/O77 [D12] X68KBUS A17 DB12
  a_D2 => { icct => [$ATOM, 'x_D2w'] },  #artemis 22 I/O1 [D2] X68KBUS A6 DB2
  a_D3 => { icct => [$ATOM, 'x_D3w'] },  #artemis 13 I/O95 [D3] X68KBUS A7 DB3
  a_D4 => { icct => [$ATOM, 'x_D4w'] },  #artemis 11 I/O93 [D4] X68KBUS A8 DB4
  a_D5 => { icct => [$ATOM, 'x_D5w'] },  #artemis 9 I/O91 [D5] X68KBUS A9 DB5
  a_D6 => { icct => [$ATOM, 'x_D6w'] },  #artemis 7 I/O89 [D6] X68KBUS A10 DB6
  a_D7 => { icct => [$ATOM, 'x_D7w'] },  #artemis 5 I/O87 [D7] X68KBUS A12 DB7
  a_D8 => { icct => [$ATOM, 'x_D8w'] },  #artemis 3 I/O85 [D8] X68KBUS A13 DB8
  a_D9 => { icct => [$ATOM, 'x_D9w'] },  #artemis 128 I/O83 [D9] X68KBUS A14 DB9
  a_DATAEXT => { ncct => [$ATOM, 't_DATA'] },  #artemis 74 I/O44 [DATAEXT] TC9245F 16 DATA 光入力データ
  a_ERROR => { ncct => [$ATOM, 't_ERROR'] },  #artemis 75 I/O45 [ERR] TC9245F 20 ERROR 光入力エラー
  a_EXACK => { ncct => [$ATOM, 'x_EXACK'] },  #artemis 88 I/O51 [EXACK_] X68KBUS B37 EXACK_
  #EXPCLOUTの.oeは他のボードの邪魔をしないため。入力はない
  a_EXPCLOUT => { icct => [$ATOM, '1'] },  #artemis 41 I/O19 [EXPCLOUT_] 74LS07 9 - X68KBUS B38 EXPCL_
  #EXREQOUTの.oeは他のボードの邪魔をしないため。入力はない
  a_EXREQOUT => { icct => [$ATOM, '1'] },  #artemis 89 I/O52 [EXREQOUT_] 74LS07 11 - X68KBUS B36 EXREQ_
  a_EXRESET => { ncct => [$ATOM, 'x_EXRESET'] },  #artemis 84 IN4 [EXRESET_] X68KBUS A37 EXRESET_
  a_FS1 => { ncct => [$ATOM, 't_FS1'] },  #artemis 77 I/O47 [FS1] TC9245F 26 FS1 サンプリング周波数
  a_FS2 => { ncct => [$ATOM, 't_FS2'] },  #artemis 76 I/O46 [FS2] TC9245F 27 FS2 サンプリング周波数
  a_HSYNC => { ncct => [$ATOM, 'x_HSYNC'] },  #artemis 61 I/O33 [HSYNC] X68KBUS B32 HSYNC
  a_IACK2 => { ncct => [$ATOM, 'x_IACK2'] },  #artemis 87 I/O50 [IACK2] X68KBUS B44 IACK2
  a_IACK4 => { ncct => [$ATOM, 'x_IACK4'] },  #artemis 86 I/O49 [IACK4] X68KBUS B45 IACK4
  a_IYAN => { ncct => [$ATOM, 'j_J3'] },  #artemis 32 I/O11 [THRU] JP3 THRU - GND
  a_LDS => { ncct => [$ATOM, 'x_LDS'] },  #artemis 98 I/O60 [LDS] X68KBUS A28 LDS
  a_LRCKEXT => { ncct => [$ATOM, 't_LRCK'] },  #artemis 72 I/O42 [LRCKEXT] TC9245F 18 LRCK LRクロック
  a_R_W_ => { ncct => [$ATOM, 'x_R_W_'] },  #artemis 93 I/O56 [R/W_] X68KBUS A30 R/W_
  a_SOUT => { ncct => [$ATOM, 'l_SOUT'] },  #artemis 110 IN5 [SOUT] lunar 13 I/O4 SOUT
  a_UDS => { ncct => [$ATOM, 'x_UDS'] },  #artemis 95 I/O58 [UDS] X68KBUS A29 UDS
  a_VSYNC => { ncct => [$ATOM, 'x_VSYNC'] },  #artemis 56 I/O28 [VSYNC] X68KBUS B33 VSYNC
  a_n384FS => ($DELAY_n384FS == 0 ?
               { ncct => [$ATOM, 'a_n384FSOUT1'] } :  #artemis 83 Y1 [384FS1] artemis 36 I/O14
               { ncct => [$ATOM, 'd_n384FS'] }),  #ディレイ
  a_n384FSEXT => { ncct => [$ATOM, 't_FS384'] },  #artemis 71 I/O41 [384FSEXT] TC9245F 22 FS384 384fsクロック

  #ディレイ
  d_n384FS => ($DELAY_n384FS == 0 ?
               '' :
               $DELAY_n384FS == 1 ?
               { ncct => [$AND,
                          [$ATOM, 'a_n384FSOUT1'],
                          [$ATOM, 'a_n384FSOUT1']] } :
               $DELAY_n384FS == 2 ?
               { ncct => [$AND,
                          [$AND,
                           [$ATOM, 'a_n384FSOUT1'],
                           [$ATOM, 'a_n384FSOUT1']],
                          [$AND,
                           [$ATOM, 'a_n384FSOUT1'],
                           [$ATOM, 'a_n384FSOUT1']]] } :
               $DELAY_n384FS == 3 ?
               { ncct => [$AND,
                          [$AND,
                           [$AND,
                            [$ATOM, 'a_n384FSOUT1'],
                            [$ATOM, 'a_n384FSOUT1']],
                           [$AND,
                            [$ATOM, 'a_n384FSOUT1'],
                            [$ATOM, 'a_n384FSOUT1']]],
                          [$AND,
                           [$AND,
                            [$ATOM, 'a_n384FSOUT1'],
                            [$ATOM, 'a_n384FSOUT1']],
                           [$AND,
                            [$ATOM, 'a_n384FSOUT1'],
                            [$ATOM, 'a_n384FSOUT1']]]] } :
               die),

  #lunar
  l_AD1 => { ncct => [$ATOM, 'a_AD1'] },  #lunar 40 IN3 [AD1] artemis 73 I/O43 AD1
  l_CLK10M => { ncct => [$ATOM, 'x_10M'] },  #lunar 5 Y0 [10M] X68KBUS B2 10M
  l_CLK10M_ => { ncct => [$ATOM, 'x_10M_'] },  #lunar 29 Y1/RESET [10M_] X68KBUS B3 10M_
  l_D0 => { icct => [$ATOM, 'x_D0w'] },  #lunar 19 I/O8 [D0] X68KBUS A4 DB0
  l_D1 => { icct => [$ATOM, 'x_D1w'] },  #lunar 20 I/O9 [D1] X68KBUS A5 DB1
  l_D10 => { icct => [$ATOM, 'x_D10w'] },  #lunar 33 I/O18 [D10] X68KBUS A15 DB10
  l_D11 => { icct => [$ATOM, 'x_D11w'] },  #lunar 34 I/O19 [D11] X68KBUS A16 DB11
  l_D12 => { icct => [$ATOM, 'x_D12w'] },  #lunar 35 I/O20 [D12] X68KBUS A17 DB12
  l_D13 => { icct => [$ATOM, 'x_D13w'] },  #lunar 36 I/O21 [D13] X68KBUS A18 DB13
  l_D14 => { icct => [$ATOM, 'x_D14w'] },  #lunar 37 I/O22 [D14] X68KBUS A19 DB14
  l_D15 => { icct => [$ATOM, 'x_D15w'] },  #lunar 38 I/O23 [D15] X68KBUS A20 DB15
  l_D2 => { icct => [$ATOM, 'x_D2w'] },  #lunar 21 I/O10 [D2] X68KBUS A6 DB2
  l_D3 => { icct => [$ATOM, 'x_D3w'] },  #lunar 22 I/O11 [D3] X68KBUS A7 DB3
  l_D4 => { icct => [$ATOM, 'x_D4w'] },  #lunar 23 I/O12 [D4] X68KBUS A8 DB4
  l_D5 => { icct => [$ATOM, 'x_D5w'] },  #lunar 24 I/O13 [D5] X68KBUS A9 DB5
  l_D6 => { icct => [$ATOM, 'x_D6w'] },  #lunar 25 I/O14 [D6] X68KBUS A10 DB6
  l_D7 => { icct => [$ATOM, 'x_D7w'] },  #lunar 26 I/O15 [D7] X68KBUS A12 DB7
  l_D8 => { icct => [$ATOM, 'x_D8w'] },  #lunar 31 I/O16 [D8] X68KBUS A13 DB8
  l_D9 => { icct => [$ATOM, 'x_D9w'] },  #lunar 32 I/O17 [D9] X68KBUS A14 DB9
  l_EXRESET => { ncct => [$ATOM, 'x_EXRESET'] },  #lunar 14 I/O5 [EXRESET] X68KBUS A37 EXRESET
  l_FMVR => { ncct => [$ATOM, 'a_FMVR'] },  #lunar 44 I/O27 [FMVR] artemis 37 I/O15 FMVR
  l_FMVW => { ncct => [$ATOM, 'a_FMVW'] },  #lunar 12 I/O3 [FMVW] artemis 38 I/O16 FMVW
  l_G1R => { ncct => [$ATOM, 'a_G1R'] },  #lunar 43 I/O26 [G1R] artemis 55 I/O27 G1R
  l_IACKx => { ncct => [$ATOM, 'j_J2'] },  #lunar 10 I/O1 [IACKX] JP2 2  1 [IACK2] X68KBUS B44  3 [IACK4] X68KBUS B45
  l_IRQM => { ncct => [$ATOM, 'm_IRQ'] },  #lunar 16 I/O7 [IRQM] YMF288 MASTER 15 IRQ
  l_LDS => { ncct => [$ATOM, 'x_LDS'] },  #lunar 2 I/O29 [LDS] X68KBUS A28 LDS
  l_LROM => { ncct => [$ATOM, 'm_LRO'] },  #lunar 15 I/O6 [LROM] YAC512 MASTER 7 LRCK / YMF288 MASTER 26 LRO
  l_LROS => { ncct => [$ATOM, 's_LRO'] },  #lunar 4 I/O31 [LROS] YAC513 SLAVE 7 LRCK / YMF288 SRAVE 26 LRO
  l_R_W_ => { ncct => [$ATOM, 'x_R_W_'] },  #lunar 9 I/O0 [R/W_] X68KBUS A30 R/W_
  l_S0 => { ncct => [$ATOM, 'a_S0'] },  #lunar 8 SDI/IN0 [SDI] [SDO] artemis 50 SDO/NC [S0] artemis 30 I/O9 S0
  l_S1 => { ncct => [$ATOM, 'a_S1'] },  #lunar 18 SDO/IN1 [SDO] ISP CABLE 2 SDO [S1] artemis 31 I/O10 S1
  l_SIN => { ncct => [$ATOM, 'a_SIN'] },  #lunar 30 MODE/IN2 [MODE] artemis 46 MODE/IN1 / ISP CABLE 6 MODE [SIN] artemis 40 I/O18 SIN
  l_n384FS => { ncct => [$ATOM, 'a_n384FSOUT0'] },  #lunar 27 SCLK/Y2 [SCLK] artemis 78 SCLK/IN3 / ISP CABLE 8 SCLK [384FS0] artemis 67 I/O37 n384FSOUT0

  #X68KBUS
  x_10M => {
    ccct => [$ATOM, 'x_20M'],
    rcct => [$NOT, [$ATOM, 'x_10M']],
  },  #10Mは20Mの立ち上がりで反転する
  x_10M_ => {
    ncct => [$NOT, [$ATOM, 'x_10M']],
  },  #10M_は10Mを反転したもの
  x_20M => {},
  x_A10 => {},
  x_A11 => {},
  x_A12 => {},
  x_A13 => {},
  x_A14 => {},
  x_A15 => {},
  x_A16 => {},
  x_A17 => {},
  x_A18 => {},
  x_A19 => {},
  x_A20 => {},
  x_A21 => {},
  x_A22 => {},
  x_A23 => {},
  x_A3 => {},
  x_A4 => {},
  x_A5 => {},
  x_A6 => {},
  x_A7 => {},
  x_A8 => {},
  x_A9 => {},
  x_AS => {},
  #データバス
  #  拡張スロット基板でプルアップされている
  #  誰もアクティブにしていなければ1
  #  !(a_DOE || l_DOE) || (a_DOE && a_D0) || (l_DOE && l_D0)
  #  ArtemisはD12まで、LunarはD15まで
  x_D0r => { ncct => [$IOR,
                      [$NOT, [$IOR, [$ATOM, 'a_DOE'], [$ATOM, 'l_DOE']]],
                      [$IOR,
                       [$AND, [$ATOM, 'a_DOE'], [$ATOM, 'a_D0']],
                       [$AND, [$ATOM, 'l_DOE'], [$ATOM, 'l_D0']]]] },
  x_D0w => {},
  x_D1r => { ncct => [$IOR,
                      [$NOT, [$IOR, [$ATOM, 'a_DOE'], [$ATOM, 'l_DOE']]],
                      [$IOR,
                       [$AND, [$ATOM, 'a_DOE'], [$ATOM, 'a_D1']],
                       [$AND, [$ATOM, 'l_DOE'], [$ATOM, 'l_D1']]]] },
  x_D1w => {},
  x_D10r => { ncct => [$IOR,
                       [$NOT, [$IOR, [$ATOM, 'a_DOE'], [$ATOM, 'l_DOE']]],
                       [$IOR,
                        [$AND, [$ATOM, 'a_DOE'], [$ATOM, 'a_D10']],
                        [$AND, [$ATOM, 'l_DOE'], [$ATOM, 'l_D10']]]] },
  x_D10w => {},
  x_D11r => { ncct => [$IOR,
                       [$NOT, [$IOR, [$ATOM, 'a_DOE'], [$ATOM, 'l_DOE']]],
                       [$IOR,
                        [$AND, [$ATOM, 'a_DOE'], [$ATOM, 'a_D11']],
                        [$AND, [$ATOM, 'l_DOE'], [$ATOM, 'l_D11']]]] },
  x_D11w => {},
  x_D12r => { ncct => [$IOR,
                       [$NOT, [$IOR, [$ATOM, 'a_DOE'], [$ATOM, 'l_DOE']]],
                       [$IOR,
                        [$AND, [$ATOM, 'a_DOE'], [$ATOM, 'a_D12']],
                        [$AND, [$ATOM, 'l_DOE'], [$ATOM, 'l_D12']]]] },
  x_D12w => {},
  x_D13r => { ncct => [$IOR,
                       [$NOT, [$ATOM, 'l_DOE']],
                       [$AND, [$ATOM, 'l_DOE'], [$ATOM, 'l_D13']]] },
  x_D13w => {},
  x_D14r => { ncct => [$IOR,
                       [$NOT, [$ATOM, 'l_DOE']],
                       [$AND, [$ATOM, 'l_DOE'], [$ATOM, 'l_D14']]] },
  x_D14w => {},
  x_D15r => { ncct => [$IOR,
                       [$NOT, [$ATOM, 'l_DOE']],
                       [$AND, [$ATOM, 'l_DOE'], [$ATOM, 'l_D15']]] },
  x_D15w => {},
  x_D2r => { ncct => [$IOR,
                      [$NOT, [$IOR, [$ATOM, 'a_DOE'], [$ATOM, 'l_DOE']]],
                      [$IOR,
                       [$AND, [$ATOM, 'a_DOE'], [$ATOM, 'a_D2']],
                       [$AND, [$ATOM, 'l_DOE'], [$ATOM, 'l_D2']]]] },
  x_D2w => {},
  x_D3r => { ncct => [$IOR,
                      [$NOT, [$IOR, [$ATOM, 'a_DOE'], [$ATOM, 'l_DOE']]],
                      [$IOR,
                       [$AND, [$ATOM, 'a_DOE'], [$ATOM, 'a_D3']],
                       [$AND, [$ATOM, 'l_DOE'], [$ATOM, 'l_D3']]]] },
  x_D3w => {},
  x_D4r => { ncct => [$IOR,
                      [$NOT, [$IOR, [$ATOM, 'a_DOE'], [$ATOM, 'l_DOE']]],
                      [$IOR,
                       [$AND, [$ATOM, 'a_DOE'], [$ATOM, 'a_D4']],
                       [$AND, [$ATOM, 'l_DOE'], [$ATOM, 'l_D4']]]] },
  x_D4w => {},
  x_D5r => { ncct => [$IOR,
                      [$NOT, [$IOR, [$ATOM, 'a_DOE'], [$ATOM, 'l_DOE']]],
                      [$IOR,
                       [$AND, [$ATOM, 'a_DOE'], [$ATOM, 'a_D5']],
                       [$AND, [$ATOM, 'l_DOE'], [$ATOM, 'l_D5']]]] },
  x_D5w => {},
  x_D6r => { ncct => [$IOR,
                      [$NOT, [$IOR, [$ATOM, 'a_DOE'], [$ATOM, 'l_DOE']]],
                      [$IOR,
                       [$AND, [$ATOM, 'a_DOE'], [$ATOM, 'a_D6']],
                       [$AND, [$ATOM, 'l_DOE'], [$ATOM, 'l_D6']]]] },
  x_D6w => {},
  x_D7r => { ncct => [$IOR,
                      [$NOT, [$IOR, [$ATOM, 'a_DOE'], [$ATOM, 'l_DOE']]],
                      [$IOR,
                       [$AND, [$ATOM, 'a_DOE'], [$ATOM, 'a_D7']],
                       [$AND, [$ATOM, 'l_DOE'], [$ATOM, 'l_D7']]]] },
  x_D7w => {},
  x_D8r => { ncct => [$IOR,
                      [$NOT, [$IOR, [$ATOM, 'a_DOE'], [$ATOM, 'l_DOE']]],
                      [$IOR,
                       [$AND, [$ATOM, 'a_DOE'], [$ATOM, 'a_D8']],
                       [$AND, [$ATOM, 'l_DOE'], [$ATOM, 'l_D8']]]] },
  x_D8w => {},
  x_D9r => { ncct => [$IOR,
                      [$NOT, [$IOR, [$ATOM, 'a_DOE'], [$ATOM, 'l_DOE']]],
                      [$IOR,
                       [$AND, [$ATOM, 'a_DOE'], [$ATOM, 'a_D9']],
                       [$AND, [$ATOM, 'l_DOE'], [$ATOM, 'l_D9']]]] },
  x_D9w => {},
  x_DTACK => { ncct => [$ATOM, 'a_DTACKOUT_PIN'] },
  x_EXACK => {},
  x_EXPCL => { ncct => [$ATOM, 'a_EXPCLOUT'] },
  x_EXREQ => { ncct => [$ATOM, 'a_EXREQOUT'] },
  x_EXRESET => {},
  x_HSYNC => {},
  x_IACK2 => {},
  x_IACK4 => {},
  x_LDS => {},
  x_R_W_ => {},
  x_UDS => {},
  x_VDISP => {},
  x_VSYNC => {},

  #X1,X2,X3
  o_X1 => {},
  o_X2 => {},
  o_X3 => {},

  #TC9245F
  t_DATA => { ncct => [$ATOM, '0'] },
  t_ERROR => { ncct => [$ATOM, '0'] },
  t_FS1 => { ncct => [$ATOM, '0'] },
  t_FS2 => { ncct => [$ATOM, '0'] },
  t_FS384 => { ncct => [$ATOM, '0'] },
  t_LRCK => { ncct => [$ATOM, '0'] },

  #JP2,JP3
  j_J2 => { ncct => [$ATOM, 'x_IACK2'] },
  j_J3 => { ncct => [$ATOM, '0'] },

  #YMF288 MASTER
  m_IRQ => { ncct => [$ATOM, '0'] },
  m_LRO => { ncct => [$ATOM, '0'] },

  #YMF288 SLAVE
  s_LRO => { ncct => [$ATOM, '0'] },

};

#単項
sub parse_unary {
  my ($s) = @_;
  if ($s =~ /^\s*(\w+)/) {  #数値とシンボル
    $s = $';
    return ([$ATOM, $1], $s);
  } elsif ($s =~ /^\s*\(/) {  #括弧
    $s = $';
    my ($v, $t) = parse_ior ($s);
    $t =~ /^\s*\)/ or die $t;
    $t = $';
    return ($v, $t);
  } elsif ($s =~ /^\s*\!/) {  #論理否定
    $s = $';
    my ($v, $t) = parse_unary ($s);
    return ([$NOT, $v], $t);
  } else {
    die $s;
  }
}

#論理積
sub parse_and {
  my ($s) = @_;
  my ($v, $t) = parse_unary ($s);
  $t =~ /^\s*\&/ or return ($v, $t);
  my $w = [$AND, $v];
  do {
    $t = $';
    ($v, $t) = parse_unary ($t);
    push @$w, $v;
  } while ($t =~ /^\s*\&/);
  return ($w, $t);
}

#排他的論理和
sub parse_eor {
  my ($s) = @_;
  my ($v, $t) = parse_and ($s);
  $t =~ /^\s*\$/ or return ($v, $t);
  my $w = [$EOR, $v];
  do {
    $t = $';
    ($v, $t) = parse_and ($t);
    push @$w, $v;
  } while ($t =~ /^\s*\$/);
  return ($w, $t);
}

#包含的論理和
sub parse_ior {
  my ($s) = @_;
  my ($v, $t) = parse_eor ($s);
  $t =~ /^\s*\#/ or return ($v, $t);
  my $w = [$IOR, $v];
  do {
    $t = $';
    ($v, $t) = parse_eor ($t);
    push @$w, $v;
  } while ($t =~ /^\s*\#/);
  return ($w, $t);
}

#組み合わせ回路を文字列から木構造に変換する
sub parse {
  my ($s) = @_;
  my ($v, $t) = parse_ior ($s);
  $t =~ /^\s*$/ or die $t;
  return $v;
}

my $symbol_to_signal = {};

#組み合わせ回路を木構造からC言語に変換する
sub generate {
  my ($symbol, $v) = @_;
  my $type = $v->[0];
  if ($type == $ATOM) {  #数値とシンボル
    my $s = $v->[1];
    if ($s !~ /^\d/) {
      my $up_symbol = $s;
      my $up_signal = $symbol_to_signal->{$up_symbol};
      my $up_type = $up_signal->{'type'};
      if ($up_type == $IOPIN &&  #参照先がIOPINかつ
          (ord $symbol) ne (ord $up_symbol)) {  #参照元と参照先のグループが異なるとき
        $s .= '->out';
      } else {  #さもなくば
        $s .= '->val';
      }
    }
    return $s;
  } elsif ($type == $NOT) {  #論理否定
    my $s = '!';
    $v->[1]->[0] < $NOT or $s .= '(';
    $s .= generate ($symbol, $v->[1]);
    $v->[1]->[0] < $NOT or $s .= ')';
    return $s;
  } else {  #論理積と排他的論理和と包含的論理和
    my $s = '';
    for (my $i = 1; $i < @$v; $i++) {
      $i == 1 or $s .= ($type == $AND ? ' && ' :
                        $type == $EOR ? ' != ' :
                        $type == $IOR ? ' || ' :
                        die $type);
      $v->[$i]->[0] <= $NOT or $s .= '(';
      $s .= generate ($symbol, $v->[$i]);
      $v->[$i]->[0] <= $NOT or $s .= ')';
    }
    return $s;
  }
}

sub collect {
  my ($v) = @_;
  my @v = @$v;
  my $type = shift @v;
  if ($type == $ATOM) {  #数値とシンボル
    my $s = shift @v;
    return $s =~ /^\d/ ? () : ($s);
  }
  (map { collect ($_) } @v);
}

sub depth {
  my ($v) = @_;
  my @v = @$v;
  my $type = shift @v;
  if ($type == $ATOM) {
    return 0;
  }
  my $d = 0;
  while (@v) {
    my $e = depth (shift @v);
    $d < $e and $d = $e;
  }
  $d + 1;
}

{
  #  signal
  #    symbol
  #    cct  node
  #      occt  iopin
  #      rcct  reg
  #    dcct  iopin
  #    ccct  reg
  foreach my $module_name ('artemis', 'lunar') {
    my $module_head = substr $module_name, 0, 1;
    my $file_name = "$module_name.abl.txt";
    open IN, '<:encoding(utf8)', $file_name or die "cannot open $file_name to read";
    my $body = join '', <IN>;
    close IN;
    while ($body ne '') {
      if ($body =~ /^\s+/) {
        $body = $';
        next;
      }
      if ($body =~ /^(?:\/\/|\")/) {
        $body =~ /(?:\r?\n|$)/;
        $body = $';
        next;
      }
      if ($body =~ /^(?:end|equations|module|title)/i) {
        $body =~ /(?:\r?\n|$)/;
        $body = $';
        next;
      }
      if ($body =~ /^plsi/i) {
        $body =~ /(?:;|$)/;
        $body = $';
        next;
      }
      if ($body =~ /^!?[A-Z_a-z]\w*(?:\s*,\s*!?[A-Z_a-z]\w*)*\s+(?:node|pin)/i) {
        $body =~ /(?:;|$)/;
        $body = $';
        next;
      }
      if ($body =~ /^(?<symbol>[A-Z_a-z]\w*)(?<mode>\.clk|\.oe)?\s*:?=/) {
        my $symbol = $+{'symbol'};
        my $mode = $+{'mode'} // '';
        $body = $';
        $body =~ /(?:;|$)/;
        my $cct = $`;
        $body = $';
        $symbol = $module_head . '_' . $symbol;
        $cct =~ s/\s+//g;
        $cct =~ s/[A-Z_a-z]\w*/$module_head . '_' . $&/eg;
        my $signal;
        if (exists $symbol_to_signal->{$symbol}) {
          $signal = $symbol_to_signal->{$symbol};
        } else {
          $signal = $symbol_to_signal->{$symbol} = {
            symbol => $symbol,
          };
        }
        #回路をcct,dcct,ccctのいずれかに入れる
        if ($mode eq '.oe') {
          exists $signal->{'dcct'} and die;
          $signal->{'dcct'} = parse ($cct);
        } elsif ($mode eq '.clk') {
          exists $signal->{'ccct'} and die;
          $signal->{'ccct'} = parse ($cct);
        } else {
          exists $signal->{'cct'} and die;
          $signal->{'cct'} = parse ($cct);
        }
        #回路で使われているシンボルがなければ作る
        foreach my $symbol2 ($cct =~ /([A-Z_a-z]\w*)/g) {
          if (!exists $symbol_to_signal->{$symbol2}) {
            $symbol_to_signal->{$symbol2} = {
              symbol => $symbol2,
            };
          }
        }
        next;
      }
      die;
    }
  }
  #cctをncct,occt,rcctに振り分ける
  foreach my $symbol (sort { $a cmp $b } keys %$symbol_to_signal) {
    my $signal = $symbol_to_signal->{$symbol};
    exists $signal->{'cct'} or next;
    if (exists $signal->{'dcct'}) {  #入出力ピン
      $signal->{'occt'} = $signal->{'cct'};
      delete $signal->{'cct'};
    } elsif (exists $signal->{'ccct'}) {  #レジスタ
      $signal->{'rcct'} = $signal->{'cct'};
      delete $signal->{'cct'};
    } else {  #ノード、入力ピン、出力ピン
      $signal->{'ncct'} = $signal->{'cct'};
      delete $signal->{'cct'};
    }
  }
  #ノードと回路を追加する
  foreach my $symbol (sort { $a cmp $b } keys %$ADDITION) {
    my $addition = $ADDITION->{$symbol};
    $addition or next;
    exists $symbol_to_signal->{$symbol} or $symbol_to_signal->{$symbol} = {};
    my $signal = $symbol_to_signal->{$symbol};
    foreach my $key (sort { $a cmp $b } keys %$addition) {
      exists $signal->{$key} and die "$symbol $key";
      $signal->{$key} = $addition->{$key};
    }
  }
  #回路の組み合わせを確認する
  foreach my $symbol (sort { $a cmp $b } keys %$symbol_to_signal) {
    my $signal = $symbol_to_signal->{$symbol};
    if ((!exists $signal->{'ccct'}) &&
        (!exists $signal->{'dcct'}) &&
        (!exists $signal->{'icct'}) &&
        (!exists $signal->{'occt'}) &&
        (!exists $signal->{'ncct'}) &&
        (!exists $signal->{'rcct'})) {  #ポート
      $signal->{'type'} = $PORT
    } elsif ((!exists $signal->{'ccct'}) &&
             (!exists $signal->{'dcct'}) &&
             (!exists $signal->{'icct'}) &&
             (!exists $signal->{'occt'}) &&
             (exists $signal->{'ncct'}) &&
             (!exists $signal->{'rcct'})) {  #ノード、入力ピン、出力ピン
      $signal->{'type'} = $NODE;
    } elsif ((!exists $signal->{'ccct'}) &&
             (exists $signal->{'dcct'}) &&
             (exists $signal->{'icct'}) &&
             (exists $signal->{'occt'}) &&
             (!exists $signal->{'ncct'}) &&
             (!exists $signal->{'rcct'})) {  #入出力ピン
      $signal->{'type'} = $IOPIN;
    } elsif ((exists $signal->{'ccct'}) &&
             (!exists $signal->{'dcct'}) &&
             (!exists $signal->{'icct'}) &&
             (!exists $signal->{'occt'}) &&
             (!exists $signal->{'ncct'}) &&
             (exists $signal->{'rcct'})) {  #レジスタ
      $signal->{'type'} = $REG;
    } else {
      die $symbol;
    }
  }
  #シンボル毎に直下のシンボルの集合を作る
  #  すべてのシンボルについて
  #    rcctを除くすべての回路について
  #      回路に含まれる自分以外のすべてのシンボルについて
  #        そのシンボルの直下のシンボルの集合に自分を加える
  foreach my $symbol (sort { $a cmp $b } keys %$symbol_to_signal) {  #すべてのシンボルについて
    my $head = substr $symbol, 0, 1;
    my $signal = $symbol_to_signal->{$symbol};
    foreach my $prefix ('c', 'd', 'i', 'n', 'o') {  #'r'を除く
      my $Xcct = "${prefix}cct";
      my $Xdown = "${prefix}down";
      exists $signal->{$Xcct} or next;
      my $cct = $signal->{$Xcct};
      foreach my $up_symbol (collect ($cct)) {  #回路に含まれる自分以外のすべてのシンボルについて
        $up_symbol ne $symbol or next;
        my $up_head = substr $up_symbol, 0, 1;
        my $up_signal = $symbol_to_signal->{$up_symbol};
        exists $up_signal->{$Xdown}->{$symbol} or $up_signal->{$Xdown}->{$symbol} = {};
        $up_signal->{$Xdown}->{$symbol} = 1;  #そのシンボルの直下のシンボルの集合に自分を加える
      }
    }
  }
  #  downをHASHからARRAYに変更する
  foreach my $symbol (sort { $a cmp $b } keys %$symbol_to_signal) {
    my $signal = $symbol_to_signal->{$symbol};
    foreach my $prefix ('c', 'd', 'i', 'n', 'o') {  #'r'を除く
      my $Xdown = "${prefix}down";
      exists $signal->{$Xdown} or next;
      $signal->{$Xdown} = [sort { $a cmp $b } keys %{$signal->{$Xdown}}];
    }
  }
  #コードを生成する
  #宣言
  foreach my $symbol (sort { $a cmp $b } keys %$symbol_to_signal) {
    my $signal = $symbol_to_signal->{$symbol};
    my $simbol = $signal->{'symbol'};
    print "node_t *$symbol;\n";
  }
  #回路
  foreach my $symbol (sort { $a cmp $b } keys %$symbol_to_signal) {
    my $signal = $symbol_to_signal->{$symbol};
    foreach my $prefix ('c', 'd', 'i', 'n', 'o', 'r') {
      my $Xcct = "${prefix}cct";
      exists $signal->{$Xcct} or next;
      my $cct = $signal->{$Xcct};
      if ($cct) {
        my $code = generate ($symbol, $cct);
        print "_Bool ${symbol}_$Xcct () {\n";
        print "  return $code;\n";
        print "}\n";
      } else {
        print "_Bool ${symbol}_$Xcct ();  //to be defined later\n";
      }
    }
  }
  #直下のシンボル
  foreach my $symbol (sort { $a cmp $b } keys %$symbol_to_signal) {
    my $signal = $symbol_to_signal->{$symbol};
    foreach my $prefix ('c', 'd', 'i', 'n', 'o') {  #'r'を除く
      my $Xdown = "${prefix}down";
      exists $signal->{$Xdown} or next;
      my $count_plus_1 = @{$signal->{$Xdown}} + 1;
      print "node_t *${symbol}_${Xdown}[$count_plus_1];\n";
    }
  }
  #初期化
  print "void create_nodes () {\n";
  #  ノードを作る
  print "  node_count = 0;\n";
  foreach my $symbol (sort { $a cmp $b } keys %$symbol_to_signal) {
    my $signal = $symbol_to_signal->{$symbol};
    if ($signal->{'type'} == $PORT) {
      print "  $symbol = new_port (\"$symbol\"";
      print ", 0";
      print ");\n";
    } elsif ($signal->{'type'} == $NODE) {
      my $ncct = $signal->{'ncct'};
      my $ndpt = depth ($ncct);
      print "  $symbol = new_node (\"$symbol\"";
      if ($ncct->[0] == $ATOM && $ncct->[1] eq '1') {
        print ", 1";  #定数のとき一度も評価されないので式が1を返しても値が1にならない
      } else {
        print ", 0";
      }
      print ", ${symbol}_ncct, $ndpt";
      print ");\n";
    } elsif ($signal->{'type'} == $IOPIN) {
      my $ddpt = depth ($signal->{'dcct'});
      my $idpt = depth ($signal->{'icct'});
      my $odpt = depth ($signal->{'occt'});
      print "  $symbol = new_iopin (\"$symbol\"";
      print ", 0";
      print ", ${symbol}_dcct, $ddpt";
      print ", ${symbol}_icct, $idpt";
      print ", ${symbol}_occt, $odpt";
      print ");\n";
    } elsif ($signal->{'type'} == $REG) {
      my $cdpt = depth ($signal->{'ccct'});
      my $rdpt = depth ($signal->{'rcct'});
      print "  $symbol = new_reg (\"$symbol\"";
      print ", 0";
      print ", ${symbol}_ccct, $cdpt";
      print ", ${symbol}_rcct, $rdpt";
      print ");\n";
    } else {
      die;
    }
  }
  #  直下のノードの集合を作る
  foreach my $symbol (sort { $a cmp $b } keys %$symbol_to_signal) {
    my $signal = $symbol_to_signal->{$symbol};
    foreach my $prefix ('c', 'd', 'i', 'n', 'o') {  #'r'を除く
      my $Xdown = "${prefix}down";
      exists $signal->{$Xdown} or next;
      my $down = $signal->{$Xdown};
      my $count = @$down;
      for (my $i = 0; $i < $count; $i++) {
        print "  ${symbol}_${Xdown}[$i] = $down->[$i];\n";
      }
      print "  ${symbol}_${Xdown}[$count] = NULL;\n";
      print "  $symbol->$Xdown = ${symbol}_$Xdown;\n";
    }
  }
  print "}\n";
  #  すべてのポートに0を入力するイベントを作成する
  print "void input_zeros () {\n";
  foreach my $symbol (sort { $a cmp $b } keys %$symbol_to_signal) {
    my $signal = $symbol_to_signal->{$symbol};
    $signal->{'type'} == $PORT or next;
    print "  add_event (event_time + GATE_DELAY, $symbol, VAL, 0);\n";
  }
  print "}\n";
}