たま日記

たまに書く

メモリマップを調査した

TX81Zには、64KBのROMと8KBのSRAMが搭載されている。CPUであるHD63B03XPのアドレス空間は64KBで、次のような割り当てになっている。

$0000-$001F: CPUの内部レジスタ
$0040-$00FF: CPUの内部RAM
$2000-$3FFF: YM2414Bのアドレス($2000)とデータ($2001)
$4000-$5FFF: LCD -> あんまり調べてない
$6000-$7FFF: SRAM (8KB)
$8000-$FFFF: ROM (64KBのうち、前半32KBと後半32KBをバンク切替で割り当てる)

ROMの解析と並行してメモリの使われ方も解析した。YM2414の制御に関係のある部分を以下に書く。よく分からないものについては省略する。

SRAMのメモリマップ

from -to    len: desc.
$6000         1: ???
$6001-69C0 2496: I01~32のVMEMデータ 4E(78)*20(32)=2496
$69C1-6A0E   78: VCED(1)の退避領域 @D797, B0CE, B18C (jsr $AE9Eでコピーする)
$6A19-6A66   78: VCED(1)の退避領域 @B77C, C2BC, C30D ( 〃                   )
$6A67        13: VCED op4 (VCED) VCED領域の大きさは110
$6A74        13: VCED op3
$6A81        13: VCED op2
$6A8E        13: VCED op1 
$6A9B         1: VCED Algorithm
:
$6AB4-64BD   10: VCED Voice name char 1~10
$6ABE-64C2    5: ACED(FIX, Fix Range, FIN(RATIO), OSW, SHFT) op4
$6AC3-64C7    5: ACED op3
$6AC8-6ACC    5: ACED op2
$6ACD-6AD1    5: ACED op1
$6AD2-6AD4    3: ACED Reverb Rate, FC Pitch, FC Amplitude
$6AD5-6B42  110: VCED+ACED(2)
$6B43-6BB0  110: VCED+ACED(3) 
$6BB1-6C1E  110: VCED+ACED(4)
$6C1F-6C8C  110: VCED+ACED(5)
$6C8D-6CFA  110: VCED+ACED(6)
$6CFB-6D68  110: VCED+ACED(7)
$6D69-6DD6  110: VCED+ACED(8)
$6DD7-6E36   96: PCED INST1~8 12*8=96 
$6E37         1: PCED Micro Tune Table(0-12) 
$6E38         1: PCED Assign Mode (0:normal, 1:alternate)
$6E39         1: PCED Effect Select    (1:delay,2:pan,3:chord)
$6E3A         1: PCED Key (for Micro Tune)(0-11)
$6E3B-6E44   10: PCED Name Character 1~10 
$6E45-7564 1824: (76*24) PMEM(1)~(24) 6E45-, 6E91-, 6EDD-, 6F29-, …
$756B         1: エフェクト(bit1=Pan, bit2=Chord, bit4=Delay)
$7628-763F   24: Micro Tuning Table関連?
$7751-7754    4: Delay関連?
$775D-7764    8: Pan関連? Inst0~7がPanするなら011/101,しないなら0
$7765         1: Pan関連? EC17のみread/write
$7766         1: Chord関連?
$7772         1: 動作モード?
                 bit2    0:シングル  1:パフォーマンス
                 bit1,0 00:Util 01:Edit 10:Play 11:N/A
                 他のbit不明
$7773         1: 現在の音色番号(I01~32=00~1F, A01~32=20~3F, B01~32=40~5F, C01~32=60~7F, D01~32=80~9F)
$7779         1: 現在のパフォーマンス番号(0~23)
$77C6-77CD    8: ch0~7のPortamento time
$780E-781D   16: ch0~7のLFD由来の値(0-99 -> 4864-64) 2byteデータ
$781E-782D   16: ch0~7のLFO Speedを換算したテーブル($9B6D~9C34) 2byteデータ
$782E-7835    8: ch0~7を支配しているLFOLFO Sync (0-1)
$7846-784D    8: ch0~7が利用するLFO番号(0,1,2,3=vib, 1と2の場合bit432にINST番号 @940C)
                 LFOが他のchのINSTに支配されている可能性があるのでbit432に保存しているらしい
$784E         1: LFO1に使うINST番号 (or FF)
$784F         1: LFO2    〃
$7850         1: LFO1を使う最小のch番号 784Eの下2桁が0or3の場合FF
$7851         1: LFO2    〃      784F    〃
$7852         1: 0000 | LFW2 | LFW1
$7853-7872   32: EBSテーブル @9D7E
$78D3-78F2   32: KVSのテーブル(@A00E)
$78F3-7912   32: KVSのテーブル(@A010)
$7913-791A    8: ch0~7について,レジスタ20-27に書き込んだ値 (R | FBL | ALG)
$791B-7922    8: ch0~7のTranspose(-48~0~+48, VCED+PCED)
$7923-792A    8: ch0~7のPitch Bend Range
$792B-7932    8: ch0~7のBC Pitch Bias
$7933-793A    8: ch0~7のOutput番号0,2,4,7 (INST0~7のoutput assign(0,1,2,3)に対応)
$793B-7942    8: ch0~7のINST番号 ($FFで初期化@9382 シングルは#FFのまま?)
$7943-7CE2  928: ch0~7のLS表(29*4*8ch)
$7D13-7D14    2: AMD    1,2
$7D15-7D16    2: MW Amp 1,2
$7D17-7D18    2: BC Amp 1,2
$7D19-7D1A    2: FC Amp 1,2
$7D1B-7D22    8: ch0~7のFC Volume
$7D33-7D3A    8: ch0~7のBCEG Bias
$7D3B-7D42    8: ch0~7のMW Pitch
$7D43-7D4A    8: ch0~7のBC Pitch
$7D4B-7D52    8: ch0~7のFC Pitch
$7D6B-7D72    8: INST0~7の音量(vol99~0 -> 0~255)
$7D7B-7D82    8: ch0~7のPMD
$7DCC-7E0B   64: MIDI送信バッファ
$7F9A-B1     24: Micro Tuning関連?

ROMのメモリマップ

# 前半ROM:
from -to    len: desc.
$8086-80D3   78: INIT VOICEのVMEM(0-83のうち,67-72は省略されていてROMにはない)
$80D4-80FF   44: 80D4:"(TX)"+00, 80D9:"(DX)"+00, 80DE:"Memory Protected"+00, 80EF:""+00
$8202-821B   26: (2*13)    前半バンクのサブルーチン呼び出し(後半で$40にエントリ番号をセットしてJSR $8039で呼び出す
                 82 1C  89 22  95 39  86 C9  87 1E  84 6B  83 7E  85 09 
                 85 94  85 D4  85 A3  9C E5  86 45
$85F1-861E   46: VCEDの各パラメータの最大値テーブル
                 1F 1F 1F | 0F 0F 63 03 07 01 07 63 | 3F 06 | 07 07 63 63 63 63 01 03 07 03 30 01 0C 01 
                 63 63 01 01 01 63 63 63 63 64 63 | 01 07 0F 07 03 | 07 63 63 | 
$861F-862E   16: PCEDの各パラメータの最大値テーブル
                 08 00 9F 10 7F 7F 0E 30 63 03 03 01 | 0C 01 03 0B
$870D-871D   17: "Transmitting!!  "+00
$87A6-8921  380: PMEM SINGLE/DUAL/SPLIT/MONO8/POLY4 (76*5)
$8F1E-903E  289: 文字列 8F1E!"ready? " 8F26!" Verify" 8F2E!" Save" 8F34!" INT    " 8F3D!" ERR"
                 8F42!" Verify Tape" 8F4F!" Tape to INT" 8F5C!"Verify Completed" 8F6D!"Load all  ready?"
                 8F7E!"Load    ready?  " 8F8F!"Tape ?? to BUFF?" 8FA0!" Search Tape  "
                 8FAF!" Load Completed " 8FC0!"(PFM)" 8FC6!" Tape to PFM" 8FD3!" PFM    " 8FDC!"to Tape"
                 8FE4!" Tape to " 8FEE!" PGMCNG " 8FF7!" SYSTEM " 9000!" EFFECT " 9009!" MICTUN "
                 9012!"ALL" 9016!"SYS" 901A!"PC " 901E!"EF " 9022!"MC " 9026!"(AL)" 902B!"(SY)" 9030!"(PC)"
                 9035!"(EF)" 903A!"(MC)"
$9872-9882   17: "Check MIDI THRU "+00
$D1C0-F8BF 9984: A01~D32のVMEM 78byte * 32voice * 4bank
$F8C0-FFDF 6*24: PMEM01~24

# 後半ROM:
from -to    len: desc.
$8086-80D3   78: INIT VOICEのVMEM(0-83のうち,67-72は省略されていてROMにはない)
$80D4-80FF   44: 80D4:"(TX)"+00, 80D9:"(DX)"+00, 80DE:"Memory Protected"+00, 80EF:""+00
$8264-82A1   62: 後半バンクのサブルーチン呼び出し(前半で$40にエントリ番号をセットしてJSR $8039で呼び出す (2*31)
                 91 2C 90 D5 C3 48 96 0E E1 18 92 06 E1 16 E0 98 E0 79 C6 B7 E1 19 E1 05 E6 57 E1 17 E2 DA
                 93 38 E6 49 9A 37 D8 D1 B7 ED B1 4C E8 F4 E9 A1 AC 64 A1 2D C2 F2 C2 C2 C3 19 85 96 82 BA
                 80 00
$9703-9820  286: 2段テーブル @969Bで見てる
                 9703-9718: index: 9719 9731 9749 9761 9779 9791 97A9 97C1 97D9 97F1 9809
                 9719-      3C 00 3D 00 3E 00 3F 00 40 00 41 00 42 00 43 00 44 00 45 00 46 00 47 00
                 9731-      3C 0A 3C 37 3E 0D 3F 14 40 01 41 09 41 36 43 0B 43 38 45 00 46 15 47 03
                 9749-      3C 0A 3C 37 3D 3F 3F 14 40 01 41 09 41 36 43 0B 43 38 45 00 46 15 47 03
                 9761-      3C 07 3C 37 3E 02 3F 0D 3F 3E 41 09 41 39 43 04 43 35 45 00 46 0B 46 3C
                 9779-      3B 3C 3D 05 3D 3F 3E 38 40 01 40 3B 42 04 42 3D 44 06 45 00 45 3A 47 03
                 9791-      3C 08 3D 01 3E 03 3F 04 40 01 41 06 42 00 43 05 44 03 45 00 46 05 47 03
                 97A9-      3C 07 3D 00 3E 02 3F 03 3F 3E 41 05 42 00 43 04 44 02 45 00 46 04 46 3F
                 97C1-      3C 04 3D 00 3E 01 3F 03 3F 3F 41 05 41 3F 43 03 44 01 45 00 46 04 46 3D
                 97D9-      3C 20 3D 20 3E 20 3F 20 40 20 41 20 42 20 43 20 44 20 45 20 46 20 47 20
                 97F1-      42 00 42 20 43 00 43 20 44 00 44 20 45 00 45 20 46 00 46 20 47 00 47 20
                 9809-9820  3F 00 3F 10 3F 20 3F 30 40 00 40 10 40 20 40 30 41 00 41 10 41 20 41 30
$9821-9880   96: テーブル @96C0で見てる
                 00 00 00 48 00 75 00 CA 00 F7 01 3F 01 6C 01 B3 02 09 02 36 02 7D 02 AB 
                 00 00 00 48 00 75 00 CA 00 F7 01 3F 01 6C 01 C1 02 09 02 36 02 7D 02 AB 
                 00 00 00 4B 00 7C 00 C7 00 F7 01 42 01 73 01 BE 02 09 02 39 02 84 02 B5 
                 00 00 00 3A 00 83 00 BC 01 05 01 3F 01 88 01 C1 01 FB 02 44 02 7D 02 C6
$9B6D-9C34  200: LFO Speed(2byte * 100個)
$9D89-9D90    8: EBSテーブル 00 25 49 6E 92 B7 DB FF (00 37 73 110 146 183 219 255)
$9E18-9E57   64: CRSテーブル(微妙に昇順ではない)
                 00 01 02 03 04 05 06 07 08 09 0C 0A 0B 10 0D 0E    6bitのうち
                 14 0F 11 18 12 13 1C 15 16 20 19 17 24 1A 1D 28    9ECCでは下位2bitしか見てない
                 1B 1E 2C 21 30 1F 22 25 34 23 38 29 26 3C 2D 27    9E09では上位4bit見る
                 2A 31 2E 2B 35 32 2F 39 36 33 3D 3A 37 3E 3B 3F
$9FA8-9FBB   20: Operator Output Level 0~19のテーブル
                 7F 7A 76 72 6E 6B 68 66 64 62 60 5E 5C 5A 58 56 55 54 52 51
$9FBC-9FD8   29: Level Scalingでつかう減衰パラメータ
                 00 01 02 03 04 05 06 07 08 09 0B 0E 10 13 17 1C 21 27 2F 39 43 50 5F 71 86 A0 BE E0 FF
$A06D-A0D0  100: Portamento time(0-99)
                 FF FE F3 E8 D3 CA C1 B9 B2 AB A5 9F 99 93 8D 87 82 7D 78 73 6E 6A 66 62 5E
                 5B 58 55 52 4F 4C 4A 48 46 44 42 40 3E 3C 3A 38 36 35 33 31 2F 2E 2C 2A 29
                 27 26 25 24 22 21 1F 1E 1C 1B 1A 19 18 17 16 15 14 13 12 12 11 10 10 0F 0E 
                 0E 0D 0D 0C 0C 0B 0B 0A 0A 09 09 08 08 07 07 06 06 05 05 04 04 03 03 02 01
$A0E4-A0F0   13: Pitch Bend Range(0-12) 00 10 21 31 41 51 61 71 81 91 A2 B2 C2
$A2F0-A34F   96: KCのテーブル(A2B1などで参照)
                 00 01 02 04 05 06 08 09 0A 0C 0D 0E 10 11 12 14 15 16 18 19 1A 1C 1D 1E 20
                 21 22 24 25 26 28 29 2A 2C 2D 2E 30 31 32 34 35 36 38 39 3A 3C 3D 3E 40 41
                 42 44 45 46 48 49 4A 4C 4D 4E 50 51 52 54 55 56 58 59 5A 5C 5D 5E 60 61 62
                 64 65 66 68 69 6A 6C 6D 6E 70 71 72 74 75 76 78 79 7A 7C 7D 7E
$A350-A353    4: opの並び  03 01 02 00
$A485-A4B7   51: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0F 11 14 17 1A 1C 1F 23 27 2A 2E 
                 32 36 3A 3E 43 48 4E 52 58 5D 62 69 6E 75 7B 81 88 8E 95 9C A4 AB B3 BB C2 C2
$A4B8-A537  128: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 
                 1E 1F 20 21 22 23 24 25 26 27 28 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 
                 3D 3E 3F 40 40 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 58 59 
                 5A 5B 5C 5D 5E 5F 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 71 72 73 74 75 76 77 
                 78 79 7A 7B 7C 7D 7E 7F
$A82B-A92A  256: LFO Speed
                 FF FF E0 CD C0 B5 AD A6 A0 9A 95 91 8D 89 86 82
                 80 7D 7A 78 75 73 71 6F 6D 6B 69 67 66 64 62 61 
                 60 5E 5D 5B 5A 59 58 56 55 54 53 52 51 50 4F 4E 
                 4D 4C 4B 4A 49 48 47 46 46 45 44 43 42 42 41 40 
                 40 3F 3E 3D 3D 3C 3B 3B 3A 39 39 38 38 37 36 36 
                 35 35 34 33 33 32 32 31 31 30 30 2F 2F 2E 2E 2D 
                 2D 2C 2C 2B 2B 2A 2A 2A 29 29 28 28 27 27 26 26 
                 26 25 25 24 24 24 23 23 22 22 22 21 21 21 20 20 
                 20 1F 1F 1E 1E 1E 1D 1D 1D 1C 1C 1C 1B 1B 1B 1A 
                 1A 1A 19 19 19 18 18 18 18 17 17 17 16 16 16 15 
                 15 15 15 14 14 14 13 13 13 13 12 12 12 12 11 11 
                 11 11 10 10 10 10 0F 0F 0F 0F 0E 0E 0E 0E 0D 0D 
                 0D 0D 0C 0C 0C 0C 0B 0B 0B 0B 0A 0A 0A 0A 09 09 
                 09 09 08 08 08 08 08 07 07 07 07 06 06 06 06 06 
                 05 05 05 05 05 04 04 04 04 04 03 03 03 03 03 02 
                 02 02 02 02 02 01 01 01 01 01 00 00 00 00 00 00
$A92B-A94A   32: 30 30 30 20 1C 1B 1A 19 18 17 16 15 14 13 12 11 
                 10 0F 0E 0D 0C 09 09 09 09 08 08 08 08 06 04 02
$AF13-AF5E   76: PMEMデータ "SINGLE    " AF0Fで参照される
                 68 00 20 00 7F 07 18 63 MAX Note=8
                 60 00 41 00 7F 07 18 63 
                 60 00 62 00 7F 07 18 63 
                 60 00 63 00 7F 07 18 63 
                 60 00 64 00 7F 07 18 63 
                 60 00 65 00 7F 07 18 63 
                 60 00 66 00 7F 07 18 63 
                 60 00 67 00 7F 07 18 63 
                 00 00 
                 53 49 4E 47 4C 45 20 20 20 20 "SINGLE    "
$B5E5-B61D   57: @B2C0で使うテーブル (3*19)
                 75 65 7F  75 70 01  75 6D 01  75 6E 01  75 66 10  75 67 0F  75 68 02  75 69 11
                 75 6F 01  75 6A 11  75 6B 02  75 6C 01  75 F1 7F  75 F2 30  75 F3 07  75 F4 63
                 75 F5 02  75 F6 01  75 F7 63
$B61E-B627   10: @B246で使うテーブル (2*5)
                 34 07  35 07  DA 07  8C 06  8A 63
$B628-B671   74: @B384で使うテーブル(2*37, 全て使うか未確認)
                 3B 03  36 63  37 63  38 63  39 63  3A 01  3C 07  3D 03 
                 88 01  87 07  89 07  D8 07  8B 3F  D7 01  80 1F  81 1F 
                 84 0F  82 1F  83 0F  DB 03  86 03  85 63  3F 01  40 0C 
                 41 01  42 63  43 63  6C 63  6D 63  47 63  48 63  49 63 
                 4A 63  4B 64  4C 63  3E 30  6B 07
$B672-B68F   30: @B2E3で使うテーブル(2*15, 全て使うか未確認)
                 61 01  80 08  82 9F  83 10  84 7F  85 7F  86 0E  87 30 
                 88 63  89 03  8A 03  60 0C  62 03  8B 01  63 0B
$BBE4-BBEF   12: @B981で使うテーブル
                 05 03 0B 05 01 0F 0C 0A 03 02 0B 03
$C6A6-C6B6   17: "* YAMAHA TX81Z *" + 00
$C7BB-C7BE    4: テーブル @C73F B=0,1,2,3のループで参照  7F 30 07 63
$C7BF-C7C1    3: テーブル @C757   B=0,1,2のループで参照  02 01 63
$D476-D47F   10: "LM  976CRT" @D3B9
$D508-D51F   24: @D4DFで使うテーブル(3*8)
                 00 22 84  02 0A 85  00 25 3A  02 0A 3B  00 41 3C  00 78 4C  13 0A 54  00 21 3D
$D899-D8A9   17: " Midi Received  "+00
$D8AA-D8BA   17: "Midi CSUM Error "+00
$DB7B-DB8E   20: テーブル @DAC5
                 07 00 07 01 07 03 07 04 07 02 09 01 09 00 03 02 03 01 03 03
$DB8F-DBBC   46: テーブル @DB2A
                 02 01 02 02 02 03 02 04 02 05 02 00 03 00 03 01 0A 0D 0A 00
                 0A 01 0A 02 0A 03 0A 04 00 00 00 00 00 00 0A 07 0A 08 0A 09 0A 0A 0A 0B 0A 0C
$DC18-DC23   12: @DC09で使うテーブル
                 29 2A 2A 2B 2C 2D 2E 2F 30 31 32 33
$DCD8-DCE1   10: テーブル @DCC8 B=0-6,9 7と8は参照されない
                 00 01 02 03 05 06 07 00 00 04
$DD4A-DD57   14: テーブル 2*7 @DD31
                 19 00 19 01 19 02 19 03 1A 01 1A 00 1A 02
$DF87-DF92   12: テーブル @DF58
                 00 00 05 06 07 08 09 02 01 00 00 0C
$DF94-E013  128: ベロシティ関連のテーブルぽい
                 FE C0 B4 AE A8 A2 9E 98 94 90 8D 8A 86 82 80 7D 
                 7A 77 74 72 70 6D 6B 69 67 65 63 61 5F 5D 5C 5A 
                 58 56 55 53 52 51 4F 4E 4C 4B 4A 48 47 46 44 43 
                 42 41 40 3F 3E 3C 3B 3A 39 38 37 36 35 34 32 31 
                 30 2F 2E 2D 2C 2B 2A 29 28 27 26 25 24 23 22 21 
                 20 1F 1E 1D 1C 1B 1A 1A 19 18 17 16 16 15 14 13 
                 12 12 11 10 10 0F 0E 0E 0D 0C 0C 0B 0B 0A 09 09 
                 08 07 07 06 06 05 05 04 04 03 03 02 02 01 01 00
$E15A-E16A   17: "Transmitting!!  "+00
$E231-E235    5: F0 43 03 00 5D (VCED Bulk)
$E236-E244   15: F0 43 7E 00 21 "LM  8976AE"    (ACED+VCED)
$E2B3-E2C1   15: F0 43 7E 13 0A "LM  8976PM"    (PMEM)
$E301-E305    5: F0 43 04 20 00
$E3DE-E3EA   13: 7E 00 78 "LM  8976PE"    (PCED)
$E449-E455   13: 7E 00 22 "LM  MCRTE0"    (Micro Tune Octave)
$E4B4-E4C0   13: 7E 02 0A "LM  MCRTE1"    (Micro Tune Full Kbd)
$E536-E542   13: 7E 00 25 "LM  8976S0"    (System)
$E5B1-E5BD   13: 7E 00 0A "LM  8976S1"    (Program Change Table)
$E61C-E628   13: 7E 00 41 "LM  8976S2"    (Effect Data)
$E69B-E6AB   17: "Midi Buffer Full"+00
$E6AC-E6BC   17: "Midi Data Error!"+00
$EBA9-EBB0    8: FF 76 3B 27 1D 17 13 0E
$EF41-F040  256? LFO関連?
                 00 08 0C 13 18 1D 20 27 2B 30 33 38 3C 3F 43 47 
                 4B 50 54 58 5B 5F 63 65 68 6C 6F 72 75 78 7B 7D 
                 80 83 9A 9C 8B 8D 90 92 94 96 99 9B 9D 9F A1 A3 
                 A5 A7 A9 AB AD AE B0 B2 B3 B5 B7 B8 BA BB BD BE 
                 BF C1 C2 C4 C5 C6 C7 C9 CA CB CC CD CE CF D1 D1 
                 D2 D3 D4 D5 D6 D7 D8 D8 D9 DA DB DC DC DD DE DE 
                 DF E0 E1 E1 E2 E2 E3 E4 E4 E5 E5 E6 E6 E7 E7 E8 
                 E8 E9 E9 EA EA EB EB EB EC EC ED ED ED EE EE EE 
                 EF EF F0 F0 F0 F0 F1 F1 F1 F2 F2 F2 F2 F3 F3 F3 
                 F3 F4 F4 F4 F4 F5 F5 F5 F5 F5 F6 F6 F6 F6 F6 F6 
                 F7 F7 F7 F7 F7 F7 F8 F8 F8 F8 F8 F8 F8 F9 F9 F9 
                 F9 F9 F9 F9 F9 FA FA FA FA FA FA FA FA FA FA FA 
                 FB FB FB FB FB FB FB FB FB FB FB FB FB FC FC FC 
                 FC FC FC FC FC FC FC FC FC FC FC FC FC FC FC FC 
                 FD FD FD FD FD FD FD FD FD FD FD FD FD FD FD FD 
                 FD FD FD FD FD FD FD FD FD FD FD FD FD FE FE FF
$F041-F051   17: "V1.6  03-Feb-88 "+00

コールグラフを描いてみた

TX81Zの解析を続けている。MAMEのデバッガには逆アセンブラが内蔵されている。しばらく使っていたのだが実行場所から離れたアドレスのコードが表示できないことがあったり、switch-case文的な意味合いのコードを文面通りにしか出力してくれなかったり、不便に思えた。

作るのは難しくなさそうなので、MAMEソースツリー内の6800dasm.cppを参考にJavaで逆アセンブラを作った。メモリ64KB時代のプログラムの解析のために現代のPCの潤沢なメモリを使う気持ちよさを味わえた。富豪プログラミング、とは少し違うか…。

YM2414のレジスタに書き込む部分、本体のボタンで音色を変更する部分などを中心に処理の流れを追い始めたのだが、全体の流れも知りたくなった。そこでサブルーチン間の呼び出し関係を解析してコールグラフを作成してみた。

プログラムの開始アドレス

5つある。

  1. $8000:リセット後に実行されるアドレス。初期化などをした後、$82A2~$82B8のメインループを実行する。
  2. $802E:YM2414からの割り込みを処理するハンドラ。
  3. $800D:Timer1 Output Compare1,2の割込みハンドラ。前回の積み残し。動作はまだ解析できていない。MAMEの動作を見ていると、MIDIキーボードのキーオン時に1回、キーオフ時に1回呼び出されている。
  4. $8023:SIO割込みハンドラ。MIDIデータが到着したりするときに実行されるみたい。MIDIってシリアル通信で実現されているものだったんですね。知らなかった!
  5. $8018:Timer2 Counter Matchの割り込みハンドラ。一定時間ごとに呼び出される。PMD/AMDを変化させたり、ポルタメントやピッチベンドを担当しているようだ。

コールグラフ

次のプログラム片をサブルーチンと見なしたときのコールグラフを作成した。

  • jsr/bsrで呼び出されるアドレスから始まる
  • rtsで終了する
  • braやjmpなどでつながっている

開始アドレス2.~5.から始まるグラフはこんな感じ。Graphvizに描いてもらった。

$802E、$800D、$8023、$8018からのコールグラフ

開始アドレス1.のコールグラフは貼るには大きすぎるので自粛。本体の音色変更ボタンを押したときに実行される部分($91FE~$92B5)を代わりに貼る。左から2列目の各サブルーチンでYM2414のレジスタに書き込みを行っている。

音色変更時のコールグラフ

次は

コードやテーブル、音色データなどがROMのどこにあるのかは把握できた。次は各サブルーチンの処理内容を読んでSRAMのメモリマップを作ってみたい。

MAMEのTX81Zに手を入れた(1)

ここしばらくMAMEソースコードを読んでいた。まだまだTX81Zのエミュレーションは不完全であることが分かった。Youtubeでも確認できる。

もちろんYM2414の実装であるymfm_opz.cpp, ymfm_opz.hが未完成であることが原因なのだが、どうもTX81Zの実装であるymtx81z.cpp、そのCPUであるHD6303の実装m6801.cppにも起因するようだ。IRQや内部タイマからの割込みに対応できていない。

興味が出てきたので、これらの動作を改善してみることにした。MAMEでTX81Zがうまく動けば、TX81ZやYM2414の解析にも役立つかもしれない。というわけでC++とHD6303アセンブラの勉強をしていたのが、ここ一月ほどの作業。とりあえず割込み関連から手を付けた。

参考資料

それから、https://www.rutles.net/products/detail.php?product_id=794 。当時?の空気を感じたり、CPUの設計思想を理解するのによい資料。文体も楽しい。

YM2414のIRQ

MAMEのデバッガについてる逆アセンブラでTX81ZのROMを読んでみた。$8FF6から始まるIRQの割り込みハンドラの中で、YM2414の発音に必要なレジスタ書き込み($08+ch)が行われている。しかしながら、この割込みハンドラはMAMEでは実行されていない。YM2414はIRQをどうやって出すのだろう?

TX81Z実機で観察したレジスタ書き込み内容をYM2414に流し込んで発音させる、という実験を前回おこなった。YM2414のIRQ出力をロジアナで観察すると、普段はHのIRQ出力が発音時に一瞬だけLに落ちていた。書き込みシーケンスのどこかでIRQが変化するようだ。

ROMを読んだり動作を観察したりした結果、どうもこういうことらしい。TX81Zは音を出すときにYM2414のレジスタ$20+ch(0~7)にbit6=1の書き込みを行う。bit6はYM2151では未定義の機能で、この書き込みを行うとYM2414は次の動作を行う。

  1. IRQをLにして、CPUに割込み要求を行う
  2. 読み出し専用レジスタのbit5を1にする
  3. 読み出し専用レジスタのbit4,3,2にch番号をセットする

YM2151では読み出し専用レジスタはbit7,1,0しか使わないのに対し、仕様が変わっているみたい。前回作成したESP32+YM2414の回路を使って観察した。レジスタ読み出しのために3.3V<->5V双方向のレベル変換を追加した。


Timer2の割込み

TX81Zは1秒間に96回ほど、YM2414に書き込みを行っていることを以前観察した。LFO Delayやポルタメント、ピッチベンド?をソフトウェアで実現するためのようだが、この書き込みをMAMEはしていない。

ROMを眺めていたら、$8F1Fから始まるTimer2 Counter Matchの割込みハンドラの中で行っていることが分かった。

現在の状況

m6801.cpp・ymtx81z.cppを変更し、上記2つの割込みハンドラを実行するようにしてみた。出音の内容は変わってないが、より実機に近いエミュレーションができるようになったはず。

例えば、PLAY/PERFORMボタン上のLEDの動作が改善された。音を出したときON→OFFのまま再度点灯しなかったのが、実機のように再度ONになるようになった。Timer2の割り込みハンドラ内に再点灯のコードがあった。

次は?

$8F8Bから始まるTimer1の割り込みハンドラが何をしているのかよく分からない。これを少しつついたあと、ymfm_opz.cppに手を付ける。Nuked-OPMベースだがOPZのエミュレーションはできているので何とかなりそう。

以前から何らかのエミュレータを作ってみたいなーと思っていたので、作業はとても楽しい。HD6303のアセンブラ解析も暗号解読のようで面白い。C++は難しい…

2023/07/09追記
$8F8Bから始まるTimer1の割込みハンドラはDelay処理だった。MIDIノートオンを受け取った時、MIDIノートオフを受け取った時、Delayする音色の音を出している時(ディレイ音が出ている間)に呼び出されるようだ。

FM音源チップを動かしてみた(3)

ブレッドボード上のYM2151を外し、代わりにYM2414をTX81Zの中の人として動作させる実験を行った。

PC  ->  esp32  ->  YM2414  ->  TX81Zの音色
  1. PCからesp32にレジスタアドレスと書き込む値を送る(シリアル経由)
  2. esp32は受け取ったとおりにYM2414に書き込む
  3. TX81Z実機と同じ音が出ればOK!

受け取る関数はこんな感じ。1byteずつ受け取って、2byte毎に書き込みを行う。esp-idfはArduinoより難しかった…

// app_main()中で
// xTaskCreatePinnedToCore(register_loop, "register", 8192, NULL, 1, &vgmtaskHandle, 1); 
// として動かす
void register_loop() {
    uart_config_t uart_config = {
        .baud_rate = 115200,
        .data_bits = UART_DATA_8_BITS,
        .parity = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
        .source_clk = UART_SCLK_DEFAULT,
    };
    ESP_ERROR_CHECK(uart_param_config(UART_NUM_0, &uart_config));
    ESP_ERROR_CHECK(uart_set_pin(UART_NUM_0, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
    ESP_ERROR_CHECK(uart_driver_install(UART_NUM_0, 256*2, 0, 0, NULL, 0));

    writeData(0x01, 0x02); // A0のLEDを消すため

    uint8_t buf[256];
    int len = 0;
    uint8_t addr=0, data=0;
    int count = 0;
    while (true) {
        while (uart_read_bytes(UART_NUM_0, buf, 1, 1) > 0) { // 1byte読む
            if (++len % 2 == 1) {
                addr = buf[0];
            } else {
                data = buf[0];
                writeData(addr, data);
            }
        }
        if (++count > 1000) {
            count = 0;
            vTaskDelay(1);
        }
    }
}

TX81Z実機を同じスピーカーにつなげて聴き比べしてみた。ピアノとかギターとかは概ね同じように聞こえるのだが、D11 Hole in 1がノイズまみれになる…?

いろいろパラメータをいじくって分かったのだが、LFO Wave=3(Sample & Hold)の場合は以前調査したLFO Speed変換表とは異なる変換が必要なことが分かった。スピードが速すぎるので、うねりではなくノイズっぽく聞こえるみたい。測定した結果を以前の記事に追記した。

nornand.hatenablog.com

今回の測定では、実機ではなくMAMEのTX81Zエミュレーションを使った。開腹してテストクリップを付けなくても、エミュレーションのレジスタ書き込み時にアドレスとデータをprintf()させることで挙動が観察出来て便利。なんとMIDIキーボードに接続して演奏することもできる!感動的!

ただし、実機で測定したときにはAMDとPMDの値を(LFO Delayのために?)延々と更新しつづけてたような気がするのだが、これはエミュでは観察できなかった。

FM音源チップを動かしてみた(2)

YM2151が出力するデジタル音声データを受け取り発声するプログラムを作った。VGMデータを演奏することができた。プログラムと回路図はGitHubに。


参考にしたサイト

  • YM2151からの左右チャンネルの切り替え信号(SH1, SH2)をI2Sフォーマットに変換する方法。EXORゲートとJK-FFを使ってWSEL信号を作り出すことができる。

pcm1723.hateblo.jp

  • YM2151が出力する音声信号(指数部3bit + 仮数部10bit)をI2S 16bitに変換する方法。

kikb.web.fc2.com

  • VGMファイルやその演奏方法については以下より(ほぼ丸パクリ…)。素敵な曲とプログラムに感謝!

github.com

接続

ESP32の2つのI2Sを使う。1つはYM2151の出力を受け取る。1つはUDA1334Aに送り出して音を出す。

[YM2151]                            [ESP32]
  clock1             -> |5->3.3V| -> BCLK_0
      SO             -> |5->3.3V| -> DIN_0

     SH1 -> |EXOR &| -> |5->3.3V| -> WSEL_0
     SH2 -> | JK-FF|
                                                   [UDA1334A]
                                     BCLK_1   ->   BCLK
                                     DOUT_1   ->   DIN
                                     WSEL_1   ->   WSEL
    clockM   <-         |JK-FF|   <- MCLK_1
    D0~D7   <-                      GPIO
    ~iC      <-                      GPIO
    A0       <-                      GPIO
    ~WR      <-                      GPIO

~CS -> GND
~RD -> 5V

電圧

YM2151は5V、ESP32は3.3Vで動くので、レベル変換が必要になる。これを使ったけどこっちでも動くと思う。ESP32→YM2151への向き(L→H)の場合は不要(知らなかった!)。

クロック

前回は3.579545MHzのクリスタルオシレータでクロックを与えていた。このままESP32で受け取ろうとする場合、

  • I2S SLAVE RX(受信):うまく受け取れる
  • I2S SLAVE TX(送信):なんかうまくいかなかった。SLAVEでTXの場合、MCLKを作る必要があるとか見たような気も。APLLを使えばOKという記事も。位相が合わない?同期がとれない?よく分からない…

そこで今回はESP32のI2Sが出力するMCLK信号を1/2に分周してYM2151のΦMとした。SAMPLE_RATE = 55930Hzで、これの128倍の1/2倍 = 3.579520MHz。ちと足りないがこれで行く。

左右チャンネルの区別

SH1とSH2を等価に扱っているので、こちらでも指摘されているように左右が正しく一致する確率は50%となる。初期化時に左チャンネルだけ音を出して、それを確認することで整合性を取れそう(そのうちやる)。

プログラム

2つのタスクをCore1で並行動作させた。

  • YM2151からの信号を受け取り、16bitの2の補数に変換し、UDA1334Aに送り出す、というのをひたすら繰り返すタスク。
  • VGMデータを順に読み取り、YM2151のレジスタに書き込むタスク。

某所よりダウンロードしたGalaga'88のVGMを演奏させてみた。うまく動いているようだ。耳も記憶もあやふやだが何とも懐かしい音がする。試しにYM2414に付け替えたところ、音が消えるはずなのに消えない箇所がいくつか見られた。

まとめ

YM2151のデジタル音声データを取得することができた。次は、これをPCに送って波形を観察したい。

FM音源チップを動かしてみた(1)

YM2151(OPM)とYM2414B(OPZ)が出てきた。YM3012(DAコンバータ)も。2年くらい前に買ったような気がする。5個セット。本物か偽物か分からない。せっかくなので動くかどうか試してみた。YM2151の1つはデートコードが削られてる(今回のテストではこれだけが動かなかった)。

参考としたサイト

  • あちこちに置いてあるYM2151のデータシート(YM2414Bはあまりないみたい?)
  • YM2151の制御方法については以下からたどれるarduino-vgmplayerのプログラムを見てると分かる

another.maple4ever.net

j-7system.blog.ss-blog.jp

  • YM2414BのSH2に2.2kプルアップを付けたら動いたという記事

min.togetter.com

  • SO, SH1, SH2に瞬間的なノイズが入っていたが、5VとGNDの間にコンデンサを入れたら解決した。

www.denshi.club

配線

まずは電源つなげてリセットした直後の動作を観察することにした。次のように配線した。

1,11: GND
22: 5V
3: ~iC -> 10kΩでプルアップ(GNDに触れて離すことでリセットする)
5: ~WR -> 5V
6: ~RD -> 5V
7: ~CS -> つなぐの忘れてた!
24: ΦM -> 3.579545MHzのクリスタルオシレータ

以下は観察対象(DAコンバータYM3012への出力)
21: SO  -> ロジアナch0(未使用3bit + 仮数部10bit + 指数部3bitの音声データ LR交互)
23: Φ1  -> ロジアナch1(1→0でSO・SH1・SH2が変化する 0→1でYM3012が読み取る)
20: SH1 -> ロジアナch2(1→0でLチャンネルの音声データ開始)
19: SH2 -> ロジアナch3(1→0でRチャンネルの音声データ開始)※

※YM2414Bの場合、SH2は2.2KΩでプルアップ必要(後述)


観察結果

リセット直後の多分音が出ていない状態では、以下のような出力が延々と続いた。

  • 上から順に、SO、Φ1、SH1、SH2
  • 右と左の1サンプル分のビット列 001(未使用3bit) + 0000000001(仮数部10bit) + 100(指数部3bit) が見える(並び順はLSB→MSB)
  • 仮数部1000000000、指数部001なので、サンプル値は0 ←DAC出力フォーマットより
  • デートコードが削られた1つを除いて、4つのYM2151は全てこのような出力になった。
  • 一方YM2414はSH2の出力がLのまま変化しない。試しに2.2kΩでプルアップしたら正しく変化するようになった。

本物と偽物

FM音源チップはニセモノが出回っているようだ。自分は本物と確信できるものを所持していないので真贋判別できない。果たして手持ちのチップは本物なのだろうか。「YM2151 偽物」で検索すると 写真が見つかる。

ただし、見た目があやしくても動作した、という例も見つかる。
yfl711.hateblo.jp

ニセモノというより「互換チップ」なのかな?(12月4日と5日の記事)。以下のページには互換品リストへのリンクがある。いやでも互換品なんてヤマハが認めるものなのか…?
oykenkyu.blogspot.com

まとめ

手持ちのYM2151、YM2414Bは動きそうだということが分かった。次はnote onしたときの出力を観察してみよう。

2号機を組み立てた

ウィンドシンセの2号機を組み立てを終えた。

f:id:androuet:20220314164753j:plain

f:id:androuet:20220314171738j:plain

使った部品

各部の説明

筐体は再びエムケーダクトの0号を。今回は縦向きに使ってみた。ふたの面に配線しなくてもいいので組み立てやすい反面、スピーカーの設置に困った。結局スピーカーは小箱に入れて両面テープで固定した。ダクトの切断・加工には超音波カッターを使った。溶かすように切断できるので便利。

バッテリーは本当はリチウムイオンとかリチウムポリマーを使いたかった。でも充電回路に自信がないので、ダクト幅に収まる小型のモバイルバッテリーを探して使った。

キーは画鋲、スズメッキ線、ホームセンターで売っているミニステーで作った。オクターブキーは横に並べてみた。左手親指の動きを小さくすればオクターブ変更時のピロ音がなくなるかな、という目論見。サムフックは100均のコードフックにシリコンチューブを巻いた。

マウスピースは以前作ったものをそのまま利用。気圧センサ+バイトセンサ。これはもうちょっとなんとかしたい。1号機は耳栓、2号機はティッシュの栓。少し格好悪い。他の人の開発事例を見るとこんな感じの気圧センサをシリコンチューブにつないでブレスセンスしている。このほうが信号線が長くならないのでよさそう。現在の信号線はI2Cで、MPR121・GY-521・LPS33HWをつないでいる。

重さは292g。1号機は電池なしで205gなので、ほぼバッテリー分だけ増加した。手持ちのアルトヴェノーヴァは283g、アルトリコーダーは203g。もう少し軽くしたい。

次回?の課題

  • プリント基板の作成:はんだ付けを楽にしたい
  • 組み立て簡単化:どうしてもキーの配線がぐちゃぐちゃになる。キーを基板に組み込むか、筐体基板とマイコン基板に分けてI2Cだけで接続するかして簡単に組み立てられるようにしたい
  • バッテリー小型化:リチウムイオン電池18650+充電モジュールを使えばよい?

まだ各部の動作テスト用のプログラムしか作っていない。ウィンドシンセとして動作させるために、これからソフト開発。