セキュリティ系の勉強・その他開発メモとか雑談. Twitter, ブログカテゴリ一覧
本ブログはあくまでセキュリティに関する情報共有の一環として作成したものであり,公開されているシステム等に許可なく実行するなど、違法な行為を助長するものではありません.

ファミコンエミュレータを写経してみるお話4【nestest.nesの起動】

//

前回

ファミコンエミュレータを写経してみるお話3【HelloWorld(sample1.nes)の実行】 - 196Log

前回は

sample1.nesを起動させ、ひとまず橋渡し系の実装はできていたことが確認できたので、いよいよ実装自体を修正していきます。使うのはnestest.nesですが、起動させようにもまず画面が表示されませんでした、、

参考にするもの

  • コード周りはこちらのコードを観させてもらおうと思います。またRustの書き方の勉強としても頼りにさせてもらいます。
    github.com

ファミコンエミュレータの創り方 - Speaker Deck

  • こちらに有志の皆さんがNESの情報をまとめて下さっているので、利用させてもらいます。特に、コードを写経しているだけでは意味がないので、まずこちらを見て自分で実装を考えます。

hp.vector.co.jp

pgate1.at-ninja.jp

  • こちらの本も参考にさせてもらいます。1つ目のリンクの方の実装と見比べる事で、実装にマストな部分を見出すために使いました。また、こちらの方の実装順番も参考にしてます。

booth.pm

読み始める前に

  • ファミコンの仕様についてはネット上にたくさん情報が上がっているので、ここでは詳しい事は書かないです。(自分も分からない)用語の説明もほとんどしていません。 しかし、実装手順の再現性があるサイトは個人的に少ないなと感じたので、自分の試行錯誤をここにまとめて、うまく踏み台にしてもらえたらなと考えています。言語はRustを使っており参考サイトもRustが中心のものが多いです。
  • また、解説力も乏しいので、適宜実装が完了したと思われるcommitにジャンプできるGithubのリンクをそれぞれに置いておくので、そこからコミット履歴などを参照していただいてもらう形にします。申し訳ないです。

デバッグの開始

どうやらPCを0xC000にすることで、画面が見えなくても機能テストをやってくれるモードがある模様です。ありがたすぎる。ログが下のサイトに一緒に載っているので、そちらと自前で仕込んだログを見比べながら修正していくそうです。

Emulator tests - Nesdev wiki

デバッグの仕方

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のメソッドを呼んでいるので、registernew内ではなくresetPCを書き換えることで任意の場所から実行することが可能です。

変更点

詳細はリンク先のプルリクエストのコミットから確認可能になっているはずです。このプルリクエストでnestest.nesの画面が表示できるようになります。(サウンドキーパッドは未実装) 既に上手く起動している方はあまり関係ないかもしれません。

github.com

下にいくつか代表的な変更を書きます。

opecode, fetch命令の修正

サイクル数が間違っていたり、そもそも挙動が違っていた箇所を修正しています。txsに関しては研究室のページの説明がおそらく間違っていた気がします。 NES研究室 - 6502

不足分の命令

ドキュメントに載っていない命令が出てくるのでそれも実装します。(実装面は参考リポジトリ頼りでしたが、上のログにも出てきているので発見はできるかと思います。)

clear_sprite_hit

ppuレジスタのメソッドが大きく違っていました。これが原因で画面が表示される1日潰しました。

起動

起動しました。心が折れる前に動いてよかったです。参考リポジトリのプロジェクトにデバッグコードを仕込んで、自分の実装とどう動きが違うのか確認するのが正直手っ取り早いですが、バイナリ呼んで変な動きを探す方がカッコ良いです。。笑
f:id:thinline196:20200310210534p:plain

次回

ようやく画面が出てきたので、次はキーパッドを実装してnestestcpuデバッグができるようにしようと思います。 次

ファミコンエミュレータを写経してみるお話5【keypad, sound】 - 196Log