Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

The IF Register

IF (FF0F) is five chip-level DFFs — one per source — outside the SM83 core, each with D tied high and the source as its clock. That one structural fact generates all the behaviour: edge-triggered capture, held-high sources that cannot re-set after a clear, and two distinct clear-vs-rise races with opposite outcomes.

At a glance

  • Each IF bit is a DFF with D tied high, clocked by its source — a held-high source cannot re-set a cleared bit.
  • FF0F-write clear vs same-cycle rise: rise wins (the clear pulse closes before the mid-M-cycle sources fire).
  • Dispatch-acknowledge clear vs same-cycle rise: clear wins (the ack pulse straddles the boundary and absorbs the edge).
  • IF survives LCD-off — no first-frame VBlank suppression of any kind.
BitCellClock (source)Source driver
0 (VBlank)LOPEint_vblPOPU.q (mode1) through TOLU + VYPU
1 (STAT)LALUint_statSUKO through TUVA + VOTY (STAT interrupts)
2 (Timer)NYBOint_timerMOBA.q — a CLK9-aligned DFF (timer)
3 (Serial)UBULint_serialCALY toggle at the transfer clock
4 (Joypad)ULAKint_jpAND2(APUG, BATU) — combinational, no source latch

Each cell's s_n (FF0F write of 1) and r_n (FF0F write of 0, or dispatch acknowledge) are active-low overrides. Since D is constant, only the clock matters: a held-high source cannot re-set a cleared bit — a fresh rising edge is required. That is the LALU edge-trigger rule generalised to all five bits.

Source cascades, measured

VBlank (POPU↑ → IF[0]):

POPU↑ → two-NOT buffer (768 ps) → cpu_irq0 (+1,064 ps) → irq_pending (+4,144 ps)

The whole chain is sub-dot. VBlank IF and the Mode 1 STAT condition co-rise on the same dot with nanosecond ordering.

The Mode 1 STAT leg at VBlank entry depends on the active enables:

  • LYC leg active — the entry traverses the SUKO glitch (the combining gate dips and recovers); IF[1] sets ~4.4 ns after POPU, ~3.6 ns behind IF[0].
  • Mode 0 + Mode 1 enables — the Mode 1 arm covers SUKO before the Mode 0 arm drops, so IF[1] does not change at all at the boundary; the measured handler reads IF[0]=1, IF[1]=0.

(dmg-sim measurements; the case analysis is the leg-swap table in STAT interrupts.)

The boundary into the CPU: the per-bit IF&IE latch inside the SM83 is an enabled D-latch transparent outside the data phase (interrupt dispatch) — a mid-M-cycle IF rise reaches irq_pending with no clock-edge wait; the first CLK9-aligned capture is YOII. Timer is the one boundary-aligned source (its driver is itself a CLK9 DFF), which is the entire origin of the source-class split in HALT wake latencies.

Race 1: CPU FF0F-write-clear vs same-M-cycle source rise

The FF0F write-clear pulse rides the CPU write strobe — measured co-located with CUPA (T3 to mid-T4, width 1.493 dots, matching to sub-ps). The mid-M-cycle PPU sources rise mid-T4 (+3.5 dots) — after the clear pulse closes (measured: the reset releases 1,155 ps before the VBlank clock arrives) — and the boundary-aligned timer source rose in T1, before the write window opened. Either way:

Rise wins. A source firing in the same M-cycle as a CPU clear of its bit leaves the bit set.

(The held-high case is the separate rule above: a clock that rose in an earlier M-cycle and stayed high cannot re-set after the clear.)

Race 2: dispatch-acknowledge clear vs same-M-cycle source rise

The acknowledge pulse is different: it asserts in T4 of the dispatch's vector-resolution M-cycle and releases only at the next M-cycle boundary — width ≈1.000 dot, straddling into the successor M-cycle's opening. A source edge landing inside it — measured with a second STAT dispatch deliberately aligned so the LY 143→144 SUKO-glitch edge fires +0.529 dots into the ack pulse — is captured but suppressed: the DFF sees the clock edge while r_n=0 holds Q at zero, and when the reset releases the clock is already high, so no later edge exists.

Clear wins. A source edge inside the acknowledge window is absorbed; IF stays 0 (dmg-sim measurement).

Clear pathPulse positionWidthvs +3.5-dot source rise
FF0F write+2.0–3.5 dots (T3–T4)1.493 dotslands after — rise wins
Dispatch ack+3.0–4.0 dots (T4 into next M-cycle)1.000 dotlands inside — clear wins

For implementors

Emulators that clear the IF bit as a single step "at the end of dispatch" miss the absorbing behaviour; the faithful model holds the reset active for the full acknowledge window.

Across LCD-off

LOPE (and its siblings) have no LCD-off reset path — their resets are the FF0F-write and acknowledge chains plus system reset only. IF survives LCDC.7 toggles; POPU (which is LCD-off-reset) restarts cleanly and the first post-LCD-on VBlank entry sets IF[0] with the steady-state edge structure — no first-frame gating of any kind (dmg-sim measurement; LCD-on power-up).