Technical document (GB Emulator)

Technical document (GB Emulator)
Contact: gucchan@sfc.wide.ad.jp
Author: gucchan (B1, Keio University)
Jun Murai Lab., ARCH(Internet Architecture Research Team) @ Delta North.
英語の資料しかなかったのでとりあえず初代ゲームボーイに関するところだけ日本語でまとめた。
メモリマップ (CPU空間)
0x0000 ~ 0x3fff: (Ext.) ROM bank #0 (fixed, 0x0100 ~ 0x014f: カートリッジヘッダ)
0x4000 ~ 0x7fff: (Ext.) ROM bank #1..n (switchable, MBC(Memory Bank Controller))
0x8000 ~ 0x9fff: VRAM (0x8000 ~ 0x8fff, 0x8800 ~ 0x97ff: パターンテーブル)
0xa000 ~ 0xbeff: (Ext.) 外部RAM
0xc000 ~ 0xcfff Work RAM 0 (4KB)
0xd000 ~ 0xdfff: Work RAM 1 (4KB)
0xe000 ~ 0xfdff: 0xc000 ~ 0xddff間のミラーリング
0xfe00 ~ 0xfe9f: OAM(Object Attribute Memory)
0xfea0 ~ 0xfeff: 未使用
0xff00 ~ 0xff7f: I/Oポート用レジスタ
0xff80 ~ 0xfffe: HRAM(High RAM)
0xffff: 割り込みイネーブルレジスタ
jump vector
0000h, 0008h, 0010h, 0018h, 0020h, 0028h, 0030h, 0038h (RSTコマンド用)
0040h, 0048h, 0050h, 0058h, 0060h (割り込み用)
任天堂ロゴ16進ダンプ (location: 0x0104 ~ 0x0133)
CE ED 66 66 CC 0D 00 0B 03 73 00 83 00 0C 00 0D
00 08 11 1F 88 89 00 0E DC CC 6E E6 DD DD D9 99
BB BB 67 63 6E 0E EC CC DD DC 99 9F BB B9 33 3E
グラフィック
パレット (location: 0xff47)
Bit
Bit
Bit
Bit
7-6
5-4
3-2
1-0
-
Shade
Shade
Shade
Shade
for
for
for
for
Color
Color
Color
Color
Number
Number
Number
Number
3
2
1
0
0(0b00)=白, 1(0b01)=明るいグレー, 2(0b10)=暗いグレー, 3(0b11)=黒
スプライトDMA転送(location: 0xff46)
ROMやRAMの内容をOAMに転送するためにはここの領域にアドレスをセットする
ここのアドレスにはDMA転送対象のアドレスを0x100で割った結果、(つまり何が言いたいかというと上位1バイト)をロードする。
DMA転送自体は160msあたりで完了する(ゲームボーイColorは80ms程度らしい)
多くのプログラムはこのDMA転送をVBlank(垂直帰線時間)期間中に行う。
VRAMとパターンテーブル
VRAMの領域はメモリマップ中の 0x8000 ~ 0x97ff 中に存在しており、ここに192個のタイルが格納できるようになっている。それぞれのタイ
ルは8x8ピクセルの色の深さ段階が4段階のグレー(白,薄い灰色,グレー,黒)となっている。タイルは背景やスプライトなどに利用することができ
るが最前面のスプライトには3段階の色しか使えない(そのうちの1つであるColor-0は透明と定義されているため)
パターンテーブルはメモリマップ上の 0x8000 ~ 0x8fff, 0x8800 ~ 0x97ff に存在しており、最初のパターンテーブルはスプライトと背景の
ために利用され、番号は符号なし8-bit整数(0 ~ 255)。もう一つのパターンテーブルは背景とウィンドウ(背景の上位レイヤとして覆うもの)のた
めに利用され、符号付き8-bit整数(-128 ~ +127)で番号付けされる。パターンタイルは1個あたり16byteなので、 0x8000 ~ 0x8fff 上にあるパ
ターン#0は 0x8000, 0x8800 ~ 0x97ff に存在するパターン#0では、 0x8800 + (128 * 16) = 0x9000 となる。
Tile:
.33333..
22...22.
11...11.
2222222. <-- digits
33...33.
represent
22...22.
color
11...11.
numbers
........
Image:
.33333.. -> 01111100
01111100
22...22. -> 00000000
11000110
11...11. -> 11000110
00000000
2222222. -> 00000000
11111110
33...33. -> 11000110
11000110
22...22. -> 00000000
11000110
11...11. -> 11000110
00000000
........ -> 00000000
00000000
->
->
->
->
->
->
->
->
->
->
->
->
->
->
->
->
$7C
$7C
$00
$C6
$C6
$00
$00
$FE
$C6
$C6
$00
$C6
$C6
$00
$00
$00
1ラインは2byteで表現される。どの色なのかを取得するためには、
C(x,y) = ((m+(2*y)) & (1<<(7-x))) | ((m+(2*y)+1) & (1<<(7-x))<<8) (m:pattern table address)
という式で求められるということになる。
スプライトとOAM(Object Attribute Memory)
8x8, 8x16ピクセルまでの最大40個のスプライトを配置でき、ハードウェアの制約という観点の問題から1スキャンライン10個までしか同時にス
プライトを描画できない。
スプライトとして描画されるタイルはOAMメモリへと移動して管理されるようにしなければならない。
ただし8x16スプライトの時はスプライトパターン番号のLSBを無視し、0として扱わなければならない
1スプライトあたりのOAMの中身は次のようになっている。
Byte0 Byte1 Byte2 Byte3 Bit7
Y position
X position
Tile/Pattern Number
Attributes/Flags
OBJ-to-BG Priority (0=OBJ Above BG, 1=OBJ Behind BG color 1-3)
(Used for both BG and Window. BG color 0 is always behind OBJ)
Bit6
Y flip
(0=Normal, 1=Vertically mirrored)
Bit5
X flip
(0=Normal, 1=Horizontally mirrored)
Bit4
Palette number **Non CGB Mode Only** (0=OBP0, 1=OBP1)
Bit3
Tile VRAM-Bank **CGB Mode Only**
(0=Bank 0, 1=Bank 1)
Bit2-0 Palette number **CGB Mode Only**
(OBP0-7)
カートリッジヘッダ
ゲームボーイにはファミコンでいう.nesや.fds、DSという.ndsのようなエミュレータ界隈で発明された野良ファイルフォーマットが存在しな
い。理由はカートリッジのROM自体にヘッダが存在しているからである。便宜上.gbという拡張子を利用する。
0x0100 ~ 0x0103: エントリポイント
0x0104 ~ 0x0133: 任天堂ロゴ
0x0134 ~ 0x0143: ゲームタイトル
0x013f ~ 0x0142: 製造元コード
0x0143: CGBフラグ
0x0144 ~ 0x0145: ライセンスコード
0x0146: SGBフラグ
0x0148: ROMサイズ
0x0149: RAMサイズ
0x014a: Destination Code
0x014b: ライセンスコード(Old)
0x014c: マスクROMバージョン
0x014d: ヘッダチェックサム
0x014e ~ 0x014f: グローバルチェックサム
0x0100 ~ 0x0103: エントリポイント
任天堂ロゴが表示された後にこのエントリポイントにジャンプ命令が掛かり飛ばされてくる。大体この4byte領域にはNOP命令(=0x00)が格納さ
れており、その後にJP 0150hが実行される(これはいつも実行される訳ではない模様)
0x0104 ~ 0x0133: 任天堂ロゴ
ロゴのビットマップのダンプデータはメモリマップの項目の任天堂ロゴを参照のこと
ゲームボーイはこのロゴを本体が起動されると上から画面の中心部分までスクロールさせた後、ブートプロシージャはこのロゴのバイナリを自
身も保持しており、この値が一致することによってROMの内容を引き続き実行することができるようになっている。一致しなかった場合、ロゴ
が表示されたままフリーズすることになる。
面白いことに、ゲームボーイColor以外のゲームボーイシリーズではすべてのロゴデータ48byte分をチェックするのに対してゲームボーイColor
ではロゴデータの先頭24byteのみチェックを行う(ハードウェアスペックを向上させた分どっか節約させたかったのだろうか)
0x0134 ~ 0x0143: ゲームタイトル
大文字ASCIIで16文字のゲームタイトルが入る。
もしゲームタイトルが16文字より少なかった場合は0-paddingで埋められる。ここも16文字ではなくゲームボーイColorでは15文字になってい
る。
0x013f ~ 0x0142: 製造元コード
この部分は製造者コードとなっているがそれは比較的GB向けに製造された新しいカートリッジがこの領域に格納されており、多くの場合
0x0134 ~ 0x0143部分に格納できなかったゲームタイトルがそのまま入る模様
0x0143: CGB-Flag
この部分もゲームタイトル領域に利用されている。この部分の上位ビットはゲームボーイColor向けのカートリッジを起動する時に使われてお
り、もしそうでなければ非ゲームボーイColorモード(つまりカラーではないゲームボーイ向け)だということになる。ゲームボーイColor向けのモ
ードだった場合、次に二通りが存在する。
0x80: ROMがゲームボーイColorに対応しているが、カラー非対応でも動作するROM
0xc0: ROMがゲームボーイColor専用になっている
0x0144 ~ 0x045: ライセンスコード
2文字のASCIIコードで表現される。この値はどうやらゲーム内でゲーム製造元を表示するためにある値らしい。(製造元コードとは何だったの
か….)
ただしこの部分の領域がライセンスコードとして使われ始めたのはどうもゲームボーイSuperがリリースされた頃からのようでそれ以前の古い
ゲームは0x041bを代わりにライセンスコードとして利用していたようだ。
0x0146: SGB-Flag
ROMがゲームボーイSuperをサポートするかを決めているフラグ。値としては二種類。こちらは下位ニブルに対してのフラグとなっている。
0x00: ゲームボーイSuper用の機能がROMに存在しない
0x03: ROMがゲームボーイSuper向けの機能をサポートしている
0x0147: カートリッジタイプ
どのMBC (Memory Bank Controller)がカートリッジで利用されているかを示すフラグ。
00h
01h
02h
03h
05h
06h
08h
09h
0Bh
0Ch
0Dh
0Fh
10h
11h
12h
NROM (ROM ONLY)
MBC1
MBC1+RAM
MBC1+RAM+BATTERY
MBC2
MBC2+BATTERY
ROM+RAM
ROM+RAM+BATTERY
MMM01
MMM01+RAM
MMM01+RAM+BATTERY
MBC3+TIMER+BATTERY
MBC3+TIMER+RAM+BATTERY
MBC3
MBC3+RAM
13h
15h
16h
17h
19h
1Ah
1Bh
1Ch
1Dh
1Eh
FCh
FDh
FEh
FFh
MBC3+RAM+BATTERY
MBC4
MBC4+RAM
MBC4+RAM+BATTERY
MBC5
MBC5+RAM
MBC5+RAM+BATTERY
MBC5+RUMBLE
MBC5+RUMBLE+RAM
MBC5+RUMBLE+RAM+BATTERY
POCKET CAMERA
BANDAI TAMA5
HuC3
HuC1+RAM+BATTERY
0x0148: ROMサイズ
カートリッジのROMサイズを示す領域。 下の図のアドレスの遷移に規則性があることから
0x7fff << *((uint16_t *)0x0148) という式でサイズを算出できる。
00h
01h
02h
03h
04h
05h
06h
07h
52h
53h
54h
-
32KByte
64KByte
128KByte
256KByte
512KByte
1MByte
2MByte
4MByte
1.1MByte
1.2MByte
1.5MByte
(no ROM banking)
(4 banks)
(8 banks)
(16 banks)
(32 banks)
(64 banks) - only 63 banks used by MBC1
(128 banks) - only 125 banks used by MBC1
(256 banks)
(72 banks)
(80 banks)
(96 banks)
0x0149: RAMサイズ
外部RAM(カートリッジ)のサイズを決めている領域
ただ、MBC2を利用しているカートリッジは512 x 4bitのRAMを内部で保持しているにも関わらず 00h指定しなければならない
00h
01h
02h
03h
-
None
2 KBytes
8 Kbytes
32 KBytes (4 banks of 8KBytes each)
0x014a: Destination Code (なんて訳して良いか分からない)
このゲームが日本国内で販売されたかどうかを表すフラグ
00h - Japanese
01h - Non-Japanese
0x014b: ライセンスコード(Old)
256通りのうちで製造元のコードが記述されている。特にこの領域に0x33が埋められている場合は新しいライセンスコードが上のほうで示した
0x0144 ~ 0x0145の2byte領域中に存在していることを示している
0x014c: マスクROMヘッダバージョン : このゲームのマスクROMのバージョンを表している
0x014d: ヘッダーチェックサム
0x0134(タイトル)~ 0x014c(マスクROMヘッダバージョン)までのチェックサムが含まれている。
チェックサムの算出方法は以下。
x=0:FOR i=0134h TO 014Ch:x=x-MEM[i]-1:NEXT
C++に直すとこうなるのかな、xがチェックサム。この値の下位8bitがこの領域に示されているチェックサムと一致しなければゲームが動作しな
いようになっている。
uint16_t checksum() {
uint16_t x = 0;
for(uint16_t n = 0x0134; n <= 0x014c; n++) x = x - *((uint16_t *)n) - 1;
return x;
}
0x014e ~ 0x014f: グローバルチェックサム
ROM全体のチェックサムが格納されている(ただしリトルエンディアンなのでバイトオーダーに気を付けること)
ただこの領域はゲームボーイはチェックすることはない。
起動時シーケンス
ゲームボーイを起動するとメモリ番地0から始まる0x0000 ~ 0x00ffまでの256byteのプログラムをロードする。このプログラムはROMの中に格
納されており、最初の処理はカートリッジの0x0104 ~ 0x0133までの任天堂ロゴをスクロールしながら中央部分まで描画を続けること。その次
の処理でカートリッジ内のROMに格納されている任天堂ロゴと内部RAMにある任天堂ロゴを比較し、このバイナリが完全に一致するかどうか
をチェックする。完全に一致しなければゲームボーイはそのまま動作を停止する。
すべて等しいことを確認すると、0x0134 ~ 0x014dまでの領域をすべて足し、この総和に25を足してLSBをチェックする。LSBが0ではない場
合、ゲームボーイは実行を停止する。
0であった場合は、内部ROMが無効になり、0x0100(ブートエントリポイント)の値がpcレジスタにロードされる。
起動チェック完了後の初期値は次のようになっているが、この値が常に存在しているという信頼はできないため、リセット時に常にセットする
のが望ましい。
AF=$01B0
BC=$0013
DE=$00D8
HL=$014D
Stack Pointer=$FFFE
[$FF05] = $00
; TIMA
[$FF06] = $00
; TMA
[$FF07] = $00
; TAC
[$FF10] = $80
; NR10
[$FF11] = $BF
; NR11
[$FF12] = $F3
; NR12
[$FF14] = $BF
; NR14
[$FF16] = $3F
; NR21
[$FF17] = $00
; NR22
[$FF19] = $BF
; NR24
[$FF1A] = $7F
; NR30
[$FF1B] = $FF
; NR31
[$FF1C] = $9F
; NR32
[$FF1E] = $BF
; NR33
[$FF20] = $FF
; NR41
[$FF21] = $00
; NR42
[$FF22] = $00
; NR43
[$FF23] = $BF
; NR30
[$FF24] = $77
; NR50
[$FF25] = $F3
; NR51
[$FF26] = $F1-GB, $F0-SGB ; NR52
[$FF40] = $91
; LCDC
[$FF42] = $00
; SCY
[$FF43] = $00
; SCX
[$FF45] = $00
; LYC
[$FF47] = $FC
; BGP
[$FF48] = $FF
; OBP0
[$FF49] = $FF
; OBP1
[$FF4A] = $00
; WY
[$FF4B] = $00
; WX
[$FFFF] = $00
; IE
割り込み
IME (Interrupt Master Enable Flag (W/-))
0の時すべての割り込みを無効に、
1のときすべての割り込みを有効にするためのレジスタ(IE(Interrupt Enable)=0xfffに変更)
IE (Interrupt Enable (W/R))
Bit
Bit
Bit
Bit
Bit
0:
1:
2:
3:
4:
V-Blank
LCD STAT
Timer
Serial
Joypad
Interrupt
Interrupt
Interrupt
Interrupt
Interrupt
IF (Interrupt Flag (W/R))
Enable
Enable
Enable
Enable
Enable
(INT
(INT
(INT
(INT
(INT
40h)
48h)
50h)
58h)
60h)
(1=Enable)
(1=Enable)
(1=Enable)
(1=Enable)
(1=Enable)
IRQフラグ
Bit
Bit
Bit
Bit
Bit
0:
1:
2:
3:
4:
V-Blank
LCD STAT
Timer
Serial
Joypad
Interrupt
Interrupt
Interrupt
Interrupt
Interrupt
Request
Request
Request
Request
Request
(INT
(INT
(INT
(INT
(INT
40h)
48h)
50h)
58h)
60h)
(1=Request)
(1=Request)
(1=Request)
(1=Request)
(1=Request)
割り込みが実行されるとIFの内容は自動的にCPUによってリセットされるべきで、IMEも0に戻される。IMEの値はまた次に実行される命令で
RETIなどの命令を利用してIMEをイネーブルにしないといけない。
そして、上に示した割り込みベクタにPCの値を飛ばさなければならない。もし、IFに複数のビットがセットされた場合、それはIF,IEのビット0
から順番で優先度が高いと判断されるため、一番優先度が高いのはVBlank(垂直帰線時間に突入したことを示す割り込み)であり、一番優先度の
低い命令はジョイパッドからの入力ということになる。
タイマー
ゲームボーイでは、4096(4.096kHz),16384(16.384kHz), 65536(65.536kHz),262144(262.144kHz)の4種類の中から周波数を選択した上でのタイマ
ー割り込みが可能。
この周波数ごとにI/OレジスタのTIMA(カウンターレジスタ,)に値がインクリメントされていき、最終的にTIMAがオーバーフローした時に割り込
みが発生し、TMAレジスタにロードされた値で割られる。
TIMA, TMAレジスタの詳細を見ていく
0xff07(-----ti): TAC (Timer control register) (r/w)
下位3bitのみがTACレジスタとして利用される。
t(bit 2): タイマー ストップ(0)/スタート(1)
i(bit 0-1): タイマー割り込み周波数 (0b00=4.096kHz, 0b01=262.144kHz, 0b10=65.536kHz, 0b11=16.384kHz)
0xff05(xxxxxxxx): TIMA (Timer counter register) (r/w)
TACレジスタで設定したタイマ割り込み周波数値ごとに自身の値がインクリメントされていく。TIMAがオーバーフローした時に割り込みが起こ
る。
0xff06(xxxxxxxx): TMA (Timer Modulo register) (r/w)
このレジスタにロードされた値がTIMAがオーバーフローした時にロードされる
ld
ld
ld
ld
a,1
($ff06), a ; TMAに1を設定
a, 4
($ff07), a ; TACに4(0b00000100)を設定,t=1,0b00=4.096kHz.
TIMA=1, TAC=4なので, 1秒間に4096回割り込みが発生することになる。
ld
ld
ld
ld
a, 4
($ff06), a ; TMAに4を設定
a, 5 ;
($ff07), a ; TACに5(0b00000101)を設定,t=1,0b01=262.144kHz.
TIMA=1, TAC=5なので、262144 / TIMA = 262144 / 4 = 65536回一秒間に割り込みが発生することになる。
参考資料
GameBoy CPU Manual
http://marc.rawer.de/Gameboy/Docs/GBCPUman.pdf
GameBoy Technical Specifications
http://problemkaputt.de/pandocs.htm
http://www.geocities.co.jp/Playtown/2004/gmbspecj.txt