Skip to content

bus: mirror CPU byte writes to custom registers onto both bus halves#141

Merged
LinuxJedi merged 1 commit into
mainfrom
fix/custom-reg-byte-write-mirror
Jul 7, 2026
Merged

bus: mirror CPU byte writes to custom registers onto both bus halves#141
LinuxJedi merged 1 commit into
mainfrom
fix/custom-reg-byte-write-mirror

Conversation

@LinuxJedi

Copy link
Copy Markdown
Owner

Summary

The residual vAmigaTS CIA/oldcnt divergence (cnt1/cnt3/cnt5 at 31-33%)
turned out not to be CIA CNT counting at all: the failing variants are
exactly the ones that paint the timer LO byte with move.b TALO,COLOR00+1.
Extracting the drawn ramp values showed CL and vAmiga agree on every timer
value and every beam position; the only difference was the COLOR00 red
nibble (vAmiga $vAv, CL $0Av).

A 68000 byte write drives the byte onto BOTH halves of the data bus, and
the custom chips have no byte lanes: they latch the full 16-bit word
regardless of which strobe (/UDS or /LDS) the CPU asserted. move.b v,COLOR00+1 therefore lands $vvvv in the register. Copperline instead
merged the byte into the addressed lane of an internal register latch.
vAmiga's CPU poke8 to custom space doubles the byte the same way
(pokeCustom16(addr & 0x1FE, HI_LO(value, value))).

Change

  • Bus::custom_write size-1 path now always mirrors the byte to both
    halves of the 16-bit word; the addressed-lane merge and the COPCON
    special case (custom_byte_write_mirrors_to_word) are gone.
  • custom_byte_write_latch is renamed custom_reg_latch and remains as
    what it really was for the debugger: the side-effect-free internal
    latch view behind debug_custom_word.
  • custom_byte_write_drives_the_byte_onto_both_bus_halves regression
    test (BLTADAT even/odd byte, COLOR00 mirrored red nibble, COPCON bset,
    AUDxPER byte writes); docs/internals/timing.md documents the rule.

No CIA change: the screen data disproves the CNT count-pipeline
hypothesis at the resolutions these tests can see. The mid-ramp readback
values match vAmiga exactly (the vAmiga 2-PHI2-cycle count pipeline is
invisible at >= 3 E-cycle read distances), and the CNT-underflow IRQ
variants without the readback (oldcnt cnt2/cnt4/cnt6/cnt7) were already
at 1.6-2.7%.

vAmigaTS CIA sweep (COPPERLINE_VAMIGATS_FILTER=CIA/)

case before after
CIA/CIA/oldcnt/cnt1 31.459% 2.778%
CIA/CIA/oldcnt/cnt3 31.459% 2.778%
CIA/CIA/oldcnt/cnt5 33.205% 2.651%

No other case moved by more than 0.01pp (full-family sum 693.6 -> 605.7).

Residual characterization (not fixed here)

The remaining cnt family residuals are a different, already-documented
class, measured from the same ramp extraction:

  • cnt/cnt3 / cnt5 / cnt6 (~10.8%): ramp values and loop positions
    identical; only the white IRQ bar (level-6 handler COLOR00=$FFF)
    starts ~14 cck earlier in CL on each PHI2 timer-A underflow IRQ, and
    the polling loop re-locks ~10 cck shifted after the handler returns.
    This is the known interrupt-recognition/CPU-write beam-timing class
    that also bounds the UART/POT families.
  • cnt/cnt3b / cnt3d / cnt3e (19-26%): same loop with 5 bitplanes
    enabled; the vAmigaTS README states these exist to "verify E clock
    access timing in combination with many blocked DMA cycles". Ramp
    values match; the loop cadence drifts because CL composes the E-clock
    sync wait with bitplane-DMA CPU blocking differently from vAmiga.

Gates

  • cargo test --lib: 1324 passed
  • cargo clippy --all-targets -- -D warnings and cargo fmt --check clean
  • 14-demo byte-identity vs merge-base (6cd266b): all screenshots
    byte-identical (the mirrored byte write is not on any title's path)
  • No serialized state shape change (no STATE_VERSION bump needed)

A 68000 byte write drives the byte onto both halves of the data bus,
and the custom chips have no byte lanes: they latch the full 16-bit
word regardless of which strobe (/UDS or /LDS) the CPU asserted, so
move.b v,COLOR00+1 lands $vvvv in the register. Copperline instead
merged the byte into the addressed lane of an internal register latch,
leaving the other lane at its previous value.

Replace the addressed-lane merge in the custom_write size-1 path with
the mirrored word for every register (this subsumes the COPCON special
case, which is removed). custom_byte_write_latch remains as what it
really was for the debugger - the side-effect-free internal latch view
behind debug_custom_word - renamed custom_reg_latch.

vAmigaTS CIA/oldcnt cnt1/cnt3/cnt5 paint move.b TALO,COLOR00+1 while a
CNT-clocked timer ramps; the photographed frames show the mirrored red
nibble on hardware. Sweep: cnt1 31.5% -> 2.8%, cnt3 31.5% -> 2.8%,
cnt5 33.2% -> 2.7%; no other CIA case moved. The 14-demo raw-screenshot
suite stays byte-identical to the merge base.
@LinuxJedi LinuxJedi merged commit 1a9c3e4 into main Jul 7, 2026
7 checks passed
@LinuxJedi LinuxJedi deleted the fix/custom-reg-byte-write-mirror branch July 7, 2026 05:43
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