| name | 86box-vm-operations |
| description | Safely launch, track, observe, debug, and analyze one or more 86Box VMs without touching another operator's processes. |
86Box VM operations
Use this skill whenever a task requires starting, configuring, waiting for,
restarting, debugging, or inspecting an 86Box virtual machine.
Non-negotiable safety rules
- Resolve the exact VM directory before launch.
- Launch with
--vmpath <VM directory> --vmname <display name>.
- Track the PID returned for that launch and the stable harness process name.
- Verify PID identity using the executable and exact
--vmpath before any
signal.
- Never use
killall, broad pkill, broad pgrep, or an unverified PID.
- Assume other people are running independent 86Box processes.
- Prefer clean UI shutdown when NVRAM or configuration changes matter.
- Never overwrite shared ROMs for a one-VM experiment; clone a complete ROM set and select it with
--rompath.
Preflight
-
Resolve a name under the platform VM root. On macOS:
~/Library/Application Support/86Box/Virtual Machines/<NAME>
A direct VM path is also valid.
-
Read <VM>/86box.cfg. Record:
machine;
cpu_use_dynarec;
- optional
gdbstub_port;
- configured media and adapter devices.
-
Choose the binary:
- normal interaction:
/Applications/86Box.app/Contents/MacOS/86Box;
- guest debugging: the local
-DGDBSTUB=ON build.
-
Audit the command (run from the 86Box-tool project root, or install it):
cd ~/src/86Box-tool && uv run ./86boxctl.sh <VM> command
-
If debugging multiple VMs, require a distinct gdbstub_port in every
config and cpu_use_dynarec = 0.
Launch
Standalone controller:
./86boxctl.sh <VM> start
./86boxctl.sh <VM> start --gdb-stub
Patched IBM PS/2 Model 80 Type 2 BIOS:
./86boxctl.sh <VM> command \
--model-80-type-2-rom /path/to/sequential-128k.bin
./86boxctl.sh <VM> start \
--model-80-type-2-rom /path/to/sequential-128k.bin
The VM must use machine = ibmps2_m80. The controller discovers the platform
stock ROM root, clones the complete tree under <VM>/romsets/, splits the
image into the active even/odd lane files, validates them, and launches with
that generated root through --rompath. Use --rom-source when discovery is
ambiguous. Use prepare-roms to build without launching.
For a long harness-mediated GUI session, start the executable through the
harness process manager with:
name = unique stable name such as 86box-automatic-configuration
application = selected 86Box binary
args = [--vmpath, <absolute VM dir>, --vmname, <display name>]
pty = false
persist = true when interaction spans turns
Record the returned PID. Use only that stable process name for subsequent
describe, logs, restart, and stop operations. A restart produces a new
PID; record it.
Do not pass 86box.cfg as a lone positional argument. That selects a config
but does not set 86Box's VM user-files path, so relative media, NVRAM, and local
ROMs can resolve against the wrong directory.
Establish readiness
Process creation alone is not guest readiness.
- Normal build: require the exact process to survive startup, then inspect the
visible VM or a concrete VM artifact.
- GDB build: require that this VM's configured RSP port listens.
- Never interpret another 86Box process or port as evidence for this VM.
Use:
./86boxctl.sh <VM> status
./86boxctl.sh <VM> logs --lines 100
Wait for a user
When the user says to wait until a harness interruption, do exactly that. Do
not advance when the VM exits unless the user requested exit-triggered work.
The harness job waiter does not treat a managed GUI process as a background
job, so create a long finite asynchronous wait sentinel, wait on that job with
an indefinite harness timeout, and cancel it after the user's interruption.
Keep the VM process separately tracked by its stable process name and PID.
Observe
Host lifecycle
./86boxctl.sh <VM> status
./86boxctl.sh <VM> wait
./86boxctl.sh <VM> logs --lines 100
Guest screen, console, and memory
Two independent paths:
-
RSP stub (requires the GDB-stub build; connecting pauses the guest):
RSP_PORT=<port> ./rsp.py probe
RSP_PORT=<port> ./rsp.py video-mode
RSP_PORT=<port> ./rsp.py screen
RSP_PORT=<port> ./rsp.py wait-screen "expected text" --wait-timeout 600
RSP_PORT=<port> ./rsp.py dump b8000 fa0
In text modes 0, 1, 2, 3, and 7, screen selects 40x25 or
80x25, uses B8000h for colour or B0000h for mono, and follows the
active video page. In graphics mode 11h, it reads the packed 640x480
1bpp framebuffer at A0000h, resumes the guest, and asks the harness's
configured @vision model to interpret the generated PNG. omp must be
on PATH; use --vision-question to customize the prompt. Snapshot
commands pause only for inspection and detach before model inference.
-
Memdump server (no GDB stub, no CPU pause): set memdump_port in the
VM config. Poll the text framebuffer with a live delta view:
./screenmon.py --memdump-port 12348
./screenmon.py --memdump-port 12348 --plain
./screenmon.py --memdump-port 12348 --count 30 --interval 0.1
screenmon.py renders only what changed between polls; the mode is chosen
by isatty() unless --full/--plain override it. The default interval
is 0.1 s.
For continuous bidirectional operation:
RSP_PORT=<port> ./guest_console.py
RSP_PORT=<port> ./guest_console.py \
--format jsonl --no-input --duration 10
RSP_PORT=<port> ./guest_console.py \
--send "DIR" --enter --duration 10
RSP_PORT=<port> ./guest_console.py \
--input-mode hardware --key F1 --duration 5
The console samples at 70 Hz, retains the complete character/attribute buffer,
and emits only coalesced changed runs. A TTY gets ANSI cursor updates;
redirected output defaults to JSONL. Input defaults to the validated BIOS
keyboard ring after boot. Hardware mode injects paced set-1 scan bytes through
the emulated 8042 for POST. Keep Ctrl-] for detach so guest Ctrl-C remains
available.
Never infer success merely from an injected key. Observe the resulting screen
delta or explicit guest output.
Persistent VM configuration
After a clean shutdown, inspect only the resolved VM directory:
<VM>/86box.cfg
<VM>/nvr/<machine>.nvr
<VM>/nvr/<machine>_sec.nvr
For this repository:
uv run --project . refdisk-view-config --vm <NAME> --adf-source rf7080a/disk
Adding a card in the 86Box UI does not by itself populate guest MCA NVRAM. The
guest Reference Disk must configure POS state and 86Box must save it cleanly.
ROM verification
machine = ... determines required ROM filenames. Check:
- the exact
--rompath recorded by 86boxctl status, when present;
- the generated set's
.86boxctl-romset.json;
- the exact active filenames from the 86Box machine definition;
- hashes or byte comparisons of those active files.
For this workflow, an explicit --rompath must contain the complete 86Box ROM
set. Although 86Box can fall back to other search roots, completeness makes the
run self-contained and prevents silent mixing. Do not point it at a directory
containing only one machine.
For a raw sequential 128 KiB Model 80 Type 2 image, use
--model-80-type-2-rom. The controller writes:
machines/ibmps2_m80/15f6637.bin = input[0::2] # low/even lane
machines/ibmps2_m80/15f6639.bin = input[1::2] # high/odd lane
Both files are 65,536 bytes; interleaving them must reproduce the input.
start and restart create or validate the isolated clone automatically:
./86boxctl.sh <VM> prepare-roms \
--model-80-type-2-rom /path/to/sequential-128k.bin
./86boxctl.sh <VM> status
Defaults are the standard 86Box ROM roots: macOS Application Support,
XDG_DATA_HOME/XDG_DATA_DIRS on Unix, and Local/Program/AppData on Windows.
Override with --rom-source PATH or 86BOX_ROM_ROOT. --refresh-rom-set
rebuilds only a recognized generated directory.
Backup suffixes such as .orig and .altered are not selected automatically.
Preserve shared originals.
For an end-to-end proof using a real GDB-stub-enabled 86Box process:
./model80_rom_smoke.py <MODEL80_VM>
Require machine = ibmps2_m80, cpu_use_dynarec = 0, and a unique
gdbstub_port. The harness derives a sequential image from the stock lanes,
changes KB OK to OKBMR, repairs the 64 KiB checksum, launches through the
isolated complete ROM clone, observes OKBMR in guest text VRAM, and stops only
its recorded PID. Use --keep-running only when manual inspection is required.
Stop or recover
Prefer a clean UI shutdown, then confirm the tracked process exited. If control
is required:
./86boxctl.sh <VM> stop
./86boxctl.sh <VM> restart
stop sends SIGTERM only to the identity-verified PID and does not silently
force-kill it. Use --kill-after-timeout only after accepting possible NVRAM
or configuration loss.
On a crash:
- capture
status and logs;
- confirm the record belongs to the intended VM;
- restart that record only;
- record the new PID;
- return to the prior wait or observation step.
Completion evidence
Before reporting success, provide the evidence appropriate to the task:
- exact VM path and machine identifier;
- binary and exact launch command;
- stable process name and current PID;
- readiness evidence;
- clean exit or crash status;
- relevant config/NVRAM paths and sizes;
- guest screen, register, memory, or decoded configuration output.
See README.md for implementation details, GDB-stub quirks, ROM lookup, and
failure diagnosis.