Skip to content

SWI 0x0C — CpuFastSet

  • Entry: 0x00000BC4 (ARM)
  • Status: verified (hardware-checked 2026-07-08: cycle counts and return values measured on real GBA via the worker ROM)

Summary

High-speed memory copy or fill in fixed blocks of eight 32-bit words (32 bytes) using LDMIA/STMIA. Only 32-bit units are supported. Because it always moves whole 8-word blocks, the length should be a multiple of 8 words; non-multiples are rounded up (see edge cases).

Parameters

Reg In Meaning
r0 u32 Source address (word aligned). For fill, points to the fill word.
r1 u32 Destination address (word aligned).
r2 u32 Control word (see below).

Control word (r2) bit layout

Bits Meaning
0–20 Word count.
24 0 = copy (source advances), 1 = fill (source word fixed).

Unit size is always 32-bit; there is no datasize bit.

Returns

None. Memory at r1 is modified.

Clobbered registers

r2–r9, sl (r10), ip (r12) used internally; r4–r9/sl restored from the pushed frame. r0/r1 advance during copy. r2, r3, ip destroyed.

Hardware audit (2026-07-08, canary r4–r12 + CPSR snapshot): caller-visible clobbers: r0/r1 advanced past the source/destination end; r2 (count) preserved; r3 garbage (0x0F080000 observed). 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. Compute byte length = (count bits 0–20) × 4.
  2. Call the shared guard routine at 0x00000BA4 (same as CpuSet): zero-length aborts, and a source below 0x02000000 is rejected.
  3. Compute end pointer sl = dest + bytelen.
  4. Decode bit 24:
  5. Fill: load one source word into r2, copy it into r3–r9 (eight identical registers), then loop STMIA r1!, {r2..r9} while r1 < sl.
  6. Copy: loop LDMIA r0!, {r2..r9} then STMIA r1!, {r2..r9} while r1 < sl.

Each iteration transfers exactly 8 words (32 bytes).

Edge cases & known bugs

  • Count not a multiple of 8: the loop condition is only r1 < end. Since each pass writes a full 8-word block, the routine keeps writing complete blocks until the destination pointer reaches or passes end. It therefore rounds the count up to the next multiple of 8 words, overwriting up to 7 words past the requested region (and, in copy mode, reading up to 7 words past the source end). Callers must size buffers accordingly. Hardware-verified: a copy with count=12 wrote exactly 16 words; sentinel values beyond word 16 were untouched.
  • Count 0: aborted by the guard, nothing written.
  • Source below 0x02000000: aborted by the guard (BIOS-region protection).
  • Alignment: addresses must be word-aligned; misalignment is force-aligned by the CPU (LDM/STM ignore the low two address bits).

  • BIOS-source guard hardware-confirmed (2026-07-08 anomaly tests, results/cycles_phase3_anomaly.csv): src=0x100 returns in 86 cycles with the destination untouched.

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
copy, n=8 EWRAM→EWRAM 196
copy, n=64 EWRAM→EWRAM 917
copy, n=256 EWRAM→EWRAM 3389
fill, n=64 EWRAM 532
copy, n=64 IWRAM→IWRAM 277
copy, n=12 (not a multiple of 8) EWRAM→EWRAM 299

EWRAM copy slope ≈12.9 cycles/word (≈103 per 8-word LDM/STM block); the same 64-word copy in IWRAM costs 277. The n=12 case wrote exactly 16 words (readback-verified — see Edge cases).

Open questions (need hardware verification)

  • Confirm the round-up overshoot on real hardware for a non-multiple-of-8 count.
  • Exact semantics of the shared 0x0E000000 source-region guard.

GBATEK cross-reference

Agrees with GBATEK (8-word blocks, bit 24 fill, 32-bit only). Clarifies the documented "must be multiple of 8 words" constraint: a non-multiple is rounded up and overshoots, it is not truncated.