saino.me (kaishuu0123)

都内でひっそりと生きる IT エンジニアの個人ブログです

Go で実装したファミコンエミュレータ「ChibiNES」を公開しました

何番煎じか分かりませんが、私も「ファミコンエミュレータを書いてみたい!」と思って「ChibiNES」というファミコンエミュレータを公開しました

github.com

Releases からバイナリをダウンロードできます

ファミコンは子供の頃にやっていたこともあり、特段と思い入れがありました (とはいえ、私の世代はスーファミ全盛期なのだろうか)

機能紹介、苦労話、振り返りと雑多に書いていこうと思います

2022/08/01 追記

  • ToyNES から ChibiNES という名前に改名しました

機能紹介

README.md に貼り付けていますが、マリオや私の独断と偏見で選んだゲームが動きます。

NSF Player も(別コマンドでですが)動きます (音無しバージョン)

youtu.be

このプロジェクトを通じてやりたかったこと

  1. 自分の好きだったゲームを動かしたかった
    • 吸い出し機買いました
  2. マルチプラットフォーム(Windows, Mac, Linux)でバイナリを提供したい
    • マルチプラットフォームなライブラリの選定
    • GitHub Actions で Releases にバイナリが登録できるように
  3. ネイティブアプリケーションで動かしたい
    • ブラウザで動かす方法(Canvas, WebAudio)もありますが、そこはロマンです
  4. VirtuaNES に実装されている NSFプレイヤーのようなものを作りたい
  5. Go で何か重めな実装をしてみたかった

中身の話 (ライブラリとか選定理由とか)

ライブラリのお話

  • GUI 周りは go-gl/glfw (GLFW) と inkyblackness/imgui-go (Dear ImGUI)
    • Qt とか GTK (!?) とかも迷ったんですが、OS ネイティブな何かを使うわけではないので、Dear ImGUI で実装することにしました
    • Qt の OSS 版は M1 Mac というか Apple Silicon に対応していなかったので残念ながら却下となりました
    • GLFW 側で D&D に対応してくれていたので、ファイルダイアログの実装はしないことにしました
    • (余談) Mesen は Core 部分は C++、GUI 部分は C# (.NET Framework かな?) で書かれているみたいですね
  • 音声は portaudio (fogleman/nes から)
    • SDL2 の audio を使っても良かったのですが、SDL2 に移植するモチベは無かったのでそのままにしています

また、いろいろなところからコードを引っ張ってきてるので、Core 部分はキメラ化しています

  • Core 部分 (CPU, PPU, APU) は libretro/Mesen の丸パクリ を参考
  • NSF Player の再生機構は theinternetftw/famigo から
    • 表示部分は調べつつ書きました

エミュレータ実装を体験してみての感想

  • ファミコン文化すごい
    • 自作の ROM を開発していたり、チップチューンな曲を作っている方がいたり、モチベーションが凄いなぁ、と
    • NSD.lib などのライブラリを公開されている方もいらっしゃったり、奥が深い世界だなぁ、と感銘を受けています
  • ファミコン本体の拡張をせずに「カセットでどうにかしよう!」の気持ちが凄すぎる
    • 仕様理解として一番苦労したところなんですが、当時のファミコンゲーム開発者の方々の「とにかくカセットでなんとかしよう!」という情熱が凄いですね
    • バンク切り替えから始まり、拡張音源もあったり、チャレンジングすぎます
  • Mapper の各種実装が全然分からん
    • FPGA を使ったエミュレータとかであれば、カセットの中にチップが入ってるので Mapper の実装はいらないのですが、ソフトウェアエミュレーションではもちろん必要です
    • 「先人の方々はカセットをめちゃくちゃ解析して挙動を見たんだろうなぁ」と思い、その情熱に感服します
    • 全 Mapper 対応は趣味でやる領域じゃない 対応できてるソフトはすごい
  • 音のプログラミング奥が深すぎる
    • 未だに理解が浅いと思います
    • まず音の出し方から分からない、APU のコードを読んで「なんでこの音が出るんだ?」という疑問が晴れない、みたいなのを延々と繰り返してました
    • 小さいプログラムを書いて、サイン波を鳴らしてみたり、APU 周りはとにかく苦労しました
    • 拡張音源というものを知った時には「なんでやヽ(`Д´)ノ」と思わず叫びそうになったほど

一旦まとめ

  • 実際に仕様を読んでみたり、コードを書いてみないと知ることができないエミュレータの世界を知ることができました
  • ゲームボーイやスーパーファミコンのエミュレータはどうなってるんだろう?という気持ちが出てきました
    • Mapper ってやっぱりありそうな気がするけど、あるんかな。どれくらい複雑なんだろう・・・?
  • エミュレータ書きたいときはファミコンより仕様がシンプルなハードから始めたほうがいいかもしれない

あとは参考リンクと自分用の Twitter 振り返りを貼り付けておきます

興味がある方はどうぞ〜

参考リンク

www.nesdev.org

pgate1.at-ninja.jp

www.masswerk.at

振り返り

4 月中旬 fogleman/nes を Mapper 16 に対応させようと悪戦苦闘しているとき

fogleman/nes に Mapper 16 の実装ができて、セーブデータの保持のために EEPROM を書いていたころ

現実逃避でなぜか画面の拡大方法を検討しはじめる

この頃に nes-test-roms の存在に気づき、各種エミュレータの動作検証を開始

自分の好きだったゲームが動く

自分の作っているエミュレータの実装方針でまずいことに気づく (CPU と PPU のステップ実行の件)

ここらへんから Mesen の Core を Go に移植開始

ppu_vbl_nmi の rom が動く

自分でやりたいことがあったので imgui-go に PR を送る

NSF Player が一旦キリがいいところまで出来上がる