Closes issue #1.
The RRDC control port (-controlport <port>), serving contract 0.6.
- The core: vendored as an oxy submodule at
src/extern/retro-remote-debug-controller, following RRDC'sperf-6bookmark until retro-remote-debug-controller #7 lands. - The backend (
src/control_backend.c): ported from the plain fork's. It covers memory, registers, the screen, stepping, keys, reset, audio, the pointer and pads; the pads go through this emulator's own virtual joysticks. GETandPOST /perfanswer from the measurements the Performance panel and DAP make. DAP's JSON builders moved intoperf_json.c, so both surfaces give the same fields. Added for/perf:- each zone's statistics per window;
- raw frames
since=a frame; - each scanline's bus clocks (
lines=1); - the sprite renderer's time, not exposed remotely before.
perf_budget_zone_add_marker: a client names a guest marker region before the guest enters it.- Hooks: the main loop serves the port's requests between instructions and closes
/stepafter the budget closes its frame. The Windows drag path stands aside while the port is on, as it does for the debuggers. - CMake: the sources and RRDC's include path.
/perfcomes with cJSON (USE_DAP); without it the port serves 0.5.
Two fixes in the measurement code, found by measuring Metal Gear:
- A framed guest's regions carry across the frame boundary. The module's frame runs vsync to vsync, while a game's starts where it waits, so its work runs across the boundary. Closing regions there handed the rest of the phase then running to no zone: Metal Gear's "common" read 2.8 scanlines against the true 4.3 its cycle counter gives. A guest that frames its work with
$F0…$F1now keeps its regions until its own next$F0/$F1. A guest that never writes$F0has them closed at the boundary, as before. - An interrupt after
$F1is work. In marker mode, cycles in an IRQ handler during the idle span counted as idle, so main (work − IRQ) came up short. The docs already said interrupt cycles are never idle.
Docs: README (the control port, /perf, a run recipe) and docs/perf-budget.md (named regions, the framed-region rule, the IRQ rule, and where it's implemented).
Tests:
- Unit tests: 40 of 40.
perf_budgetpasses 113 checks, with new ones for the framed carry, the IRQ rule and named markers. The old "a region left open is closed by the frame boundary" test is now two cases: unframed (still closed) and framed (closed at the guest's next$F0). - RRDC's conformance suite: 27 of 27, including the five 0.6 tests.
testbench/test_dap.py: 84 passed, 0 failed, 1 skipped (needs-bp), so DAP is unchanged by the builders' move.- Metal Gear's harness on this build:
test_smokeandtest_frame_cost(64 tests) andtest_perf_markers. Its per-phase means from/perfmatch the guest's cycle counter to within 0.1 of a scanline.