Slide 1

Slide 1 text

TTERY DOT MATRIX WITH STEREO SOUND Common Lispでの ゲームボーイエミュレータ実装 進捗報告 vol.1 @t-sin 2022-03-31 Lispmeetup #102

Slide 2

Slide 2 text

TTERY DOT MATRIX WITH STEREO SOUND もくじ ● 発表の目的 ● 実装の動機と目的 ● ゲームボーイについて軽く ● エミュレータ実装について軽く ● 実装の方針 ● 実装日記 vol. 1 ● 実装の現状 ● まとめ

Slide 3

Slide 3 text

TTERY DOT MATRIX WITH STEREO SOUND 発表の目的 ● エミュレータ実装の進め方を伝えたい – エミュレータ自体の最も基本的な構造 – とっかかりとなるドキュメント – 動作テストのための情報 ● エミュレータ実装の雰囲気を伝える – 現在進行形で開発しているライブ感 – 試行錯誤のようす ● 実装の現状を話すことでやる気を生成する

Slide 4

Slide 4 text

TTERY DOT MATRIX WITH STEREO SOUND 実装の動機 〜 建前 ● エミュレータのしくみが知りたい – Wii U/3DSのバーチャルコンソール – QEMU – Virtual Box ● ゲームボーイについて知りたい – どんなハードウェアだったのか – なにができる/できないのか – ゲームボーイのソフトウェア開発の足がかり

Slide 5

Slide 5 text

TTERY DOT MATRIX WITH STEREO SOUND 実装の動機 〜 本音 ● ファンタジーコンソールを考える足がかり – 架空のゲームコンソールのエミュレータのこと – PICO-8とか有名 – Common Lisp製のlambda8ってのもある ● https://github.com/raydeejay/lambda8 ● すき – はじめて持ったゲーム機なので – レジェンドなハードウェアなので – ゲームボーイの音楽がすきなので – 『星のカービィ2』…!!!

Slide 6

Slide 6 text

TTERY DOT MATRIX WITH STEREO SOUND 実装の目的 ● エミュレータ実装の勘所を得る ● エミュレータ実装にCommon Lispが活きるか調べる ● エミュレータのCommon Lispにおける形を模索する ● 既存のゲームボーイ向けソフトウェアを動かす – Little Sound DJ: GBを作曲・演奏環境にする – 『星のカービィ2』…!!!!!!!

Slide 7

Slide 7 text

TTERY DOT MATRIX WITH STEREO SOUND 概説: ゲームボーイ (1) ● 1989年発売の携帯ゲーム機 ● 緑がかったモノクロ液晶、ピコピコ音源 ● みんなも知ってるあのソフト – 『星のカービィ』『ドクターマリオ』『ゼルダの伝説 夢を見る島』『ポケモン赤緑』 ● 後にいくつかのバリエーションがでる – モノクロ液晶: GBライト、GBポケット – カラー液晶: GBカラー – 次世代機: GBアドバンス

Slide 8

Slide 8 text

TTERY DOT MATRIX WITH STEREO SOUND 概説: ゲームボーイ (2) ● ハードウェア仕様 – CPU: Intel 8080に似たカスタムCPU – ROMカセット: 256Kb ~ 32Mb – RAM: 8Kb – Video RAM: 8Kb – メモリマップドI/O ● 本体に入ってるペリフェラル – モノクロLCD (4階調、160x144) – SSG(Software-controled Sound Generator)音源およびスピーカー – 入力関連デバイス (十字キーとかAボタンとか) ● ソフトウェアおよびカセット – 挿したカセットのROMはメモリの0x0000から0x7fffにマップされる

Slide 9

Slide 9 text

TTERY DOT MATRIX WITH STEREO SOUND ゲームボーイ関連リソース集 ● Pan Docs: https://gbdev.io/pandocs/ – GBのハードウェアの構成や仕様が書かれてる – エミュ開発するならまずは読みたい – ぼくの理解は基本PanDocsでできています ● Opcode table: https://www.pastraiser.com/cpu/gameboy/gameboy_opcodes.html – GBのCPUの命令テーブル – PanDocsの命令一覧は具体的なバイナリが書かれてないので ● Awesome List: https://gbdev.io/resources.html – GB関連キュレーションリスト – docリンクや開発ツール、GBのFLOSSなソフトなど ● Gb-docs-ja: https://github.com/pokemium/gb-docs-ja – いま見つけたけど日本語のドキュメントあった – Pan Docsあたりがベースっぽい

Slide 10

Slide 10 text

TTERY DOT MATRIX WITH STEREO SOUND エミュレータ: 基本 ● エミュレータ – ある計算機システムの動作を再現するソフトウェア ● 計算機システム – CPUとメモリと周辺機器(とバス) ● エミュレータ実装 – CPUがバスを通じてメモリや周辺機器と起こす相互作用をプログラムで再現 ● 相互作用のキーパーソンはCPU 1. CPUがメモリから命令を読む 2. CPUが命令に対応した処理を実行する 3. 1に戻る

Slide 11

Slide 11 text

TTERY DOT MATRIX WITH STEREO SOUND エミュレータ: 実装方針 ● まずCPUの動きを(不完全でも)実装する – あとメモリ ● 周辺機器(画面、音、入力)は後からやる – I/Oはメモリを介するのでCPUとメモリが必要 ● システムの起動プロセスは再現しなくてよい – ピローンはいらない ● 初めてなので愚直にやる – エミュレータ自作はじめてなので – 勘所まったくわからないので – 早すぎる最適化・抽象化をしない

Slide 12

Slide 12 text

TTERY DOT MATRIX WITH STEREO SOUND 実装日記 vol.1 (1) ● レジスタを定義する – 基本は16ビット – 8bit幅でも使える ● レジスタのアクセサを定義 – 全レジスタに同じことするのでマクロ書く ● https://github.com/t-sin/lambdaboy/commit/ 28d34885f3cb564386b1cb23bc34b43acdc99ef4 ;; レジスタ定義 (defstruct register (af 0 :type (unsigned-byte 16)) ... (sp 0 :type (unsigned-byte 16)) (pc 0 :type (unsigned-byte 16))) ;; レジスタアクセサをマクロで生成 (make-accessors af)

Slide 13

Slide 13 text

TTERY DOT MATRIX WITH STEREO SOUND 実装日記 vol.1 (2) ● メモリを定義する – メモリマップ ● 領域を二分木としたい ● でもまずは愚直に領域の配列とする – メモリアクセサ ● getter/setterをマクロで生やす ● https://github.com/t-sin/lambdaboy/commit/ 8b60adcfb4c2b0e1d3c0d6242d88266121c370d0 0x0000 ~ 0x3fff カセットROM #1 0x4000 ~ 0x7fff カセットROM #2 0x4000 ~ 0x7fff カセットROM #2 0x8000 ~ 0x9fff Video RAM メモリブロック メモリ全体 →メモリブロックの配列

Slide 14

Slide 14 text

TTERY DOT MATRIX WITH STEREO SOUND 実装日記 vol.1 (3) ● CPUを定義する ● 空のCPUループをつくる (defstruct gameboy (register (make-register) :type register) (memory (make-memory) :type memory)) (defun start (gb) (loop ;; (execute-1 gb) ← あとで実装する ))

Slide 15

Slide 15 text

TTERY DOT MATRIX WITH STEREO SOUND 実装日記 vol.1 (4) ● レジスタを初期化する – 電源ON後、どこにPCがセットされるか ● 起動時、PCは0x1000 – initialize-gameboy関数をつくって初期化しておく ● 1個の命令実行処理を書く – 命令データをメモリからフェッチ – 命令データをデコード – オペコードに合わせて分岐し実行 (defun execute-1 (gb) (let* ((b (fetch-byte gb))) (case b (<バイト値> 処理…) (t (error "unknown instruction: ~x" b)))))

Slide 16

Slide 16 text

TTERY DOT MATRIX WITH STEREO SOUND 実装日記 vol.1 (5) ● 「なにもしない」NOP命令の実装 – オペコード分岐のところに追記 ● NOPのみのプログラムを実行可能にする – 「メモリにプログラムを置く」 ● 単にメモリを0初期化しておく – CPUループで1命令実行関数を呼ぶ ● 無限ループ成功!! (defun execute-1 (gb) (let* ((b (fetch-byte gb))) (case b (#x00 (vom:debug "op: NOP") nil) (t (error "unknown instruction: ~x" b))))) (defun start (gb) (loop (execute-1 gb))) ; ←コメントアウトはずす

Slide 17

Slide 17 text

TTERY DOT MATRIX WITH STEREO SOUND 実装日記 vol.1 (6) ● ROMファイルを読み込む処理の実装 – ROMファイルを開く – 先頭からメモリに書き込む ● 適当なROMファイルをもってきて実行してみる – 動作確認用ROMとしてBlagg’s test ROMsを用いた ● https://github.com/retrio/gb-test-roms ● 命令の機能テストからハードウェアの挙動的なテストまで、いろいろカバーしてる ● /cpu_instrs/individual/06-ld r,r.gb: ld命令のみをテストするROM ● NOPしか実装してないのでエラーになる ● しかしながら実際のROMを実行できた!!!

Slide 18

Slide 18 text

TTERY DOT MATRIX WITH STEREO SOUND 実装日記 vol.1 (7) ● 命令実装をひたすら繰り返す: – テストROMを実行する – 未知の命令だよエラーがでたらその命令を実装する – 最初にもどる ● 命令を実行するたびにログを吐かせる – 無言だとなにが起こってるかわからぬ – [PC: 0xXX SP: 0xXX] op: 0xXX: OP REG のような感じ – 実行されてる感が得られテンション爆上げ

Slide 19

Slide 19 text

TTERY DOT MATRIX WITH STEREO SOUND 実装日記 vol.1 (8) ● そうやって実装していったところ… ● なんだか無限ループはしてる気がする – 対象テストROMは結果を画面に表示するっぽい ● ので正しい可能性がある ● 何を描画しているかわからないので描画内容を見たい – 描画のしくみ無知なので別の手で表示を確認したい

Slide 20

Slide 20 text

TTERY DOT MATRIX WITH STEREO SOUND 実装日記 vol.1 (9) ● テストROMはソースコードが公開されているので 実行ログとソースコードを突き合わせて違ってる箇所を探る – テストROMはアセンブリで書かれている – ありがとうc-lesson (https://karino2.github.io/c-lesson/) ● このあたりまではちゃんと実行されてそう – https://github.com/retrio/gb-test-roms/blob/master/cpu_instrs/source/common/runtime.s#L96 – 表示系サブルーチンのための初期化処理を呼ぶところ ● 文字表示サブルーチンへのCALLを捕捉してみる – 特定アドレスへCALLしたらオペランドの文字を(format t “…” ...)して確認 ● 特定アドレス: 文字表示コードの開始アドレス ● 実際は文字表示サブルーチンに飛んできてなかった…

Slide 21

Slide 21 text

TTERY DOT MATRIX WITH STEREO SOUND 実装日記 vol.1 (10) ● 実際は文字表示サブルーチンに飛んできてなかった… – なぜ… ● ところで全ての命令がログにでるので何がわるいかわからぬ – PCがある値になったら処理を実行 ● PCが表示初期化処理にきたらログをONにしたい – ブレークポイントとフック的なものを実装する – ところで最近gdbと戯れていますが、そういう機能ほしい ● 文字表示初期化処理をCALLした時点からログ表示を開始する – それ以前はログ無効

Slide 22

Slide 22 text

TTERY DOT MATRIX WITH STEREO SOUND 実装日記 vol.1 (11) ● 初めてのRET命令の戻り先が1byte違うことが原因 – テストROMのソースコードと実行ログを目diff – つまり正しい無限ループではなかった ● 分岐命令(JP, CALL, RET)でPCの扱いがよくないっぽい – PCなにもわからない(´・ω・`) ● 現在の実装の問題点: 命令の処理が関数になってない – オペコードごとに処理を重複して書いてた ● コピペミスやtypoで命令の本体がパラレルワールドになってた – PC進める量もついでに異なってたりした

Slide 23

Slide 23 text

TTERY DOT MATRIX WITH STEREO SOUND 実装日記 vol.1 (12) … 現在 ● 命令の本体を関数化 – 粛々とやっている – さらば、冗長クソデカecase式 ● やる気の低下 – 未実装の命令まだまだたっくさんある – よくわからぬ壊れかたするので地道 ● OpenGLでテクスチャ描画を試す気晴らし – ゲーム画面やウィンドウを表示するテスト – 最終的な画面状態をテクスチャに書き込んで描画する予定 – https://github.com/t-sin/lambdaboy/blob/05d3c43233e73488eef483beb32cd0879b425df0/ window.lisp

Slide 24

Slide 24 text

TTERY DOT MATRIX WITH STEREO SOUND 現時点での考察・反省 ● GOOD: ROMを実行して未知命令を実装する方法はよい – 実装済み命令が増えると先まで動くのできもちよい ● BAD: PCがどこを指すべきかは混乱があった – GBのCPUは1byteまたは2byteのため – 理解が不明瞭なまま進めたため分岐命令のバグを生んだ ● BAD: テスト書いてなかった – モチベの維持を最優先したためTDDはしなかった – TDDではないとしても命令を組み合わせたときの挙動はテストで確認しておくべき ● PROBLEM: デバッガほしいなァ… – ステップ実行、シンボル情報の利用、レジスタの内容表示… – GDBのTUIモードがすてきなので、そういうのほしい…

Slide 25

Slide 25 text

TTERY DOT MATRIX WITH STEREO SOUND 今後は ● 粛々と実装をすすめていく ● 描画まわりのドキュメント読む ● Common Lisp-wayを模索する – SLIMEによるデバッグやステップ実行? ● やる気を醸成していく – ちなみに: 発表資料を書くだけでも現状が整理されてだいぶモチベが復活してきた ● そしてvol.2へ…

Slide 26

Slide 26 text

TTERY DOT MATRIX WITH STEREO SOUND まとめ ● エミュレータ実装するときに必要な知識を述べた – くそ雑・計算機アーキテクチャ – ゲームボーイ自体の情報 – 実装に必要な・知っておくとよい情報 ● エミュレータ実装の現時点の進捗を述べた – https://github.com/t-sin/lambdaboy ● エミュレータ実装の今後の方針を考えた ● エミュレータ実装の雰囲気とライブ感を伝えた(かった) – つたわった? – 懇親会でツッコミや感想ください