SWI 0x19 — SoundBias¶
- Entry:
0x00000800(THUMB; SWI table stores0x00000801) - Status: verified (hardware-checked 2026-07-08: cycle counts and return registers measured on real GBA; struct layouts remain from static analysis)
Summary¶
Gradually ramps the bias level field of the SOUNDBIAS register (0x04000088)
up to its normal running value (0x200) or down to 0, changing it by 2 LSB per
step with a short busy-wait delay between steps. Used to fade the analog output
DC bias in/out so that switching the sound hardware on or off does not produce an
audible click/pop. Typically called with a non-zero argument at sound init, and
with 0 before powering the sound system down (or entering Stop mode).
Parameters¶
| Reg | In | Meaning |
|---|---|---|
| r0 | flag | 0 = ramp bias level down to 0; non-zero = ramp bias level up to 0x200 |
Returns¶
| Reg | Out | Meaning |
|---|---|---|
| — | — | No return value; SOUNDBIAS has been driven to the target level |
Clobbered registers¶
r1, r2, r3, ip (r12). r0 preserved-in-value not guaranteed.
Hardware audit (2026-07-08, canary r4–r12 + CPSR snapshot): caller-visible clobbers: r0 (flag) preserved; r1 = final bias level (0x200); r3 = 0x04000088 (the SOUNDBIAS address); r2 preserved. 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¶
ipis loaded with the constant0x200(the normal/high bias level target).- Read the 16-bit
SOUNDBIASregister and isolate the current bias level (the low 10 bits, i.e.value & 0x3FF; the hardware bias-level field is bits 1..9). - Compare against the target selected by r0:
- r0 != 0 (ramp up): if current level
>= 0x200, stop. Otherwise add 2. - r0 == 0 (ramp down): if current level
<= 0, stop. Otherwise subtract 2. - Write the adjusted 16-bit value back to
SOUNDBIAS. - Busy-wait a fixed short delay (a countdown loop of ~9 iterations).
- Loop back to step 2 until the target is reached.
The function only ever ramps to the two fixed endpoints 0 or 0x200; r0 is a
direction selector, not a target level.
Edge cases & known bugs¶
- Only the bias-level bits are modified; the amplitude-resolution/sampling-cycle
bits (14–15) of
SOUNDBIASare read-modify-preserved implicitly because the written value is the full 16-bit register with only the level field changed. - The step size of 2 means an odd starting level would be walked past its endpoint
parity, but the comparisons use
>=/<=so it still terminates. - The delay loop length is fixed; it is not scaled to CPU clock or WAITCNT.
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).
Full ramp 0x200→0: 15688; full ramp 0→0x200: 15942; already at target level: 70 (no ramp iterations).
Open questions (need hardware verification)¶
- Exact SOUNDBIAS bit layout assumptions (bias level in bits 1–9) vs. the 10-bit mask used here — confirm whether bit 0 is ever non-zero in practice.
- Real-world audibility of the 2-LSB step granularity.
GBATEK cross-reference¶
Matches GBATEK's description of SWI 19h SoundBias. Adds the precise detail that the
targets are hard-coded to 0 and 0x200, the step is ±2, and the delay loop count.