< all news

Copperline 0.19.0: Amiga debugging from VS Code, Bartman's profiler on Copperline, and two open ROMs

Copperline 0.19.0 is the developer release. A new Debug Adapter Protocol server lets you debug an Amiga program at source level from Visual Studio Code, with breakpoints, stepping and Step Back. Bartman's Amiga C/C++ extension also runs on Copperline, with its visual profiler fed by the emulator's per-colour-clock bus captures.

Developers also get an MCP server for AI coding agents, a WinUAE-compatible uaelib trap, an HRTMon freezer cartridge and CPU wait attribution in the Frame Analyzer. Away from the debugger, there are open-source replacements for the CD32 FMV and A2091/A590 ROMs, Simon Dick's copperhf.device hard-disk controller, and uncapped integer scaling with autocrop. As always, the compatibility fixes model the Amiga's chips rather than individual titles.

Source-level debugging in VS Code

copperline-ctl --dap is a Debug Adapter Protocol server built on the control protocol, and tools/vscode-copperline packages it as a VS Code extension. A launch.json names the executable and the machine. F5 boots the bundled AROS ROM or a Kickstart ROM from 1.3 up, warp-boots to the program and stops at main with the source open.

Copperline's native debugger in VS Code stopped in a C worker function, with the parameter and globals, the cross-file call stack, and the matching 68000 disassembly
The native adapter stopped in a C function of a two-file relocatable ELF build: parameter, globals, call stack and the matching 68000 instructions. Step Back had just been used to get here.

From there it works like any other VS Code debugger. Source, function, instruction, data and exception breakpoints take conditions the machine evaluates itself (d0 == 5, [$DFF006] != 0). Step Over, Into and Out work by statement or instruction. There are Registers (FPU included), Locals and Globals, hovers and watch expressions, and Memory and Disassembly views with per-instruction cycle counts and ROM addresses named live from the running guest ([exec] AllocMem+$12). Two things are specific to Copperline. Step Back and Reverse Continue work, because the machine is deterministic and keeps a ring of snapshots. A Chipset scope and a Custom Registers tree document every custom register's bit fields and access rules, from a new reference covering all 237.

Debug information comes from vasm's LINE hunks, the DWARF that amiga-gcc 6.5 leaves in the executable, or an ELF sibling from Bartman's m68k-amiga-elf-gcc. For relocatable multi-file builds, the reader applies the relocations itself. The Profile button captures frames as a source-mapped .cpuprofile with a [Bus wait] child separating chip-DMA contention from CPU work. nvim-dap and any other DAP client can use the same adapter. See the VS Code chapter and the DAP reference.

Bartman's extension, running on Copperline

Bartman's Amiga C/C++ extension (compiler, patched GDB, source annotations and the visual profiler) only spoke to its own WinUAE build. A public fork adds a Copperline backend, proposed upstream as PR 307. The fork is pinned and documented, so nothing waits on the merge.

VS Code paused on a C breakpoint in the demo's frame loop with 68000 registers shown, beside the Copperline window running the demo with a guest-drawn debug overlay
Paused on a breakpoint in the template's frame loop, with the demo still on screen in Copperline and a debug overlay drawn by the guest.

Copperline gained the other half. --gdb-gui attaches the GDB stub to the interactive window, so the screen stays visible and the audio plays. --gdb-dialect bartman speaks the patched GDB's wire contract (hunk offsets, the return-from-ROM breakpoint, monitor reset, DBG: output). monitor profile writes the extension's profile format from Copperline's precise sampler and full bus trace. A windowed launch waits paused until GDB connects, because on the first attempt the guest finished loading before the debugger arrived.

Bartman's profiler showing a frame timeline, blitter A, B, C and D channel images for each blit, and the Copper instruction list
The blitter view: A, B, C and D channel images for every blit in the frame, next to the Copper list.

The result is Bartman's profiler on a cycle-driven machine. In a captured frame, the timeline shows the CPU call stack against DMA and blitter activity. The Resources view reconstructs the displayed bitmap from the recorded Copper writes, or shows a bitmap the guest registered by name. The Blitter view renders any channel of any blit from the DMA words it moved, honouring shifts, masks, modulos and the latched BLTxDAT inputs. The Copper list traces each MOVE back to its frame and beam position.

A captured CPU and DMA timeline above the Copper bitmap inferred from the captured custom registers, with the Copper instruction list alongside
The Resources view reconstructing the 320x256 display from captured custom registers and Copper writes.

The walkthrough, with a reproducible install of the fork, is Bartman with Copperline.

Profiling and chip-bus tracing

None of the above needs VS Code. profile.start over the control protocol writes one JSON record per committed frame: chip-bus ownership per Agnus owner, blit records, the guest's idle clocks, and optionally the per-colour-clock owner grid in vAmiga's DMA-debugger letter code and a screenshot per frame. "samples": true records every retired instruction with its PC and colour-clock cost, plus live call stacks from an unwind table. copperline-ctl profile-report converts a capture to a Chrome .cpuprofile.

Source-mapped CPU call timing above and chip-bus DMA usage by bitplane, blitter, CPU, refresh and Copper below
Source-mapped CPU call timing and chip-bus DMA usage for one frame.

frame.slots and the bus event family expose a 24-byte record per raster colour clock: register, DMA kind and subtype, size, IPL, CIA E-clock phase, address, data, and a hardware-event mask (blitter start and finish, Copper WAIT wake, DDF strobes, vertical blank, CIA IRQ pins, STOP entry and wake). The Frame Analyzer's Beam tab gains a CPU wait view (W). It attributes every colour clock the 68000 lost to the DMA class that held the bus, with a per-line stall gutter and the top stalled PCs. ROM addresses in call stacks, disassembly and profiles are resolved from the running guest's library and resident lists, so names follow SetFunction() patches and work on any Kickstart or AROS revision.

uaelib, MCP, and a freezer cartridge

A WinUAE-compatible uaelib trap at $F0FF60 answers the calls Bartman's template makes. warpmode() unpaces the window, and KPrintF output goes to stdout and the debugger console. The debug_register_* markers feed a new Frame Analyzer Resources tab with decoded bitmap previews and palette swatches. The overlay commands are drawn on the window but never reach a screenshot. The trap is 32 bytes of real 68k code decoded on the bus, with no A-line or TRAP hook.

copperline-ctl --mcp exposes every control-protocol method as a Model Context Protocol tool: 95 of them, plus session launch and attach. Claude Code, Cursor or any other MCP client can boot a machine, set breakpoints, step, read memory and receive screenshots as images. For Claude Code, the whole setup is claude mcp add copperline -- copperline-ctl --mcp.

[cartridge] model = "hrtmon" fits an Action Replay-style freezer with HRTMon 2.39, built reproducibly from the upstream GPL source. It has a 1 MiB bank at $A10000, and a freeze copies a shadow of every custom and CIA register into it. The freeze itself is a level-7 NMI through the current VBR, which the 68000 takes at the next instruction boundary whatever the SR mask. Freeze with Tools → Freeze in the window or --freeze-after headless, and use x in the monitor to carry on.

Two open ROMs

The last release added the CD32 FMV cartridge but needed Commodore's cartridge ROM. 0.19.0 ships a clean-room GPL replacement with no firmware copied. It installs cd32mpeg.device, which reads MPEG sectors through the host cd.device and feeds the CL450 and L64111 ports. A videocd.library and boot strap cold-boot a White Book Video CD into a track player under Kickstart. For AROS, the matching driver was merged upstream this cycle. Cannon Fodder's filmed intro plays on both paths, and the CD32 profile now fits the cartridge by default (fmv_rom = "" gives a stock machine).

The A2091 and A590 get a clean-room 64 KiB autoboot ROM: scsi.device 42.40 derived from the open A4091 software, an RDB automounter with Kickstart 1.3 support, and a WD33C93 transport. The transport uses the board's DMAC for even-aligned chip-side buffers and bounces through chip RAM otherwise. The build also emits split U13/U12 EPROM halves for a real board.

copperhf.device

Simon Dick (@sidick) built copperhf.device, Copperline's own hard-disk controller and the analogue of WinUAE's uaehf.device. It is a Zorro II board with a doorbell and completion-queue register block, and its autoboot ROM has an RDB mounter that works from Kickstart 1.3 to AROS. It implements the full trackdisk family (TD64, NSD, HD_SCSICMD with autosense, disk-change interrupts). Completion time is a pure function of emulated time, so host disk speed never reaches the guest. [copperhf] unit0..unit6 take RDB hardfiles, bare images, .hdz and host directories. The launcher gains a Copperline HD page, and copperline-import-uae maps WinUAE's uaeN drives onto it.

Scaling, autocrop, warp boot

A new WGSL scaler pass replaces the pixels crate's built-in draw, so integer scaling is no longer capped at 4x on a 5K or 6K display. Under the default TV pixel aspect, it now presents the unresampled canvas with a separate whole-number factor per axis, chosen to approximate the glass's pixel shape. [display] autocrop crops the window to the display window the program sets up. It uses the renderer's own per-line DIW bounds, never a title list. A latch grows the crop immediately and shrinks it after half a second of stability, and it works for DblPAL and Multiscan screens too. The browser build gains both controls.

--warp-boot runs any configuration unpaced from power-on until its disks have been idle for ten seconds, then re-paces with audio. --warp-until warps to an emulated timestamp. Lee Hobson's [emulation] auto_launch boots a saved default straight past the launcher, and he moved the host data directory out of the hidden config tree into Documents. --load-uss imports a WinUAE .uss state as an approximate interchange for resident code. --run now works on a bare Kickstart 1.3, because the staged boot volume carries its own small GPL shell commands.

Hardware accuracy fixes

  • Floppy motor latch: every drive, DF0 included, clocks /MTR on its own /SEL edge. DF0 was level-sensitive, which stopped a motor a loader had just started. Alongside it, the 68000 IPL recognition pipe keeps one deadline per source and bounds an idle STOP nap. Pre-display sprite DMA is replayed at its own beam time, and a loader parked on DSKSYNC no longer costs a whole-revolution scan per idle slice (Spooky Town under Kickstart 1.3).
  • DSKBYTR and IPF density: DSKBYTR is framed by the read shifter's bit counter rather than the track's byte grid, and WORDEQUAL is a one-cell level. IPF density models for Copylock, Speedlock and Brierley tracks decode with their mastered cell timing (Lemmings' key-track check).
  • Akiko: subcode frames with a regenerated Q position are delivered during CD audio play (Liberation's intro waits on them). The TX DMA keeps draining the command ring while a lead-in dump streams, so a 39-track disc no longer laps it (Pinball Illusions).
  • Agnus: a bitplane fetch run carried past the last display line is cancelled with the vertical window. This removes a phantom row the refreshed AROS boot screen exposed, and a new ddfprobe-vspill golden pins it.
  • 68030 and 68040 timing were recalibrated against a real A4000 with three CPU boards and a new bootable probe. The 68030 now runs on the MC68020UM tables and the 68040 gets a single-issue pipeline model, replacing a scaled-68000 approximation that over-billed plain 68040 execution by about 2x.
  • Toccata: the mixer no longer holds the last DAC sample forever after the codec stops (Simon Dick).
  • Bundled AROS ROM refreshed to upstream master with the open cd32mpeg.device, the CD32 chip-memory savings and CDXL presentation series, and a locally patched 8 KiB process-stack floor.

Compatibility notes

Save states from 0.18.0 do not load in 0.19.0. The state version moves from 70 to 79. Saves are now written atomically, so a failed save no longer destroys the previous snapshot.

Your host data moved to Documents, with no automatic migration. Configs, save states, screenshots, recordings, NVRAM, gamepad calibrations and the WHDLoad library now live in ~/Documents/Copperline/ (macOS, Linux) or Documents\Copperline\ (Windows). Copy your old ~/.config/copperline or %APPDATA%\copperline there once, or Copperline starts at factory settings.

Some defaults changed. The CD32 profile fits the open FMV cartridge unless fmv_rom = "", and an A2091 with no named ROM loads the bundled one instead of refusing to start. An explicit ROM path is honoured as before. The uaelib trap is on by default ([emulation] uaelib = false turns it off), and emulated 68030/68040 speed changed with the recalibration.

No configuration keys were removed or renamed.

Contributed this cycle

Simon Dick (@sidick) built copperhf.device end to end and fixed the Toccata's stopped-codec DC offset. Lee Hobson (@hobbo91) added auto-launch and the Documents move. Bartman wrote the extension this release builds on, and the AROS developers merged the open FMV driver upstream.

Get it

Pre-built packages for macOS, Linux, and Windows (including Windows on ARM) are on the GitHub release page, and the Homebrew tap has been updated. You can also run the emulator in your browser at copperline.dev/try.

Copperline is funded by its patrons, who pay for the hardware every timing model is measured against. If it is useful to you, consider supporting it on Patreon.

- Andrew Hutchings (LinuxJedi) · linuxjedi.co.uk