Skip to content

SWI 0x04 — IntrWait

  • Entry: 0x00000330 (ARM)
  • Status: verified (hardware-checked 2026-07-08: fixture-driven runs on real GBA via the worker's no-blacklist opcode; see Hardware verification section)

Summary

Repeatedly Halts the CPU until one or more of a caller-specified set of interrupts has been serviced, tracked through the BIOS interrupt-flag word at 0x03007FF8. The flag word is maintained by the user's IRQ handler (the BIOS IRQ dispatcher does not set it), and IntrWait acknowledges (clears) the bits it was waiting for before returning. This is the building block for VBlankIntrWait (SWI 05h).

Parameters

Reg In Meaning
r0 discard-old flag 0 = return as soon as a matching flag is (or already was) set; nonzero = clear any already-pending matching flags first, then wait for a fresh interrupt.
r1 interrupt mask Bitmask of interrupts to wait for, in the same bit layout as REG_IE/REG_IF (bit0 = VBlank, etc.).

Returns

Returns once at least one requested bit has been seen and acknowledged. No explicit return value in registers.

Clobbered registers

r0r4, ip (r12) are used as scratch. r4 is callee-saved/restored.

Algorithm

Uses a check-and-acknowledge helper (0x00000358) that runs with interrupts briefly masked: 1. Write 0 to REG_IME (0x04000208) to block IRQs during the read-modify-write. 2. Read the 16-bit BIOS flag word from 0x03007FF8 (accessed via IWRAM mirror [0x04000000 - 8]). 3. Compute matched = mask(r1) & flags. If matched != 0, clear those bits in the flag word (flags ^= matched) and write the result back to 0x03007FF8. 4. Write 1 to REG_IME to re-enable IRQs. 5. Return with the CPU Z-flag reflecting whether anything matched (Z set = no match yet).

Main loop: 1. If r0 != 0, call the helper once up front to clear any already-pending requested flags (so the wait blocks until a new interrupt arrives). 2. Halt the CPU by writing 0 to HALTCNT (0x04000301); the CPU sleeps until any enabled interrupt fires. 3. After wake-up, call the helper to test/acknowledge the requested bits. 4. If none of the requested bits were set, loop back to step 2 (Halt again). Otherwise return.

Real constants observed: BIOS flag word 0x03007FF8; REG_IME = 0x04000208; HALTCNT 0x04000301.

Edge cases & known bugs

  • Requires a cooperating IRQ handler. IntrWait only works if the program's installed IRQ handler ORs serviced interrupt bits into 0x03007FF8. If the handler does not, the requested bit is never observed and IntrWait halts forever. (The BIOS IRQ dispatcher at 0x128 merely calls the user handler at 0x03007FFC; it does not touch 0x03007FF8 itself.)
  • r0 = 0 first-iteration quirk. On the r0 = 0 path the initial helper call is skipped, so ip (the I/O base) is not yet loaded when the first HALTCNT store executes; at that point ip still holds the handler entry address passed by the SWI dispatcher, so the first "Halt" write lands in ROM and does nothing (no true halt). The routine then polls the flag word once and returns immediately if a match already exists; only from the second iteration onward (after the helper sets ip = 0x04000000) does a real Halt occur. Net effect matches the documented "return immediately if already requested" behavior, but the mechanism is subtle — flagged for verification.
  • Only the low 16 bits of the mask are meaningful (matches REG_IF width).

Hardware verification (2026-07-08)

Fixture-driven runs via the worker's no-blacklist opcode (swf), with wake sources / handlers installed through CpuSet pokes. Raw data: results/blacklist_fixtures1.csv.

Timer-2 IRQ fixture with a minimal user handler (acks IF and mirrors bits into 0x03007FF8), results/blacklist_fixtures1.csv: - r0=1, r1=TIMER2: slept until the next TM2 IRQ (measured 839,179 cycles = the phase remainder of the running timer), consumed the matched BIOS_IF bit, and returned. IME was left = 1 afterwards (IntrWait force-enables it and does not restore). - r0=0 with the requested bit already pending in 0x03007FF8: returned in 95 cycles — immediate return, no halt, flag consumed. This is the caller-visible behavior predicted by the first-iteration HALTCNT-quirk analysis (95 cycles is far too fast for any real halt/IRQ round trip). - r0=0 with the flag clear: slept until the next IRQ exactly like r0=1.

Cycle count

95 cycles (raw) for the r0=0 already-pending immediate return; otherwise sleep-until-IRQ (externally determined).

Open questions (need hardware verification)

  • Confirm the r0 = 0 first-iteration ROM-write behavior described above on real silicon.
  • Confirm the flag word is treated as 16-bit and that upper bits of r1 are ignored.

GBATEK cross-reference

Matches GBATEK SWI 04h IntrWait: polls [3007FF8h], acknowledges by clearing the matched bits, and depends on the user IRQ handler maintaining that word. This doc adds the concrete IME-masked read-modify-write sequence and the r0 = 0 first-iteration detail.