NES関連メモ

重箱の隅的なメモ。たぶん役に立つことはありません^^;

ハードリセット/ソフトリセットの判別

FC本体のリセットスイッチには「リセットスイッチを押すと、それまで記憶され ていた得点が消去されます」と書かれていますが、実際にはリセットスイッチを押し てもハイスコアなどが維持されるゲームも結構あります。これを実現するには、プロ グラム側でハードリセット/ソフトリセットを判別する必要があります。

FC/NESの場合、ハードリセット/ソフトリセットの割り込みアドレスは共通であり、 電源投入(ハードリセット)直後のハードウェアの状態についても特に仕様が定まって いないようなので、100%確実に判別できる方法は存在しないと思われます。いくつか のゲームを調べてみたところ、確率的アプローチをとるのが定石のようです。

例えば、SMB1では

以上の条件を全て満たしていればゲーム初期化処理が既に行われたとみなし、 RESET割り込みはソフトリセットとして扱われます。この場合、電源投入直後のメモ リ状態がランダムで、どの値も等確率で現れると仮定すれば、ハードリセットがソフ トリセットと誤認される確率は

(10/256)^5 * 1/256 = 0.000000000355...

となり、実際上は無視できます。他のゲームもいくつか調べましたが、基本的に 同様の手法で判別を行っています。

FCEUの場合、ムービーの再現性を保証するためにメモリの初期状態は常に一定に なりますが、実機ではおそらくランダムと思われるので、電源投入直後から異常なハ イスコアなどが記録されているというケースも理論上はありうると思います。ただ、 ランダムといってもどの値も等確率で現れるわけではないかも(NesDevWikiの Power-up state of PPU の項目を見ると、電源投入直後のネームテーブル領域は "mostly $FF" と なっているので)。

追記: 実機でも電源投入直後のメモリ状態は一定か、もしくはそれに近いと思わ れます(例えば、FF2のエンカウント処理は起動時に初期化されないアドレスの値に依 存しているが、実機でも起動直後のエンカウントには一定の法則がある)。ただし、 FC互換機などの場合は仮に一定であっても初期値は異なるかもしれません。このあた りを厳密に調べるには、メモリ状態を表示するプログラムを書いて実機で動かしてみ るしかなさそうです(自分は今のところそういうことができる環境を持っていません が)。

RMW命令のRMWW動作

Ki氏の FC ハードウェア永久保存計画 によると、6502のRMW命令(Read-Modify-Write命令。INC など)はRMWW動作 (Read-Modify-Write-Write動作。結果の値を書き込む前に変更前の値が書き込まれる) をするようです。

この動作が問題になるのはI/Oレジスタに対してRMW命令を実行する場合ですが、 今のところそのようなコードは発見していません。いくつかのゲーム(Wizardryなど) でMMC1を INC 命令を使ってリセットする(0x80 が入っているROMアドレスを INC す る)例は確認されていますが、これは単に2回リセットを行うだけと思われます。

FCEUでもRMW命令はRMWW動作をするように実装されていますが、なぜかzeropage, zeropage indexedアドレッシングモードではRMWW動作になっていません。ただ、 FC/NESの場合、どのみちゼロページにI/Oレジスタが置かれることはないはずなので、 単に速度向上のために省略されているだけかも。

VBLANKフラグに関する競合状態

NesDevWikiのNMIの項 目を見ると、$2002 の bit7 (VBLANKフラグ)には競合状態の問題があるため、初期化 処理以外ではNMI割り込みを使用するのが確実、とされています。具体的には:

  wait_vblank:
    lda $2002
    bpl wait_vblank
    rts

以上のようなコードで、VBLANKと $2002 の読み込みが同じタイミングで発生した 場合、$2002 の bit7 は0を返し、それ以降も0にセットされたままとなるため、フレー ム落ちが発生しうる、ということのようです。

ソースを見る限りFCEUではこの競合状態は再現されていないようです(おそらく FCEUXも同様)。ただ、どのみちほとんどのゲームでは初期化処理を終えた後はNMIを 利用するはずなので、あまり問題にはならないと思われます。


Back