Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Reverse debugging

Copperline supports reverse stepping and reverse execution. Emulation is deterministic, so Copperline keeps a ring of in-memory snapshots (see save states) and reconstructs an earlier state by restoring the nearest preceding snapshot and replaying forward to the exact instruction or beam position requested.

Reverse debugging is available through:

Headless “last writer” reverse watchpoints

When diagnosing memory corruption or unexpected state changes, a reverse watchpoint identifies the instruction that last wrote a memory word.

Set COPPERLINE_DBG_RWATCH to the target address and COPPERLINE_DBG_UNTIL to the emulated time at which the check should run:

RUST_LOG=info \
COPPERLINE_RTC_FIXED_SECS=1000000000 \
COPPERLINE_DBG_RWATCH=DE488 \
COPPERLINE_DBG_UNTIL=12.5 \
./target/release/copperline --config demo.toml --noaudio \
  --screenshot-after 13 /tmp/out.png

Example log output:

DBG RWATCH last writer of $0DE488: CAFE->0000 by pc=0x00FA37D8 pos=561401 f=40 cck=2864664

The line gives the writing instruction’s PC, the word’s value before and after the write, the position in retired instructions (pos), the video frame, and the colour clock of the write. The run then continues.

Environment variables

The last two also size the ring that a control protocol server arms for its reverse commands.

Interactive reverse controls

In the debugger window, the reverse controls follow the forward ones in the transport row:

ButtonFunction
< FrameStep backward to the previous video frame
< StepStep backward by one CPU instruction
< RunRun backward until a breakpoint or watchpoint condition is met

Opening the debugger arms the snapshot ring (512 MiB, one snapshot every 10 frames) if nothing has armed it already. History accrues only while the machine advances (Run, Frame), not while it is paused.

< Run (and the console command RRUN) replays the history and stops at the most recent event that any armed stop condition would have caught: PC breakpoints (their conditions are not re-evaluated), memory watchpoints, register watches, beam traps, Copper breakpoints, exception catches, and the task catch.

Rewind in normal sessions

Rewind also works outside the debugger:

Determinism requirements for reverse replay

For reverse replay to be exact:

  1. Guest clock: Use --rtc-time or COPPERLINE_RTC_FIXED_SECS so clock reads do not drift with host wall-clock time.

  2. Deterministic input: Input events must be repeatable (scripted input and recorded interactive inputs are handled automatically).

  3. Storage: RAM contents and floppy disk states are captured directly in memory snapshots. However, hard drive and CD images, and host directory mounts (including the volumes --run and --whdload stage), are accessed live from the host and are not rolled back on restore, so guest disk writes or host-side changes after a snapshot can make replay diverge. Taking a fresh snapshot after I/O gives later steps a new replay starting point (reverse_anchor in CCP; DAP does this at every run stop), but it does not make earlier external I/O reversible. Physical devices and live network, serial, MIDI, or sampler input have the same limitation.