REFORGE

2001 / PUBLIC DOSSIER

Game Boy Advance

A chaptered hardware reference with research coverage, visible citation status, and a separate production record.

CHAPTERS
13
PRODUCTION RECORD
NOT STARTED
SOURCE STATUS
EXTERNAL REFERENCES LINKED

The Game Boy Advance is a 16.78 MHz ARM7TDMI system with a 3-stage pipeline, 96 KB of VRAM, 6 display modes, and a hybrid audio subsystem combining legacy Game Boy PSG channels with dual 8-bit PCM DMA streams. Every register address, bit field, cycle count, and behavioral quirk documented below targets the implementation needs of a static recompilation engine. The ARM7TDMI executes both 32-bit ARM and 16-bit THUMB instruction sets, with the vast majority of GBA game code running in THUMB from the 16-bit ROM bus for bandwidth efficiency.

CHAPTER 01

ARM7TDMI core at 2²⁴ Hz

The CPU clock is exactly 16,777,216 Hz (2²⁴). The ARM7TDMI implements the ARMv4T architecture with a 3-stage pipeline (fetch → decode → execute). Due to prefetch, reading PC yields current instruction address + 8 in ARM state and + 4 in THUMB state. Any taken branch flushes the pipeline at a cost of 2S+1N cycles.

Register file and processor modes

The processor exposes 16 registers (R0–R15) at any time, drawn from 37 physical registers (31 GPRs + 6 status registers). R13 serves as SP by convention, R14 is LR (link register), and R15 is PC.

CPSR layout (32 bits):

Bit(s)FieldDescription
31NNegative / sign flag
30ZZero flag
29CCarry flag
28VOverflow flag
27–8Reserved
7IIRQ disable (1 = masked)
6FFIQ disable (1 = masked)
5TState bit (0 = ARM, 1 = THUMB)
4–0M[4:0]Processor mode

Processor modes and banked registers:

ModeM[4:0]HexBanked registers
User100000x10(none)
FIQ100010x11R8\_fiq–R14\_fiq, SPSR\_fiq
IRQ100100x12R13\_irq, R14\_irq, SPSR\_irq
Supervisor100110x13R13\_svc, R14\_svc, SPSR\_svc
Abort101110x17R13\_abt, R14\_abt, SPSR\_abt
Undefined110110x1BR13\_und, R14\_und, SPSR\_und
System111110x1F(shares User registers)

FIQ uniquely banks R8–R12 in addition to R13–R14, giving it 7 private registers for fast interrupt handling. Each exception mode except User/System has a dedicated SPSR that saves CPSR on entry.

Exception vectors:

AddressExceptionEntry mode
$00000000ResetSVC
$00000004Undefined InstructionUND
$00000008SWISVC
$0000000CPrefetch AbortABT
$00000010Data AbortABT
$00000018IRQIRQ
$0000001CFIQFIQ

On exception entry the CPU saves CPSR → SPSR\_mode, sets mode bits, forces T=0 (ARM state), sets I=1 (disables IRQ; FIQ additionally sets F=1), and jumps to the vector.

CHAPTER 02

ARM instruction set (32-bit)

Every ARM instruction is conditionally executed. Bits 31–28 encode one of 16 conditions evaluated against CPSR flags:

CodeMnemonicCondition
0000EQZ=1
0001NEZ=0
0010CS/HSC=1
0011CC/LOC=0
0100MIN=1
0101PLN=0
0110VSV=1
0111VCV=0
1000HIC=1 ∧ Z=0
1001LSC=0 ∨ Z=1
1010GEN=V
1011LTN≠V
1100GTZ=0 ∧ N=V
1101LEZ=1 ∨ N≠V
1110ALAlways
1111NVNever (reserved, unpredictable)

A failed condition costs 1S cycle with no execution.

Data processing encoding

31-28  27-26  25  24-21    20  19-16  15-12  11-0
cond   0 0    I   opcode   S   Rn     Rd     operand2

The 16 opcodes in bits 24–21

BitsOpOperationFlags (if S=1)
0000ANDRd = Rn AND Op2N,Z,C
0001EORRd = Rn XOR Op2N,Z,C
0010SUBRd = Rn − Op2N,Z,C,V
0011RSBRd = Op2 − RnN,Z,C,V
0100ADDRd = Rn + Op2N,Z,C,V
0101ADCRd = Rn + Op2 + CN,Z,C,V
0110SBCRd = Rn − Op2 − NOT(C)N,Z,C,V
0111RSCRd = Op2 − Rn − NOT(C)N,Z,C,V
1000TSTtest Rn AND Op2N,Z,C
1001TEQtest Rn XOR Op2N,Z,C
1010CMPtest Rn − Op2N,Z,C,V
1011CMNtest Rn + Op2N,Z,C,V
1100ORRRd = Rn OR Op2N,Z,C
1101MOVRd = Op2N,Z,C
1110BICRd = Rn AND NOT Op2N,Z,C
1111MVNRd = NOT Op2N,Z,C

TST/TEQ/CMP/CMN always have S=1 and do not write Rd. If S=1 and Rd=R15, then CPSR ← SPSR (exception return).

Operand 2 when I=1 (immediate)bits 11–8 = rotate_imm, bits 7–0 = imm_8. Value = imm_8 ROR (rotate_imm × 2). When I=0 (register): bits 3–0 = Rm, bits 6–5 = shift type, bit 4 selects shift-by-immediate (0) vs shift-by-register (1).

Barrel shifter operations and special cases

Bits 6–5OperationShift-by-0 special
00LSLNo shift, C unchanged
01LSREncodes LSR #32 (result=0, C=Rm[31])
10ASREncodes ASR #32 (result=sign-fill, C=Rm[31])
11ROREncodes RRX (33-bit rotate right through carry: C→bit31, bit0→C)

Shift-by-register uses Rs[7:0]. If Rs=0, result=Rm and C is unchanged. Register-specified shifts add +1I cycle.

Multiply instructions

All multiplies use the encoding pattern cond 0000_xxAS with bits 7–4 = 1001.

InstructionBits 23–21OperationCycles
MUL000Rd = Rm × Rs1S + mI
MLA001Rd = Rm × Rs + Rn1S + (m+1)I
UMULL100RdHi:RdLo = Rm × Rs (unsigned)1S + (m+1)I
UMLAL101RdHi:RdLo += Rm × Rs (unsigned)1S + (m+2)I
SMULL110RdHi:RdLo = Rm × Rs (signed)1S + (m+1)I
SMLAL111RdHi:RdLo += Rm × Rs (signed)1S + (m+2)I

The multiplier cycle count m depends on Rs leading bits: m=1 if bits 31–8 are all 0s or all 1s, m=2 if 31–16, m=3 if 31–24, m=4 otherwise. Restrictions: Rd ≠ Rm, R15 not allowed. Flags (S=1): N and Z set, C is destroyed on ARMv4, V unaffected.

Branch, load/store, block transfer

B/BL: cond 101L imm24. L=1 sets LR to return address. PC += sign_extend(imm24) << 2. Range ±32 MB. 2S+1N cycles.

BX: cond 0001_0010_1111_1111_1111_0001 Rn. PC = Rn & ~1. T flag = Rn[0]. This is the ARM/THUMB interworking instruction. 2S+1N cycles.

LDR/STR: cond 01 I P U B W L Rn Rd offset. I=0 for 12-bit immediate offset, I=1 for shifted register. P=pre/post, U=add/subtract, B=byte, W=writeback, L=load/store. LDR costs 1S+1N+1I (add 1S+1N if Rd=PC). STR costs 2N.

LDRH/STRH/LDRSB/LDRSH: cond 000 PUIW L Rn Rd offset_hi 1SH1 offset_lo. S,H bits select: 01=LDRH/STRH, 10=LDRSB, 11=LDRSH. I=1 for 8-bit immediate (split across fields), I=0 for register offset.

LDM/STM: cond 100 PUSW L Rn register_list. P,U = {IA,IB,DA,DB}. S=1 with R15 loaded → CPSR←SPSR. S=1 without R15 → user bank transfer. LDM: nS+1N+1I (n = register count; +1S+1N if PC loaded). STM: (n−1)S+2N.

SWP/SWPB: cond 00010 B 00 Rn Rd SBZ 1001 Rm. Atomic read-modify-write. 1S+2N+1I.

MRS: cond 00010 R 001111 Rd 000000000000. R=0→CPSR, R=1→SPSR. 1S.

MSR: Register form or immediate form. field_mask bits 19–16 select which CPSR/SPSR fields to write (c=control, x=extension, s=status, f=flags). 1S.

SWI: cond 1111 imm24. Enters SVC mode, ARM state, IRQ disabled, PC → $00000008. 2S+1N.

Cycle timing summary

InstructionCycles
Data processing (no shift-by-register)1S
Data processing (shift-by-register)1S + 1I
Data processing (Rd=PC)2S + 1N
MRS, MSR1S
B, BL, BX2S + 1N
LDR1S + 1N + 1I
LDR (Rd=PC)2S + 2N + 1I
STR2N
LDM (n regs)nS + 1N + 1I
STM (n regs)(n−1)S + 2N
SWP1S + 2N + 1I
MUL1S + mI
MLA1S + (m+1)I
MULL1S + (m+1)I
MLAL1S + (m+2)I
SWI2S + 1N
Condition false1S

S (sequential), N (non-sequential), and I (internal/no-bus) cycles map to actual clock ticks based on the memory region's wait-state configuration.

CHAPTER 03

THUMB instruction set (16-bit)

THUMB encodes a useful subset of ARM operations in 16-bit opcodes. Only R0–R7 ("Lo" registers) are freely accessible; R8–R15 require Format 5 hi-register operations. Below are all 19 formats with exact bit encodings.

Format 1 — Move shifted register: 000 Op[1:0] Offset5[4:0] Rs[2:0] Rd[2:0]. Op: 00=LSL, 01=LSR, 10=ASR. LSL#0 = MOV. LSR#0 encodes LSR#32. ASR#0 encodes ASR#32. Sets N,Z,C.

Format 2 — Add/subtract: 00011 I Op Rn/nn3[2:0] Rs[2:0] Rd[2:0]. I=0 register, I=1 3-bit immediate. Op: 0=ADD, 1=SUB. Sets N,Z,C,V.

Format 3 — Move/compare/add/sub immediate: 001 Op[1:0] Rd[2:0] Offset8[7:0]. Op: 00=MOV, 01=CMP, 10=ADD, 11=SUB. 8-bit unsigned immediate.

Format 4 — ALU operations: 010000 Op[3:0] Rs[2:0] Rd[2:0]. All 16 ALU ops

OpMnemonicOperation
0000ANDRd &= Rs
0001EORRd ^= Rs
0010LSLRd <<= Rs
0011LSRRd >>= Rs (logical)
0100ASRRd >>= Rs (arithmetic)
0101ADCRd += Rs + C
0110SBCRd -= Rs + NOT(C)
0111RORRd = Rd ROR Rs
1000TSTtest Rd AND Rs
1001NEGRd = 0 − Rs
1010CMPtest Rd − Rs
1011CMNtest Rd + Rs
1100ORRRd= Rs
1101MULRd *= Rs
1110BICRd &= ~Rs
1111MVNRd = ~Rs

Format 5 — Hi register ops / BX: 010001 Op[1:0] H1 H2 Rs[2:0] Rd[2:0]. Op: 00=ADD, 01=CMP, 10=MOV, 11=BX. H1/H2 extend Rs/Rd to 4-bit (accessing R8–R15). Only CMP sets flags. BX uses Rm[0] to select ARM (0) or THUMB (1) state.

Format 6 — PC-relative load: 01001 Rd[2:0] Word8[7:0]. Address = (PC & ~2) + (Word8 << 2). Loads 32-bit word. Max offset 1020 bytes.

Format 7 — Load/store register offset: 0101 L B 0 Ro[2:0] Rb[2:0] Rd[2:0]. Address = Rb + Ro.

Format 8 — Load/store sign-extended: 0101 H S 1 Ro[2:0] Rb[2:0] Rd[2:0]. S|H: 00=STRH, 01=LDRH, 10=LDSB, 11=LDSH.

Format 9 — Load/store immediate offset: 011 B L Offset5[4:0] Rb[2:0] Rd[2:0]. Word: offset = Offset5 << 2. Byte: offset = Offset5.

Format 10 — Load/store halfword: 1000 L Offset5[4:0] Rb[2:0] Rd[2:0]. Offset = Offset5 << 1.

Format 11 — SP-relative load/store: 1001 L Rd[2:0] Word8[7:0]. Address = SP + (Word8 << 2).

Format 12 — Load address: 1010 SP Rd[2:0] Word8[7:0]. SP=0: Rd = (PC & ~2) + (Word8 << 2). SP=1: Rd = SP + (Word8 << 2). Flags unaffected.

Format 13 — Add offset to SP: 10110000 S SWord7[6:0]. S=0: SP += SWord7 << 2. S=1: SP -= SWord7 << 2.

Format 14 — Push/pop: 1011 L 10 R Rlist[7:0]. PUSH = STMDB SP!,{Rlist,LR if R=1}. POP = LDMIA SP!,{Rlist,PC if R=1}. Full descending stack.

Format 15 — Multiple load/store: 1100 L Rb[2:0] Rlist[7:0]. STMIA/LDMIA Rb!, {Rlist}. Always increment-after with writeback.

Format 16 — Conditional branch: 1101 Cond[3:0] SOffset8[7:0]. Target = PC + sign_extend(SOffset8) << 1. Range −256 to +254 bytes. Same condition codes as ARM.

Format 17 — SWI: 11011111 Value8[7:0]. Enters SVC/ARM mode, PC → $00000008.

Format 18 — Unconditional branch: 11100 Offset11[10:0]. Target = PC + sign_extend(Offset11) << 1. Range ±2048 bytes.

Format 19 — Long branch with link (BL): Two-instruction pair. First: 11110 OffsetHigh[10:0] → LR = PC + (sign_extend(OffsetHigh) << 12). Second: 11111 OffsetLow[10:0] → temp = next_instr | 1; PC = LR + (OffsetLow << 1); LR = temp. Total range ±4 MB.

Critical ARMv4T interworking note: On the GBA's ARMv4T, LDR PC, POP {PC}, and MOV PC, Rn in THUMB do not interwork — bit 0 is ignored (no state switch). Only BX performs interworking. ARMv5T+ changed this behavior, so recompilers must enforce v4T semantics.

CHAPTER 04

Memory map and bus architecture

RegionAddressSizeBusAccess timing (N/S cycles)
BIOS$00000000–$00003FFF16 KB32-bit1/1/1 (8/16/32)
EWRAM$02000000–$0203FFFF256 KB16-bit3/3/6
IWRAM$03000000–$03007FFF32 KB32-bit1/1/1
I/O$04000000–$040003FE~1 KB32-bit1/1/1
Palette$05000000–$050003FF1 KB16-bit1/1/2
VRAM$06000000–$06017FFF96 KB16-bit1/1/2
OAM$07000000–$070003FF1 KB32-bit1/1/1
ROM WS0$08000000–$09FFFFFF32 MB16-bitconfigurable
ROM WS1$0A000000–$0BFFFFFF32 MB16-bitconfigurable
ROM WS2$0C000000–$0DFFFFFF32 MB16-bitconfigurable
SRAM$0E000000–$0E00FFFF64 KB8-bitconfigurable

EWRAM's 16-bit bus makes 32-bit ARM accesses slow (6 cycles). IWRAM at 32-bit/0-wait is the fastest RAM and ideal for ARM code hot loops. Palette and VRAM add +1 cycle when the display controller is simultaneously accessing them.

BIOS protection: Reads from BIOS ROM when PC is outside the BIOS region return the last successfully fetched BIOS opcode. Typical values: [$00DCh+8] after SoftReset, [$0134h+8] during IRQ, [$013Ch+8] after IRQ return.

Mirroring: EWRAM mirrors across the full $02XXXXXX 16 MB block. IWRAM mirrors across $03XXXXXX. Palette, VRAM, and OAM mirror across their respective 16 MB blocks. VRAM mirrors in 128 KB steps with the upper 32 KB being a mirror of $06010000–$06017FFF.

WAITCNT register ($4000204, 16-bit R/W)

Bits 0-1:   SRAM wait         (0=4, 1=3, 2=2, 3=8 cycles)
Bits 2-3:   WS0 first access  (0=4, 1=3, 2=2, 3=8 cycles)
Bit  4:     WS0 second access (0=2, 1=1 cycle)
Bits 5-6:   WS1 first access  (0=4, 1=3, 2=2, 3=8 cycles)
Bit  7:     WS1 second access (0=4, 1=1 cycle)
Bits 8-9:   WS2 first access  (0=4, 1=3, 2=2, 3=8 cycles)
Bit  10:    WS2 second access (0=8, 1=1 cycle)
Bits 11-12: PHI terminal out  (0=Disable, 1=4.19MHz, 2=8.38MHz, 3=16.78MHz)
Bit  14:    Prefetch buffer   (0=Disable, 1=Enable)
Bit  15:    Game Pak type      (Read Only, 0=GBA)

Default at startup$0000. Typical cart setting: $4317 (WS0=3/1, SRAM=8, prefetch on). 32-bit ROM accesses are split into two 16-bit accesses (second always sequential). Non-sequential access is forced at every 128 KB boundary.

Open bus behavior

Reads from unmapped addresses return the most recently prefetched opcode. In ARM state this is the 32-bit word at [PC+8]. In THUMB state the result depends on bus width — on 32-bit bus regions: LSW=MSW=[$PC+4]; on 16-bit bus regions: LSW=[$PC+4], MSW=[$PC+2]. No cartridge inserted returns (address/2) & 0xFFFF.

Internal memory control ($4000800, 32-bit R/W)

Initialized to $0D000020. Controls EWRAM access timing. Setting bit 0 can disable EWRAM entirely. Writing $0E000020 reduces EWRAM wait states from 2 to 1 on original GBA/SP hardware. Mirrored every 64 KB in the I/O region.

CHAPTER 05

Graphics subsystem

The LCD renders at 240×160 pixels, 59.7275 Hz. Each frame is 280,896 CPU cycles across 228 scanlines (160 visible + 68 VBlank). Each scanline is 1232 cycles (960 HDraw + 272 HBlank). The HBlank flag in DISPSTAT is set for only 226 cycles of the 272-cycle HBlank period.

DISPCNT — $4000000 (16-bit R/W)

Bit 0-2:   BG Mode (0–5)
Bit 3:     CGB Mode (set by BIOS only)
Bit 4:     Display Frame Select (0/1, bitmap modes 4–5)
Bit 5:     HBlank Interval Free (1 = allow OAM access during HBlank)
Bit 6:     OBJ Character Mapping (0=2D, 1=1D)
Bit 7:     Forced Blank (1 = white screen, all video RAM freely accessible)
Bit 8:     BG0 Enable
Bit 9:     BG1 Enable
Bit 10:    BG2 Enable
Bit 11:    BG3 Enable
Bit 12:    OBJ Enable
Bit 13:    Window 0 Enable
Bit 14:    Window 1 Enable
Bit 15:    OBJ Window Enable

Display modes

ModeTypeBG LayersColor depthResolutionNotes
0TileBG0–BG3 (all text)4bpp/8bppup to 512×512Most flexible tile mode
1TileBG0,BG1 (text) + BG2 (affine)4/8bpp text, 8bpp affineBG3 unavailable
2TileBG2,BG3 (both affine)8bpp onlyup to 1024×1024No tile flip in affine
3BitmapBG2 only15bpp direct (RGB555)240×160Single frame, ~75 KB
4BitmapBG2 only8bpp paletted240×1602 frames, page flip
5BitmapBG2 only15bpp direct160×1282 frames, page flip

Text BG map sizesSize 0 = 256×256 (1 screenblock), Size 1 = 512×256 (2 SBs horizontal), Size 2 = 256×512 (2 SBs vertical), Size 3 = 512×512 (4 SBs). Affine BG map sizes: 128×128 / 256×256 / 512×512 / 1024×1024.

Text map entries are 16-bitbits 0–9 = tile number (0–1023), bit 10 = H-flip, bit 11 = V-flip, bits 12–15 = palette (4bpp). Affine map entries are 8-bit: tile number 0–255, no flip, always 8bpp.

DISPSTAT ($4000004) and VCOUNT ($4000006)

DISPSTAT: Bit 0 = V-Blank flag (R), Bit 1 = H-Blank flag (R), Bit 2 = V-Counter match flag (R), Bit 3 = VBlank IRQ enable, Bit 4 = HBlank IRQ enable, Bit 5 = VCounter IRQ enable, Bits 8–15 = LYC (V-Count setting).

VCOUNT: Bits 0–7 = current scanline (0–227), read-only.

BGxCNT — $4000008$400000E (16-bit R/W per BG)

Bits 0-1:   Priority (0=highest, 3=lowest; ties: BG0 > BG1 > BG2 > BG3)
Bits 2-3:   Character Base Block (0–3, addr = $06000000 + CBB×$4000)
Bit  6:     Mosaic enable
Bit  7:     Color mode (0=4bpp/16pal, 1=8bpp/256col)
Bits 8-12:  Screen Base Block (0–31, addr = $06000000 + SBB×$800)
Bit  13:    Overflow wrap (affine BG2/BG3 only; 0=transparent, 1=wrap)
Bits 14-15: Screen Size (0–3)

BG scroll and affine registers

Scroll registers ($4000010$400001E, write-only)BGxHOFS/BGxVOFS, 9-bit offsets (0–511). Used in text mode only.

Affine parameters ($4000020$400003F)BG2/BG3 each have PA (dx), PB (dmx), PC (dy), PD (dmy) as 8.8 signed fixed-point (16-bit write-only), and reference points BG2X/BG2Y/BG3X/BG3Y as 20.8 signed fixed-point (28 significant bits in 32-bit write-only registers).

Critical internal behavior: Reference points are copied to internal registers at VBlank start. Each scanline, internal registers increment by PB/PD. Writing reference points outside VBlank immediately updates the internal register for the current scanline. Per-pixel rendering: src_x = (internal_x + PA×pixel) >> 8, src_y = (internal_y + PC×pixel) >> 8.

VRAM layout (96 KB at $06000000)

In tile modes$06000000–$0600FFFF (64 KB) for BG tiles and maps, $06010000–$06017FFF (32 KB) for OBJ tiles. Character Base Blocks at 16 KB intervals, Screen Base Blocks at 2 KB intervals.

In bitmap modesframe buffers start at $06000000. Mode 4 Frame 1 at $0600A000. OBJ tiles limited to $06014000–$06017FFF (16 KB, tiles 512–1023 only).

8-bit writes to VRAM write the byte to both halves of the 16-bit unit. 8-bit writes to OAM are ignored. 8-bit writes to Palette double the byte.

OAM and sprites ($07000000, 1 KB)

128 OAM entries × 8 bytes. Three 16-bit attributes interleaved with rotation/scaling data. 32 rotation groups of 4 parameters each (PA,PB,PC,PD as 8.8 fixed-point), spread at 32-byte intervals.

Attribute 0: Bits 0–7 = Y coord; Bit 8 = rotation flag; Bit 9 = double-size (if rot) or disable (if no rot); Bits 10–11 = OBJ mode (0=normal, 1=semi-transparent, 2=OBJ window); Bit 12 = mosaic; Bit 13 = color mode (0=4bpp, 1=8bpp); Bits 14–15 = shape (0=square, 1=horizontal, 2=vertical).

Attribute 1: Bits 0–8 = X coord (9-bit); if rotation: bits 9–13 = rotation group (0–31); if no rotation: bit 12 = H-flip, bit 13 = V-flip; Bits 14–15 = size (0–3).

Attribute 2: Bits 0–9 = tile number (0–1023); Bits 10–11 = priority (0–3); Bits 12–15 = palette (4bpp).

Size table (shape × size):

Size 0Size 1Size 2Size 3
Square8×816×1632×3264×64
Horizontal16×832×832×1664×32
Vertical8×168×3216×3232×64

Palette RAM ($05000000, 1 KB)

BG palette at $05000000 (256 entries), OBJ palette at $05000200 (256 entries). RGB555 format: bits 0–4 = Red, 5–9 = Green, 10–14 = Blue, bit 15 unused. 32,768 possible colors. In 4bpp mode, 16 sub-palettes of 16 colors each (base = $05000000 + palette × $20). Color 0 of any palette is transparent. BG palette 0, color 0 = backdrop color.

Windowing

WIN0H/WIN1H ($4000040/$4000042, write-only)Bits 8–15 = X1 (left), Bits 0–7 = X2 (right+1). WIN0V/WIN1V ($4000044/$4000046): same layout for Y. If X2>240 or X1>X2, treated as X2=240.

WININ ($4000048)Bits 0–3 = Win0 BG0–3 enable, Bit 4 = Win0 OBJ, Bit 5 = Win0 special effects; Bits 8–13 = same for Win1.

WINOUT ($400004A)Bits 0–5 = outside window settings; Bits 8–13 = OBJ window settings.

PriorityWIN0 > WIN1 > OBJ Window > Outside.

Blending and special effects

BLDCNT ($4000050)Bits 0–5 = 1st target (BG0–3, OBJ, Backdrop); Bits 6–7 = effect mode (0=none, 1=alpha, 2=brighten, 3=darken); Bits 8–13 = 2nd target.

BLDALPHA ($4000052)Bits 0–4 = EVA (0–16), Bits 8–12 = EVB (0–16). Alpha formula per channel: I = min(31, I_1st × EVA/16 + I_2nd × EVB/16).

BLDY ($4000054)Bits 0–4 = EVY (0–16). Brighten: I = I + (31−I) × EVY/16. Darken: I = I − I × EVY/16.

Semi-transparent OBJs (mode 1) always use alpha blending regardless of BLDCNT mode setting, and are always treated as 1st target regardless of BLDCNT bit 4.

Mosaic ($400004C, write-only)

Bits 0–3 = BG mosaic H-size (value N → (N+1) pixel blocks), Bits 4–7 = BG mosaic V-size, Bits 8–11 = OBJ mosaic H-size, Bits 12–15 = OBJ mosaic V-size.

CHAPTER 06

Audio subsystem

Legacy PSG channels (channels 1–4)

Registers at $4000060$400009F. Internal generation at 262,144 Hz (2²⁴/64).

Channel 1 — Square wave with sweep:

  • SOUND1CNT_L ($4000060): Bits 0–2 = sweep shift (n), Bit 3 = sweep direction (0=increase, 1=decrease), Bits 4–6 = sweep time in 7.8 ms units (0=disabled). Sweep formula: freq(t) = freq(t−1) ± freq(t−1)/2ⁿ. Overflow (>2047) kills the channel.
  • SOUND1CNT_H ($4000062): Bits 0–5 = length (duration = (64−n)/256 s), Bits 6–7 = duty (0=12.5%, 1=25%, 2=50%, 3=75%), Bits 8–10 = envelope step (n/64 s, 0=no envelope), Bit 11 = envelope direction (0=decrease, 1=increase), Bits 12–15 = initial volume (0=silent).
  • SOUND1CNT_X ($4000064): Bits 0–10 = frequency (output = 131072/(2048−n) Hz), Bit 14 = length flag, Bit 15 = restart (write 1).

Channel 2 mirrors Channel 1 without sweepSOUND2CNT_L ($4000068) = duty/length/envelope, SOUND2CNT_H ($400006C) = frequency/control.

Channel 3 — Programmable wave:

  • SOUND3CNT_L ($4000070): Bit 5 = wave RAM dimension (0=one bank/32 digits, 1=two banks/64 digits), Bit 6 = bank select, Bit 7 = playback on/off.
  • SOUND3CNT_H ($4000072): Bits 0–7 = length, Bits 13–14 = volume (0=mute, 1=100%, 2=50%, 3=25%), Bit 15 = force 75%.
  • SOUND3CNT_X ($4000074): Bits 0–10 = sample rate (2097152/(2048−n) Hz), Bit 14 = length flag, Bit 15 = restart.
  • Wave RAM ($4000090$400009F): 16 bytes = 32 × 4-bit samples per bank. Selected bank plays; CPU reads/writes the other bank.

Channel 4 — Noise:

  • SOUND4CNT_L ($4000078): Bits 0–5 = length, Bits 8–10 = envelope step, Bit 11 = envelope direction, Bits 12–15 = initial volume.
  • SOUND4CNT_H ($400007C): Bits 0–2 = dividing ratio (r), Bit 3 = counter width (0=15-bit LFSR, 1=7-bit), Bits 4–7 = shift clock (s), Bit 14 = length flag, Bit 15 = restart. Frequency = 524288/r/2^(s+1) Hz (r=0 treated as 0.5).

DMA sound channels (Direct Sound A & B)

Two 8-bit signed PCM channels fed via 32-byte FIFOs at $40000A0 (FIFO A) and $40000A4 (FIFO B), both 32-bit write-only. Each write delivers 4 signed bytes. The FIFOs are drained by timer overflow — one sample per overflow. When FIFO drops to ≤16 bytes, a DMA request refills 16 bytes (4×32-bit, always, ignoring word count/transfer type in DMA control).

DMA1 feeds FIFO A. DMA2 feeds FIFO B. No other DMA channels can service sound FIFOs.

Sound control registers

SOUNDCNT_L ($4000080)Bits 0–2 = right volume (0–7), Bits 4–6 = left volume (0–7), Bits 8–11 = Ch1–4 right enable, Bits 12–15 = Ch1–4 left enable.

SOUNDCNT_H ($4000082)Bits 0–1 = PSG volume (0=25%, 1=50%, 2=100%), Bit 2 = DMA-A volume (0=50%, 1=100%), Bit 3 = DMA-B volume (0=50%, 1=100%), Bit 8 = DMA-A right enable, Bit 9 = DMA-A left enable, Bit 10 = DMA-A timer select (0=Timer0, 1=Timer1), Bit 11 = DMA-A FIFO reset, Bit 12 = DMA-B right, Bit 13 = DMA-B left, Bit 14 = DMA-B timer select, Bit 15 = DMA-B FIFO reset.

SOUNDCNT_X ($4000084)Bits 0–3 = channel 1–4 ON status (read-only), Bit 7 = master enable (clearing this resets all PSG registers to zero).

SOUNDBIAS ($4000088)Bits 1–9 = bias level (default $100 = register value $200), Bits 14–15 = amplitude resolution (0 = 9-bit/32.768 kHz default, 1 = 8-bit/65.536 kHz, 2 = 7-bit/131.072 kHz, 3 = 6-bit/262.144 kHz).

Mixing pipeline

PSG channels output ±$80 each. DMA channels span ±$200. After volume scaling and L/R mixing, bias ($200 default) is added, result clipped to 0–$3FF (10-bit), then resampled to the SOUNDBIAS-selected rate.

CHAPTER 07

Timer-DMA-audio chain

This is the most timing-critical subsystem for recompilation accuracy.

4 timers at $4000100$400010E

TimerCounter/ReloadControl
TM0$4000100$4000102
TM1$4000104$4000106
TM2$4000108$400010A
TM3$400010C$400010E

TMxCNT_L (16-bit)Write sets reload value; read returns current counter. Reload copied to counter on overflow or when start bit transitions 0→1.

TMxCNT_H (16-bit)

Bits 0-1:  Prescaler (0=F/1=16.78MHz, 1=F/64=262kHz, 2=F/256=65.5kHz, 3=F/1024=16.4kHz)
Bit  2:    Cascade mode (increment on previous timer overflow; prescaler ignored)
Bit  6:    IRQ enable on overflow
Bit  7:    Timer start/stop

Audio sample rate formula: reload = 65536 − round(16777216 / desired_rate). Common values: 16384 Hz → $FC00, 32768 Hz → $FE00, 65536 Hz → $FF00. Use prescaler 0 (F/1) for audio timers.

The complete audio DMA chain

Timer overflow → pop 1 byte from FIFO → output to DAC
→ if FIFO ≤ 16 bytes → trigger DMA request
→ DMA transfers 4×32-bit (16 bytes) to FIFO
→ DMA source address increments by 16
→ FIFO destination address stays FIXED

Only Timer 0 or Timer 1 can drive DMA sound (selected per-channel in SOUNDCNT_H). Timer 2/3 cannot. Both DMA Sound A and B can share the same timer for synchronized stereo.

CHAPTER 08

DMA controller (4 channels)

Register map

ChSADDADCNT_LCNT_H
0$40000B0$40000B4$40000B8$40000BA
1$40000BC$40000C0$40000C4$40000C6
2$40000C8$40000CC$40000D0$40000D2
3$40000D4$40000D8$40000DC$40000DE

SAD/DAD are 32-bit write-only. CNT_L is 16-bit write-only (word count). CNT_H is 16-bit R/W.

DMA CNT_H layout:

Bits 5-6:   Destination control (0=Inc, 1=Dec, 2=Fixed, 3=Inc/Reload)
Bits 7-8:   Source control (0=Inc, 1=Dec, 2=Fixed, 3=Prohibited)
Bit  9:     Repeat
Bit  10:    Transfer type (0=16-bit, 1=32-bit)
Bit  11:    Game Pak DRQ (DMA3 only)
Bits 12-13: Start timing (0=Immediately, 1=VBlank, 2=HBlank, 3=Special)
Bit  14:    IRQ on completion
Bit  15:    DMA Enable

Special timing (bits 12–13 = 3): DMA0 = prohibited, DMA1/DMA2 = sound FIFO mode (always 4×32-bit, ignoring word count), DMA3 = video capture.

Priority: DMA0 (highest) > DMA1 > DMA2 > DMA3. CPU halts during DMA. Source address limits: DMA0 = 27-bit (internal only), DMA1–3 = 28-bit. Word count: DMA0–2 = 14-bit (max 16384), DMA3 = 16-bit (max 65536).

Transfer timing: 2N + 2(n−1)S + xI per transfer, where x=2 normally, x=4 if both source and destination are GamePak.

CHAPTER 09

Interrupt system

Registers

IME ($4000208)Bit 0 = master interrupt enable. IE ($4000200): interrupt enable mask. IF ($4000202): interrupt request flags (write 1 to acknowledge).

BitSource
0V-Blank
1H-Blank
2V-Counter match
3Timer 0
4Timer 1
5Timer 2
6Timer 3
7Serial
8DMA 0
9DMA 1
10DMA 2
11DMA 3
12Keypad
13Game Pak

Interrupt dispatch flow

  1. Hardware fires IRQ → CPU enters IRQ mode (ARM state), CPSR→SPSR\_irq, I=1, PC→$00000018
  2. BIOS handler at $00000018 saves context, reads user handler address from $03007FFC (top of IWRAM)
  3. BIOS switches to System mode and branches to user handler
  4. User handler checks IE & IF, processes interrupt, writes 1s to IF to acknowledge, and also ORs into BIOS IF at $03007FF8 (required for IntrWait/Halt functions)
  5. Returns → BIOS restores context, executes SUBS PC, LR, #4

HALTCNT ($4000301, write-only)Bit 7 = 0 for Halt (CPU paused until interrupt), Bit 7 = 1 for Stop (full power-down, wake only via keypad/gamepak/SIO).

CHAPTER 10

BIOS SWI functions

Invoke via SWI n in THUMB or SWI n<<16 in ARM. Parameters in R0–R3; returns in R0, R1, R3.

SWINameInputs → Outputs
$00SoftResetClears $03007E00+, resets SP (svc=$03007FE0, irq=$03007FA0, sys=$03007F00), jumps to $08000000 or $02000000 per flag at $03007FFA
$01RegisterRamResetR0=flags (bit0=EWRAM, 1=IWRAM, 2=Palette, 3=VRAM, 4=OAM, 5=SIO, 6=Sound, 7=Other regs). Sets DISPCNT=$0080
$02HaltWrites $00 to HALTCNT; CPU sleeps until any interrupt
$03StopWrites $80 to HALTCNT; deep sleep until keypad/gamepak/SIO
$04IntrWaitR0=1 discard old flags; R1=interrupt mask. Halts until (BIOS_IF & R1) != 0
$05VBlankIntrWaitCalls IntrWait with R0=1, R1=1 (VBlank). VBlank IRQ must be enabled
$06DivR0=numerator, R1=denominator → R0=quotient, R1=remainder, R3=abs(quotient)
$07DivArmR0=denominator, R1=numerator (swapped) → same outputs
$08SqrtR0=value → R0=sqrt (u16)
$09ArcTanR0=tan (1.14 fixed) → R0=angle
$0AArcTan2R0=x, R1=y → R0=angle (0–$FFFF for 0–2π)
$0BCpuSetR0=src, R1=dst, R2=count/mode. Bit24: fill. Bit26: 32-bit
$0CCpuFastSet32-bit only, 8-word blocks (LDMIA/STMIA). Bit24: fill
$0EBgAffineSetR0=src params, R1=dst affine, R2=count
$0FObjAffineSetR0=src, R1=dst, R2=count, R3=dst stride (2 or 8)
$10BitUnPackR0=src, R1=dst, R2=info ptr. Expands 1/2/4/8-bit to wider
$11LZ77UnCompWramLZ77/LZSS decompress, 8-bit writes. Header: type $10, 24-bit size
$12LZ77UnCompVramSame with 16-bit writes (for VRAM)
$13HuffUnCompHuffman decompress. Header bit0–3 = data size (4 or 8 bit)
$14RLUnCompWramRun-length decompress, 8-bit writes. Header type $30
$15RLUnCompVramSame with 16-bit writes
$16Diff8bitUnFilterWram8-bit differential unfilter, 8-bit writes
$17Diff8bitUnFilterVram8-bit differential unfilter, 16-bit writes
$18Diff16bitUnFilter16-bit differential unfilter
$19SoundBiasR0 bit0: 0=decrease to 0, 1=increase to $200
$1FMidiKey2FreqR0=wave ptr, R1=MIDI key, R2=pitch adj → R0=freq
CHAPTER 11

Cartridge and save system

ROM is up to 32 MB on a 16-bit bus, mirrored across WS0 ($08000000), WS1 ($0A000000), and WS2 ($0C000000). Only CPU and DMA3 can access ROM. 32-bit fetches split into two 16-bit accesses.

Save type detection

Games embed ASCII identification strings in ROM

StringTypeSize
SRAM_VSRAM/FRAM32 KB at $0E000000, 8-bit bus
FLASH_VFlash 64 KB64 KB at $0E000000
FLASH512_VFlash 64 KB64 KB
FLASH1M_VFlash 128 KB128 KB (bank-switched)
EEPROM_VEEPROM512 B or 8 KB, serial DMA3 access

Flash command protocol

All flash commands use three sequential 8-bit writes$AA→$0E005555, $55→$0E002AAA, then command byte → $0E005555. Key commands: $90 = enter chip ID mode (read manufacturer at $0E000000, device at $0E000001), $F0 = exit ID mode, $80 + $10 = erase chip, $80 + $30 = erase 4 KB sector, $A0 = write byte, $B0 = set bank (128 KB only, write 0 or 1 to $0E000000).

Example chip IDsPanasonic 64K = $32/$1B, Sanyo 128K = $62/$13, Macronix 128K = $C2/$09.

EEPROM protocol

Accessed via DMA3 at the top of ROM address space (≤16 MB ROM: $0D000000+, >16 MB: $0DFFFF00+). Serial, 1 bit per halfword. 512-byte EEPROM uses 6-bit addresses (64 blocks × 64 bits). 8 KB EEPROM uses 14-bit addresses (1024 blocks × 64 bits). Read request: send 11 + address + 0, then read 4 dummy bits + 64 data bits. Write: send 10 + address + 64 data bits + 0, poll until bit 0 returns 1.

CHAPTER 12

Static recompilation pitfalls for REFORGE

ARM/THUMB interworking on ARMv4T is the single largest source of recompiler bugs. On the GBA, only BX performs interworking. LDR PC, POP {PC}, and MOV PC, Rn do not switch state — bit 0 of the loaded address is placed into PC directly (potentially causing misaligned execution, not a mode switch). ARMv5T+ changed this, and many reference materials describe the newer behavior. REFORGE must enforce strict v4T semantics.

Open bus must be emulated for correct behavior. Some games detect emulators by reading unmapped addresses and checking the prefetch value. The BIOS protection mechanism (returning last-fetched BIOS opcode) is also tested.

Forced blank (DISPCNT bit 7) renders white and makes all video memory freely accessible with no timing penalties. Clearing forced blank during active scanlines restarts rendering with a 2-line delay. Games toggle this during VBlank for bulk VRAM transfers.

Timer-DMA audio requires cycle-accurate timer overflow → DMA triggering. The FIFO holds only 32 bytes. At 64 kHz, there is a 0.25 ms window before underrun. Higher-priority DMA (HBlank DMA via DMA0) can delay sound DMA1/2, causing audio pops. Games that dynamically change timer reload values mid-playback require precise cycle accounting.

Self-modifying code in IWRAM (where ARM code hot loops live) requires invalidating recompiled blocks when writes to the code region are detected. Some games load decompressed overlays into IWRAM and branch to them.

Prefetch buffer (WAITCNT bit 14) allows the CPU to prefetch ROM opcodes during internal cycles, effectively hiding wait states for sequential execution. When enabled, sequential ROM fetches may appear as 1-cycle if the prefetch buffer has data ready. A recompiler that pre-computes cycle counts must model this buffer or accept timing inaccuracy on ROM-heavy code paths.

CHAPTER 13

I/O Dispatch Reference (Iteration 13 Synthesis)

Complete Register Dispatch Table

AddressNameR/WSide Effects
$4000000DISPCNTR/WMode change resets internal counters. Bit 7 (Forced Blank) enables 1-cycle VRAM/Palette/OAM access
$4000004DISPSTATR/WBits 0-2 read-only (VBlank/HBlank/VCount flags). Bits 3-5 writable (IRQ enables). Bits 8-15 writable (LYC)
$4000006VCOUNTRCurrent scanline 0-227. Stable during VBlank (160-227)
$4000008-0EBG0-3CNTR/WSome bits mode-locked (e.g. BG2/3 affine bits in mode 0)
$4000010-1EBGxHOFS/VOFSW9-bit scroll. Internal latch updated on write
$4000020-3CBG2/3 affineWReference points latched at VBlank; PA/PB/PC/PD are 8.8 fixed-point
$4000040-4AWindowR/WWIN0/1 H/V + WININ/WINOUT per-layer enable
$400004CMOSAICWBG/OBJ mosaic H/V sizes
$4000050BLDCNTR/WBlend target selection + mode (none/alpha/brighten/darken)
$4000052BLDALPHAR/WEVA/EVB coefficients (0-16 each)
$4000054BLDYWEVY brightness coefficient (0-16)
$4000060-66SOUND1CNTR/WCh1 sweep + duty + envelope + freq. Bit 15 of CNT_X = restart
$4000068-6ESOUND2CNTR/WCh2 duty + envelope + freq
$4000070-76SOUND3CNTR/WCh3 wave bank/enable + volume + rate
$4000078-7ESOUND4CNTR/WCh4 envelope + LFSR noise
$4000080SOUNDCNT_LR/WPSG volume/panning per-channel L/R
$4000082SOUNDCNT_HR/WDMA sound volume, timer select, FIFO reset strobes (bits 11/15)
$4000084SOUNDCNT_XR/WBits 0-3 read-only (channel active). Bit 7 only writable (master enable; clearing resets all PSG)
$4000088SOUNDBIASR/WBias level (default $200) + amplitude resolution
$4000090-9FWave RAMR/W2 banks x 32 x 4-bit samples. CPU accesses inactive bank
$40000A0FIFO_AWDirectSound FIFO A (32-byte ring, 8-bit signed PCM)
$40000A4FIFO_BWDirectSound FIFO B (32-byte ring, 8-bit signed PCM)
$40000B0-BADMA0R/WSAD/DAD/CNT_L (W), CNT_H (R/W). Internal memory only. Priority 0
$40000BC-C6DMA1R/WSound FIFO A special mode (timing=3: fixed dest, 16-byte burst)
$40000C8-D2DMA2R/WSound FIFO B special mode
$40000D4-DEDMA3R/WGeneral purpose + GamePak + EEPROM + video capture
$4000100-02TM0R/WCNT_L (reload/counter), CNT_H (prescaler/cascade/IRQ/start). Drives FIFO A/B
$4000104-06TM1R/WCan also drive FIFO A/B via SOUNDCNT_H timer select
$4000108-0ATM2R/WGeneral timer
$400010C-0ETM3R/WGeneral timer
$4000130KEYINPUTRActive-low buttons (default $03FF = all released)
$4000132KEYCNTR/WKeypad IRQ control (AND/OR mode)
$4000200IER/W14-bit interrupt enable mask
$4000202IFR/WInterrupt flags. Write-1-to-clear (AND NOT pattern)
$4000204WAITCNTR/WGamePak wait states + prefetch buffer enable (bit 14)
$4000208IMER/WMaster interrupt enable (bit 0)
$4000301HALTCNTWBit 7: 0=halt (wake on IRQ), 1=stop (deep sleep)
$4000800Internal Mem CtrlR/WEWRAM wait states. Default $0D000020. Set to $0E000020 for overclock (3/3/6 → 2/2/4)

Sound FIFO Internal Details

The DirectSound FIFOs are 8-entry x 32-bit ring buffers (32 bytes each) with a separate 32-bit playing buffer:

  • Pre-pop DMA request: Triggered when FIFO drops to ≤4 entries (≤16 bytes remaining)
  • Timer overflow behavior: Pops one 8-bit sample from the playing buffer (MSB-first within each 32-bit word)
  • Playing buffer refill: When all 4 bytes consumed from playing buffer, next 32-bit word dequeued from ring
  • Underflow: Repeats the last sample (no silence — causes a DC offset click if sustained)
  • FIFO reset (SOUNDCNT_H bits 11/15): Clears ring buffer and playing buffer, resets pointers

Sound DMA behavior (DMA1/DMA2 in FIFO mode):

  • Always transfers exactly 4 x 32-bit words (16 bytes) regardless of CNT_L word count
  • Destination address is FIXED (FIFO register), source increments
  • Transfer type forced to 32-bit regardless of CNT_H bit 10
  • Repeat bit must be set for continuous playback

Internal Memory Control ($4000800) Deep Dive

Bits 0:      EWRAM disable (0=enabled, 1=disabled)
Bits 3-5:    EWRAM area
Bits 24-27:  EWRAM wait state control
  • Default value: $0D000020 → EWRAM at 3/3/6 wait states (non-seq/seq/32-bit)
  • Overclock value: $0E000020 → EWRAM at 2/2/4 wait states
  • Only original GBA and GBA SP respond to the overclock; DS/micro ignore it
  • Mirrored every 64 KB throughout the I/O region
  • Some commercial games (e.g., Golden Sun) use the overclock for performance

Flash 128KB Save System (Pokémon Emerald / FireRed / LeafGreen)

Chip detection: Enter ID mode ($90 command), read manufacturer at $0E000000 and device at $0E000001. Common IDs: Sanyo LE26FV10N1TS = $62/$13, Macronix MX29L010 = $C2/$09.

Bank switching (128KB only): Command $B0 then write bank number (0 or 1) to $0E000000. This maps the selected 64KB bank into the $0E000000-$0E00FFFF window.

128KB .sav format (Pokémon Emerald):

  • Two save blocks (A and B) for double-buffering, each containing 14 sections
  • Each section has a 12-byte footer:
    • Bytes 0-1: Section ID (0-13)
    • Bytes 2-3: Checksum (16-bit, see below)
    • Bytes 4-7: Signature ($08012025)
    • Bytes 8-11: Save Index (increments each save; higher = newer)

Checksum algorithm:

uint32_t sum = 0;
for (int i = 0; i < data_size / 4; i++)
sum += ((uint32_t*)data)[i];
uint16_t checksum = ((sum >> 16) + (sum & 0xFFFF)) & 0xFFFF;

Security key (Pokémon Emerald):

  • Located in Section 0 at offset $0AF8 (Emerald) or $0F20 (FRLG)
  • XORs sensitive fields: money (Section 1 @ $0490), coins ($0494), bag item quantities (lower 16 bits of key)
  • Key is regenerated on each save; verifier must decrypt before checksum validation

Load/verify path:

  1. Read both blocks A and B
  2. Compare Save Index — highest is primary, other is backup
  3. Verify signature ($08012025) in all 14 sections
  4. Verify checksums; if primary fails, fall back to backup
  5. Reassemble sections 0-13 in order by Section ID into contiguous save data

x86-64 Dispatch Table Skeleton

// Conceptual dispatch for REFORGE GBA I/O
void mmio_dispatch(uint32_t addr, uint32_t value, bool is_write) {
switch (addr & 0xFFFF) {
// LCD
case 0x0000: dispcnt_write(value); break;
case 0x0004: dispstat_rw(value, is_write); break;
case 0x0006: return vcount_read();
// Sound
case 0x0082: soundcnt_h_write(value); break;  // check FIFO reset bits
case 0x0084: soundcnt_x_partial_write(value); break;  // bit 7 only
case 0x00A0: fifo_a_write(value); break;
case 0x00A4: fifo_b_write(value); break;
// DMA (enable bits trigger transfer)
case 0x00BA: dma0_cnt_h_write(value); break;
case 0x00C6: dma1_cnt_h_write(value); break;  // sound FIFO A
case 0x00D2: dma2_cnt_h_write(value); break;  // sound FIFO B
case 0x00DE: dma3_cnt_h_write(value); break;
// Timers
case 0x0102: case 0x0106: case 0x010A: case 0x010E:
timer_cnt_h_write(addr, value); break;
// Interrupts
case 0x0200: ie_write(value); break;
case 0x0202: if_w1c(value); break;  // current &= ~value
case 0x0208: ime_write(value); break;
// System
case 0x0301: haltcnt_write(value); break;
case 0x0800: memctrl_write(value); break;  // EWRAM overclock
}
// +1 cycle penalty on VRAM/Palette/OAM access during active display
if (video_contention) add_cycle_penalty(1);
// Prefetch suspended during DMA
if (dma_active) prefetch_suspend();
}

Verification Notes

Test ROM approach (devkitARM):

  1. VBlank OAM DMA — verify 128 OBJs transferred within 272-cycle HBlank window
  2. Sound FIFO — Timer 0 at 16384 Hz feeding DMA1, verify no underflow for 60 frames
  3. Affine BG — set reference point mid-frame, verify internal latch update
  4. Flash save — write/read/verify cycle with bank switch + checksum + security key
  5. IF write-1-to-clear — set IF bits via hardware, write partial mask, verify only specified bits cleared

mGBA trace validation: Compare register read/write traces between mGBA and REFORGE dispatch output. Expected match: ≥99.9% for all MMIO addresses, <0.1% deviation on cycle-timing-dependent reads (VCOUNT during mode transitions).

Public sources

Production ledger

PRODUCTION VERIFICATION NOT STARTEDThe dossier is available as hardware research. No implementation result is recorded here yet.