SWI 0x20 — SoundWhatever0 (MusicPlayerOpen / MPlayOpen)¶
- Entry:
0x000013C4(THUMB; SWI table stores0x000013C5) - Status: verified (hardware-checked 2026-07-08: cycle counts and return registers measured on real GBA; struct layouts remain from static analysis)
Summary¶
Behaviorally this is the music-sequencer MPlayOpen: it prepares a MusicPlayerInfo structure to control a set of sequencer tracks and registers that player in the BIOS's global player list. Despite the working name "MidiKey2Freq helper", the disassembly shows MidiKey2Freq does not call this routine; it is the first of the five sequencer-management SWIs (0x20–0x24) built on the m4a-style MusicPlayerInfo/Track structures.
Parameters¶
| Reg | In | Meaning |
|---|---|---|
| r0 | ptr | MusicPlayerInfo to initialize |
| r1 | ptr | MusicPlayerTrack array (each track = 0x50 bytes) |
| r2 | count | number of tracks (clamped to max 16) |
Returns¶
No return value; MusicPlayerInfo is initialized and linked into the global list.
Clobbered registers¶
r0–r5, r7.
Hardware audit (2026-07-08, canary r4–r12 + CPSR snapshot): caller-visible clobbers: r0 = 0x2149 leftover, r1 = sound-area pointer, r2 (count) preserved, r3 = 0x170 leftover. r2, r4–r12, r13, and CPSR (flags and mode) came back bit-identical on every tested path. r11/r12 are explained by the SWI dispatcher (it pushes {r11, r12, lr} — see 10_irq_boot_and_iwram.md) and CPSR by the SPSR restore on return; r2 and r4–r10 must be preserved or restored by the routine itself — where a static note above claims r2 is destroyed, the hardware disagrees at the caller level. Raw data: results/clobber_audit_pass2.csv.
Algorithm¶
- Run a base init helper (
0x23B0) on the MusicPlayerInfo. - Clamp track count to
1..16; store it at MusicPlayerInfo+0x08, the track-array pointer at +0x2C, and an initial flags/status word (0x00008000) at +0x04. - Zero the status byte of each track (
track[i]at +0, stride0x50). - Link this player into the global player registration list (rooted via the global
sound block at
[0x03007FC0]+0x30): bump the global open counter, save the previous head's fade linkage into MusicPlayerInfo+0x38/+0x3C, and make this player the new head. Store the player identity word (0x68736d53) at MusicPlayerInfo+0x34 and a per-player id at +0x34-region.
MusicPlayerInfo fields touched (inferred)¶
| Offset | Field |
|---|---|
| 0x04 | flags/status (init 0x8000) |
| 0x08 | track count |
| 0x2C | pointer to track array |
| 0x30 | current song header pointer (set by MPlayStart) |
| 0x34 | identity 0x68736d53 / player id |
| 0x38, 0x3C | saved fade/link callback of previous player |
Edge cases & known bugs¶
- Track count above 16 is silently clamped.
- The global list linkage assumes single-threaded (non-reentrant) setup.
Struct layout (static analysis)¶
From static analysis (Ghidra 12.1.2 decompilation of MPlayOpen + MPlayStart):
MusicPlayerInfo (the control block, r0):
| Off | Field |
|-----|-------|
| 0x00 | song header pointer |
| 0x04 | status / activity word |
| 0x08 | track count (byte, clamped to 16) |
| 0x09 | byte copied from song header +0x02 |
| 0x1C/0x20 | tempo/volume defaults (init 0x96,0x96) |
| 0x1E | pitch/master (init 0x100) |
| 0x22 | init 0 |
| 0x2C | MusicPlayerTrack array pointer |
| 0x30 | voicegroup (from song header +0x04) |
| 0x34 | identity 0x68736D53 ('Smsh') |
| 0x38/0x3C | intrusive linked-list links |
MusicPlayerTrack = 0x50 bytes each: status byte @+0x00 (0xC0 = active on start, 0 = unused), track-data pointer @+0x40.
Song header: track count @+0x00, byte @+0x02, flags byte @+0x03 (bit7 set → MPlayStart calls SoundDriverMode with it), voicegroup pointer @+0x04, then one 4-byte track-data pointer per track from @+0x08.
Cycle count¶
Hardware-measured net CPU cycles (worker-ROM harness, TM0/TM1 cascade at F/1,
13-cycle baseline subtracted; identical across 3 runs — see
02_hardware_verification_checklist.md § Measurement setup).
4 tracks: 331 cycles on the first open, 358 on re-open.
Open questions (need hardware verification)¶
- Full MusicPlayerInfo and MusicPlayerTrack (
0x50-byte) layouts. - Exact meaning of the
0x8000initial flag and the id assigned during linkage.
GBATEK cross-reference¶
GBATEK marks SWI 0x20 as reserved/undocumented; this identifies it as the sequencer
MPlayOpen operating on the 0x68736d53-tagged MusicPlayerInfo.