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

Writes

Reads drive the bus combinationally (reads); writes work the other way around — the PPU is the receiver, so the write must strobe the PPU-side register latches closed on specific edges. Every writable PPU register (LCDC, STAT, SCX, SCY, LYC, BGP, OBP0, OBP1, WX, WY) is a level-sensitive latch, transparent during the CUPA strobe — which fires once per M-cycle at a deterministic position, making every writable register transparent at the same time.

At a glance

  • One shared strobe: every writable PPU register is a level-sensitive latch enabled by AND(address decode, CUPA).
  • CUPA rises at the start of T3 and falls mid-T4 — 1.493 dots wide; the latch is transparent for the whole pulse.
  • What a mid-rendering write affects is decided by the bus-settle instant (~17 ns into the pulse), not by the window edges.
  • An LCDC.7=1 write releases VID_RST 3,173 ps after CUPA↑ — purely combinational, no synchroniser — and the dividers count cleanly from zero, which is why the first scanline runs ≈454 dots.
GateRoleTypeClock / TriggerNotes
CUPAShared PPU register write strobecombinational (derived from phase gen)Primary here; fires once per M-cycle, 1.493 dots wide. Its output wire is named ppu_wr in the simulation — ppu_wr elsewhere in this book is this strobe
DYKYCUPA driver stage 1notInput: TAPU
TAPUCUPA driver stage 2notInput: UBAL
UBALCUPA driver stage 3muxiTest gate
APOVCUPA driver stage 4notInput: AREV
AREVCUPA driver stage 5nand2Inputs: cpu_write, AFAS
AFASRing decode driving CUPAnor2Inputs: ADAR, ATYPHigh three ATAL half-cycles per M-cycle (1.504 dots measured)
AFURPhase generator ring stage 1dffDriven by ATAL/ADEH4-stage ring (AFUR/ALEF/APUK/ADYK)
ALEFPhase generator ring stage 2dffDriven by ATAL/ADEH
APUKPhase generator ring stage 3dffDriven by ATAL/ADEH
ADYKPhase generator ring stage 4dffDriven by ATAL/ADEH
WARULCDC write enableand2Inputs: VOCA, CUPAEnables XUBO/XURE
SEPASTAT write enableand2Inputs: VARY, CUPAEnables RYVE/PUPU
VELYBGP write enableand2Inputs: WERA, CUPAEnables TEPO/LYFA (palette latches)
ARURSCX write enableand2Inputs: XAVY, CUPA
BEDYSCY write enableand2Inputs: XARO, CUPA
XODOVID_RST signalcombinationalInput: XONADeasserts 3,173 ps after CUPA↑ on an LCDC.7=1 write
XONALCDC.7 storage latchdrlatch_eed: d7; ena: XURE/XUBOq (ff40_d7) drives XODO/VID_RST on an LCDC.7 write

The shared CUPA strobe

All CPU-writable PPU registers are level-sensitive latches (drlatch_ee or dlatch_ee) whose enable is AND(address decode, CUPA):

  • LCDC: WARU = AND2(VOCA, CUPA) → XUBO/XURE
  • STAT: SEPA = AND2(VARY, CUPA) → RYVE/PUPU
  • BGP: VELY = AND2(WERA, CUPA) → TEPO/LYFA
  • SCX: ARUR = AND2(XAVY, CUPA)
  • SCY: BEDY = AND2(XARO, CUPA)
  • …and so on: every writable register shares CUPA, with per-register address decode.

Phase generator derivation

ADAR/ATYP ring decode → AFAS → AREV (with cpu_write) → APOV → UBAL → TAPU → DYKY → CUPA

The phase generator is a 4-stage ring (AFUR/ALEF/APUK/ADYK) driven by ATAL/ADEH, the complementary 4 MHz enables from the clock tree. Each stage output is high for four consecutive ATAL half-cycles — 2 dots — per M-cycle (measured: AFUR 487,713 ps, ADYK 488,188 ps). The AFAS decode is high for three half-cycles per M-cycle — 367,065 ps = 1.504 dots, from the ALET rising edge of dot 2 to the ALET falling edge of dot 3 (dmg-sim measurement) — and CUPA follows it through the five-stage drive chain at 1.493 dots.

CUPA pulse position

Measured across three independent CUPA pulses with identical alignment (dmg-sim measurement):

  • Rises at the start of T3, 4,848 ps after T3's ALET rising edge.
  • Falls mid-T4, 1,716 ps after its ALET falling edge.
  • Width: 364,298 ps = 1.493 dots.

The register latch is transparent for the entire pulse; bus data propagates through combinationally.

Alignment to TALU/SONO. One TALU cycle = one M-cycle = 4 dots. CUPA fires at the start of T3; the LX counter increments on TALU rising. That alignment decides which LX count a write lands on.

Behaviour on the write dot

While CUPA is high (T3–T4): the latch is transparent — combinational consumers and sampling DFFs see the new value as soon as the bus carries it. When CUPA falls (T4's ALET falling edge): the latch closes and holds.

The bus, not the strobe, sets the commit instant. The latch is open for the whole 1.493-dot pulse, but the captured value follows the CPU data bus — which settles ~17 ns in, just past the write-dot SACU↓. The palette write shows the consequence: the BGP dlatch updates ~15.6 ns after that pixel edge, so the pixel shifted on the write dot uses the OLD palette value (palette latches). What a mid-rendering write affects is decided by the bus-settle instant, not by the window edges. (clk_t4, the CPU's bus-drive clock, is the BAPY = NOR3(clk_ena_n, AROV, ATYP) decode — the same ring vocabulary as CUPA.)

VID_RST deassertion

After a CPU write sets LCDC.7=1, the VID_RST signal (XODO) deasserts through a purely combinational chain (dmg-sim measurement):

  • CUPA↑ → XONA↑ (+1,003 ps; the LCDC.7 bit captures) → XODO↓ (+2,170 ps further)
  • Total CUPA↑ to XODO↓: 3,173 ps (~0.013 dots)

At the instant XODO deasserts, the divider toggle DFFs are in reset (q=0): WUVU = VENA = BOGA = 0, with ATAL = ALET = 1. The combinational complements settle within gate delays of the just-reset q_n outputs: TALU = NOT(vena_n) = 0, XUPY = NOT(wuvu_n) = 0, SONO = NOT(TALU) = 1.

The dividers then count cleanly from zero (dmg-sim measurement):

EventTime after XODO↓Dots
WUVU.q↑ (toggle DFF)+114,938 ps+0.471
XUPY↑ (combinational from wuvu_n, +2,135 ps after WUVU.q↑)+117,073 ps+0.480
VENA.q↑ (toggle DFF, captures wuvu_n's first rise)+360,224 ps+1.476
TALU↑ (combinational from vena_n through the not_x4, +1,628 ps after VENA.q↑)+361,852 ps+1.483

VID_RST therefore clears within a single dot of the CPU's write; there is no multi-cycle synchroniser delay.

First-scanline consequence

The write's T3 landing plus the divider startup phase make the post-LCD-on first scanline run ≈454 dots instead of 456, the deficit entirely in its Mode 0 — Mode 2 and Mode 3 match steady state. The decomposition, the direct Mode-0-span measurement, and the phase-conservation proof live in LCD-on power-up.