ファミコンエミュレータを写経してみるお話4【nestest.nesの起動】
前回
ファミコンエミュレータを写経してみるお話3【HelloWorld(sample1.nes)の実行】 - 196Log
前回は
sample1.nes
を起動させ、ひとまず橋渡し系の実装はできていたことが確認できたので、いよいよ実装自体を修正していきます。使うのはnestest.nes
ですが、起動させようにもまず画面が表示されませんでした、、
参考にするもの
- コード周りはこちらのコードを観させてもらおうと思います。また
Rust
の書き方の勉強としても頼りにさせてもらいます。
github.com
ファミコンエミュレータの創り方 - Speaker Deck
- こちらに有志の皆さんが
NES
の情報をまとめて下さっているので、利用させてもらいます。特に、コードを写経しているだけでは意味がないので、まずこちらを見て自分で実装を考えます。
- こちらの本も参考にさせてもらいます。1つ目のリンクの方の実装と見比べる事で、実装にマストな部分を見出すために使いました。また、こちらの方の実装順番も参考にしてます。
読み始める前に
- ファミコンの仕様についてはネット上にたくさん情報が上がっているので、ここでは詳しい事は書かないです。(自分も分からない)用語の説明もほとんどしていません。 しかし、実装手順の再現性があるサイトは個人的に少ないなと感じたので、自分の試行錯誤をここにまとめて、うまく踏み台にしてもらえたらなと考えています。言語は
Rust
を使っており参考サイトもRust
が中心のものが多いです。 - また、解説力も乏しいので、適宜実装が完了したと思われる
commit
にジャンプできるGithub
のリンクをそれぞれに置いておくので、そこからコミット履歴などを参照していただいてもらう形にします。申し訳ないです。
デバッグの開始
どうやらPC
を0xC000にすることで、画面が見えなくても機能テストをやってくれるモードがある模様です。ありがたすぎる。ログが下のサイトに一緒に載っているので、そちらと自前で仕込んだログを見比べながら修正していくそうです。
デバッグの仕方
cpu
周りであれば実行される命令を逐次表示すると良いと思います。下は先で紹介したログとの比較に使いました。(正確には参考リポジトリのコードにもprintln
を仕込んで自分のと照らし合わせていたので、先で紹介したログと若干結果が違うかもしれない。)
print!("{:x} ", register.get_PC()); let code = fetch(register, cpu_bus); print!("cpde:{:x}", code); let ref opemap = opecodes::OPEMAP; let code = &*opemap.get(&code).unwrap(); let operand = fetch_operand(&code, register, cpu_bus); println!("opecode = {:?},operand = {:x},A:{:x},X:{:x}.Y:{:x},statys:{:x},SP:{:x}", code, operand, register.get_A(),register.get_X(), register.get_Y(),register.get_Status(),register.get_S());
ppu
周りは今回はあまりデバッグできていないのですが、僕の場合はとりあえず画面が表示されなかったので、そこまではnestest.nes
のバイナリを読んで、どの命令で詰まっているのか確認しました。エントリーポイントは0xC00C
でした。
ちなみに、画面なしのデバッグ0xC000
の場合もそうですが、今回の実装は起動時にreset
のメソッドを呼んでいるので、register
のnew
内ではなくreset
でPC
を書き換えることで任意の場所から実行することが可能です。
変更点
詳細はリンク先のプルリクエストのコミットから確認可能になっているはずです。このプルリクエストでnestest.nes
の画面が表示できるようになります。(サウンド、キーパッドは未実装) 既に上手く起動している方はあまり関係ないかもしれません。
下にいくつか代表的な変更を書きます。
opecode, fetch命令の修正
サイクル数が間違っていたり、そもそも挙動が違っていた箇所を修正しています。txs
に関しては研究室のページの説明がおそらく間違っていた気がします。
NES研究室 - 6502
不足分の命令
ドキュメントに載っていない命令が出てくるのでそれも実装します。(実装面は参考リポジトリ頼りでしたが、上のログにも出てきているので発見はできるかと思います。)
clear_sprite_hit
ppu
レジスタのメソッドが大きく違っていました。これが原因で画面が表示される1日潰しました。
起動
起動しました。心が折れる前に動いてよかったです。参考リポジトリのプロジェクトにデバッグコードを仕込んで、自分の実装とどう動きが違うのか確認するのが正直手っ取り早いですが、バイナリ呼んで変な動きを探す方がカッコ良いです。。笑
次回
ようやく画面が出てきたので、次はキーパッドを実装してnestest
でcpu
のデバッグができるようにしようと思います。
次