Companion to 2D Art βΊ Composition. This page traces a single sprite placement from the overlay code that requested it through the engine's bookkeeping into NDS hardware OAM.
Stage 1 β overlay places a sprite
A controller / body overlay calls the engine sprite API in
ov137:
FUN_021850D4(
slot, // sprite-slot index (allocated earlier via FUN_02184990/FUN_021849AC)
x, y, // screen coords
w, h, // pixel dimensions
type, // 1=BG, 2=OBJ, 4=blend, β¦
flags, // priority, layer, blend mode
data_ptr // pointer into the content-cache (NCGR + NCLR bytes)
); FUN_021850D4 calls func_0x02071E78 (arm9-side
sprite emitter). 12 distinct ov137 functions call this entry point.
Stage 2 β engine bookkeeping (ov137 internal)
Engine API table:
| Function | Role |
|---|---|
FUN_02184990(?, n_tiles) | Allocate sprite slot. |
FUN_021849AC(?, n_tiles, flag) | Allocate sprite slot with flag. |
FUN_02185508(size, align) | malloc for sprite-related structs. |
FUN_0218472C(slot, mask, val, flag) | Attribute set. |
FUN_021848A8(slot, mask, val) | Attribute set. |
FUN_02184814(slot, mask, val, flag) | Attribute set variant. |
FUN_02185154(slot, ?, ?, ?, ?) | Sprite position update. |
FUN_02185384(?, ?, ?, ?) | Write attribute. |
FUN_021852A8(?, ?, ?) | Tile-data update. |
FUN_02184D6C(?, ?, ?, ?, &local, ?) | Sprite-list builder. |
Stage 3 β write to shadow OAM
ov137 writes 8 bytes of OAM data per sprite to a shadow OAM buffer in main RAM (BSS). Sprite-slot address computed by the chain:
oam_slot_addr = *0x021907BC + i * 0x40 + field_offset
*0x021907BC β base of sprite-slot array (28 writes observed)
i * 0x40 β 64 bytes per slot record
field_offset β 0x00..0x07 holds OAM attr0/attr1/attr2 Stage 4 β VBlank DMA flush
At VBlank, the engine DMAs the shadow buffer to hardware OAM. Two VBlank handlers cooperate:
| Handler | Address | Role |
|---|---|---|
| arm9 | 0x0207001C | System-side VBlank (audio, DMA timing) |
| ov137 | 0x02185BF8 | Game-engine VBlank (DMAs shadow OAM/VRAM) |
Transfer wrappers
Each wrapper takes (src, dst_off, byte_count) and dispatches
to the DMA0 primitive FUN_02017D9C at
0x040000B0..0x040000BC (DMA0 source / dest / count /
control).
| Wrapper | Destination | Hardware base |
|---|---|---|
0x020769C0 | OAM main | 0x07000000 + dst_off |
0x02076A00 | OAM sub | 0x07000400 + dst_off |
0x02076A40 | OBJ VRAM main | 0x06400000 + dst_off |
0x02076A7C | OBJ VRAM sub | 0x06600000 + dst_off |
OAM manager pointers (BSS)
| Address | Refs | Role |
|---|---|---|
0x020A749C | 42 | ARM9 OAM manager pointer |
0x02190908 | β | ov137 OAM manager pointer |
0x021907BC | 28 writes | ov137 sprite-slot array pointer |
0x021A55C0 | 750 | Game state struct pointer (top-most BSS reference) |
Dispatcher (ov137)
FUN_021708CC: command dispatcher with a 10-entry jump table
at 0x021708E6. Each opcode handler sits at offset
(table[op] + 0x021708E8). Called 98 times across overlays.
Pipeline summary
controller overlay (e.g. ov003)
β loads asset via FUN_02009894 β master cache table 0x020AA740
βΌ
body overlay (e.g. ov070) walks placement table at .rodata
β for each entry: FUN_021850D4(slot, x, y, w, h, type, flags, data)
βΌ
ov137 sprite API
β STR/STRH into shadow-OAM at *0x021907BC + i*0x40 + offset
βΌ
[VBlank fires]
β
βΌ
ov137 VBlank handler @ 0x02185BF8
β calls 0x020769C0 (OAM main transfer wrapper)
β β FUN_02017D9C (DMA0 primitive)
β β 0x040000B0..BC (DMA registers)
βΌ
NDS hardware OAM @ 0x07000000
β
βΌ
GPU rasterises sprites at scanline time Source
notes/phaseA_ghidra/DISCOVERIES.mdΒ§"OAM/sprite rendering pipeline"arm9_full_plaintext.bin@0x02017D9Cβ DMA primitiveov137β engine VBlank + sprite API