8bit CPU(GB) で 8bit CPU(NES) をエミュレートしています。
インタープリター方式で、フラグ変化などは GB のフラグを流用したり新たに計算したりしています。
テーブルを使うのも一つの手なのですが、参照の手間を考えるとそこまで有利でも無い気がします。
NSF のファイルは 4KB 単位のメモリブロックになっており、GB ではこれを 16KB 単位 $4000-$7FFF のバンクメモリに順次格納しています。
NSF 仕様ではこのメモリブロックは $8000-$FFFF の 32KB メモリ範囲に 4KB 単位で自由に並べ替えて配置できます(演奏中も変更可)。
なのでエミュレートする際は、例えば NES のアドレス $8000 がどのバンクのどのアドレスを指すのかを逐一調べる必要があり、
GB の場合は該当アドレスが格納されているバンクに適切に切り替えた後、アドレスを変換して読み書きする処理をしています。
こんなに極端な配置ではない…。
NES のメモリアクセスの内、RAM は $0000-$07FF の 2KB しか(基本的には)ないのですが、
GB 側で 2KB だけを用意しておけばよいのかというとそんなことはなく、読み書きできるメモリマップド I/O レジスタも用意しなければいけません。
具体的には $2000-$2007 の PPU、$4000-$401F の APU、$5FF8-$5FFF のバンク切り替えレジスタです。
ROM/RAM 含め、64KB メモリ空間のアクセスの内、どのアドレスにアクセスしたらどこにリダイレクトさせるかは
いちいち条件分岐していては遅すぎるのでテーブル化して参照しています。
NSF に PPU は必要ないので省略していますが、NES エミュレータの方では $B000-$BFFF に割り当てています。
$6000-$7FFF の FDS RAM は無理をすれば割り当てられるのですが、連続した 8KB を確保するための犠牲が大きいので断念。
APU(音源レジスタ)のように書き込んだら何か特定の動作をするようなものは
一旦書き込んだ後、その値を解釈して音源シミュレータにて近似した動作になるよう変換した上で GB 音源を操作しています。
過去のバージョンではフレームシーケンサは vblank 同期(つまり 60Hz)で一度に 4 回分の動作をすることで 240Hz 相当にしていました。
今バージョンでは 240Hz に近いタイマー割込みで駆動し、エンベロープもそれに同期した音量変化をするようにしています。