Skip to content

SWI 0x1B — SoundDriverMode

  • Entry: 0x0000179C (THUMB; SWI table stores 0x0000179D)
  • 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

  1. Load SoundArea via [0x03007FF0]; if its identity word ≠ 0x68736d53, return.
  2. Bump the identity word (busy lock).
  3. Reverb: if (mode & 0xFF) != 0, store mode & 0x7F to +0x05.
  4. Channel count: if (mode >> 8) & 0xF, store it to +0x06 and clear all 12 channel status bytes.
  5. Master volume: if (mode >> 12) & 0xF, store it to +0x07.
  6. DAC resolution: if bits 20–21 set, merge them into SOUNDBIAS bits 14–15.
  7. Sampling rate: if bits 16–19 set, call SoundDriverVSyncOff then the frequency helper to re-derive rate/period fields and restart DMA.
  8. 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 on mode & 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.