RubyでNESエミュレータをつくりはじめた話

先週末からつくっている NES エミュレータ r7kamura/rnes の話。

NES エミュレータ

そもそも NES って何?という話。 NES というのは、Nintendo Entertainment System の略称で、日本で販売されていたファミコンを海外向けに若干カスタマイズして販売したやつの名称です。NES エミュレータっていうのは、その NES と互換性のあるソフトを PC などで動かせるようなプログラム。但し NES エミュレータと呼ぶ場合、ファミコンと NES の差異が意識されていることはほぼなくて、NES エミュレータとファミコンエミュレータを総称して NES エミュレータと呼ばれていることがほとんどだと思われる。

なぜ NES エミュレータを?

自分が NES エミュレータをつくっているのは、コンピューターの仕組みとプログラミング言語の勉強を楽しく学ぶためです。そもそもよく出来た NES エミュレータって世の中に沢山あるので、それを置き換えようという話ではなくて。NES と互換性のある、つまり NES エミュレータで動かすためのフリーソフトって同人界隈とかでつくられたりもしているので、そういうのを動かしたりしながら楽しく学べたらなと。

NES エミュレータを実装するための工程って、小さな CPU レジスタを実装して、NES のハードウェアに組み込まれている CPU のベースになっている MOS 6502 という CPU に実装されている命令群を実装して、CPU が作業するためのメモリを実装して、CPU がメモリ内に用意してくれたデータを使って 256 × 240 ピクセルの画面を描画する GPU を実装して、カセットに書き込まれているバイト列のパーサを書いて、という感じの工程になる。なので、コンピュータの仕組みを学ぶにはとても良い教材だと思っている。

なぜ Ruby で?

以前 JavaScript で Hello, World! を出すくらいまでは少しだけ実装したことがあったんだけど、JavaScript って良くも悪くも、いや悪いか、書いたコードのスタイルが数年後に古くて良くないものだとみなされやすくなる傾向が比較的強い。「去年 JavaScript で書いたときに使ったツールチェインってあれとあれで、ビルドもあれでやっているので、今だったら当然これとこれを使うだろうけど、書き直すの面倒だな」ということになりやすいので、それがモチベーションを削ぐことを一番恐れている。モチベーションは貴重なので…。

あと関数型的スタイルと OOP 的スタイルの思想が混在していることもあって、書き方が自分の中でも他人との間でもブレやすい。なので、よくわからないまま試行錯誤したり、数ヶ月おきに飽きたり思い出したりしてたまに書いたりする趣味のコードだったら、自分が普段よく使う Ruby で書いた方が良いかなと思い直してしまった。但し GUI やハードウェアとの連携、音声周りなんかの言語を動かす環境は Web ブラウザなどの上で動かせる JavaScript の方が便利なので、動くものがつくりたい場合には JavaScript でつくったあと WebAssembly との相性の良い言語に移植して遊んだりするのが楽しいと思う。

Ruby で実装すると他のスクリプト言語で実装した場合に比べて今のところ少し遅いので (Ruby 製の NES エミュレータ Optcarrot でも 60fps 出すのはそこそこ難しいらしいと音に聞いている)、何がこれを遅くしてるのかなというところにも少し興味がある。普段 Web アプリを実装するときのようにオブジェクト生成していると遅そうなので、逆方向に全振りして、今回の NES エミュレータも普段 Web アプリを実装するときのようにオブジェクト生成するような実装にしています。それで動くようになったあとで内部を解析して、うわ遅ッてなりながらマッチポンプ感のある高速化をしようかなと。

進捗

進捗ほんとうにダメで、まだ有志の作成した簡単な作業テスト用 ROM しか動かせていない状況です。いろいろ動くようになって敗北を知りたい状況になったら、メルカリで ROM 吸出し機買って実家にあるスーパーマリオブロス動かしてみよかなと思ってるんだけど、まだ全然その域に達していなくて悲しい。

上の画像は初めて『ギコ猫でもわかるファミコンプログラミング』の最も簡単な ROM が動いたときの様子。まだ高級な GUI を実装できていないので、ターミナルで8点点字を利用して描画している。

一応キーパッドも実装していて、WASD で猫が動くんだけど、改行無しで標準入力に受け付けてもらうために端末の raw モードを有効化すると、標準出力が改行できなくなってしまうためにこれを有効化できず、現状 W + Enter とか A + Enter みたいな入力が必要になってしまっている。

上の画像は『NES 研究室』のシューティングゲームの ROM を動かしてみた様子。上端と右端がかなりずれていることが分かる。

上の画像はスクロールを実装したあとで動かしてみたレースゲームの様子。