Copperline is configured by a TOML file: the file passed with --config,
else ./copperline.toml if present, else the configuration saved with the
configuration screen’s Save default button (see The window, status bar, and menus). Every field
is optional; missing fields use the defaults documented here.
copperline.example.toml in the repository root is a commented companion to
this reference.
--factory ignores the saved default and starts from Copperline’s own
settings; --config and ./copperline.toml always win over it anyway. With
no default saved, the flag changes nothing.
Configuration parsing rejects unknown keys, invalid CPU or chipset names, and out-of-range sizes. Media are opened during startup; depending on the device, an unavailable image either stops startup or leaves the unit absent with a warning in the log.
Paths on Windows¶
This applies to every path field below (rom, disk images, hard-drive
files, the SCSI ROMs, and so on). In a TOML double-quoted string the
backslash is an escape character, so a Windows path written the obvious way
(rom = "C:\Kickstarts\KICK31.ROM") is rejected: \K is not a valid escape.
Use any one of:
rom = 'C:\Kickstarts\KICK31.ROM' # single quotes: a literal string, no escaping
rom = "C:\\Kickstarts\\KICK31.ROM" # double quotes: backslashes doubled
rom = "C:/Kickstarts/KICK31.ROM" # forward slashes also work on WindowsSingle-quoted literal strings are the least error-prone. macOS and Linux paths
use forward slashes and need none of this. TOML paths do not expand ~ or
shell variables; use an absolute path or a path relative to the working directory.
Command-line overrides¶
The most common machine knobs can be set on the command line without writing a config file. These flags layer on top of the config file (or, when there is none, the built-in defaults) and are validated by exactly the same parsers and range checks as the equivalent TOML fields:
| Flag | Overrides | Accepts |
|---|---|---|
--model NAME | [machine] profile | A1000, A500, A500OCS, A500Plus, A600, A1200, A3000, A4000, CDTV, CD32 |
--chipset NAME | [chipset] revision | OCS, ECS, AGA |
--video STANDARD | [chipset] video | PAL, NTSC |
--cpu MODEL | [cpu] model | 68000, 68010, 68EC020, 68020, 68030, 68040, 68060 |
--cpu-clock MHZ | [cpu] clock_mhz | a number of MHz |
--fpu / --no-fpu | [cpu] fpu | fit / omit a 68881/68882 |
--jit / --no-jit | [cpu] jit | experimental fast batch/trace-JIT CPU execution (68020+; not cycle-exact) |
--clipboard / --no-clipboard | [clipboard] share | share the host clipboard with the guest’s clipboard.device; fits an extra autoconfig board, so it is opt-in (default: off) |
--cartridge MODEL | [cartridge] model | none (default) or hrtmon, the bundled HRTMon freezer cartridge |
--chip SIZE | [memory] chip | 512K, 1M, 2M, ... |
--fast SIZE | [memory] fast | 0, 1M, 4M, 8M, ... |
--slow SIZE | [memory] slow | 0, up to 512K |
--ram-init MODE | [memory] init | zero (default), random[:SEED], pattern:WORD, or 0xWORD |
--motherboard SIZE | [memory] motherboard | Ramsey RAM (A3000/A4000): 0, 1M..4M, 8M, 12M, 16M; A4000 up to 64M |
--accelerator SIZE | [memory] accelerator | CPU-slot RAM at $08000000 (32-bit CPUs): 0 to 128M |
--floppy-drives COUNT | [floppy] drives | 0 to 4 wired drives (DF0: plus external drives) |
--floppy-speed PERCENT | [floppy] speed | 100 (real), 200, 400, 800, or 0 (turbo) |
--floppy-bridge DFN NAME | [floppy.dfN] bridge | drive a physical floppy drive: greaseweazle, or off |
--floppy-bridge-port DFN PORT | [floppy.dfN] bridge_port | that interface’s serial port (default: auto-detect) |
--floppy-bridge-cable DFN SEL | [floppy.dfN] bridge_cable | drive select: a/b (IBM PC cable) or 0-3 (Shugart) |
--floppy-bridge-mode DFN MODE | [floppy.dfN] bridge_mode | how tracks are captured: normal, compatible, stalling |
--floppy-bridge-density DFN D | [floppy.dfN] bridge_density | force a density: auto, dd, hd |
--floppy-replay-speed DFN SPEED | [floppy.dfN] replay_speed | replay already-captured tracks at fast (the default, double speed) or normal (the platter’s own) |
--floppy-bridge-writable DFN | [floppy.dfN] write_protected = false | allow writing to the real disk |
--joystick MODE | [input] joystick | gamepad (default), keyboard |
--mouse-sensitivity N | [input] mouse_sensitivity | 0-100 host mouse speed (50 default = 1:1) |
--mouse-capture MODE | [input] mouse_capture | when the host mouse is grabbed: click (default), auto, manual |
--port1 DEVICE | [input] port1 | mouse (default), gamepad-mouse, joystick, cd32, analogue, lightpen, none |
--port2 DEVICE | [input] port2 | the same devices except gamepad-mouse; default joystick (cd32 on the CD32 profile) |
--port3 DEVICE / --port4 DEVICE | [input] port3 / port4 | joystick or none in the parallel-port four-player adapter’s sockets (a joystick implies --parallel joystick-adapter) |
--autofire HZ | [input] autofire_hz | 0 (off, the default) to 30 |
--run-ahead FRAMES | [emulation] run_ahead_frames | run-ahead latency reduction: 0 (off) to 4 |
--warp-boot | [emulation] warp_boot | warp the boot until the boot storage goes idle |
--warp-until SECS | [emulation] warp_until | warp the boot until an absolute emulated time |
--whdload GAME | [whdload] game | boot a WHDLoad package: an .lha/.zip archive or a directory holding a .slave (WHDLoad) |
--coverage FILE | (none; needs --run) | write lcov line/function coverage of the --run program to FILE when it exits or the run ends (coverage) |
--coverage-source-map FROM=TO | (none; needs --coverage) | rewrite a source path prefix in the coverage file; repeatable |
--full-screen / --windowed | [display] full_screen | open fullscreen or windowed at start (default windowed) |
--maximized | [display] maximized | open a maximized window with borders and the desktop taskbar visible |
--monitor SELECTOR | [display] monitor | host display: auto, primary, a one-based number, or an exact name |
--window-position X Y | [display] position = [X, Y] | window top-left in logical pixels relative to the selected monitor |
--list-monitors | -- | list host display numbers, names, resolutions and scale factors, then exit |
--window-scale SCALE | [display] window_scale | initial window width and height multiplier, 0.5-4.0 (default 1) |
--show-status-bar / --hide-status-bar | [display] status_bar | status bar at start (default shown) |
--perf-overlay | [display] perf_overlay | show performance overlay at start |
--menu-scale SIZE | [display] menu_scale | size of the pop-up menu: 1x (default) or 2x |
--rtc-time TIME | [machine] rtc_time | seed battery clock with Unix seconds or "YYYY-MM-DD HH:MM[:SS]" |
--rtc-frozen | [machine] rtc_frozen | stop seeded clock at --rtc-time exactly |
--audio / --noaudio | [audio] output_enabled | enable (default) or disable real-time audio |
--audio-device NAME | [audio] output_device | host audio output device (substring match) |
--audio-channel-mode MODE | [audio] channel_mode | stereo (default) or mono |
--audio-stereo-separation PCT | [audio] stereo_separation | stereo width 0-100 (100 default, 0 = mono) |
--audio-filter MODE | [audio] audio_filter | Paula filter: auto (default), on, off |
--serial MODE | [serial] mode | off, stdout, midi, tcp, tcp-connect, pty, modem, device |
--serial-connect HOST:PORT | [serial] connect | remote to dial (implies --serial tcp-connect) |
--serial-device PATH | [serial] device | host serial port (/dev/tty..., COM3; implies --serial device) |
--serial-session FILE | [serial] session | replay a scripted modem session (implies --serial modem) |
--parallel DEVICE | [parallel] device | none, printer, sampler, joystick-adapter |
--a2065-net BACKEND | [a2065] net | none, loopback, nat, bridge |
--a2065-interface NAME | [a2065] interface | bridge adapter name (implies --a2065-net bridge) |
--hostsocket-net BACKEND | [hostsocket] net | none, loopback, nat, bridge, host |
--hostsocket-interface NAME | [hostsocket] interface | bridge adapter name (implies --hostsocket-net bridge) |
--host-disk DEVICE [ATTACH] | [[host_disk]] | give the machine a host disk read-write; ATTACH takes the attach values (default ide-master) |
--host-disk-read-only DEVICE [ATTACH] | [[host_disk]] | the same, read-only |
--type-after SECS TEXT | (none) | type TEXT on the US Amiga keyboard from SECS (Headless) |
--expect-screenshot SECS PATH [TOLERANCE] | (none) | compare the frame at SECS with a PNG; exit status 3 on mismatch (Headless) |
--exit-on-return | (none) | with --run: exit with the program’s AmigaDOS return code (Run) |
--pcmcia-cf PATH | [pcmcia] card = "cf", path | a CompactFlash card over a hard-disk image in the A600/A1200 slot |
--pcmcia-sram SIZE | [pcmcia] card = "sram", size | a session-only SRAM card in the slot, 64K to 4M |
--gif-after SECS PATH | (none) | write an animated GIF clip of the display from SECS emulated seconds, then exit (see Capturing GIF clips) |
--gif-seconds N | (none) | length of each --gif-after clip in emulated seconds; defaults to [recording] clip_seconds |
For example, to boot a stock A1200 profile but with 8 MB of fast RAM and a faster CPU, with no config file at all:
./target/release/copperline --model A1200 --fast 8M --cpu-clock 28 KICK31.ROMA --model profile supplies the chipset, CPU, and memory defaults of a real
machine; the other flags then override individual values on top of it, just as
explicit [cpu]/[chipset]/[memory] sections override a [machine]
profile in a config file.
The serial and parallel ports have further per-run flags -- --midi-in,
--midi-out, --mt32-control-rom, --mt32-pcm-rom, --mt32-panel,
--sampler-audio-input, and --sampler-input-gain -- described with their
[serial] and [parallel] keys below.
Top level¶
rom = "KICK13.ROM" # Kickstart image, 512 KiB (or a 256 KiB 1.x part)
extended_rom = "cd32ext.rom" # optional: CDTV (256K at $F00000) or
# CD32 (512K at $E00000) extended ROM
# fmv = true # CD32: fit the FMV module (bundled open ROM)
# fmv_rom = "another.rom" # CD32: fit the FMV module with this ROM
# identify = false # drop the Copperline identification board
# from the Zorro chain (default: present)identify controls a small, inert Zorro autoconfig board Copperline puts on
the expansion chain (manufacturer 5192 / product 2) so guest software such
as identify.library can detect that it
is running under the emulator. It is on by default and does not change the
machine’s usable memory; set identify = false for a chain with no
emulator-identifying board. See Writing Zorro board plugins for details.
A positional ROM argument on the command line overrides rom. Omit rom
entirely (and pass no ROM argument) to boot the bundled AROS open-source
Kickstart replacement, Copperline’s default boot ROM; its main and
extended halves are found as described under
Where bundled files are found. The menu’s
Load Kickstart ROM... item fits a different ROM at runtime and
hard-resets the machine. Machine profiles that need an extended ROM (CDTV,
CD32) will tell you if it is missing.
The Kickstart and extended-ROM keys accept images in either byte order.
Besides plain CPU-order dumps, Copperline recognises the byte-swapped images
prepared for EPROM programmers from their header and restores them on load,
so either file boots identically. (The single-chip .bin ROM files in
Hyperion’s Kickstart 3.1.4/3.2 releases, such as
kick.a500a600a2000.46.143.bin, are of this kind: every 16-bit word has its
bytes exchanged.) A 256 KiB Kickstart 1.x part is mirrored across the
512 KiB ROM window, as it decodes on real hardware. The split hi/lo chip
pairs for the 32-bit machines are not accepted; use the matching single-file
image instead.
fmv = true fits the CD32 Full Motion Video module with Copperline’s bundled
open-source 256 KiB replacement ROM, and fmv_rom = "path" fits it with a
custom image (such as the Commodore v40.30 ROM) instead; both are valid only
with the CD32 machine profile. The cartridge slot is empty by default: a
stock CD32 has no module, and fitting one changes the guest’s memory layout
and boot timing enough to alter titles that never used it (fmv_rom = "", the
older spelling of an empty slot, is still accepted). The module autoconfigures
ahead of the standard Zorro chain, driving the CL450 video and L64111 MPEG
Layer II audio decoders through guest code. The launcher’s ROM tab exposes
this setting as FMV module ROM.
The open ROM includes a clean-room cd32mpeg.device and videocd.library
compatible with CD32 Kickstart 3.1. The library identifies inserted Video CDs
and parses their metadata. Under CD32 Kickstart, the bundled version 41
cdstrap autoboots Video CDs into a track menu: Up/Down navigates, Red plays,
and Blue stops or exits. Standard CD32 game discs pass through to the default
boot sequence. The bundled AROS ROM has its own MPEG device and CDXL playback
support but bypasses cartridge diagnostics, so the cartridge’s library and
player currently work only under Kickstart. Neither path needs proprietary
Commodore code or CL450 microcode: Copperline models the CL450 command
interface at the host level.
Copperline also names the ROM it is given. A ROM file’s name is whatever its
dumper called it, so the image is identified by checksum against a table of
the released Amiga boot ROMs -- every Kickstart from 1.0 to 3.2.3, the CD32
Kickstart and extended ROM, the CDTV/A570 extended ROMs and the A1000
bootstrap -- and the version is reported as, for example,
Kickstart 3.1 (40.68) A1200. Identification survives the same forms the
loader accepts (byte-swapped dumps, a 256 KiB part stored doubled), and an
Amiga Forever image still in its AMIROMTYPE1 container is reported as
encrypted rather than unknown. The identification appears in the start-up
config: log line, in the About window’s ROM: line, in the OSD when a ROM
is fitted from the menu, and under each path row of the machine-configuration
screen’s ROM tab. The bundled AROS ROM reports itself as bundled AROS; any
other image the table does not carry -- DiagROM, a ROM you built yourself --
goes unnamed and boots as usual.
Where bundled files are found¶
Copperline ships the open files it fits by default -- the AROS boot ROM, the
open CD32 FMV, A2091, A4091, and lide boot ROMs, and the HRTMon cartridge
image -- together with the WHDLoad support archives (release builds include
them; otherwise fetch them once with tools/fetch-whdload.sh or
Download on the launcher’s WHDLoad page). Each set lives in its own
subdirectory, looked for in this order:
the directory named by the set’s environment variable (below);
<sub>/beside the executable;Contents/Resources/<sub>/in a macOS.appbundle;../share/copperline/<sub>/relative to the executable (a Homebrew or Unix install);assets/<sub>/in the source tree, for a development build.
The WHDLoad archives are also looked for in whdload/support/ under the
host data directory (where the launcher downloads them), ahead of the source
tree.
| Files | <sub> | Override |
|---|---|---|
AROS boot ROM (aros-amiga-m68k-rom.bin, aros-amiga-m68k-ext.bin) | aros | COPPERLINE_AROS_DIR |
CD32 FMV module ROM (copperline-fmv.rom) | fmv | COPPERLINE_FMV_DIR |
HRTMon cartridge image (hrtmon.rom) | hrtmon | COPPERLINE_HRTMON_DIR |
A2091 boot ROM (copperline-a2091.rom) | a2091 | COPPERLINE_A2091_DIR |
A4091 boot ROM (a4091_cdfs.rom) | a4091 | COPPERLINE_A4091_DIR |
lide boot ROMs (lide.rom, lide-atbus.rom, cdfs.rom) | lide | COPPERLINE_LIDE_DIR |
WHDLoad support archives (WHDLoad_usr.lha, skick346.lha) | whdboot | COPPERLINE_WHDBOOT_DIR |
A key that names its own file (rom, fmv_rom, [scsi] rom, [lide] rom,
[cartridge] rom, [whdload] whd_package / skick_package) is used
instead of the search.
[paths] -- where files go¶
[paths]
# base = "/somewhere/else" # move the whole tree; the rest are under it
# states = "states" # save states, incl. the quick-save slots
# screenshots = "screenshots"
# recordings = "recordings" # video captures, GIF clips and recorded input scripts
# nvram = "nvram" # battery-backed RAMs, incl. CD32 game saves
# traces = "traces" # debugger traces and waveform captures
# configs = "configs" # configurations saved from the launcher
# roms = "roms" # the rest set where an empty file dialog opens:
# mt32_roms = "roms/mt32" # MT-32 control and PCM ROMs
# floppies = "floppies"
# harddrives = "harddrives"
# cds = "cds"[paths] sets where Copperline writes its output files and where file
dialogs open when their field is empty. Every key is optional and falls back
to the default directory structure under the host data directory
(~/Documents/Copperline on macOS and Linux,
%USERPROFILE%\Documents\Copperline on Windows, or the application folder
in a portable installation).
Relative paths are resolved against base (which itself defaults to the host
data directory), while absolute paths are used directly. Output folders are
created automatically on first write. Dialog folders are not created
automatically and only open in existing directories.
These name folders on the machine that runs the configuration, so a config
copied to another machine may name folders that are not there. At startup
each entry set here is checked and one that cannot be found is ignored with
a warning in the log, falling back to its default -- the check waits no
more than a moment, so a dead network mount delays starting rather than
preventing it. Explicit output paths on the command line
(--screenshot-after, --save-state-after, and so on) are used exactly as
written and never consult this section.
[machine] -- machine profiles¶
[machine]
profile = "A1200" # A1000, A500, A500OCS, A500Plus (A500+), A600, A1200, A3000, A4000, CDTV, CD32
rtc = true # add a battery RTC (default: only A500+/CDTV/A3000/A4000 ship with one)
# rtc_chip = "RP5C01" # MSM6242 (default) or RP5C01 (A3000/A4000 default)
# rtc_time = "2005-03-18 01:58:29" # seed the clock; it then ticks in emulated time
# rtc_frozen = true # stop the seeded clock at rtc_time exactly
# battmem = "battmem.nvram" # RP5C01 battery-RAM backing file (default when fitted)
# mem_controller = "ramsey-07" # none, ramsey-04 (A3000), ramsey-07 (A4000)
# rom_scsi_device_disable = true # skip the ROM's scsi.device (default: when its bus has no drives)A machine profile bundles the chipset, CPU, memory, gate array, and
peripheral defaults of a real machine. The key is profile (the deprecated
model alias still parses) so that it never collides with [cpu] model.
Explicit [cpu], [chipset], and [memory] sections override individual
profile defaults. Without a
[machine] section you get the A500 Rev 6A default (the same as the A500
profile: ECS 8372A Agnus, OCS 8362 Denise, 68000, 512K chip RAM, 512K
trapdoor slow RAM) -- the most common and most-targeted Amiga. An explicit
[chipset] revision overrides the per-machine chips, so revision = "OCS"
gives a plain 8371/8362 OCS machine.
| Profile | Chipset | CPU | Chip RAM | Slow RAM | Extras |
|---|---|---|---|---|---|
A1000 | OCS (8361/8367 Agnus, OCS Denise) | 68000 @ 7.09 MHz | 256K | 0 | WCS, boot ROM + Kickstart disk |
A500 | Rev 6A: ECS 8372A Agnus, OCS 8362 Denise | 68000 @ 7.09 MHz | 512K (up to 1M) | 512K | -- |
A500OCS | OCS (8371 Fat Agnus, OCS Denise) | 68000 @ 7.09 MHz | 512K | 512K | early A500 / A2000 |
A500Plus | ECS (8375 Agnus, ECS Denise) | 68000 @ 7.09 MHz | 1M | 0 | RTC |
A600 | ECS (8375 Agnus, ECS Denise) | 68000 @ 7.09 MHz | 1M | 0 | Gayle IDE |
A1200 | AGA (Alice/Lisa) | 68EC020 @ 14.18 MHz | 2M | 0 | Gayle IDE |
A3000 | ECS | 68030 @ 25 MHz | 2M | 0 | Ramsey-04 with 4M motherboard RAM, RP5C01 RTC, SCSI, Z3660 RTG |
A4000 | AGA (Alice/Lisa) | 68040 @ 25 MHz | 2M | 0 | Ramsey-07 with 4M motherboard RAM, RP5C01 RTC, IDE, Z3660 RTG |
CDTV | ECS | 68000 @ 7.09 MHz | 1M | 0 | DMAC CD controller, RTC, 256K extended ROM, no floppy drive |
CD32 | AGA (Alice/Lisa) | 68EC020 @ 14.18 MHz | 2M | 0 | Akiko, CD32 pad, NVRAM, 512K extended ROM, no floppy drive |
rtc exists because most Amigas shipped without a battery-backed clock. The
A500Plus (an OKI RTC soldered to the Rev 8A
board), CDTV, A3000, and A4000 fit one by default; the base
A500/A500OCS, A600, A1200, A1000, and CD32 have none. Set rtc = true to add
one -- for an A600HD or a clock-equipped A1200, say -- so the Workbench clock
keeps time.
rtc_chip names the part in that socket, because Commodore used two with
different register protocols: the OKI MSM6242 on the small boxes, the
CDTV, and the aftermarket clock expansions, and the Ricoh RP5C01 on the
A3000/A4000 motherboards (the Ricoh also carries 26 nibbles of battery RAM,
which AmigaOS uses via battmem.resource on those machines). The default
follows the profile -- RP5C01 on A3000/A4000, MSM6242 everywhere else
-- and setting the key implies rtc = true. AmigaOS probes for either part,
so the choice is mostly invisible to it, but Linux/m68k does not probe: it
drives the chip the machine model dictates, so an A3000/A4000 booting Linux
needs the RP5C01 answering for its clock to work.
battmem persists the RP5C01’s battery-backed registers -- the 26 RAM
nibbles behind battmem.resource plus the alarm and 12/24 settings --
across runs, the way the real board’s battery does. This is where
scsi.device keeps its per-unit SCSI host settings (an A3000’s or A4091’s
synchronous-transfer, disconnect, and last-drive options, including
remembering attached CD-ROM drives), so without it those revert every run.
The file uses the same .nvram layout as WinUAE and Amiberry, so backing
files interchange between emulators; only the battery payload loads back --
the time-of-save digits in the file never override the (host- or
rtc_time-driven) clock. Whenever an RP5C01 is fitted it defaults to
battmem.nvram in the [paths] nvram folder (a battmem.nvram already in
the working directory from an earlier Copperline keeps being used from
there); point it elsewhere with a path, or set battmem = "" to keep the
battery registers session-only. A persisted file carries guest-visible
state from one run into the next by design, so delete or disable it where
byte-for-byte reproducible headless runs matter.
rtc_time seeds the clock instead of letting it mirror the host’s: the value
is either an integer (Unix seconds, UTC) or a string
"YYYY-MM-DD HH:MM[:SS]" giving exactly the wall-clock time the guest reads
at power-on. A seeded clock ticks with emulated time, so the time the guest
sees is deterministic and reproducible byte-for-byte across runs -- the way
to test time-dependent guest software (TOTP/RFC 6238 vectors, timestamped
logs, date rollovers) or just to boot into a fixed date. Setting a time
implies rtc = true; combining it with an explicit rtc = false is an
error. rtc_frozen = true additionally stops the tick so every read returns
rtc_time exactly. Both are also available as --rtc-time / --rtc-frozen
CLI flags, and a control-protocol session can inspect and move the clock live
with rtc.get / rtc.set (see the control protocol).
Two guest-side notes: Kickstart 2.0+ loads the system time from the battery
clock automatically at boot, while Kickstart 1.3 only does so when the
startup-sequence runs SetClock LOAD; and the chip’s two-digit year registers
mean AmigaOS applies its usual century window, so seeds outside 1978-2077
will not read back as the year you set. A host-initiated reset or power
cycle restarts the emulated timeline and therefore restarts a seeded clock
from rtc_time; a guest-initiated reboot (the 68000 RESET instruction)
leaves it ticking, like real battery-backed hardware.
The A3000 and A4000 profiles are the big-box machines. They carry a
Ramsey memory controller (mem_controller), whose two registers answer at
$DE0003 and $DE0043, and Gary rather than Gayle -- so no PCMCIA slot and
no Gayle IDE.
The A3000 has its motherboard SCSI: a Super DMAC at
$DD0000driving a WD33C93, which Kickstart’s ownscsi.deviceinitialises at boot. Attach drives to it with[scsi] controller = "a3000"(the default controller on an A3000; see the[scsi]section below).The A4000 has its motherboard IDE interface at
$DD2020; attach drives with[ide], exactly as on a Gayle machine.
rom_scsi_device_disable skips Kickstart’s built-in disk driver. It defaults
on when the machine’s own controller -- the IDE port of an A600, A1200, or
A4000, or the A3000’s SCSI -- has no drives configured: with nothing to boot,
the driver only costs startup time probing an empty bus. Configuring a drive
turns the driver back on automatically (it is the boot path for those
drives), and setting the flag explicitly wins in either direction. The ROM
file itself is never modified.
Both profiles fit their stock 4M of Ramsey-controlled motherboard fast RAM;
[memory] motherboard resizes it up to 16M, and on the A4000 up to 64M
via the motherboard RAM expansion space (see the [memory] section).
[memory] accelerator adds CPU-slot RAM at $08000000 on any 32-bit
machine.
mem_controller is normally left to the profile. It is a separate key
because Ramsey’s registers collide with nothing else, so it can be fitted to
a wedge machine to exercise diagnostic tools that expect one.
The A1000 profile models the original Amiga, which has no Kickstart ROM.
Its rom is instead the 64K bootstrap ROM (“Amiga ROM Bootstrap”); on
power-up the bootstrap loads Kickstart from the Kickstart disk in DF0 into
256K of writable control store (WCS) at $FC0000, write-protects it, and runs
it, as the real machine does. So an A1000 config names the bootstrap ROM as
rom and puts the Kickstart disk in [floppy.df0]; leave it in and the
machine boots to Kickstart (which then asks for a Workbench disk). See the
ready-made a1000.example.toml.
The A500 profile models the common Rev 6A board: the ECS “Fatter” 8372A
Agnus (a 1 MiB chip-RAM reach and the software-selectable PAL/NTSC switch via
BEAMCON0) paired with the original OCS 8362 Denise. It is therefore an
Agnus-only ECS upgrade, not a full-ECS machine -- the OCS Denise means no
superhires or BRDRBLNK, as on the real board. Chip RAM defaults to
the stock 512K but accepts up to 1M ([memory] chip = "1M"); more than 1M is
rejected because the 8372A cannot address it. Booting with no [machine]
section uses the same Rev 6A defaults; select A500OCS or set
[chipset] revision = "OCS" for the older 8371/8362 machine.
[emulation]¶
[emulation]
power_on = true # false = start powered off at the test screen
auto_launch = false # true = the launcher runs this file at once
pacing_budget = "cycles" # "cycles" (hardware-accurate) or "instructions"
realtime_priority = false # true = raise the pacer/audio thread priority
warp_speed = "max" # turbo limit: "2x", "4x", "8x", "16x", or "max"
warp_boot = false # warp the boot until storage goes idle
warp_boot_idle = 10 # ...for this many emulated seconds
# warp_until = 12.0 # or warp until an absolute emulated time
uaelib = true # uaelib trap and WinUAE memory-write debug output
uaelib_files = false # opt-in file helpers, below the --run directory
rewind = false # true = record rewind history from power-on
rewind_budget_mb = 256 # host memory the rewind history may hold
rewind_interval_frames = 25 # emulated frames per rewind step
run_ahead_frames = 0 # run-ahead input-latency reduction, 0..4 (0 = off)The deterministic cycle-driven core is the only emulation timing. It is
paced to wall-clock for the interactive window and runs unthrottled for
headless captures; the emulated result is identical. (An older speed key
here is accepted but ignored -- “real” was the only timing model, so it
carried no information.)
power_on = falsestarts the machine powered off showing a test screen until you click the status-bar power button -- useful for arming video capture first. The power button cold-boots (reinitialising RAM according to[memory] init).auto_launch = truemakes the launcher run this configuration as soon as it opens it, including when it is loaded from the launcher. Set in the saved default configuration, it makes Copperline boot straight into the machine without showing the configuration screen. The launcher’s A/V & Emu -> Emulation -> Run on startup row controls this setting. Command-line launches are unaffected.pacing_budgetselects how real-time pacing budgets CPU work per frame:"cycles"(default) charges each instruction its actual 68000 cycle cost plus chip-bus waits, matching real hardware speed;"instructions"uses a flatCOPPERLINE_REAL_CPU_CPI(default 4.0) cycles/instruction quota, which is cheaper but runs the CPU faster than hardware.COPPERLINE_REAL_PACING_BUDGEToverrides this for one run. See The timing model for the full rationale.realtime_priority = trueasks the OS to schedule Copperline’s two latency-critical threads -- the wall-clock pacer and the audio callback -- above normal, which reduces frame stutter and audio glitches when the host is busy. It is best effort and off by default, and never fails the run:macOS -- the pacer thread joins the
USER_INTERACTIVEQoS class. The audio callback is left alone because Core Audio already runs it on a real-time thread (overriding that would only demote it).Windows -- both threads are raised via
SetThreadPriority; no privilege required.Linux/other Unix -- raising priority needs privilege (an
rtpriorlimit,CAP_SYS_NICE, or root). Without it the request is logged and declined, and the thread keeps normal scheduling.
COPPERLINE_REALTIME_PRIORITYoverrides this for one run; set it to0/false/offto force it off, or to any other value (or leave it empty) to force it on.warp_speedsets the default speed of Warp Speed (turbo) mode. With VSync enabled (the default), emulating one frame per presented frame would pin warp to the host monitor’s refresh rate. This option is an output frame skip --"2x","4x","8x","16x", or"max"(default) -- so warp retires that many emulated frames per presented frame, making warp roughly the limit times the refresh rate (host CPU permitting)."max"runs flat out and still presents at vsync when enabled. With VSync off, presentation frequency depends on host throughput. Adjust it live from the Warp Limit menu item orCmd+Shift+W/Alt+Shift+W(see The window and its controls).warp_boot = trueaccelerates boot sequences: from power-on, the machine runs unthrottled in warp mode (with audio muted) until storage activity (floppy and hard disk) remains idle forwarp_boot_idleemulated seconds (default 10), at which point normal real-time execution and audio resume. Large setups such as AmigaVision or a full Workbench installation then reach their menus quickly. Setwarp_boot_idlehigh enough to bridge expected pauses in disk access during boot (such as when SetPatch builds MMU tables across large memory configurations). CD audio playback does not count as storage activity, so a background CD-DA track cannot hold the warp phase open. If storage never becomes idle, warp boot times out after 600 emulated seconds.warp_until = SECSis the deterministic alternative: it warps until a fixed emulated timestamp.warp_bootandwarp_untilare mutually exclusive and cannot be combined with--run(which manages its own warp phase);--warp-bootand--warp-untilset them from the command line. Toggling warp manually (Cmd+W/Alt+W) or sendingwarp.set {"on": false}over the control protocol ends the warp phase at once. Headless captures ignore these settings, since they run unthrottled anyway. The launcher’s Warp boot and Warp boot idle rows (A/V & Emu, Emulation) configure storage-idle mode, while a loadedwarp_untilsetting appears as its own Until Ns option.uaelib = true(default) enables the WinUAE-compatible service trap at$F0FF60. Guest programs can use this interface to toggle warp mode (viawarpmode()in the vscode-amiga-debug template), emit debug log messages, and register bitmaps, palettes, and copper lists with the debugger (see Direct executable launching (--run)). It also enables the memory-write debug ports at$BFFF00/$BFFF04. Setting this tofalsedisables the ports and leaves$F0FF60unmapped. On CDTV configurations, the extended ROM occupies$F00000and covers the trap; the debug ports remain available.uaelib_files = truelets the uaelibdebug_load/debug_savecalls read and write files below the--runprogram’s directory. It is off by default because guest programs may be untrusted; absolute paths, parent-directory traversal, and transfers over 16 MiB are always refused.rewind = truerecords rewind history from power-on, soCmd+Z/Alt+Zand the Rewind menu item can step the whole machine backward through it. It rides the same deterministic snapshot ring as the debugger’s reverse controls (see Reverse debugging) and is off by default because it is not free:rewind_budget_mb(default 256) of host memory holds the snapshots, and the whole machine is serialized once everyrewind_interval_frames(default 25, half a second of PAL). One rewind step goes back exactly one interval; oldest snapshots are evicted first, so how much emulated time the budget buys scales with the machine’s RAM size. Turning the menu item off releases the retained snapshots. The same determinism preconditions as reverse debugging apply -- a real-time clock and host disk writes are not rolled back.run_ahead_framesreduces input latency in compatible windowed sessions. Each refresh commits one ordinary frame, snapshots the machine, executes the requested number of future frames, presents the last future image, and restores the snapshot. Audio, serial output, and control events come only from the committed frame; speculative output is discarded. The committed machine timeline is therefore unchanged. This costs one whole-machine snapshot per refresh and roughly(run_ahead_frames + 1)x realtime host performance, so start at 1 and raise it only while the performance overlay shows comfortable headroom. Large values also skip visible intermediate animation.Run-ahead stays inactive when a speculative frame could observe or change host state that the snapshot cannot restore. This includes warp and headless capture, RTG output, rewind/reverse debugging, debugger stops, injected faults, validation/SMC/heat-map/frame-analyzer observers, instruction or waveform traces, a connected control client, video recording, loaded save states, live serial or parallel devices, writable or physical floppy media, mounted CDs, hard-drive and host-directory storage, host networking or plugin boards, MHI decoding, and a live or persistent RTC/NVRAM. Offline WAV and stem capture are supported: speculative audio is discarded at the mixer before any sink receives it. The Run Ahead item under Emulation Settings adjusts the level live and reports the current blocking reason.
--run-ahead FRAMESoverrides the config for one run.
[cpu]¶
[cpu]
model = "68000" # 68000, 68010, 68EC020, 68020, 68030, 68040, 68060
clock_mhz = 14.0 # optional; defaults to the model's stock speed
# icache = false # instruction-cache model (on by default on all 020+ models)
# dcache = false # data-cache model (on by default: 030/040/060)
# fpu = true # fit a 68881/68882 (needs the 68020+ coprocessor
# # interface, so not valid on a 68000/68010). The full
# # 68040's and 68060's on-die FPUs are on by default.
# unimplemented = "trap" # 68060 only: "trap" (faithful; the OS needs
# # 68060.library) or "native" (execute the removed
# # instructions directly)
# jit = true # experimental: fast batch/trace-JIT CPU execution
# # (68020+; not cycle-exact)model: the 68010 models the vector base register, the format-stacking exception model, and DBcc loop mode; the 68EC020 is a 68020 instruction set with a 24-bit external address bus.clock_mhzdefaults to the model’s stock speed (68000/68010 ~7.09, 020 ~14, 030/040 ~25, 060 50) and is modelled as a whole multiple of the colour clock (3.546895 MHz), rounded to the nearest one. Fast RAM and ROM run at the CPU clock; chip and slow RAM stay chip-bus bound, so overclocking speeds up only what a real accelerator would speed up. TheA1200andCD32profiles keep their authentic 14.18 MHz while[cpu] modelis unset.icache/dcachemodel the on-chip caches and default on for the silicon that has them (instruction cache on the 020/68EC020/030/040/060, data cache on the 030/040/060), matching real hardware where AmigaOS enables them via CACR. The cache is sized to the CPU: 256 bytes on the 020/030, 4 KB on the 040, 8 KB on the 060. This is not cosmetic: code that loops out of chip RAM otherwise contends with bitplane DMA on every instruction fetch and can run at roughly half speed, which is why an AGA demo’s music or animation may pace correctly only with the cache modelled. The data cache covers expansion RAM/ROM only, since chip and slow RAM are DMA-visible and cache-inhibited as on real Amigas. Set either key tofalseto opt out.unimplemented(68060 only) picks what happens on the instructions the 68060 dropped from silicon: MOVEP, CHK2/CMP2, CAS2, misaligned CAS, 64-bit MUL/DIV, and most of the FPU beyond basic arithmetic (transcendentals, FMOVECR, packed decimal)."trap"(the default) is what the chip does -- they raise the unimplemented-instruction exceptions and the OS-side68060.libraryemulates them, as on a real CyberStorm or Blizzard board, so software using them needs that library installed."native"executes them directly for systems without it. Kickstart 3.1 itself boots under"trap".fpu = falseon the 68060 models the LC/EC060: FP instructions take the FPU-disabled exception (PCR.DFP), which an OS handler can also use to enable and restart. The 68060’s superscalar dual-issue and branch cache are modelled and activate when system software enables them (PCR.ESS and CACR.EBC, which68060.librarydoes at boot); until then the chip runs scalar, as on real silicon.jit(experimental, also--jit/--no-jit) runs the CPU through the m68k core’s batch/trace-JIT path instead of the cycle-exact per-instruction model: hot code compiles to native traces and fast-RAM accesses run through a zero-cost direct-memory window. The machine behaves like an ideal accelerator running atclock_mhz: one instruction per CPU clock with zero-wait fast RAM and ROM, so a 50 MHz 68040 delivers on the order of 50 MIPS (raiseclock_mhzfor more). Interrupts are recognized at batch boundaries, so chip-level races and cycle-counted effects no longer line up; leave it off for anything timing-sensitive (games, demos). Chip and slow RAM still arbitrate onto the shared chip bus in order, and the on-chip cache models stay active (as on a real accelerator, they are what lets chip-RAM-resident code run at CPU speed), so displays, blits, and device I/O work normally. Requires a 68020 or later: the 68000/68010 share one bus with the chipset and their floating-bus and prefetch semantics need the precise core, sojit = trueon those models logs a note and stays precise (the launcher greys the toggle). Known issue: the bundled AROS ROM’s boot screen may stay grey under JIT on some 020+ configurations (the guest still runs; Kickstart ROMs are unaffected).
[memory]¶
[memory]
chip = "512K" # OCS max 512K; ECS/AGA max 2M
fast = "0" # Zorro II fast RAM at $200000: 64K..8M board sizes
slow = "512K" # A500 trapdoor RAM at $C00000: 0 or up to 512K
init = "zero" # or "random[:SEED]" / "pattern:0x5555" for read testing
motherboard = "0" # Ramsey motherboard RAM (A3000/A4000): up to 16M (A4000: 64M)
accelerator = "0" # CPU-slot RAM at $08000000 (32-bit CPUs): up to 128M
z3 = "0" # Zorro III RAM (needs a 32-bit CPU): 64K..1G, power of twoSizes take a K, M, or (for Zorro III) G suffix -- KB/KiB and so
on work too -- or a plain byte count, and must be multiples of 4 KiB.
init controls cold power-on contents for writable system RAM. The default,
"zero", preserves Copperline’s normal compatibility behaviour. "random"
fills chip, slow, motherboard, accelerator, A1000 WCS, and RAM-backed Zorro
boards with deterministic pseudo-random bytes, which helps expose guest code
that reads memory before initialising it. The fixed default seed is identical
across hosts and repeated cold resets; "random:SEED" selects another decimal
or 0x hexadecimal 64-bit seed for test matrices. "pattern:WORD" instead
repeats one decimal or 0x hexadecimal 16-bit word in big-endian Amiga byte
order; for convenience a bare value such as "0x5555" means the same thing as
"pattern:0x5555". The Machine Configuration screen exposes these as
Power-on fill and Fill pattern on its Memory page. Warm keyboard resets
still preserve RAM, and save-state restores reproduce the saved bytes exactly.
For a one-off developer run:
./target/release/copperline --ram-init random --config game.toml --noaudio \
--screenshot-after 20 /tmp/game-random-ram.pngChip RAM is range-checked against the chipset: 512K on OCS, 2M on ECS/AGA (also bounded by the selected Agnus revision’s address reach).
Fast RAM is exposed as a Zorro II autoconfig board at
$200000, so it must be a legal Zorro II board size: 64K, 128K, 256K, 512K, 1M, 2M, 4M, or 8M. On the A600/A1200 more than 4M reaches the PCMCIA slot’s common-memory window at$600000and disables the slot (see[pcmcia]).Slow RAM ($C00000 “ranger” RAM) is arbitrated on the chip bus through Agnus exactly like chip RAM -- it is slow in the authentic way.
Motherboard RAM is the 32-bit local memory Ramsey drives on the A3000/A4000: it ends at
$08000000and grows downward (16M reaches$07000000), and Kickstart sizes it with its own probe -- no autoconfig involved. It needs a Ramsey ([machine] mem_controller, fitted by the A3000/A4000 profiles, which also fit their stock 4M of this RAM by default) and a 32-bit CPU, and must fill whole Ramsey banks: 1M-4M in 1M steps, or 8M, 12M, 16M. On the A4000 (Ramsey-07), sizes beyond 16M keep growing downward into the$04000000-$06FFFFFFmotherboard RAM expansion space, in 4M steps up to 64M (which reaches$04000000). Setmotherboard = "0"to remove it.Accelerator RAM is CPU-slot local memory: it starts at
$08000000and grows upward through the coprocessor-slot expansion space, up to 128M (ending at$10000000, where Zorro III space begins). This is the RAM an accelerator/CPU board carries, so it needs a 32-bit CPU but no particular machine profile; any whole number of megabytes fits. Kickstart sizes it with its own probe, like the motherboard bank.Z3 RAM requires a 68020/68030/68040/68060 (a 24-bit bus cannot reach it); Kickstart assigns its base address, usually
$40000000.
Additional expansion boards can be described with [[zorro]] metadata
files; see Writing Zorro board plugins.
[chipset]¶
[chipset]
revision = "OCS" # OCS, ECS, or AGA preset
video = "PAL" # PAL or NTSC
# agnus = "8372A" # optional fine-grained override
# denise = "OCS" # optional fine-grained overriderevision is a preset; agnus and denise allow the mixed configurations
real machines shipped with (a late A500 with an ECS Agnus but OCS Denise,
for example):
agnus:OCS/8370/8371(OCS),8372/8372A(ECS, 1M chip),8375/8372B(ECS, 2M chip),8374/ALICE(AGA).denise:OCS/8362,ECS/8373,LISA/4203.
The ECS preset picks an 8372A for up to 1M chip RAM and an 8375 above; the
A500Plus and A600 profiles always use the 8375, as the real boards did.
The AGA preset resolves to Alice and Lisa: 8 bitplanes, the 256-entry
25-bit palette with BPLCON3 BANK/LOCT banking, HAM8, FMODE wide bitplane and
sprite fetch (DMA and manual sprites), SSCAN2/BSCAN2 scan doubling, 35 ns
BPLCON3 SPRES output, BPLCON4, and CLXCON2. Remaining gaps are recorded in
Chipset modules.
[display]¶
[display]
overscan = "tv" # "tv" (default), "smart" or "full"
tv_h_centre = 0 # TV picture centring in lo-res pixels, -16..16 (+ = right)
tv_v_centre = 0 # TV picture centring in scan lines, -8..8 (+ = down)
pixel_aspect = "tv" # "tv" (default, 4:3 CRT) or "square" (exact 2x2 lo-res)
scaling = "smooth" # "smooth" (default, aspect fit) or "integer" (whole multiples)
autocrop = false # crop the window to the programmed display window (default false)
deinterlace = true # motion-adaptive interlace weaving (default true)
phosphor = 0.0 # CRT persistence fraction, 0.0 (off) to 0.95
shader = "none" # "none" (default), "scanlines", "mask", "crt", or a .wgsl file
shader_strength = 1.0 # how strongly the shader is mixed in, 0.0-1.0
bezel = "off" # monitor front: "off" (default), "1084", or "classic"
# bezel_stickers = "stickers" # folder of PNG stickers for the bezel (default: none)
perf_overlay = false # show the performance overlay at start (default false)
tint = "none" # "none" (default), "bw", "green", "amber", or "sepia"
menu_scale = "1x" # size of the pop-up menu: "1x" (default) or "2x"
full_screen = false # open borderless fullscreen at start (default false)
maximized = false # maximize with title bar and desktop taskbar (default false)
monitor = "auto" # host display: auto, primary, one-based number, or exact name
window_scale = 1.0 # initial window width and height multiplier, 0.5-4.0
# title = "{app} {version} · {machine} · {rom}"
status_bar = true # show the status bar at start (default true)
vsync = true # synchronise desktop presentation to vblank (default true)
hidpi_texture = true # draw the presentation texture at device-pixel density (default true)The default window title shows the machine and boot ROM. For a different
layout, set title with {app}, {version}, {machine} (or {m}), {rom}
(or {r}), and {hash} (eight hexadecimal digits of the loaded ROM). Other
text is literal; {{ and }} insert braces. For example,
title = "Workbench test · {m} · {r}" gives an instance a visible name.
Known Kickstarts use their checksum-identified version; AROS and unrecognised
ROMs include the short hash. The title follows ROM changes and loaded states.
vsync controls desktop presentation. On uses strict FIFO vsync to prevent
tearing. Off requests unsynchronised presentation where the graphics backend
supports it; it may reduce latency but can tear. Video Settings -> VSync
changes it live without restarting or changing normal emulation speed. The
choice carries into the configuration screen when saving a config. Headless
captures and the browser frontend are unaffected. Roughly 50 Hz PAL output
can still show an uneven cadence on fixed 60 Hz or 120 Hz displays: VSync
does not make those refresh rates match.
hidpi_texture decides the resolution of the texture the window is drawn
from. On (the default) it follows the display’s device-pixel density, so a
200% (Retina) window is fed a 2x texture and every host row picks its own
woven scanline. Off keeps the texture at canvas resolution and leaves the
upscale to the GPU’s scaler pass, a quarter of the per-frame texture
upload (and of the CPU copy, when a menu or overlay makes the frame
compose on the CPU) -- worth trying on a slow host that falls short of
real time in a high-density window. Integer scaling looks the same either
way (its whole-number blocks are point-sampled from the 1x texture); the
smooth fit selects rows at canvas resolution instead of the finer texture
grid, which fine interlaced detail can show. Captures are never affected.
The emulated framebuffer always carries the full overscan field Denise
produces; overscan picks how much of it is presented. "tv" presents
what the monitor’s glass shows: the captured aperture -- the standard
window plus the symmetric overscan margin the framebuffer captures on its
right edge -- fills the whole 4:3 glass, the way a real set’s raster
overscans its screen, so the picture (border colour included) reaches every
edge with no black bezel columns. The live window and PNG screenshots /
--dump-frames present the same 716x540 glass. PAL and NTSC scans share
that shape because both apertures fill the same glass: an NTSC scan’s
shorter crop (the 200-line standard window plus the same overscan margin)
is scaled onto the same output rows. "full" shows everything, which is
useful when debugging display alignment. COPPERLINE_OVERSCAN=full|tv|smart
overrides this for a single run. In all modes the presentation geometry
holds steady across the blank frames a screen change produces: a frame
showing only border colour keeps the previous frame’s aperture and
centring instead of snapping to the full framebuffer, so the picture does
not jump sideways at Kickstart screen changes.
"smart" keeps the TV aperture and automatically adjusts its horizontal
position after the hardware display envelope has remained stable for 25
distinct presented emulated frames. The automatic correction is limited to
eight lo-res pixels each way; blank frames retain the last position, and
small display windows do not pull the aperture toward individual objects.
It can bring slightly off-centre artwork, such as the CD32 boot logo, into
view without zooming out. Manual H/V centring remains available as a trim;
the combined horizontal correction is limited to the manual control’s
16-pixel travel. The menu’s Video Settings -> Framing switches between
TV, Smart and Full overscan live. TV remains the default.
Smart framing follows display geometry, not software identity or pixel colour. A fixed aperture can still crop a large overscan display, and deliberate effects in its newly exposed margin may become visible. Use TV for a fixed crop, or Full overscan to inspect the entire captured field.
tv_h_centre / tv_v_centre nudge where the TV presentation centres the
picture on the glass -- the H-CENTER/V-CENTER controls a real monitor
carried on its front. tv_h_centre is in lo-res pixels, positive moving
the picture right; tv_v_centre in scan lines, positive moving it down.
The default aperture centres the standard window, which leaves most of
the captured left overscan off-glass; software that leans its artwork
into that overscan (the CD32 boot logo’s leading “A” serif, for example)
comes fully into view a few pixels of tv_h_centre later, as it did on a
set whose picture sat a little right of centre. Glass the nudge
exposes beyond the captured raster is unscanned and shows black. The
knobs move the TV aperture, so overscan = "full" ignores them; captures
(screenshots, frame dumps) follow them like the live window. The menu’s
Screen Centring rows (under Video Settings) step the same knobs live
without touching the config.
pixel_aspect selects how emulated scanlines map to host rows. The default
"tv" presents the field with the non-square pixel aspect of a 4:3 CRT:
the full overscan scan fills a 4:3 picture, so PAL lo-res pixels come out
slightly wider than tall, as a real TV shows them (a 320x256 screen
spans about 640x482 window pixels). "square" uses one host row per woven
scanline instead, so every low-resolution pixel is an integer 2x2 square
and a 320x256 PAL screen occupies precisely 640x512 window pixels --
slightly taller than a real CRT picture, but exact for side-by-side pixel
comparison with square-pixel emulators. The menu’s Pixel Aspect item
flips the mode live without touching the config, and
COPPERLINE_PIXEL_ASPECT=tv|square overrides it for a single run.
scaling selects how that presentation canvas reaches the window, which is
a separate question from what the canvas is. The default "smooth" fits the
canvas to the window preserving its aspect ratio and interpolates, so the
picture always uses the full window height (or width) whatever fraction the
scale works out to. "integer" instead draws the canvas at whole-number
multiples -- the largest that fits the window, measured in physical device
pixels, and under the TV aspect a separate whole number per axis (below)
-- centred in black borders and point-sampled: every canvas pixel becomes
the same block of host pixels, with no row or column sampled twice, which
is the look WinUAE and Amiberry call integer scaling.
The fit is taken in whole canvas pixels against the physical surface: the canvas is re-rendered at whatever factor fits, rather than drawn at whole multiples of a fixed high-DPI texture, so every step exists on every display. A 2x-DPI laptop whose screen holds three physical pixels per canvas pixel but not four gets the 3x picture, and fractional desktop scales such as 150% take their whole physical multiples the same way. A 5K or 6K display in fullscreen fills as much of its height as the next whole multiple allows. The internal render factor is capped at 4x but the displayed multiple is not, so the display’s pixels stay exact whole blocks past the cap; only the status bar and menus, drawn at the capped factor, are resampled there. Only when the window is too small for even a 1:1 copy -- smaller than the canvas itself in physical pixels -- does the picture fall back to the smooth fit rather than cropping to what fits. RTG board modes follow the setting too: their frame is scaled from its own native resolution, so a 640x480 board screen is drawn at 1x, 2x, 3x of those pixels inside the display area.
Integer scaling draws from the unresampled canvas (one host row per woven
scanline, matching pixel_aspect = "square"). Under the default TV aspect,
the horizontal and vertical axes take independent integer multiples to
approximate the 4:3 CRT pixel aspect (PAL pixels about 1.08x wider than
tall, NTSC about 0.85x). The scaler picks the tallest integer fit whose
aspect is within 10% of the display shape. With autocrop freeing the
vertical margin that unused borders take, NTSC 200-line games reach a 4:5
pixel aspect on a 1080p display (a 1280x1000 active area), 6:7 at 1440p,
and 4:5 at 4K. PAL uses square integer multiples until the display is tall
enough for an 8:7 ratio. Odd scanline multiples stay crisp on progressive
displays because woven line pairs are identical. pixel_aspect = "square"
with "integer" keeps the multiples uniform and square, for side-by-side
pixel comparisons. Switching integer scaling on in TV mode resizes the
window to the unresampled canvas; captures always keep the configured
aspect geometry. Monitor bezels (bezel) use the resampled TV canvas under
both scaling modes. Programmable (ECS/AGA VARBEAMEN) and RTG displays scale
by uniform integer multiples. The menu’s Video Settings -> Scaling item
switches modes at runtime.
autocrop (default off) crops the window display to the active raster
window the hardware programs rather than showing the entire TV aperture.
Most software occupies less than the full raster (a 320x200 screen, for
example, uses 400 of 570 PAL woven lines), so cropping the unused borders
lets the active image fill more of a 16:9 or 21:9 monitor. With
scaling = "integer", the integer multiple is recalculated for the
cropped dimensions, which often allows a larger factor.
The crop area is computed dynamically each frame from scanlines containing fetched bitplane data. The viewport expands immediately when a program opens a larger display and tightens smoothly after a smaller viewport has remained stable for roughly half a second, preventing visual jitter during scene changes. The status bar and overlay panels remain docked at the bottom of the window; opening a menu or panel temporarily reveals the full display area so no controls are obscured. Autocrop only affects window presentation (screenshots, frame dumps, and recordings always capture the full configured aperture). CRT shader presets render over the cropped area. Programmable multisync modes (such as DblPAL or 31 kHz displays) are also cropped based on their active display windows. Autocrop is automatically suspended when using monitor bezels or RTG modes. The menu’s Video Settings -> Autocrop option toggles it at runtime.
For an automatic crop with the largest integer fit, use:
[display]
overscan = "smart"
autocrop = true
scaling = "integer"Smart autocrop selects directly from the unmasked raster, before the TV aperture clips its edges. It includes the full detected display window, including artwork outside the usual TV view. PAL/NTSC pixel aspect comes from the scan rather than from the crop dimensions. For a 320x200 display on a 1920x1080 surface with the status bar hidden, PAL fits at 5x; NTSC uses the existing per-axis 4:5 fit. Larger title screens reduce the scale to fit their complete detected display. No picture lines are discarded to reach a larger multiplier. The detector uses fetched raster lines and programmed display spans; black pixels inside those spans remain picture content. This is automatic framing rather than per-game overscaling presets. Captures retain the configured aperture and centring, as with ordinary autocrop. Monitor bezels and RTG modes suspend autocrop.
deinterlace controls how interlaced (LACE) displays are presented. On
(the default), a motion-adaptive deinterlacer weaves the two fields into a
full-height picture where the content is static and interpolates where it
moves, recovering the full vertical resolution without combing. Off, every
field is simply line-doubled as it arrives, which shows interlace bob and
flicker much as a TV without persistence would. COPPERLINE_DEINTERLACE=0
overrides the config for a single run.
phosphor blends each presented frame with a fraction of the previous
one, approximating the exponential decay of CRT phosphor. Software that
relies on the tube to fuse field-rate flicker -- alternate-field dither
transparency or flicker-dithered animation -- reads as intended with values
around 0.3-0.5, at the cost of a slight motion trail. It is off by
default so screenshots and frame dumps stay frame-exact.
COPPERLINE_PHOSPHOR=0.4 overrides the config for a single run.
shader runs a GPU shader pass over the window’s picture, for the tube
look a phosphor trail on its own cannot give. Three presets are built in:
"scanlines"-- the line structure a 15 kHz set leaves between beam passes: a raised-cosine gap at the pitch of the emulated lines, with the brightness the gaps cost compensated back so the picture dims only slightly rather than by half."mask"-- a shadow mask. The picture is modulated through staggered RGB phosphor triads keyed to physical window pixels, so the mask keeps its size whatever the Amiga resolution behind it, again brightness-compensated."crt"-- all of it, modelled on the 1084 the Amiga shipped with: a bowed tube face, scanlines, an aperture grille, and a corner vignette, faded in together. The face geometry is taken from the datasheet of the 1084’s picture tube (the Philips M34EAQ10X): the bow reproduces its published screen-edge arcs -- the top and bottom edges bow about twice as far as the sides, as on the real screen -- and the corners are rounded at the scale of its 11.6 mm corner arcs. The bow shapes only the face outline, not the picture: a real monitor’s deflection is corrected so the raster stays rectilinear on the curved glass, and straight content stays straight here too. The picture overscans the face as the real raster overscans the glass, filling it to the edges, with the bowed outline deepening the crop toward the rounded corners. A faint glass glow (the room light a real tube reflects) lifts on-face black, so the face keeps its silhouette even when the picture is dark.
"none" (the default; "off" is accepted for the same thing) presents the
picture untouched, and any value ending in .wgsl is the path of a shader
of your own -- see Custom WGSL shaders below.
The scanline gaps are drawn at the pitch of the emulated field lines the
window is actually showing: 270 in the default TV-overscan presentation
(214 on an NTSC scan) and 285 in "full", so the line structure follows
the picture rather than the window size. TV overscan with
pixel_aspect = "square" is 285 as well: that canvas is taller than the
TV aperture and pads it with bezel rows, so the same 270 lines are rescaled
to keep their pitch across the whole window. Interlaced content is
deliberately drawn at field-line pitch
over the woven frame, which is what a 15 kHz set fed an interlaced signal
looks like, rather than one gap per woven row.
shader_strength (0.0 to 1.0, default 1.0) is how strongly the effect is
mixed in, so a preset can be dialled back without editing shaders. At 0.0
the shader arithmetic is an exact no-op, but the pass still resamples the
picture through a plain bilinear sampler, which is a shade softer than the
texel-snapped pass-through the window otherwise uses at magnification.
"none" skips the pass altogether and is the only truly zero-cost setting.
The menu’s Video Settings -> CRT Shader and Shader Strength items change
the preset and the mix live for the rest of the session without touching the
config file; the launcher’s A/V & Emu tab (Video category) has
CRT shader and Shader strength rows that do write it.
COPPERLINE_SHADER=crt|scanlines|mask|none|PATH.wgsl and
COPPERLINE_SHADER_STRENGTH=0.0..1.0 override the config for a single run.
There is no command-line flag.
The pass is presentation and nothing else. Screenshots
(--screenshot-after), frame dumps (--dump-frames), video recordings, the
control protocol’s capture methods and the web
frontend all read the CPU presentation buffer, which the shader never
touches, so captures stay comparable whatever is selected here. Individual
frames also skip the pass in three cases: while a menu or overlay panel is
open (a phosphor mask and a curved face make overlay text unreadable), for
frames coming from an RTG board’s scanout (see [rtg] below), and for
programmable multisync scan modes -- a 31 kHz scanout has no 15 kHz line
structure to reproduce.
bezel frames the window’s picture with a monitor front, drawn at the
window’s resolution so it stays sharp at any size, with the picture keeping
its aspect inside the opening. Three settings:
"off"(the default) -- no frame; the picture fills the display."1084"-- the monitor the Amiga shipped with: a pale cabinet with a darker moulding sunk around the tube, the model badge, the Copperline name where the 1084 wore its maker’s, and a red power lamp."classic"-- the plainer rounded frame Copperline drew before the 1084 arrived.
The older true and false values, from when there was only one frame to
turn on, are still read: true means "1084" and false means "off".
A drawn bezel also changes what its tube shows: the glass presents the whole captured raster -- every rendered line, with all the overscan border the framebuffer captures -- rather than the tighter TV aperture the plain window crops to. The raster fills the glass edge to edge the way a real set’s overscanned picture does, border colour and all, and the opening’s rounded corners crop into that border instead of into the picture, which the extra border keeps clear of the arcs.
The bezel is independent of shader and composes with any preset: with
"crt" the bowed tube face sits inside the opening for the full monitor
look. Cmd+M (macOS) / Alt+M turns it off and back on to whichever front is
chosen, for the rest of the session and without touching the config;
Video Settings -> Monitor Bezel picks the front, the launcher’s Monitor
bezel row (A/V & Emu, Video) writes it, and COPPERLINE_BEZEL (a
style name, or the old 1/0) overrides the config for a single run.
Like the shader pass, a bezel is presentation and nothing else: captures never include it, and it is skipped while a menu or overlay panel is open and for RTG scanout frames. Unlike the shader it does stay on for programmable multisync scans -- a frame has no line structure to get wrong.
bezel_stickers names a folder of PNG images to draw onto the bezel as
die-cut stickers, the way owners dress a real monitor with community and
maker logos. Unset (the default) draws none. Every *.png in the folder
becomes one decal (up to 16, alpha respected, large sources scaled down),
laid along the cabinet’s top band in file-name order with a slight
alternating tilt; each picks up a soft drop shadow and the plastic’s
lighting so it reads as stuck on rather than pasted over. The folder is
re-read when a machine starts, so editing it and restarting (or switching
machine in the launcher) picks up changes; a folder that fails to load
reports on the on-screen display and falls back to bare plastic.
An optional stickers.toml in the folder chooses and places the images
instead, one [[sticker]] table per decal, drawn in written order:
[[sticker]]
image = "retro32.png" # file name in the folder
x = 0.32 # sticker centre, fraction of the front's width
y = 0.935 # and of its height (down from the top)
width = 0.11 # width as a fraction of the front's width
rotate = -2.5 # degrees clockwise
opacity = 1.0
[[sticker]]
image = "badge.png" # no x/y: an auto slot on the top bandx and y come as a pair; a table without them takes the next automatic
top-band slot (where width and rotate still override that slot’s own).
Height always follows the image’s aspect. The same keys, as JSON, drive
the web player’s #bezel-stickers page hook
(the browser chapter), so one sheet lays out identically in
the app and on a hosting page.
Stickers ride the bezel: they draw only while a front does, follow it
through Cmd+M / Alt+M, and are presentation only -- captures never include
them. COPPERLINE_BEZEL_STICKERS (a folder path, or empty for none)
overrides the config for a single run.
perf_overlay (default false) shows the performance overlay at start: a
live readout of emulated fps, speed factor, per-frame emulation cost, host
utilisation, audio health, and pacer slips in the top-right corner of the
display, one line per data point (see
the window chapter for what each line means).
Cmd+P (macOS) / Alt+P toggles it live for the rest of the session without
touching the config, --perf-overlay shows it for one run, and
COPPERLINE_PERF_OVERLAY=1|0 overrides the config for a single run; the
launcher’s Perf overlay row (A/V & Emu, Display) writes it. Like the
transient message overlay it is presentation only: screenshots, frame
dumps, and recordings never include it.
tint recolours the picture like the phosphor of a monochrome monitor:
"bw" (black and white), "green" and "amber" (the two classic
monochrome phosphors), or "sepia"; "none" (the default; "off" is
accepted) presents full colour. These are the five looks the web frontend’s
Screen selector offers, produced by the same colour chain, so a tint
chosen in the browser matches the desktop. It composes with shader --
green phosphor under the crt preset makes a convincing monochrome tube.
Like the shader, the tint is presentation only: screenshots, frame dumps,
recordings and headless runs stay untinted, the status bar and overlay
menus keep their colours, and RTG board scanout (the monitor on the
board’s own output, not the Amiga’s video output) is never tinted. The
menu’s Screen Tint item changes the tint live for the rest of the
session without touching the config file; the launcher’s A/V & Emu tab
(Video category) has a Screen tint row that does write it.
COPPERLINE_TINT=bw|green|... overrides the config for a single run.
menu_scale draws the pop-up menu at "1x" (the default) or "2x" -- the
whole menu, rows and text together. It is a start-up preference:
Video Settings -> Menu Size changes it live without altering the saved
value, --menu-scale sets it on the command line, and the launcher’s A/V &
Emu page (Display category) has a Menu size picker for the same.
window_scale multiplies the normal window width and height in logical host
pixels: 2 opens at twice the normal size on both standard and HiDPI displays.
It accepts 0.5 through 4.0, including fractional values such as 1.5, and
defaults to 1. The window keeps this multiple when its canvas changes, such
as when hiding the status bar, until manually resized. The host may limit
the requested size to the available desktop area. Screenshots and headless
captures keep their usual resolution.
maximized opens a normal, decorated window filling the available desktop
area, keeping the title bar and desktop taskbar visible where the platform
provides them. --maximized selects this mode from a build pipeline:
copperline --run build/hello --window-scale 2
copperline --run build/hello --maximizedfull_screen opens the window fullscreen at start (borderless), and
status_bar chooses whether the status bar starts visible. Both are start-up
preferences; the runtime toggles -- Cmd+F / Alt+F for fullscreen and
Cmd+Shift+F / Alt+Shift+F for the status bar, plus their menu items --
still flip either live without changing the saved value. On the command line
--full-screen / --windowed set the fullscreen state and --show-status-bar /
--hide-status-bar set the status bar; the launcher’s A/V & Emu page (Display
category) has Start fullscreen and Status bar toggles for the same. Left
unset they keep the defaults: windowed, status bar shown. --windowed also
clears maximization; the last of --full-screen, --maximized, and
--windowed wins. In a config that enables both full_screen and
maximized, fullscreen takes precedence. Canvas changes leave a maximized
window at its desktop size; restoring it resumes the normal canvas sizing.
monitor chooses the host display for the main window. The launcher’s
Host monitor picker is in A/V & Emu, Display. It shows the connected
displays with their names and resolutions; Run applies the choice and Save
retains it. Monitor selection also applies when entering fullscreen later.
The Window position field on that page accepts X, Y; emptying it restores
automatic placement. Coordinates are logical pixels from the chosen display’s
top-left corner, including negative offsets. With monitor = "auto", a
specified position uses the primary display. Without a position, a selected
display centres the window; auto leaves placement to the host. Fullscreen
and maximized windows ignore the coordinates.
copperline --list-monitors
copperline --monitor 2 --full-screen
copperline --monitor "External display" --maximized
copperline --monitor 2 --window-position 100 80The default, "auto", leaves placement to the host and enters fullscreen on
the window’s current monitor. "primary" selects the primary display when
the host reports one. Numbers start at 1 and refer to --list-monitors;
their ordering can change when displays are connected or disconnected.
Names match exactly. "name:primary" or "name:2" selects a display literally
named primary or 2. The launcher saves unique names with this prefix,
using numbers for unnamed displays or duplicate names. An unavailable
display, ambiguous name or unreported primary display logs a warning and
falls back to automatic placement without changing the saved preference.
Windows, macOS and X11 can place normal and maximized windows on the selected
display. On Wayland, the compositor controls ordinary window placement;
Copperline can request fullscreen on a selected display, subject to the
compositor’s policy. Wayland does not report a primary monitor, so use a
name or number there. Window positions are also compositor controlled on
Wayland. Headless captures ignore monitor selection and position, and
--list-monitors loads no ROM or machine configuration.
Saved auto-launch configurations use these window preferences too. Changing
window_scale by running a configuration from Debug applies the new size when
returning to Play, leaving the inspector workspace at its current size.
Rendering completed frames uses a worker thread by default so emulation can
advance while the previous frame is painted. The worker is an implementation
detail of presentation: screenshots, frame dumps, and recordings wait for
the exact frame they save. COPPERLINE_THREADED_RENDER=0 forces the old
synchronous render path for comparison. Presenting the composed frame --
the texture upload, the GPU passes and the wait for the display’s vsync --
runs on a second worker, so the main thread’s redraw ends at hand-off;
COPPERLINE_THREADED_PRESENT=0 presents from the main thread instead.
Custom WGSL shaders¶
Pointing shader at a .wgsl file loads a fragment shader of your own into
the same pass. The quickest start is to copy one of the presets --
src/video/window/shaders/scanlines.wgsl, mask.wgsl or crt.wgsl in the
source tree -- and edit its fs_main. The block between the
--- begin shared contract --- and --- end shared contract --- markers in
those files is the contract, and is byte-identical in all three.
A shader must declare exactly these bindings and both entry points:
struct CrtUniforms {
// Display sub-rect of src_tex in UV space: xy origin, zw size.
src_rect: vec4<f32>,
// xy: viewport size in physical pixels. zw: source display texels.
size: vec4<f32>,
// x: strength, 0 (no-op) to 1 (full). y: scanline count across the
// display height. zw: preset-internal, do not rely on them.
params: vec4<f32>,
// Preset-internal and reserved; zero for a custom shader.
params2: vec4<f32>,
};
@group(0) @binding(0) var src_tex: texture_2d<f32>;
@group(0) @binding(1) var src_samp: sampler;
@group(0) @binding(2) var<uniform> u: CrtUniforms;
struct VOut {
@builtin(position) pos: vec4<f32>,
@location(0) uv: vec2<f32>,
};
@vertex
fn vs_main(@builtin(vertex_index) idx: u32) -> VOut {
// Fullscreen triangle; the viewport restricts it to the display rect.
let tc = vec2<f32>(f32((idx << 1u) & 2u), f32(idx & 2u));
var out: VOut;
out.uv = tc;
out.pos = vec4<f32>(tc * vec2<f32>(2.0, -2.0) + vec2<f32>(-1.0, 1.0), 0.0, 1.0);
return out;
}
// Sample the display region only, clamped half a texel inside src_rect so
// a linear tap on the bottom edge never blends in the status bar's first
// row underneath it.
fn sample_display(uv: vec2<f32>) -> vec4<f32> {
let half_texel = 0.5 * u.src_rect.zw / max(u.size.zw, vec2<f32>(1.0));
let lo = u.src_rect.xy + half_texel;
let hi = u.src_rect.xy + u.src_rect.zw - half_texel;
let tc = clamp(u.src_rect.xy + uv * u.src_rect.zw, lo, hi);
return textureSample(src_tex, src_samp, tc);
}
@fragment
fn fs_main(in: VOut) -> @location(0) vec4<f32> {
let uv = clamp(in.uv, vec2<f32>(0.0), vec2<f32>(1.0));
let base = sample_display(uv);
let strength = clamp(u.params.x, 0.0, 1.0);
// Your own look goes here. This one is a green monochrome monitor with
// a gap between beam passes at the emulated line pitch.
let lines = max(u.params.y, 1.0);
let profile = 0.5 - 0.5 * cos(6.283185307 * uv.y * lines);
let luma = dot(base.rgb, vec3<f32>(0.299, 0.587, 0.114));
let tube = vec3<f32>(0.2, 1.0, 0.35) * luma * (0.6 + 0.4 * profile);
// Mixing back toward the untouched sample keeps strength 0 a no-op.
return vec4<f32>(mix(base.rgb, tube, strength), 1.0);
}Notes:
All three bindings are fragment-visibility only.
vs_maincannot read them, so all the work happens infs_main.Sampling goes through
src_rect. The pass draws over the display rectangle of a texture that also carries the status bar below it, and the half-texel inset is what keeps the status bar’s separator hairline out of the bottom of a magnified picture.Of the uniforms, a custom shader can count on
src_rect,size,params.x(strength) andparams.y(scanline count).params.z,params.wandparams2carry the built-in presets’ own look parameters, are zero for a custom shader, and are reserved for future use.Making strength
0.0a visual no-op is a convention, not something the loader enforces, but the Shader strength control andCOPPERLINE_SHADER_STRENGTHare only useful if you honour it.
The file is read and checked when the window is created, when the launcher starts a machine, and every time Video Settings -> CRT Shader -> Custom is chosen, which re-reads it from disk. To reload a shader live, leave the emulator running, edit the file, then pick Custom again.
The check parses and fully validates the file and looks for the two entry
points, all before any GPU pipeline is built, so a mistake is reported as
WGSL with its line and column rather than as a driver error. Files over
1 MiB are refused unread: a shader that big is almost certainly a mistyped
path.
Whatever goes wrong -- a missing file, a syntax error, a missing fs_main --
the full diagnostic goes to the log, a one-line summary appears in the
window’s on-screen message, and the shader falls back to off. A bad custom
shader never fails the config, and never stops the machine from running.
[audio]¶
[audio]
floppy_sounds = true # synthesized drive sounds (not sampled)
floppy_sounds_volume = 100 # 0-100, relative to Paula's output
# output_device = "..." # host output device (substring); omit = system default
# output_enabled = true # false = no sound (GUI "Disabled"); --audio/--noaudio still win
channel_mode = "stereo" # "stereo" (default) or "mono"
stereo_separation = 100 # 0-100; 100 = hardware panning, 0 = mono
audio_filter = "auto" # Paula filter: "auto" (guest-driven), "on", or "off"
# stem_granularity = "master,source" # default --audio-stems-mode; see belowThe drive sounds are generated from scratch: motor hum with spin-up/down over a rumble that repeats with each platter revolution, and head-step clacks (an isolated step -- the empty-drive poll, or the track-to-track advance while loading -- lands with its rebound clatter, and fast multi-track seeks blur into the characteristic buzz). Reading adds no noise of its own; the loading sound is the step rhythm over the spinning motor, as on the real mechanism. The synthesis targets were measured from recordings of real Amiga drive mechanisms, but no sample data is used. Only step pulses that actually fire the stepper are audible: like a real 3.5" mechanism, an outward pulse with the head at track 0 is gated by the /TRK0 sensor, so NoClick-style patches silence the empty-drive poll just as they do on real hardware.
output_device picks the host output by a case-insensitive substring of the
names --list-audio-devices prints (--audio-device overrides it); an omitted
or unmatched name uses the system default. channel_mode = "mono" averages the
left and right output into both channels, and stereo_separation narrows the
Amiga’s hardware left/right panning between full (100) and mono (0) -- so it is
ignored when channel_mode is mono. output_enabled = false runs with no sound
at all (the launcher and runtime-menu “Disabled” option); the --audio and
--noaudio CLI flags still override it. These are host-output settings that do
not change the emulated audio and are not stored in save states. The matching
CLI flags are --audio-device, --audio-channel-mode, and
--audio-stereo-separation; --list-audio-devices prints the device names.
audio_filter (also accepted as filter) controls Paula’s analogue
low-pass filter, the one a post-A1000 Amiga switches with the same CIA-A
line that drives the power LED. "auto" (the default) lets the guest engage
or bypass it as the software asks, matching real hardware; "on" and
"off" force it either way as a listener override. Unlike the host-output
settings above it is part of the emulated audio path, so it also affects WAV
capture. It can also be set with --audio-filter, the menu’s Audio
Settings -> Audio Filter, and Cmd/Alt+A, which cycles it. The status-bar PWR
LED is lit whenever the machine is powered and follows the guest’s /LED line
itself -- full brightness while engaged, dimmed like an A500 rev 6+ board
while released -- so this override changes what you hear, never the LED.
On Linux with PipeWire/PulseAudio, individual sinks are not ALSA devices, so
only the default/pipewire route is offered; pick the output in the desktop
sound settings (or route Copperline in pavucontrol) and it follows. macOS and
Windows select each device directly.
stem_granularity sets the default granularity list for --audio-stems
(headless-only; see Headless and scripted execution), so --audio-stems-mode need not be
repeated on every invocation; the CLI flag still wins when both are given.
It has no effect without --audio-stems DIR on the command line.
[recording]¶
[recording]
clip_seconds = 10 # emulated seconds kept for Save Clip as GIF; 0 = off
clip_fps = 0 # GIF frame rate; 0 = 25 on PAL, 30 on NTSC
native_screenshot_button = false # camera button and Cmd/Alt+S save native pixelsThe window keeps a rolling ring of the last clip_seconds (up to 120) of
the presented picture for Save Clip as GIF;
0 switches the ring and the menu item off. clip_fps (1 to 60) is the
rate every GIF clip is written at, the interactive one and headless
--gif-after captures alike; the default 0 picks half the field rate,
25 fps on PAL and 30 on NTSC. Clips land in the [paths] recordings
folder.
native_screenshot_button = true makes the window’s camera button and
Cmd+S / Alt+S save the native playfield pixels. The same setting is
available in Video Settings → Native Screenshot Button. Its default
is false, which saves the presented picture. It does not change scheduled
screenshots, screenshot expectations, clips, or recordings; use
--native-screenshots for native scheduled captures.
[input]¶
[input]
port1 = "mouse" # mouse | gamepad-mouse | joystick | cd32 | analogue | lightpen | none
port2 = "joystick" # same values except gamepad-mouse; default "cd32" on the CD32 profile
# port3 = "joystick" # parallel-port four-player adapter sockets:
# port4 = "joystick" # joystick | none (see [parallel] joystick-adapter)
joystick = "gamepad" # "gamepad" (default) or "keyboard"
mouse_sensitivity = 50 # host mouse speed 0-100 (50 default = 1:1)
mouse_capture = "click" # when to grab the mouse: click | auto | manual
middle_click_release = false # middle click releases capture instead of reaching the guest
autofire_hz = 0 # pulse a held fire button at this rate; 0 = offPort devices¶
port1 and port2 name the controller device plugged into each game port.
Either port accepts any device, as on real hardware:
mouse-- a quadrature mouse. Its three buttons are the left (/FIRx), right (POTxY), and middle (POTxX) lines.gamepad-mouse(port 1 only) -- the same quadrature mouse, moved by a gamepad as well as by the host mouse: the d-pad and left stick move the pointer and the pad’s two buttons click. While it is chosen the pad drives no joystick port (see Controller ports).joystick-- a digital switch joystick with a fire button and a second button on thePOTxYline.cd32-- a CD32 joypad: a digital joystick plus the serial button protocol lowlevel.library reads (Red/Blue ride the fire/button-2 lines; Green, Yellow, Play, Rewind and Forward exist only serially).analogue-- analogue paddles or a proportional stick presenting resistances on thePOTxX/POTxYpins, with the two paddle buttons on the left/right direction lines. No live host device maps to it yet: drive it with--pot-afterscripting or the control protocol’sinput.analoguemethod (positions default to centre).lightpen(aliaslightgun) -- a light pen or light gun. Its photodetector pulls the port’s pin 6 (/FIRx) low as the beam sweeps past it, and on the board that pin is also Agnus’sLPinput: whileBPLCON0LPENis set,VPOSR/VHPOSRthen read the latched beam position instead of the live counters (ECS/AGABEAMCON0LPENDISholds the latch off). Only one game port is wired toLP: port 1 on the A1000, port 2 on the A500 and every later Amiga -- a pen in the other port has a working switch but its pulses reach nothing, and Copperline logs a warning at start-up. The pen’s tip switch (a gun’s trigger) is the port’s third-button line (POTxX, read throughPOTGOR), since/FIRxis the pulse line itself. In the window the pen follows the host pointer over the display and a left click presses the switch; headless,--pen-after SECS X Yholds it over a screen pixel (in--mouse-to-aftercoordinates) and--joy-after SECS red MS PORTor--click-after SECS left MS PORTworks the switch. The control protocol hasinput.pen.none-- an empty port.
port3 and port4 are the two sockets of the passive parallel-port
four-player adapter (see [parallel]) and take only
joystick or none. Naming a joystick there fits the adapter; an adapter
named in [parallel] fills every socket these keys leave unset.
The defaults are the stock wiring: a mouse in port 1 and a joystick in
port 2 -- a CD32 pad on the CD32 profile, whose bundled controller the
machine expects (an explicit key beats the profile; a real CD32 accepts any
controller too). --port1 / --port2 override for one run, the runtime
menu’s Port 1/2 Device items hot-plug a device live, and the control
protocol’s input.set_port does the same from a script.
With joysticks in both ports, two gamepads, a gamepad and the keyboard, or two keyboard mappings can drive them (see Controller ports).
Joystick input source¶
joystick selects the initial host source for the joystick/CD32-pad port.
There are two explicit modes, so the active source is always visible rather
than depending on whether a pad happens to be connected:
gamepad(the default) -- only a physical pad drives the joystick port. The keyboard is left to the Amiga, so it passes straight through to a Shell, an editor, or Workbench, and no keys are unexpectedly captured as joystick input. With no pad connected there is simply no joystick input.keyboard-- use the keyboard-joystick mapping (cursor keys plus the fire keys), so the port stays usable without a controller.
With one joystick/CD32-pad port the mode picks its source. With two, both
sources are in play -- the gamepad and the cursor-key mapping drive one
port each -- and the mode picks which source gets the lower-numbered port;
whenever no physical pad is present, a second keyboard mapping on the
numeric keypad (8/2/4/6 directions, 0 fire, . second button)
stands in for the gamepad, so two players can share one keyboard.
The keyboard mapping drives whatever device its port carries. In
particular, with mice in both ports the host mouse takes the
lower-numbered one and, in keyboard mode, the cursor-key mapping drives
the second as an emulated mouse: cursor keys move the pointer, the fire
keys are the left button, X the right, D the middle.
This only sets the starting mode. The status-bar toggle (the gamepad /
keyboard icon next to the volume control), Cmd+J / Alt+J, the menu’s
Joystick Input item, and the launcher’s Input tab all flip it live
without changing the config. --joystick MODE overrides this for a single
run. (auto is still accepted here as a backward-compatibility alias for
gamepad; the old auto-detect mode has been removed.)
mouse_sensitivity scales how fast the emulated pointer tracks the host
mouse, 0-100. 50 (the default, shown as Default in the GUI) is 1:1,
0 is a quarter speed and 100 quadruple, on an exponential scale so each
step is an even ratio. It is a host-input
scale applied to live mouse motion only: it never touches the emulated machine
or scripted --mouse-after input, so headless and recorded runs stay
deterministic. Set it from the launcher’s Input tab or
--mouse-sensitivity N, and adjust it live with Cmd+Shift+> / Cmd+Shift+<
(Alt+Shift+> / Alt+Shift+< on Linux and Windows), which ramp while held.
Mouse capture¶
Capturing the mouse confines the host pointer to the window and hides the
host cursor, so the Amiga pointer is the only one on screen. mouse_capture
decides when that grab is taken:
click(the default) -- clicking the display grabs it. That click is a window action and is not passed to the Amiga, so the first click the guest sees is the first one aimed at it.auto-- grab as soon as the window has the focus, and again whenever it regains it, so no host cursor is ever loose over the display. Entering fullscreen grabs too. This suits a mouse-driven game or a fullscreen session where the host desktop is not wanted.manual-- only the shortcut grabs. Clicks on the display go straight to the Amiga and the host cursor is left alone.
Cmd+G / Alt+G releases and re-takes the grab by hand in every mode, and
an explicit release is never undone automatically. Opening a panel or tool
window borrows the cursor and hands the capture back when the last one
closes.
Set middle_click_release = true to release capture with the middle mouse
button. This is off by default, so middle click reaches the guest as usual.
While captured, the release click is consumed and any held guest mouse
buttons are released. An uncaptured middle click keeps its usual behavior.
The launcher’s Input tab has the same Middle to release setting.
Uncaptured, host cursor motion over the display still drives the emulated
mouse in every mode; this setting only decides when the grab is taken, not
whether motion reaches the machine. Set it from the launcher’s Input tab
or --mouse-capture MODE.
Autofire¶
autofire_hz turns a held fire button into a pulse train at that many
presses per second; 0 (the default) leaves the button alone. It applies to
live input only -- the gamepad and the keyboard mapping -- and never to
scripted input (--joy-after, --script, the control protocol’s
input.joy), which must replay exactly the events it was given.
The phase comes from emulated time, so the rate is the same under warp and
on PAL or NTSC. Nothing about the emulated machine changes: the port sees an
ordinary button being pressed and released on /FIRx. Only the fire button
is pulsed; directions and the second button pass through untouched.
--autofire HZ sets it for one run, and the menu’s Autofire item cycles
off / 3 / 5 / 8 / 12 / 16 Hz live. The maximum is 30 Hz -- above that the
assert window is shorter than the frame the guest samples the port on.
Remapping the keyboard controller¶
The keyboard-to-controller bindings are a host preference, not part of the
emulated machine, so they live beside the gamepad calibration rather than in
a machine config: the menu’s Input Mapping... item edits them and Save
writes keymap.toml next to gamepads.toml (see
Gamepad calibration for the per-platform
location). Any control may take several keys -- fire ships with four aliases
so compact keyboards without the right-hand modifiers still work -- and
binding a key removes it from wherever it was before, including from the
other mapping, so the two controllers can never fight over one key. Deleting
the file restores the built-in layouts, as does the panel’s Defaults
button.
[serial] -- serial port and MIDI¶
[serial]
mode = "stdout" # off, stdout, midi, tcp, tcp-connect, pty, modem, or device
# midi_out = "FluidSynth" # midi mode: host destination, "mt32", or "coppersynth"
# midi_in = "Keystation" # midi mode: host source, or "mt32"
# listen = "127.0.0.1:1234" # tcp mode: bind address; modem mode: incoming-call address
# connect = "bbs.example.com:1337" # tcp-connect mode: remote to dial
# device = "/dev/tty.usbserial-1420" # device mode: the host serial port (COM3 on Windows)
# telnet = true # modem mode: start with AT*T1 telnet NVT translation on (default off)
# session = "bbs-demo.session" # modem mode: replay a scripted session instead of TCP
# mt32_control_rom, mt32_pcm_rom, mt32_panel, mt32_lcd # the emulated MT-32 (see below)
# coppersynth_soundfont, coppersynth_mt32_mode, coppersynth_panel # Coppersynth (see below)
# [serial.phonebook] # modem mode: numbers ATD can dial (see below)
# "5551234" = "bbs.example.com:23"The Amiga serial port doubles as the MIDI port. mode selects where
Paula’s serial in/out is connected:
stdout(the default) -- serial output prints to the host terminal, matching the historical behaviour (DiagROM and similar tools log here).off-- serial output is discarded and there is no serial input.midi-- serial in/out is bridged to host MIDI endpoints. Needs a build with themidifeature (the default);midi_out/midi_inname the endpoints by case-insensitive substring (a USB interface or a virtual port).--list-midiprints the host endpoints. Either may instead be"mt32", which is Copperline’s own emulated MT-32 rather than anything on the host, and brings its ownmt32_*keys with it; see the MT-32 chapter.midi_outmay also be"coppersynth"-- Coppersynth, the built-in General MIDI synthesizer, which needs no ROMs at all and brings its owncoppersynth_*keys with it; see the Coppersynth chapter.tcp-- serial in/out is bridged to a host TCP port, like UAE’sTCP:device.listensets the bind address (default127.0.0.1:1234); connect with e.g.nc,socat, or a raw-mode telnet client.tcp-connect-- the outbound counterpart oftcp: at startup the serial port dials the remote named byconnect(required,host:port) and the session talks to that service. Point a guest terminal program at a telnet BBS, atcpsermodem bridge, or any TCP byte service. The connection is made once; if the remote hangs up, output drops like an unplugged cable until the next run. The wire carries raw bytes: for telnet servers that insist on option negotiation, put a telnet-aware relay in between, or pick a BBS/port that accepts raw connections (most do).pty-- serial in/out is bridged to a host pseudo-terminal (Unix only). The slave path (/dev/pts/N) is logged at startup; attach a terminal with e.g.minicom -D,screen, orcu -l.modem-- a Hayes/AT-command modem personality (see the modem chapter for a BBS quick-start and the full command set): the guest dials out withATD host:portrather than the port being wired to a peer at startup, so (unliketcp-connect) there is noconnectaddress in the config; carrier presence drives /CD.listenhere means something different fromtcpmode’s bind address: it is the address the modem answers incoming calls on, and an incoming connection producesRINGon the guest’s serial port rather than being bridged straight through -- the guest (or its S0 register, if auto-answer is configured) has toATAto pick up, as a real modem waits for the terminal program to answer.telnet = trueswitches onAT*T1, the WiModem-style telnet NVT translation layer, at power-on (it is off otherwise, and the guest can still toggle it withAT*T1/AT*T0). Turn it on when the far end is a real telnet server (a BBS) rather than a raw TCP byte service, so IAC option negotiation and CR handling are answered instead of leaking into the data stream.[serial.phonebook]maps a dialable number to ahost:port(or a bare host, which dials the modem’s default port), so anATD<number>from a terminal program’s dialing directory resolves to a real address without the guest ever typing one:[serial.phonebook] "5551234" = "bbs.example.com:23" "5555678" = "bbs2.example.com"The phonebook is config-file-only; there is no launcher row for it.
The command set is the Hayes base plus the WiModem232
AT*extensions most Amiga BBS/terminal guides are written against:AT*B<baud>(stored for CONNECT text only -- pacing always follows the guest’s own SERPER rate),AT*T0/AT*T1(this session’s telnet translation),AT*L<port>/AT*P<port>(listen/default outgoing port),AT*N/AT*NS<n>,<pass>(a stubbed network scan/join, so a guide’s Wi-Fi setup steps do not error out), andAT*REBOOT(resets the state machine, likeATZ).S9(tenths of a second) is the WiModem connect-delay register: CONNECT fires immediately but remote output is withheld forS9of host time first, the pause older terminal software expects before BBS output starts.AT&Wpersists the active settings to a sidecar file beside the machine’s other batteries;ATZreloads it (an explicit[serial] telnetalways wins over what was stored);AT&Fis a hardcoded factory reset that ignores both.session = "path"replays a scripted dial-out session from a file instead of dialing out over TCP. That makes a modem-using headless run or test case deterministic; live network traffic, like MIDI, is otherwise outside Copperline’s replay guarantees. See the modem chapter for the file format;sessioncannot be combined withlisten(the scripted transport plays back outbound calls only, with no inbound side).device-- serial in/out is wired to a real serial port on the host, named bydevice: a USB serial adapter’s path (/dev/tty.usbserial-1420on macOS,/dev/ttyUSB0or/dev/ttyACM0on Linux) or a COM name (COM3) on Windows.--list-serial-portsprints the host’s ports. With a null-modem cable to a real Amiga or PC this is the wiring Amiga Explorer, a term program’s ZMODEM transfer, or a serial link between two machines expects, handshake included: the guest’s/DTRand/RTSdrive the host port’s DTR and RTS pins, and the host port’s CTS, DSR and DCD come back on CIA-B, soserial.device’s 7-wire handshake works against the far end’s real lines (the Amiga has no input for RI; it is logged on the host side when it changes). The host port follows the guest’s line settings: the rateSERPERworks out to is mapped to the nearest standard rate (Paula’s divisor never lands exactly on one, so 19172 bps becomes 19200 and 114416 becomes 115200; a rate more than 3% from any standard rate -- 110, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 31250, 38400, 57600, 76800, 115200, 230400, 460800, 921600 -- is passed through as-is for the driver to accept or refuse), and the stop-bit count follows the words the guest writes (one or two). Host UARTs carry eight data bits: a 9-bit word goes out without its ninth bit, and 7-bit-plus-parity framings, whichserial.devicebuilds in software, cross the wire unchanged. Host bytes still arrive at the emulated rate -- Paula clocks them in atSERPER’s bit period, not instantly. A port that cannot be opened (missing, in use by another program, permission denied -- on Linux, thedialoutoruucpgroup) is a startup error that names the ports the host does have; an adapter pulled mid-run degrades to an unplugged cable (every handshake input high, output dropped) and is reopened, with the guest’s settings, when it comes back. The wire is the host’s clock, not the emulated one: like the network backends, this mode is outside the byte-identical replay guarantees, and a save state does not carry the port (it is reopened, and the restored machine’s DTR/RTS and rate are pushed onto it, on load). Needs a build with thehost-serialfeature (the default).
Every mode also drives the port’s RS-232 handshake inputs, which the guest
reads on CIA-B port A (/DSR, /CTS, /CD) and serial.device reports
through SDCMD_QUERY and honours in 7-wire mode. off (and midi, whose
interface only uses the data lines) is an unplugged cable: every input
floats high, so a guest waiting for CTS or carrier waits forever, as on a
real machine with nothing attached. stdout is a ready device with no
call behind it: DSR and CTS asserted, no carrier. tcp and tcp-connect
behave as a modem: DSR and CTS asserted from startup, and carrier only
while a client is connected (or the dial-out is up) -- so a guest BBS sees
the caller hang up as carrier loss, and a terminal program sees the remote
drop the same way. pty reports a host terminal with the port open (all
three asserted), since a null-modem cable crosses its DTR and RTS to
these inputs and the terminal’s side cannot be observed from here.
device reports the real lines: whatever the far end of the host port’s
cable is driving on CTS, DSR and DCD, sampled continuously. The
guest’s own /DTR and /RTS outputs are the CIA’s pins as written, and
under device they drive the host port’s pins too.
With an AUX: shell on the Amiga side, tcp/pty give a remote AmigaDOS
console. --serial MODE overrides the mode per run.
--serial-connect HOST:PORT sets the dial-out target (implying
mode = "tcp-connect"), --serial-session FILE replays a scripted modem
session (implying mode = "modem"), --serial-device PATH names the host
port (implying mode = "device"), and --midi-out NAME /
--midi-in NAME imply mode = "midi".
The launcher’s I/O Ports tab (Serial Port page) sets all of this
interactively. Device / Mode picks the mode, and each mode brings its
own rows: Connect under tcp-connect for the remote to dial and
Listen under tcp for the local bind address, each a pair of boxes for
host and port; under device (shown as Host port), a Port picker
that steps through the serial ports the host has right now (re-read on
every step, so a just-plugged adapter appears; a saved path the host does
not list is kept and marked). A box left empty shows its greyed default
(host 127.0.0.1 on Listen, port 1234), and an emptied box reverts to it;
an IPv6 literal is typed bare or in brackets, and the saved key spells it
bracketed ([::1]:1337). Emptying both boxes unsets the key. Under modem
the page shows the same Listen box (the incoming-call address above)
plus a Telnet toggle; the phonebook has no row. The in-window
MIDI In / MIDI Out menu items select the MIDI endpoints.
The browser build has its own serial transport (the page bridges the port to a WebSocket); see the browser chapter.
[parallel] -- Centronics parallel port¶
[parallel]
device = "printer" # none | printer | sampler | joystick-adapter
output = "printer.raw" # printer capture path
# device = "sampler"
# sampler_input = "MacBook Air Microphone" # host input; omit for the default
# sampler_gain = 6.0 # preamp gain in dB (0 = unity)
# device = "joystick-adapter" # four-player ports 3 and 4device chooses the peripheral on the parallel port (one at a time). Without
this section the connector is electrically disconnected: CIA-A still produces
its hardware PC strobe on port-B accesses ($BFE101), but no peripheral
acknowledges it and port-B reads see the CIA’s own pins. The equivalent per-run
flags are --parallel DEVICE, --sampler-audio-input NAME, and
--sampler-input-gain X; --sampler-list-audio-inputs prints the input-device
names and exits.
"printer" attaches a raw Centronics sink at output (a bare output with no
device still selects the printer, for compatibility). The file is created at
startup, replacing any existing file; each strobed byte is written verbatim and
returns the printer /ACK falling edge through CIA-A FLAG, including the
normal CIA interrupt delay. The printer also drives the Centronics status
lines on CIA-B port A -- SEL high, BUSY and POUT low -- so the guest’s
parallel.device sees a ready online printer and starts sending. (Without an
attached device those lines float high, and printing waits forever for a
printer to appear, as on a real machine.) The byte stream is intentionally
not decoded, since the guest may emit any printer language; pass it to a
converter or spooler afterwards.
"sampler" attaches an 8-bit audio sampler (digitizer) on the data lines -- the
emulated equivalent of a classic parallel-port sampler cartridge, driving
software such as AudioMaster, ProTracker, OctaMED, and TurboSound. It captures
from a host input device (cpal, like live audio output, so it needs a build with
the frontend feature) and presents each read of the data lines as an 8-bit
offset-binary sample in emulated time, mono (host left/right are summed).
sampler_input names the host device (case-insensitive substring, as
--sampler-list-audio-inputs prints; omitted uses the system default);
sampler_gain is the preamp gain in decibels (0 dB = unity) applied before the
ADC, within the sampler’s range of -24 to +24 dB (a value outside it is a
configuration error). The input device and gain can also be changed live
from the runtime menu, and the gain with Cmd/Alt+Shift +/-. On macOS the CLI
binary needs microphone permission to capture a real input; routing audio in
through a loopback device such as BlackHole needs none.
"joystick-adapter" (also accepted as "multitap") is the classic passive
four-player adapter (Kick Off 2, Sensible Soccer, Dyna Blaster, Gauntlet II,
Super Skidmarks and others read it): two switch joysticks wired straight to
the connector, with no active parts. Port 3’s directions short the data pins
D0-D3 (CIA-A port B bits 0-3, up/down/left/right) to ground and port 4’s
short D4-D7; port 3’s fire shorts the Centronics SEL line (CIA-B
PA2) and port 4’s fire shorts BUSY (CIA-B PA0), all active low, with a
second button on either stick on the spare POUT line (PA1). The
assignment matches WinUAE’s parallel joystick model. The switches only pull
down pins the guest has programmed as inputs (the games clear DDRB), so a
port a printer driver is driving as outputs is left alone.
[input] port3/port4 (or --port3/--port4) say which sockets hold a
joystick; naming a joystick there fits the adapter by itself, and an adapter
named here fills every socket left unset. Up to four host gamepads and the
two keyboard mappings reach the sockets through the usual routing (see
Controller ports), and --joy-after ... 3 /
--joy-after ... 4, --script, and the control protocol’s input.joy
drive them directly.
[floppy] and [floppy.df0] .. [floppy.df3]¶
[floppy]
drives = 2 # DF0 and DF1 connected; 0 disconnects every drive
speed = 100 # 100/200/400/800 percent, or 0 for turbo
[floppy.df0]
path = "demo.adf" # single image, or:
# paths = ["disk1.adf", "disk2.adf"] # swap playlist (shortcut cycles)
write_protected = true # default true
# enabled = true # implied by path/pathsdrives controls how many mechanisms are wired, from zero to four. DF0 is
the internal drive on models that have one; DF1-DF3 are external drives that
answer the standard Amiga external-drive ID protocol when connected. The CDTV
and CD32 profiles default to zero, matching the physical machines; every other
profile defaults to DF0 only. With drives unset, a configured disk image
also connects its drive, so existing configs that name [floppy.df0] ..
[floppy.df3] keep working; with drives set, media in a drive it leaves
disconnected is an error.
speed accelerates the emulated drives beyond the authentic data rate.
100 (the default) is real speed. 200, 400, and 800 clock the whole
data path -- platter rotation, the MFM read shifter, sync detection,
DSKBYTR, and DMA pacing -- at that multiple, so everything software can
observe stays bit-identical to real speed, only compressed in time. 0
selects turbo: a started disk DMA transfer completes almost instantly
(deferred by two scanlines, matching other emulators’ turbo modes, so
loaders that clear stale interrupt flags right after starting a transfer
still see the completion). Drive mechanics are never accelerated: motor
spin-up, head stepping, and post-seek settle always run at real time.
Faster-than-real speeds are a compatibility trade-off, as in other
emulators: the operating system and most loaders tolerate them, but
software that times its own loading against the beam, CIA timers, or music
playback can break. The menu’s Emulation Settings -> Floppy Speed changes
the setting live without restarting the machine. It applies to image-backed
bays only; a physical drive has its own replay_speed.
Supported image formats: standard 901120-byte DD ADF, gzip-compressed
images (ADZ), single file ZIP archives, DMS archives, UAE extended ADF, and
read-only IPF and SCP images. DMS, gzip, IPF, and SCP images are decoded at
load time and always treated as write-protected; set write_protected = false
on a plain ADF to allow write-through updates to the image file. A DMS archive
is unpacked cylinder by cylinder into the DD image the disk was read from, so
archives that repeat a cylinder (an advertising boot block ahead of the real
one) or omit the cylinders that read back blank load normally.
The emulated drives are double-density units, so high-density images are rejected: a 1,802,240-byte HD ADF, a high-density DMS archive, an IPF recorded at the HD cell rate, and a UAE extended ADF whose AmigaDOS tracks hold more than the 11 sectors a DD track carries.
IPF (the SPS/CAPS preservation format) is decoded by Copperline itself rather
than through the closed-source capsimg library, so every build reads IPF on
every platform with nothing to install. Because an IPF preserves the encoded
track -- sync marks, gaps, and sector headers, not just sector contents -- it
carries the custom trackloaders and copy protections that an ADF cannot
express. Each track is decoded to the revolution of MFM the head would pass
over and read back through the same path as a flux capture. The decoder has
two limits. Tracks recorded with a variable cell rate (the Copylock,
Speedlock, and Brierley density models) are decoded with uniform 2 us cells,
which is logged at load time, so a protection that measures cell timing sees
the wrong answer. Weak (“flakey”) bits are replayed as the single
deterministic revolution the file stores rather than varying per revolution.
The browser frontend shares this decoder, so it reads IPF too.
A paths playlist lets multi-disk software that only uses DF0: run
without a second drive. The first entry is the boot disk, and the disk-swap
shortcut (Cmd+D on macOS, Alt+D on Linux/Windows) or the status-bar
swap button cycles to the next image, wrapping around. If a bay has both
path and paths, path is the first entry and paths follows it.
A real drive on a bay¶
A bay can be given a physical 3.5" drive instead of an image, over a Greaseweazle:
[floppy.df0]
bridge = "greaseweazle" # or "off"
write_protected = true # emulator-level protection, on top of the tab
# bridge_port = "/dev/ttyACM0" # omit to auto-detect the interface
# bridge_cable = "a" # a/b (IBM PC) or 0..3 (Shugart)
# bridge_density = "auto" # auto/dd/hd
# bridge_mode = "normal" # normal/compatible/stalling
# replay_speed = "fast" # or "normal"; fast is the defaultA bay takes either a bridge or an image, never both: the disk in the drive
is its media, and naming a path alongside is an error. bridge = "off"
returns the bay to images and keeps the other bridge settings for later.
replay_speed also accepts its older name, bridge_speed, and the
percentages 100 (normal) and 200 (fast).
Nothing needs installing -- the pure-Rust FluxBridge library is built into
Copperline -- but a physical drive changes how the machine runs in several
ways: writes need both the disk’s tab and write_protected = false, the
status bar’s eject and swap do nothing for that bay, and a machine with a
physical drive is paced to wall-clock time and is not reproducible.
Physical floppy drives (FluxBridge) covers the whole feature: what each option does, which mode
suits which disks, and what to expect of it.
[ide] -- IDE hard disks¶
[machine]
profile = "A600" # IDE needs a machine with an IDE port
# (A600 or A1200 Gayle, or the A4000)
[ide]
master = "AmigaSYS.hdf" # raw flat HDF, read/write
# slave = "scratch.hdz" # gzip-compressed hardfile, writes not keptImages are opened read/write. Both kinds of HDF work directly:
a full disk image with its own Rigid Disk Block (RDSK/PART chain), and
a bare partition hardfile (boot block starts with
DOS\x..), which is wrapped in a synthesized RDB on the fly: one extra cylinder of 16-surface x 32-sector geometry holding an RDSK and a bootableDH0PART block, with the image’s own dostype. The image must be a multiple of 256 KiB so the partition is an exact cylinder count. Writes to the partition go back to the image file; writes to the synthesized RDB area (re-partitioning) live only for the session.
A gzip-compressed hardfile attaches too -- the .hdz convention, or a
plainly gzipped .hdf. Like the floppy formats it is recognised by content
and not by name, so the extension is free. Deflate cannot be read a sector
at a time, so the image is unpacked into memory when the drive opens: the
guest sees an ordinary writable disk of either kind above, but the writes
live only in that unpacked copy -- nothing goes back to the compressed
file, and changes are lost at exit. An image that unpacks to more than
1 GiB is refused rather than held in host RAM; decompress that one to a
plain hardfile and attach it instead.
A CHD hard-disk image attaches too: MAME’s compressed CHD container as
chdman createhd -i disk.hdf -o disk.chd writes it (v5, LZMA/Deflate-
compressed hunks, GDDD geometry metadata; chdman ships in MAME’s tools,
brew install rom-tools on macOS). It is recognised by content like the
gzip form, and both kinds of hardfile convert -- an RDB image stays an RDB
image, a bare partition hardfile still gets its synthesized RDB. Sectors
are decompressed on demand, so nothing is held in memory and the image can
be as large as any HDF. The CHD itself is never written: the guest’s
writes go to a copy-on-write overlay beside the image, disk.chd.wov,
created the first time the disk attaches and read back every time after,
so changes persist across sessions while the CHD stays pristine (delete
the .wov to return to the pristine image; chdman extracthd gives back
the original HDF, without the overlay’s writes). A converted disk can be
slightly larger than its hardfile, since chdman pads the last cylinder of
its own geometry with zero sectors. Where the overlay cannot be created
(a read-only directory, say) the disk attaches write-protected with a
warning, which the guest sees as a write-protected volume: a cleanly
unmounted volume still boots, but anything that writes (icon positions,
WHDLoad saves, a volume needing validation) gets the write-protect error.
A delta CHD (one made against a parent image) is refused; flatten it with
chdman first.
A VHD attaches too: the Microsoft Virtual Hard Disk container WinUAE
creates hardfiles in (and Windows Disk Management, Virtual PC, VirtualBox
and qemu-img write). It is recognised by content, like the forms above,
and both kinds of hardfile work inside it. A fixed VHD is a raw image
with a 512-byte footer on the end; the footer is not part of the disk the
guest sees. A dynamic VHD only holds the parts of the disk that have
been written: the rest reads as zeros, and the file grows a block (2 MiB,
usually) at a time as the guest writes somewhere new. Either way the
guest’s writes go back into the .vhd, as they do for an HDF, so the
image stays usable in WinUAE. A differencing VHD (one holding only the
changes to a parent image) is refused; merge it into its parent first.
A path may also name a host directory: its tree is built into an
in-memory volume at startup (volume name = directory name, files and
subdirectories included; entries whose names cannot exist on an Amiga
volume are skipped with a warning). The guest sees an ordinary bootable
disk and may write to it, but the volume lives only in memory -- nothing
is written back to the host directory, and changes are lost at exit. The
stock A1200/A600 Kickstart scsi.device only probes the IDE master; a
slave drive needs a guest OS or driver that supports two units (Kickstart
3.1.4, for example).
To override the volume name (instead of inheriting the directory name),
the boot priority, or the filesystem, give the drive as a table with
path plus name, bootpri, and/or filesystem:
[ide]
master = { path = "/host/Games", name = "Games" }
slave = { path = "wb.hdf", bootpri = 6 }
# slave = "scratch.hdf" # the bare-string form still worksThe name sets the volume label of a directory mount; AmigaDOS volume
names hold up to 30 characters and cannot contain : or /. It has no
effect on a raw HDF, which carries its own label inside the image.
filesystem ("ffs", the default, or "ofs") picks the filesystem a
directory mount’s in-memory volume is built with; it only applies to a
directory (an HDF/gzip image already carries its own filesystem inside
it, and Copperline refuses the key on one at config-parse time). FFS is
the default because it is the more modern, more capacious choice, but
Kickstart 1.3’s ROM has no FFS handler built in -- real 1.3 hardware needs
L:FastFileSystem loaded from disk, or an RDB FileSystemHeader chain,
neither of which Copperline can bundle (it is Commodore-copyrighted). OFS
(DOS\x00) is built into every Kickstart ROM from 1.2 onward, so a
directory mount meant to work under 1.3 with no guest-side setup should
set filesystem = "ofs":
[ide]
master = { path = "/host/Games", filesystem = "ofs" }bootpri (-128..127, default 0) is the de_BootPri written into the
synthesized RDB’s partition, which is what the ROM’s strap ranks boot
candidates by. Kickstart enters DF0: at priority 5, so the default 0 loses
the tie to a bootable floppy; raise it to 6 to boot the hard disk ahead of
one, or lower it to sort two hardfiles against each other. The sentinel
-128 also clears the partition’s PBFB_BOOTABLE flag, so the volume
mounts but is never offered for boot. It has no effect on an image that
carries its own RDB -- those priorities live inside the image, where
HDToolBox put them -- and Copperline logs a warning if you set it on one.
The configuration screen edits bootpri on the Storage tab’s Boot
Priority sub-page, one row per drive (see The window, status bar, and menus): a Priority number
and a Bootable box, where a cleared box is the -128 sentinel. A drive left at 0 with
no cascade default writes no bootpri key.
The drive responds to ATA IDENTIFY with the Gayle byte order real hardware
uses, so both Kickstart 3.1 variants boot from it. An HDD activity LED
appears in the status bar on IDE machines. On the A4000 profile the same
[ide] section attaches drives to the motherboard IDE interface at
$DD2020 (no Gayle involved; Kickstart’s scsi.device drives it the same
way).
A path ending in .cue, .iso, .nrg, or a .chd holding a CD (chdman’s
createcd; one holding a hard disk, createhd, attaches as the hard disk
described above -- the CHD’s own metadata decides, not its name) attaches an
ATAPI CD-ROM drive at that slot instead of a hard disk, through the
PACKET (0xA0) command -- the same read-only SCSI-2 command engine [scsi]
CD-ROM units use (see below), reached over the ATA task file instead of a
WD33C93 SCSI bus. It mounts and swaps discs, plays CD audio, and answers
scsi.device filesystems the same way a [scsi] CD-ROM unit does.
[pcmcia] -- the A600/A1200 credit-card slot¶
[machine]
profile = "A1200" # only the Gayle machines have the slot
[pcmcia]
card = "cf" # "none" (default), "cf", or "sram"
path = "card.hdf" # cf: the card's image; sram: optional backing file
# size = "2M" # sram only: 64K..4M
# read_only = false # sram only: the card's write-protect switchThe A600 and A1200 carry a PCMCIA slot wired through Gayle. Copperline fits one of two card types:
card = "cf"-- a CompactFlash card, which is a PCMCIA ATA device.pathis any image the[ide]drive backend accepts (an RDB HDF, a bare partition hardfile, a.hdz, or a host directory). The card presents the CF register layout: memory-mapped at power-on, then contiguous I/O or PC-style primary/secondary I/O once a driver writes the card’s Configuration Option Register -- the layouts the Aminet CF drivers (compactflash.device,cfd.device, and thescsi.devicePCMCIA patches) use. Kickstart’scard.resourcesees the card but does not mount it: install one of those drivers. To put a real card from a reader in the slot instead, use[[host_disk]]withattach = "pcmcia"(see Physical host disks); the slot holds one card, so the two cannot be combined.card = "sram"-- a static-RAM memory card ofsizebytes (a multiple of 64K up to 1M, or of 128K up to 4M, so its CIS can state the size). With apath, the file seeds the card and is written back as the guest changes it (about once a second, on eject, and at exit); without one the card starts blank and lives for the session (and inside save states).read_only = truesets the card’s write-protect switch. The card’s CIS is what Kickstart 2.05/3.xcard.resourcereads: a card present at boot is added to the system as credit-card RAM, and one inserted later can be used throughcarddisk.device(CC0:).
Command-line equivalents: --pcmcia-cf PATH and --pcmcia-sram SIZE.
Interaction with Zorro II fast RAM. The slot’s common-memory window
is $600000-$9FFFFF, inside Zorro II expansion space, and [memory] fast autoconfigures upward from $200000. With more than 4M of fast RAM
the RAM board covers the window and, as on a real A1200 with an 8M Zorro
II expansion, Gayle’s PCMCIA decode gives way: the slot is disabled, reads
as empty, and Copperline warns at start-up that the configured card will
not be seen. Keep fast = "4M" or less to use the slot (accelerator,
motherboard, and Zorro III RAM do not overlap it). The two can never
claim the same addresses: autoconfigured RAM always answers first.
At run time the window’s PCMCIA Card menu (offered only on a machine
with the slot) inserts a CF image or ejects the card, and the control
protocol’s pcmcia.insert / pcmcia.eject do the same headlessly; both
raise Gayle’s card-detect interrupt as a physical insertion does.
[scsi] -- SCSI controllers¶
[scsi]
# controller = "a2091" # a2091 (default), a4091, or a3000
# rom = "a2091-v7.0.rom" # optional replacement; A2091/A4091 default to bundled open ROMs
# rom_odd = "a2091-odd.rom" # a2091 only: split even/odd EPROM dumps
unit0 = "workbench.hdf" # SCSI IDs 0-6
unit1 = "data.hdf"
unit2 = "game.cue" # .cue/.iso/.nrg/.chd attaches a CD-ROM drive
# unit3..unit6 = ...The [scsi] section attaches a SCSI host adapter with up to seven
drives. controller picks which one:
"a2091"(the default on machines without onboard SCSI): a Commodore A2091 (Commodore DMAC + WD33C93A) as a Zorro II autoconfig board. It works on any machine model (the board needs no Gayle) and has no dependence on the Kickstart IDE driver -- the board’s own boot ROM carriesscsi.deviceand autoboots on Kickstart 1.3 and newer. Omitromto use Copperline’s bundled clean-room open ROM, which uses PIO for control commands and 24-bit DMAC transfers with safe bounce buffers for inaccessible or unaligned memory. Bounce buffers prefer DMA-capable 24-bit Fast RAM, including controller RAM, and fall back to Chip RAM. Bounced disk reads use two buffers to overlap DMA with copying; scarce memory automatically selects smaller or single-buffer transfers. This also sidesteps the stock A600/A1200scsi.deviceonly probing the IDE master.[ide]remains available, and both can be used at once."a4091": a Commodore A4091 (NCR 53C710 SCSI-2) as a Zorro III autoconfig board, for machines with a 32-bit CPU.romis a raw A4091 EPROM image (single ROM, sorom_odddoes not apply); omit it to use the bundled open-source ROM from the A4091 software project. The ROM only autoboots under AmigaOS -- Linux and NetBSD drive the board without it."a3000"(the default on theA3000profile): the A3000’s motherboard SCSI -- the Super DMAC at$DD0000driving a WD33C93. It is silicon, not a card, so it needs no boot ROM: Kickstart’s ownscsi.devicedrives it and autoboots from an RDB drive. It is only valid on a machine with the Super DMAC (the A3000).
For the A2091, omit rom to use the bundled copperline-a2091.rom (see
Where bundled files are found), or point it at another
16K/32K/64K A590/A2091 boot ROM image. Dumps split into even/odd EPROM
halves can be given as rom (even, U13) plus rom_odd (odd, U12). The ROM
is required on the
Zorro boards because the autoboot DiagArea and the scsi.device driver
itself live in it; the autoconfig identity comes from the board (the
A2091 is Commodore product 3, with its DiagArea vector at $2000). The
replacement ROM sources and EPROM-pair build outputs live in a2091-rom/.
Each unitN accepts everything [ide] paths do: RDB images, bare
partition hardfiles (a synthesized RDB advertises a bootable DHn
partition, named after the SCSI ID), gzip-compressed hardfiles (.hdz),
CHD hard-disk images (.chd, writes kept in the .chd.wov overlay),
fixed and dynamic VHDs (.vhd), and host directories built into in-memory
FFS/OFS volumes -- including the
{ path = "...", name = "...", bootpri = N, filesystem = "..." } table
form that overrides a directory mount’s volume name, filesystem, and the
synthesized partition’s boot priority. The HDD activity LED covers SCSI
traffic too.
A unitN path ending in .cue, .iso, .nrg, or a .chd holding a CD
(a hard-disk CHD attaches as a hard disk; the metadata decides) attaches a
SCSI CD-ROM drive at that ID instead of a hard disk: a read-only removable
SCSI-2 target (INQUIRY device type 5) serving 2048-byte blocks, with the
full READ TOC / READ CD / mode-page surface CD filesystems expect.
Cue sheets, NRG, and CHD images may mix data and audio tracks (a cue sheet’s
audio tracks may be WAVE or MP3 files, as described under [cd]
below); a bare .iso is a single data track. The drive answers on the host
adapter’s scsi.device like any other unit, so mount it the way you would
on real hardware: a
DOSDrivers mount entry (or MountList) pointing CDFileSystem --
CacheCDFS, AsimCDFS, and AmiCDROM work the same way -- at the controller’s
scsi.device and the drive’s unit number.
CD audio plays: the PLAY AUDIO command group streams the disc’s audio
tracks into the machine’s audio output at 75 sectors per second of
emulated time (as if the drive’s analogue output were cabled to the
machine), the sub-channel reports the live playback position, and the
debugger’s Audio tab shows the stream on its CD-DA row with the play
state, track, and position. Discs swap at runtime like CDTV/CD32 media:
the status bar’s CD load/eject buttons, dropping a .cue/.iso/.nrg/.chd
on the window, the scheduled --insert-cd-after SECS PATH flag, or the
control protocol’s media.cd.insert all eject the current disc, run the
tray for a second of emulated time, and mount the new one with a
medium-change unit attention for the guest’s filesystem to notice.
[copperhf] -- Copperline’s virtual hardfile controller¶
[copperhf]
unit0 = "workbench.hdf" # units 0-6
unit1 = "data.hdf"
# unit2..unit6 = ...[copperhf] fits copperhf.device, Copperline’s own virtual hardfile
controller: an emulator-only board with no real-hardware counterpart, the
Copperline equivalent of WinUAE’s uaehf.device. It exists to move data at
zero emulated cost. Rather than modelling a real chip’s register timing and
DMA behaviour as [ide], [scsi], and [lide] do, it has a purpose-built
register protocol, and there is no host adapter to choose.
It takes up to seven units (0-6), each in the same bare-path/table drive
form as [ide]/[scsi]/[lide]: RDB images, bare partition hardfiles (the
synthesized RDB names the bootable partition DH0..DH6 after the unit
number), gzip-compressed hardfiles (.hdz), CHD hard-disk images (.chd,
writes kept in the .chd.wov overlay), VHDs (.vhd), and host directories built into
in-memory FFS/OFS volumes, including the
{ path = "...", name = "...", bootpri = N, filesystem = "..." } table form
described under [ide]. The images go through the same shared hardfile
layer as the other three controllers, so a unit here behaves like the
equivalent [scsi] unit in everything but the register protocol. A
write-protected image (a CHD whose overlay could not be created) reports as
such through TD_PROTSTATUS, and its writes fail with TDERR_WriteProt.
Hard disks only: unlike [ide]/[scsi]/[lide], a unitN path ending
in .cue, .iso, .nrg, or a .chd holding a CD is rejected at
config-parse time rather than attaching a CD-ROM drive -- copperhf.device
has no ATAPI/SCSI-CDROM command set behind it. Attach CD images to [scsi]
or [ide]/[lide] instead. A .chd holding a hard disk is fine.
The guest sees copperhf.device with unit numbers 0-6, matching the
unitN keys. The board carries a boot ROM (a DiagArea plus a working
copperhf.device exec-device stub and partition mounter). A program that
knows the device’s name can OpenDevice("copperhf.device", unit, ...) and
drive it directly, and at boot the mounter walks each unit’s RDSK/PART
chain (synthesizing one from a bare partition hardfile, as the other
controllers do) and mounts and offers its bootable partitions like any
other controller’s. See copperhf.device: register protocol and boot ROM for the board, the
register protocol, and the boot ROM’s current behaviour in full.
[lide] -- a lide.device-compatible Zorro II IDE board¶
[lide]
# board = "ripple" # ripple (default), ride, or atbus2008
# rom = "lide.rom" # omit for the bundled default; "" for hardware-only mode
# rom_bank2 = "cdfs.rom" # omit for the bundled default (ripple/ride only)
drive0 = "workbench.hdf" # channel 0 master
drive1 = "data.hdf" # channel 0 slave
# drive2, drive3 # channel 1 master/slave (ripple only)[lide] fits a Zorro II IDE board compatible with LIV2’s open-source
lide.device, giving autobooting IDE
storage under any Kickstart including 1.3. Unlike [ide], which needs a
Gayle or A4000 IDE port, [lide] works on any machine model, as
[scsi]'s Zorro boards do. A .cue/.iso/.nrg (or CD-holding .chd)
drive entry attaches an ATAPI CD-ROM drive, as it does on [ide].
board picks the AutoConfig identity: "ripple" (the default), LIV2’s
open-hardware Zorro II card with two ATA channels (four drives); "ride",
LIV2’s expansion-port board, which shares RIPPLE’s ROM image and register
layout but has one channel (two drives); or "atbus2008", the AT-Bus 2008
and its clone family (Dicke Olga, the TK accelerator boards’ IDE,
CDTV-RAM-IDE, Zorro-LAN-IDE), one register model covering the whole family,
one channel, no ROM banking. None of the three wire an interrupt line --
lide.device is a purely polling driver.
drive0..drive3 take the same bare-path/table form as [ide]/[scsi]
(RDB images, bare partition hardfiles, .hdz, .chd, .vhd, host directories, and the
{ path = "...", name = "...", bootpri = N, filesystem = "..." } table), one
key per slot in (channel, master/slave) order: drive0 and drive1 are
channel 0’s master and slave, drive2 and drive3 are channel 1’s
("ripple" only -- "ride" and "atbus2008" have one channel). Each is
independent, so any slot can be filled or left empty on its own, the same way
[ide]'s master/slave and [scsi]'s unit0..unit6 are:
[lide]
board = "ripple"
rom = "lide.rom"
drive1 = "ch0-slave.hdf" # channel 0 slave, with no master fittedAn older drives = [...] array is still read, so configs written before the
named keys existed keep working. It is positional, so it cannot leave a slot
empty ahead of a filled one (a channel 0 slave with no master, say), which
is why the named keys replaced it. A named key wins over the same slot in
the array, and saving from the launcher rewrites a config to the named
form.
A fitted board (an explicit board, or a drive image) with no rom
defaults to a Copperline-bundled ROM from
lide.device’s GitHub releases page:
"ripple"/"ride" get the CIDER/RIPPLE/RIDE build (lide.rom),
"atbus2008" gets its own lide-atbus.rom -- the two are different linker
layouts, not interchangeable, so which board is fitted picks which bundled
file resolves. Set rom = "" to opt back out into hardware-only mode:
no DiagArea, no autoboot, but drives still work once a disk-loaded
lide.device finds them -- the same setup lide’s own CI uses to test
in-development driver builds without flashing anything. rom_bank2 (a
second flash bank, such as LIV2’s cdfs.rom CD filesystem) defaults the
same way alongside a fitted rom, except on "atbus2008", which has no ROM
banking and is never offered one; rom_bank2 = "" opts out on its own
without touching rom. Either key can instead name a release download of
your own, which always wins over the bundled default. The bundled files are
found as described under Where bundled files are found.
[sf2000sd] -- the SF2000 accelerator’s Zorro II SD card controller¶
[sf2000sd]
card = "workbench.hdf"
# rom = "sf2000sd.rom" # omit for hardware-only mode (no autoboot)[sf2000sd] fits the SF2000 accelerator’s built-in Zorro II SD card
controller: a 64K I/O window over an SPI-mode SD card that autoconfigures on
the chain like [lide]'s boards. card takes the same bare-path/table
drive form as [copperhf]'s units (RDB images, bare partition hardfiles,
.hdz, host directories, and the
{ path = "...", name = "...", bootpri = N, filesystem = "..." } table) --
hard disks only: unlike [lide], a CD image path is rejected, since the
controller speaks the SD card command set, not ATAPI/SCSI-CDROM.
Unlike [lide]'s rom, there is no Copperline-bundled default: the boot
ROM is the SF2000 firmware author’s own, not Copperline’s to ship. rom
absent (or "") is hardware-only mode -- registers are live from
power-on, no autoboot, but the card still works once a disk-loaded driver
finds it. Point rom at a dump of your own board’s flash to get autoboot.
The full SD-over-SPI command set, multi-block transfers included, is
implemented and has been checked against a real Amiga driver’s source. The
launcher’s Storage page has a card slot and a boot-ROM picker for the
board, like every other controller. [[host_disk]] passthrough of a real
host SD reader is not supported yet. See Peripherals and expansion for
the register protocol and its current limits.
[[host_disk]] -- a real disk of the host’s¶
Give the machine a real disk of this computer’s instead of an image -- a card in a reader, a USB drive, a drive on a SATA port. The medium is used exactly as it is, with its own RDB, partitions, and filesystem.
[[host_disk]]
device = "sdb" # the name `--list-disks` prints
fingerprint = "v1-..." # opaque identity written by the launcher
attach = "ide-master" # ide-master (default), ide-slave, lide0-master,
# lide0-slave, lide1-master, lide1-slave, scsi0..scsi6,
# or pcmcia (a CF card in the A600/A1200 slot)
read_only = true # the default; false explicitly allows writesdevice is the host’s current enumeration name -- sdb on Linux, disk4 on
macOS, PhysicalDrive1 on Windows -- and copperline --list-disks prints it.
Those names can change between boots. A launcher-saved fingerprint is
authoritative: it lets Copperline follow the same hardware to a changed name,
but only when exactly one attached disk matches; a missing or ambiguous
match is refused. Keep this opaque value as written. Older and hand-written entries
without it retain exact device lookup. To record one, select and mount the
disk afresh in the launcher, then save or launch; a plain open-and-save keeps
the field absent. An unambiguous fingerprint is sufficient for read-only use.
Persisted writable use additionally requires a fixed, non-removable disk with
a credible serial/WWN: removable media is considered weak even when a USB
bridge reports its own serial, and must be freshly selected for each writable
session, as must a fixed disk without a credible serial/WWN.
Absent read_only means read-only, including for older configurations that
previously relied on the writable default. Set it to false explicitly to
allow guest writes. The command-line equivalents make the access choice for
the current run: --host-disk DEVICE [ATTACH] is read-write, while
--host-disk-read-only is protected. An unresolved disk leaves that drive
slot empty and the machine starts anyway.
The disk this computer is running from is never offered and never opened, whatever is written here, and no RDB is synthesised over a disk that has none. Opening a real disk asks for permission the first time, and the host’s volumes on it are unmounted while the machine has it. Attach read-only the first time: Physical host disks covers the whole of it.
[[filesys]] -- host directories as live volumes¶
[[filesys]]
path = "/data/amiga/Workbench"
volume = "Workbench" # optional, defaults to the directory name
bootpri = 6 # optional boot priority; default -128 = never boot
[[filesys]]
path = "/data/amiga/downloads"
readonly = true # optional, export the directory write-protectedEach [[filesys]] entry exports a host directory to the guest as an
AmigaDOS volume on its own HOSTFS<n>: device, served live by the
emulator: no disk image is built, and guest reads always see the current
host contents. This differs from giving [ide]/[scsi] a directory
path, which snapshots the tree into an in-memory FFS or OFS volume at
startup (see filesystem above). Up to 8 mounts are allowed; the
launcher’s Host Folder page edits the first four.
The volumes are read-write by default: the guest creates, writes, renames,
and deletes the host’s files directly, and changes land in the directory
as you would expect. Set readonly = true to export a directory
write-protected instead -- the guest sees a read-only disk and every write
fails with the same “disk is write-protected” error a physical
write-protected disk gives, which is worth setting on anything you would
rather the Amiga could not damage. The launcher’s Host Folder sub-page (under
the Storage tab) offers the same choice as its Access row.
Amiga file attributes a host filesystem cannot hold -- protection bits
such as script/pure/archive, file comments, and exact datestamps -- are
kept in UAE-style .uaem sidecar files, read when present and written
back when the guest changes them; the sidecars stay hidden from guest
listings, and the delete-protection bit is honoured. Host filenames are
mapped between UTF-8 and the guest’s Latin-1 (names with no Latin-1
spelling are hidden, since the guest could neither display nor reopen
them). Host symlinks inside the mount are followed, wherever they point:
the guest has no way to create one, so a symlink is treated as the host
user deliberately grafting a directory into the mount, the same trust
model as the UAE family.
volume sets the AmigaDOS volume name (up to 30 characters, no : or
/). bootpri enters the volume in the boot-device vote (-128..127;
the default -128 means mounted but never booted from): hard-disk boot
partitions typically sit at priority 0 and DF0: at 5, so a bootable
Workbench directory with bootpri = 6 boots ahead of both.
Kickstart 1.3 and newer get the full feature set, booting included: a
bootpri above the competition boots the machine from the host
directory as SYS: under 1.3 exactly as under 3.1 (the service speaks
both the V36 boot-node protocol and V34’s own autoboot and handler
startup conventions). Kickstart 1.2 and older lack the expansion-ROM
hook entirely and never see the mounts.
[clipboard] -- host clipboard sharing¶
[clipboard]
share = true # default: false -- sharing is opt-inText copied in the guest -- anything an application posts to
clipboard.device unit 0 as an IFF FTXT clip -- lands on the host
clipboard, and text on the host clipboard is available to paste in the
guest, the way WinUAE shares its clipboard. The guest side is a small
resident bridge in the Copperline services board’s ROM (the same board
that serves [[filesys]] mounts), so it needs no guest configuration and
no software installed: under Kickstart 2.0 and later it hooks
clipboard.device for change notifications; under Kickstart 1.3, whose
clipboard.device has no hooks, it re-reads the clip every two seconds and
pushes it only when its ID is new. The device is disk-based on 1.3 and 3.1
(DEVS:clipboard.device), so the bridge waits for the boot volume to
provide it, backing off and giving up
quietly on a system without one -- a game booting from its own bootblock
never sees any of this. The bridge’s DOS device, HOSTCLIP:, is not a
filesystem and refuses everything but its own startup.
Host text reaches the guest as Latin-1 with LF line ends (characters the
Amiga cannot show become ?, CRLF folds to LF); guest text reaches the
host as UTF-8 with the platform’s line ends. Only text is shared; a
picture on either clipboard is left alone. The host clipboard is read a
few times a second while the emulator window has the focus, and only
when its text changed. Up to 1 MiB of text crosses in either direction.
A host with no clipboard to read -- a Wayland compositor that offers no
data-control protocol, with no X11 display behind it -- is reported once
in the log and then left alone rather than reopened on every poll; the
guest side of the bridge still runs, so clipboard.get/clipboard.set
over the control protocol keep working.
share is off by default, windowed and headless alike, and the bridge
is fitted only where it is asked for: share = true or --clipboard. The
bridge is a unit of the Copperline services board, so sharing puts an
autoconfig board on the Zorro chain that the machine you configured does
not otherwise have. Kickstart binds the board at boot, and every Exec
allocation after it lands somewhere else. That is a different machine, and
some software can tell: a program that points COP1LC at a buffer before
it has written the list there has the Copper read whatever the buffer
holds, and where the buffer sits decides what that is. Lotus Esprit Turbo
Challenge hangs on one layout where the data decodes as a MOVE to
INTENA that clears the master interrupt enable. A host convenience should
not change the guest’s memory map unless asked to.
Up to and including 0.20.0 share defaulted on for windowed sessions
(the launcher’s machines too), which is what made that Lotus hang look
like an emulation bug. It is off everywhere now; turn it back on with
share = true or --clipboard.
Headless, share = true is still safe for determinism: the bridge is then
reachable only through the control protocol’s clipboard.get/clipboard.set
(see Control), never through the host clipboard
itself, since the host clipboard is live host state a replay cannot
reproduce (see Determinism and the host boundary).
That is also how a recording made in a windowed session with sharing on
replays headless on the same machine: pass --clipboard. Netplay peers
never share, on either side of the session and even with an explicit
--clipboard: every peer must build the same machine, and the bridge is
part of the services board’s layout, so a session where one side fitted it
could not agree on a machine at all. Input Settings -> Share Clipboard
toggles the host side at runtime; it is greyed out when the machine was
started without the bridge, which is part of the board’s boot and cannot be
added later.
[whdload] -- direct WHDLoad boot¶
[whdload]
game = "Turrican.lha" # .lha, .zip, or a folder with a .slave
# library = "..." # unpacked games and saves; default:
# # <host data dir>/whdload/save
# kickstarts = "..." # directory scanned for Kickstart images; default:
# # the rom's directory, then <library>/Kickstarts
# args = "ButtonWait" # extra WHDLoad command-line options
# machine_type = "auto" # or "copperline" to boot on this machine
# whd_package = "..." # your own WHDLoad_usr.lha
# skick_package = "..." # your own skick*.lha
# Launcher only.
# enabled = true # false removes the WHDLoad page entirely
# games = "..." # the folder the Library page lists
# library_db = "..." # default: <host data dir>/whdload/support/launcher.db
# library_cache = "..." # default: <host data dir>/whdload/support/cache[whdload] boots straight into a WHDLoad-installed game. The package is
unpacked into the game library (once -- saves the game writes persist
there), a minimal boot volume is synthesized around the real WHDLoad
program, raw Kickstart images from kickstarts are identified by content
and staged into Devs:Kickstarts/, and the machine is derived from the
slave header (an A1200 with 8 MiB fast RAM) unless [machine], rom, or
[memory] say otherwise; machine_type = "copperline" boots on the
machine the rest of the configuration describes instead. --whdload GAME
is the command-line equivalent and overrides game. WHDLoad support covers
the whole feature, including what the support archives are and how
Kickstart identification works.
The developer-oriented counterpart is --run PROG: it stages the same kind
of boot volume around an ordinary Amiga executable and warp-boots into
it. It is command-line only by design (no config section) and is covered in
Direct executable launching (--run).
[cd] -- CDTV and CD32¶
[machine]
profile = "CD32"
[cd]
image = "disc.cue" # cue sheet, bare .iso, Nero .nrg, or CHD .chd
insert_delay = 0.0 # emulated seconds after power-on to insert
# nvram = "cd32-nvram.bin" # CD32 save-game EEPROM backing fileimage takes a cue sheet, a bare .iso (single data track), a Nero
.nrg, or a .chd -- MAME’s compressed CHD CD format (v5, as chdman’s
createcd writes: LZMA/Deflate/FLAC-compressed hunks with data and audio
tracks).
The disc mounts on the machine’s CD controller: Akiko on CD32, the DMAC on
CDTV.
NRG supports both the older NERO/32-bit and newer NER5/64-bit footer
formats, with DAO (CUES/DAOI, CUEX/DAOX) or TAO (ETNF/ETN2)
track layouts. MODE1/2048, MODE1/2352, MODE2/2336, MODE2/2352, and CD-DA
tracks are supported; multi-session discs and images with stored subchannel
bytes are rejected explicitly.
CDXL video discs need no special setting: mount the image on a CD32
profile and launch it as on the console. For a bare ISO, Copperline promotes
each cooked 2048-byte sector to a complete raw Mode 1 frame (including EDC
and P/Q parity) when an Akiko client requests raw sectors. CD32 discs that
use a CDTV trademark boot record for backward-compatible startup are accepted
by the same path.
CD32 FMV-enhanced titles use the same Akiko media path once the module is
fitted: top-level fmv = true fits the bundled open module, and naming
fmv_rom fits another image. Under CD32 Kickstart, raw Video CD movie discs
auto-launch the open cartridge’s track player; use Up/Down, Red, and Blue
to select, play, and stop. The bundled AROS ROM deliberately skips the
cartridge diagnostic and so does not install that player.
A cue sheet’s FILE lines may be BINARY (raw sector images, single- or
multi-file) or, for audio tracks, WAVE and MP3 -- the packaged form a
disc’s audio tracks often come in, one file per track:
FILE "game.bin" BINARY
TRACK 01 MODE1/2352
INDEX 01 00:00:00
FILE "game (Track 02).mp3" MP3
TRACK 02 AUDIO
PREGAP 00:02:00
INDEX 01 00:00:00
FILE "game (Track 03).wav" WAVE
TRACK 03 AUDIO
PREGAP 00:02:00
INDEX 01 00:00:00Audio files are decoded to CD-DA as the drive reads them, not up front,
so a disc with an hour of MP3 audio loads as fast as a BIN/CUE and never
holds its decoded audio in memory. Any WAV PCM layout is accepted
(8/16/24/32-bit or float, mono or stereo, any rate); MP3 covers
MPEG-1/2/2.5 Layer III at the standard bitrates, constant or variable
(free-format streams are not read), with ID3 tags skipped and a LAME tag’s
encoder delay trimmed so the track is sample-exact against the WAV it was
encoded from. Sources not at 44.1 kHz are resampled. PREGAP/POSTGAP
lines add the gap sectors such files cannot hold (they read as silence, and
the TOC points past them), as does an INDEX 00 inside a file. Data tracks
must be in BINARY files; MOTOROLA and AIFF files are not supported.
MP3 decoding is the default-on cd-mp3 Cargo feature; a build without it
loads WAVE tracks and rejects MP3 ones with a message saying so.
insert_delay inserts the disc that many emulated seconds after power-on,
with the proper media-change notification; some CDTV discs only boot when
inserted after the boot screen appears. CD32 NVRAM persists to
cd32-nvram.bin in the [paths] nvram folder unless overridden with
[cd] nvram; a cd32-nvram.bin already in the working directory from an
earlier Copperline keeps being used from there.
[[zorro]] -- expansion boards¶
[[zorro]]
metadata = "boards/megaram.toml"
[[zorro]]
metadata = "boards/myboard.toml"
# config = { mode = "fast" } # WASM plugin boards: setting overridesEach entry adds a Zorro board described by a TOML metadata file, configured
in file order after the built-in [memory] fast/z3 boards. For a WASM
plugin board, the optional config table overrides individual settings
that the plugin’s manifest declares (layered over the manifest’s [config]
defaults; the launcher’s Zorro tab edits the same values). See
Writing Zorro board plugins for the metadata format, the plugin ABI, and how autoconfig
assigns addresses.
[a2065] -- Ethernet¶
[a2065]
net = "nat" # or "bridge", "loopback"; "none" for an isolated NIC
# interface = "en0" # required for "bridge"[a2065] fits a Commodore A2065 Ethernet board (Am7990 LANCE) on the Zorro
chain; --a2065-net BACKEND is the matching per-run flag, and the launcher’s
I/O Ports tab (Networking page) has the same picker. net selects the
host network backend:
"nat"-- userspace NAT: the guest gets outbound IPv4 internet through a virtual gateway with no host privileges or setup, identically on Linux, macOS, and Windows. Configure the guest’s TCP/IP stack with IP10.0.2.15, netmask255.255.255.0, gateway10.0.2.2, DNS10.0.2.3(or let it BOOTP/DHCP). Outbound only, IPv4 only."bridge"-- attaches complete Ethernet frames directly tointerface, so the Amiga is a separate station on the physical LAN and can accept inbound connections from LAN peers. Usecopperline --list-net-interfacesfor exact identifiers, or--a2065-interface NAME(which implies the bridge backend). Configure the guest by DHCP from the real LAN or with an address appropriate to that LAN. The guest can reach peers and the router; communication with the host’s own IP is adapter/OS-dependent and is not guaranteed. Frames keep the Amiga’s source MAC, so Wi-Fi is best-effort: many access points reject a second source MAC behind one wireless station. Copperline reports adapter, driver, and permission failures at startup instead of falling back to NAT."loopback"-- echoes transmitted frames back (self-contained, useful for driver bring-up)."none"-- the NIC is fitted but isolated.
Omit the section entirely for no board. Host networking is inherently non-deterministic: inbound frames arrive on the host’s schedule, not the emulated clock, so a NIC board breaks byte-identical replay and save-state determinism while traffic flows. A save state stores the bridge adapter identifier and must be restored on a host where that adapter can be opened. See Writing Zorro board plugins for board details, platform bridge setup, and the NAT’s limitations.
[toccata] -- AD1848 sound board¶
[toccata]
enabled = true[toccata] fits a MacroSystem Toccata sound board (an AD1848 codec) on the
Zorro chain. Its stock, open-source AHI driver (toccata.audio) works
unmodified, so any AHI-aware guest application gets 16-bit sound with no
Copperline-specific setup. No other options exist yet. Omit the section
(or enabled = false) for no board. The launcher’s I/O Ports tab
(Audio page) has the matching fit/don’t-fit toggle. The board’s output
joins the mixer as the toccata source for
--audio-stems (see Headless and scripted execution); see Writing Zorro board plugins and
Toccata and the AD1848 model for the register model.
[mhi] -- virtual MPEG audio decoder board¶
[mhi]
enabled = true[mhi] fits a virtual MPEG-1/2/2.5 Layer III audio decoder board on the
Zorro chain, serving the Amiga MHI API through the ported
mhi_copperline.library, so MHI-aware guest software (AmigaAMP, for
example) gets hardware-accelerated MP3 decoding. No other options exist
yet. Omit the section (or enabled = false) for no board. It needs a build
with the mhi feature (on by default; off only for the wasm32 build). The
launcher’s I/O Ports tab (Audio page) has the matching fit/don’t-fit
toggle.
The guest library is not auto-installed: copy the committed artifact,
guest/mhi/mhi_copperline.library, into the guest’s LIBS:mhi/ drawer.
AmigaAMP (and other MHI clients) find MHI drivers by scanning LIBS:mhi/
for mhi#?.library, so the file must sit in that drawer under a matching
name; check the player’s own MHISupport/MHI-Driver settings if it does
not pick up the library automatically. The board’s decoded output joins
the mixer as the mhi source for --audio-stems (see Headless and scripted execution).
The board consumes each descriptor’s bitstream at the decoded audio’s own
emulated-time rate, so playback through it is deterministic and
byte-for-byte reproducible, like --audio-wav captures of the rest of
Copperline’s audio path. See Writing Zorro board plugins and The MHI decoder board: mailbox register protocol
for the register model.
[hostsocket] -- bsdsocket.library without a guest TCP/IP stack¶
[hostsocket]
net = "nat" # or "bridge", "loopback", "host"; "none" for a dead wire
# interface = "en0" # required for "bridge"
# dns_server = "10.0.2.3" # only used when resolver = "dns" (see below)
# hostname = "amiga" # gethostname() return value (cosmetic)
# address = "192.168.1.50/24" # interface address; only for "bridge" (see below)
# gateway = "192.168.1.1" # default gateway; only for "bridge" (see below)
# resolver = "dns" # gethostbyname() via dns_server directly instead
# # of the host's own resolver (the default under
# # "nat"/"bridge"/"host"); see below[hostsocket] fits the bundled HostSocket board: bsdsocket.library for the
guest, backed by a TCP/IP stack that runs on the host instead of inside the
emulated CPU. Any bsdsocket-consuming application (an Aminet tool, an MQTT
client, a game with an online mode) opens bsdsocket.library and calls
socket()/connect()/send()/recv() as it would against AmiTCP or
Roadshow, but there is no guest-side stack to install, configure, or boot:
the library autoboots from the board’s ROM on Kickstart 1.3 through 3.x and
on the bundled AROS ROM. --hostsocket-net BACKEND is the matching per-run
flag, and the launcher’s I/O Ports tab (Networking page) has the same
picker.
net takes the same host network backends as the A2065 (see above for the
"nat" and "bridge" details and caveats), with one difference: the guest
never configures an IP address, because the host-side stack owns
addressing. Under "nat" sockets reach the outside world; under
"loopback" the guest can talk to itself (127.0.0.1) with fully
deterministic behaviour, which makes "loopback" the right backend for
reproducible headless tests of socket-using software. gethostbyname()
works under "nat", "bridge", and "host" with no further configuration
(see resolver below); under "loopback" lookups fail, since loopback
never routes through a real resolver.
net = "host" is a different kind of backend. Instead of terminating
TCP/UDP on the board’s own embedded TCP/IP stack and pushing frames through
a NAT, bridge, or loopback backend, each socket operation
(socket/connect/send/recv/bind/listen/accept/sendto/
recvfrom/getsockname/getpeername) goes straight to a real host OS
socket, as Amiberry’s and WinUAE’s bsdsocket emulation does. The guest
shares the host’s network identity instead of getting an address of its
own: outbound connections work with no configuration at all (no interface
address, no gateway, no pcap/TAP privileges), and a
bind()/listen()/accept() server is a plain host port bind. This is
not true bridging -- the Amiga gets no LAN IP -- but it covers the common
reason for wanting bridge mode, with none of its setup. interface,
address, and gateway are rejected under "host", since the board has
no interface of its own to configure. Raw ICMP (ping) is unaffected:
"host" only handles TCP and UDP, and ICMP still goes through the board’s
smoltcp interface, which is fixed to "loopback" under "host".
address and gateway matter only under "bridge". The defaults
(10.0.2.15/24 and 10.0.2.2) are Copperline NAT’s own virtual addresses:
correct under "nat" and "loopback", but meaningless on a real LAN, where
nothing answers ARP for a 10.0.2.2 gateway. Set both to match the LAN
interface is bridged to (address = "192.168.1.50/24",
gateway = "192.168.1.1", say). There is no DHCP client, so pick a static
address as you would for any other statically configured device on that
network. Leave both unset for "nat" and "loopback".
resolver picks how gethostbyname() works, independent of the
addressing above. Unset, it defaults to "host" under "nat", "bridge",
and "host": Copperline resolves the name through the host OS’s resolver
on a background thread -- the mechanism [a2065]'s NAT backend uses for
its own DNS forwarding -- so lookups work with no dns_server to
configure. resolver = "dns" makes the board’s own smoltcp stack send a
real DNS query to dns_server (default 10.0.2.3, the NAT’s DNS
forwarder) over the fitted backend, which is how to target a specific
resolver (an internal DNS server on a bridged LAN, for instance) rather
than whatever the host uses. Under net = "host" that query goes out over
the loopback interface and never gets an answer, so "dns" is of no use
there. An explicit resolver = "host" is rejected under "loopback" and
"none", which get no resolver at all: a real host resolver would defeat
loopback’s byte-identical replay. Reverse lookups (gethostbyaddr())
ignore this setting and always send a PTR query to dns_server.
Do not fit this board and also boot a real guest TCP/IP stack (AmiTCP,
Roadshow, ...) in the same session: both would add a bsdsocket.library,
and which one an application opens is undefined. Use [a2065] for testing
real stacks and SANA-II drivers, and [hostsocket] for running or testing
the applications above them. The determinism note on [a2065] applies
here too: "nat", "bridge", and "host" traffic arrives on the host’s
schedule and breaks byte-identical replay, while "loopback" and "none"
stay deterministic. The board state, open sockets included, rides in save
states, but live TCP/UDP peers do not survive a restore. Under "host"
every socket a save state remembers comes back closed (host OS socket
handles are never part of the snapshot), so the guest sees each connection
as dropped, as a resumed "nat" or "bridge" session already does for its
live traffic.
The board is implemented as a bundled WASM plugin (see Writing Zorro board plugins); its
verification record against the external bsdsocktest conformance suite is
in crates/hostsocket-plugin/docs/bsdsocktest-status.md.
[zz9k] -- ZZ9000 SDK crypto accelerator board¶
[zz9k]
enabled = true
# zorro = 3 # 2 or 3; default: 3 on a 32-bit CPU, else 2
# size = "4M" # Zorro III window, power of two 1M..256M; Z2 is fixed at 4M
# int2 = false # completion interrupt on INT2 instead of the INT6 default
# seed = "..." # reserved deterministic DRBG seed (1-64 hex digits); no current consumer[zz9k] fits a register-compatible subset of the MNT ZZ9000’s “SDK v2” service
platform -- the CORE, MEMORY, and CRYPTO services -- with the crypto
computed host-side: SHA-1/256/384/512, BLAKE2s, HMAC, Poly1305, ChaCha20,
ChaCha20-Poly1305 and AES-GCM AEAD, X25519 and P-256 key exchange, P-256
keygen, and ECDSA/RSA signature verification, all at host speed. The
zz9000-sdk’s unmodified Amiga-side software -- zz9k.library, the
zz9k-info/zz9k-hash/zz9k-aead/... tools, and its accelerated AmiSSL
build -- detects the board as it detects real hardware
(manufacturer 0x6D6E, product 4 or 3), so TLS on an emulated 68k stops
being bottlenecked by guest-side crypto. See The zz9k crypto board: ZZ9000 SDK v2 protocol subset for the
protocol contract and the pinned SDK revision.
The board is pure compute: fitting it keeps the machine fully
deterministic and replay-safe, and it works headless, in save states
(including mid-operation), and on any machine profile. On Zorro II the
window is pinned at 4M -- the only Zorro II size the SDK transport accepts
shared-buffer allocations for -- and the SDK’s Zorro II path polls instead
of using the doorbell, which the board handles transparently. zorro = 3
needs a CPU with a 32-bit address bus (68020/030/040/060, not the
68EC020), like any Zorro III board. The other keys take effect only with
enabled = true; setting them without it is an error.
The guest picks the completion-interrupt line itself via the SDK’s
ENV:ZZ9K_INT2 / ZZ9000.CFG convention; int2 here sets what the
board’s config-key register reports (leave it false for the INT6 default
unless guest-side software asks otherwise).
Only the SDK services above exist: the real ZZ9000’s RTG, USB, and
Ethernet faces are absent (registers read zero, services report
unsupported), so do not install the P96 zz9000.card display driver
against this board -- it is harmless but shows nothing.
[rtg] -- RTG graphics card¶
[rtg]
card = "picasso2"
vram = "2M"card is "picasso2", "picasso2plus", "graffityz2", "graffityz3",
"z3660", or "none"; a machine takes at most one. All of these boards give
the guest high-resolution, high-colour screens through Picasso96.
"picasso2" fits a Village Tronic Picasso II with a CL-GD5426 graphics
controller. "picasso2plus" fits the later CL-GD5428 revision, reports its
distinct autoconfig serial number, and wires vertical blank to INT2. Both are
Zorro II boards, so they work with 68000/68010 and 24-bit 68EC020 machines as
well as 32-bit CPUs. vram selects either real board’s "1M" or "2M"
memory configuration and defaults to "2M"; it is ignored for other cards.
Install the Picasso96 PicassoII.card driver and its monitor file in the
guest. The board starts on native Amiga pass-through and switches the
Copperline display to RTG only while the guest enables a valid Picasso screen.
"graffityz2" and "graffityz3" fit Ateo Concepts’ Graffity, which reuses
the same CL-GD5428 core as Picasso II+ under its own autoconfig identity;
vram selects "1M" or "2M" for either, same as the Picasso II family.
"graffityz2" is a Zorro II board, so it works on any CPU. "graffityz3" is
a Zorro III board and needs a 32-bit address bus (68020/030/040/060), same
restriction as "z3660". Install the Picasso96 Graffity.card driver and
its monitor file in the guest -- it ships in the classic Aminet
Picasso96Install package, so no separate download is needed.
"z3660" is a Zorro III board. It comes fitted by default on the A3000
and A4000 profiles, whose stock CPUs have a 32-bit address bus; other
machines leave the slot empty unless card asks for one. Like Zorro III RAM
and "graffityz3", it needs a 32-bit address bus (68020/030/040/060), and
asking for it with a 24-bit CPU is an error. It needs the open-source
Z3660.card driver installed in the
guest (with its monitor in DEVS:Monitors). With that in place, Z3660 screen
modes appear in ScreenMode, and the window shows the board’s output when a
screen is opened.
The Z3660 board’s stock monitor ships with the DISPLAYCHAIN=NO tooltype, which
models the real hardware’s separate RTG monitor and never hands the display
back to the native screen. On a single-window emulator you usually want
DISPLAYCHAIN=YES, so the one window follows whichever screen is active.
[cartridge] -- freezer cartridge¶
[cartridge]
model = "hrtmon" # "none" (the default) or "hrtmon"
# rom = "/roms/hrtmon.rom" # an HRTMon cartridge image of your own[cartridge] fits an Action Replay-style freezer cartridge: a system
monitor mapped into its own memory space that can interrupt and inspect
running software at any time.
model = "hrtmon"fits the cartridge with the bundled HRTMon 2.39 monitor (hrtmon.rom, found as described under Where bundled files are found), built from upstream source (hrtmon-rom/README.md) under GPL-2.0-or-later."none"(the default) leaves the slot empty.romreplaces the bundled image with an HRTMon-compatible cartridge image of your own, recognised by itsHRT!tag at offset 0 or 4. It applies only withmodel = "hrtmon"; setting it without a model is an error.
--cartridge hrtmon fits the cartridge from the command line, and the
launcher’s System -> Freezer cartridge row sets it.
The cartridge maps a 1 MiB bank at $A10000 and keeps live snapshots of the
write-only custom and CIA registers. A freeze -- the menu’s
Freeze (HRTMon) item, Cmd+Shift+B / Alt+Shift+B, headless
--freeze-after SECS, or the control protocol’s cartridge.freeze method --
asserts a level-7 NMI, which enters the HRTMon monitor at once. Enter x in
the monitor to resume, or help for its commands.
Cartridge memory, register snapshots, and an active monitor session are
preserved across save states and rewind history. Input scripts and
recordings log the button press itself as freeze-after SECS. See
Peripherals and expansion for the architecture.
[debug] -- diagnostics¶
[debug]
log_unmapped = "DD0000-DEFFFF"
validate_chipset = true
detect_smc = truelog_unmapped logs every CPU read and write inside the given range that no
device decodes. Reads report the floating bus value they returned, writes
report the value that went nowhere. The value is a hex START-END range whose
end is included (a leading 0x is allowed), or all for the whole address
space.
This is how you find the registers a guest expects and Copperline does not
implement yet. A missing register is usually invisible: a read floats, a write
is dropped, and the guest misbehaves or hangs with no diagnostic. Pointing
this at the window a driver probes shows the access pattern directly -- an IDE
presence probe, say, appears as a write of $A0 to the device/head register
followed by a long run of status reads that never come back ready.
A booting Kickstart probes enough empty address space that all produces on
the order of a million lines per boot, so prefer a range once you know roughly
where to look.
validate_chipset arms the custom-register access validator: a running
report of software using the chipset in ways the hardware quietly ignores.
It flags writes to registers the fitted Agnus/Denise does not have, bits a
register does not define, writes to read-only registers and reads of
write-only ones, byte or odd-address access to word registers, access
through an address mirror, and DMA pointers aimed past the chip RAM Agnus
can address. It also covers the engines behind those registers, where
misuse hangs rather than glitches: a blit started while the previous one
is still running (there is no register-file interlock, so the running
blit is drained and the replacement starts from whatever pointer state it
left) or with its DMA switched off (BBUSY is set and the blit stays
pending until BLTEN and DMAEN are enabled), disk DMA armed against a
drive that could not serve it at that moment -- no media, or the motor
still off, the class behind the classic loader dead-spins -- and a
keyboard handshake pulse too narrow to count as one while the MCU was
waiting for it, which costs a key and stalls input until the keyboard
resynchronises after 143 ms. Each finding names the PC (or Copper
address) that made the access and the beam position, is deduplicated by
(kind, register, writer) with a repeat count, and is logged the first time
it is seen. It also arms
a per-register last-writer table, which answers “what set BPLCON3, and
from where?” without a bisect. Read both over the control protocol with
chipset.report and custom.writer (see
the control protocol), which can also arm and
disarm the validator live. Off by default; an unarmed machine pays nothing
for it.
detect_smc reports writes that land on memory the CPU has already
executed. Self-modification is legitimate on a 68000 -- decrunchers,
trackers and Copper-list patchers all do it -- but it is also where a
prefetch-related bug hides, since the CPU has already fetched the word
ahead of the one it is executing, and neither a patch applied too late
nor one applied to the wrong address leaves a trace at the moment it
happens. Each report names the written address, the instruction that
wrote it, and the distance between them, calling out a patch close enough
to sit inside the prefetch. An address counts as code once an instruction
there has retired, so an instruction patching its own extension words on
its only execution is not reported; every repeating pattern is caught on
the pass after the first. Read it with smc.report over the control
protocol, which can also arm and disarm the detector live. Off by
default; it costs a 1 MiB execution map while armed.
Rollback netplay¶
Rollback netplay has no configuration keys; it is set up per run.
--netplay-host PATH and --netplay-join CODE start an Internet session,
and --netplay-bind, --netplay-peer, --netplay-player, and
--netplay-session start a direct one. --netplay-players gives the host
three or four controller ports, the last two through the
four-player adapter, and --netplay-delay and
--netplay-rollback set the input delay and prediction limit. The
configuration screen also has a Netplay page; its connection details are
kept for the app session and are not written to machine configuration files.
See Rollback netplay for commands, controls, spectators, and
supported machine configurations.