state save はゲーム機エミュレータによく実装されているやつで、その瞬間を再開できるのでインチキセーブ機能と呼ぶこともある。
インチキセーブはゲーム機に関わる変数つまり、レジスタとRAMを全部保存してしまう必要がある。RAM のほうは外部インタフェースがしっかり決まっているのでいいとして、レジスタの場合はハード的にはどうやって取得するのが難しい。
結局のところ CPU のバスを監視して、すべての write (written?) log を取るのが無難が気がする。
CPU side
ピンの名前を羅列していく。
監視が必要 (25)
- AD15:0 (address bus)
- DB7:0 (data bus)
- PHI2
- IRQ, NMI
- R/W
- RES
監視不要
- XIN, XOUT (発信子信号は analog だが、別途 TTL level の 21.47MHz はマスターとして必要)
- AUX A, AUX B(audio)
- RDP1:0 ($4016, $4017 を decode しただけ)
- P2:0 (output port で input device の serial clock, 重要度低)
PPU side
監視が必要 (18)
- CLK
- WR, RD
- A13:8, AB7:0, ALE
不要
RAM
本体内蔵の RAM 2つはバスマスタを停止させて dump するだけでいいはず。
EWROM のような cartridge banked RAM は... 面倒なので後回し。
write レジスタ
RAM のように 1 address : 1 data の場合は簡単。
address multiplexed, serial device, twice write など1addressへの複数の書き込みを持って1つのデータシーケンスとする場合は面倒。このようなデバイスは例としてbit7=1とするとか初回の書き込みを指定できる(ことが大半な)ので、それをベースに専用の logger が必要な気がする。
PPU side には write register がないはずなので CPU side だけの考えとする。
CPU レジスタと取り込みのタイミング
これはちょっと難しい。任意の CPU タイミングで取り込みをかけると、load時の同期処理が複雑になるので特定のタイミングでとれるようにしたい。普通に考えて NMI 実行, $FFFA への read をきっかけにする。このときに本来 ROM へアクセスするデータバスへのアクセスを止めて、バスマスタが偽の命令を発行して CPU レジスタ (PC,SP,A,X,Y)を吐かせる。
$FFFA の read の前後(どっちか実機で調べる必要あり)で NMI 復帰 address が push されるので PC と SP はわかる。その後 sta; stx; styを実行。 その後バス停止で RAM 取り込みとなる。reset にすればバス停止できるんだけど、取り込み後に元のプログラムの動作がおかしくなりそう。reset にするか phi2 強制 HIGH 固定だろうか。
NMI 実行は CPU の設定で決められるのであって、特定のタイミングでは取り込めないし、NMI 実行を全く使っていないソフトというのも理論上は存在できるのでそれらは考えなくていいのだろうか...