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 Y Comparator

During the scan, the hardware compares each sprite's Y position against LY. The comparison is an 8-stage full-adder carry chain with a NAND6 decoder, and the match result is combinational — there is no DFF capture of the match during the scan. sprite_y_match gates the per-slot sprite-store write-enables directly via the CARE chain.

GateRoleTypeClock / TriggerNotes
GESEsprite_y_match drivernot_x1combinational (input: WOTA)sprite_y_match = NOT(WOTA)
WOTAY-compare decodernand6combinationalInputs: GACE, GUVU, GYDA, GEWY, wuhu_c, GOVU. Low when the Y-in-range condition holds
GYKYCarry-chain bit 3full_addcombinationalSee the chain table
GOVULCDC.2 maskor2combinationalGOVU = OR2(ff40_d2, GYKY) — masks the bit-3 comparison for 8×16 sprites
XUSOOAM Y-offset bit 0 latch (Mode 2) / tile-index bit 0 latch (Mode 3)dlatch_eeEnable: COTAData: YDYV (Stage 1). xuso_n feeds carry-chain bit 0 operand B in Mode 2 and the sprite tile VRAM row-bit derivation in Mode 3 (Sprite pipeline)
Y-side Stage-1 register8 × dlatch on the bank-B busdlatchEnable: oam_data_latch (shared)YDYV, YCEB, ZUCA, WONE, ZAXE, XAFU, YSES, ZECA — capture the even byte of the addressed OAM byte-pair (DMA for the byte-pair architecture)

The chain computes LY − spriteY in two's-complement form:

  • Operand A — LY, complemented: each a-input is a not_x1 on a V-counter bit (EBOS…GUSU = NOT(v0v7)).
  • Operand B — the OAM Y byte, complemented: the Stage-2 latches' q̄ outputs (XUSO and its seven siblings, each holding the Stage-1 byte through the COTA-gated rank).
  • Stage-0 carry-in — constant 0.
StageGatea input (NOT LY bit)b input (OAM-Y latch q̄)cincoutsum
0ERUCEBOSxuso_nconst0eruc_cERUC
1ENEFDASAxegu_neruc_cenef_cENEF
2FECOFUKYyjex_nenef_cfeco_cFECO
3GYKYFUVExyju_nfeco_cgyky_cGYKY
4GOPUFEPUybog_ngyky_cgopu_cGOPU
5FUWAFOFAwyso_ngopu_cfuwa_cFUWA
6GOJUFEMOxote_nfuwa_cgoju_cGOJU
7WUHUGUSUyzab_ngoju_cwuhu_cWUHU

The decoder is WOTA = NAND6(GACE, GUVU, GYDA, GEWY, wuhu_c, GOVU). Its six inputs:

  • GACE, GUVU, GYDA, GEWY = NOT(GOPU, FUWA, GOJU, WUHU) — the upper-nibble sums, inverted (not_x1 cells, netlist-derived);
  • wuhu_c — the top-stage carry-out, the sign of the difference;
  • GOVU — the LCDC.2 size mask (below).

WOTA asserts (low) exactly when sum bits 4–7 are all zero, the sign carry is set, and the size mask holds — i.e. the difference lies in the sprite-height range.

LCDC.2 (OBJ_SIZE) in the comparator. GOVU = OR2(XYMO, GYKY): with XYMO=1 (8×16 sprites) the OR masks one bit of the range comparison, accepting a 16-row match; with XYMO=0 it passes GYKY unchanged for 8 rows. Because the compare is combinational, a mid-scan CUPA write to LCDC.2 splits the scan — entries before the write use the old range, entries after use the new — so hybrid sprite-store populations are possible. XYMO's other consumer (the sprite tile VRAM address bit, Mode 3) lives in the sprite pipeline.

Capture durability. XUSO's enable (COTA) is high through most of Mode 3 steady-state, so XUSO is transparent there — its held Mode-2 value survives only because the Stage-1 latch upstream (YDYV) holds when oam_data_latch stops pulsing. On scanlines with Mode-3 sprite fetches, each fetch pulses oam_data_latch once and overwrites the Stage-1 byte-pair with the fetched sprite's (tile index, attribute) — the Mode-2 Y bits do not survive past the first fetch. This breaks nothing: the Y-compare chain is only consumed during Mode 2.