Skip to content

blitter: BLTPRI asserts BLS for the whole blit, fencing the CPU off the bus#162

Merged
LinuxJedi merged 1 commit into
mainfrom
fix/bltpri-bls-cpu-fence
Jul 11, 2026
Merged

blitter: BLTPRI asserts BLS for the whole blit, fencing the CPU off the bus#162
LinuxJedi merged 1 commit into
mainfrom
fix/bltpri-bls-cpu-fence

Conversation

@LinuxJedi

Copy link
Copy Markdown
Owner

Summary

With BLTPRI ("blitter nasty") set, BBUSY holds the BLS line asserted until BLTDONE, so the CPU is denied every chip-bus cycle for the duration of the blit -- including the pipeline's bus-free micro-cycles (the first empty D phase, fill's extra idle cycle, line-mode Bresenham cycles). The Copper and fixed DMA still use those cycles; only the CPU is fenced. Previously scheduled_dma_owner_after_fixed returned the bubble cycles as Idle before checking BLTPRI, letting the CPU steal them mid-blit.

Regression example: Jim Power (guru 00000003 after level load)

The FLT trackloader MFM-decodes each sector with a blitter pass under BLTPRI (BLTCON0=$FDFC, BLTSIZE=$4041). It saves the word below the blit destination, writes BLTSIZE, and restores the word two instructions later, relying on that CPU write being held until after the blit's first D write (D-pipeline warm-up garbage lands one word below the real output):

912: move.w (a4),d3       ; save word below dest
914: move.w #$4041,(a0)   ; BLTSIZE - blit starts
918: lsr.w  #8,d4
91a: move.w d3,(a4)       ; restore - must land AFTER the blit's first D write

Granting the CPU the first-D bubble inverted the order: the restore landed first, the blitter then overwrote it, corrupting the last data word ($1FE/$1FF) of every decoded sector. The level depacker consumed the corrupt stream, ended with an odd A5, and MOVE.L (A5)+,(A6)+ address-errored; the Alert re-entry (loc0='HELP') then dropped KS1.3 into the LED-flash deadend. With the fence the loaded data verifies byte-identical against the ADF and the game plays.

Test/golden changes

  • bltpri_stalls_cpu_chip_access_through_blitter_access_cycles: CPU now waits through the whole blit (3 -> 6 cck).
  • blithog_set_blocks_cpu_slowdown_back_pressure_until_blitter_finishes: wait includes the terminal E/F cycles (14 -> 16 cck).
  • timing-test golden row 26 (A->D fill + 3bpl display + BLTPRI) re-blessed 25073 -> 25161 (references: FS-UAE 25095 / vAmiga 25097). The old -22 proximity was a cancellation -- the polling CPU sneaked DMACONR reads into blit bubbles and observed completion early, masking blit-under-display-contention drift that is now visible. Documented in timing-test/README.md; rows 23-25 are unchanged.

Also included

  • BLTP START slot-trace diag prints DMACON alongside BLTCON0/1.
  • Env-gated diagnostics (COPPERLINE_DIAG_SLOTMAP, COPPERLINE_DUMP_BUS_ACCOUNTING) are re-derived from the environment after a save-state load instead of inheriting the saved-off values.
  • docs/internals/timing.md CPU-vs-blitter and CPU-contention sections updated.

Verification

  • Jim Power: level loads and plays from the pre-crash save state; no address error, loaded data matches the ADF byte-for-byte.
  • cargo test --release green (1361 lib tests + golden suite), cargo clippy and cargo fmt --check clean.

…he bus

With BLTPRI set, BBUSY holds the BLS line asserted until BLTDONE, so the
CPU is denied every chip-bus cycle for the duration of the blit --
including the pipeline's bus-free micro-cycles (the first empty D phase,
fill's extra idle cycle, line-mode Bresenham cycles). The Copper and
fixed DMA still use those cycles; only the CPU is fenced. Previously the
arbitration returned the bubble cycles as Idle before checking BLTPRI,
letting the CPU steal them mid-blit.

Software depends on the fence. Jim Power's trackloader MFM-decodes each
sector with a blitter pass under BLTPRI: it saves the word below the
blit destination, writes BLTSIZE, and restores the word two instructions
later, relying on that CPU write being held until after the blit's first
D write (pipeline warm-up garbage lands one word below the real output).
Granting the CPU the first-D bubble inverted the order: the restore
landed first, the blitter then overwrote it, corrupting the last data
word of every decoded sector. The depacker consumed the corrupt stream,
ended on an odd address, and the game crashed with guru 00000003
(address error) after the level load.

- bltpri_stalls_cpu_chip_access_through_blitter_access_cycles now
  expects the CPU to wait through the whole blit (3 -> 6 cck); the
  nasty-lockout comment names the BLS behaviour.
- blithog_set_blocks_cpu_slowdown_back_pressure_until_blitter_finishes
  now includes the terminal E/F cycles in the wait (14 -> 16 cck).
- timing-test golden row 26 (A->D fill + 3bpl display + BLTPRI)
  re-blessed 25073 -> 25161 (FS-UAE 25095 / vAmiga 25097). The old -22
  proximity was a cancellation: the polling CPU sneaked DMACONR reads
  into blit bubbles and observed completion early, masking
  blit-under-display-contention drift that is now visible; noted in
  timing-test/README.md.
- BLTP START slot-trace diag now prints DMACON alongside BLTCON0/1.
- Env-gated diagnostics (COPPERLINE_DIAG_SLOTMAP,
  COPPERLINE_DUMP_BUS_ACCOUNTING) are re-derived from the environment
  after a save-state load instead of inheriting the saved-off values.
- docs/internals/timing.md: CPU-vs-blitter and CPU-contention sections
  updated to describe the BLS fence.
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