From 519d35609a98edf9a67700b29d3026a5c890bccc Mon Sep 17 00:00:00 2001 From: Andrew Hutchings Date: Sat, 4 Jul 2026 15:48:16 +0100 Subject: [PATCH 1/3] agnus: BPLCON0 reaches the DDF sequencer four colour clocks after the write Align the sequencer's CON strobe with vAmiga's Agnus-side BPLCON0 commit (DMA_CYCLES(4)); the previous three-cck figure was the legacy coarse fetcher delay. Denise's own interpretation of BPLCON0 is unchanged. Score-neutral on the vAmigaTS BPLCON0/DDFTIM buckets; kept for source fidelity with the hardware-verified sequencer model. --- src/bus/custom_regs.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/bus/custom_regs.rs b/src/bus/custom_regs.rs index 9e559ab..ac07b29 100644 --- a/src/bus/custom_regs.rs +++ b/src/bus/custom_regs.rs @@ -966,7 +966,10 @@ impl Bus { self.agnus.set_ersy(val & 0x0002 != 0); if self.denise.bplcon0 != previous { self.record_bitplane_bplcon0_write(previous); - self.ddf_seq_record_bplcon0_write(self.denise.bplcon0, previous, 3); + // Agnus's sequencer copy of BPLCON0 updates four colour + // clocks after the write slot (vAmiga DMA_CYCLES(4); + // Denise's own interpretation switches earlier). + self.ddf_seq_record_bplcon0_write(self.denise.bplcon0, previous, 4); } false } From e5ba8cabb24837a5c6c2ffffdd809e613c9a54df Mon Sep 17 00:00:00 2001 From: Andrew Hutchings Date: Sat, 4 Jul 2026 16:14:37 +0100 Subject: [PATCH 2/3] agnus/denise: overprogrammed BPU fetches nothing and never arms the playfield Two halves of one hardware rule, verified on the invplanes1 A500 photo (hi-res BPU=7 with armed BPL5DAT/BPL6DAT latches shows BLACK): - Agnus's fetch-unit table schedules at most 4 plane streams in hi-res and 2 in SHRES; an overprogrammed BPU (hi-res 5-7, SHRES 3-7) fetches NOTHING, unlike the lo-res BPU=7 overprogrammed 4-plane fetch. Both plane-count decoders (agnus::bitplane_dma_planes and the Denise BitplaneMode dma_planes) now follow the fetch-unit table. - Denise's playfield output arms on BPL1DAT loads: a row whose fetch table carries no plane streams never loads BPL1DAT, so the non-DMA planes' latches are not painted on their own. Manual BPL1DAT writes still display through the manual-BPL replay. vAmigaTS Denise/Registers/BPLCON0 (vs vAmiga 4.4 refs): invplanes1 20.7% -> 0.000%, invprio2 11.8% -> 3.3%, invprio0 16.3% -> 7.8%, invprio1 14.9% -> 6.4%, invprio3 20.3% -> 11.8%, modes0-2 b/c variants 10.8-11.1% -> 4.0%, modes3/3b/3c 14-15% -> 8%. KS1.3, Inside the Machine, Hamazing, Zool, and A1200 boot screenshots stay byte-identical to main. --- src/chipset/agnus.rs | 26 +++++++++++++++++++------- src/chipset/denise.rs | 27 +++++++++++++++++++++------ src/video/bitplane.rs | 10 ++++++++++ src/video/bitplane/tests.rs | 5 ++++- 4 files changed, 54 insertions(+), 14 deletions(-) diff --git a/src/chipset/agnus.rs b/src/chipset/agnus.rs index fcb6f19..e01ff7e 100644 --- a/src/chipset/agnus.rs +++ b/src/chipset/agnus.rs @@ -536,15 +536,27 @@ pub fn bitplane_dma_planes(bplcon0: u16, aga: bool) -> usize { // OCS lowres BPU=7 overfetch quirk does not apply. return if bplcon0 & 0x0010 != 0 { 8 } else { code }; } - let display_planes = if bitplane_shres(bplcon0) { - code.min(2) - } else { - code.min(6) - }; - if code == 7 && !bitplane_hires(bplcon0) && !bitplane_shres(bplcon0) { + // OCS/ECS fetch-unit table (hardware-verified: the invplanes1 A500 + // photo shows no fetch at all for hi-res BPU>4): lo-res BPU=7 is the + // overprogrammed 4-plane fetch, hi-res schedules at most 4 plane + // streams (BPU 5-7 fetch nothing), SHRES at most 2 (BPU 3-7 fetch + // nothing). + if bitplane_shres(bplcon0) { + if code <= 2 { + code + } else { + 0 + } + } else if bitplane_hires(bplcon0) { + if code <= 4 { + code + } else { + 0 + } + } else if code == 7 { 4 } else { - display_planes + code } } diff --git a/src/chipset/denise.rs b/src/chipset/denise.rs index f41d7e2..f5ccb77 100644 --- a/src/chipset/denise.rs +++ b/src/chipset/denise.rs @@ -274,14 +274,29 @@ impl BitplaneMode { }; } let display_planes = if shres { code.min(2) } else { code.min(6) }; - let dma_planes = if code == 7 && !hires && !shres { - // OCS lowres BPU=7 is an overprogrammed fetch mode: Denise can - // still decode six BPLDAT latches, but Agnus only schedules the - // first four bitplane DMA streams. Higher planes display their - // current BPLDAT latch values until software updates them. + // Agnus's fetch-unit table schedules at most 4 plane streams in + // hi-res and 2 in SHRES; overprogrammed counts fetch NOTHING there + // (hardware-verified: the invplanes1 A500 photo shows no fetch for + // hi-res BPU=7). Lo-res BPU=7 is the overprogrammed 4-plane fetch: + // Denise still decodes six BPLDAT latches, but only the first four + // streams are fed by DMA; higher planes display their current + // latch values until software updates them. + let dma_planes = if shres { + if code <= 2 { + code + } else { + 0 + } + } else if hires { + if code <= 4 { + code + } else { + 0 + } + } else if code == 7 { 4 } else { - display_planes + code }; Self { display_planes, diff --git a/src/video/bitplane.rs b/src/video/bitplane.rs index 8228d97..ee46f28 100644 --- a/src/video/bitplane.rs +++ b/src/video/bitplane.rs @@ -3679,6 +3679,16 @@ pub fn render_from_input(input: &RenderInput, fb: &mut [u32]) -> RenderResult { if captured_row.is_none() && !control.bitplane_dma_enabled() { continue; } + // Denise's playfield output arms on BPL1DAT loads. A mode whose + // fetch table carries no plane streams at all (overprogrammed + // hi-res/SHRES BPU) never loads BPL1DAT, so nothing displays - + // the non-DMA planes' latches are not painted on their own + // (hardware-verified: the invplanes1 A500 photo shows black for + // hi-res BPU=7 despite armed BPL5DAT/BPL6DAT latches). Manual + // BPL1DAT writes still display through the manual-BPL replay. + if captured_row.is_none() && dma_planes == 0 { + continue; + } for (plane, words) in row_words.iter_mut().enumerate() { words.clear(); if plane < nplanes { diff --git a/src/video/bitplane/tests.rs b/src/video/bitplane/tests.rs index a28d837..16691dc 100644 --- a/src/video/bitplane/tests.rs +++ b/src/video/bitplane/tests.rs @@ -1752,7 +1752,10 @@ fn shres_limits_bitplane_depth_and_disables_ham() { }; assert_eq!(state.nplanes(), 2); - assert_eq!(state.dma_planes(), 2); + // Agnus's SHRES fetch-unit table schedules at most 2 plane streams; + // an overprogrammed BPU (here 6) fetches nothing at all (the same + // hardware rule as hi-res BPU>4, invplanes1 photo). + assert_eq!(state.dma_planes(), 0); assert!(!state.hold_and_modify()); } From 31abf7b3088a5b38604f97aaf22ee2761daadcee Mon Sep 17 00:00:00 2001 From: Andrew Hutchings Date: Sat, 4 Jul 2026 16:37:04 +0100 Subject: [PATCH 3/3] agnus: sprite DMA slots at their hardware positions ($15+4N/$17+4N) Sprite N's two DMA slots sit at colour clocks $15+4N and $17+4N (the HRM slot chart and vAmiga's DAS table). Copperline reserved a pair-shaped band four clocks later ($19..$37, two sprites per 8-cck band) and captured both sprites of a pair at one coarse position. Now each sprite owns its own two odd slots in the arbiter, capture happens at each sprite's first slot, and SPREN is sampled per slot (honouring the DMACON commit delay). The corrected band phase-locks the copper's free-running loops against sprite DMA: vAmigaTS Agnus/Copper/oldJump jumpbpu1 16.1% -> 0.016%, jumpbpu2 7.4% -> 0.000%, jumpbpu3 5.9% -> 0.000%, jumpbpu4 1.5% -> 0.001% (the precession residual documented in #109). The blitter timing family net-improves (timing8/9/9f/12/15 -2.2, timing6/10/15l +1..2 - the remaining spread sits in the blitter model). The sprena/sprdis DMACON sweeps (29-43%) still need the true two-slot fetch split - the atomic per-line sprite capture cannot express a word-level SPREN edge between a sprite's two slots; unchanged here. KS1.3, Inside the Machine, Hamazing, Gen-X, and Zool screenshots are byte-identical to main. Second Nature's falling-leaves scene renders identically with a small animation-phase drift (timing-sensitive demo; chip-bus contention legitimately changed). --- src/bus.rs | 5 +- src/bus/dma_slots.rs | 39 +++++----- src/bus/frame_capture.rs | 44 +++++++----- src/bus/tests.rs | 151 ++++++++++++++++++++------------------- 4 files changed, 124 insertions(+), 115 deletions(-) diff --git a/src/bus.rs b/src/bus.rs index c3c0cab..7c111a3 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -262,7 +262,10 @@ const CLXDAT_SPRITE_PLAYFIELD_MASK: u16 = 0x01FE; const CLXDAT_SPRITE_SPRITE_MASK: u16 = 0x7E00; const BITPLANE_DDF_HARD_START: u16 = 0x0018; const BITPLANE_DDF_HARD_STOP: u16 = 0x00D8; -const SPRITE_DMA_PAIR_CAPTURE_HPOS: [u32; 4] = [0x018, 0x020, 0x028, 0x030]; +/// First DMA slot colour clock of each sprite channel (the POS/DATA word; +/// the CTL/DATB word follows two clocks later). Hardware slot chart (and +/// vAmiga's DAS table): sprite N fetches at $15+4N / $17+4N. +const SPRITE_DMA_SLOT1_HPOS: [u32; 8] = [0x15, 0x19, 0x1D, 0x21, 0x25, 0x29, 0x2D, 0x31]; const NANOS_PER_SECOND: u128 = 1_000_000_000; const VIDEO_FETCH_TIMING_SAMPLE_RATE: u128 = 128; const VIDEO_COLLISION_TIMING_SAMPLE_RATE: u128 = 16; diff --git a/src/bus/dma_slots.rs b/src/bus/dma_slots.rs index 450a5ef..563fe01 100644 --- a/src/bus/dma_slots.rs +++ b/src/bus/dma_slots.rs @@ -189,7 +189,12 @@ impl Bus { self.check_ui_beam_traps((old_vpos, old_hpos), old_frame_lines, tick.new_frames); } if tick.new_frames == 0 && tick.new_lines == 0 { - self.capture_sprite_dma_words_if_due(old_vpos, old_hpos, self.agnus.hpos); + self.capture_sprite_dma_words_if_due( + old_vpos, + old_hpos, + self.agnus.hpos, + old_emulated_cck, + ); self.capture_bitplane_dma_words_if_due( old_vpos, old_hpos, @@ -572,36 +577,26 @@ impl Bus { pub(super) fn sprite_slot_active_at(&self, hpos: u32) -> bool { // Real OCS sprite DMA fetches only on lines where a sprite is actually - // active (within its vstart..vstop), not on every line. The reserved - // slots map to sprite pairs by `SPRITE_DMA_PAIR_CAPTURE_HPOS` - // (0x18->sprites 0/1, 0x20->2/3, 0x28->4/5, 0x30->6/7), so reserve a - // pair's slot only when one of its sprites is fetching data this line -- - // gating on the same `data_dma_active` the renderer uses, so the bus - // model and the captured image agree. Parked/off-screen sprites free - // their slot for the CPU/blitter; previously they were reserved - // unconditionally whenever SPREN was on (~2504 cck/frame of phantom DMA - // stolen from the blitter). + // active (within its vstart..vstop), not on every line. Sprite N owns + // the two odd slots $15+4N and $17+4N (the hardware slot chart / + // vAmiga's DAS table), so reserve them only when that sprite is + // fetching data this line -- gating on the same `data_dma_active` the + // renderer uses, so the bus model and the captured image agree. + // Parked/off-screen sprites free their slots for the CPU/blitter. if self.agnus.dmacon & DMACON_SPREN == 0 { return false; } // Sprite DMA slots sit on ODD color clocks (same parity as refresh/ // disk/audio -- the HRM chart's fixed-DMA band), so they never block - // the Copper's even-clock fetches. Each active sprite pair reserves - // the two odd slots of its 8-cck band (0x19/0x1B, 0x21/0x23, ...). - if !(0x019..=0x037).contains(&hpos) || hpos & 1 == 0 { - return false; - } - let rel = hpos - 0x019; - if rel % 8 >= 4 { + // the Copper's even-clock fetches. + if !(0x015..=0x033).contains(&hpos) || hpos & 1 == 0 { return false; } - let pair = (rel / 8) as usize; - if pair >= 4 { + let sprite = ((hpos - 0x015) / 4) as usize; + if sprite >= 8 { return false; } - let first = pair * 2; - self.display_dma_sprite_state[first].data_dma_active - || self.display_dma_sprite_state[first + 1].data_dma_active + self.display_dma_sprite_state[sprite].data_dma_active } pub(super) fn record_bitplane_dmacon_write(&mut self, previous: u16) { diff --git a/src/bus/frame_capture.rs b/src/bus/frame_capture.rs index 46a3a26..684d890 100644 --- a/src/bus/frame_capture.rs +++ b/src/bus/frame_capture.rs @@ -320,7 +320,7 @@ impl Bus { vpos >= self.current_frame_visible_start_vpos && beam_y >= vstart && beam_y < vstop - && hpos >= SPRITE_DMA_PAIR_CAPTURE_HPOS[sprite / 2] + && hpos >= SPRITE_DMA_SLOT1_HPOS[sprite] } pub(super) fn latch_display_sprite_register_data_stream_at( @@ -334,7 +334,7 @@ impl Bus { if beam_y >= frame_lines { return; } - let fetch_slot = SPRITE_DMA_PAIR_CAPTURE_HPOS[sprite / 2]; + let fetch_slot = SPRITE_DMA_SLOT1_HPOS[sprite]; let stream_start = if hpos >= fetch_slot { beam_y.saturating_add(1) } else { @@ -470,9 +470,8 @@ impl Bus { let in_window = beam_y >= control.vstart && beam_y < control.vstop; let sprite_dma_enabled = dmacon & (DMACON_DMAEN | DMACON_SPREN) == (DMACON_DMAEN | DMACON_SPREN); - let reaches_current_fetch_slot = beam_y == control.vstart - && hpos <= SPRITE_DMA_PAIR_CAPTURE_HPOS[sprite / 2] - && sprite_dma_enabled; + let reaches_current_fetch_slot = + beam_y == control.vstart && hpos <= SPRITE_DMA_SLOT1_HPOS[sprite] && sprite_dma_enabled; let keep_held_line = !sprite_dma_enabled && in_window && state.data_dma_active && state.last_line.is_some(); let keep_active_dma_line = @@ -545,7 +544,7 @@ impl Bus { } else { (beam_y - control.vstart) as u32 }; - if beam_y >= control.vstart && hpos > SPRITE_DMA_PAIR_CAPTURE_HPOS[sprite / 2] { + if beam_y >= control.vstart && hpos > SPRITE_DMA_SLOT1_HPOS[sprite] { line = line.saturating_add(1); } let line = if sprite_scan_doubled(self.agnus.fmode()) { @@ -748,7 +747,7 @@ impl Bus { .collect(); let mut idx = 0; for vpos in 0..display_start { - for (pair, &capture_hpos) in SPRITE_DMA_PAIR_CAPTURE_HPOS.iter().enumerate() { + for (sprite, &capture_hpos) in SPRITE_DMA_SLOT1_HPOS.iter().enumerate() { while idx < writes.len() && (writes[idx].0 < vpos || (writes[idx].0 == vpos && writes[idx].1 < capture_hpos)) @@ -770,7 +769,7 @@ impl Bus { if self.sprite_dma_inhibited_by_vertical_blank_at(vpos) { continue; } - for sprite in pair * 2..pair * 2 + 2 { + { if sprite_dma_disabled_by_bitplane_ddf( sprite, self.agnus.revision(), @@ -840,26 +839,21 @@ impl Bus { vpos: u32, old_hpos: u32, new_hpos: u32, + old_emulated_cck: u64, ) { - // No sprite DMA pair slot lies in [old_hpos, new_hpos): nothing below - // can run (the per-pair loop checks the same window), so skip the + // No sprite DMA slot lies in [old_hpos, new_hpos): nothing below can + // run (the per-sprite loop checks the same window), so skip the // sprite-state scan on the vast majority of beam advances. - if old_hpos > SPRITE_DMA_PAIR_CAPTURE_HPOS[3] || new_hpos <= SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - { + if old_hpos > SPRITE_DMA_SLOT1_HPOS[7] || new_hpos <= SPRITE_DMA_SLOT1_HPOS[0] { return; } if self.sprite_dma_inhibited_by_vertical_blank_at(vpos) { return; } - let sprite_dma_enabled = - self.agnus.dmacon & (DMACON_DMAEN | DMACON_SPREN) == (DMACON_DMAEN | DMACON_SPREN); let sprite_vertical_bar_active = self .display_dma_sprite_state .iter() .any(|state| state.data_dma_active && state.last_line.is_some()); - if !sprite_dma_enabled && !sprite_vertical_bar_active { - return; - } let Some(fb_y) = visible_framebuffer_y( vpos, self.current_frame_visible_start_vpos, @@ -875,15 +869,27 @@ impl Bus { let mut fetched_lines = 0usize; let bitplane_bplcon0 = self.effective_bitplane_bplcon0(); let bitplane_dmacon = self.effective_bitplane_dmacon(); - for (pair, &capture_hpos) in SPRITE_DMA_PAIR_CAPTURE_HPOS.iter().enumerate() { + for (sprite, &capture_hpos) in SPRITE_DMA_SLOT1_HPOS.iter().enumerate() { if old_hpos > capture_hpos || new_hpos <= capture_hpos { continue; } + // SPREN is sampled by each sprite's own DMA slot (the sprena/ + // sprdis vAmigaTS sweeps step DMACON writes in two-colour-clock + // increments around individual slots), honouring the DMACON + // write commit delay. + let slot_cck = + old_emulated_cck.saturating_add(u64::from(capture_hpos.saturating_sub(old_hpos))); + let sprite_dma_enabled = self.effective_bitplane_dmacon_at(slot_cck) + & (DMACON_DMAEN | DMACON_SPREN) + == (DMACON_DMAEN | DMACON_SPREN); + if !sprite_dma_enabled && !sprite_vertical_bar_active { + continue; + } if sprite_dma_enabled { pair_slots += 1; } let mut captured_line = false; - for sprite in pair * 2..pair * 2 + 2 { + { if sprite_dma_disabled_by_bitplane_ddf( sprite, self.agnus.revision(), diff --git a/src/bus/tests.rs b/src/bus/tests.rs index 9d39688..f3d32a8 100644 --- a/src/bus/tests.rs +++ b/src/bus/tests.rs @@ -18,7 +18,7 @@ use super::{ DENISE_HPOS_LAG_CCK, DMACON_AUD_MASK, DMACON_BLTEN, DMACON_BLTPRI, DMACON_BPLEN, DMACON_SPREN, PAL_SPRITE_DMA_FIRST_ACTIVE_VPOS, RENDER_COPPER_WAIT_HPOS_FB0, RENDER_DIW_HSTART_FB0, RENDER_MIN_OVERSCAN_START_VPOS, RENDER_VISIBLE_LINES, RENDER_VISIBLE_START_VPOS, - SPRITE_DMA_PAIR_CAPTURE_HPOS, + SPRITE_DMA_SLOT1_HPOS, }; use crate::audio::AudioSink; use crate::chipset::agnus::{ @@ -4716,7 +4716,7 @@ fn sprite_dma_capture_samples_line_words_at_beam_time() { bus.agnus.dmacon = DMACON_DMAEN | DMACON_SPREN; bus.agnus.vpos = 0x2C; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.denise.sprpt[0] = sprite_ptr as u32; bus.display_dma_sprpt[0] = sprite_ptr as u32; @@ -4767,7 +4767,7 @@ fn inactive_sprite_pointer_write_before_pair_slot_seeds_next_descriptor_fetch() bus.agnus.vpos = 0x2C; bus.agnus.hpos = 0; bus.capture_current_frame_display_start(); - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.advance_chipset(2); let lines = bus.frame_captured_sprite_lines(); @@ -4816,7 +4816,7 @@ fn vertical_blank_sprite_pointer_write_reloads_descriptor_in_offscreen_replay() bus.agnus.vpos = RENDER_VISIBLE_START_VPOS; bus.capture_current_frame_display_start(); - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.advance_chipset(2); let lines = bus.frame_captured_sprite_lines(); @@ -4844,14 +4844,14 @@ fn post_vertical_blank_sprite_pointer_write_retargets_pending_descriptor() { bus.sprite_dma_frame_start_ptr[0] = descriptor_ptr as u32; bus.current_frame_render_events.push(BeamRegisterWrite { vpos: PAL_SPRITE_DMA_FIRST_ACTIVE_VPOS + 11, - hpos: SPRITE_DMA_PAIR_CAPTURE_HPOS[0], + hpos: SPRITE_DMA_SLOT1_HPOS[0], offset: 0x120, value: (data_ptr >> 16) as u16, source: BeamWriteSource::Copper, }); bus.current_frame_render_events.push(BeamRegisterWrite { vpos: PAL_SPRITE_DMA_FIRST_ACTIVE_VPOS + 11, - hpos: SPRITE_DMA_PAIR_CAPTURE_HPOS[0] + 2, + hpos: SPRITE_DMA_SLOT1_HPOS[0] + 2, offset: 0x122, value: data_ptr as u16, source: BeamWriteSource::Copper, @@ -4859,7 +4859,7 @@ fn post_vertical_blank_sprite_pointer_write_retargets_pending_descriptor() { bus.agnus.vpos = RENDER_VISIBLE_START_VPOS; bus.capture_current_frame_display_start(); - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.advance_chipset(2); let lines = bus.frame_captured_sprite_lines(); @@ -4889,10 +4889,10 @@ fn manual_sprite_control_write_fetches_data_from_sprpt() { assert!(!bus.write_custom_word_from(0x142, ctl, BeamWriteSource::Copper)); bus.agnus.vpos = 0x2C; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.advance_chipset(2); bus.agnus.vpos = 0x2D; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.advance_chipset(2); let lines = bus.frame_captured_sprite_lines(); @@ -4931,15 +4931,15 @@ fn pending_register_control_sprite_pointer_write_retargets_data_stream() { assert!(!bus.write_custom_word_from(0x142, ctl, BeamWriteSource::Copper)); bus.agnus.vpos = 0x24; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0]; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0]; let _ = bus.write_custom_word_from(0x120, (new_data_ptr >> 16) as u16, BeamWriteSource::Copper); let _ = bus.write_custom_word_from(0x122, new_data_ptr as u16, BeamWriteSource::Copper); bus.agnus.vpos = 0x40; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.advance_chipset(2); bus.agnus.vpos = 0x41; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.advance_chipset(2); let lines = bus.frame_captured_sprite_lines(); @@ -4972,22 +4972,22 @@ fn pending_descriptor_sprite_pointer_write_retargets_data_stream() { bus.display_dma_sprpt[0] = old_ptr as u32; bus.agnus.vpos = 0x2C; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.advance_chipset(2); let pending = bus.display_dma_sprite_state[0]; assert_eq!(pending.control.map(|control| control.vstart), Some(0x60)); assert!(!pending.data_dma_active); bus.agnus.vpos = 0x30; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0]; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0]; let _ = bus.write_custom_word_from(0x120, (new_data_ptr >> 16) as u16, BeamWriteSource::Copper); let _ = bus.write_custom_word_from(0x122, new_data_ptr as u16, BeamWriteSource::Copper); bus.agnus.vpos = 0x60; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.advance_chipset(2); bus.agnus.vpos = 0x61; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.advance_chipset(2); let lines = bus.frame_captured_sprite_lines(); @@ -5031,12 +5031,12 @@ fn armed_pointer_reload_before_vstart_fetches_descriptor_words() { }; bus.agnus.vpos = 0x24; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0]; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0]; let _ = bus.write_custom_word_from(0x120, (sprite_ptr >> 16) as u16, BeamWriteSource::Copper); let _ = bus.write_custom_word_from(0x122, sprite_ptr as u16, BeamWriteSource::Copper); bus.agnus.vpos = 0x40; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.advance_chipset(2); let lines = bus.frame_captured_sprite_lines(); @@ -5056,7 +5056,7 @@ fn after_slot_armed_sprite_pointer_write_seeds_dma_data_stream() { bus.agnus.dmacon = DMACON_DMAEN | DMACON_SPREN; bus.agnus.vpos = 0x40; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0]; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0]; bus.denise.sprpos[0] = pos; bus.denise.sprctl[0] = ctl; bus.denise.spr_armed[0] = true; @@ -5064,7 +5064,7 @@ fn after_slot_armed_sprite_pointer_write_seeds_dma_data_stream() { let _ = bus.write_custom_word_from(0x120, (data_ptr >> 16) as u16, BeamWriteSource::Copper); let _ = bus.write_custom_word_from(0x122, data_ptr as u16, BeamWriteSource::Copper); bus.agnus.vpos = 0x41; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.advance_chipset(2); let lines = bus.frame_captured_sprite_lines(); @@ -5093,15 +5093,15 @@ fn active_sprite_control_rewrite_preserves_descriptor_data_origin() { bus.display_dma_sprpt[0] = sprite_ptr as u32; bus.agnus.vpos = 0x2C; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.advance_chipset(2); bus.agnus.vpos = 0x2D; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 8; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 8; assert!(!bus.write_custom_word_from(0x140, moved_pos, BeamWriteSource::Copper)); assert!(!bus.write_custom_word_from(0x142, moved_ctl, BeamWriteSource::Copper)); - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.advance_chipset(2); let lines = bus.frame_captured_sprite_lines(); @@ -5140,7 +5140,7 @@ fn sprite_pointer_write_after_pair_slot_seeds_next_descriptor_fetch() { bus.denise.sprpt[2] = old_ptr as u32; bus.display_dma_sprpt[2] = old_ptr as u32; - let slot = SPRITE_DMA_PAIR_CAPTURE_HPOS[1]; + let slot = SPRITE_DMA_SLOT1_HPOS[2]; bus.agnus.vpos = 0; bus.agnus.hpos = slot - 1; bus.advance_chipset(2); @@ -5180,7 +5180,7 @@ fn sprite_dma_inverted_vstop_runs_to_frame_bottom() { bus.agnus.dmacon = DMACON_DMAEN | DMACON_SPREN; bus.agnus.vpos = 0x2C; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.denise.sprpt[0] = sprite_ptr as u32; bus.display_dma_sprpt[0] = sprite_ptr as u32; @@ -5217,7 +5217,7 @@ fn fmode_wide_sprite_dma_captures_extension_words() { bus.agnus.dmacon = DMACON_DMAEN | DMACON_SPREN; bus.agnus.vpos = 0x2C; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.denise.sprpt[0] = sprite_ptr as u32; bus.display_dma_sprpt[0] = sprite_ptr as u32; @@ -5264,7 +5264,7 @@ fn fmode_sscan2_sprite_dma_doubles_each_data_line() { for vpos in 0x2C..=0x32u32 { bus.agnus.vpos = vpos; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.advance_chipset(2); } @@ -5442,7 +5442,7 @@ fn sprite_dma_capture_preserves_sprite_started_before_visible_area() { bus.sprite_dma_frame_start_ptr[0] = sprite_ptr as u32; bus.capture_current_frame_display_start(); - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.advance_chipset(2); let lines = bus.frame_captured_sprite_lines(); @@ -5486,7 +5486,7 @@ fn pending_sprite_control_rewrite_preserves_descriptor_data_origin() { assert!(!bus.write_custom_word_from(0x142, moved_ctl, BeamWriteSource::Copper)); bus.agnus.vpos = vstart as u32; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.advance_chipset(2); let lines = bus.frame_captured_sprite_lines(); @@ -5518,7 +5518,7 @@ fn active_sprite_pos_write_retimes_hstart_without_clearing_dma_stream() { bus.denise.sprpt[0] = sprite_ptr as u32; bus.display_dma_sprpt[0] = sprite_ptr as u32; bus.agnus.vpos = 0x2C; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.advance_chipset(2); // Rewriting SPRxPOS while DMA is already enabled changes the horizontal @@ -5528,11 +5528,11 @@ fn active_sprite_pos_write_retimes_hstart_without_clearing_dma_stream() { bus.agnus.vpos = 0x2D; bus.agnus.hpos = 0; assert!(!bus.write_custom_word_from(0x140, moved_pos, BeamWriteSource::Copper)); - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.advance_chipset(2); bus.agnus.vpos = 0x2E; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.advance_chipset(2); let words: Vec<(i32, i32, u16, u16)> = bus @@ -5630,7 +5630,7 @@ fn sprite_dma_capture_treats_zero_pointer_as_chip_address() { bus.agnus.dmacon = DMACON_DMAEN | DMACON_SPREN; bus.agnus.vpos = 0x2C; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.denise.sprpt[0] = 0; bus.denise.sprpt[1] = 0x20; bus.display_dma_sprpt[0] = 0; @@ -5821,7 +5821,7 @@ fn sprite_dma_zero_height_descriptor_terminates_stream() { bus.agnus.dmacon = DMACON_DMAEN | DMACON_SPREN; bus.agnus.vpos = 0x2C; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.denise.sprpt[0] = sprite_ptr as u32; bus.display_dma_sprpt[0] = sprite_ptr as u32; @@ -5845,7 +5845,7 @@ fn sprite_dma_capture_wraps_control_words_at_chip_ram_end() { bus.agnus.dmacon = DMACON_DMAEN | DMACON_SPREN; bus.agnus.vpos = 0x2C; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.denise.sprpt[0] = sprite_ptr as u32; bus.display_dma_sprpt[0] = sprite_ptr as u32; @@ -5876,7 +5876,7 @@ fn sprite_dma_zero_height_descriptor_terminates_after_chip_address_wrap() { bus.agnus.dmacon = DMACON_DMAEN | DMACON_SPREN; bus.agnus.vpos = 0x2C; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.denise.sprpt[0] = sprite_ptr as u32; bus.display_dma_sprpt[0] = sprite_ptr as u32; bus.denise.sprpt[1] = 0x0200; @@ -5904,7 +5904,7 @@ fn sprite_dma_capture_latches_control_words_until_stop() { bus.agnus.dmacon = DMACON_DMAEN | DMACON_SPREN; bus.agnus.vpos = 0x2C; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.denise.sprpt[0] = sprite_ptr as u32; bus.display_dma_sprpt[0] = sprite_ptr as u32; @@ -5914,7 +5914,7 @@ fn sprite_dma_capture_latches_control_words_until_stop() { write_chip_word(&mut bus, sprite_ptr, rewritten_pos); write_chip_word(&mut bus, sprite_ptr + 2, rewritten_ctl); bus.agnus.vpos = 0x2D; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.advance_chipset(2); let lines = bus.frame_captured_sprite_lines(); @@ -5946,7 +5946,7 @@ fn sprite_dma_capture_samples_later_pairs_at_their_fetch_slot() { bus.agnus.dmacon = DMACON_DMAEN | DMACON_SPREN; bus.agnus.vpos = 0x2C; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.denise.sprpt[0] = sprite0_ptr as u32; bus.denise.sprpt[6] = sprite6_ptr as u32; bus.display_dma_sprpt[0] = sprite0_ptr as u32; @@ -5955,7 +5955,7 @@ fn sprite_dma_capture_samples_later_pairs_at_their_fetch_slot() { bus.advance_chipset(2); write_chip_word(&mut bus, sprite0_ptr + 4, 0xAAAA); write_chip_word(&mut bus, sprite6_ptr + 4, 0xBBBB); - let remaining = SPRITE_DMA_PAIR_CAPTURE_HPOS[3] + 1 - bus.agnus.hpos; + let remaining = SPRITE_DMA_SLOT1_HPOS[6] + 1 - bus.agnus.hpos; bus.advance_chipset(remaining); let lines = bus.frame_captured_sprite_lines(); @@ -5987,7 +5987,7 @@ fn sprite_pointer_write_at_pair_slot_seeds_next_line_descriptor_fetch() { bus.display_dma_sprpt[0] = old_ptr as u32; bus.agnus.vpos = 0x24; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.advance_chipset(1); let _ = bus.write_custom_word_from(0x120, (new_ptr >> 16) as u16, BeamWriteSource::Copper); let _ = bus.write_custom_word_from(0x122, new_ptr as u16, BeamWriteSource::Copper); @@ -5999,7 +5999,7 @@ fn sprite_pointer_write_at_pair_slot_seeds_next_line_descriptor_fetch() { ); bus.agnus.vpos = 0x40; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.advance_chipset(2); let lines = bus.frame_captured_sprite_lines(); @@ -6014,7 +6014,7 @@ fn empty_sprite_dma_slot_does_not_mark_frame_dma_observed() { let mut bus = empty_bus(); bus.agnus.dmacon = DMACON_DMAEN | DMACON_SPREN; bus.agnus.vpos = RENDER_VISIBLE_START_VPOS; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.advance_chipset(2); @@ -6044,7 +6044,7 @@ fn sprite_dma_capture_blocks_sprite_seven_when_ddfstrt_uses_early_fetch_slot() { bus.agnus.dmacon = DMACON_DMAEN | DMACON_BPLEN | DMACON_SPREN; bus.agnus.vpos = 0x2C; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[3] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[6] - 1; bus.denise.bplcon0 = 0x1000; bus.denise.ddfstrt = 0x0028; bus.denise.ddfstop = 0x0038; @@ -6053,7 +6053,7 @@ fn sprite_dma_capture_blocks_sprite_seven_when_ddfstrt_uses_early_fetch_slot() { bus.display_dma_sprpt[6] = sprite6_ptr as u32; bus.display_dma_sprpt[7] = sprite7_ptr as u32; - bus.advance_chipset(2); + bus.advance_chipset(SPRITE_DMA_SLOT1_HPOS[7] + 1 - bus.agnus.hpos); let lines = bus.frame_captured_sprite_lines(); assert!(lines.iter().any(|line| line.sprite == 6)); @@ -6079,7 +6079,7 @@ fn sprite_dma_capture_keeps_sprite_seven_when_ddfstrt_matches_sprite_slot() { bus.agnus.dmacon = DMACON_DMAEN | DMACON_BPLEN | DMACON_SPREN; bus.agnus.vpos = 0x2C; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[3] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[6] - 1; bus.denise.bplcon0 = 0x3000; bus.denise.ddfstrt = 0x0030; bus.denise.ddfstop = 0x0038; @@ -6088,7 +6088,7 @@ fn sprite_dma_capture_keeps_sprite_seven_when_ddfstrt_matches_sprite_slot() { bus.display_dma_sprpt[6] = sprite6_ptr as u32; bus.display_dma_sprpt[7] = sprite7_ptr as u32; - bus.advance_chipset(2); + bus.advance_chipset(SPRITE_DMA_SLOT1_HPOS[7] + 1 - bus.agnus.hpos); let lines = bus.frame_captured_sprite_lines(); assert!(lines.iter().any(|line| line.sprite == 6)); @@ -6111,17 +6111,17 @@ fn sprite_dma_capture_repeats_last_fetched_line_after_dma_disable_until_vstop() bus.agnus.dmacon = DMACON_DMAEN | DMACON_SPREN; bus.agnus.vpos = 0x2C; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.denise.sprpt[0] = sprite_ptr as u32; bus.display_dma_sprpt[0] = sprite_ptr as u32; bus.advance_chipset(2); bus.agnus.dmacon = DMACON_DMAEN; bus.agnus.vpos = 0x2D; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.advance_chipset(2); bus.agnus.vpos = 0x2E; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.advance_chipset(2); let lines = bus.frame_captured_sprite_lines(); @@ -6156,13 +6156,13 @@ fn sprite_dma_capture_does_not_start_descriptor_at_or_before_current_vpos() { bus.agnus.dmacon = DMACON_DMAEN | DMACON_SPREN; bus.agnus.vpos = 0x2C; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.denise.sprpt[0] = sprite_ptr as u32; bus.display_dma_sprite_state[0].next_ptr = Some(sprite_ptr as u32); bus.advance_chipset(2); bus.agnus.vpos = 0x2D; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.advance_chipset(2); assert!(bus @@ -6188,16 +6188,16 @@ fn sprite_dma_reuse_skips_descriptor_with_vstart_before_current_vpos() { bus.agnus.dmacon = DMACON_DMAEN | DMACON_SPREN; bus.agnus.vpos = 0x2B; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.denise.sprpt[0] = sprite_ptr as u32; bus.display_dma_sprpt[0] = sprite_ptr as u32; bus.advance_chipset(2); bus.agnus.vpos = 0x2C; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.advance_chipset(2); bus.agnus.vpos = 0x2D; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.advance_chipset(2); let lines = bus.frame_captured_sprite_lines(); @@ -6237,7 +6237,7 @@ fn sprite_dma_chained_descriptor_with_same_vstart_arms_after_control_fetch_line( for vpos in 0x2C..=0x31u32 { bus.agnus.vpos = vpos; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.advance_chipset(2); } @@ -6275,7 +6275,7 @@ fn visible_sprite_pixels_accumulate_live_sprite_sprite_clxdat() { bus.agnus.dmacon = DMACON_DMAEN | DMACON_SPREN; bus.agnus.vpos = 0x2C; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.denise.sprpt[0] = sprite0_ptr as u32; bus.denise.sprpt[2] = sprite2_ptr as u32; bus.display_dma_sprpt[0] = sprite0_ptr as u32; @@ -6285,7 +6285,7 @@ fn visible_sprite_pixels_accumulate_live_sprite_sprite_clxdat() { bus.advance_chipset(1); assert_eq!(bus.custom_read(0x00E, 2), 0x8000); - let remaining = SPRITE_DMA_PAIR_CAPTURE_HPOS[1] + 1 - bus.agnus.hpos; + let remaining = SPRITE_DMA_SLOT1_HPOS[1] + 1 - bus.agnus.hpos; bus.advance_chipset(remaining); assert_eq!(bus.custom_read(0x00E, 2), 0x8000); @@ -6658,7 +6658,7 @@ fn bplcon3_spres_hires_narrows_live_sprite_sprite_clxdat() { bus.agnus.dmacon = DMACON_DMAEN | DMACON_SPREN; bus.agnus.vpos = 0x2C; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.denise.bplcon0 = 0x8000; bus.denise.bplcon3 = bplcon3; bus.denise.sprpt[0] = sprite0_ptr as u32; @@ -6698,7 +6698,7 @@ fn same_line_clxcon_odd_sprite_enable_does_not_retime_earlier_live_sprite_sprite bus.agnus.dmacon = DMACON_DMAEN | DMACON_SPREN; bus.agnus.vpos = 0x2C; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.denise.clxcon = initial_clxcon; bus.denise.sprpt[1] = sprite1_ptr as u32; bus.denise.sprpt[2] = sprite2_ptr as u32; @@ -6707,7 +6707,7 @@ fn same_line_clxcon_odd_sprite_enable_does_not_retime_earlier_live_sprite_sprite bus.current_frame_sprite_display_enable_x_by_y[0] = Some(0); bus.current_frame_render_base = bus.capture_render_snapshot(); - let after_pair_capture = SPRITE_DMA_PAIR_CAPTURE_HPOS[1] + 1 - bus.agnus.hpos; + let after_pair_capture = SPRITE_DMA_SLOT1_HPOS[1] + 1 - bus.agnus.hpos; bus.advance_chipset(after_pair_capture); if let Some(enable_hpos) = enable_hpos { @@ -7389,7 +7389,7 @@ fn captured_sprite_and_bitplane_rows_accumulate_live_sprite_playfield_clxdat() { bus.agnus.dmacon = DMACON_DMAEN | DMACON_SPREN | DMACON_BPLEN; bus.agnus.vpos = 0x2C; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.denise.diwstrt = 0x2C81; bus.denise.diwstop = 0x2DC1; bus.denise.ddfstrt = 0x0038; @@ -7425,7 +7425,7 @@ fn explicit_bpl1dat_output_accumulates_live_sprite_playfield_clxdat() { bus.agnus.dmacon = DMACON_DMAEN | DMACON_SPREN; bus.agnus.vpos = 0x2C; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.denise.diwstrt = 0x2C83; bus.denise.diwstop = 0x2DC1; bus.denise.bplcon0 = 0x1000; @@ -7483,7 +7483,7 @@ fn same_line_bplcon1_scroll_increase_latches_later_live_sprite_playfield_clxdat( bus.agnus.dmacon = DMACON_DMAEN | DMACON_SPREN | DMACON_BPLEN; bus.agnus.vpos = 0x2C; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.denise.diwstrt = 0x2C83; bus.denise.diwstop = 0x2DC1; bus.denise.ddfstrt = 0x0038; @@ -7524,7 +7524,7 @@ fn same_line_clxcon_odd_sprite_enable_does_not_retime_earlier_live_sprite_playfi bus.agnus.dmacon = DMACON_DMAEN | DMACON_SPREN | DMACON_BPLEN; bus.agnus.vpos = 0x2C; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.denise.diwstrt = 0x2C83; bus.denise.diwstop = 0x2DC1; bus.denise.ddfstrt = 0x0038; @@ -7575,7 +7575,7 @@ fn bplcon3_spres_hires_narrows_live_sprite_playfield_clxdat() { bus.set_agnus_revision(AgnusRevision::Ecs8372Rev4); bus.agnus.dmacon = DMACON_DMAEN | DMACON_SPREN | DMACON_BPLEN; bus.agnus.vpos = 0x2C; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.denise.diwstrt = 0x2C81; bus.denise.diwstop = 0x2DC1; bus.denise.ddfstrt = 0x0038; @@ -7621,7 +7621,7 @@ fn same_line_bplcon3_spres_write_does_not_retime_earlier_live_sprite_playfield_c bus.set_agnus_revision(AgnusRevision::Ecs8372Rev4); bus.agnus.dmacon = DMACON_DMAEN | DMACON_SPREN | DMACON_BPLEN; bus.agnus.vpos = 0x2C; - bus.agnus.hpos = SPRITE_DMA_PAIR_CAPTURE_HPOS[0] - 1; + bus.agnus.hpos = SPRITE_DMA_SLOT1_HPOS[0] - 1; bus.denise.diwstrt = 0x2C81; bus.denise.diwstop = 0x2DC1; bus.denise.ddfstrt = 0x0038; @@ -8261,16 +8261,21 @@ fn fixed_agnus_dma_slot_bands_drive_owner_selection() { bus.agnus.dmacon = DMACON_DMAEN | DMACON_SPREN; // A sprite's slot is reserved only while that sprite is actually - // fetching (data_dma_active); a parked sprite frees it. Sprite slots - // sit on odd color clocks (0x019/0x01B for pair 0). - bus.agnus.hpos = 0x019; + // fetching (data_dma_active); a parked sprite frees it. Sprite N owns + // the odd color clocks $15+4N and $17+4N (hardware slot chart). + bus.agnus.hpos = 0x015; assert_eq!(bus.scheduled_dma_owner(false), ChipBusOwner::Idle); bus.display_dma_sprite_state[0].data_dma_active = true; assert_eq!(bus.scheduled_dma_owner(false), ChipBusOwner::Sprite); - // The even color clock inside the band stays free for the Copper/CPU. - bus.agnus.hpos = 0x018; + bus.agnus.hpos = 0x017; + assert_eq!(bus.scheduled_dma_owner(false), ChipBusOwner::Sprite); + // The even color clock inside the band stays free for the Copper/CPU, + // and another sprite's slot stays free while that sprite is parked. + bus.agnus.hpos = 0x016; + assert_eq!(bus.scheduled_dma_owner(false), ChipBusOwner::Idle); + bus.agnus.hpos = 0x019; assert_eq!(bus.scheduled_dma_owner(false), ChipBusOwner::Idle); - bus.agnus.hpos = 0x037; + bus.agnus.hpos = 0x035; assert_eq!(bus.scheduled_dma_owner(false), ChipBusOwner::Idle); bus.display_dma_sprite_state[0].data_dma_active = false;