ファミリーコンピュータをエミュレートします。
ファミリーコンピュータは動作タイミングが非常にシビアで、1clkずれると画像が正しく表示されなくなるなど、libvdmgrでどの程度再現できるか確認する意味で作成しました。
機能 | 状況 |
---|---|
6502 | ○※1 |
PPU | △※2 |
APU | ○ |
対応マッパー | 0,1,2,3,4,5,7,9,10,16,18,19,20,21,22,23,24,25,26 32,33,34,48,64,65,66,67,68,69,70,72,73,75,76,77,78 80,82,85,86,87,88,89,93,94,95,96,97,101,113,118 152,153,154,157,159,180,184,185,188,207,210※3 |
周辺機器 | × |
※1 | 未定義命令は、実機で確認できたISC, SXA, SYAのみサポート。 |
※2 | Nes Devのdemoを動作させると、隙間の色が完全な形で再現できていないものあり。 ただし、実機でもたまに違う結果ができるので、タイミングが非常にシビア? |
※3 | そういったものにはたぶん対応できていません。 ヘッダーは自分で作成する(ROMイメージには存在しない)ため、適当にフラグを追加すれば良いのですが、そういったROMが手元にないので無視しています。 NES2.0と言う仕様があったので、32/78/185のサブマッパーのみ対応 |
宇宙警備隊SDFを手に入れたので、MMC5のメモリ管理と割り込みのみ追加。
宇宙警備隊SDFが旧ファミコン実機で動かすとサウンドが正しく鳴らず(なぜ?)、ファミコン互換機だと画面が正しく出ない。
ネームテーブルの管理、カウンタの開始タイミングがまだ良く分からない。
カウンタをMMC3のようにCHR ROMをアクセスするアドレスバスのエッジを見たけど駄目で、ROMにはVSyncもHSyncも来ておらず、クロックもCPUと同じクロックだけなので、とりあえずCHR ROMをアクセスしてから一定クロック空いた後のアクセスからカウントを始める用に実装(とりあえず)。
宇宙警備隊SDFはBGがおかしい状態で動作、沙羅曼蛇ACはとりあえず動作、その他は手元にないので未確認。
PPUの1行描画の最後に、ダミーのネームテーブル2回読み込みがあるので、2回連続が来た時にカウンタをクリアし、ネームテーブル読み出し32回目にスプライト用のパターンテーブルに切り替え、39回目に割り込みカウンタ、40回目に背景用のパターンテーブルに切り替えするようにしたらとりあえず動くようになった。
ただし、デモ画面・ゲーム開始画面が崩れているのと、縦分割はまだ未対応。
割り込み状態用のフラグ生成がまだ良く分かってない。
非表示時にキャラクターROMを読み込んでいたため、非表示時にVRAMへアクセスするとネームテーブルを正しくカウントできなくなり、割り込みの発生と、キャラクターROMのバンク切り替えが正しくできていなかった。
ただ、ROM(マッパー)側から表示・非表示を判断することができないので、ダミーのネームテーブル2回を常に監視することで対応(ドットクロックよりCPUのクロックの方が遅いので、ダミーのネームテーブル2回は信用できるはず)。
縦分割に関しては、ROMからはx方向8ドット以下のスクロール位置を判断できない(PPU内で行っている)ため、分割していない側画面の左右スクロールは8の倍数でないと駄目で良いはず(宇宙警備隊SDFのデモは8ドット単位でスクロールしている)。
分割側は、ネームテーブルをカウントして作成したキャラクター座標と、描画行から座標を作成。
PPUが計算したアドレスなどは全て無視し、もう一つの画面をROM内で作成したようなイメージで対応。
プログラムROMのアドレス(0x8000-0xffff)をRAMにする機能は、宇宙警備隊SDFは使用していないようなので、正しく動作するか未確認。
拡張音源は未対応(ROMに来ているクロックから作成するのは面倒なので、APUのタイマーを使用するか考え中)。
MMC5の拡張音源に対応。
MMC5以外の拡張音源にも対応できるよう、音源用のインタフェースを追加し、マッパー側に音源の処理を持つことで対応。
ただし、実際のROMのように、サウンドの入力と出力ピンで対応すると無駄が多くなるので、アドレスバスインタフェースを使用し、APUが音を作成する時にマッパーの音作成を呼び出すようにした。
矩形波は、内蔵のAPUからスイープを取り除いたものを使用、PCMはそのまま加算(対応したソフトがないので音が鳴るこのと確認のみ)。
宇宙警備隊SDFの効果音が鳴るようになったのを確認。
アフターバーナーを手に入れたので、マッパー68に対応。
ただし、同じマッパーで特殊なICを持つものがありますが未対応。
ラグランジュポイント、エスパードリーム2、ガンサイトを入手。
ガンサイトは、MMC5で動作することを確認、ラグランジュポイント、エスパードリーム2のマッパーを追加予定(メモリー管理などは簡単そうだが、音源が面倒)。
エスパードリーム2(VRC6)、ラグランジュポイント(VRC7)に対応。
VRC6のキャラクターROM管理の説明に、複雑な制御方法が書かれているが、シンプルに書くと動いた。
他ソフトが使用している仕様?
割り込みは、基本的な考えがVRC IVと同じ(CPUクロックを使用)なので、比較的簡単に動作。
VRC7は、VRC6と同じ割り込みコントローラを使用し、メモリー管理がシンプルなので、一発で動作。
拡張音源は非対応(VRC6はそれほど複雑ではなさそう、VRC7は保留)。
VRM6の拡張音源に対応。
内蔵アセンブラ追加。
DPCMが動作していない時の処理を修正(DPCMを動作・停止を頻繁に繰り返すとノイズが増えるかも)。
DPCMの停止中の動作が良くわからなかったので実機で調査。
書き込んだ値がそのままDACへ出力されると思っていたが、前回書き込んだ値との差分で、-64~63(最下位ビットが無視されるので実際は-32~31)の範囲でクリップされて出力されていた
(10を書きた後、20を書き込むと10が出力される)。
一度書き込んだ後に放置すると、かなりなまった波形で0へ戻る(現在は、このへんの動作と異なるため、急激な変化の時に音が汚い)。
現在のバンク番号や、PPUのメモリー内容をvdmgrdebugから確認できるようにしたので、シンプルなマッパーを大量に追加。
ただし、サンプルプログラムを作成し、vdmgrdebug上で確認だけで、実機の動作は未確認。
ジャンク品のキーボード(HVC-001)を手に入れたが、ファミリーベーシックのソフトがないので、テストアプリを作成して動作確認(NESASM、内蔵アセンブラで動作するソース)し、モジュールへ追加。
キーボードは、結構な数のキーを同時に押せたが、これはエミュレートできない(通常のキーボードはここまで同時に押せないので無理)。
6502のアセンブラは、動作確認にレジスタを叩くだけのものしか作成していなかったので、初めて単体で動作するものを作成したが、いまいち6502の癖が良くわからないのと、内蔵アセンブラがマクロをサポートしていないので、結構面倒なので、気が向いたらマクロを追加する。
ファミコンの起動直後のメモリーが良くわからなかったが、NESDEVのCPU power up stateに書かれていたので(RP2A03の型番が違うけどほとんど同じだと思う)それに合わせた。
そのため、ギコ猫でもわかるファミコンプログラミングのソースは正しく動かない(メモリの初期化をしていないため、スプライトが表示されないなど)。
未発売の、NES用のHard Drivin'が、こちらで公開されていたので、マッパー64に対応しました。
本来はマッパー158だが、ダウンロードできるのが64になっている+日本のソフトで64は存在しないので、マッパー64としています。
このマッパーを動かすのは結構大変で、マッパー自体はMMC3とほぼ一緒だが、割り込みのタイミングが複雑です。
MM3のPPUのアドレスバスA12の立ち上がりを検出するのは一緒だが、割り込みが実際に発生するのは、ROMへ入力されているM2のクロックの立下りが2回発生した時になっている(NESDEVで、波形を載せてくれている人たちがいます)。
M2の波形は結構ななまっているようで、正確なタイミングは難しいですが、CPUクロックとほぼ同期しており(同じ?)、ハイx2、ローx1を繰り返しているので、このクロックに合わせて動作させています。
また、道路を1行描画するのに、112cycle+割り込みを抜けた後の命令3cycleの115cycleで、113.5cycleを超えてしまうため、行がずれてしまいます。
実機で動作させている方の動画を見ると正しく表示されているようなので、どうも6502のIRQを連続で発生した場合、2回に一度は割り込みを抜けた後の命令が実行されずに、直ぐに割り込みが発生しているようです。
無条件に2回に1回なのか、それとも割り込みを抜けた後、2回に1回分岐命令があるので、この辺の命令が影響するのか不明だが、とりあえず、2回に1回直ぐに割り込みを発生させています。
開発向けに、現在実行している位置の走査線を表示できるようにしました(システム名 HVC-001(開発))。
ウィンドウには、H-BLANK、V-BLANKを含む全てを見えるようにしてあり、vdmgrdebugでステップ実行すると、走査線を緑色の線で表示します。
これを使用すると、CPUの1命令を実行すると、画面がどれだけ進んでいるかが視覚的に確認できます。
ネームテーブル、キャラクター、パレットも表示しようとしましたが、MMC3のように、キャラクターの読み込みアドレスにより割り込み作成しているROMが動作しないので、入れてありません(表示するだけで、割り込みが発生してしまうため)。
MMC1Cが、リロード値に0を許しているようなので、0を指定できるように修正(今までは、カウンタのリロードとカウントを別々に処理していたが、カウンタへリロード時に+1し、その後にカウントするように変更)。
開発向けに、ネームテーブル、キャラクター、パレットを表示するようにしました(通常のPPUメモリへのアクセスとは別のアドレスバスを追加することで対応)。
ネームテーブル全体を表示しているため、結構ウィンドウサイズが大きくなっていますので、邪魔なら、config/hvc-001_devel.iniファイル内の、subwindow関係の項目を変更することで、ウィンドウサイズの変更、表示サイズの変更、ネームテーブル/キャラクターROM/パレットの表示オン・オフなどできます。
また、メインウィンドウに画面、サブウィンドウにネームテーブル・キャラクター、パレットを描画していますが、ネームテーブル・キャラクター、パレットを別々のウィンドウにすることや、メインウィンドウ1つにまとめると言ったこともできようになっています。
えりかとさとるの夢冒険を手に入れたので、マッパーと拡張音源を追加。
この拡張音源は最大8チャンネルで、使用するチャンネル数が増えると音質が悪くなると書いてあったので、作るのが大変かと思ったが、更新レートがCPUクロック1.78977/15/チャンネル数となるので、単純にCPUクロック1.78977/15分周ごとに1チャンネル分の周波数を足すだけで音が鳴った。
音を鳴らす処理は、ソースコードが1画面に収まる数十行程度で、思った以上にシンプルです。
ただ、実機だと音がかなり小さいので、NesDevで確認してみると、ソフト毎に違う抵抗が入ているようで、NES2.0で調整できるようにする必要があるらしい。
手元にはえりかとさとるの夢冒険しかないので、現在は内蔵と拡張が同じ比率になるように設定してあります。
ついでなので、AY-3-8910のデータシートを拾ってきたが、トーン部分はSN76489よりもシンプルなので実装してみました。
こちらもトーンを鳴らすだけなら数十行程度で完成。
ただし、ノイズやエンベロープは、詳細な波形を確認する手段がないため、未実装です。
MSXにAY-3-8910が載っているみたいだが、この確認のためだけにMSXを手に入れるのは嫌(ファミリーベーシックのキーボードですら邪魔なのに)なので調べてみたら、AY-3-8910単体でまだ売られているようなので、時間があるなら実際に波形を出力させて確認したい。
ラグランジュポイントが手元にあるので、VRC7の拡張音源も対応したいが、FM音源の仕組みを良く知らないため、まだ実装は無理そう。
Bandai FCGシリーズがI2CのEEPROMを使用しているようなので、I2Cのスレーブ処理とEEPROMを実装。
I2Cのスレーブ処理は単体のモジュールとして動作させることを考えてあるため、ほとんどの機能は実装済みだが、ゼネラルコードアドレスと10ビットスレーブアドレス、クロックストレッチは未対応(Bandai FCGシリーズはSCLの読み込みができないようなのでとりあえず無視)。
EEPROMのX24C01は、ATMELのAT24C系と同じだと思っていたが、データシートを拾って確認すると、スレーブアドレスがワードアドレス(読み書きの開始アドレス)になっていた。
これってI2Cデバイスではないだろうと思ったら、やはりデータシートにはI2Cと書いていなかった(X24C02は一般的なI2Cデバイス)。
これで日本のソフトが使用しているマッパーは一通り作成したはず(キャラ化けや、セーブできない、WRAMの設定忘れ、初期値の間違い、NES2.0に対応していないマッパーなど問題もあるが)。
また、割り込みの発生タイミングが1-11クロック程度ずれてるかも(0スプライトが立つのも1-3クロック程度ずれてるかも)。
PPUの256CLK目で、色の強調やモノクロ、表示のオンオフを切り替える($2000,$2001)を変更すると、その行全ての設定を反映してしまうことがあったのを修正しました。
キャラクターROM/RAMへの読み書きは実機と同じタイミングで行っていますが、描画のタイミングは、$2000,$2001へのアクセスがない時は1行、アクセスがあると現在位置まで描画と言う処理をしていますが、なぜか256DCLKで1行描画はせず、257DCLKで1行描画していました。
256DCLKで立てている描画完了フラグ後の、$2000,$2001へのアクセスは現在位置までの描画は行わないが、設定は更新されてしまうため、257DCLKでは新しい設定で1行描画をしていました(行の開始で表示オンで、256DCLKで表示オフにすると1行表示されない)。
AY-3-8910のノイズとエンベロープを実装しました。
実際にAY-3-8910を購入し、RL78/G13で動作を確認したので比較的簡単に動かすことができました(RL78/G13はCコンパイラが馬鹿だけど、個人で使用するには非常に便利なマイコンだと思う)。
音に詳しくはないので、適当に抵抗を入れて3チャンネルをミキシングし、オシロで周波数を確認後、スピーカーへ接続して音を確認。
結構きれいに音が出ているが、波形になまりが結構あり、これが実力なのか良くわからないため、周波数とエンベロープの動作が同じタイミングになるように実装してあります。
動作確認に使用したソースは、ダウンロードにある20141102_AY-3-8910.zipです。
APUのスイープ動作が完了した時に、音の出力は停止状態になると思っていましたが、スイープ後、音をオンにするレジスタ以外を操作しても音がなるのが正しい動作なので修正しました。
データレコーダを追加しました。
録音は、標準のwavファイルで出力し、周波数44KHz、モノラル、8bitで作成します(wavファイルだとファイルサイズが大きくなりますが、通常の音楽と違いzipなどで圧縮が効きますので、現在は特殊なフォーマットはサポートしていません)。
録音で作成したwavファイルをPCで再生したものをファミリーベーシックへ入力させ、データとして読み込めることは確認しました。
再生は、標準のwavファイルで、周波数は自由、モノラル/ステレオ(左チャンネルのみ有効)、8/16bitを指定できます。
ファミリーベーシックからデータを出力しPCへ入力しましたが、波形がうまく読み込めなかったため、ファミリーベーシックからのデータが読み込めるかは不明です(波形を加工したりするのは面倒なので、実機から録音した波形でレベルを調整したい)。
しばらくディスクシステムを使う機会があったため、ディスクシステムに対応。
レジスタ数が少ないので簡単に対応できると思っていたが、拡張音源・ドライブ制御が思った以上に大変で、何とか動作しています。
完全に対応するには、FDS形式のディスクイメージでは無理(現在は内部でGAPやチェックサムを作成して対応)。
ROMデータベースに対応。
不要なら、resource/hvc-001.csvファイルを削除します。
一応マッパー68の子ガメカセット対応。
内蔵ROMの後に子ガメ(外部ROM)のイメージを結合して、イメージを作成します。
燃えろ!!プロ野球と燃えろ!!プロ野球’88決定版を分解してuPD7756を取り出し、RL78/G13を使用して音声を鳴らすことができたので、マッパー86・92のuPD7756に対応しました。
ハードウェアで音を鳴らす時に気を付けることなどが良く分かりませんが、以前AY-3-8910を動かした時は簡単に音を鳴らすことができたので、その感覚でuPD7756を動かしましたが、音がクリアに出力できず。
仕方ないので、制御などはRL78/G13で行い、AVO端子をカートリッジへ戻すことで鳴らし、waveファイルを作成しています。
本体起動直後のRAM内容を確認するため、ボンバーマンのROMをフラッシュROMへ変更して自作の確認プログラムを書き込み、実機で動作確認しました。
ゼロページの一部が、リセットするたびに内容が壊れるようですが、それ以外の個所が同じになるように修正しました。
ガンと麻雀コントローラ(井出洋介名人の実戦麻雀の専用コントローラ)を追加。
ガンですが、ガンからは画面上の白色の個所を認識したかどうかの判定状態を1ビットで返すだけのようです。
トリガーを引くと当たり判定以外の輝度を下げ、当たり判定個所を白くして判定すれば良いようなのでそのように対応しています。
マッパー18の音声ファイル対応を忘れていたので対応しました。
アセンブラにマクロなどの疑似命令をいくつか追加したため、NESASMと互換性は無くなっていると思います。
そのため、NESファイルの出力に対応しました。
アセンブラを作り直したので、その時にアセンブラの動作確認とMMC5の動作確認用のサンプルとして、ダライアスもどきを作成。
まだまだアセンブラが使いにくいが、とりあえず動かせるものを作成できることは確認できた。
MMC5は高機能なので、ROM/RAMを大量に使って適当に作っても、そこそこ動かせるのを確認。
信長の野望 戦国群雄伝のROMを、PLCCのフラッシュメモリへ載せ替えて動作確認したが、割り込みが1-3PPUクロック程度早いように見える。
実機で割り込み中にネームテーブルの操作などを行っていると、たまにノイズが出たりする。
画面縦分割モード時の縦スクロールは1ドット単位で指定できるが、8の倍数以外は綺麗にスクロールできない。
ただ、割り込みを使用すれば特殊な演出に使用できそう。
MMC5の拡張音源には本体内蔵の矩形波の簡略版2cとPCMCが1chあるが、PCMはDPCMのようにハードで鳴らしてくれるモードはないの?
レジスタがありそうなアドレスへ適当に書き込んでみたが無反応。
ファミリーベーシックを起動した時に、テキストファイルのソースをファイルを読み書きできる機能を追加しました。
vdmgr上で中間言語のコードへ変換し、直接RAMへ展開するため、ファミリーベーシックを起動してから、キー入力を受け付けている状態でソースファイルの読み込みを行う必要があります。
ファイルの拡張子は.basで、テキストファイルの文字コードは、SHIFT-JIS、またASCII形式保存します。
改行コードはCR+LF,CR,LF,LF+CRのどれでも可。
SHIFT-JISで表現できない特殊文字は、小文字xから始まる16進数2桁で指定します。
※半角カナのアならx60、●ならxCFと言った形式
ファイルの拡張子に.fbgを指定すると、BG1を保存します。
.fbsはテキストファイルで、.basファイル内へ.fbs内のテキストをコピーすることで、ソースコードの読み込みとBG1の読み込みを同時に行います。
中間言語コードとして確保されているサイズより、実際の中間言語コードが小さい場合(中間言語コード後にマシン語やデータを直接置いているようなプログラムの場合)、余り部分をデータとしてテキストファイルへ出力します。
行番号を書かずにコメント(')を指定した場合、中間コードを出力しません。
無駄なRAM消費を防げるほか、コメントに日本語が使用できます。
空白行は無視します。
行の先頭が空白やタブの場合、その文字を無視します。
ファミリーベーシックV1/V2/V3、牧村製作所さんのMMC5/VRC BASICに対応したつもりですが、V3とそれ以外の中間言語コードにいくつか違いがあるようです。
現在わかっているのは、命令数の違いの他、V3は1-9までの数値を1バイト、それ以外は3バイトで管理していることです。
入力例)説明書にあるメリーさんの羊 mary.bas
暇つぶしにアセンブラとMMC5サンプルのダライアスもどきにエレクトリックファンのようなもの追加。
何も考えずに作成していたら、スプライトの数が全然足りない。
以前からYM2413をエミュレートするため、VRC7でレジスタ操作アプリをラグランジュポイントへ書き込み、ファミコン実機で確認していましたが、試せば試すほど意味が分からないため諦めました。
と言うことで、emu2413を利用させて頂きました(MIT Licenseと言うことですが、使用しているモジュールのソースはこちらに置いてあります)。
大変有用なライブラリを有難うございます。
おかげさまで、SEGA MARKIII/MASTER SYSTEMのFM音源ユニットも動作するようになりました。
VRC7の音源を調べていて、キャリアは周波数を設定すれば思った通りのサイン波ができるのですが、モジュレータが入ると意味不明な波形になる。
ただ、周波数を早くすると波形の振れ幅が大きくなったりするのが仕様書のどの計算にあたるのか分からず(良く分からないBessel関数とか言うので計算すればエミュレートできる?)
これは正しくエミュレートできるものなのか確認したくなったので、wavファイルを出力できる他のエミュレータをいくつか試して見ましたが、実機と同じ波形を出力できるエミュレータがありませんでした。
これってどう処理すれば実機通りの波形が出るのだろうか、仕様書見てもさっぱりわからん。
もしかして、YM2413とVRC7の出力結果は違う?とYM2413で確認したいのですが、輸入しないと手に入らなそう(あってもすごく高い)なので放置します。
とりあえず、実機で左の値を設定すると、右の波形が出力される。
ちなみに、実機で左の値(キャリアのみ)を設定すると、右のサイン波が出力される。
こちらは仕様書通りのサイン波が出ているが波形の振れ幅が非常に小さい。
MULTIを増やせば波形の振れ幅がでかくなるがこの辺が仕様書のどこにあたるのが良く分からない。
ディスクシステムを動作させるには、BIOSのイメージファイルが必要になります。
RAMアダプターの0xe000-0xffffを吸出し、disksystem.romという名前で保存します。
disksystem.romファイルを、biosディレクトリへ配置し、vdmgr/vdmgrdebugを起動します。
マッパー18・72・86・92は、uPD7756を使用して音声を鳴らしていますので、音声に対応するにはROMイメージとは別に、音声ファイルが必要になります。
ROMイメージと一緒にwavファイルを圧縮することで音声に対応しています。
音声ファイルはwave形式で、ファイル名は「最後が2桁の数値.wav」の形式であれば読み込むことができます(wavファイルだけ別フォルダに格納して圧縮しても問題ありません)。
waveファイルのフォーマットですがサンプリング周波数は自由、ビット数は8/16、チャンネル数は1/2(左チャンネルのみ有効)。
※uPD7756のサンプリング周波数が8kHz(6kHz?)で、DACが9bitのモノラルなので、この倍数で良いと思いますが
マッパー86は00.WAVから15.WAVの16ファイル、マッパー18・92は00.WAVから31.WAVまでの32ファイルが必要です(ファイルが存在しない番号は無音になります)。
例)ファイル名
00.wav ← 正常 燃えプロ_01.wav ← 正常 02_燃えプロ.wav ← 数値が.wavの前にない 燃えプロ_3.wav ← 数値が2桁ない pcm/燃えプロ_004.wav ← 正常
CPUのエミュレートが1clkずれると、PPUのクロックは3clkずれるので、見た目が全然違うということがあります。
Nes Dev - PPU renderingを参考に、以下の方法で実機に近いタイミングを作成しています。
CPU | フェッチ・デコードと、実行・ライトバックを2回に分けて処理。 |
PPU | キャラクターROMの読み込み2clkをまとめて1clkで処理している以外、全てPPUクロック単位で処理。 |
APU | タイマー・シーケンサーなど全てAPUへのクロックで処理。 |
上記の対応で実機と同じ状態が再現できています(特に、描画行の中間や、後半のみちらつくと言った所)。
F-1で画面左端へ行った時の1ラインずれ
イース1,2のステータスのチラつき
スーパーマリオ3の画面下のチラつき
沙羅曼蛇の画面下チラつき
(行の最後の方でスクロールしている)
ただし、CPUを1clk単位で処理していないため、チラつきが実機と微妙にずれたりとまだ完全ではない。
また、音の作りが甘いです(実機から音を録音するとノイズが酷すぎて正確な波形が良く分かりません)。
下記動作は、テストプログラムを作成して実機で確認。
今回作成するにあたり、下記サイトを参考に作成しました。
大変貴重な情報を公開していただき有難うございます。