Skip to content

SWI 0x0B — CpuSet

  • Entry: 0x00000B4C (THUMB; jump-table value 0x00000B4D)
  • Status: verified (hardware-checked 2026-07-08: cycle counts and return values measured on real GBA via the worker ROM)

Summary

Copies or fills a block of memory in 16-bit or 32-bit units. The transfer mode and length are packed into a single control word in r2. Unlike CpuFastSet, CpuSet works one unit at a time (simple LDRH/STRH or LDR/STR loop), so it can move any number of units and is used for small or odd-length transfers.

Parameters

Reg In Meaning
r0 u32 Source address. For fill mode, points to the single fill value.
r1 u32 Destination address.
r2 u32 Control word (see bit layout below).

Control word (r2) bit layout

Bits Meaning
0–20 Unit count (number of 16-bit or 32-bit units to write). Max 0x1FFFFF.
24 0 = copy (source advances), 1 = fill (source address fixed).
26 0 = 16-bit units, 1 = 32-bit units.

Bits 21–23, 25 and 27–31 are ignored (masked off when the byte length is computed).

Returns

None. Register outputs are not defined; memory at r1 is modified.

Clobbered registers

r3, r4, r5 are used internally (r4/r5 restored via the pushed frame). r0 and r1 are advanced in the 32-bit path. Caller-saved r3 is destroyed.

Hardware audit (2026-07-08, canary r4–r12 + CPSR snapshot): caller-visible clobbers: 16-bit copy returned r0/r1/r2 all unchanged; 32-bit fill advanced r0 by 4 and r1 past the written region; r3 = 0x170 leftover in both. 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. Extract the count: mask bits 0–20 and multiply by 4, giving the 32-bit byte length. (Implemented as (r2 << 11) >> 9.)
  2. Call the shared guard routine (see below). If the count is zero the whole transfer is skipped.
  3. Decode bit 26 (unit size):
  4. 32-bit units: compute end pointer r1 + bytelen. If bit 24 is set (fill) load one word from the source and repeatedly STMIA r1! it until r1 reaches the end. Otherwise LDMIA r0! / STMIA r1! one word per iteration.
  5. 16-bit units: halve the byte length (so it equals count×2). Iterate an index in steps of 2. Fill mode loads one halfword once and stores it at each index; copy mode does LDRH [r0+i] / STRH [r1+i].

Shared guard routine (0x00000BA4)

Common to CpuSet, CpuFastSet and BitUnPack. With ip = byte length and r0 = source: - If length == 0, return with Z set → caller aborts (nothing is transferred). - Otherwise compute end = source + length and test the source region: tst source, #0x0E000000 then tstne end, #0x0E000000. The transfer only proceeds when both the source start and source end have at least one of address bits 25–27 set, i.e. lie at or above 0x02000000. A source inside the 0x00000000–0x01FFFFFF range (BIOS / unused) yields Z set and the transfer is aborted. This acts as a guard preventing the copy routines from reading out of the BIOS ROM.

Edge cases & known bugs

  • Count 0: transfer skipped entirely.
  • Source in BIOS region (< 0x02000000): transfer silently skipped by the guard (see above).
  • Alignment: 16-bit mode needs halfword-aligned addresses, 32-bit mode needs word-aligned addresses. No explicit check — the ARM7TDMI forces alignment on misaligned accesses, so misuse produces rotated/wrong data rather than a fault.
  • Count is measured in units, not bytes.

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).

Operation Buffers Cycles
16-bit copy, n=4 EWRAM→EWRAM 156
16-bit copy, n=64 EWRAM→EWRAM 1056
16-bit copy, n=256 EWRAM→EWRAM 3936
32-bit copy, n=64 EWRAM→EWRAM 1374
32-bit copy, n=256 EWRAM→EWRAM 5214
16-bit fill, n=64 EWRAM 739
32-bit fill, n=64 EWRAM 868
16-bit copy, n=64 IWRAM→IWRAM 800
32-bit copy, n=64 IWRAM→IWRAM 734
BIOS-source guard trip (src=0x100, n=4) 84

EWRAM slopes ≈15 cycles/halfword (16-bit copy) and ≈20 cycles/word (32-bit copy) with ≈96 cycles fixed overhead. The BIOS-source guard returns in 84 cycles without writing anything (hardware-confirmed).

Open questions (need hardware verification)

  • Exact intent/semantics of the 0x0E000000 source-region guard (confirmed to abort BIOS-region sources in static analysis; verify on hardware).
  • Behavior when destination overlaps source in copy mode.

GBATEK cross-reference

Matches GBATEK's CpuSet: bit 0–20 count, bit 24 fixed-source (fill), bit 26 datasize. Adds the observation that a 0x00000000–0x01FFFFFF source is rejected by the shared guard routine.