セキュリティ系の勉強、その他開発メモとか雑談. GithubはUnity触っていた頃ものがメイン Twitterフォローもよろしくです

ファミコンエミュレータを写経してみるお話1【CPU】

//

はじめに

タイトル通りです。不定期に進めるつもりですが、最後まで進むかはまだ分からないです。


モチベーション

なぜ突然始めようと思ったか。 - 自分で調べてみたいゲームがある(秘密) - 低レイヤーに強くなりたい - Rustに興味がある

巷では自作OSが流行っていますが、みんなと同じだと芸がないので若干遠いものを選んでみました。

参考にするもの

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

github.com

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

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

hp.vector.co.jp

pgate1.at-ninja.jp

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

booth.pm

読み始める前に

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

始めにやること

まず、ファミコンを構成する全体像を把握していくと良いと思います。CPUPPUがどうつながっているのかなどなど。

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

色んなサイトがそれぞれの図で説明しているので、目を通した方が掴みやすいかもしれません。

CPUから作り始めてみる

僕はCPUから作り始めてみることにしました。理由は以下です。

  • ここの命令を駆使してコンピュータが動いている
  • 割と他の機器に依存しておらず、コードが読み始めやすい
  • 同じ理由で、とりあえず命令が1つずつ動くように作れば大丈夫そう


命令を1つずつ実装する

実装が必要なCPUの命令は参考サイトに一覧で載っているので、簡単に言えばこれらを呼び出して動くようにできればokなはず。まず、大まかな流れを作りながら、LDA imm関数(中身未実装)が呼べるまでを作ってみました。(リンク先はその時のコミットに飛びます)

github.com

ここでcargo runを実行すれば以下のような出力が得られ、とりあえず呼び出せた事がわかりました。

f:id:thinline196:20200228012740p:plain

足りないもの

命令だけ実装していくつもりでしたが、CPU レジスタBusまわりの実装も必要そうだと感じてきました。命令による状態の変化であったり、アドレスの指す他の場所のデータを読んでくるといった処理を命令に実装しなければならないためです。特にレジスタ周りはプログラムカウンタなど特に不可欠な要素が多いので、命令系よりも先に実装しておく必要がありそうです。

レジスタの実装

簡易的に必要となりうる機能を参考サイト様から引っ張って実装してみました。実際に操作はしていないものの、操作を呼び出せるようにCPUに接続してみたつもりです。 github.com
テストを走らせるために、データを他とやり取りするbusも必要そうです。テストだけであれば細かな実装はいらなそうなので、うまいことモックテストができるように実装してみます。

busの仮実装と初めての命令の実装

はじめに仮実装としてBusと思われるものを実装しました。機能は適当ですが、読み書きができるメソッドが呼び出し可能なようにしておき、周辺が充実してきたら機能を実装してく形にします。

github.com


実際にBusをCPUのなかに組み込み、LDA imm命令を実装、テストを追加してみたのもがこちらです。これだとまだモックは必要ありませんでした。

github.com

LDA系の命令を一通り実装する

だんだん説明が少なってますが、こちらがLDA系を網羅した際のコミットです。それぞれある程度のテストも実装しています。modでのテストはBusにより実際に命令を読んできて、その命令を実行させるという、大分本番に近い感じで動いていると思います。楽しいです。

はじめはLDAの命令を網羅的に実装していたのですが、「読んだ値をそのままAへロード」か「読んだアドレスの指す場所の値をAへロード」の2パターンしかないので、結果的にInstructionには2つしかメソッドが実装されていません。

github.com

全ての命令を網羅する

あとは全ての実装をしていくだけです。基本的に下の2つのサイトを確認しながら、命令の挙動を把握して行きます。

hp.vector.co.jp

pgate1.at-ninja.jp
一通り網羅しました。これにより基本的なCPU周りの実装はひとまず完了したと思われるので、プルリクにしてあります。

github.com


割り込み系の追加実装

上ではソフトウェアからのBRKの割り込みは実装したはずですが、ハードウェアからなどの割り込みは未実装でしたので追加します。下のリンク先では、追加実装時のプルリクを紹介しています。

github.com


ここまで

一通りテストも書いたはずなので、ポカはないと思いますが、間違っているとしたら自分の解釈部分で違っていた故のミスかと思います。ひとまずCPUの実装(写経)はこれで一段落つけます。もし追加で必要なものや修正があれば、後の実装で適宜直していこうと思います。このエントリがいつまで続くか分からないですが、引き続き頑張ります。
つづき

thinline196.hatenablog.com

追記

後日romを起動させながらデバッグを行い修正が加わったので、CPUの該当コードあたりは直しておいてください。修正する記事で後々出てくるコードなので今じゃなくても大丈夫です。

github.com

github.com