Skip to content

copper: model the WAIT/SKIP/COPJMP pipeline against the vAmiga copper trace#146

Merged
LinuxJedi merged 3 commits into
mainfrom
fix/copper-write-landing
Jul 7, 2026
Merged

copper: model the WAIT/SKIP/COPJMP pipeline against the vAmiga copper trace#146
LinuxJedi merged 3 commits into
mainfrom
fix/copper-write-landing

Conversation

@LinuxJedi

@LinuxJedi LinuxJedi commented Jul 7, 2026

Copy link
Copy Markdown
Owner

Rebased onto main c4c7207 (#141-#145); all numbers below are re-measured against a fresh c4c7207 baseline. savestate::STATE_VERSION is now 26 (#145 took 25). The interrupt-latency compensation from the pre-rebase version was dropped: #144's IPL poll placement models the recognition, and with the offset the Copper/Irq family regressed 15.0 -> 42.3 (without it: 14.9).

The class

Copper MOVE-after-WAIT register writes landed ~4 colour clocks late relative to vAmiga/real hardware (SPR0CTL at hpos $26 where the vAmigaTS spritedma/interfere real-A500 photos and vAmiga land it at $22). Three independent investigations converged on this class as the dominant Agnus/Copper residual.

Two-sided trace evidence

Both emulators instrumented with env-gated landing probes: COPPERLINE_DIAG_COP_WRITES=1 (new, this PR) and VAMIGA_COP_PROBE=1 in the local vAmiga tree (snapshot: vamigats-reports/vamiga-cop-probe.patch). Denise/Sprites/spritedma/interfere2 (WAIT $4721 / MOVE SPR0CTL / MOVE COLOR00 / WAIT $47B1 / MOVE COLOR00), per-frame steady state:

event vAmiga CL before CL after
WAIT $4721 wakeup v=047 h=1e (match at h=20) match at h=1e
MOVE SPR0CTL=$7F00 v=047 h=22 v=047 h=26 v=047 h=22
MOVE COLOR00=$F00 h=26 h=2a h=26
MOVE COLOR00=$000 (after $47B1) h=b2 h=b6 h=b2

The jumpbpu trace pinned two more pipeline differences: the frame-start stream (vAmiga's first line-0 MOVE write lands at h=$04; CL's landed at $08) and the post-COPJMP fetch (vAmiga strobe write at h=$04 fetches the new list's first MOVE at $0A, write at $0C; CL wrote at $08). Both are now cycle-exact against the vAmiga trace.

The pipeline model (all vAmiga-verified)

  1. Comparator horizontal lookahead: the WAIT/SKIP comparator's horizontal input runs two colour clocks ahead of the beam and wraps through zero over the last three clocks of a line (vAmiga runHorizontalComparator). A sleeping WAIT wakes at target-2; the match clock is the bus-free wake-up cycle; the next fetch lands on the masked target, its write two clocks later.
  2. WAIT/SKIP tail: two Copper cycles after the second word fetch (Minimig WAITSKIP1/2, vAmiga COP_WAIT1/2) -- an immediately-true WAIT resumes fetching at fetch2+6.
  3. SKIP decision at the next first-word fetch (vAmiga evaluates the skip flag inside COP_FETCH): a line-end SKIP pushed past the wrap by the bus lockout sees the next line's vertical phase, which is what fires jumpbpu-style list switches.
  4. COPJMP strobe tail: two Copper cycles (COP_JMP1/COP_JMP2) before the PC reloads from the live location register.
  5. Line-end bus lockout at $E0 on short lines, $E1 on long lines (vAmiga busIsFree<COPPER>), previously fixed $E1.
  6. Frame start at hpos $02 (first fetch), previously $06.

One calibration that had absorbed the old +4 landing moves in lockstep, with a TODO naming the CPU write-landing class it still compensates: copper-sourced render events are recorded at their Denise-effective position (DENISE_WRITE_EFFECT_DELAY_CCK, bus landing +4; CPU-sourced writes already carry that offset in their landing).

vAmigaTS results on the rebased stack (tools/vamigats-compare.py, diff% family sums, baseline = main c4c7207)

family main c4c7207 this PR note
Agnus/Copper (223) 1002.9 949.2 (-5.4%) target family
- Skip 65.1 1.5 copstrt1/2 28.3 -> 0.000, copskip1 -> 0.0
- coptim 50.5 44.2 must-hold: improved
- mask 113.5 86.2
- cross 217.2 193.9 cross6 14.0 -> 0.024
- oldJump 138.0 132.0 jump2 -> 0.012, jumpbpu at base
- Irq 15.0 14.9 holds (compensation-free on top of #144)
- Wait / copvbl / CopDma 7.6 / 234.6 / 55.1 7.9 / 236.8 / 54.3 hold
- lc / halt / copbpl hold hold
- coprace 96.4 167.4 worst residual: 2 CPU-vs-copper COLOR race programs; the CPU loop's stripes interleave through bus contention with the shifted copper stream
Denise/Sprites/spritedma (66) 128.0 126.5 interfere1/1b/5 improved, interfere2/3/3b +0.3; residual is the sprite-side CTL-vs-DMA race replay, not the landing (now trace-exact)
Agnus/Blitter guard (233) 1641.5 1647.4 (+0.36%) composes cleanly with #145 (pre-rebase, against old main, this guard read +4.2%); worst case +4.97pp (invisible11/13), improvements up to -6.0 (bltint6); no case over the 5pp gate

Other gates (rebased stack)

  • cargo test --lib: 1329 passed.
  • cargo clippy --all-targets --all-features --locked -- -D warnings and cargo fmt --check clean.
  • 14-demo byte-identity vs main c4c7207: 13/14 byte-identical (eon, sota, itm, hamazing, genx, kick13boot, kicka1200, secondnature, roots-aga, gods, rebirth, tomato, zool). roots-ecs differs 1.76%: the bouncing "?" object at a different animation position at the 35s shot -- screenshot pair inspected, no glitches or corruption.

Rebase conflict resolutions

docs

  • docs/internals/timing.md: fetch-cadence section rewritten for the lookahead/wake/tails/COPJMP model and the Denise write-effect recording.
  • docs/debugger/headless.md: COPPERLINE_DIAG_COP_WRITES row.

@LinuxJedi

Copy link
Copy Markdown
Owner Author

Merge-order note: this PR and #145 both bump savestate::STATE_VERSION to 25 - whichever merges second must renumber to 26 (one-line change). The small guard-family movements (CIA +2.8, Paula +1.3, Blitter +4.2) are the CPU-vs-copper phase boundary and should be re-validated once stacked on #145's blitter model.

LinuxJedi added 3 commits July 7, 2026 06:48
… trace

Copper MOVE-after-WAIT register writes landed four colour clocks late
(SPR0CTL at hpos $26 where the vAmigaTS spritedma/interfere real-A500
photos and vAmiga land it at $22). Two-sided landing probes isolated four
pipeline differences, each modelled here:

- The WAIT comparator's horizontal input runs two colour clocks ahead of
  the beam and wraps through zero over the last three clocks of a line
  (vAmiga runHorizontalComparator). A sleeping WAIT wakes at target-2, the
  match clock is the bus-free wake-up cycle, the next fetch lands on the
  masked target and its write two clocks later.
- A WAIT/SKIP spends two Copper cycles after its second word fetch
  (Minimig WAITSKIP1/WAITSKIP2, vAmiga COP_WAIT1/COP_WAIT2): an
  immediately-true WAIT resumes fetching at fetch2+6.
- The SKIP decision samples the comparator at the next instruction's
  first-word fetch (vAmiga evaluates the skip flag inside COP_FETCH), so a
  line-end SKIP pushed past the wrap by the bus lockout sees the next
  line's vertical phase (jumpbpu-style list switches).
- A Copper MOVE to COPJMP1/2 spends two tail cycles (COP_JMP1/COP_JMP2)
  before the PC reloads; the first fetch from the new list lands three
  Copper cycles after the strobe (vAmiga trace: strobe write at $04, next
  list's write at $0C).

Calibrations that had absorbed the old +4 landing move in lockstep: the
copper bus lockout sits at $E0 on short lines ($E1 on long lines, vAmiga
busIsFree), the vertical-blank restart fetches from hpos $02 (vAmiga
trace: first frame-start MOVE write at $04), copper-sourced render events
are recorded at their Denise-effective position (bus landing plus
DENISE_WRITE_EFFECT_DELAY_CCK), and copper-raised interrupts take
COPPER_IRQ_EXTRA_LATENCY_CCK on top of the calibrated recognition
latency. The last two compensate the still-unmodelled CPU write-landing
bias and carry a shared TODO.

vAmigaTS Agnus/Copper family sum drops 1115.7 -> 1008.2 (copstrt1/2
28.3 -> 0.0, cross6 14.0 -> 0.0, Skip family 65.8 -> 2.4, mask
113.5 -> 86.2, Wait 10.5 -> 4.5, coptim 50.6 -> 45.4, oldJump
137.6 -> 131.9 with jumpbpu1-4 restored). Remaining regressions are the
CPU-vs-copper colour race class (coprace, copcpu1) bounded by the CPU
write-landing bias.

Bumps savestate::STATE_VERSION to 25 (Copper gained skip_eval and the
Jumping strobe-tail state).
Env-gated stderr trace of every Copper MOVE's landing colour clock (beam
position, register, value), for cross-emulator write-landing comparison
against vAmiga's VAMIGA_COP_PROBE trace (snapshot of that patch in
vamigats-reports/vamiga-cop-probe.patch).
…nt models it

The +4 recognition-latency offset for copper-raised interrupts was
calibrated against the pre-#144 IPL pipe, which itself embedded the old
+4-late copper landings. With the per-instruction IPL poll placement on
main, copper INTREQ/INTENA writes recognize correctly from their bus-true
landings: with the offset the vAmigaTS Copper/Irq family regressed
15.0 -> 42.3 and coprace 96.4 -> 167.1; without it they sit at the
new-main baseline.
@LinuxJedi LinuxJedi force-pushed the fix/copper-write-landing branch from b765ec1 to 1d5ccc6 Compare July 7, 2026 06:24
@LinuxJedi LinuxJedi merged commit 12fc0b1 into main Jul 7, 2026
8 checks passed
@LinuxJedi LinuxJedi deleted the fix/copper-write-landing branch July 7, 2026 06:34
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