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.

Direct executable launching (--run)

The --run flag boots Copperline straight into an Amiga executable on the host filesystem, with no disk image or Workbench installation to prepare. This suits development with an Amiga cross-compiler toolchain:

copperline --run build/hello
copperline --run build/hello --run-args "-level 2"
copperline --run build/hello --run-stack 32768 --run-detach

Add --window-scale 2 to open at twice the normal window width and height, or --maximized for a maximized window that keeps its title bar and the desktop taskbar visible. --full-screen opens borderless fullscreen. These also work with --config; see Configuration.

To turn an already linked hunk executable into a standard 880 KiB floppy, use copperline-ctl exe2adf PROG --boot [--out FILE] (by default the output is PROG with its extension changed to .adf). It writes the executable and an S/Startup-Sequence that runs it, using the same OFS directory-tree writer as Copperline’s virtual filesystems; --boot installs the AmigaDOS boot block. Omit --boot for a mountable data disk. The executable’s filename must be 1-30 Latin-1 characters and cannot contain : or /; the generated script uses the same single-byte name stored in the disk directory.

How it works

When --run is used, Copperline mounts two host directories as live AmigaDOS volumes:

  1. RunBoot: (boot priority 6) -- A generated boot volume containing an S/Startup-Sequence that sets the current directory, launches the executable, and writes a completion marker holding the program’s AmigaDOS return code when it exits. Copperline stages it in run/boot-<pid>/ under the Copperline host data folder and regenerates it on every launch. Bundled C:FailAt, C:CD, C:Stack, C:Echo, and C:Done executables supply the commands missing from a bare Kickstart 1.3 ROM (Done writes the return code the CLI keeps in cli_ReturnCode); C:Execute supplies the detached script handoff on later ROMs. No Workbench command files are needed.

  2. RunProg: (read/write) -- The host directory containing the executable. The guest loads the binary directly from this volume, and any files the program writes land in the same host directory.

--run-args STRING appends arguments to the program’s command line. --run-stack BYTES accepts 2048 through 2147483644 bytes and issues an AmigaDOS Stack command before the executable; invalid sizes are rejected before booting. --run-detach launches the program through Run >NIL: <NIL: and closes the boot CLI (Kickstart 2.0+ or AROS).

Unlike WHDLoad, --run derives nothing: the machine is whatever the configuration and CLI flags describe, which by default is an A500 with the bundled AROS Kickstart replacement. Choose another machine as usual:

copperline --model A1200 --fast 8M KICK31.ROM --run build/demo

Guest exit status (--exit-on-return)

With --exit-on-return (which requires --run), the session ends the moment the program’s return code lands in the completion marker, and Copperline’s own exit status is that code (clamped to 0-255). Like --coverage, the flag on its own makes a headless run: no window, unthrottled, and no capture flag needed to end it. Only with --control-gui or --gdb-gui does the session stay windowed, and the window then closes when the program returns. --gdb and --control, which own the run loop themselves, refuse the flag.

copperline --run build/tests --exit-on-return --noaudio; echo $?

If the run ends for another reason before the program returns (the last --screenshot-after fired, or the window was closed), the status is 4. A program that never returns therefore needs a bounding flag such as --screenshot-after 60 /tmp/end.png to turn into a 4 rather than an endless run. The generated script sets FailAt 2147483647 so no return code aborts it before the marker is written. A non-zero code takes precedence over a failed --expect-screenshot (status 3); a zero one does not hide it. The full status table is in Headless. guest/run-tools/retcode returns the number given as its argument, for checking the plumbing end to end.

Fast-forward boot (Warp mode)

In interactive windowed sessions, --run automatically enables warp mode during boot. (For configurations booting from media rather than --run, warp boot is also available via --warp-boot / --warp-until; see Configuration.) The emulator runs unthrottled with audio muted until the guest OS loads the executable (detected at its LoadSeg, before its first instruction runs). Emulation and audio then return to real time.

Additional operational notes:

Debugging

When launched with --gdb, Copperline halts execution at the entry point of the loaded program before the first instruction runs:

copperline --run build/hello --gdb :2345
m68k-amiga-elf-gdb hello.elf -ex "target remote :2345" -ex continue

When halted, the GDB stub reports the base address of the first hunk for symbol loading via add-symbol-file. The GDB monitor command monitor segments lists all hunk addresses; monitor return-to-program runs out of the Kickstart ROM window after an OS call.

The control protocol gets the same break-at-entry: with --run, --control and --control-gui arm a one-shot loadseg stop for the program, and segments.list reports its hunk addresses at that stop. Scripts can also arm their own loadseg breakpoint to catch every load.

For an IDE, the Debug Adapter Protocol server does all of this by itself: a VS Code (or nvim-dap) launch configuration naming the program starts Copperline, stops at the entry point, and debugs by source line from the executable’s own debug information.

--coverage FILE turns the same load-to-exit window into lcov line and function coverage of the program, written when it exits or the run ends; see Guest code coverage.

WinUAE-compatible uaelib trap

WinUAE’s boot ROM provides guest programs with a lightweight service interface, the “uaelib” trap at $F0FF60. Cross-compiler toolchains and templates (such as vscode-amiga-debug) use this trap for helpers like warpmode(), KPrintF(), and debug_register_*(). Copperline implements the same ABI at the same address, so code written for that template works unmodified.

Guest code checks the instruction word at $F0FF60 (0x4EB9 for a JSR, or WinUAE’s A-line 0xA00E) and invokes the address as a C function, passing the function index as the first stack parameter and receiving the return value in D0:

long (*UaeConf)(long fn, int index, const char *param, int len, char *out, int outlen)
    = (long (*)(long, int, const char *, int, char *, int))0xf0ff60;
if (*(UWORD *)UaeConf == 0x4eb9 || *(UWORD *)UaeConf == 0xa00e) {
    char out;
    UaeConf(82, -1, "warp true", 0, &out, 1);   /* warpmode(1) */
}
FunctionWinUAE meaningCopperline
13ExitEmu: quit the emulatorEnds the session cleanly at the next frame boundary: the window closes, a headless run stops, and the process exits with status 0 (or 3 if a screenshot expectation had failed). No arguments; returns 1. guest/uaelib-test/exitemu calls it.
82uae-configuration-style "key value" linewarp true / warp false (also yes / no) toggles warp mode. Parameters like cpu_speed and *_cycle_exact are accepted as no-ops. Returns 0.
86Debug log stringPrinted to the host console as DBG: <text> (shared with serial output), streamed to control-protocol debug subscribers as event.debug, and mirrored into the debugger console. Returns 1.
88debug_cmd multiplexerdebug_register_bitmap / _palette / _copperlist and debug_unregister register guest assets, viewable in the Frame Analyzer (Resources and Memory tabs), exportable as PNG there or with debug.resource.export, searchable via palette.dump / copper.list, and listed with the console DBGRES command; debug_start_idle / debug_stop_idle report guest idle time in debug.idle and event.frame.guest_idle_cck. Overlay drawing (debug_clear / debug_rect / debug_filled_rect / debug_text on a 768x576 virtual canvas) renders on screen in the window (excluded from captures and recordings). debug_load / debug_save are disabled by default; see below.
othersversion, disks, RTG, ...Return 0 with no side effects.

Memory-mapped debug output

Copperline also accepts WinUAE’s printf-style memory writes. Write each argument to $BFFF00, in order, then write a pointer to a NUL-terminated format string to $BFFF04. The format write prints one message and clears the argument queue. It uses the same DBG: output as KPrintF: the host terminal, debugger console, and control-protocol debug subscribers.

volatile ULONG *debugArgument = (volatile ULONG *)0xbfff00;
volatile ULONG *debugFormat = (volatile ULONG *)0xbfff04;
static const char valueFormat[] = "value = %ld (0x%08lx)\n";

*debugArgument = (ULONG)value;
*debugArgument = (ULONG)value;
*debugFormat = (ULONG)valueFormat;

The repeated value supplies two arguments, one for each conversion. Use volatile pointers so the compiler preserves all writes and their order. This interface needs no guest library or operating-system call, so it also works in code that has taken over the machine.

ConversionMeaning
%d, %i, %uSigned or unsigned 16-bit integer; l selects 32 bits (%ld, %lu).
%x, %X, %oHexadecimal or octal 16-bit integer; l selects 32 bits.
%p32-bit address as $ followed by eight lowercase hex digits.
%cLow byte as a character.
%sPointer to a NUL-terminated string.
%bPointer to a length-prefixed Amiga BSTR (a byte address, not a BPTR).
%%Literal percent sign; consumes no argument.

Numeric widths, precision, and the -, +, space, #, and 0 flags are supported; %08lx prints a zero-padded longword. String widths and precision are supported too. Pointer width and - alignment add spaces around the full $ prefix and eight hex digits; pointer precision does not shorten them. Floating point, * widths, %n, and WinUAE’s custom %[CYCLES] conversion are unsupported; unsupported conversions remain literal.

Argument writes can be bytes, words, or longwords. Format pointers must be longwords; paired word transfers within a 68000 or 68010 instruction are assembled automatically in either order, including predecrement MOVEM.L. Separate word-store instructions remain separate arguments. Up to 32 arguments and 4096 bytes of format/output are accepted. Extra arguments are ignored, missing ones print <missing>, and unreadable string arguments print <invalid>. An unreadable format pointer clears the queue without printing. Strings are read only from guest RAM or ROM, without accessing hardware registers.

The ports are enabled by default with [emulation] uaelib = true; setting it to false disables them along with the trap. The ports also work on CDTV, where an extended ROM can cover the separate $F0FF60 trap. Pending arguments are preserved in save states and rewind, and cleared by a machine reset. The queue is shared, so an interrupt handler must not interleave another message’s writes.

Kickstart compatibility

Normal --run launches support bare Kickstart 1.3, later ROMs, and bundled AROS. The generated boot volume supplies small GPL-licensed 68000 versions of FailAt, CD, Stack, Echo, and Done for the 1.x CLI. Working-directory changes, arguments, --run-stack, and the completion marker with its return code work without Workbench. The program itself must also use APIs available on the selected ROM.

--run-detach still requires Kickstart 2.0+ or AROS: the bundle does not replace the Run and EndCLI commands used by detached launches. The bundled Execute handles the generated child script without parameter substitution or nested scripts. Kickstart 1.2 lacks filesystem autoconfig support and cannot boot the host-directory volumes, even though the bundled commands use 1.x APIs.