state save を実装できるか考えてみる

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

不要

  • DB7:0, R/W, RS2:0(=CPU A2:0), DBE($2000-$3fff の decode), INT(->CPU NMI)
  • Video (NTSC analog output)
  • EXT3:0 (state save 的には重要度低)

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 実行を全く使っていないソフトというのも理論上は存在できるのでそれらは考えなくていいのだろうか...