Reviewing an M7 branch took six DOSBox-X runs to find three build blockers that each take a second to explain. The runner threw away everything needed to see them. Capture the compiler build's output. Case commands were redirected to RESULTS\<key>.LOG but `call BUILD.BAT` was not, so the step that fails first and blocks every case left only BUILD.FAIL containing the string "FAIL". The twelve wcl invocations inside it were invisible; finding "Unable to open src\emit_c_m7.c" meant hand-editing build-dos.bat to add a redirect and re-running the VM. Record exit codes. The batch collapsed every outcome to `if errorlevel 1`, so a compiler that aborted and one that exited 1 with a diagnostic were the same FAIL. RC.BAT now walks a descending errorlevel ladder into RESULTS\<key>.RC and the host derives pass/fail from it, which immediately separates an ordinary rejection (1) from a trap (255). Note the space in `echo 0 >FILE`: without it DOS parses `0>` as a redirect of handle 0. Stop falling back to CONSOLE.LOG. That is DOSBox-X's own log -- display enumeration and INT15 chatter -- so a crashed command reported fifty lines of emulator noise instead of saying it produced no output. Add tools/tests/test_dos_names.py. An over-long source name reaches the DOS build as `Unable to open "src\..."`, which reads as a missing file rather than a name FAT cannot represent, and only after a VM boot and ten object builds. The check runs on the host in 0.03s and flags emit_c_m7.c (9-character stem) on the branch that prompted this. Also pass -k through to pytest so a single case can be re-run without its whole milestone, and print the resolved ROOT at startup: an editable install plus a git worktree will otherwise silently build a different checkout than the one the shell is in. Verified on master: 155 passed, unchanged. Recorded codes are 0 for success, 1 for rejections, 255 for the three bounds traps. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BScg8CF1sAAM2zVHAu5zvW
Development tools
Host support
The automated DOS development environment currently supports Windows 10/11.
The host only needs uv. The setup command downloads the pinned DOSBox-X and
Open Watcom DOS archives, verifies their SHA-256 hashes, and installs them in the
ignored .dosboxx/ cache.
uv run ferro-dos setup --accept-watcom-license
Review the Open Watcom license referenced by
tools/toolchains/dosboxx.lock.json before accepting it. Neither downloaded
archives nor installed tools are committed.
General DOS environment
ferro-dos provides the development entry points:
uv run ferro-dos build
uv run ferro-dos exec "FEC.EXE --check TESTS\M6\OKLAST.FE"
uv run ferro-dos batch fec\test-dos.bat
uv run ferro-dos shell
uv run ferro-dos --help
Every invocation creates an isolated host directory under .dosboxx/runs/ and
mounts it as writable C:. The repository is mounted read-only as R: and the
pinned Open Watcom installation as read-only W:. Current compiler sources,
the standard library, and fixtures are copied to C:\FEC; all compilation and
execution happen there inside DOSBox-X. Successful runs are removed by default.
Use --keep to retain a workspace and --show-dos to display the DOS window.
This directory-backed layout deliberately has no QEMU, disk-image, TCP-agent, or OCR dependency. A future disk-image backend can be added without changing the command interface.
Pytest regression suite
ferro-test uses the same isolated DOSBox-X/Open Watcom environment, builds
FEC.EXE once, and executes all selected cases sequentially in that one DOS
instance. Pytest still reports each registered case separately.
uv run ferro-test run --through m6 -v
uv run ferro-test run --only m6 --dos-log
uv run ferro-test --help
--keep-failed preserves a failed workspace, --dos-log prints captured DOS
output, --trace-dos disables per-command redirection, and --show-dos displays
the GUI. Working rules and DOS/Open Watcom build traps are in AGENTS.md.