From ff3ea170f7f7fb67ff7bb8f8d650cc521b3ab67c Mon Sep 17 00:00:00 2001 From: Andrew Hutchings Date: Sun, 12 Jul 2026 00:30:43 +0100 Subject: [PATCH] blitter: a blit with no channels enabled asserts no BLTPRI CPU fence BLS follows the blitter's bus request line, and a null blit (BLTSIZE with all BLTCON0 USE bits clear) never requests the bus at all -- so with BLTPRI set it must not fence the CPU during its startup ladder either. The warm-up fence charged every BLTPRI blit ~4 colour clocks of CPU denial, which broke interrupt-driven null-blit chains: vAmigaTS Agnus/Blitter/bltint1/5 restart a USE=0 1x1 blit from the blitter interrupt every scanline, and the accumulated denial wedged the test's COLOR00 choreography into a near-solid frame (8.5% -> 95.5% divergence against the vAmiga reference in the pre-0.11 sweep). With the exemption the full Agnus/Blitter suite reads: bltint1 95.5 -> 3.9 and bltint5 95.5 -> 7.4 (both better than the 0.10-era baseline), the null-blit cputim/invisible0 case 21.8 -> 1.5, and bususage0/race0 improve; no other case moves. Jim Power (channel blits keep the warm-up fence), Rampage, and the dot-cube save-state oracles are unchanged, and all 18 golden probes are byte-identical. --- src/chipset/blitter.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/chipset/blitter.rs b/src/chipset/blitter.rs index 5f3ae38..0b70fc1 100644 --- a/src/chipset/blitter.rs +++ b/src/chipset/blitter.rs @@ -1686,8 +1686,15 @@ impl NormalBlitState { /// the hold register. That covers the first-word pipeline bubble; from /// the second word on, bus-free micro-cycles release the request line. /// The terminal E/F cycles are past the fence: BBUSY has already - /// dropped at the last body cycle. + /// dropped at the last body cycle. A blit with NO channels enabled + /// never asserts a bus request at all, so it fences nothing: BLS + /// follows the request line, and interrupt-driven null-blit chains + /// (BLTSIZE with BLTCON0 USE=0 restarted every scanline, vAmigaTS + /// Agnus/Blitter/bltint) must run with the CPU at full speed. fn bltpri_warmup_fences_cpu(&self) -> bool { + if !self.use_a && !self.use_b && !self.use_c && !self.use_d { + return false; + } match self.phase { NormalBlitPhase::StartDelay | NormalBlitPhase::Init => true, NormalBlitPhase::A