ファミコンエミュレータを写経してみるお話3【HelloWorld(sample1.nes)の実行】
前回
ファミコンエミュレータを写経してみるお話2【PPU】 - 196Log
やること
前回はPPU
の機能を実装しましたが、まだ動作確認ができていないので、CPU
と合わせてテストROMが動くようにしようと思います。まずはこちらのsample1.nes
を起動させてみます。hello word!
と表示されるものです。他のromはおそらく動きません。
参考にするもの
- コード周りはこちらのコードを観させてもらおうと思います。また
Rust
の書き方の勉強としても頼りにさせてもらいます。
github.com
ファミコンエミュレータの創り方 - Speaker Deck
- こちらに有志の皆さんが
NES
の情報をまとめて下さっているので、利用させてもらいます。特に、コードを写経しているだけでは意味がないので、まずこちらを見て自分で実装を考えます。
- こちらの本も参考にさせてもらいます。1つ目のリンクの方の実装と見比べる事で、実装にマストな部分を見出すために使いました。また、こちらの方の実装順番も参考にしてます。
読み始める前に
- ファミコンの仕様についてはネット上にたくさん情報が上がっているので、ここでは詳しい事は書かないです。(自分も分からない)用語の説明もほとんどしていません。 しかし、実装手順の再現性があるサイトは個人的に少ないなと感じたので、自分の試行錯誤をここにまとめて、うまく踏み台にしてもらえたらなと考えています。言語は
Rust
を使っており参考サイトもRust
が中心のものが多いです。 - また、解説力も乏しいので、適宜実装が完了したと思われる
commit
にジャンプできるGithub
のリンクをそれぞれに置いておくので、そこからコミット履歴などを参照していただいてもらう形にします。申し訳ないです。
修正
sample1.nes
を呼び出すために修正した箇所があります。まだ修正してない場合は参照してください。
【Fix】Hello world bug fix by 196Ikuchil · Pull Request #7 · 196Ikuchil/nes_emulator · GitHub
ひとまず呼び出そうとしてみる
現状頑張って機能を使おうとした場合、何が足りないのか把握します。下のリンクは軽い修正も含めて今までのものを実装したものになります。
renderer
という生成したスプライトを描画する機能- カセットを読み込む機能(
ROM
,parse
,Cassette
) - 上を操る
js
側の実装全般
カセットのデータ
Bus
も通さなければいけないので、最初にカセットの読み込みを実装するのが良さそうです。下のリンクではカセットデータをRom
へ流し込み、エミュレータのContext
内で扱えるように実装しなおしています。コミット履歴から、カセットのパーサーやRom
の実装についても確認可能です。
描画担当
次はrenderer
を実装します。これは描画を担当する部分です。PPU
で生成したデータを取り扱ってくれるようにします。
環境を整える
Rust
の成果物をjs
から呼び出して、ブラウザ上で動作させるようにします。npm
によって入れるものはローカルで成果物を走らせてブラウザで表示するために使用します。main.rs
には、js
側から呼び出すrun
メソッドを実装します。Makefile
を作りmake
でビルド等を走らせます。.cargo/config
に、ビルドに必要な設定を記述しておきましょう。
ビルドの際に必要になるものは以下なので、PCにインストールしておいてください。
- `nodejs` - `cmake` - `emscripten`(emcc) - `rustup`
externs
にはjs
側が公開しているメソッドをrust
側から呼び出すためのことを記述しています。起動実行にはemscripten
が用意したメソッドを、描画にはcanvas_render
というメソッドを呼び出せるようにしています。
最終的にプルリクにまとめているので、こちらを見た方が早いかもしれません。
sample1.nes
は起動しますが、他はまだ起動しないでしょう。
デバッグに関して
起動しない場合、まずCPU
の実装を疑うべきですが、初回はbackground
の値を出力してみるべきと思います。それにより描画部分かそれ以前かに分けられるので、以前であればtile
あたりのデータを覗いてみましょう。
そこにもデータが上手く反映されていなければ、CPU
の実装ミスを疑います。特に今回のROMは短いので、バイナリエディタと合わせて、実装される
opecodeを追っていくことで、
CPUの実装ミスに気がつけると思います。
<br>
それがダメであれば
PPUの実装をみますが、この辺りは単独では大変なので、僕は参考コードと自分の実装、同じ位置に
println!`をおき、値をくらべながら違う値が扱われている箇所を探しました。実装がほぼ同じであれば、意外と有効な技なので、試してみてください。
次回
次回はデバッグを行うためにnestest.nes
を起動させ(るためにデバッグを行います。
次
ファミコンエミュレータを写経してみるお話4【nestest.nesの起動】 - 196Log