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 Penalty Model

A sprite fetch costs a fixed 6-dot core plus an alignment overhead set by (K + SCX) mod 8, writing K for SPRITEX (the sprite's OAM X). Stacked sprites at one X compose exactly: each sprite after the first adds 6.000 dots. This chapter measures both, and the two edge regimes — the prelude at SPRITEX < 8 and the right edge at K = 167.

At a glance

  • Every fetch core costs exactly 6.000 dots. The documented "6–11 dots per sprite" is the core plus 0–5 dots of BG-fetcher alignment.
  • The maximum +11.000 plateau occurs at (K + SCX) mod 8 = 0.
  • N sprites at one X: first sprite's penalty + (N − 1) × 6.000 dots — measured exact through N=10.
  • SPRITEX < 8 hides the fetch in the startup prelude — same total cost, different shape.
  • The K=167 right edge is not special: the single-sprite cost floors at the 6-dot core and the same composition rule applies per effective sprite.

Penalty by SPRITEX

Single-sprite and four-sprite penalty by SPRITEX (= OAM X; displayed X + 8), SCX=0 (dmg-sim measurement, gbmicrotest sprite ROMs; firing-scanline median minus non-firing median):

SPRITEXSingle sprite (dots)Four sprites (dots)
0+11.000 (prelude-absorbed)—
8+11.000+45.030
9+10.000+40.937
10—+36.843
11—+32.749
12—+27.646
13—+24.562
14—+27.895 (tile-boundary anomaly)
15—+38.643 (tile-boundary anomaly)

The single-sprite cost decrements exactly 1 dot per +1 SPRITEX over [8, 9]. The four-sprite penalty decrements a clean ~4.094 dots per +1 SPRITEX over the first three steps of [8, 13]. That is ≈1 dot of alignment per sprite × 4 sprites. The SPRITEX=12 row (+27.646) carries a sub-dot wobble. Its non-firing baseline itself drifts ~1 dot by tile-boundary alignment, so the 11→12 and 12→13 steps read −5.103 / −3.084 rather than the clean −4.094. At SPRITEX ∈ {14, 15} the sprites straddle a BG tile boundary. Per-sprite alignment overhead then re-enters in a form the linear model does not capture (dmg-sim measurement).

Measured: the plateau is a function of (K + SCX) mod 8

The +11.000 value reproduces at (K, S) = (8, 0), (7, 1), and (167, 1), writing S for SCX. All three pairs have (K + S) mod 8 = 0. Variance is zero across 72 firing scanlines per ROM (dmg-sim measurement).

Stacked sprites at one X: the chain collapses to combinational

When N sprites share an OAM X byte, each sprite runs its full fetch core. The inter-fetch gap collapses to gate delays: WUTY↑ᵢ → VEKU → TAKA↓ᵢ → SOWO↑ → TEKY↑ᵢ₊₁, with no clocked element on the path. SACU never resumes between fetches, the BG counter stays at 5, and LYRY stays high. That state is exactly what keeps the AND4 re-fire chain armed. Each completed fetch's per-slot fetched-flag DFF (EBOJ/CEDY/FONO family, clocked by WUTY) clears that slot's X latch. The next unfetched same-X slot therefore drives FEPO for the re-fire. The chain ends when no unfetched same-X slot remains.

Measured cadence (zero variance, 7 firing scanlines per N, the gambatte 10spritesPrLine K=167 chain):

StageΔ from TEKY↑ᵢ (dots)
SOBU↑ᵢ+0.481
TAKA↑ᵢ+0.484
WUTY↑ᵢ (counter=5)+5.998
TAKA↓ᵢ+5.999
TEKY↑ᵢ₊₁ (same dot)+5.999

Rule: stacked-sprite composition

mode3_ext(N, K, S) = single_sprite_penalty(K, S) + (N − 1) × 6.000 dots

The rule's measured anchors:

ConfigurationPredictedMeasured
N=1, K=0 (prelude-absorbed)11.00011.000
N=2, K=017.00017.000
N=3, K=023.00023.000
5 × K=7 + 5 × K=167, SCX=170.00070.000

The Mooneye intr_2_mode0_timing_sprites calibration table lists extras of 2/4/5/7/8/10/11/13/14/16 M-cycles for N = 1..10. Those figures are M-cycle rounding of this uniform 6-dots-per-sprite extension. There is no hardware shared-fetch alternation. The BG fetcher plays no per-sprite role, because it is frozen throughout the chain. Multi-group configurations compose per X-group. When PX passes from group A to group B, FEPO drops, SACU resumes, and the BG fetcher advances normally. Group B then re-pays its own first-sprite alignment.

The right edge: K = 167

At K = 167 the X match fires at the dot WODU would otherwise fire. The cost is not right-edge-special. (167 + 0) mod 8 = 7 puts the single sprite at the alignment model's 6-dot floor. The stacked-composition rule applies unchanged: ext = 6.000 + (N − 1) × 6.000 per effective sprite. Measured (dmg-sim measurement, gambatte 10spritesPrLine_10xposA7 ROM — nine on-screen sprites — and N ∈ {1,2,3,4} patched variants, zero variance, 7 firing lines each):

Effective NMode 3 extension (dots)Integer part
1+5.9786 = 1 × 6
2+11.975 (two ROM variants)12 = 2 × 6
3+17.97818 = 3 × 6
9+53.97754 = 9 × 6

The ~0.02-dot residual against exact N × 6 is the end-of-chain WODU rise arriving through the FEPO↓/XENA arm rather than the PX-decode arm. Per-sprite TEKY spacing in the long chain is 5.947 dots between #1 and #2, then exactly 6.000 between each subsequent pair. The chain ends in a final TEKY glitch pulse as FEPO falls, too narrow for SOBU's capture.

Sprites at SPRITEX < 8: the fetch hides in the prelude

When the X match fires before the first SACU↑, the whole fetch lands inside the AVAP→first-SACU prelude instead of between SACU pulses. The total cost is the same; only the shape differs. Measured at SPRITEX=0 (zero variance, 23 firing scanlines): the real TEKY fires at +11.255 dots from AVAP. That is the second LYRY-high event; the TAKA carry-over clear consumes the first. The fetch runs +11.766 → +17.399, and the first SACU lands at +18.430, against a 7.026 baseline. Total Mode 3 duration at SPRITEX=0 is identical to the SPRITEX=8 case.