Kotlinでファミコンのエミュレータを書いた

f:id:Yuiki0627:20181018001606p:plain

ファミコン(NES)のエミュレータを書いてみたいとずっと思っていたので、書いてみることにしました。
そう思っている人は少なからず居るようで、先日もPHPで書いたというエントリが出たようです。

とりあえず先人たちがやっているように僕もSuper Mario Brothers(SMB.)を動かすのを目標にしました。

やったことがあるファミコンのゲームはSMB.しかないのでぴったりそうです。
(僕は1998年生まれの20歳で、実はファミコンの実機を触ったことが無い)

SMB.を動かすまで

SMB.を動かすまでの過程を解説していきます。
基本的に既存の文献やコードを読みつつ、コードに落としていきました。

1. Hello, World! を動かす

f:id:Yuiki0627:20181017232148j:plain

Hello, World!を表示するだけのROMを動かしましょう。
ROMは以下のページから手に入ります。
NES研究室 - サンプル

Hello, World!を動かすところまでは、以下の記事がとても詳しいので参照してみるのが良いでしょう。
ファミコンエミュレータの創り方 - Hello, World!編 -

Hello, World!が表示されたときはドーパミンがドバドバ出た感じがして、この時点でエミュレータを書いた意味がありました。最高。

おまけ

上手く動かなかったHello, World!の図

f:id:Yuiki0627:20181017232540p:plain

なんやこれ

2. nestest.nesの全テストに通す

CPUのテストをするROMがあるのでそれを動かして全テストに通しましょう。
ROMは以下のページから手に入ります。
Emulator tests - Nesdev wiki

既存の他のエミュレータで吐き出したログと作っているエミュレータのログを突き合わせてみるというのは良い手法です。
nes-test-roms/nestest.log at master · christopherpow/nes-test-roms · GitHub

ちなみにここで非公式オペコードをひたすら実装していく必要があります。
非公式と言いつつめっちゃ多いのでここは気合で💪

それと、コントローラも実装しておくと良いでしょう。

f:id:Yuiki0627:20181017233556p:plain

3. gikoシリーズを動かす

ギコ猫でもわかるファミコンプログラミング さんではいくつかの簡単な、しかしファミコンの機能の要点が抑えられているROMがいくつか公開されているので、それらを動作させていきましょう。

f:id:Yuiki0627:20181017234336p:plainf:id:Yuiki0627:20181017234349p:plain

だんだん動くものができてきて面白くなってきます。

アセンブリも公開されているので、それを元にプログラムの流れを掴んでいくのは効果的です。

それにしてもラスタースクロールってすごいですね...。

4. SMB.に挑戦する

ここまできたらSMB.に挑戦できるくらいエミュレータが育ってきているはずです。

何も出なくてもくじけてはいけません。

f:id:Yuiki0627:20181017235251p:plain

変なのが出てもくじけてはいけません。(自戒)

f:id:Yuiki0627:20181017235253p:plain

nestest.nes を通している時点でCPUはほとんど問題がないはずなので、大抵の問題はPPU(画像処理)にあるはずです。

頑張ってbug fixするとなんとか動きます!

f:id:Yuiki0627:20181017235747g:plain

ROMの入手について

僕はAmazonで吸出し機を、メルカリでカセットを購入しました。

f:id:Yuiki0627:20181017234959j:plain

まとめ

まだ音の実装ができてなかったり描画に問題があったりと難点は多いのですが、60fps近く出ていて、とりあえず遊べる程度にはSMB.が動いているので一旦良しとします。

エミュレータ開発、結構面白かったです。

コードはMITライセンスで公開しています!
github.com

参考文献