Skip to content
Go back

AGB仕様メモ

· Updated:

AGBについて個人的にまとめたものです。内容には間違いが含まれている場合があります。

CPU

ARM7TDMI

  • ARMv4Tアーキテクチャ
  • 32ビットのRISC CPU
  • 16.78MHz = 約59.59ns/clks
  • 16つのレジスタを持つ
  • 6つのプロセッサモードを持つ
  • 2つのCPU状態を持つ
  • フェッチ、デコード、実行の3段のパイプラインを持つ

レジスタ

  • r0,…,r12:汎用用途のレジスタ
  • r13(SR):スタックポインタ。プロセッサモードに応じて既定値が異なる
    • USR:03007F00h
    • IRQ:03007FA0h
    • SVC:03007FE0h
  • r14(LR):リンクレジスタ。BL命令によって次の命令へのアドレスが格納される
  • r15(PC):プログラムカウンタ。CPUが3段のパイプラインを持つので、PCは2命令分だけ常に先行している
  • CPSR:現在のプログラムの実行状態を持つレジスタ
    • 0-4ビット目:プロセッサモード
    • 5ビット目:Thumbモードで動作しているか
    • 6ビット目:FIQが無効化されているか
    • 7ビット目:IRQが無効化されているか
    • 8-27ビット目:予約済み
    • 28ビット目:オーバーフロー
    • 29ビット目:キャリー
    • 30ビット目:ゼロ
    • 31ビット目:負
  • SPSR:各プロセッサモードのCPSRを保持するレジスタ

プロセッサモード

  • USR(ユーザー):プログラムを実行するための通常のモード
  • SYS(システム):OSのための特権ユーザーモード。AGBでは使われない
  • IRQ(割り込み):通常の割り込みを処理するモード
  • FIQ(高速割り込み):高速で低レイテンシな高速割り込みを処理するモード。AGBでは使われない
  • SVC(スーパーバイザーコール):OSのリソースにアクセスするためのモード。AGBではSWI命令を介してBIOSを呼び出すために使われる
  • ABT(アボート):メモリアクセスが正常に完了できなかったときに入る
  • UND(未定義):定義されていない命令が実行されたときに入る

CPU状態

  • 32ビット長の命令を実行するARM state
    • ✔ 1命令でできることが多い
    • ✔ すべてのレジスタが使える
    • ❌️ コードサイズが大きくなる
  • 16ビット長の命令を実行するThumb state
    • ✔ コードサイズが小さくなる
    • ❌️ 1命令でできることが少ない
    • ❌️ すべてのレジスタが使えない

メモリ

  • メモリの読み書きは固定で1クロックかかり、追加でメモリの特性に応じた待ち時間がかかる
  • 16ビット幅のメモリは32ビットの読み書き1回を16ビットの読み書き2回で行う
    • 待ち時間もその都度に挿入される
  • プログラムの格納場所は命令長とバス幅を合わせると最適なパフォーマンスを得られる
    • 1命令を1回のメモリアクセスで読み出せるようになるため
  • リトルエンディアンのみをサポートする
  • ディスプレイコントローラとCPUのメモリアクセスがかち合ったとき、CPUの方に待ち時間を挿入する
  • 各メモリは0#000000hから0#FFFFFFhまで有効な領域がミラーリングされる
    • 例えば、IWRAMは03FF8000hから03FFFFFFhまででもアクセスできる

System ROM

  • 00000000h-00003FFFh(16KB)
  • 16ビット幅
  • BIOSが格納される
  • 読み書き不可、実行可

EWRAM(External Work RAM)

  • 02000000h-0203FFFF(256KB)
  • 16ビット幅
  • 低速なオンボードメモリ
    • 2クロック分の待ち時間が挿入される
  • プログラムやデータを格納する
  • マルチブート時、ダウンロードしたプログラムが格納される

IWRAM(Internal Work RAM)

  • 03000000h-03007FFF(32KB)
  • 32ビット幅
  • 高速なオンチップメモリ
  • プログラムやデータを格納する

IO RAM

  • 04000000-040003FF(1KB)
  • 32ビット幅
  • 各種機能を制御するメモリマップドなIOレジスタ

Palette RAM

  • 05000000h-050003FF(1KB)
    • 05000000h-050001FFhをBGで使う
    • 05000200h-050003FFhをOBJで使う
  • 16ビット幅
  • 16ビットの色情報を格納する配列として扱う
  • 8ビット書き込み不可

VRAM

  • 06000000h-06017FFFh(96KB)
  • 16ビット幅
  • ビデオモードに応じてレイアウトが異なる
    • ビットマップのモードでは色情報または色番号を格納する
    • タイルベースのモードでは各BGのタイル情報とマップ情報を格納する
  • 8ビット書き込み不可

OAM(Object Attribute Memory)

  • 07000000h-070003FF(1KB)
  • 32ビット幅
  • OBJの制御情報を格納する
  • 8ビット書き込み不可

GamePak ROM

  • 08000000h-09FFFFFFh(最大32MB)
  • 16ビット幅
  • ゲームデータが格納される
  • WAITCNTの2-3ビット目の値に従って初回のアクセスに待ち時間が挿入される
  • WAITCNTの4ビット目の値に従って後続の連続アクセスに待ち時間が挿入される
  • 書き込み不可
  • 通常はこちらが使われる

GamePak ROM

  • 0A000000h-0BFFFFFFh(最大32MB)
  • 16ビット幅
  • ゲームデータが格納される
  • WAITCNTの5-6ビット目の値に従って初回のアクセスに待ち時間が挿入される
  • WAITCNTの7ビット目の値に従って後続の連続アクセスに待ち時間が挿入される
  • 書き込み不可

GamePak ROM

  • 0C000000h-0DFFFFFFh(最大32MB)
  • 16ビット幅
  • ゲームデータが格納される
  • WAITCNTの8-9ビット目の値に従って初回のアクセスに待ち時間が挿入される
  • WAITCNTの10ビット目の値に従って後続の連続アクセスに待ち時間が挿入される
  • 書き込み不可

GamePak RAM

  • 0E000000h-0E00FFFFh(最大64KB)
  • 8ビット幅
  • セーブデータを格納する
  • WAITCNTの0-1ビット目の値に従ってアクセスに待ち時間が挿入される
  • 8ビットの読み書き可

ディスプレイ

  • 240x160ピクセル
  • フレームあたり280896サイクル = 約59.73Hz
  • HDraw中はディスプレイコントローラがVRAM、OAM、Palette RAMを使っている
    • 基本的にVRAM、OAM、Palette RAMの書き換えはHBlank中かVBlank中に行う

タイミング

  • HDraw:1行分を更新するのにかかる時間
    • 960サイクル(240ピクセル分)かかる
  • HBlank:1行を更新し終わってから次の行の更新が始まるまでの空き時間
    • 272サイクル(68ピクセル分)かかる
  • スキャンライン:HDraw + HBlank = 1232サイクル
  • VDraw:1画面分を更新するのにかかる時間
    • 197120サイクル(160スキャンライン分)かかる
  • VBlank:1画面を更新し終わってから次の画面の更新が始まるまでの空き時間
    • 83776サイクル(68スキャンライン分)かかる

色フォーマット

  • 色情報は16ビットで、RGB各5ビットずつ、計32768色を表現できる
    • 0-4:赤
    • 5-9:緑
    • 10-14:青
    • 15:未使用
struct Color {
  uint16_t red : 5;
  uint16_t green : 5;
  uint16_t blue : 5;
  uint16_t _unused : 1;
};

パレット

  • パレットとはPalette RAMに格納された色情報の集合を指す
    • Palette RAMはBGとOBJそれぞれに対して256色の色情報を格納できる
  • 16色モードでは16色を持つ16つのパレットとしてデータを解釈する
  • 256色モードでは256色を持つ1つのパレットとしてデータを解釈する
// 256色モードのパレット
struct Palette256 {
  Color colors[256];
};

// 16色モードのパレット
struct Subpalette {
  Color colors[16];
};
struct Palette16 {
  Subpalette subpalettes[16];
};

固定小数点数

struct fixed28_t {
  uint32_t fraction : 8;  // 小数点以下の値
  uint32_t integer : 19;  // 整数部分の値
  uint32_t sign : 1;  // 符号
};

struct fixed16_t {
  uint16_t fraction : 8;  // 小数点以下の値
  uint16_t integer : 7;  // 整数部分の値
  uint16_t sign : 1;  // 符号
};

struct fixed32_t {
  uint32_t fraction : 8;  // 小数点以下の値
  uint32_t integer : 23;  // 整数部分の値
  uint32_t sign : 1;  // 符号
};

背景

ビデオモード

  • DISPCNTの0-2ビット目でビデオモードを指定する
    • 0, 1, 2 => タイルベース方式
    • 3, 4, 5 => はビットマップ方式

タイルベース

  • タイル単位で管理する
  • VRAMの先頭64KBを使う
  • タイル情報へのオフセットは16KB単位でBG#CNTの2-3ビット目に指定する
  • マップ情報へのオフセットは2KB単位でBG#CNTの8-12ビット目に指定する
モードレイヤタイル数備考
00,1,2,3102416,256スクロールできる
1---BG0とBG1をモード0として、BG2をモード2として表示する
22,3256256アフィン変換できる
タイル
  • 8x8ピクセルを1タイルとする
  • ピクセルあたりに1つの色番号を指定する
    • BG#CNTの7ビット目によってレイアウトが異なる
    • 16色モードの場合
      • 4ビットのパレット番号をマップ情報として指定する
      • 4ビットの色番号をタイル情報として指定する
    • 256色モードの場合
      • 8ビットの色番号をタイル情報として指定する
マップ
  • モードによってレイアウトが異なる
  • モード0の場合
    • エントリあたり2バイトの情報を用意する
      • 0-9:タイル番号(0から1023まで)
      • 10:水平方向に鏡写しにするか
      • 11:垂直方向に鏡写しにするか
      • 12-15:パレット番号
    • マップサイズはBG#CNTの14-15ビット目の値に従う
      • 0 => 32x32タイル(2KB)
      • 1 => 64x32タイル(4KB)
      • 2 => 32x64タイル(4KB)
      • 3 => 64x64タイル(8KB)
  • モード2の場合
    • エントリあたり1バイトの情報を用意する
      • 0-7:タイル番号(0から255まで)
    • マップサイズはBG#CNTの14-15ビット目の値に従う
      • 0 => 16x16タイル(256B)
      • 1 => 32x32タイル(1KB)
      • 2 => 64x64タイル(4KB)
      • 3 => 128x128タイル(16KB)

ビットマップ

  • ピクセル単位で管理する
  • VRAMの先頭80KBを使う
  • BG2のみ有効
  • アフィン変換できる
  • 表示に使うページはDISPCNTの4ビット目で指定する
モードピクセルサイズページ数
3240x160132768
4240x1602256
5160x128232768

スクロール

  • モード0のBGに対して適用される
  • 左上を(0, 0)として、BG#HOFSBG#VOFSの値だけ表示位置がオフセットされる

アフィン変換

  • モード2,3,4,5のBG2,3に対して適用される
  • BG#XおよびBG#Yは平行移動成分を指定する
    • 28ビットの符号付き固定小数点数で表現される
      • 0-7:小数点以下
      • 8-26:整数部
      • 27:符号
      • 28-31:未使用
  • BG#PABG#PBBG#PCBG#PDは変換行列の各成分を指定する
    • [ABCD]\left[ \begin{array}{cc} A & B \\ C & D \end{array} \right]
    • 16ビットの符号付き固定小数点数で表現される
      • 0-7:小数点以下
      • 8-14:整数部分
      • 15:符号

変換処理の詳細

  • コピー先座標はスキャンラインごとに各ピクセルの位置が計算される
    • 左上から右下へ向けて行優先で行われる
  • 対応するコピー元座標は変換行列の各成分に従って以下のように計算される
    • BG#XBG#Yを初期値とする
    • BG#PBBG#PDをスキャンラインごとの変化量として、コピー元座標の始点を計算する
    • BG#PABG#PCをピクセルごとの変化量として、ピクセルの位置を計算する
const int SCREEN_WIDTH = 240;
const int SCREEN_HEIGHT = 160;
for (int dst_y = 0; dst_y < SCREEN_HEIGHT; dst_y++) {
  int src_x = BG2X, src_y = BG2Y;
  for (int dst_x = 0; dst_x < SCREEN_WIDTH; dst_x++) {
    draw(src_x, src_y, dst_x, dst_y);
    src_x += BG2PA;
    src_y += BG2PC;
  }
  BG2X += BG2PB;
  BG2Y += BG2PD;
}
  • BG#XBG#Yはコピー元座標の始点位置を保持している(ように見える)
    • 実際には確認できないので、そのように解釈できるというだけの話
  • BG#XBG#YをHBlank中に書き換えると、それまでに累積した変位がリセットされる
    • 単に変換行列を入れ替えるということではない

まとめると

  • この変換処理はコピー元の視点を変化させると考えるのが分かりやすい
    • BG#XBG#Yによって平行移動した後、行列で変形する
    • その座標系から、1行240ピクセルを160行分だけディスプレイに表示する
  • 計算途中に介入できる手段があるという点で単なるアフィン変換とは異なる
    • その状態はBG#XBG#Yに保管されていると見て良い

オブジェクト

  • 各OBJは最大64x64ピクセルの大きさを取れる
  • 最大128つを一度に表示できる
  • 32つの変換行列を格納できる

属性

  • エントリあたり8バイトの情報を用意する
  • 7-8バイト目は変換行列の成分を順々に配置する
    • 例えば0番目の行列は07000006hにPA、0700000EhにPA、07000016hにPC、0700001EhにPDを格納する
struct ObjectAttribute {
  // attribute 0
  struct {
    uint16_t y : 8;  // Y座標
    uint16_t enable_transformation : 1;  // 変換するか
    uint16_t enable_double_size : 1;  // サイズを2倍にするか
    uint16_t mode : 2;  // 0:通常、1:半透明、2:ウィンドウ
    uint16_t enable_mosaic : 1;  // モザイクを有効化するか
    uint16_t palette_mode : 1;  // 0:16色、1:256色
    uint16_t shape : 2;  // 0:正方、1:横長、2:縦長
  };

  // attribute 1
  union {
    // 変換ありの場合
    struct {
      uint16_t x : 9;  // X座標
      uint16_t transformation_matrix_index : 5;  // 変換行列のインデックス
      uint16_t size : 2;  // shape = 0のとき、0:8x8、1:16x16、2:32x32、3:64x64
                          // shape = 1のとき、0:16x8、1:32x8、2:32x16、3:64x32
                          // shape = 2のとき、0:8x16、1:8x32、2:16x32、3:32x64
    };
    
    // 変換なしの場合
    struct {
      uint16_t x : 9;  // X座標
      uint16_t _unused_29 : 3;
      uint16_t enable_horizontal_flip : 1;  // 水平方向に鏡写しにするか
      uint16_t enable_vertical_flip : 1;  // 垂直方向に鏡写しにするか
      uint16_t size : 2;  // shape = 0のとき、0:8x8、1:16x16、2:32x32、3:64x64
                          // shape = 1のとき、0:16x8、1:32x8、2:32x16、3:64x32
                          // shape = 2のとき、0:8x16、1:8x32、2:16x32、3:32x64
    };
  };

  // attribute 2
  uint16_t tile_offset : 10;  // 06010000hからタイルへの32B単位のオフセット
                              // => 256色モードでは必ず偶数になる
                              //    ビットマップモードでは512から1023までの範囲になる
  uint16_t priority : 2;  // 0を最前面とする優先度
  uint16_t palette_index : 4;  // 16色モードの場合のパレット番号

  // attribute 3
  uint16_t transformation_matrix_element;  // 変換行列の成分
};

union TransformationMatrix {
  ObjectAttribute attributes[4];
  struct {
    uint16_t _attributes_pa[3];
    fixed16_t pa;
    uint16_t _attributes_pb[3];
    fixed16_t pb;
    uint16_t _attributes_pc[3];
    fixed16_t pc;
    uint16_t _attributes_pd[3];
    fixed16_t pd;
  };
};

タイル

  • 8x8ピクセルを1タイルとする
  • OBJに割り当てられるVRAM領域はビデオモードによって異なる
    • モード0,1,2では32KB
    • モード3,4,5では16KB
  • 加えて、扱う色数によって格納できるタイル数が決まる
    • モード3と256色モードで256タイルを格納できる
    • モード0と16色モードで1024タイルを格納できる
// 16色モードでのタイル
union ObjectTile16Row {
  uint32_t combined_pixels;
  struct {
    uint32_t _0 : 4;
    uint32_t _1 : 4;
    uint32_t _2 : 4;
    uint32_t _3 : 4;
    uint32_t _4 : 4;
    uint32_t _5 : 4;
    uint32_t _6 : 4;
    uint32_t _7 : 4;
  };
};
struct ObjectTile16 {
  ObjectTile16Row rows[8];
};

// 256色モードでのタイル
struct ObjectTile256Row {
  uint8_t pixels[8];
};
struct ObjectTile256 {
  ObjectTile256Row rows[8];
};

マッピング

  • OBJサイズが8x8より大きい時、後続のタイルのオフセットを自動で計算する
    • 列単位で見ると、線形に(16色モードなら1ずつ、256色モードなら2ずつ)増加する
    • 行単位で見ると、マッピング方式によって異なる
  • マッピング方式はDISPCNTの6ビット目で指定する
    • 2Dマッピング:各行ごとに32ずつ増加する
    • 1Dマッピング:各行ごとに1ずつ増加する
例:16色モード、4x4タイル(32x32ピクセル)の場合

2Dマッピング:

0123
32333435
64656667
96979899

1Dマッピング:

0123
4567
891011
12131415

効果

ウィンドウ

  • ウィンドウの内側または外側に表示するBGおよびOBJを制御できる

モザイク

  • 水平または垂直方向にピクセルを伝播させる機能を持つ
    • 左端または上端の色を何ピクセル先まで伝播させるかを制御できる

ブレンディング

  • アルファブレンディング、フェードイン、フェードアウトを行う機能を持つ
  • BGまたはOBJのうち、最前面であるものを第1ターゲットとし、それより後ろにある最も近いものを第2ターゲットとする
    • 対象となるBGやOBJはそれぞれ別に指定できる

アルファブレンディング

  • BLDCNTの6-7ビット目が1の場合
    • BLDALPHAの0-4ビット目の値(AA)に従って第1ターゲットに係数をかけて、BLDALPHAの8-12ビット目の値(BB)に従って第2ターゲットに係数をかけて、足し合わせる
    • I=min(31,I1(A/16)+I2(B/16))I = \min(31, I_1 * (A / 16) + I_2 * (B / 16))
  • ターゲットが2つ揃わない場合、ブレンディングは行われない

フェードイン・フェードアウト

  • BLDYの0-4ビット目の値(YY)に従って第1ターゲットに係数を掛け合わせる
    • BLDCNTの6-7ビット目が2の場合:I=(31I1)(Y/16)I = (31 - I_1) * (Y / 16)
    • BLDCNTの6-7ビット目が3の場合:I=I1(Y/16)I = I_1 * (Y / 16)

割り込み

  • 割り込みを有効化するには3段階の有効無効ビットをセットする必要がある
    • CPUの割り込み機能を無効化するにはCPSRの7ビット目を1にする
    • IMEの0ビット目に割り込み処理全体の有効無効を指定する
    • 各種割り込みの有効無効はIEと各レジスタで指定する
      • VBlank
        • IEの0ビット目
        • DISPSTATの3ビット目
      • HBlank
        • IEの1ビット目
        • DISPSTATの4ビット目
      • VCountの一致:処理するスキャンラインの行数がDISPSTATの8-15ビット目の値と一致するとき
        • IEの2ビット目
        • DISPSTATの5ビット目
      • Timer 0
        • IEの3ビット目
        • TM0CNTの6ビット目
      • Timer 1
        • IEの4ビット目
        • TM1CNTの6ビット目
      • Timer 2
        • IEの5ビット目
        • TM2CNTの6ビット目
      • Timer 3
        • IEの6ビット目
        • TM3CNTの6ビット目
      • シリアル通信の完了
        • IEの7ビット目
        • SCCNT_Lの14ビット目
      • DMA 0
        • IEの8ビット目
        • DMA0CNT_Hの14ビット目
      • DMA 1
        • IEの9ビット目
        • DMA1CNT_Hの14ビット目
      • DMA 2
        • IEの10ビット目
        • DMA2CNT_Hの14ビット目
      • DMA 3
        • IEの11ビット目
        • DMA3CNT_Hの14ビット目
      • Keypad
        • IEの12ビット目
        • KEYCNTの14ビット目にを指定する
      • GamePak(RTCやカードリーダなど)
        • IEの13ビット目
        • GamePakが指定するビット

割り込み処理

  • システムの割り込みハンドラのアドレスは03FFFFFChに格納する
    • ARMステートのコードであること
  • 発生した割り込みの種類はIFの各ビットから確認できる

割り込みへの応答

  • プログラムは割り込みを処理したことを伝える必要がある
    • ハードウェアに伝えるには、当該ビットを立ててIFに書き込む
    • そのビットは後にクリアされる
    • BIOSに伝えるには、3007FF8hにある割り込み確認フラグの当該ビットを立てる
    • 割り込みハンドラからリターンした後にクリアされる

ユーザーハンドラの呼び出し

  • 呼び出された時点でのレジスタ状態を保存しておく
    • r0、r1、r2、r3、r12、LR、SPSR
    • リターンする前に復元する
  • ハンドラの実行はシステムモードで行う
    • IRQモードのスタックを消費させないため
  • モード切り替えと一緒にIRQ割り込みを無効化する
    • 割り込みをネストさせたくない場合
  • ハンドラを呼び出す前にユーザーモードのLRをスタックに避難させる
    • ユーザーモードの状態を壊さないように
  • 制御が戻った後はIRQモードに戻してリターンする

DMA

  • 4つのDMAチャネルを持つ
    • 0が最も優先度が高い
    • 優先度が高いものから処理される
    • その他のチャネルは実行中のチャネルが完了するまで一時停止する
  • DMAコントローラとCPUはメモリバスを取り合うので排他的である

レジスタ

  • DMA#SADはコピー元のアドレスを入れる
  • DMA#DADはコピー先のアドレスを入れる
  • DMA#CNT_Lはワードの個数を入れる
    • ワードの大きさはDMA#CNT_Hの10ビット目に16ビットか32ビットかを指定する

内部状態

  • 実際の転送処理はレジスタとは別の内部状態を持つ
    • DMA#SADDMA#DADDMA#CNT_Lは初期値として用いる
    • DMA#CNT_Hの15ビット目を0から1に書き換えると内部状態を初期化する
  • リピートしない設定の場合、転送が完了するとDMAは自動的に無効化される
  • リピートする設定の場合、転送が完了してもDMAは有効化されたまま
    • スタート条件を満たすたび、転送を再開する

想定される使用用途

  • DMA0:HBlank中のデータ転送
    • HBlank中に制御情報を書き換えることでより柔軟な表現が可能になる
      • ラスタースクロール
      • スキャンラインごとにスクロール位置を変える
      • より高度なモード7表示
      • 途中でビデオモードを切り替える
      • 任意形状のウィンドウ
  • DMA1,2:サウンドのストリーミング
    • サウンドコントローラと協調動作する特別モードがある
  • DMA3:GamePak ROMのデータ転送
    • 唯一、ワードの個数を16ビットまで取れる(最大64KBを転送できる)

タイマー

  • タイマー0とタイマー1はサウンドのストリーミングを制御するのに使われる
  • TM#CNT_Lはカウンタの値を読み書きできる
    • 読み込む場合、現在のカウンタの値が返る
    • 書き込む場合、リロードで使われる初期値を設定できる
    • タイマーがオーバーフローしたり再開したりすると反映される
  • TM#CNT_Hの2ビット目を1にすると、1つ前のタイマーがオーバーフローしたときにカウントアップするようになる

サウンド

  • 内部処理はステレオ
    • イヤホンはステレオ
    • 本体スピーカーはモノラル
  • SOUNDCNT_Lでマスタ音量やチャネル1-4の制御を行う
  • SOUNDCNT_HでチャネルA,Bの制御を行う
  • SOUNDBIASで最終出力を調整する
    • 最終的なサンプリングレートが32.768kHzとなるよう調整する

チャネル1:矩形波

  • SOUND1CNT_LSOUND1CNT_HSOUND1CNT_Xで制御する
  • 131072/(2048X[0:10])131072 / (2048 - X[0:10])Hzを基本として(64H[0:5])/256(64 - H[0:5]) / 256秒だけ鳴らす
  • X[14]が1であれば、再生時間を超過するときにマスタレジスタをクリアする

スイープ

  • 周波数を段々と大きく/小さくする機能
  • L[4:6]/128KL[4:6] / 128Kミリ秒の間隔で周期を1±2L[0:2]1 \pm 2^{L[0:2]}倍する
  • L[3]で増減の符号を指定する
  • 周波数が0または最大値に達すると増減が止まる

エンベロープ

  • 音量をフェードイン/アウトさせる機能
  • H[8:10]/64H[8:10] / 64秒の間隔で音量を増減する
  • H[11]で増減の符号を指定する
  • H[12:15]で音量の初期値を指定する
    • 書き込んだ時には反映されず、リセット時に反映される
  • 音量が0または最大値に達すると増減が止まる

デューティー比

  • 矩形波が”オン”(ハイレベル)になっている時間割合を指定する機能
  • H[6:7]の値で指定する
    • 0 => 12.5%
    • 1 => 25%
    • 2 => 50%
    • 3 => 75%

チャネル2:矩形波

  • スイープできない以外はチャネル1と同じ
  • SOUND2CNT_LSOUND1CNT_Hと同様のレイアウトを取る
  • SOUND2CNT_HSOUND1CNT_Xと同様のレイアウトを取る

チャネル3:波形メモリ

  • 2097152/(2048X[0:10])2097152 / (2048 - X[0:10])HzのサンプルレートでH[0:7]/256H[0:7] / 256秒だけ再生する
  • 32サンプルの2バンクモードと64サンプルの1バンクモードがある
  • サンプルあたり4ビットの波形データを16バイトのWAVE_RAMに書き込む
    • 書き込み先のバンクはL[6]で指定する
  • 音量はH[13:15]の値に従って指定する
    • 0 => 0%
    • 1 => 100%
    • 2 => 50%
    • 3 => 25%

チャネル4:ノイズ

  • 7段または15段の線形帰還シフトレジスタ(Linear Feedback Shift Register; LFSR)
    • 段数はH[3]で指定する
  • (5242882(H[4:7]+1))/H[0:2](524288 * 2^{(H[4:7] + 1))} / H[0:2]Hzで(64L[0:5])/256(64 - L[0:5]) / 256秒だけ再生する
    • ただし、H[0:2]が0のときは代わりにH[0:2]=0.5H[0:2] = 0.5として計算する
  • エンベロープはL[8:10]/64L[8:10] / 64秒の間隔で増減する
  • H[14]を立てると、L[0:5]の分だけ再生した後に自動で停止する
  • 再生中にレジスタを書き換えても大丈夫
    • エンベロープの初期音量(H[12:15])や周波数の分割数(H[0:2])などを反映するにはH[15]を立てて初期化する必要がある

チャネルA,B:PCM

  • サンプルあたり8ビットの音声データをFIFOに書き込む
    • 一度に4サンプルずつを計32サンプルを書き込むことができる
  • タイマー割り込みかDMAを使ってサウンドエンジンのサンプル消費に合わせて書き込む
    • 基本的にはDMA1,2を使ってサンプルを書き込む
    • シリアル通信と併用したいときはタイマー割り込みで処理する

BIOS

  • BIOSにはシステムコールによって呼び出される関数が定義されている
  • 0xNN番の関数を呼び出す場合
    • ARMステートではswi 0xNN0000
    • Thumbステートではswi 0xNN
  • 引数の受け渡しにはr0r1r2r3を使う
  • 結果の受け渡しにはr0r1r3を使う
  • r2r4からr14は関数の実行によって変化しない

00h - SoftReset

  • ソフトリセット
  • AGBをリセットして80000000hから実行し直す?

01h - RegisterRamReset

  • 指定したメモリやレジスタをクリアする
  • 引数:
    • r0:フラグ
      • 0:EWRAM
      • 1:IWRAM(末尾512Bを除く)
      • 2:Palette RAM
      • 3:VRAM
      • 4:OAM
      • 5:SIOレジスタ
      • 6:サウンドレジスタ
      • 7:その他すべてのレジスタ

02h - Halt

  • 割り込みが発生するまでCPUを停止させる
    • CPU以外は生きたまま

03h - Stop

  • ハードウェアの大部分を割り込みが発生するまで停止させる
    • CPU、クロック、サウンド、ビデオ、SIOのシフトクロック、DMA、タイマーは停止する
    • キーパッド、GamePak、汎用用途SIOは生きている
  • “システムクロックが止まっているのでIFフラグはセットされない”

04h - IntrWait

  • 指定の割り込みが発生するまでCPUを停止させる
  • 発生した割り込みに応答するとき、3007FF8hの当該ビットを立てる
  • 引数:
    • r0:開始時に確認用フラグをクリアするか
    • r1:待機する割り込みの種別フラグ(IEIFと同じレイアウト)

05h - VBlankIntrWait

  • VBlank割り込みが発生するまでCPUを停止させる
  • r0 = 1r1 = 1swi 0x04を実行するのと同じ

06h - Div

  • 整数の除算を計算する
  • ゼロ除算は無限ループを引き起こす
  • 引数:
    • r0:分子(符号あり32ビット整数)
    • r1:分母(符号あり32ビット整数)
  • 戻り値:
    • r0:商(符号あり32ビット整数)
    • r2:剰余(符号あり32ビット整数)
    • r3:商の絶対値(符号なし32ビット整数)

07h - DivArm

  • 引数の順序が入れ替わっているDiv
  • Divより若干(3クロック)遅い

08h - Sqrt

  • 整数の平方根を計算する
  • 引数:
    • r0:xx(符号なし32ビット整数)
  • 戻り値:
    • r0:x\sqrt{x}(符号なし16ビット整数)

09h - ArcTan

  • アークタンジェントを計算する
  • 0x10 \le x \le 1の範囲で有効
  • 引数:
    • r0:xx(16ビット固定小数点数)
      • 0-13:小数点以下
      • 14:整数部分
      • 15:符号
  • 戻り値:
    • r0:ラジアン角(符号あり16ビット整数)
      • [32768,32768)[-32768, 32768)[π,π)[-\pi, \pi)にマッピングされる

0Ah - ArcTan2

  • arctan(y/x)\arctan(y/x)を計算する
  • 引数:
    • xx(符号あり32ビット整数)
    • yy(符号あり32ビット整数)
  • 戻り値:
    • r0:ラジアン角(符号あり16ビット整数)
      • [32768,32768)[-32768, 32768)[π,π)[-\pi, \pi)にマッピングされる

0Bh - CpuSet

  • 2バイトまたは4バイト単位でデータをコピーする
  • 引数:
    • r0:コピー元のアドレス
    • r1:コピー先のアドレス
    • r2:
      • 0-20:転送回数
      • 24:コピー元アドレスを固定するか
      • 26:転送単位サイズ(0 => 16ビット、1 => 32ビット)

0Ch - CpuFastSet

  • 4バイト単位でデータをコピーする
  • AGBでは32バイト単位に丸められる
  • 引数:
    • r0:コピー元のアドレス
    • r1:コピー先のアドレス
    • r2:
      • 0-20:4バイト単位の個数
      • 24:コピー元アドレスを固定するか

0Dh - BiosChecksum

  • BIOSのチェックサムを取得する
    • 32ビット単位で足し合わせる
    • GB系のBIOSは0xBAAE187Fを返す
    • DS系のBIOSは0xBAAE1880を返す
  • 戻り値:
    • r0:チェックサム

0Eh - BgAffineSet

  • BGの変換行列を計算する
  • 引数:
    • r0:入力データの配列へのポインタ
    • r1:出力データの配列へのポインタ
    • r2:配列の個数
struct Input {
  int32_t center_x;  // 中心のX座標(8ビットの小数部)
  int32_t center_y;  // 中心のY座標(8ビットの小数部)
  int16_t display_x;  // ディスプレイの中心のX座標
  int16_t display_y;  // ディスプレイの中心のY座標
  int16_t scaling_x;  // X方向の拡大率(8ビットの小数部)
  int16_t scaling_y;  // Y方向の拡大率(8ビットの小数部)
  uint16_t rotation;  // 回転角(0-65536が0-360にマッピングされる)
};

struct Output {
  int16_t pa;  // $a_{11}$
  int16_t pb;  // $a_{12}$
  int16_t pc;  // $a_{21}$
  int16_t pd;  // $a_{22}$
  int32_t x;  // X軸の平行移動成分
  int32_t y;  // Y軸の平行移動成分
};

0Fh - ObjAffineSet

  • OBJの変換行列を計算する
  • 引数:
    • r0:入力データの配列へのポインタ
    • r1:出力データの配列へのポインタ
    • r2:配列の個数
    • r3:ストライド(2=連続、8=OAM)
struct Input {
  int16_t scaling_x;  // X方向の拡大率(8ビットの小数部)
  int16_t scaling_y;  // Y方向の拡大率(8ビットの小数部)
  uint16_t rotation;  // 回転角(0-65535が0-360にマッピングされる)
};

struct OutputWithStride2 {
  int16_t pa;  // $a_{11}$
  int16_t pb;  // $a_{12}$
  int16_t pc;  // $a_{21}$
  int16_t pd;  // $a_{22}$
};

struct OutputWithStride8 {
  int16_t dummies0[3];
  int16_t pa;  // $a_{11}$
  int16_t dummies1[3];
  int16_t pb;  // $a_{12}$
  int16_t dummies_c[3];
  int16_t pc;  // $a_{21}$
  int16_t dummy_for_pd[3];
  int16_t pd;  // $a_{22}$
};

カートリッジ

ヘッダ

アドレスバイト数説明
00h4プログラムの始点へのジャンプ命令
04h156ロゴの圧縮済みビットマップ画像
A0h12ゲームタイトル
ACh4ゲームコード
B0h2メーカーコード
B2h1固定値(96h)
B3h1メインユニットコード
B4h1デバイスタイプ
B5h7予約済み
BCh1ソフトウェアバージョン
BDh1ヘッダのチェックサム
BEh2予約済み
  • ROMの先頭192バイトは実行可能な領域でありながらROMのヘッダ情報が格納される
  • エントリポイントである80000000hにはヘッダを避けるためのジャンプ命令がある

参考文献