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:
|
||||
|
||||
- Setting breakpoints
|
||||
- Reading/writing registers and memory
|
||||
- Stepping through code
|
||||
- Tracing execution
|
||||
- Setting breakpoints and tracing execution
|
||||
- Reading/writing CPU registers and memory
|
||||
- Disassembling code at any address
|
||||
- Step-by-step instruction execution
|
||||
|
||||
## 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
|
||||
|
||||
@ -19,168 +19,243 @@ This MCP server enables Claude to programmatically debug DOS programs running in
|
||||
|
||||
- Python 3.11+
|
||||
- [uv](https://github.com/astral-sh/uv) package manager
|
||||
- Docker (for DOSBox-X container)
|
||||
- X11 display (for DOSBox GUI)
|
||||
- Docker and Docker Compose
|
||||
- X11 display (for DOSBox GUI) or use headless mode
|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone https://github.com/yourusername/dosbox-mcp.git
|
||||
git clone https://github.com/ryanmalloy/dosbox-mcp.git
|
||||
cd dosbox-mcp
|
||||
|
||||
# Install dependencies
|
||||
uv sync
|
||||
|
||||
# Build Docker image
|
||||
make build
|
||||
# Build Docker image (uses lokkju/dosbox-x-remotedebug fork)
|
||||
docker compose build
|
||||
|
||||
# Create DOS directory
|
||||
make init
|
||||
# Create DOS directory for your binaries
|
||||
mkdir -p dos
|
||||
```
|
||||
|
||||
### Running
|
||||
|
||||
```bash
|
||||
# Allow X11 access for Docker
|
||||
# Allow X11 access for Docker (Linux)
|
||||
xhost +local:docker
|
||||
|
||||
# Start DOSBox-X
|
||||
make up
|
||||
# Start DOSBox-X with GDB server
|
||||
docker compose up -d
|
||||
|
||||
# Register MCP server with Claude Code
|
||||
make register
|
||||
# Verify GDB port is listening
|
||||
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)
|
||||
|
||||
# Set a breakpoint
|
||||
breakpoint_set("1234:0100")
|
||||
|
||||
# Run until breakpoint
|
||||
continue_execution()
|
||||
|
||||
# Read registers
|
||||
# Read current CPU state
|
||||
registers()
|
||||
|
||||
# Read memory
|
||||
memory_read("DS:0100", 64)
|
||||
# Read memory at CS:IP
|
||||
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)
|
||||
|
||||
# Clean up
|
||||
# Disassemble at current location
|
||||
disassemble("CS:IP", 20)
|
||||
|
||||
# Disconnect when done
|
||||
quit()
|
||||
```
|
||||
|
||||
## Tools Reference
|
||||
|
||||
### Execution Control
|
||||
### Execution Control (6 tools)
|
||||
|
||||
| Tool | Description |
|
||||
|------|-------------|
|
||||
| `launch` | Start DOSBox-X with optional binary |
|
||||
| `attach` | Connect to GDB stub |
|
||||
| `continue_execution` | Run until breakpoint |
|
||||
| `step` | Step N instructions |
|
||||
| `launch` | Start DOSBox-X container with optional binary |
|
||||
| `attach` | Connect to GDB stub at host:port |
|
||||
| `continue_execution` | Run until breakpoint or signal |
|
||||
| `step` | Step N instructions (default: 1) |
|
||||
| `step_over` | Step over CALL instructions |
|
||||
| `quit` | Stop DOSBox-X |
|
||||
| `quit` | Disconnect and optionally stop DOSBox-X |
|
||||
|
||||
### Breakpoints
|
||||
### Breakpoints (3 tools)
|
||||
|
||||
| Tool | Description |
|
||||
|------|-------------|
|
||||
| `breakpoint_set` | Set breakpoint at address |
|
||||
| `breakpoint_list` | List all breakpoints |
|
||||
| `breakpoint_delete` | Remove breakpoint(s) |
|
||||
| `breakpoint_set` | Set software breakpoint at address |
|
||||
| `breakpoint_list` | List all active breakpoints |
|
||||
| `breakpoint_delete` | Remove breakpoint by ID or all |
|
||||
|
||||
### Inspection
|
||||
### Inspection (6 tools)
|
||||
|
||||
| Tool | Description |
|
||||
|------|-------------|
|
||||
| `registers` | Read all CPU registers |
|
||||
| `memory_read` | Read memory region |
|
||||
| `memory_write` | Write to memory |
|
||||
| `disassemble` | Simple disassembly view |
|
||||
| `stack` | Dump stack contents |
|
||||
| `status` | Get debugger status |
|
||||
| `registers` | Read all CPU registers (32-bit, 16-bit, segments, flags) |
|
||||
| `memory_read` | Read memory in hex/ASCII/dump format |
|
||||
| `memory_write` | Write hex or ASCII data to memory |
|
||||
| `disassemble` | Disassemble instructions with opcode hints |
|
||||
| `stack` | Dump stack contents from SS:SP |
|
||||
| `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:
|
||||
|
||||
- **Segment:offset**: `1234:5678` (standard DOS format)
|
||||
- **Flat hex**: `0x12345` or `12345h`
|
||||
- **Decimal**: `#65536`
|
||||
- **Register-based**: `DS:SI`, `CS:IP`
|
||||
| Format | Example | Description |
|
||||
|--------|---------|-------------|
|
||||
| Segment:offset | `1234:5678` | Standard DOS format |
|
||||
| Flat hex | `0x12345` or `12345` | Physical address |
|
||||
| Decimal | `#65536` | Decimal address |
|
||||
| Register-based | `CS:IP`, `DS:SI` | Uses current register values |
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
|
||||
│ Claude Code │────▶│ DOSBox-X MCP │────▶│ DOSBox-X │
|
||||
│ │ MCP │ Server │ GDB │ (GDB stub) │
|
||||
│ │ MCP │ Server │ GDB │ Container │
|
||||
└─────────────────┘ └──────────────────┘ └─────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────┐
|
||||
│ GDB Remote │
|
||||
│ Protocol │
|
||||
│ (TCP :1234) │
|
||||
└──────────────────┘
|
||||
│ │ │
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
FastMCP 2.x GDB Remote lokkju/dosbox-x-
|
||||
(stdio) Protocol remotedebug fork
|
||||
(TCP :1234) (--enable-remotedebug)
|
||||
```
|
||||
|
||||
## Development
|
||||
### Why lokkju/dosbox-x-remotedebug?
|
||||
|
||||
```bash
|
||||
# Run tests
|
||||
make test
|
||||
Mainline DOSBox-X doesn't include a GDB server. The [lokkju/dosbox-x-remotedebug](https://github.com/lokkju/dosbox-x-remotedebug) fork adds:
|
||||
|
||||
# Lint code
|
||||
make lint
|
||||
|
||||
# Format code
|
||||
make format
|
||||
|
||||
# View logs
|
||||
make logs
|
||||
```
|
||||
- `--enable-remotedebug` build flag
|
||||
- GDB server on port 1234 (configurable)
|
||||
- QMP server on port 4444 for machine control
|
||||
- Config options in `[dosbox]` section: `gdbserver=true`, `gdbserver port=1234`
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
dosbox-mcp/
|
||||
├── src/dosbox_mcp/
|
||||
│ ├── server.py # FastMCP server (tools)
|
||||
│ ├── gdb_client.py # GDB protocol client
|
||||
│ ├── dosbox.py # DOSBox-X management
|
||||
│ ├── types.py # Type definitions
|
||||
│ └── utils.py # Utilities
|
||||
├── tests/
|
||||
├── examples/
|
||||
├── Dockerfile # DOSBox-X with GDB
|
||||
└── docker-compose.yml
|
||||
│ ├── server.py # FastMCP server entry point
|
||||
│ ├── gdb_client.py # GDB Remote Serial Protocol client
|
||||
│ ├── dosbox.py # DOSBox-X process/container management
|
||||
│ ├── state.py # Shared global state (manager, client)
|
||||
│ ├── types.py # Type definitions (Registers, Breakpoint, etc.)
|
||||
│ ├── utils.py # Address parsing, format conversion
|
||||
│ └── tools/ # MCP tool implementations
|
||||
│ ├── execution.py # launch, attach, continue, step, quit
|
||||
│ ├── 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
|
||||
|
||||
### 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`)
|
||||
- Memory read/write (`m`, `M`)
|
||||
- Software breakpoints (`Z0`, `z0`)
|
||||
- Execution control (`c`, `s`, `?`)
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `qSupported` | Capability negotiation (handshake) |
|
||||
| `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
|
||||
|
||||
@ -190,13 +265,43 @@ DOS uses real mode with segment:offset addressing:
|
||||
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
|
||||
|
||||
- [RIPscrip Research](../rpmesh/) - Parent project for RIPscrip graphics protocol
|
||||
- [dosbox-x-gdb](https://github.com/hezi/dosbox-x-gdb) - DOSBox-X fork with GDB support
|
||||
- [FastMCP](https://gofastmcp.com/) - MCP server framework
|
||||
- [RIPscrip Research (rpmesh)](https://github.com/ryanmalloy/rpmesh) — Parent project
|
||||
- [lokkju/dosbox-x-remotedebug](https://github.com/lokkju/dosbox-x-remotedebug) — DOSBox-X fork with GDB
|
||||
- [FastMCP](https://gofastmcp.com/) — MCP server framework
|
||||
- [GDB Remote Protocol](https://sourceware.org/gdb/current/onlinedocs/gdb.html/Remote-Protocol.html)
|
||||
|
||||
## License
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user