# dbx_control

> **Tool `tools/dos/dbx_control.py`** (program, 16-bit DOS). Drive DOSBox-X's built-in debugger from a script, through its debugger control channel.

Run it with `./gameframe run dbx_control ARGS` or `python3 tools/dos/dbx_control.py`. [Source on GitHub](https://github.com/tanepiper/game-frame/blob/main/tools/dos/dbx_control.py).

## Its own help

This is the help text in the tool itself, as `./gameframe tool dbx_control` prints it.

```text
Drive DOSBox-X's built-in debugger from a script, through its debugger control channel.

DOSBox-X (2026.10 and later, built with the debugger) connects to 127.0.0.1:<mcp_server> when
the [dosbox] setting mcp_server is set, and accepts one debugger command at a time over a
line-based text protocol (README.debugger in DOSBox-X). The agent-facing side of the same
channel is the dosbox-x-mcp-server program (registered in .mcp.json as "dosbox"); this module
is the script-facing side, for repeatable ground-truth runs and tests.

    from dbx_control import Debugger
    with Debugger(port=0) as dbx:                      # listen first, on a free port...
        dbx.launch([os.path.abspath('scripts/dosbox-run.sh'), '--break', '--mcp', str(dbx.port), GAME, 'GAME.EXE'])
        dbx.wait_connected()                            # DOSBox-X connected
        dbx.wait_break()                                # DEBUGBOX stopped at the first instruction
        dbx.exec('BPINT 21 3D')                         # any debugger command; returns its output lines
        dbx.run()                                       # resume
        regs = dbx.wait_break()                         # stopped again: registers (EV), one step taken
        data = dbx.memdump('DS', 0x12DE, 4)             # bytes, through MEMDUMPBIN
    Debugger commands that draw into the debugger window (CPU, D) return no text: use EV for
    registers and MEMDUMPBIN for memory (these helpers do).

Command line: dbx_control.py --port P [--timeout S] COMMAND ...   waits for DOSBox-X to connect,
runs each debugger COMMAND (for example BREAK "EXEC CPU" "EXEC D 0040:0000"), prints the replies.

Addresses are what the running program uses: real-mode segments, or the DPMI host's selectors
for a protected-mode program, which are NOT Ghidra's 1000/1008 numbering. Find a segment's
selector with the debugger's LDT command, or match its bytes (see docs/DOS.md).
Use port=0 (a free port) for scripts and tests: a DOSBox-X left from another run, or another
session's, may still be trying to connect to a fixed port, and the listener would accept it.
close() ends the whole process group that launch() started.
Standard library only. It does not start DOSBox-X itself unless you call launch().
```

## Named by

Skills: [re-setup](/skills/re-setup), [re-observe](/skills/re-observe), [re-emulate](/skills/re-emulate), [re-verify](/skills/re-verify).
