NES Emulator for Gameboy Color


img
自分の作ったCPUエミュレータがどこまで正確か知りたかったので
資料をななめ読みして適当に書いたPPUシミュレータと組み合わせて
動かしてみました。





■ NES Emulator for Gameboy Color

・GBNES 本体 gbnes_20090604.zip (2009.06.04版)

技術デモまたは観賞用、コードを見てニヤニヤする以外の目的での使用はお勧めできません。
音も鳴りません(NSF Playerで実証済みなので)。

プログラムROMが 16KB でキャラクターROMが8KBのものしか動きません。たぶん。
スクロールしたりメモリーコントローラを積んでいるようなものも不可です。おそらく。
初期のいくつかしか動作しないでしょう。きっと。
筆者も GBC 実機では(怖くて)テストしていません。

img

コマンドラインから上のように入力します。中身は単なるバイナリ結合のバッチです。
RGBFIX は出来上がった GBROM のヘッダやチェックサムを修正してくれるツールですが
無くてもエミュレータが警告を出すだけだと思います。


(2008.01.04追記)自前のROMヘッダ+サイズ修正ツール GBFIX.EXE を添付しました。
RGBFIX の機能縮小版ですが、RGBFIX -p -v と同じ結果を出力します。

パッドのセレクトボタン+上下左右で画面をスクロールできます。GBだと全画面見えないので。
img
VBA 等のビュワーを持つエミュレータだと、様々な情報が見られます。
色化けしているところは VRAM 操作が明らかにBlank期間中に終わっていませんね。
動かすものによりますが、全体的に2〜3FPSという所でしょうか。

改良の予定は今のところありません。
2009.05.27版での変更
 ・コントローラーをフックしました。しかしボタンは効きにくいでしょう。   これは押した瞬間を判別するためにゲーム側で ON->OFF となるタイミングを見ているからです。押しっぱなしは問題なし。  ・PPUがあまりにひどいコードだったので若干整理しました。   GBの作法通り、きちんとvblankを見るようになったので安全性は上がりましたが速度は落ちました。  ・スプライトは 40 番以降 (40〜63)を処理しません。   見えない敵に当たって死んだりします。   ほとんど、コード整理がメインの修正なので、あまり変わっていません。   PPU_2001の背景色変更を実装したかったのですが、NESエミュでテストしてもよくわからなかったのでコメントアウトしています。 2009.06.04版での変更  ・$2006 のアドレス書き込み順序がリセットされるタイミングが $2007 読み込み時になっていたのを $2002 読み込み時に修正  ・メモリインターフェースを高速化。add/sub $80 を set/res 7,r に変更。多くの命令で 4〜8clockの高速化。  ・PHP命令の動作をG-NESっぽく変更。Bフラグ関連。影響は不明。  ・NESのVBlank 間隔を GB側のn倍設定とするようにした。現状24倍。   動いたり動かなかったりは、ほとんどタイミング関連だと思います。   GBのコードもまだまだ怪しいところがあります。ネームテーブル化けは、おそらくVblank中の書き換えが間に合っていない。   GB側のスプライトのDMA転送をやめて自前でOAM領域に転送するようにすれば、ちらつきアリで64個だせるはず。


■ PPU シミュレータを作る

・いきなり反省会

CPUについては NSF Player で沢山書いたので簡単に。
エミュレータを作る上で、単に CPU が論理的に正しい答えを出すだけではダメだな、というのを
痛切に感じる一連の作業だったのですが、もうすこし応用を利かせられないかと思って PPU を
書いてみることにしました。nestech.txtを、この段階に至ってようやく斜め読み。

APU を弄るときもそうなのですが、メモリ(I/O含む)とのやりとりを CPU 処理のどこに挟むか
というのが難しい問題でした。同じメモリでも『READ』と『WRITE』の意味が違っていて
書きこんだ値と読み出した値が違う場合もあります。また、一度読み出し動作をすると
その後値がリセットされてしまう特殊なケースもあります。

そういうのはメモリハンドラを通してそこで一括管理すればいいのでは、という気もしますが
特殊なパターンに備えて全てのケースで条件分岐していてはパフォーマンスにも影響します。
NES の場合でも、ゼロページメモリ($0000-$00FF)にアクセスする事が分かり切っている命令で
APU I/O テスト($4000-$4018かどうか)を行うのは無駄です。

というわけで、これまでは命令毎にチェックを挟んでいたのですが、構造的に今ひとつスマートでなく
不満がありました。そして今、そこに PPU チェックが加わることに。

以下、おおざっぱに違いをまとめてみました。


NESGB
解像度256*240160*144
タイルマップ面数
タイルマップ解像度32*3032*32
キャラクター解像度8*8ドット8*8ドット
キャラパターン数256*2384*2(内256*2)
1画面あたりの
スプライト数
6440
1スキャンラインあたりの
スプライト数制限
10
タイルパターン色数背景色含め4色4色
スプライト色数透明色除く3色透明色除く3色
総色数64色(重複あり)16384色
パレット数
色指定単位2*2タイル毎1タイル毎

他にもパターンデータが NES は ROM なのに対し、GB は RAM であるとか、
H-Blank 割り込みの有無とか細かいところが色々違います。

色に関しては GB の方が自由度が高いですが、解像度とスプライト数では
やはり NES に分があります。もっとも NES も拡張機能で随分変わりますが
それについては今回は考えないことにします。

最大の違いは PPU が独立してメモリを積んでいて、それが CPU から直接見えない
という点で、PPUレジスタを通したメモリの読み書きが大量発生することが予想されます。

が、逆に考えると、読み書きは1つか2つのレジスタに集中していて、他は
設定用に使われるだけで、そう頻繁なアクセスもないようです。
その設定自体も、割と GB と似ていたりして、躓くような所も少なかったのでした。

というわけで、一通りレジスタを作って、CPU エミュレータを作った時に
一緒に書いておいた NMI ルーチンを適当に駆動すると、さっくり動いてくれました。
PRG-ROMを 16KB に限定して速度を稼いだ CPU コアは、軽快に動作します。

問題はスプライトで、GB と同じく DMA を使ってスプライト専用メモリ 256 byte に
V-Blank毎に転送されるのですが、微妙に GB と仕様が違う・・・。
ここは力業で pop 転送を使ってやりくりしていますが、これさえなければもう少し
速度が稼げていると思います。

スプライト数が64<->40というのもネックで、V-Blank毎に前半40と後半40というのも
出来なくはなさそうですが、表示領域との兼ね合いで、馬鹿正直に64個分転送するのは
無駄かもしれません。一応、GB の 160*144 領域外のスプライトをスキップするコードも
書いてみたのですが、ゴミが残ったり(その判定に時間がかかる)して面倒なので
あまり改善はできませんでした。



■ NES

エミュレータは難しいですが、面白いですね。
ピタゴラスイッチ(あるいはインクレディブルマシーン)のように、適当なところに
個別の機能を持った部品を置いておもむろにスタートさせると、プログラムが意志を
持ったかのように動き出すところに独特の快感があります。

NES も散々弄られた(現在進行形かも)ハードだけあって奥が深いです。
ドラクエなども Wizardry や Ultima の影響など言われ尽くした感がありますが、
あのゲームデザインもハードを理解した上でのものなのだと、改めて感じました。

CPUにやっつけ仕事のPPUをくっつけて、いいかげんなNMIを駆動しただけでも
そこそこ動くものだなあというのが今回の感想。GBにはお疲れ様、と。


▲ TOP