ハードウェアの気になるあれこれ

技術的に興味のあることを調べて書いてくブログ。主にハードウェアがネタ。

ゲームボーイを作る(5) - テストROMの解析3

スポンサーリンク

ゲームボーイを作るその5。元に戻ってblargg-gb-testsのcpu_instrsの解析を続ける。

cpu_instrsテストの解析

前回のお試しビルドで、zipに同梱されている01-special.gbと自前でビルドしたバイナリの比較を行った。 LCDへの出力の文字列と思しき差分以外は見当たらなかったので、shell.inc内のいくつかあるdefineオプションは、オプション未指定時のbuild_rom.sという事で良さそう(と考えている)。

01-special.gbやそれ以降のテストを、ひととおり実行できるテスト環境を作りたいので、もう少し01-special.gbの細かい部分を調べておく。

リセット解除〜メインまでの動き

前回調べた部分も一部重複するけど、改めて流れを追っていく。 ひとまず、GameBoyのCPUの初期PCは$0100でここから処理が始まる。 ソースコード的にはbuild_rom.sが以下の部分。

; GB header read by bootrom
.org $100
     nop
     jp   reset

ここからリセット直後の初期化処理が行われるが、ジャンプ先のファイルはcommon/runtime.sになる。アドレス的には$0200からがruntime.sのコード。

.org $200 ;; アドレスオフセットを$200に設定


;;;; Shell
;; その直後にruntime.sのインクルード
.include "runtime.s"
.include "console.s"

runtime.sの先頭のコードは、ROM領域のデータをRAM領域にコピーするcopy_to_wram_then_runのコードになる。

copy_to_wram_then_run:
     ld   b,a

     ld   de,$C000
     ld   c,$10
-    ldi  a,(hl)
     ld   (de),a
     inc  e

この部分をデバッガでみると次のようになっており、上記の説明とも一致している。

f:id:diningyo-kpuku-jougeki:20210717224115j:plain
デバッガで見る$200のコード

build_rom.sjp resetruntime.s内の処理にジャンプしてきた後、上記に記載したcopy_to_wram_then_runを実行した後、$c000にジャンプする。

copy_to_wram_then_run:
     ld   b,a

     ld   de,$C000
     ld   c,$10
-    ldi  a,(hl)
     ld   (de),a
     inc  e
     jr   nz,-
     inc  d
     dec  c
     jr   nz,-

     ld   a,b
     jp   $C000 ;; $c000にジャンプ

.ifndef CUSTOM_RESET
     reset:
          ; Run code from $C000, as is done on devcart. This
          ; ensures minimal difference in how it behaves.
          ld   hl,$4000
          jp   copy_to_wram_then_run

     .bank 1 slot 1
     .org $0 ; otherwise wla pads with lots of zeroes
          jp   std_reset
.endif

このときのジャンプ先$c000は、.bank 1 slot 1とその次の行.org $0により、jp std_resetになる。 このジャンプ命令の直後には次のインクルードにより、各種処理のコードブロックが存在している。そのため、この部分を飛ばして基本のリセット処理を実行する。

          jp   std_reset
.endif

; 以下のブロックを飛ばして、std_resetへ。
; Common routines
.include "gb.inc"
.include "macros.inc"
.include "delay.s"
.include "crc.s"
.include "printing.s"
.include "numbers.s"
.include "testing.s"

; Sets up hardware and runs main
std_reset:

ここまでくれば、メインの実行まで後一歩。std_reset内では各ペリフェラルの初期化等を行った後、call mainにてメインのテスト処理が実行される。

; Sets up hardware and runs main
std_reset:

     ; Init hardware
     di
     ld   sp,std_stack

     ; Save DMG/CGB id
     ld   (gb_id),a

     ; Init hardware
     .ifndef BUILD_GBS
          wreg TAC,$00
          wreg IF,$00
          wreg IE,$00
     .endif

     wreg NR52,0    ; sound off
     wreg NR52,$80  ; sound on
     wreg NR51,$FF  ; mono
     wreg NR50,$77  ; volume

     ; TODO: clear all memory?

     ld   hl,std_print
     call init_printing
     call init_testing
     call init_runtime
     call reset_crc ; in case init_runtime prints anything

     delay_msec 250

     ; Run user code
     call main

     ; Default is to successful exit
     ld   a,0
     jp   exit

cpu_instrs(というかblargg-gb-tests)はかなり、実際のROMに近い処理が行われてて、初期化処理で周辺のデバイスの設定等も結構やってくれてるっぽい? これverilatorとかとうまく連携したら、周辺環境コミコミのテスト環境とか作れそう。ちょっと考えてみたいな。