描画中にレジスタを書き換えて pixel 単位で演出を加えるテクニック

以前 twitter でも書いたけど、消してしまったので再掲。

ファイナルファンタジー初代でカオスを倒した後に灰色の斜めの線が入る演出がある。実機でプレイした人の動画。
http://youtu.be/RM_XGzzI3QQ?t=6m40s

大半のエミュレータではこれが表示されない。Nestopia はどういう管理をしているかわからないがちゃんと表示される。(牧村さん調べてくれてありがとう)
http://youtu.be/O5ZDBj22aEM?t=37m30s

処理の推測

このシーンはゲームをかなり進めないと見れない上にFF1はかなり癖があるので自分はここまでやれないので、この場面にするのが面倒でいつも後回しになっている。予想では下記の処理をしていると思われる。

  • CPU からは描画する座標は得られないので、CPU の命令実行数かなにかで精度の低いものの座標を推測している
    • MMC3 のような Y 座標カウント機能や、RAMアダプタのようなφ2カウンタ機能がないので、Y座標は0番スプライト検出か vblank 割り込みから命令実行だけでカウントしている
    • こういう実装をやるのはかなり面倒くさいと思われる。
  • 描画表示期間の指定時間だけ PPU のモノクロレジスタを有効にしている
  • 効果音も鳴っているので画面下半分あたりで音関係のレジスタを更新しているかもしれない

描画レジスタの解説

実のところファミコンのPPUはあまり詳しくないので間違いもあると思うが解説する。この手のビデオレンダラは描画中に定期的にレジスタを参照して映像を作る。1scanline 毎に1度スクロールの X を参照するとか、8scanline 毎に1度スクロールの Y を参照するらしい。

ハードウェアの場合は各機能が並列(同時)に動いているが、ソフトウェアエミュレータの場合は各機能を指定した時間で切り換えて順番に動かして同期を取る。理想としては 1 pixel 単位で同期を取ることが好ましいが、ソフトでは切り換えに処理がいるので前述のスクロールレジスタの都合も考えて 1 scanline 単位で同期取ると切り換え処理が 255 回(非表示期間も含めたら300いくかも)省くことが出来る。

今回のモノクロレジスタというのはどうもパレットRAMのアドレス7bitのうち、下位4bitを0に固定することで実現しているようだ。パレットRAMとかカラーROMという処理は描画では最終段階の処理になる*1ので、表示期間中は常に参照される。このため指定した間だけモノクロにしてオーラのような演出をしているようだ。

そういうことなので、line単位で描画レジスタを参照しているソフトエミュレータは、1本まるまるモノクロ(本来左端がモノクロで開始している)だったり、なんにもでなかったり(本来左端がカラーで開始)、映像が崩れたり(同期がとれてない)すると思われる。

やはり、CPU だけで経過時間(=描画座標)を得るというのはかなり変態的なことだと思うので Nasir は超人といわれる所以かもしれない。

募集

他のゲームでもそういう場面で再現されてないとかあったら教えてください。

*1:レイヤが統合された状態に反映される。スプライトだけモノクロということはできない。