Headless Runs and Testing
Bizmuth runs with no window for testing: a fixed number of frames with the result written to files, a debug server left running for a client to drive, or a file of FantASM tests run on the emulated Next.
bizmuth --machine next --sd next.img --sd-ro --no-rtc --screenshot boot.png --screenshot-frames 400Captures
--screenshot FILE.png runs the machine for --screenshot-frames frames, 600 by default, saves the screen as a PNG and exits. --video FILE.png records every frame of the run as an animated PNG.
| Option | Written at the end of the run |
|---|---|
--screenshot FILE.png | the screen |
--savestate FILE | a compressed save state |
--savestate-raw FILE | an uncompressed save state |
--state-json FILE | the machine state as JSON |
A capture with --loadstate starts from that state in place of booting. Its frame counter continues from the frame the state was saved at.
A script can end a capture early with stop(); everything is then written from that frame. See Rhai Scripting.
Boot regression check
A capture taken with --no-rtc and --sd-ro is the same file every time for the same build and card. --no-rtc takes the clock off NextZXOS’s boot screen, the one part of it that changes with time, and --sd-ro stops the guest changing the card between runs. Two builds are then compared with cmp:
bizmuth --machine next --sd next.img --sd-ro --no-rtc --screenshot a.png --screenshot-frames 400
cmp a.png baseline.pngScripted input
--keys presses keys at given frames. Each entry is FRAME:KEY, and a key is one Spectrum key, a named key, or several joined with +:
bizmuth --machine 48k --screenshot load.png --screenshot-frames 300 \
--keys "100:j 140:sym+p 180:sym+p 220:enter"types LOAD "" and presses ENTER. Keys are the letters, digits, enter, space, caps and sym; down, up, left and right press 6, 7, 5 and 8, the keys NextZXOS’s menus read. Each press is held for a few frames.
⚠ Space repeated presses well apart. The ROM must see a key released before it reads the next press. With the presses above 10 frames apart, the 48K read the second sym+p and the ENTER as nothing; 40 frames apart, it read them all.
--mouse moves the mouse. Each entry is FRAME:DX,DY,BUTTONS or FRAME:DX,DY,BUTTONS,WHEEL:
--mouse "120:+8,0,0 130:0,0,1 134:0,0,0"moves right by 8 at frame 120, holds the left button from frame 130 and releases it at 134. Movement applies to its own frame only. Every entry states the buttons, 1 left, 2 right and 4 middle, and they stay held until the next entry.
An entry that cannot be read is named in a warning and skipped.
--auto-keys [FRAME] presses ENTER once, from frame 160 by default, to answer the Next’s boot prompt. --select presses ENTER once the browser is up, launching the highlighted entry.
Programs in a capture
--load boots NextZXOS, waits for it to go idle and then loads the program, so a capture of a program needs no key script. See Loading Software.
Debug server
--headless runs the machine with the debug servers and no window until the process is stopped, for a client such as a test suite driving ADP, or DeZog, to control:
bizmuth --machine next --sd next.img --headless --debug-protocol adpAssertions in a capture
The assert annotations in a loaded SLD are checked during a capture, and a failed one is reported without stopping the run. The run ends with a count of the claims armed, reached and failed. See Assertions.
FantASM tests
--run-tests FILE runs every test in a FantASM test description file, written by fantasm link --tests, on the emulated Next. It prints a line per test and a summary, and exits with status 1 if any test failed.
$ bizmuth --run-tests game.tests.json --image-org 0x4000
test double doubles ... ok
test wrong ... FAILED
Error [E1091]: assert_reg A: expected 99, got 6
game.asm:7
test result: 1 passed, 1 failed
Error [E1138]: 1 test failedThe image the file names is read from beside it, and refused if its BLAKE3 digest differs from the one recorded. A .nex, .fxi, .sna or .hex image loads at its own addresses, an .fxi through the memory map .nexload leaves; a flat .bin loads at --image-org, which may be written 0x8000, $8000 or 32768. A .bin without --image-org is refused.
Each test runs on a fresh Next with no ROM. The image is loaded, the test’s stack pointer and eight MMU slots are set, interrupts are turned off, its body is loaded at its address, and its init_ commands are applied. The body runs until it executes HALT, and its assert_ commands are then checked against the machine. Every clock advances with the body’s instructions, so a body may enable interrupts and wait for a CTC timer, the copper or a DMA transfer as it would on the hardware.
A test’s machine has no SD card unless one is named. --sd and --sd1 attach a card to every test’s machine; the config file’s cards are not used. With --sd-ro each test writes to a RAM overlay of its own, so no test sees another’s writes and the image is unchanged. Without it, writes reach the image and later tests see them.
$ bizmuth --run-tests kernel.tests.json --image-org 0x8000 --sd card.img --sd-roReady point
A test file written with fantasm link --tests-ready LABEL names a ready point: an address the image runs to from its own entry before any test. For such a file, --run-tests boots NextZXOS from the ROM folder and the card in slot 0, loads the image as --load does, and runs it until the PC reaches the ready point.
The machine at the ready point is saved, and every test starts from it: its body is loaded and its init_ commands applied over the booted image, with the image’s memory map left in place. A test’s slots are not applied. Each test starts from the saved state, so no test sees another’s writes to memory.
The machine at the ready point is kept beside the --load boot cache, keyed by the image’s digest, the ready address, the boot ROM and the card. A later run with all four unchanged starts from it without booting. --no-boot-cache boots every time and keeps nothing.
A ready point needs a card: without --sd the run is refused. A run that does not reach the ready point within the file’s cycle limit fails the file, naming the address and the PC it stopped at:
the image did not reach its ready point $7000 within 2000000 T-states; PC is $4005assert_cycles and a cycle limit count T-states without contention, as FantASM does, so a figure that passes in FantASM passes here. An !assert inside a body fails its test when the claim is false. A quoted !debug logs its text, and one naming a function calls it in the --script file.
A failing assertion names the test’s line, not its own: the test file records no line per command.