NSF Player for Gameboy Color


img
ある日 NES の音源仕様を眺めながらふと GB と似ているな、と思いました。
後になって考えてみるとこれは大きな誤りなのですが、ともかくも
NES を GB 上でエミュレートするという大それた試みを実現すべく、
6502CPU エミュレータを書いてみることにしたのでした。

8bit CPU を 同じ 8bit CPU で現実的な速度でエミュレートする――
ある意味 GB を最も酷使するプログラムかもしれません。





■ NSF Player for Gameboy Color

本プロジェクトの、成果物をひとまず置いておきます。
開発の顛末や問題点などは以下に長々と続きますが、とりあえず試してみたいという方は
使用方法を参考に ROM イメージを作成してください。各種 GBC エミュレータでも動きます。
ポピュラーなエミュレータであれば、まず大丈夫でしょう。
なお、GameBoy"Color"専用です。8MHzモードを使用するためです。

(2007.12.03追記) 仕切り直しで新バージョン NSF Player V2 を作りましたので、そちらの方をお試し下さい。
          A new version NSF Player V2 released.
 -> NSF Player V2

・GBNSF本体 gbnsf.zip (2007.5.10版)
・GBNSF本体 gbnsf32.zip (2007.5.10版) For lesser cart RAM, a bit slower.
 .NET Framework 2.0 以上が必要です。Windows Update で入手できます。

DPCMを使う NSF や拡張音源は正常に鳴りません。
演奏中にバンク切り替えをする NSF も動作速度を極端に低下させます。

<更新>
2007.04.01 - 初版。
2007.04.10 - バンクを使わない NSF に対応。DEY 命令のジャンプテーブルが間違っていたのを修正。
2007.05.10 - Added 32Kbyte-Cart-RAM version.
             Please use new version, if old program runs incorrectly with real gbc(+flash cart).
           + Implemented frame sequencer mode 0. buggy, though.
           + Even more accurate envelope, sweep, and noise frequency.
           + Fixed checksum calculation.
           + Added player screen. How about it?

           + 32KByte の RAMカートリッジ対応版をリリースしました。実機+フラッシュカートで使う場合
             従来は 128KByte RAM 前提でしたが、より少ない RAMメモリで動作するようになりました。
             ただし、実行速度はノーマル版に比べて若干遅くなります。
           + フレームシーケンサを実装しました。mode 0 のみです。まだ多くのバグがあります。
           + エンベロープとスイープとノイズパラメータを見直しました。ほとんど耳調整です。
           + コンバータで ROM作成時のチェックサム計算のバイトオーダーが上下逆だったのを修正。
           + 黒画面では寂しいので、プレイヤー画面をつけました。
2007.05.11 - Fixed updating sound frequency(lower register). 

           + 周波数下位に書き込まれた時にも音を更新するようにしました。明らかに変なNSFが多かったので。

img

「GbNsf.exe」は変換プログラム、「nsf80.bin」は GB上で動く NSFプレイヤー本体です。
変換プログラムは、任意の NSF ファイルとプレイヤー本体を結合し、チェックサムなどを調整します。

img

上のように、ドラッグ&ドロップすると、正常に変換できると、拡張子「.gbc」のファイルが作られます。
この場合だと「sample.gbc」になります。

img

あるいはコマンドプロンプトから上のように実行すると、成功すれば「success.」のメッセージが出て
拡張子「.gbc」のファイルができあがります。

img

画面ができました。曲名しか表示しませんが、内部的には文字列情報はタイル番号(-80h)に変換済みです。
もっとナイスな画面やフォントが作れると良いのですが・・・。
一応推奨エミュレータは「BGB」ということにしておきます。


■ NSFについて

NSF(NES Sound Format)です。詳しくはこちら(MCK Wiki)。
このページを訪れるような方なら、NSF についてはどういうものかご存じとは思いますが
今一度エミュレータという側面から見た NSF について解説してみたいと思います。

img

上の図はかなりおおざっぱですが、NES の CPU の処理内容を示した物です。
一般的なものなので、以下の説明に当てはまらない例外もあります。

一番左は実際のゲーム内における処理です。
ゲーム処理では、1/60秒毎にタイミングを取ってコントローラーの状態を読み取ったり
スプライトを動かしたり点数を加算したりします。ここで VRAM を書き換えておくと、CPU とは独立した
画像処理プロセッサ PPU が VRAM の内容を参照しながら実際に画面へ反映させるわけです。
1/60秒でこの流れがスムーズに処理できれば滑らかな動きに、処理が重くて間に合わないと
ちらついたりフレーム落ちしたりするのは良く知られていますね。

そして次は音楽の処理ですが、これも CPU が処理しなければなりません。なぜなら音源を駆動する
別の処理チップを NES は持っていないからです。CD や DVD がついていれば「1曲目の音楽を演奏しろ」
とコマンドを CDプレイヤーに送れば勝手に音楽が流れるのですが(毎フレーム処理する必要もない)
そういうわけにもいかないので、演奏用のドライバと曲データを メモリ空間に置いておき、これを
ゲーム処理と同じく、タイミングを取って実行させるわけです。

ということで、もしゲームの音楽の部分だけ聞きたかったら、ゲーム処理部分はいらないことがわかります。
中央の図はまさに NSF の処理内容で、1/60秒のタイミングで演奏処理を呼び出してやれば問題なく演奏が
実行されます。曲が切り替わったり効果音が鳴ったりといった、ゲーム内容と密接な部分については、
演奏プログラム(音源ドライバ)にある値を事前に渡してやることで、適切に処理されるように(大体は)作られています

さて、NSFの演奏を行うのはほとんどPC上だと思いますが、その正体が1/60秒毎にNESのCPUをエミュレートして、
NSFファイルに含まれる演奏プログラムを実機同様に実行するものだということが分かったと思います。
今時のPCだと1/60秒の間隔は恐ろしく長い時間なので、余裕を持ってエミュレーションを実行できます。
その結果、実機のように専用の音源ハードを持つこともなく波形を合成して出力することさえ可能なのです。

ところが GB だとそうはいきません。演奏処理プログラムを、非力なGBのCPUで解釈して実行するだけでも
何十倍もの時間がかかりますし、演奏プログラムによって出力された1フレーム分の音源データを、
ハードウェア的な仕様が異なる GB の音源に合わせて加工してやる仕事も大きな負担になります。
とても「次の処理タイミング待ち」を余裕を持って取っていられないのです。
もし、処理が次の 1/60秒が来る前に終わらなければ・・・・・・演奏タイミングがずれてフレーム落ちとなり、
見苦しい(聞き苦しい)音楽の演奏をすることになってしまいます。


というわけで、エミュレーションをいかに高速にできるか(1/60秒に間に合うか)が勝負になるのですが、
これはエミュレートする演奏プログラムによっても変わってきます。なぜなら、演奏プログラムが長く複雑だと
それを解釈して実行する GB の負荷は比例的に増えるからです。
よって、「正常に演奏できないものもあるだろう」と気楽に構えつつ以下、CPUエミュレータ制作にとりかかったのでした。


■ CPUエミュレータについて

そもそも GB で NES をエミュレートするという試みは、このプロジェクトが初めてではなく、
(既にサイトは消えていますが)PlayOffline.com というサイトで発表されていた、
"FC EMULATOR for GAMEBOY" がありました。こちらは NES の機能を画像処理まで含めて
フルにエミュレートしていたようで、動作速度は非常に重くなっていたそうです。

こういった先人の試みがあったので、CPUと音源だけならなんとかなるかも、という
ある程度の見通しは立てることが出来ました。ただ、ソースが公開されている訳でもなく、
解析も苦手、筆者自身がエミュレータを書くのが初めてということで、試行錯誤しつつ
フルスクラッチで書いてみることにしました。
下の表は NES と GB のおおまかなスペックの違いです。


NES GB
CPU M6502カスタム Z80カスタム
bit数 8bit 8bit
動作クロック 1.7897725MHz 8.188608MHz
(倍速モード時)
レジスタ A X Y
ステータスレジスタ P
スタックポインタ S
プログラムカウンタ PC
A B C D E H L
フラグレジスタ F
スタックポインタ SP
プログラムカウンタ PC
1命令の最短実行クロック/
最長実行クロック
2/7 4/24
メモリ空間 64KB
バンク切り替えあり
64KB
バンク切り替えあり

一見 GB の方が恵まれているように見えますが、6502といえば AppleII から PCエンジンまで幅広く使われ、
強力なアドレッシングモードを備え、命令群が簡素な代わりに高速に実行できる仕組みを備えた RISC の
原型のような CPU で、その流れは GBA や DS で使われている ARM CPU に連なるといわれている(らしい)
由緒正しくも強力なチップなのです。Wikipedia

ここから少し技術的に難しい話になります。

NES の汎用レジスタは 3つだけですが、GB のレジスタも余裕があるとはとても言えないので
メモリ空間に格納することにしました。ただし、通常より高速にアクセスできる $FF80-FFFEの
通称 HI-RAM アドレスに配置して少しでも速度を稼ぎます。

通常のメモリアクセス:LD  A,[$C000]16clock
HI-RAMメモリアクセス:LDH  A,[$FF80]12clock

NES のアドレス空間は GB と同じ 64KB で、音源に対する入出力である I/O もメモリ空間に
割り当てられています。NES に内蔵されている RAM メモリは 2KB で、6502 特有のゼロページと
スタックにあたる $0000-01FF を含む $0000-07FF に配置されます。
わずか 2KB なので GB の 内蔵RAM $C000-DFFFの 8KB に割り当てても良かったのですが
アクセスされるメモリ(とI/O)の種類で場合分けするのが負担になりそうな気がしたので
以下の ROM と同じような仕組みにしました。

NES のカートリッジ(ROM)は $8000-$FFFF の空間に割り当てられ、4KB ずつのバンク切り替えが
可能な仕組み(NSFの仕様)になっています。すなわち $8000-8FFF が第1バンク、$9000-9FFF が第2、
以下 $F000-$FFFF の第8バンクまであり、NSF ファイルを 4KB ずつ区切ったx番目を第nバンクに
割り当てるかを決めることができます。1〜8バンク全部の中身が NSF1番目だとすると、同じ 4KB の内容が
8回繰り返されることになりますが、そういうのもアリです。
このあたりのことは nsfspec.txt という資料(後述参考資料)に書いてあります。

これを GB のメモリ空間に以下のように割り当てました。

img

前述の通り、NES の RAM は 2KB なのですが、$0000-7FFF まできっちり 32KBの空間を割り当てています。
NES の ROMについては NSF本体をアクセスせずに、一旦 仮想 ROM にバンク情報を元にコピーしてから
アクセスするようにしています。

これで、NES のアドレス $0000-FFFF のどの空間をアクセスするときも、一様に GB の $A000-$AFFF を
読み書きすれば良いことになります。GB の RAM バンク番号は NES の $8xxx なら 8、$Fxxx なら 15 と、
簡単に算出できます。

欠点といえば、ROM の内容を RAM にコピーするので、頻繁にバンク切り替えをするような曲あるいは
音源ドライバだと、実機とは比較にならないペナルティ(4KBを転送する)が必要になることです。
そんなドライバは、おそらく DPCM を使うような NSF だと思いますので、最初から動作対象外と考え
このようにしました。

GB のレジスタの中で DE ペアレジスタだけは、この $A000-AFFF の仮想メモリ空間のアドレスを指すように
特別な役割を割り当ててています。NES のプログラムカウンタから毎命令変換していたのでは
時間がかかるためです。このあたりは、まだ検証できていない疑問点があるので、この文書の下の方に
問題点として挙げておきました。

バンク切り替えが発生するかもしれないので、ストア命令のアドレスをトラップしてテストしていますが
この部分が負荷の一因になっています。そもそもバンク切り替えが $5FF8-5FFFへの書き込みをトリガとしている
という筆者の理解が正しいのかどうか疑問なので、もしかしたら全くの無駄なのかも。

エミュレータとしては、NES のオペコードを 1バイト読み、テーブルにしたがって各命令処理へジャンプ、
2バイト目・3バイト目のオペランドがあれば読み込み、実行するという手順を取ります。
ステータスレジスタの処理が NES と GB の間で大きく違うのが難点で、実行時間を取られるポイントですが
エミュレータを書くという面からしてみると、ほとんどコピペでした。・・・マクロ化が課題です。

あとは 6502 の複雑なアドレッシングですが、これも大半はコピペです。
前述の通りメモリアクセスを単純化したため、それほど難しいこともなかったように思います。

気になる負荷ですが、先に書いたように NESの各音源ドライバによって大きく変わってくると思います。
計測方法として、NSFエミュレーションが終わった時点でのスキャンラインが示す値が参考になります。
GB は Y座標 0-143 が実画面で、Y=144 になった時に Vblank 割り込みが発生します。
そして Y=153 までが Vblank 期間で、再度 Y=0 に戻ります。NSF 処理は 1/60秒毎に発生する割り込みを
トリガとしているので、終了した時点での Y の値が(フレームスキップしていなければ)処理の負荷の
おおまかな目安になるわけです。
筆者がテスト用に用いていた NSF では、CPU エミュレーションが終了した時点での最大負荷が Y=137 でした。
あと 6Line で次の割り込みが来てしまいますので、その間に APU シミュレーションまでを終わらせなければ
ならないということで、かなり厳しい状況です。NSF によってはフレームスキップ発生が頻繁に起こるかもしれません。

NSFをGBのROMに格納するにあたって、NSFのヘッダ部($0000-007F)だけは GBのアドレス空間の$3F80-3FFFに
配置されます。後述のAPUシミュレータでテーブルを1バンク確保するので、都合上以下のような配置になります。
GBのROMをバイナリエディタで見れば分かりますが、「NSFヘッダ」「テーブル」「NSF本体」の順に並ぶ形です。

img

デバッグの為に 6502 のプログラムを一通り追ってみたりしたのですが、Z80と違う部分が多く、
興味深いものがありました。自分で書こうと思うと、おそらく Z80からカルチャーを切り替えないと駄目でしょうね。
NES は熱烈な愛好家が多く、資料が充実しているのも羨ましい限りです。


■ 32KB RAM版追加へ向けて

このプロジェクトを公開してから、実機で動かないというメールを貰いました。
初版では、上でも説明したとおり、128KBのRAMを前提として動作するよう設計していたため、
多く普及している 32KB RAM のフラッシュカートリッジでは動作しませんでした。

128KB の RAM を積んでいるのは、大体 8MB ROM/128KB RAM のフラッシュカートリッジで
高価で入手も難しいのに対し、32KB のカートリッジは 2MB ROM/32KB RAM で、一般的な用途としては
十分なこともあり、このタイプのフラッシュカートは多く出回っていると思います。
国内でも何タイプか市販されていたはずです。

筆者としては実験的なプロジェクトのつもりで、エミュレータで聴ければいいか、と思っていたのですが
(嬉しいことに)そうでも無いようなので、32KB RAM でも動作するよう改良を加えることにしました。

メモリ周りを以下の通り、大幅に変更することになります。
img

ノーマル版では $A000-$BFFF のカートリッジ側RAM の内、$A000-$AFFF しか使っていませんでしたが
32KB 版ではカートリッジ側 RAMが 32KB しか無いので、$A000-$BFFF をフルに使っています。
このカートリッジ側 RAM で NES の ROM 領域 32KB をカバーし、RAM 領域は GBC 特有の内蔵 RAM メモリを使います。

内蔵 RAM は GBC の $C000-$DFFFにあるのですが、#0 は $C000-$CFFF 固定で #1-#7 までがバンク切り替え出来ます。
この $D000-$DFFF を NES の RAM 領域に割り当てます。
4KBx7=28KB ということで、$7000-$7FFF がカバーできませんが、そこにアクセスするのは FDS くらいなので
実用上問題にならないと思います。
もしアクセスしてしまうと、#8→#0(=強制的に#1)を読み書きしてしまう = NESのゼロページとスタックが飛んで暴走することでしょう。

これらの変更を加えたために、ROM領域のアクセスとRAM領域のアクセスで場合分けが必要になり、
さらにバンク切り替え方法も違うため、エミュレータ部分にも手を加えなければなりません。
この変更により、RAM 上で動くようなプログラムは実行できなくなりました。
現在のプログラムカウンタ(PC)の所属するバンクを記憶しておくワークが ROM領域にしか対応出来ないためです。
また、毎アクセス毎に条件分岐があるので、速度低下も避けられません。

32KBのメモリマップに合わせて、ノーマル版のメモリコントローラ MBC5+RAM から MBC1+RAM へ変更しました。
多くのフラッシュカートに幅広く対応させる為の変更ですが、ひょっとするとこのプロジェクトで使用している
GBC の 8MHz 高速モードに対応していないフラッシュカートがあるかもしれません。
MBC5 は高速モードを保証するための規格と聞いたような記憶があるので逆に MBC1 カートで使えない可能性も・・・。

以上の改良で 32KB版が動作するようになりましたが、筆者は 32KBのフラッシュカートを持っていないので
実機でテストすることが出来ません。お持ちの方で、従来版が動かなかった方は是非試してみてください。


■ 音源シミュレータについて

音源へのアクセスは、メモリ空間の $4000-4017 に割り当てられた音源レジスタを通して行います。
CPU から見ると、普通のメモリアクセスなので演奏プログラム実行中は CPU のするままに任せておき、
CPU の処理が終わった段階で $4000-4017 を読み出して GB の音源に合うように数値を加工して処理します。
APU シミュレータという形で別のプログラムとして実行しますが、完全に独立というわけではなく、
CPU エミュレータの仕様に影響を受けます(DEレジスタ使用禁止など)。

シミュレータは、ほとんどが NES から GB へのパラメータのテーブル置換で、細かい条件分岐などを省くことで
高速化を図っています。このため GBのROMバンクを1つ割り当てて各種テーブルを格納しています。

GB の音源仕様については、下記リンクの GB の項目が詳しいです。
・http://vsync.org/ すずめ愛好会(残念ながら閉鎖されたようです。)

NES の第3チャンネルは三角波固定ですが、GB の第3チャンネルは任意に波形を設定できる
波形メモリ音源(4bit-32Step)なので、他の波形に変えて遊ぶことも出来ます。
設定は"gb_routine.z80"の最後のあたりで行っています。

今のところ、実装状況は以下の通りです。

音程周波数(Ch.1-3) テーブルで変換。比較的まとも。
ノイズ周波数(Ch.4) 耳調整。違和感無いレベルと思う。
矩形波デューティ比(Ch.1/2) GB と互換性あり。
スイープ(Ch.1/2) GB の Ch.2 にはスイープが無いので Ch.1のみ。
120Hz分解能と128Hz分解能の誤差あり。
エンベロープ(Ch.1/2/4) エンベロープループはソフトウェアシミュレーション。
パラメータは耳調整。
長さカウンタ(Ch.1/2/3/4) 実装。120Hzの分解能を60Hz分解能で調整。
若干の誤差あり。
リニアカウンタ(Ch.3) 実装。仕様が今ひとつ不明なため不完全。
DPCM(Ch.5)× GB には存在しないので無視。
フレームシーケンサ mode0 のみ不完全ながら実装。
mode1は無理そう。

まともに実装できているのが音程と音色(デューティ比)のみというのが情けないですね。
「百聞は一見にしかず」で(逆?)、資料ばかり読んでいてもさっぱりわかりません。
色々実際に聞いてみて試行錯誤するのが良いのでしょうけれど・・・。


■ 問題点・疑問点
実際に試してみればいいことばかりなのですが、メモ代わりに。

・[CPU] 6502の命令はバンクをまたぐことがある?

 上の CPU の項目でも少し書きましたが、DEレジスタには、NESのPC(から変換した値)を
 入れてあり、命令長によってインクリメントを行います。
 このとき、たとえば以下のような状態が発生するかもしれないので、毎回チェックを行っています。

img

 $8FFFではDE=$AFFFですが、$9000ではGBのRAMバンクを変更した上でDE=$A000をセットしなければなりません。
 バンクをまたがない(≒ひとつのプログラムが4KBを超えることは無い)のであれば、チェックを省略でき、
 高速化に繋がります。

→ バンクをまたぐことがあるそうです。(Thanks. Kiさん)

・[CPU] ゼロページアクセスを伴う命令のページ境界は?

 具体的には間接アドレッシング indx,indy を使う命令なのですが、ゼロページから
 アドレスの下位・上位を読み込む際に下位が$00FFの時、上位は$0100から読むのか、
 あるいは$0000から読むのか、ということです。

img

 現状では前者(図では上)にしてあります。

→ 確認:現状の実装で問題ないようです。
ldx  #$7F       ldy  #$01
lda  [$80,x]    lda  [$FF],y
左の場合、$00FFの内容を下位、$0100の内容を上位とするアドレスの内容が a に入る。
$80+x=$FF の計算時にはキャリーは無視。8bit加算。
右の場合も$00FFの内容を下位、$0100の内容を上位とするアドレスに y を加算したアドレスの内容が a に入る。
y を加算するときにはキャリー有効。桁上がりで上位がインクリメントされる。16bit加算。
JMP_ind だけが持つアドレッシングモードはバグ有りで$0?FFの次が$0?00になる。これも実装済み。
(thanks. Kiさん)


・[CPU] NES はミラー領域に敢えてアクセスすることがある?

 NES の RAM は $0000-07FF の 2KB で、以降ミラーリングされて $0800-0FFF,$1000-$17FF・・・と
 同じ内容が続くらしいのですが、もしこの領域にアクセスされると非常にまずいことになります。
 $0000-7FFF の 32KB空間をフルに実装してしまっているので、ミラー領域であろうが独立して
 自由に読み書きできてしまいます。

 対策としてはやはりRAM/ROMでアクセスの場合分けをすることですが、せっかくアドレッシングを
 単純化した意味が無いような・・・。
 ついでに I/O もミラーリングされているようですね。

・[CPU] NES は $FFFFを超えると$C000に戻る?

 $8000 に戻るという話も。というか実際にこういうことはあるんでしょうか。
 $FFFA-FFFF には割り込みベクタがあるので、敢えてそういうプログラムを書かない限りありえないような。
 本エミュレータでは素直に $0000 に戻ります。

・[CPU] PHP → PLA での Bフラグの挙動

 上の疑問も NSF を再生するだけなら全く関係ないとは思いますが、純粋な実装上の問題点として。
 "PHPした後にPLAで読み出すとBフラグが常に1になる"という動作は本エミュレータでは未実装です。
 これ、単純にスタックにプッシュするときに1にしておけばいいだけなのでしょうか。
 「読み出すと」と書いてあったりするので実際にプッシュした値と PLA で読み出す値が違ったりするのかと。

→ 単純にプッシュ時に 1 にしておけばよいそうです。実装完了。(Thanks. Kiさん)

・[CPU] Iフラグと BRK 命令の関係

 Iフラグがセットされていると BRK 命令は無視されるという情報と、Iフラグに関わらず実行されるという
 情報がありました。一応、後者で実装してあります。

・[APU] 値の読み込みが正常に出来ない。

 GB がそうなのですが、音源はCPUからの書き込みとは独立してレジスタを逐次更新しながら発音するのでしょうか。
 だとすると、たとえば音源ドライバがレジスタを監視して条件分岐するような仕組みだと不具合が生じます。
 APUにもエミュレーションの仕組みを取り入れなければ難しいかもしれません。
 
 →調査の結果、音源レジスタで読み込み動作があるのは$4015のみであることが分かりました。
 長さカウンタが0になったときに該当するチャンネルのフラグを降ろすステータスレジスタの働きをします。
 現在のエミュレータでは、$4015自体を読み・書き含めて動作しないようにしてあります。
 頻繁に書き込むNSFを再生するとノイズが発声して聞き苦しいので。

・[APU] キーオンするのは周波数レジスタの上位に書いた時のみ?

 これはシミュレーションの再現性に関する重要なポイントになります。
 エンベロープやデューティ比、スイープ、さらに周波数下位レジスタへの書き込みなどが実際の音に反映されるタイミングが
 いつなのかということで、現状ではどのレジスタに書き込んでも、まとめて即時に反映されるようになっています。
 
 →調査の結果、キーオン後にパラメータを弄っても反映されることが分かりました。
 ただし、再キーオンでなく現在発音中の音に即時適用されるようです。
 また、三角波チャンネルのリニアカウンタと長さカウンタの動作については、未だよくわかっていません。
 曲によって三角波が鳴りっぱなしになるのはこのためです。詳しい情報お持ちの方は是非教えてください。

■ 参考資料・リンク

このプロジェクトの参考にさせていただいた資料・サイトなどです。

・nsfspec.txt
 nsfについての仕様をまとめた資料です(英語)。下記リンクの NESDEV に置いてあります。
 nsfspecj.txtという日本語訳版もあるのですが、誤訳が・・なのでオリジナルを読んだ方が良いでしょう。

http://nesdev.parodius.com/
 NESDEV NES INFO,PROGRAMS,AND DEMOS. NES開発関係の総本山です。

http://www.geocities.co.jp/SiliconValley/5604/index.html
 Jay's Factory 「ファミコンに関する技術情報」が公開されています。

http://crystal.freespace.jp/pgate1/
 pgate@crystal 「NES on FPGA」 の記事中の CPU,APU の動作が大変参考になりました。

https://kikb.web.fc2.com/
 Kiの研究部屋 「6502の研究部屋」でCPUの細かい挙動について詳しく解説されています。

http://gikofami.fc2web.com/
 ギコ猫でもわかるファミコンプログラミング 分かりやすく書かれていて初心者の筆者には助かりました。

http://wikiwiki.jp/mck/
 mck Wiki 自作のNSFファイルを作るmckに関するWikiサイト。ここを眺めたのが本プロジェクトの始まりです。

http://mck2ch.hp.infoseek.co.jp/
 MCK-2CH STORAGE MCKで作られたデータのアーカイブです。周波数の計算方法は mck で知りました。

http://famicompo-mini.com/
 Famicompo Mini NSFコンペティションを実施されています。

http://gigo.retrogames.com/
 GIGO and Hii's Page NESエミュレータ の GNES は非常に強力で使いやすいデバッガが備わっています。



▲ TOP