OXYGENHUB
# 7c83b415d7cc retro-remote-debug-controller / README.md
2.8 KiB · Markdown · last changed HTTP control contract, portable server core, and shared client 3 months ago renderedsource blame history raw
961b3c61 Andy McCall 1
# retro-remote-debug-controller
2
3
A tiny, portable **HTTP control API for retro-platform emulators**, and the
4
shared tooling that speaks it. One contract → one test harness across every
5
Doomsday One port.
6
7
An emulator launched with a control-port flag exposes a localhost HTTP server:
8
9
| Endpoint | Returns |
10
|---|---|
11
| `GET /status` | contract version, platform, frame counter, paused state |
12
| `GET /screenshot` | PPM image of the live screen |
13
| `GET /mem?addr=&len=&bank=` | raw memory bytes |
14
| `GET /regs` | CPU registers (JSON) |
15
| `POST /step?frames=` | advance N frames deterministically |
16
| `POST /key`, `/reset` | *(0.2)* inject input |
17
18
## Why
19
20
Every one of these ports (Pac-Man on X16, Neo6502, Agon; and whatever's next) is
21
chasing **frame-exact** arcade fidelity, and every one had its own fragile,
22
platform-specific way to check state headless (write to the SD card, dump 64 KB
23
of RAM on a magic jump, grep stdout — which lies). This replaces all of that
24
with one contract:
25
26
- **`GET /mem`** → assert game state (sprite table, actor positions).
27
- **`GET /screenshot`** → Pillow → assert pixels, or just catch "nothing
28
  rendered" (the exact class of bug that costs an afternoon otherwise).
29
- **`POST /step`** → deterministic, frame-addressable capture, so CI diffs don't
30
  flake against a free-running 60 Hz loop.
31
32
The payoff is uniformity: the same `emu_control.py` and the same pytest patterns
33
drive the X16, Neo6502, and Agon emulators.
34
35
## Layout
36
37
```
38
SPEC.md                 the normative HTTP contract (start here)
39
core/retro_control.h    portable backend vtable each emulator implements
40
core/retro_control.c    shared server: sockets, HTTP, PPM/JSON, marshalling  [next]
41
client/emu_control.py   shared pytest client
42
conformance/            tests an emulator runs to prove it conforms          [next]
43
```
44
45
## Adopting it in an emulator (the shape)
46
47
Write the server once, adapt per fork. Each fork vendors `core/` and implements
48
~5 callbacks, then calls the server:
49
50
```c
51
#include "retro_control.h"
52
53
static retro_control_backend_t backend = {
54
    .platform       = "x16",
55
    .read_mem       = x16_read_mem,      /* debug-read, no side effects */
56
    .get_regs_json  = x16_get_regs_json,
57
    .get_framebuffer= x16_get_framebuffer,
58
    .step_frames    = x16_step_frames,
59
    .get_frame_count= x16_get_frame_count,
60
};
61
62
if (control_port)              /* set by -controlport / --control-port */
63
    retro_control_start(control_port, &backend);
64
```
65
66
The socket loop, HTTP parsing, PPM encoding, JSON framing, and the
67
enqueue-to-emulator-thread marshalling live in the shared core — not in three
68
different codebases.
69
70
## Status
71
72
Early. `SPEC.md` (0.1.0) and the interfaces below are the stable target the
73
forks build against; the server `.c` and conformance suite are landing next.
74
Reference implementation: the Commander X16 fork (`x16-emulator`).