はじめに
以下の記事で Z80 の命令の動きについて書いてみたところ、若干理解が浅かったので、少し深堀りしてみました。
とはいっても、テクニカル・ドキュメント類を読んでもイマイチふわっとした理解しかできませんでした。シンプルに私の読解力が足りないのかもしれませんが、どうやら理解するための素地となる知識が不足しているようです。
テキスト寄り(?)ではなく実験寄りな形で学べればスッキリ理解できるかも...
という訳で色々と調べてみたところ、以下の Visual Z80 Remix というツールがとても分かりやすかったです。
上記ツールを使って LD SP, $0030
という命令(スタックポインタ SP
に $0030
を代入する命令)のトレースログを詳しく読んでみた所、何となく理解できた気がします。
LD SP, $0030
まず LD SP, $0030
という命令の仕様を Zilog が公開している Z80 User Manual で確認してみます。
M Cycles
は 2
とのことですが T States
は 10 (4, 3, 3)
とのことです。
M Cycles
の表記は多分誤植かな?
T States
の表記によると、この命令は 4Hz, 3Hz, 3Hz の3マシンサイクルで構成される 10Hz の命令とのことです。
オペコードは(SPの場合)1バイト目が %00110001 ($31)
で 2〜3バイト目が対象のレジスタペアに代入する値(リトルエンディアン)という形です。
そして、下図が LD SP, $0030
を Visual Z80 Remix で動かした時のトレースログですが、以下2点の補助を当方で加筆してます。
- マシンサイクルの区切りに赤色の実線
- レジスタが書き換わったタイミングに蛍光ペンでマーク
1/2クロック(1Hzの半分)単位でトレースログが記録されているようですね。
M1 (2Hz)
M1 は最初のマシンサイクルという意味で、全ての命令で共通して最初の4ハーフクロック(2Hz)で命令コードの1バイト目を読み込んでいます。
ハーフクロック単位の動作(※推測を含む)を箇条書きにしてみます。
- 実行前同期?
- 1回目の
MREQ/RD
:メモリのPC($0000)番地
を読み込んでPC
をインクリメント - 2回目の
MREQ/RD
:DB(Data Buffer?)
が 手順2 で読み込んだ$31
(LD SP, n2n1)
に書き換わる - 3回目の
MREQ/RD
: 多分このタイミングでDB
をデコード?
RFSH (2Hz)
M1 の後、必ず4ハーフクロック(2Hz)でリフレッシュレジスタの更新を行うようです。
M1 + RFSH は必ずセットで行われるようなので、テクニカルドキュメントでは RFSH 完了まで(4Hz)を M1 としているようです。
n1読み込み(3Hz)
SP (16bit) の下位 8bit に格納される値(2バイト目のopcode = n1)をメモリから読み出して設定します。
- 実行前同期?
- 1回目の
MREQ/RD
:メモリのPC($0001)番地
を読み込んでPC
をインクリメント - 2回目の
MREQ/RD
:DB(Data Buffer?)
が 手順2 で読み込んだ$30
書き換わる - 3回目の
MREQ/RD
: メモリ読み込み後の同期? - 4回目の
MREQ/RD
: メモリ読み込み後の同期? -
SP
の下位 8bit にDB($30)
を書き込む
n2読み込み(3Hz)
SP (16bit) の上位 8bit に格納される値(3バイト目のopcode = n2)をメモリから読み出して設定します。
- 実行前同期?
- 1回目の
MREQ/RD
:メモリのPC($0002)番地
を読み込んでPC
をインクリメント - 2回目の
MREQ/RD
:DB(Data Buffer?)
が 手順2 で読み込んだ$00
書き換わる - 3回目の
MREQ/RD
: メモリ読み込み後の同期? - 4回目の
MREQ/RD
: メモリ読み込み後の同期? -
SP
の上位 8bit にDB($00)
を書き込む
まとめ
まだ理解がふわっとしている部分が多いですが、何となく細かい動作タイミングの基本が理解できたので、これでハード寄りの各種テクニカル・ドキュメント類を読む素地はできたかな...と。
当初、1Hz単位で同期できれば完璧かと思っていましたが、どうやらハーフクロック単位での同期がベストのようですね。