何番煎じか分かりませんが、私も「ファミコンエミュレータを書いてみたい!」と思って「ChibiNES」というファミコンエミュレータを公開しました
Releases からバイナリをダウンロードできます
ファミコンは子供の頃にやっていたこともあり、特段と思い入れがありました (とはいえ、私の世代はスーファミ全盛期なのだろうか)
機能紹介、苦労話、振り返りと雑多に書いていこうと思います
2022/08/01 追記
- ToyNES から ChibiNES という名前に改名しました
機能紹介
README.md に貼り付けていますが、マリオや私の独断と偏見で選んだゲームが動きます。
NSF Player も(別コマンドでですが)動きます (音無しバージョン)
このプロジェクトを通じてやりたかったこと
- 自分の好きだったゲームを動かしたかった
- 吸い出し機買いました
- マルチプラットフォーム(Windows, Mac, Linux)でバイナリを提供したい
- マルチプラットフォームなライブラリの選定
- GitHub Actions で Releases にバイナリが登録できるように
- ネイティブアプリケーションで動かしたい
- ブラウザで動かす方法(Canvas, WebAudio)もありますが、そこはロマンです
- VirtuaNES に実装されている NSFプレイヤーのようなものを作りたい
- APU の仕様理解、実装を頑張る
- NSFプレイヤーモード
- 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 に対応してくれていたので、ファイルダイアログの実装はしないことにしました
- mlabbe/nativefiledialog という実装もあるっぽいですね
- (余談) Mesen は Core 部分は C++、GUI 部分は C# (.NET Framework かな?) で書かれているみたいですね
- 音声は portaudio (fogleman/nes から)
- SDL2 の audio を使っても良かったのですが、SDL2 に移植するモチベは無かったのでそのままにしています
また、いろいろなところからコードを引っ張ってきてるので、Core 部分はキメラ化しています
- Core 部分 (CPU, PPU, APU) は libretro/Mesen
の丸パクリを参考- nes-test-roms の ppu_vbl_nmi のテストを通すため
- NSF Player の再生機構は theinternetftw/famigo から
- 表示部分は調べつつ書きました
エミュレータ実装を体験してみての感想
- ファミコン文化すごい
- 自作の ROM を開発していたり、チップチューンな曲を作っている方がいたり、モチベーションが凄いなぁ、と
- NSD.lib などのライブラリを公開されている方もいらっしゃったり、奥が深い世界だなぁ、と感銘を受けています
- ファミコン本体の拡張をせずに「カセットでどうにかしよう!」の気持ちが凄すぎる
- 仕様理解として一番苦労したところなんですが、当時のファミコンゲーム開発者の方々の「とにかくカセットでなんとかしよう!」という情熱が凄いですね
- バンク切り替えから始まり、拡張音源もあったり、チャレンジングすぎます
- Mapper の各種実装が全然分からん
- FPGA を使ったエミュレータとかであれば、カセットの中にチップが入ってるので Mapper の実装はいらないのですが、ソフトウェアエミュレーションではもちろん必要です
- 「先人の方々はカセットをめちゃくちゃ解析して挙動を見たんだろうなぁ」と思い、その情熱に感服します
- 全 Mapper 対応は趣味でやる領域じゃない 対応できてるソフトはすごい
- 音のプログラミング奥が深すぎる
- 未だに理解が浅いと思います
- まず音の出し方から分からない、APU のコードを読んで「なんでこの音が出るんだ?」という疑問が晴れない、みたいなのを延々と繰り返してました
- 小さいプログラムを書いて、サイン波を鳴らしてみたり、APU 周りはとにかく苦労しました
- 拡張音源というものを知った時には「なんでやヽ(`Д´)ノ」と思わず叫びそうになったほど
一旦まとめ
- 実際に仕様を読んでみたり、コードを書いてみないと知ることができないエミュレータの世界を知ることができました
- ゲームボーイやスーパーファミコンのエミュレータはどうなってるんだろう?という気持ちが出てきました
- Mapper ってやっぱりありそうな気がするけど、あるんかな。どれくらい複雑なんだろう・・・?
- エミュレータ書きたいときはファミコンより仕様がシンプルなハードから始めたほうがいいかもしれない
あとは参考リンクと自分用の Twitter 振り返りを貼り付けておきます
興味がある方はどうぞ〜
参考リンク
振り返り
4 月中旬 fogleman/nes を Mapper 16 に対応させようと悪戦苦闘しているとき
fogleman/nes 改造して Mapper 16 動いた気がするけど、凄く ... 惜しいです ... (画面は「ナイトガンダム物語3」) pic.twitter.com/0WW3SdiVTC
— kaishuu0123◝( ⁰▿⁰)◜ (@kaishuu0123) 2022年4月20日
エミュレータの実装、バンク切り替え1つ取っても工夫があるんだな。
— kaishuu0123◝( ⁰▿⁰)◜ (@kaishuu0123) 2022年4月22日
確認してるのは以下の3つくらい
1. フラットなバイト列からビット演算でゴリ押しで読み込む
2. 今動いている RAM をページ単位で指定して書き換える
3. ROM を扱いやすいように下処理してから読み込む
fogleman/nes に Mapper 16 の実装ができて、セーブデータの保持のために EEPROM を書いていたころ
エミュ書くって言ってなんで自分は今 I2C の EEPROM のエミュレーションをしてるんだろう(遠い目)
— kaishuu0123◝( ⁰▿⁰)◜ (@kaishuu0123) 2022年5月8日
現実逃避でなぜか画面の拡大方法を検討しはじめる
エミュレータの拡大は NearestNeighbor(最近傍法) が一番綺麗に出るね。以前の実装から間が空いたけど、PPU 実装し直した。 pic.twitter.com/9Ruoh5yZSa
— kaishuu0123◝( ⁰▿⁰)◜ (@kaishuu0123) 2022年5月22日
この頃に nes-test-roms の存在に気づき、各種エミュレータの動作検証を開始
ファミコンエミュレータの PPU の実装。nes-test-roms にある ROM のテスト PASS させるのメチャクチャ大変だよなぁ ... (各種実装の調査結果) pic.twitter.com/poV1XvHoxq
— kaishuu0123◝( ⁰▿⁰)◜ (@kaishuu0123) 2022年5月22日
自分の好きだったゲームが動く
「SDガンダム外伝 ナイトガンダム物語2 光の騎士」のスクロールやっと動きましたワーーーーー!!!!!( ੭ ˙ᗜ˙ )੭ (超ニッチ) pic.twitter.com/MmbFKOWb2T
— kaishuu0123◝( ⁰▿⁰)◜ (@kaishuu0123) 2022年5月22日
自分の作っているエミュレータの実装方針でまずいことに気づく (CPU と PPU のステップ実行の件)
こうやって書いたら当たり前なんだけど、
— kaishuu0123◝( ⁰▿⁰)◜ (@kaishuu0123) 2022年5月27日
1. CPU 実行 (Cycle を返す)
2. PPU を CPU の Cycle の 3倍実行
という順序じゃタイミングズレるから駄目だと気づいて膝から崩れ落ちそうになった。
CPU が 1 cycle 実行する度に PPU は 3 cycle なんだよなぁ(メモリ読み書き含む)#nesdev
ここらへんから Mesen の Core を Go に移植開始
ppu_vbl_nmi の rom が動く
ppu_vbl_nmi.nes All tests passed キマシタワー(∩´∀`)∩ (Mesen からコード引っ張ってるんだからそりゃそう) pic.twitter.com/FoP3uNJq0c
— kaishuu0123◝( ⁰▿⁰)◜ (@kaishuu0123) 2022年6月3日
自分でやりたいことがあったので imgui-go に PR を送る
imgui-go で GetForegroundDrawList が無かったと思うのでPRをした。Merge されるといいな。https://t.co/M5ICfpjvN9
— kaishuu0123◝( ⁰▿⁰)◜ (@kaishuu0123) 2022年6月10日
NSF Player が一旦キリがいいところまで出来上がる
自分好みの NSF Player ができましたわー🥳 (※音が出ます)
— kaishuu0123◝( ⁰▿⁰)◜ (@kaishuu0123) 2022年6月26日
ファミコンエミュ実装でずっと作りたかったやつ(∩´∀`)∩
ちょっとギコちないのはパフォーマンスのせいです
最後の曲は Famicompo Pico 1 を使わせていただきました✨ https://t.co/KS7v3Eyq9n#nesdev pic.twitter.com/bIWRM7v6NL