Kotlinでファミコンのエミュレータを書いた
ファミコン(NES)のエミュレータを書いてみたいとずっと思っていたので、書いてみることにしました。
そう思っている人は少なからず居るようで、先日もPHPで書いたというエントリが出たようです。
とりあえず先人たちがやっているように僕もSuper Mario Brothers(SMB.)を動かすのを目標にしました。
やったことがあるファミコンのゲームはSMB.しかないのでぴったりそうです。
(僕は1998年生まれの20歳で、実はファミコンの実機を触ったことが無い)
SMB.を動かすまで
SMB.を動かすまでの過程を解説していきます。
基本的に既存の文献やコードを読みつつ、コードに落としていきました。
1. Hello, World! を動かす
Hello, World!を表示するだけのROMを動かしましょう。
ROMは以下のページから手に入ります。
NES研究室 - サンプル
Hello, World!を動かすところまでは、以下の記事がとても詳しいので参照してみるのが良いでしょう。
ファミコンエミュレータの創り方 - Hello, World!編 -
Hello, World!が表示されたときはドーパミンがドバドバ出た感じがして、この時点でエミュレータを書いた意味がありました。最高。
おまけ
上手く動かなかったHello, World!の図
なんやこれ
2. nestest.nesの全テストに通す
CPUのテストをするROMがあるのでそれを動かして全テストに通しましょう。
ROMは以下のページから手に入ります。
Emulator tests - Nesdev wiki
既存の他のエミュレータで吐き出したログと作っているエミュレータのログを突き合わせてみるというのは良い手法です。
nes-test-roms/nestest.log at master · christopherpow/nes-test-roms · GitHub
ちなみにここで非公式オペコードをひたすら実装していく必要があります。
非公式と言いつつめっちゃ多いのでここは気合で💪
それと、コントローラも実装しておくと良いでしょう。
3. gikoシリーズを動かす
ギコ猫でもわかるファミコンプログラミング さんではいくつかの簡単な、しかしファミコンの機能の要点が抑えられているROMがいくつか公開されているので、それらを動作させていきましょう。
だんだん動くものができてきて面白くなってきます。
アセンブリも公開されているので、それを元にプログラムの流れを掴んでいくのは効果的です。
それにしてもラスタースクロールってすごいですね...。
4. SMB.に挑戦する
ここまできたらSMB.に挑戦できるくらいエミュレータが育ってきているはずです。
何も出なくてもくじけてはいけません。
変なのが出てもくじけてはいけません。(自戒)
nestest.nes
を通している時点でCPUはほとんど問題がないはずなので、大抵の問題はPPU(画像処理)にあるはずです。
頑張ってbug fixするとなんとか動きます!
ROMの入手について
僕はAmazonで吸出し機を、メルカリでカセットを購入しました。
まとめ
まだ音の実装ができてなかったり描画に問題があったりと難点は多いのですが、60fps近く出ていて、とりあえず遊べる程度にはSMB.が動いているので一旦良しとします。
エミュレータ開発、結構面白かったです。
コードはMITライセンスで公開しています!
github.com