ZSIDの制限とZ80のRレジスタ [Z80]
自作のZ80ボードであるZ80GALを使ってCP/M-80でZSIDを使用中に下記の操作ログに示すようにアセンブル機能でRレジスタの設定ができないことに気が付きました。
更にメモリ設定機能で "LD R,A" のマシンコードである ED 4F を設定し、逆アセンブルしてみるとRではなくIレジスタへのLD命令として表示されました。"LD A,R"についても同様でした。どうやらZSIDはRレジスタには対応していないようです。
因みにZSIDMはZSIDに1行ダンプ表示や小文字シンボル対応し、更にMSX-DOS対応のためにZSIDで使用するRSTをRST38からRST28へ変更したものです(Z80GALもRST38はボード側で使用しているので変更が必要)
詳細は「MSX-DOSに自作スクリーンエディタ移植」を参照してください。
何故、ZSIDでRレジスタを弄ろうとしていたかというとRレジスタのインクリメント条件に関して確かめてみたいことがあったからです。
始めにおさらいとして Z80 のフェッチサイクルを下図に示します。
Rレジスタは7bitカウンタでM1サイクル(オペコード フェッチ)毎にインクリメントされますが、プレフィックス付きの命令の場合、1命令で2回インクリメントされます。
つまりRレジスタの増分は
上記の疑問を確認するために下記のプログラムを作ってみました。
この確認試験に使っているZ80GALはシリアル通信のためにタイマー割込みを使っているので割込み処理の影響を無くすためにRレジスタ参照ループ内は割込み禁止にし、またシリアル通信の送信バッファを空にするためにプログラム先頭でwait処理を行っています。
ZSIDで実行した結果は下記のようになりました。
逆アセンブル部分の011Bと011Dのアドレスで'I'と表示されているのは前述のようにZSIDが'R'に対応していないためです。
メモリダンプの結果からRレジスタはループ内で +7 づつ増加しています。
"LD A,R"はプレフィックスが付いていてRレジスタが +2 になるので "BIT 0,(IX+0)" でRレジスタは +2 の値になるということになります。
その他、上記の結果から次のことが判ります。
★追記 2021/10/17 {
"LD R,A"実行後はRレジスタはAの値のままで"LD A,R"でRレジスタが+2された後の値がAにロードされているという考え方も成り立つがマシン語のフェッチと実行タイミングを考慮するとこれは考え辛い。上記のようにマシン語実行後リフレッシュ時にRレジスタがインクリメントされると推測する。
}
実際にM1/やRFSH/等の制御信号をロジアナで確認しようかとも思いましたが「レトロマイコンZ80ボードの構想(その10)CP/M80 BIOS検討2」のコメントに書いたサイトに命令実行時の制御信号状態のデータがあったことを思い出し、アクセスしてみたらURLが変更になっていました。新URLを確認できたのでリンクを貼っておきます。
これらのデータを見るとM1サイクル数=リフレッシュ回数であり、リフレッシュする毎にRレジスタがインクリメントされるということが良く判ります。
★追記 2022/05/02
Twitterで得た情報ですが Z80 の動作の詳細が記述されている a detailed look at Z80 instruction timings with the help of a Z80 netlist simulatio のリンクを貼っておきます。
★追記 2021/10/17
twitterで The Undocumented Z80 Documented の 6.1 R register and memory refresh にプリフィックスが2つの場合のRレジスタの増分について今回の調査結果と同様のことがかかれているとのコメントを頂きました。
同資料には
It is unclear whether the Z80 increases the R register before or after putting IR on the bus.
と書かれていますが、RESET_and_NOP.txt (リセット後最初の命令を実行開始するまでの動きを調査)を参照すると、リセットでRが0の状態の直後のNOPでのリフレッシュ時のアドレスが0なのでRレジスタのインクリメントタイミングはRをバス上に出力した後だということが判ります。
※上記のIRはIレジスタとRレジスタのペアのことでリフレッシュ中はIレジスタがアドレスバスの上位に出力されます。
★追記 2021/10/19
Web上で動作するMSXのエミュレータであるMSXPenでMSX-DOS環境を使って同様の確認を行ってみました。結果は下のキャプチャーのとおりで実機のZ-80と同様でした。
Rレジスタはゲーム等で簡易乱数として使われることもあるためかエミュレータなのに良く対応できているものですね。
更にメモリ設定機能で "LD R,A" のマシンコードである ED 4F を設定し、逆アセンブルしてみるとRではなくIレジスタへのLD命令として表示されました。"LD A,R"についても同様でした。どうやらZSIDはRレジスタには対応していないようです。
因みにZSIDMはZSIDに1行ダンプ表示や小文字シンボル対応し、更にMSX-DOS対応のためにZSIDで使用するRSTをRST38からRST28へ変更したものです(Z80GALもRST38はボード側で使用しているので変更が必要)
詳細は「MSX-DOSに自作スクリーンエディタ移植」を参照してください。
CP/M-80でのZSID操作ログ |
---|
a>zsidm ZSIDM Ver 1.4 #a 0100 LD I,A 0102 LD R,A ? 0102 NOP 0103 NOP 0104 . #l100,103 0100 LD I,A 0102 NOP 0103 NOP 0104 #s100 0100 ED 0101 47 0102 00 ED 0103 00 4F 0104 3D . #l100,103 0100 LD I,A 0102 LD I,A 0104 # |
何故、ZSIDでRレジスタを弄ろうとしていたかというとRレジスタのインクリメント条件に関して確かめてみたいことがあったからです。
始めにおさらいとして Z80 のフェッチサイクルを下図に示します。
Z80 Instruction Opecode Fetch |
※Zilog's Z80 Family Product Specifications Handbook から引用 |
Rレジスタは7bitカウンタでM1サイクル(オペコード フェッチ)毎にインクリメントされますが、プレフィックス付きの命令の場合、1命令で2回インクリメントされます。
つまりRレジスタの増分は
- "XOR A"(AF)等のプレフィックスのない通常の命令では+1
- "BIT 0,(HL)"(CB 46)や"LD (IX+0),A"(DD 77 00)等のプレフィックス付きの命令では+2
- "BIT 0,(IX+0)"(DD CB 00 46)を実行した場合のRレジスタの増分は?
上記の疑問を確認するために下記のプログラムを作ってみました。
この確認試験に使っているZ80GALはシリアル通信のためにタイマー割込みを使っているので割込み処理の影響を無くすためにRレジスタ参照ループ内は割込み禁止にし、またシリアル通信の送信バッファを空にするためにプログラム先頭でwait処理を行っています。
Rレジスタカウントアップ確認プログラム |
---|
;+++++++++++++++++++++++++++++++++++++++ ; test refresh register ; Ver 0.01 2021/10/16 by skyriver ;+++++++++++++++++++++++++++++++++++++++ 1000 BUFADR1 EQU 1000H 2000 BUFADR2 EQU 2000H .Z80 0000' ASEG ORG 0100H 0100 START: 0100 01 0000 LD BC,0 0103 WAIT: ; to be empty serial buf 0103 10 FE DJNZ WAIT 0105 0D DEC C 0106 20 FB JR NZ,WAIT 0108 AF XOR A 0109 21 1000 LD HL,BUFADR1 010C CD 0118 CALL REFTEST 010F 3E 80 LD A,80H 0111 21 2000 LD HL,BUFADR2 0114 CD 0118 CALL REFTEST 0117 C9 RET 0118 REFTEST: 0118 F3 DI 0119 06 00 LD B,0 011B ED 4F LD R,A 011D ED 5F LOOP: LD A,R 011F 77 LD (HL),A 0120 DD CB 00 46 BIT 0,(IX+0) 0124 23 INC HL 0125 10 F6 DJNZ LOOP 0127 FB EI 0128 C9 RET END |
ZSIDで実行した結果は下記のようになりました。
逆アセンブル部分の011Bと011Dのアドレスで'I'と表示されているのは前述のようにZSIDが'R'に対応していないためです。
確認プログラムの実行結果 |
---|
b>a:zsidm rregtest.com ZSIDM Ver 1.4 NEXT PC END 0180 0100 C1FF #l100,128 0100 LD BC,0000 0103 DJNZ FE 0105 DEC C 0106 JR NZ,FB 0108 XOR A 0109 LD HL,1000 010C CALL 0118 010F LD A,80 0111 LD HL,2000 0114 CALL 0118 0117 RET 0118 DI 0119 LD B,00 011B LD I,A 011D LD A,I 011F LD (HL),A 0120 BIT 0,(IX+00H) 0124 INC HL 0125 DJNZ F6 0127 EI 0128 RET 0129 #g100,117 *0117 #d1000 1000: 02 09 10 17 1E 25 2C 33 3A 41 48 4F 56 5D 64 6B .....%,3:AHOV]dk 1010: 72 79 00 07 0E 15 1C 23 2A 31 38 3F 46 4D 54 5B ry.....#*18?FMT[ 1020: 62 69 70 77 7E 05 0C 13 1A 21 28 2F 36 3D 44 4B bipw~....!(/6=DK 1030: 52 59 60 67 6E 75 7C 03 0A 11 18 1F 26 2D 34 3B RY`gnu|.....&-4; 1040: 42 49 50 57 5E 65 6C 73 7A 01 08 0F 16 1D 24 2B BIPW^elsz.....$+ 1050: 32 39 40 47 4E 55 5C 63 6A 71 78 7F 06 0D 14 1B 29@GNU\cjqx..... 1060: 22 29 30 37 3E 45 4C 53 5A 61 68 6F 76 7D 04 0B ")07>ELSZahov}.. 1070: 12 19 20 27 2E 35 3C 43 4A 51 58 5F 66 6D 74 7B .. '.5<CJQX_fmt{ 1080: 02 09 10 17 1E 25 2C 33 3A 41 48 4F 56 5D 64 6B .....%,3:AHOV]dk 1090: 72 79 00 07 0E 15 1C 23 2A 31 38 3F 46 4D 54 5B ry.....#*18?FMT[ 10A0: 62 69 70 77 7E 05 0C 13 1A 21 28 2F 36 3D 44 4B bipw~....!(/6=DK #d2000 2000: 82 89 90 97 9E A5 AC B3 BA C1 C8 CF D6 DD E4 EB ................ 2010: F2 F9 80 87 8E 95 9C A3 AA B1 B8 BF C6 CD D4 DB ................ 2020: E2 E9 F0 F7 FE 85 8C 93 9A A1 A8 AF B6 BD C4 CB ................ 2030: D2 D9 E0 E7 EE F5 FC 83 8A 91 98 9F A6 AD B4 BB ................ 2040: C2 C9 D0 D7 DE E5 EC F3 FA 81 88 8F 96 9D A4 AB ................ 2050: B2 B9 C0 C7 CE D5 DC E3 EA F1 F8 FF 86 8D 94 9B ................ 2060: A2 A9 B0 B7 BE C5 CC D3 DA E1 E8 EF F6 FD 84 8B ................ 2070: 92 99 A0 A7 AE B5 BC C3 CA D1 D8 DF E6 ED F4 FB ................ 2080: 82 89 90 97 9E A5 AC B3 BA C1 C8 CF D6 DD E4 EB ................ 2090: F2 F9 80 87 8E 95 9C A3 AA B1 B8 BF C6 CD D4 DB ................ 20A0: E2 E9 F0 F7 FE 85 8C 93 9A A1 A8 AF B6 BD C4 CB ................ # |
メモリダンプの結果からRレジスタはループ内で +7 づつ増加しています。
"LD A,R"はプレフィックスが付いていてRレジスタが +2 になるので "BIT 0,(IX+0)" でRレジスタは +2 の値になるということになります。
LOOP: LD A,R ; +2 LD (HL),A ; +1 BIT 0,(IX+0) ; +2 INC HL ; +1 DJNZ LOOP ; +1 |
その他、上記の結果から次のことが判ります。
- RレジスタのMSBは設定値が保持される
- ダンプの最初の値が02なので"LD R,A"実行後のRレジスタはA+1の値になる(プレフィックスでの+1は上書きされる)
- 同様に"LD A,R"ではこの命令でRレジスタが+1された値がAレジスタに設定される(命令実行後は+2となる)
★追記 2021/10/17 {
"LD R,A"実行後はRレジスタはAの値のままで"LD A,R"でRレジスタが+2された後の値がAにロードされているという考え方も成り立つがマシン語のフェッチと実行タイミングを考慮するとこれは考え辛い。上記のようにマシン語実行後リフレッシュ時にRレジスタがインクリメントされると推測する。
}
実際にM1/やRFSH/等の制御信号をロジアナで確認しようかとも思いましたが「レトロマイコンZ80ボードの構想(その10)CP/M80 BIOS検討2」のコメントに書いたサイトに命令実行時の制御信号状態のデータがあったことを思い出し、アクセスしてみたらURLが変更になっていました。新URLを確認できたのでリンクを貼っておきます。
これらのデータを見るとM1サイクル数=リフレッシュ回数であり、リフレッシュする毎にRレジスタがインクリメントされるということが良く判ります。
★追記 2022/05/02
Twitterで得た情報ですが Z80 の動作の詳細が記述されている a detailed look at Z80 instruction timings with the help of a Z80 netlist simulatio のリンクを貼っておきます。
★追記 2021/10/17
twitterで The Undocumented Z80 Documented の 6.1 R register and memory refresh にプリフィックスが2つの場合のRレジスタの増分について今回の調査結果と同様のことがかかれているとのコメントを頂きました。
同資料には
It is unclear whether the Z80 increases the R register before or after putting IR on the bus.
と書かれていますが、RESET_and_NOP.txt (リセット後最初の命令を実行開始するまでの動きを調査)を参照すると、リセットでRが0の状態の直後のNOPでのリフレッシュ時のアドレスが0なのでRレジスタのインクリメントタイミングはRをバス上に出力した後だということが判ります。
※上記のIRはIレジスタとRレジスタのペアのことでリフレッシュ中はIレジスタがアドレスバスの上位に出力されます。
★追記 2021/10/19
Web上で動作するMSXのエミュレータであるMSXPenでMSX-DOS環境を使って同様の確認を行ってみました。結果は下のキャプチャーのとおりで実機のZ-80と同様でした。
Rレジスタはゲーム等で簡易乱数として使われることもあるためかエミュレータなのに良く対応できているものですね。
MSXPenでのRレジスタ確認プログラム実行結果 |
|
コメント 0