SWI 0x18 — Diff16bitUnFilter¶
- Entry:
0x00001399(THUMB, real entry0x00001398) - Status: verified (hardware-checked 2026-07-08: cycle counts measured and output round-trip-verified on real GBA via the worker ROM)
Summary¶
Reverses a 16-bit difference filter: reconstructs a halfword stream from a first-value-plus-deltas encoding by running-sum accumulation on 16-bit units. Reads and writes halfwords throughout, so a single routine serves both RAM and VRAM (there is no separate Wram/Vram pair).
Parameters¶
| Reg | In | Meaning |
|---|---|---|
| r0 | ptr | Source address pointing at the 4-byte header. |
| r1 | ptr | Destination address (halfword-aligned, halfword-writable). |
Returns¶
No return value.
Clobbered registers¶
r0–r3. r4 saved/restored.
Hardware audit (2026-07-08, canary r4–r12 + CPSR snapshot): caller-visible clobbers: r0/r1 advanced past stream/output; r3 = last unfiltered halfword (0x506 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¶
The header word is read; bits 8–31 are the output size in bytes. Byte 0 is not read. The shared header/region check runs; on failure nothing is written.
Reconstruction operates on 16-bit units:
- The first source halfword is copied verbatim and kept as the running sum. The
size counter is decreased by 2.
- Each subsequent source halfword is a signed 16-bit delta; the running sum is
sum = (sum + delta) truncated to 16 bits and stored. The counter is decreased
by 2 each step; the loop ends when it reaches 0 (or below).
All memory access is 16-bit, so the routine is valid for VRAM/OAM/palette as well as ordinary RAM.
Edge cases & known bugs¶
- Decompressed size 0: shared header check returns "skip"; nothing written.
- Size counted in bytes but processed in halfwords: the counter is decremented by 2 per element. A size that is not a multiple of 2 still writes whole halfwords — the routine stops once the byte counter goes ≤ 0, effectively rounding the count to halfwords.
- Type/data-size nibble not enforced: byte 0 is ignored.
- Source-region protection: source in
0x00000000–0x01FFFFFFis refused. -
Running sum wraps mod 65536.
-
Halfword round-up hardware-confirmed (2026-07-08 anomaly tests,
results/cycles_phase3_anomaly.csv): declared size 255 wrote the full 256 bytes — the final halfword is written whole (its value correctly filtered), the opposite behavior of the byte-dropping Vram variants.
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).
256-byte output = 128 halfwords (EWRAM): 2261 cycles — roughly half the 8-bit variant's cost, as expected from halfword granularity.
Open questions (need hardware verification)¶
- Exact behaviour for a size that is odd in bytes (last partial halfword).
GBATEK cross-reference¶
Matches GBATEK Diff16bit unfilter (running 16-bit sum). Adds: header byte 0 ignored, size treated as bytes-decremented-by-2, and the source-region gate.