Add comprehensive documentation for MCP server
- Update README.md with accurate lokkju fork info and troubleshooting - Add CLAUDE.md development guide for future sessions - Document GDB protocol details, architecture, and common issues
This commit is contained in:
parent
b022f6fb9e
commit
79c646cf87
371
CLAUDE.md
Normal file
371
CLAUDE.md
Normal file
@ -0,0 +1,371 @@
|
|||||||
|
# DOSBox-X MCP Server - Development Guide
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
An MCP server that enables Claude to programmatically debug DOS binaries running in DOSBox-X via the GDB Remote Serial Protocol. The primary use case is reverse engineering the unpublished Bezier algorithm in RIPTERM.EXE.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick Reference
|
||||||
|
|
||||||
|
### Running the System
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start DOSBox-X container (must be running for MCP tools to work)
|
||||||
|
xhost +local:docker && docker compose up -d
|
||||||
|
|
||||||
|
# Verify GDB server is listening
|
||||||
|
nc -zv localhost 1234
|
||||||
|
|
||||||
|
# Check container logs
|
||||||
|
docker compose logs -f
|
||||||
|
|
||||||
|
# Stop container
|
||||||
|
docker compose down
|
||||||
|
```
|
||||||
|
|
||||||
|
### Testing the MCP Server
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run full test suite
|
||||||
|
uv run pytest
|
||||||
|
|
||||||
|
# Run specific test file
|
||||||
|
uv run pytest tests/test_gdb_client.py -v
|
||||||
|
|
||||||
|
# Test MCP tools via CLI (important: --allowedTools required!)
|
||||||
|
claude -p "attach to localhost:1234 and read registers" \
|
||||||
|
--allowedTools "mcp__dosbox-mcp__*"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Common Development Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run server directly (for debugging)
|
||||||
|
uv run dosbox-mcp
|
||||||
|
|
||||||
|
# Lint and format
|
||||||
|
uv run ruff check src/ && uv run ruff format src/
|
||||||
|
|
||||||
|
# Rebuild container after Dockerfile changes
|
||||||
|
docker compose build --no-cache
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
### Component Overview
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ Claude Code │
|
||||||
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
│ MCP (stdio)
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ FastMCP Server │
|
||||||
|
│ server.py - registers tools from tools/ modules │
|
||||||
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
┌───────────────┼───────────────┐
|
||||||
|
▼ ▼ ▼
|
||||||
|
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||||
|
│ state.py │ │ types.py │ │ utils.py │
|
||||||
|
│ Global client │ │ Registers │ │ parse_address │
|
||||||
|
│ and manager │ │ Breakpoint │ │ format helpers │
|
||||||
|
└─────────────────┘ │ MemoryRegion │ └─────────────────┘
|
||||||
|
│ └─────────────────┘
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ gdb_client.py │
|
||||||
|
│ GDB Remote Serial Protocol client │
|
||||||
|
│ - Packet formatting ($cmd#checksum) │
|
||||||
|
│ - Register/memory read/write │
|
||||||
|
│ - Breakpoint management │
|
||||||
|
│ - Execution control (step, continue) │
|
||||||
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
│ TCP :1234
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ DOSBox-X Container (lokkju fork) │
|
||||||
|
│ Built with --enable-remotedebug │
|
||||||
|
│ Ports: 1234 (GDB), 4444 (QMP) │
|
||||||
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key Files
|
||||||
|
|
||||||
|
| File | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `server.py` | FastMCP entry point, registers all tools |
|
||||||
|
| `gdb_client.py` | GDB protocol implementation (~400 lines) |
|
||||||
|
| `state.py` | Singleton instances (manager, client) to avoid circular imports |
|
||||||
|
| `types.py` | Dataclasses: Registers, Breakpoint, MemoryRegion, etc. |
|
||||||
|
| `utils.py` | Address parsing, format conversion |
|
||||||
|
| `tools/execution.py` | launch, attach, continue, step, quit |
|
||||||
|
| `tools/breakpoints.py` | breakpoint_set, list, delete |
|
||||||
|
| `tools/inspection.py` | registers, memory_read/write, disassemble, stack |
|
||||||
|
| `tools/peripheral.py` | screenshot, serial_send (stubs) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## GDB Remote Serial Protocol
|
||||||
|
|
||||||
|
### Packet Format
|
||||||
|
|
||||||
|
```
|
||||||
|
Send: $<command>#<2-digit-hex-checksum>
|
||||||
|
Receive: +$<response>#<checksum>
|
||||||
|
(+ is ACK, - is NAK)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key Commands
|
||||||
|
|
||||||
|
| Command | Description | Example |
|
||||||
|
|---------|-------------|---------|
|
||||||
|
| `qSupported` | Handshake/capabilities | First packet after connect |
|
||||||
|
| `g` | Read all registers | Returns hex blob |
|
||||||
|
| `G<hex>` | Write all registers | |
|
||||||
|
| `m<addr>,<len>` | Read memory | `m7c00,10` |
|
||||||
|
| `M<addr>,<len>:<hex>` | Write memory | |
|
||||||
|
| `Z0,<addr>,1` | Set software breakpoint | `Z0,7c00,1` |
|
||||||
|
| `z0,<addr>,1` | Clear breakpoint | |
|
||||||
|
| `c` | Continue execution | |
|
||||||
|
| `s` | Step one instruction | |
|
||||||
|
| `?` | Query stop reason | Returns `S05` (SIGTRAP) etc. |
|
||||||
|
|
||||||
|
### Register Order (x86)
|
||||||
|
|
||||||
|
GDB returns registers in this order:
|
||||||
|
```
|
||||||
|
EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI, EIP, EFLAGS,
|
||||||
|
CS, SS, DS, ES, FS, GS
|
||||||
|
```
|
||||||
|
|
||||||
|
Each is 32-bit little-endian hex (8 chars), except segments (16-bit, 4 chars).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## DOSBox-X Configuration
|
||||||
|
|
||||||
|
### Critical: Use `[dosbox]` Section
|
||||||
|
|
||||||
|
The lokkju fork's GDB/QMP config goes in `[dosbox]`, NOT `[debug]`:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[dosbox]
|
||||||
|
gdbserver=true
|
||||||
|
gdbserver port=1234
|
||||||
|
qmpserver=true
|
||||||
|
qmpserver port=4444
|
||||||
|
```
|
||||||
|
|
||||||
|
### Why lokkju/dosbox-x-remotedebug?
|
||||||
|
|
||||||
|
Mainline DOSBox-X has no GDB stub. The lokkju fork (branch: `remotedebug`) adds:
|
||||||
|
- `--enable-remotedebug` configure flag
|
||||||
|
- `gdbserver.cpp` and `qmp.cpp` sources
|
||||||
|
- Runtime config options
|
||||||
|
|
||||||
|
Build command in Dockerfile:
|
||||||
|
```bash
|
||||||
|
./configure --enable-remotedebug --enable-debug --enable-sdl2
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Real Mode Addressing
|
||||||
|
|
||||||
|
DOS uses segment:offset addressing (20-bit physical):
|
||||||
|
|
||||||
|
```python
|
||||||
|
physical = (segment << 4) + offset
|
||||||
|
|
||||||
|
# Example: 1234:5678
|
||||||
|
# physical = 0x12340 + 0x5678 = 0x179B8
|
||||||
|
```
|
||||||
|
|
||||||
|
The `utils.py` module handles parsing:
|
||||||
|
- `"1234:5678"` → segment:offset
|
||||||
|
- `"0x12345"` or `"12345"` → flat hex
|
||||||
|
- `"#65536"` → decimal
|
||||||
|
- `"CS:IP"` → register-based (requires current register state)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Adding New Tools
|
||||||
|
|
||||||
|
### 1. Create the Tool Function
|
||||||
|
|
||||||
|
In appropriate `tools/*.py` file:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from ..state import client, manager
|
||||||
|
from ..types import SomeType
|
||||||
|
from ..utils import parse_address
|
||||||
|
|
||||||
|
def my_new_tool(address: str, count: int = 10) -> dict:
|
||||||
|
"""Short description for MCP.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
address: Memory address (segment:offset or flat hex)
|
||||||
|
count: Number of items (default: 10)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Result dictionary with data
|
||||||
|
"""
|
||||||
|
if not client.connected:
|
||||||
|
return {"error": "Not connected. Use attach() first."}
|
||||||
|
|
||||||
|
addr = parse_address(address, client.read_registers())
|
||||||
|
# ... implementation ...
|
||||||
|
return {"address": f"{addr:05x}", "data": result}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Export from `tools/__init__.py`
|
||||||
|
|
||||||
|
```python
|
||||||
|
from .mymodule import my_new_tool
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
# ...existing...
|
||||||
|
"my_new_tool",
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Register in `server.py`
|
||||||
|
|
||||||
|
```python
|
||||||
|
mcp.tool()(tools.my_new_tool)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Add Tests
|
||||||
|
|
||||||
|
In `tests/test_tools.py` or new file:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def test_my_new_tool():
|
||||||
|
# Mock the GDB client
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Unfinished Work
|
||||||
|
|
||||||
|
### Peripheral Tools (Stubs)
|
||||||
|
|
||||||
|
`screenshot` and `serial_send` are placeholders. Implementation ideas:
|
||||||
|
|
||||||
|
**screenshot**: DOSBox-X can capture via:
|
||||||
|
- Built-in screenshot command (key binding)
|
||||||
|
- QMP protocol on port 4444
|
||||||
|
- X11 window capture
|
||||||
|
|
||||||
|
**serial_send**: Configure serial port in dosbox.conf:
|
||||||
|
```ini
|
||||||
|
[serial]
|
||||||
|
serial1=nullmodem server:1234
|
||||||
|
```
|
||||||
|
Then send data via socket.
|
||||||
|
|
||||||
|
### Step Over Implementation
|
||||||
|
|
||||||
|
`step_over` in `execution.py` is basic — it steps but doesn't properly handle CALL instructions. A full implementation would:
|
||||||
|
1. Disassemble current instruction
|
||||||
|
2. If CALL, set temp breakpoint after it
|
||||||
|
3. Continue instead of step
|
||||||
|
4. Remove temp breakpoint
|
||||||
|
|
||||||
|
### Disassembler
|
||||||
|
|
||||||
|
Current disassembler in `inspection.py` is minimal (pattern matching common opcodes). For full disassembly, consider:
|
||||||
|
- Capstone library (`pip install capstone`)
|
||||||
|
- External tool like `ndisasm`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Testing Against Real DOSBox-X
|
||||||
|
|
||||||
|
### Manual GDB Connection
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# From host, after container is running
|
||||||
|
nc localhost 1234
|
||||||
|
|
||||||
|
# Type (no newline):
|
||||||
|
$qSupported#37
|
||||||
|
|
||||||
|
# Should receive capabilities response
|
||||||
|
```
|
||||||
|
|
||||||
|
### Python Quick Test
|
||||||
|
|
||||||
|
```python
|
||||||
|
from dosbox_mcp.gdb_client import GDBClient
|
||||||
|
|
||||||
|
client = GDBClient()
|
||||||
|
client.connect("localhost", 1234)
|
||||||
|
regs = client.read_registers()
|
||||||
|
print(f"CS:IP = {regs.cs:04x}:{regs.ip:04x}")
|
||||||
|
client.disconnect()
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Debugging Tips
|
||||||
|
|
||||||
|
### Enable Verbose Logging
|
||||||
|
|
||||||
|
```python
|
||||||
|
import logging
|
||||||
|
logging.getLogger("dosbox_mcp").setLevel(logging.DEBUG)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check Container Logs
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# GDB connections show up here
|
||||||
|
docker logs dosbox-mcp 2>&1 | grep -i gdb
|
||||||
|
```
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
| Symptom | Cause | Fix |
|
||||||
|
|---------|-------|-----|
|
||||||
|
| "Connection refused" | Container not running | `docker compose up -d` |
|
||||||
|
| "Connection closed" | Wrong DOSBox build | Verify lokkju fork |
|
||||||
|
| Config not applied | Wrong section | Use `[dosbox]` not `[debug]` |
|
||||||
|
| Timeout on continue | No breakpoint hit | Set breakpoint first |
|
||||||
|
| "unhealthy" status | Missing `nc` in image | Cosmetic, ignore or add netcat |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Future Enhancements
|
||||||
|
|
||||||
|
1. **RIPTERM Bezier Tracing**
|
||||||
|
- Set breakpoints at drawing routines
|
||||||
|
- Capture coordinate pairs from registers/stack
|
||||||
|
- Build coordinate trace for algorithm analysis
|
||||||
|
|
||||||
|
2. **QMP Integration**
|
||||||
|
- Port 4444 provides machine control
|
||||||
|
- Could enable screenshots, state snapshots
|
||||||
|
|
||||||
|
3. **Memory Watchpoints**
|
||||||
|
- GDB protocol supports `Z2`/`z2` for write watchpoints
|
||||||
|
- Useful for tracking variable changes
|
||||||
|
|
||||||
|
4. **Scripted Automation**
|
||||||
|
- Create `examples/ripterm_bezier.py` for the target use case
|
||||||
|
- Document coordinate capture methodology
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Related Resources
|
||||||
|
|
||||||
|
- [GDB Remote Protocol Docs](https://sourceware.org/gdb/current/onlinedocs/gdb.html/Remote-Protocol.html)
|
||||||
|
- [lokkju fork PR/issue](https://github.com/joncampbell123/dosbox-x/issues/752) — Original discussion
|
||||||
|
- [FastMCP Docs](https://gofastmcp.com/) — MCP server framework
|
||||||
|
- [RIPscrip Spec](https://wiki.preterhuman.net/RIPscrip_Graphics_Protocol_Specification)
|
||||||
295
README.md
295
README.md
@ -4,14 +4,14 @@ AI-assisted debugging of DOS binaries via the Model Context Protocol (MCP).
|
|||||||
|
|
||||||
This MCP server enables Claude to programmatically debug DOS programs running in DOSBox-X by providing tools for:
|
This MCP server enables Claude to programmatically debug DOS programs running in DOSBox-X by providing tools for:
|
||||||
|
|
||||||
- Setting breakpoints
|
- Setting breakpoints and tracing execution
|
||||||
- Reading/writing registers and memory
|
- Reading/writing CPU registers and memory
|
||||||
- Stepping through code
|
- Disassembling code at any address
|
||||||
- Tracing execution
|
- Step-by-step instruction execution
|
||||||
|
|
||||||
## Primary Use Case
|
## Primary Use Case
|
||||||
|
|
||||||
**Reverse engineering classic DOS programs** - specifically, tracing the unpublished Bezier curve algorithm in RIPTERM.EXE for the RIPscrip graphics protocol research project.
|
**Reverse engineering classic DOS programs** — specifically, tracing the unpublished Bezier curve algorithm in RIPTERM.EXE for the [RIPscrip graphics protocol research project](https://github.com/ryanmalloy/rpmesh).
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
@ -19,168 +19,243 @@ This MCP server enables Claude to programmatically debug DOS programs running in
|
|||||||
|
|
||||||
- Python 3.11+
|
- Python 3.11+
|
||||||
- [uv](https://github.com/astral-sh/uv) package manager
|
- [uv](https://github.com/astral-sh/uv) package manager
|
||||||
- Docker (for DOSBox-X container)
|
- Docker and Docker Compose
|
||||||
- X11 display (for DOSBox GUI)
|
- X11 display (for DOSBox GUI) or use headless mode
|
||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Clone the repository
|
# Clone the repository
|
||||||
git clone https://github.com/yourusername/dosbox-mcp.git
|
git clone https://github.com/ryanmalloy/dosbox-mcp.git
|
||||||
cd dosbox-mcp
|
cd dosbox-mcp
|
||||||
|
|
||||||
# Install dependencies
|
# Install dependencies
|
||||||
uv sync
|
uv sync
|
||||||
|
|
||||||
# Build Docker image
|
# Build Docker image (uses lokkju/dosbox-x-remotedebug fork)
|
||||||
make build
|
docker compose build
|
||||||
|
|
||||||
# Create DOS directory
|
# Create DOS directory for your binaries
|
||||||
make init
|
mkdir -p dos
|
||||||
```
|
```
|
||||||
|
|
||||||
### Running
|
### Running
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Allow X11 access for Docker
|
# Allow X11 access for Docker (Linux)
|
||||||
xhost +local:docker
|
xhost +local:docker
|
||||||
|
|
||||||
# Start DOSBox-X
|
# Start DOSBox-X with GDB server
|
||||||
make up
|
docker compose up -d
|
||||||
|
|
||||||
# Register MCP server with Claude Code
|
# Verify GDB port is listening
|
||||||
make register
|
nc -zv localhost 1234
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
docker compose logs -f
|
||||||
```
|
```
|
||||||
|
|
||||||
### Usage with Claude
|
### Register with Claude Code
|
||||||
|
|
||||||
Once registered, Claude can use these tools:
|
```bash
|
||||||
|
# Add to Claude Code
|
||||||
|
claude mcp add dosbox-mcp -- uv run --directory /path/to/dosbox-mcp dosbox-mcp
|
||||||
|
|
||||||
|
# Verify registration
|
||||||
|
claude mcp list
|
||||||
```
|
```
|
||||||
# Launch DOSBox-X with a binary
|
|
||||||
launch("/path/to/GAME.EXE")
|
|
||||||
|
|
||||||
# Connect to debugger
|
## Usage with Claude
|
||||||
|
|
||||||
|
Once registered, Claude can debug DOS binaries:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Connect to the running DOSBox-X GDB server
|
||||||
attach("localhost", 1234)
|
attach("localhost", 1234)
|
||||||
|
|
||||||
# Set a breakpoint
|
# Read current CPU state
|
||||||
breakpoint_set("1234:0100")
|
|
||||||
|
|
||||||
# Run until breakpoint
|
|
||||||
continue_execution()
|
|
||||||
|
|
||||||
# Read registers
|
|
||||||
registers()
|
registers()
|
||||||
|
|
||||||
# Read memory
|
# Read memory at CS:IP
|
||||||
memory_read("DS:0100", 64)
|
memory_read("CS:IP", 64)
|
||||||
|
|
||||||
# Step through code
|
# Set a breakpoint at the boot sector
|
||||||
|
breakpoint_set("0x7C00")
|
||||||
|
|
||||||
|
# Continue execution until breakpoint
|
||||||
|
continue_execution()
|
||||||
|
|
||||||
|
# Step through instructions
|
||||||
step(10)
|
step(10)
|
||||||
|
|
||||||
# Clean up
|
# Disassemble at current location
|
||||||
|
disassemble("CS:IP", 20)
|
||||||
|
|
||||||
|
# Disconnect when done
|
||||||
quit()
|
quit()
|
||||||
```
|
```
|
||||||
|
|
||||||
## Tools Reference
|
## Tools Reference
|
||||||
|
|
||||||
### Execution Control
|
### Execution Control (6 tools)
|
||||||
|
|
||||||
| Tool | Description |
|
| Tool | Description |
|
||||||
|------|-------------|
|
|------|-------------|
|
||||||
| `launch` | Start DOSBox-X with optional binary |
|
| `launch` | Start DOSBox-X container with optional binary |
|
||||||
| `attach` | Connect to GDB stub |
|
| `attach` | Connect to GDB stub at host:port |
|
||||||
| `continue_execution` | Run until breakpoint |
|
| `continue_execution` | Run until breakpoint or signal |
|
||||||
| `step` | Step N instructions |
|
| `step` | Step N instructions (default: 1) |
|
||||||
| `step_over` | Step over CALL instructions |
|
| `step_over` | Step over CALL instructions |
|
||||||
| `quit` | Stop DOSBox-X |
|
| `quit` | Disconnect and optionally stop DOSBox-X |
|
||||||
|
|
||||||
### Breakpoints
|
### Breakpoints (3 tools)
|
||||||
|
|
||||||
| Tool | Description |
|
| Tool | Description |
|
||||||
|------|-------------|
|
|------|-------------|
|
||||||
| `breakpoint_set` | Set breakpoint at address |
|
| `breakpoint_set` | Set software breakpoint at address |
|
||||||
| `breakpoint_list` | List all breakpoints |
|
| `breakpoint_list` | List all active breakpoints |
|
||||||
| `breakpoint_delete` | Remove breakpoint(s) |
|
| `breakpoint_delete` | Remove breakpoint by ID or all |
|
||||||
|
|
||||||
### Inspection
|
### Inspection (6 tools)
|
||||||
|
|
||||||
| Tool | Description |
|
| Tool | Description |
|
||||||
|------|-------------|
|
|------|-------------|
|
||||||
| `registers` | Read all CPU registers |
|
| `registers` | Read all CPU registers (32-bit, 16-bit, segments, flags) |
|
||||||
| `memory_read` | Read memory region |
|
| `memory_read` | Read memory in hex/ASCII/dump format |
|
||||||
| `memory_write` | Write to memory |
|
| `memory_write` | Write hex or ASCII data to memory |
|
||||||
| `disassemble` | Simple disassembly view |
|
| `disassemble` | Disassemble instructions with opcode hints |
|
||||||
| `stack` | Dump stack contents |
|
| `stack` | Dump stack contents from SS:SP |
|
||||||
| `status` | Get debugger status |
|
| `status` | Get debugger connection status |
|
||||||
|
|
||||||
### Address Formats
|
### Peripheral (2 tools — stubs)
|
||||||
|
|
||||||
|
| Tool | Description |
|
||||||
|
|------|-------------|
|
||||||
|
| `screenshot` | Capture DOSBox-X display (not yet implemented) |
|
||||||
|
| `serial_send` | Send data to serial port (not yet implemented) |
|
||||||
|
|
||||||
|
## Address Formats
|
||||||
|
|
||||||
The server supports multiple address formats:
|
The server supports multiple address formats:
|
||||||
|
|
||||||
- **Segment:offset**: `1234:5678` (standard DOS format)
|
| Format | Example | Description |
|
||||||
- **Flat hex**: `0x12345` or `12345h`
|
|--------|---------|-------------|
|
||||||
- **Decimal**: `#65536`
|
| Segment:offset | `1234:5678` | Standard DOS format |
|
||||||
- **Register-based**: `DS:SI`, `CS:IP`
|
| Flat hex | `0x12345` or `12345` | Physical address |
|
||||||
|
| Decimal | `#65536` | Decimal address |
|
||||||
|
| Register-based | `CS:IP`, `DS:SI` | Uses current register values |
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
```
|
```
|
||||||
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
|
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
|
||||||
│ Claude Code │────▶│ DOSBox-X MCP │────▶│ DOSBox-X │
|
│ Claude Code │────▶│ DOSBox-X MCP │────▶│ DOSBox-X │
|
||||||
│ │ MCP │ Server │ GDB │ (GDB stub) │
|
│ │ MCP │ Server │ GDB │ Container │
|
||||||
└─────────────────┘ └──────────────────┘ └─────────────────┘
|
└─────────────────┘ └──────────────────┘ └─────────────────┘
|
||||||
│
|
│ │ │
|
||||||
▼
|
│ │ │
|
||||||
┌──────────────────┐
|
▼ ▼ ▼
|
||||||
│ GDB Remote │
|
FastMCP 2.x GDB Remote lokkju/dosbox-x-
|
||||||
│ Protocol │
|
(stdio) Protocol remotedebug fork
|
||||||
│ (TCP :1234) │
|
(TCP :1234) (--enable-remotedebug)
|
||||||
└──────────────────┘
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Development
|
### Why lokkju/dosbox-x-remotedebug?
|
||||||
|
|
||||||
```bash
|
Mainline DOSBox-X doesn't include a GDB server. The [lokkju/dosbox-x-remotedebug](https://github.com/lokkju/dosbox-x-remotedebug) fork adds:
|
||||||
# Run tests
|
|
||||||
make test
|
|
||||||
|
|
||||||
# Lint code
|
- `--enable-remotedebug` build flag
|
||||||
make lint
|
- GDB server on port 1234 (configurable)
|
||||||
|
- QMP server on port 4444 for machine control
|
||||||
# Format code
|
- Config options in `[dosbox]` section: `gdbserver=true`, `gdbserver port=1234`
|
||||||
make format
|
|
||||||
|
|
||||||
# View logs
|
|
||||||
make logs
|
|
||||||
```
|
|
||||||
|
|
||||||
## Project Structure
|
## Project Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
dosbox-mcp/
|
dosbox-mcp/
|
||||||
├── src/dosbox_mcp/
|
├── src/dosbox_mcp/
|
||||||
│ ├── server.py # FastMCP server (tools)
|
│ ├── server.py # FastMCP server entry point
|
||||||
│ ├── gdb_client.py # GDB protocol client
|
│ ├── gdb_client.py # GDB Remote Serial Protocol client
|
||||||
│ ├── dosbox.py # DOSBox-X management
|
│ ├── dosbox.py # DOSBox-X process/container management
|
||||||
│ ├── types.py # Type definitions
|
│ ├── state.py # Shared global state (manager, client)
|
||||||
│ └── utils.py # Utilities
|
│ ├── types.py # Type definitions (Registers, Breakpoint, etc.)
|
||||||
├── tests/
|
│ ├── utils.py # Address parsing, format conversion
|
||||||
├── examples/
|
│ └── tools/ # MCP tool implementations
|
||||||
├── Dockerfile # DOSBox-X with GDB
|
│ ├── execution.py # launch, attach, continue, step, quit
|
||||||
└── docker-compose.yml
|
│ ├── breakpoints.py # breakpoint_set, list, delete
|
||||||
|
│ ├── inspection.py # registers, memory, disassemble, stack
|
||||||
|
│ └── peripheral.py # screenshot, serial (stubs)
|
||||||
|
├── tests/ # pytest test suite
|
||||||
|
├── config/
|
||||||
|
│ └── dosbox.conf # DOSBox-X configuration with GDB enabled
|
||||||
|
├── dos/ # Mount point for DOS binaries
|
||||||
|
├── Dockerfile # Multi-stage build for dosbox-x-remotedebug
|
||||||
|
├── docker-compose.yml # Container orchestration
|
||||||
|
└── pyproject.toml # Python package definition
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### DOSBox-X Config (`config/dosbox.conf`)
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[dosbox]
|
||||||
|
memsize=16
|
||||||
|
gdbserver=true
|
||||||
|
gdbserver port=1234
|
||||||
|
qmpserver=true
|
||||||
|
qmpserver port=4444
|
||||||
|
|
||||||
|
[autoexec]
|
||||||
|
MOUNT C /dos
|
||||||
|
C:
|
||||||
|
```
|
||||||
|
|
||||||
|
### Environment Variables (`.env`)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
GDB_PORT=1234 # Host port for GDB
|
||||||
|
QMP_PORT=4444 # Host port for QMP
|
||||||
|
DOS_DIR=./dos # DOS files mount point
|
||||||
|
CONFIG_FILE=./config/dosbox.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run tests
|
||||||
|
uv run pytest
|
||||||
|
|
||||||
|
# Run tests with coverage
|
||||||
|
uv run pytest --cov=dosbox_mcp
|
||||||
|
|
||||||
|
# Lint code
|
||||||
|
uv run ruff check src/
|
||||||
|
|
||||||
|
# Format code
|
||||||
|
uv run ruff format src/
|
||||||
|
|
||||||
|
# Type check (optional)
|
||||||
|
uv run mypy src/
|
||||||
```
|
```
|
||||||
|
|
||||||
## Technical Details
|
## Technical Details
|
||||||
|
|
||||||
### GDB Remote Protocol
|
### GDB Remote Serial Protocol
|
||||||
|
|
||||||
This server implements a client for the [GDB Remote Serial Protocol](https://sourceware.org/gdb/current/onlinedocs/gdb.html/Remote-Protocol.html), which provides:
|
The server implements a client for the [GDB Remote Serial Protocol](https://sourceware.org/gdb/current/onlinedocs/gdb.html/Remote-Protocol.html):
|
||||||
|
|
||||||
- Register read/write (`g`, `G`, `p`, `P`)
|
| Command | Description |
|
||||||
- Memory read/write (`m`, `M`)
|
|---------|-------------|
|
||||||
- Software breakpoints (`Z0`, `z0`)
|
| `qSupported` | Capability negotiation (handshake) |
|
||||||
- Execution control (`c`, `s`, `?`)
|
| `g` / `G` | Read/write all registers |
|
||||||
|
| `p` / `P` | Read/write single register |
|
||||||
|
| `m` / `M` | Read/write memory |
|
||||||
|
| `Z0` / `z0` | Set/clear software breakpoint |
|
||||||
|
| `c` / `s` | Continue / step |
|
||||||
|
| `?` | Query stop reason |
|
||||||
|
|
||||||
|
Packets use format: `$<command>#<2-digit checksum>`
|
||||||
|
|
||||||
### Real Mode Addressing
|
### Real Mode Addressing
|
||||||
|
|
||||||
@ -190,13 +265,43 @@ DOS uses real mode with segment:offset addressing:
|
|||||||
Physical Address = (Segment << 4) + Offset
|
Physical Address = (Segment << 4) + Offset
|
||||||
```
|
```
|
||||||
|
|
||||||
This gives a 20-bit address space (1MB), though only 640KB is conventional memory.
|
This gives a 20-bit address space (1MB). The `Registers` class provides helpers like `cs_ip` and `ss_sp` for common address calculations.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### GDB Connection Refused
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check if container is running
|
||||||
|
docker ps | grep dosbox
|
||||||
|
|
||||||
|
# Check if GDB port is listening
|
||||||
|
docker exec dosbox-mcp nc -zv localhost 1234
|
||||||
|
|
||||||
|
# Check logs for GDB server startup
|
||||||
|
docker logs dosbox-mcp 2>&1 | grep -i gdb
|
||||||
|
```
|
||||||
|
|
||||||
|
### Container Shows "unhealthy"
|
||||||
|
|
||||||
|
The healthcheck uses `nc` which may not be installed in the slim image. This is cosmetic — the GDB server still works. To fix, add `netcat-openbsd` to the Dockerfile runtime stage.
|
||||||
|
|
||||||
|
### X11 Display Issues
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Allow Docker X11 access
|
||||||
|
xhost +local:docker
|
||||||
|
|
||||||
|
# Or use headless mode
|
||||||
|
docker compose --profile headless up -d dosbox-headless
|
||||||
|
```
|
||||||
|
|
||||||
## Related Projects
|
## Related Projects
|
||||||
|
|
||||||
- [RIPscrip Research](../rpmesh/) - Parent project for RIPscrip graphics protocol
|
- [RIPscrip Research (rpmesh)](https://github.com/ryanmalloy/rpmesh) — Parent project
|
||||||
- [dosbox-x-gdb](https://github.com/hezi/dosbox-x-gdb) - DOSBox-X fork with GDB support
|
- [lokkju/dosbox-x-remotedebug](https://github.com/lokkju/dosbox-x-remotedebug) — DOSBox-X fork with GDB
|
||||||
- [FastMCP](https://gofastmcp.com/) - MCP server framework
|
- [FastMCP](https://gofastmcp.com/) — MCP server framework
|
||||||
|
- [GDB Remote Protocol](https://sourceware.org/gdb/current/onlinedocs/gdb.html/Remote-Protocol.html)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user