Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions docs/debugger/headless.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,9 @@ authoritative list. The most useful ones:

Timing-model knobs that pair well with the debugger:

- `COPPERLINE_IRQ_LATENCY_CCK=N` -- override the modelled 68000
interrupt-recognition latency (default 65 colour clocks; `0` disables).
- `COPPERLINE_IRQ_LATENCY_CCK=N` -- override the modelled Paula INTREQ-to-
IPL-pin pipe (default 5 colour clocks; `0` also disables the 68000
boundary-sampling delay, delivering interrupts immediately).
- `COPPERLINE_DBG_AFTER=SECS` / `COPPERLINE_DBG_UNTIL=SECS` -- bound
debugger and renderer diagnostics to an emulated-time window. Renderer
diagnostics parse these bounds once when their diagnostic option is first
Expand Down
4 changes: 2 additions & 2 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ written in Rust. Cycle-driven means the whole machine -- the 68000-family
CPU, Agnus, Denise, Paula, the CIAs, the floppy subsystem, and the chip bus
that ties them together -- advances on one colour-clock timeline: the chip
bus is arbitrated per colour clock, the Copper and blitter are scheduled
per DMA slot with the hardware bus sequences, and 68000
interrupt-recognition latency is modelled. That timing model is what lets
per DMA slot with the hardware bus sequences, and the Paula IPL pipe plus
68000 interrupt sampling are modelled. That timing model is what lets
it run the current cycle-sensitive OCS and AGA regression set, as well as
Kickstart, Workbench, games, and CDTV/CD32 titles.

Expand Down
2 changes: 1 addition & 1 deletion docs/internals/chipset.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ scoped. ECS adds BLTSIZV/BLTSIZH for larger blits.
## Paula (`paula.rs`)

Paula owns the interrupt system (INTENA/INTREQ, delivered through the
modelled 68000 recognition latency), serial, and audio:
modelled IPL-pin pipe and 68000 boundary sampling), serial, and audio:

- **Audio**: four DMA channels, each with location/length/period/volume,
a period accumulator clocked at CCK rate, and the hardware's one-word
Expand Down
2 changes: 1 addition & 1 deletion docs/internals/cpu.md
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ every access re-walks the tables.
## Interrupts and STOP

Paula's INTENA/INTREQ levels are delivered as M68K autovectors through the
modelled recognition latency described in [](timing). When the CPU
modelled IPL pipe and boundary sampling described in [](timing). When the CPU
executes `STOP`, the frame loop fast-forwards device time to the next
event that can raise an interrupt instead of spinning -- behaviour the
debugger's Step control inherits.
Expand Down
57 changes: 36 additions & 21 deletions docs/internals/timing.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,8 +253,12 @@ the same slot-eligibility primitive the live engine uses.
### Per-slot FSM

Scheduled normal blits use explicit phases matching the hardware
controller: a one-slot BBUSY start delay, an INIT slot, source slots
A/B/C/D, and E/F flush slots for the delayed D holding register. The
controller: three internal startup slots (the BLTSIZE register-commit
cycle plus the two bus-arbitration startup cycles real Agnus spends
before the micro-program begins, verified with a BLTSIZE-to-blitter-IRQ
beam probe against vAmiga), a one-slot BBUSY start delay, an INIT slot,
source slots A/B/C/D, and E/F flush slots for the delayed D holding
register. The
source cadence follows the enabled-channel speed table: A is always
visited, B only when enabled, C when enabled (USEC) *or* in fill mode (an
idle C slot, no bus access), and D when D is enabled or no C next-word
Expand Down Expand Up @@ -372,25 +376,36 @@ Hardware](https://www.theflatnet.de/pub/cbm/amiga/AmigaDevDocs/hard_6.html).

## Interrupt-recognition latency

A 68000 does not enter an exception the moment INTREQ rises; recognition
plus the exception sequence takes roughly 60-100 CCK on real hardware.
Copperline models this with a configurable latency on newly-raised
interrupt levels (`DEFAULT_IRQ_LATENCY_CCK = 65`, `src/bus.rs`;
`COPPERLINE_IRQ_LATENCY_CCK` overrides, `0` disables).
The delay is attached to asynchronous Paula/CIA/blitter/Copper source
assertions. A CPU write that merely changes INTENA/INTREQ masking or
acknowledges a latch normally only updates the delayed-bit bookkeeping. PORTS
is level-fed by CIA-A/Gayle-style INT2 sources and remains immediately visible
when software unmasks an already-latched level; other newly exposed latched
sources are treated as freshly-present CPU IPL inputs and still pass through
recognition latency.

This matters more than it sounds: a beam-bounded interrupt handler that
arrives 50 CCK early steals that time from the main loop every frame. The
canonical regression was a scene player running at half speed because
too-early vertical-blank IRQs truncated the depacker's per-frame slice; the
latency model fixed it, confirmed against real hardware with the timing-test
disk.
A 68000 does not enter an exception the moment INTREQ rises. Two hardware
mechanisms sit between the two events, and Copperline models both:

1. **The Paula IPL pipe.** A change to the enabled-pending interrupt set
reaches the CPU's IPL pins only after a few chip clocks of pipelining
inside Paula (`DEFAULT_IRQ_LATENCY_CCK = 5`, `src/bus.rs`;
`COPPERLINE_IRQ_LATENCY_CCK` overrides, `0` disables both mechanisms).
The delay is attached to asynchronous Paula/CIA/blitter/Copper source
assertions. A CPU write that merely changes INTENA/INTREQ masking or
acknowledges a latch normally only updates the delayed-bit bookkeeping.
PORTS is level-fed by CIA-A/Gayle-style INT2 sources and remains
immediately visible when software unmasks an already-latched level; other
newly exposed latched sources are treated as freshly-present CPU IPL
inputs and still pass through the pipe. INTREQR reads are never delayed:
the pipe sits between the level encoder and the pins, not on the register.
2. **Boundary sampling.** The CPU latches its IPL pins during bus cycles,
and the take-interrupt decision at an instruction boundary uses the level
latched at the *previous* instruction's last bus access
(`CpuBus::sample_ipl`, `src/cpu.rs`). A level that rises during an
instruction's trailing internal cycles is therefore recognised one
instruction later, exactly as on silicon.

Together these reproduce the raise-to-handler-entry positions measured
against vAmiga (and the vAmigaTS real-A500 photos) across VERTB and
copper-poked INTREQ sources under a range of foreground loops; the residual
is 0..+7 CCK of per-instruction IPL poll-point detail the vendored core does
not model. An earlier revision used a blanket 65 CCK "recognition latency"
calibrated against timing-test row 19 with a mis-decoded VHPOSR (the low
byte is the CCK position, not CCK/2); that delivered every interrupt ~50 CCK
late and dominated the vAmigaTS cputim/irqtim divergence.

## Real-time pacing

Expand Down
2 changes: 1 addition & 1 deletion docs/zorro.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ manifest capabilities; importing one that was not granted fails to load):

Interrupt lines are level-sensitive and polled, exactly like the in-tree
boards: a plugin holds `int2`/`int6` non-zero while the line is asserted, and
the bus applies the 68000 interrupt-recognition latency automatically -- the
the bus applies the interrupt-delivery pipeline automatically -- the
plugin never pulses INTREQ.

Plugins can be written in any language that targets `wasm32` (Rust, C, Zig,
Expand Down
49 changes: 30 additions & 19 deletions src/bus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,16 +114,28 @@ fn external_access_cck_x100_setting() -> u32 {
}
}

/// 68000/Amiga interrupt-recognition latency in color clocks (DEFAULT ON).
/// Real hardware takes ~96-100 cck from an interrupt request to the handler's
/// first instruction; Copperline's bare model took ~48 (finish-instruction + the
/// 44-cycle exception only), i.e. it delivered interrupts ~50 cck too early.
/// The timing-test rows 19 (handler entry) and 22 (raise position), run on
/// FS-UAE and vAmiga, localised the gap to recognition latency (the raise
/// position matches; only the raise->entry time differed). Default 65 cck makes
/// row 19 match real HW (~hpos 116 vs vAmiga 114 / FS-UAE 122). Set
/// COPPERLINE_IRQ_LATENCY_CCK to override (0 disables = the old behaviour).
const DEFAULT_IRQ_LATENCY_CCK: u32 = 65;
/// Paula INTREQ/INTENA -> CPU IPL-pin propagation delay in color clocks
/// (DEFAULT ON). A change to the enabled-pending interrupt set does not reach
/// the 68000's IPL pins combinationally: Paula pipelines the encoded level to
/// the pins over a few chip clocks (vAmiga models the same pipe as iplPipe,
/// with the pin taking the new value ~4 DMA cycles after the level change).
/// Together with the IPL sampling model in cpu.rs (the interrupt decision at
/// an instruction boundary uses the level sampled at the PREVIOUS instruction's
/// last bus access, as on the real 68000), this reproduces the raise-to-entry
/// latency measured against vAmiga and the vAmigaTS real-hardware photos.
///
/// History: this was 65 for a while, calibrated against timing-test row 19
/// with a mis-decoded VHPOSR (the low byte is the cck position, not cck/2);
/// that made every IRQ delivery ~50 cck late and dominated the vAmigaTS
/// cputim/irqtim/inttim divergence. Set COPPERLINE_IRQ_LATENCY_CCK to override
/// (0 disables the pipe AND the boundary-sampling delay = raw model).
///
/// 5 = the ~4-cycle pin pipe plus the one-cck register-change commit a poked
/// INTREQ takes before the level encoder sees it (folded into one constant;
/// an IRQ-latency probe against vAmiga across seven source/loop geometries
/// lands within 0..+7 cck, the residual being per-instruction IPL poll-point
/// detail the vendored core does not model).
const DEFAULT_IRQ_LATENCY_CCK: u32 = 5;

/// Read the COPPERLINE_IRQ_LATENCY_CCK setting once, at bus construction (stored in
/// `irq_latency_setting`). Unset uses DEFAULT_IRQ_LATENCY_CCK; 0 disables.
Expand Down Expand Up @@ -705,21 +717,20 @@ pub struct Bus {
pub(crate) delivered_copper_irq_beam: Option<(u32, u32)>,
coper_cpu_irq_delay_cck: u32,

/// General 68000 interrupt-recognition latency (COPPERLINE_IRQ_LATENCY_CCK).
/// Real HW takes ~96-100 cck from a VERTB request to the handler's first
/// instruction; Copperline's bare model takes ~48 (finish-instruction + the
/// 44-cycle exception only). The timing-test rows 19 (handler entry) and 22
/// (raise position) localised the ~50 cck gap to interrupt RECOGNITION
/// latency, not the raise position. When the setting is non-zero, a newly
/// Paula INTREQ -> CPU IPL-pin propagation pipe (COPPERLINE_IRQ_LATENCY_CCK,
/// see DEFAULT_IRQ_LATENCY_CCK). When the setting is non-zero, a newly
/// raised maskable interrupt is held invisible to the CPU for that many cck
/// (`irq_latency_mask` = the delayed bits, `irq_latency_cck` = countdown,
/// `irq_latency_last_pending` = previous pending set for rising-edge detect).
/// INTREQR reads are NOT delayed (the pipe sits between the level encoder
/// and the pins, not on the register).
irq_latency_cck: u32,
irq_latency_mask: u16,
irq_latency_last_pending: u16,
/// Configured recognition latency in cck (from COPPERLINE_IRQ_LATENCY_CCK or the
/// default); 0 disables the model. A field (not a global) so tests can set
/// it per-instance -- mechanism tests run with 0 to deliver IRQs immediately.
/// Configured IPL-pipe length in cck (from COPPERLINE_IRQ_LATENCY_CCK or the
/// default); 0 disables the pipe and the cpu.rs boundary-sampling delay. A
/// field (not a global) so tests can set it per-instance -- mechanism tests
/// run with 0 to deliver IRQs immediately.
pub(crate) irq_latency_setting: u32,

/// Palette snapshots written by CPU interrupt handlers. The top
Expand Down
Loading
Loading