SWI 0x1B — SoundDriverMode¶
- Entry:
0x0000179C(THUMB; SWI table stores0x0000179D) - Status: verified (hardware-checked 2026-07-08: cycle counts and return registers measured on real GBA; struct layouts remain from static analysis)
Summary¶
Reconfigures the running PCM mixer from a single packed 32-bit mode word:
reverb depth, number of active mixer channels, master volume, PCM sampling-rate
index, and the output DAC amplitude resolution. Each sub-field has an "unchanged"
encoding (zero) so callers can update selected parameters only. Locates the
SoundArea through the global pointer at 0x03007FF0 and verifies the identity word
before acting (no-op if the area is not initialized/locked).
Parameters¶
| Reg | In | Meaning |
|---|---|---|
| r0 | mode | packed configuration word (see bitfield below) |
Mode bitfield (r0)¶
| Bits | Field | Meaning |
|---|---|---|
| 0–6 | reverb value | 0..0x7F written to SoundArea+0x05 |
| 7 | reverb-set flag | low byte must be non-zero for the reverb write to happen (bit 7 = "apply", allowing reverb=0 to be set explicitly) |
| 8–11 | channel count | number of active PCM mixer channels (1..12); 0 = leave unchanged. Also resets all 12 channels' status bytes |
| 12–15 | master volume | 0..15 → SoundArea+0x07; 0 = unchanged |
| 16–19 | sampling-rate index | 1..12 selects PCM output rate (table below); 0 = unchanged |
| 20–21 | DAC amplitude resolution | → SOUNDBIAS bits 14–15 (bit-depth vs. sampling cycle); 0 = unchanged |
When the channel-count field is non-zero the routine additionally clears the status
byte of every one of the 12 PCM channels (SoundArea + 0x50 + i*0x40, i=0..11).
When the sampling-rate field is non-zero it first calls SoundDriverVSyncOff
(stops the FIFO DMA), then re-runs the internal frequency setup helper (0x170A).
PCM sampling-rate table¶
The frequency index (mode bits 16–19, valid 1..12) selects a "PCM samples per
V-blank frame" value from a 16-bit table at 0x000031E8, stored to SoundArea+0x10.
Dividing by the ~59.7275 Hz frame rate gives the effective output sample rate:
| Index | Samples/frame | ≈ Sample rate (Hz) |
|---|---|---|
| 1 | 0x060 (96) | 5734 |
| 2 | 0x084 (132) | 7884 |
| 3 | 0x0B0 (176) | 10512 |
| 4 | 0x0E0 (224) | 13379 |
| 5 | 0x108 (264) | 15768 |
| 6 | 0x130 (304) | 18157 |
| 7 | 0x160 (352) | 21024 |
| 8 | 0x1C0 (448) | 26758 |
| 9 | 0x210 (528) | 31536 |
| 10 | 0x260 (608) | 36314 |
| 11 | 0x2A0 (672) | 40137 |
| 12 | 0x2C0 (704) | 42048 |
The helper also computes the per-DMA-buffer period and timer reload from this value
(divisions via the internal 0x3720 divide helper) and stores them into the
SoundArea. Index 4 (~13379 Hz) is the SoundDriverInit default.
Returns¶
| Reg | Out | Meaning |
|---|---|---|
| — | — | SoundArea reconfigured |
Clobbered registers¶
r1–r5, r7.
Hardware audit (2026-07-08, canary r4–r12 + CPSR snapshot): caller-visible clobbers: r0 (mode word 0) unchanged, r1 = 0, 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¶
- Load SoundArea via
[0x03007FF0]; if its identity word ≠0x68736d53, return. - Bump the identity word (busy lock).
- Reverb: if
(mode & 0xFF) != 0, storemode & 0x7Fto +0x05. - Channel count: if
(mode >> 8) & 0xF, store it to +0x06 and clear all 12 channel status bytes. - Master volume: if
(mode >> 12) & 0xF, store it to +0x07. - DAC resolution: if bits 20–21 set, merge them into SOUNDBIAS bits 14–15.
- Sampling rate: if bits 16–19 set, call SoundDriverVSyncOff then the frequency helper to re-derive rate/period fields and restart DMA.
- Restore the identity word and return.
Edge cases & known bugs¶
- Reverb is only written when the whole low byte is non-zero; to set reverb = 0 you must set bit 7 (otherwise the field is treated as "unchanged").
- Sampling-rate index 0 and values > 12 are out of the intended range; index 0 skips the update, higher indices would read past the intended table region.
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).
Mode word 0 (every field "no change"): 126 cycles. Mode word 0x0084F800
(channel count / volume / frequency / DAC bits set): 64323 cycles — the
sampling-rate change re-runs the same PCM re-initialization as
SoundDriverInit.
Open questions (need hardware verification)¶
- ~~Exact SOUNDBIAS resolution encoding for bits 20–21 → 14–15~~ — resolved by static analysis (Ghidra 12.1.2 decompilation):
*(u8*)0x04000089 = ((mode & 0x300000) >> 14) | (*(u8*)0x04000089 & 0x3F), i.e. mode bits 20–21 map directly onto SOUNDBIAS bits 14–15 (amplitude resolution / sampling cycle). The write is gated onmode & 0xB00000(bits 20, 21, 23) — a quirk: setting bit 23 alone clears the field. - Whether changing channel count mid-playback safely silences dropped voices.
GBATEK cross-reference¶
Confirms and details GBATEK's SoundDriverMode bitfield and supplies the full sampling-rate table (matching GBATEK's documented rates), plus the exact reverb/channel/volume/DAC field boundaries.