前回
ファミコンエミュレータを写経してみるお話6【ROMの吸い出し、Mapper】 - 196Log
前回の振り返り
mapperの対応をメインにカセットが動くように修正を行いました。が、音周りに未実装な部分が残っていました。セーブも行えない状況です。
参考にするもの
- コード周りはこちらのコードを観させてもらおうと思います。また
Rustの書き方の勉強としても頼りにさせてもらいます。
github.com
ファミコンエミュレータの創り方 - Speaker Deck
- こちらに有志の皆さんが
NESの情報をまとめて下さっているので、利用させてもらいます。特に、コードを写経しているだけでは意味がないので、まずこちらを見て自分で実装を考えます。
- こちらの本も参考にさせてもらいます。1つ目のリンクの方の実装と見比べる事で、実装にマストな部分を見出すために使いました。また、こちらの方の実装順番も参考にしてます。
読み始める前に
- ファミコンの仕様についてはネット上にたくさん情報が上がっているので、ここでは詳しい事は書かないです。(自分も分からない)用語の説明もほとんどしていません。 しかし、実装手順の再現性があるサイトは個人的に少ないなと感じたので、自分の試行錯誤をここにまとめて、うまく踏み台にしてもらえたらなと考えています。言語は
Rustを使っており参考サイトもRustが中心のものが多いです。 - また、解説力も乏しいので、適宜実装が完了したと思われる
commitにジャンプできるGithubのリンクをそれぞれに置いておくので、そこからコミット履歴などを参照していただいてもらう形にします。申し訳ないです。
DMC
DMCは、サンプリングした音を鳴らす機構のようです。録音した音を再生する装置みたいな感じだと思います。本プロジェクトでは音の出力方法が本来のものと違うため、どこまで再現できるか不安ではありますが、以前参考にしたこちらのリポジトリを参考に実装してみます。
nes/apu.go at master · fogleman/nes · GitHub
そして実装はこちらのプルリクになります。
実装には
- 矩形波がうまく鳴らないホットフィックス
DMCの仮実装
が含まれています。DMCに関しては自分がうまく理解できなかったため、あまり良い実装になっていないのと、音が若干違うこと、再生停止時の特有のノイズが入らない問題があります。また、WebAudioへの渡し方も微妙なので、ここのあたりは後々大きく変更するかもしれません。
SRAM(バッテリーRAM)
バッテリーバックアップRAM(正式名称不明)は、カセット側にあるRAMで、データの記憶方法を持たないファミコンにおいて、セーブデータなどを記録しておく場所として使われているはずです。色々と策は考えられますが、現状ブラウザのJSと繋がりがあるので、ブラウザに保存しておくのが一番簡単な方法かなと思います。今回はsave ramから取ってSRAMという名前で実装しています。
機能としては、(0x6000-0x7FFF)に割り当てられている8KBのRAMをローカルストレージに保存、またロード時に自動復帰する仕組みです。ローカルストレージにはカセットデータのパス名を利用してますので、上書きの心配はないと思います。
一区切り
ここまでを通じて、mapper0, Mapper3, Mapper4に加え、バックアップRAMの記録が動作するエミュレータを作成できました。私の目標はMapper4のゲームを動かすことでしたので、ここでコンスタントな実装は終了しようと思います。
一方幾つかバグや実装残しもあります。
APUの再実装(DMCがひどい)。具体的にはWebAudioをやめて、本来のNES寄りの出力方法にしたい- Spriteの描画プライオリティが正確ではない?例えば
DQではスライムの影が手前に描画されている。 - デバッグのために
CPUのレジスタログの取得など - 長時間利用で重くなることがある?
などなどが現状気になっております。以降ブログにまとめることはないとは思いますが、リポジトリは適宜更新しようかなと思ってます。(多分)
以上。