Imported from just-chillin/dos-containers (
AGENTS.md). Install upstream withnpx skills add just-chillin/dos-containers. Copyright stays with the author.
AGENTS.md
Guidance for AI agents working in this repo.
What this is
A wrapper repo around PC-MOS/386 v5.01 (a multiuser DOS clone), vendored as
the git submodule pcmos386v501/ (from git@github.com:roelandjansen/pcmos386v501.git).
All interaction with it — booting, disk setup, driving the guest — goes through
the root Makefile and QEMU.
After a fresh clone:
git submodule update --init
Wiki
Deeper operational knowledge lives in wiki/: PC-MOS's own
command set and INT D4h API, CONFIG.SYS/SMPSIZE internals, the multitasking
model (ADDTASK/SWITCH/MOS MAP), disk-image internals and the mtools
write-lock/writeback-cache traps, building DOS programs on this host (nasm
today, Open Watcom for anything needing real C), the from-scratch install
flow, and the eval-copy timeout. The Gotchas section below is the quick
reference; the wiki has the full "why" and the exact commands.
Makefile targets
| Target | What it does |
|---|---|
hdd |
Creates images/pcmos-hdd.img, a 500MB raw disk image, if it doesn't already exist. Gitignored. Most other targets depend on this. |
run-floppy |
Boots QEMU with the Cocoa GUI display, full screen, floppy pcmos386v501/IMAGES/v501/1_main.img as drive A:, images/pcmos-hdd.img as the hard disk. No -snapshot: guest writes persist directly to both images, including the ones tracked inside the pcmos386v501 submodule — booting from this target will leave the submodule's floppy image showing as modified in git. |
run-curses |
The agent-relevant target. Boots QEMU with -display curses inside a detached tmux session named pcmos, size pinned to 80x25 via window-size manual (matches PC-MOS/386's native text mode, and stays fixed regardless of what terminal later attaches). Also exposes a QEMU monitor on a unix socket. |
run-hdd |
Same as run-curses, but boots the installed hard disk (-boot c) with no floppy attached. Use this once PC-MOS is actually installed on images/pcmos-hdd.img — booting from the floppy re-runs the HDSETUP/INSTALL flow every time. |
tmux-read |
tmux capture-pane -p -t pcmos — dumps the current guest screen as literal text. This is how an agent "sees" the DOS guest. No screenshots, no OCR, no vision model needed — QEMU's curses backend renders the guest's VGA text buffer as real characters, and tmux buffers that as capturable text regardless of what's attached. |
tmux-send CMD="..." |
tmux send-keys -t pcmos "$(CMD)" Enter — types a line into the guest and presses Enter. This is how an agent acts. |
tmux-attach |
Attaches a human to the running session interactively. |
tmux-kill |
Kills the pcmos tmux session. |
swap-main / swap-aux |
Swaps drive A: to 1_main.img / 2_additional.img via the QEMU monitor unix socket (/tmp/qemu-monitor.sock). Only works while a monitor is attached — i.e. while a run-curses instance is up. |
monitor CMD="..." |
Sends an arbitrary QEMU monitor command over the same socket, e.g. make monitor CMD="info block". |
screenshot |
Writes screenshots/screen.png, a pixel-accurate grab of the guest display taken straight from the emulated VGA framebuffer via the monitor's screendump. Bypasses curses/tmux/terminal encoding entirely. Prints the output path. Needs a monitor socket up (run-curses, or run-floppy with -monitor attached). |
clean |
Deletes images/pcmos-hdd.img. |
Agent workflow: driving the guest
Because PC-MOS/386 is text-mode, and QEMU's curses display renders that text
mode as literal terminal characters, and tmux buffers whatever's drawn to its
pane independent of any attached client — an agent can close the loop entirely
through shell commands:
make run-curses— boot it.make tmux-read— read the current screen state as text.- Decide what to do based on that text.
make tmux-send CMD="..."— act (type a command/response, or a function key — see Gotchas below).- Repeat from step 2.
This is the intended pattern for automating things like the HDSETUP /
MSYS install flow: read the prompt text, send the appropriate response,
read the next screen, and so on, without any human in the loop.
Working from a mobile session
When the user is driving this from a phone, make tmux-read text dumps are
hard to read and easy to misjudge — column alignment collapses, and CP437
box-drawing characters may not survive the trip. Take screenshots and attach
them. After any step that changes what's on the guest screen, run
make screenshot and send the resulting screenshots/screen.png to the user
rather than pasting a text dump. It's a real framebuffer grab, so it shows
exactly what the guest is displaying, free of any encoding issues.
Prefer this over tmux-read for reporting to a mobile user. tmux-read is
still the right tool for the agent's own read-decide-act loop.
Gotchas
- Stale QEMU processes hold file locks. Before running any target that
touches
images/pcmos-hdd.imgor either floppy image, runps aux | grep qemu. A leftover process from a previous run will make a new launch fail instantly withFailed to get write lock. If this happens insiderun-curses, tmux's sole pane exits immediately, which kills the whole session — so the symptom you'll actually see ismake tmux-readfailing withno server running. Fix: kill the stale QEMU process, then retrymake run-curses. - Don't rely on
Ctrl+Alt+2/Ctrl+Alt+1to switch to/from the QEMU monitor console inside a real terminal — it's unreliable (terminals don't reliably pass those modifier combos through). Use the monitor unix socket instead:make monitor CMD="...". - CP437 control-range glyphs (
0x00–0x1F) render as garbage, and this is not fixable from the terminal side. Most of CP437 comes through fine — box drawing (╔ ═ ║ ╚), shading (░ ▒ ▓) — because qemu's curses UI converts guest bytes withiconv -f CP437. But CP437's graphical meanings for the0x00–0x1Frange (↑ ↓ ♥ ♦ ¶ §…) are not in the standard CP437 mapping; iconv correctly treats those bytes as C0 control characters. qemu hands the control char to ncurses, which emits garbage — typically a double-width CJK codepoint, which also eats a cell and truncates the rest of the row. Observed: the scrollbar arrows inHDSETUP-style menus come out as鄡(U+9121) and錡(U+9321) instead of↑/↓, and those rows capture as 53 chars instead of 80. NoLANG/LC_ALL/tmux -usetting changes this — the corruption happens inside qemu before tmux ever sees it. Usemake screenshotwhenever glyph fidelity matters, and don't trusttmux-readfor the exact contents of a cell that looks wrong — cross-check it against the framebuffer. - Function keys work directly in
tmux send-keys, e.g.tmux send-keys -t pcmos F3— needed for function-key-driven DOS TUIs likeHDSETUP.