Skip to content

agnus: model sprite DMA at the register level#116

Merged
LinuxJedi merged 2 commits into
mainfrom
fix/sprite-register-fsm
Jul 4, 2026
Merged

agnus: model sprite DMA at the register level#116
LinuxJedi merged 2 commits into
mainfrom
fix/sprite-register-fsm

Conversation

@LinuxJedi

Copy link
Copy Markdown
Owner

Summary

Sprite DMA is now modelled at the register level the way Agnus works,
replacing the descriptor-precompute model (vAmiga
executeFirstSpriteCycle/executeSecondSpriteCycle/updateSpriteDMA
semantics, ground-truthed against the real-A500 sprenacpu2 photos, which
match vAmiga exactly):

  • Each channel keeps Agnus-side SPRxPOS/SPRxCTL register copies and
    vertical comparator values, updated identically by DMA fetches and
    CPU/Copper pokes (POS = vstart low bits; CTL = vstart high bit + whole
    vstop + disarm). Line-start comparators set/clear the per-channel DMA
    flip-flop -- vstop clears it even when SPREN is off, which is what
    leaves a sprite dead until the next field when software disables DMA
    across its vstop line (the sprena/sprdis mechanism).
  • The vstop line's two slots fetch the next POS/CTL control words; the
    vertical-blank reset (PAL $19 / NTSC $14) forces every vstop to that
    line, which is how each field's first control-word fetch happens. No
    descriptor chain, no precomputed stream end: terminators, inverted
    vstop pairs, deferred starts and equal start/stop descriptors all fall
    out of the comparators naturally.
  • SPRxPT advances only on words actually fetched (the skew model is
    gone; the frame-start frontier is just the live pointer), and an armed
    channel with DMA off keeps redisplaying its latches until a CTL
    fetch/poke disarms it -- this renders the vAmigaTS sprena/sprdis
    vertical-bar staircase that the old model structurally could not
    produce.
  • Deleted with the descriptor model: register-stream seeding, SPRxPT
    retarget math, same-slot descriptor-restart discard, the save-state
    reconstruction hack (the register state is chip state and is now
    serialized/restored directly), ~250 net lines removed.
  • Sprite bus-slot arbitration follows the same rules (enabled channel or
    vstop-line control fetch claims the slots, vblank-gated).
  • VideoPipelineStats (host perf telemetry) is no longer serialized:
    call chunking legitimately differs across a save-state load.
    STATE_VERSION 17.

vAmigaTS

Structural: sprenacpu2 now renders the yellow armed-persistence bar
staircase that the A500 photo and vAmiga show (the old model painted a
drifting streak instead). The family's mismatch percentages stay
~30-45%
: those sweeps step CPU DMACON writes at 2-colour-clock
resolution over a CPU-timed flickering background, so the residual is
dominated by the known CPU-write-timing class (the #102/#104 residual),
not sprite modelling. interfere1/1b improved (7.2/5.3 -> 6.1/7.2 mixed),
manual1/2 and the collision buckets are unchanged.

Testing

  • Full suite green (1296 tests), clippy/fmt clean.
  • Sprite tests rewritten to hardware flows: control words load at the
    vertical-blank reset line (shared helper), pointer reloads land before
    it, inverted vstop now pins the control-word refetch on the vstop line
    (vAmiga semantics) instead of the old run-to-bottom clamp.
  • Byte-identity vs the two-slot-split baseline (agnus: evaluate sprite DMA lines at both hardware slots #114): kick13boot 15s,
    Gen-X 110s, Hamazing, ITM, A1200 Kickstart, Zool -- identical.
    Second Nature 40s is the known benign animation-phase drift (the FSM
    build reaches the byte-identical frame 0.04s earlier).
  • Sprite-sensitive regression demos vs current main: eon at 108s (the
    wolf/SPREN-clear scene class) byte-identical; Roots II AGA 60s
    byte-identical; Roots II ECS byte-identical at 20/35/50s with a
    timeline knife-edge crossing between 50s and 60s (same benign drift
    class as Second Nature).

LinuxJedi added 2 commits July 4, 2026 18:32
Sprite N's line was captured in one shot at its second DMA slot; now the
first slot ($15+4N) runs the line entry logic (vstop comparator,
descriptor chain, arming) and samples the DATA word(s) at that slot's
beam time, and the second slot ($17+4N) samples DATB at its own beam
time and assembles the display line. Chip RAM rewritten between the two
slots is therefore seen by DATB but not DATA, and a mid-line DMACON
SPREN edge fetches exactly one word of the pair, matching the real bus
timing. A skipped slot leaves SPRxPT one fetch behind the line-derived
stream position; the display line reuses the stale latch on that side
and a missed DATA slot never arms the sprite.

The single-shot path remains for the pre-display replay wrapper and as
the second-slot fallback when the first slot did not run. Stream
re-seeds (descriptor reload, SPRxPT retarget) clear the pending state,
and the per-line markers default to unset so a fresh state cannot
false-match line 0. STATE_VERSION 17 (DisplaySpriteDmaState gained
fields).

Byte-identical on the demo regression set (Gen-X 110s/620s, ITM,
Hamazing, Zool, Second Nature, Kickstart 1.3 and A1200 boots) vs main.
Replace the descriptor-precompute sprite model with the register-level
state machine the chip implements (vAmiga executeFirst/SecondSpriteCycle
and updateSpriteDMA semantics, ground-truthed against the real-A500
sprenacpu2 photos, which match vAmiga's render exactly):

- Each channel keeps Agnus-side SPRxPOS/SPRxCTL register copies and the
  vertical comparator values derived from them, updated identically by
  DMA control-word fetches and CPU/Copper pokes (POS supplies vstart's
  low bits, CTL the high bit plus the whole vstop, and disarms the
  display latches). Line-start comparators set/clear the per-channel DMA
  flip-flop; vstop clears it even when SPREN is off, which leaves a
  sprite dead until the next field when software disables DMA across its
  vstop line - the vAmigaTS sprena/sprdis mechanism.
- The vstop line's two slots fetch the next POS/CTL control words, and
  the vertical-blank reset (PAL $19 / NTSC $14) forces every channel's
  vstop to that line, which is how each field's first control-word fetch
  happens. Terminators, inverted vstop pairs, deferred starts and equal
  start/stop descriptors all fall out of the comparators naturally.
- SPRxPT advances only on words actually fetched: the word-skew model is
  gone and the frame-start frontier is simply the live pointer. An armed
  channel with DMA off keeps redisplaying its latches until a CTL
  fetch/poke disarms it, which renders the sprena/sprdis vertical-bar
  staircase the old model structurally could not produce.
- Deleted with the descriptor model: register-stream seeding, SPRxPT
  retarget math, the same-slot descriptor-restart discard, and the
  save-state reconstruction hack - the register state is chip state and
  is now serialized and restored directly.
- Sprite bus-slot arbitration uses the same rules (enabled channel or
  vstop-line control fetch claims the slots, vblank-gated), and
  VideoPipelineStats (host perf telemetry) is no longer serialized:
  call chunking legitimately differs across a save-state load.

The sprena/sprdis vAmigaTS percentages stay ~30-45%: those sweeps step
CPU DMACON writes at two-colour-clock resolution over a CPU-timed
flickering background, so the residual is the known CPU-write-timing
class, not sprite modelling. Structurally the renders now match the
photos' bar staircase.

Byte-identical on kick13boot, Gen-X, Hamazing, ITM, A1200 Kickstart,
Zool and Roots II AGA; Second Nature and Roots II ECS show the known
benign animation-phase drift class. STATE_VERSION 17.
@LinuxJedi

Copy link
Copy Markdown
Owner Author

Note: this branch is stacked on #114 (the two-slot fetch split) and includes its commit; merging #114 first will reduce this diff to the FSM commit alone, or this PR can be merged on its own and #114 closed.

@LinuxJedi LinuxJedi merged commit e66734b into main Jul 4, 2026
7 checks passed
@LinuxJedi LinuxJedi deleted the fix/sprite-register-fsm branch July 4, 2026 18:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant