25 KiB
План сведения MCP-интерфейсов MAME и C-отладки
Цель и границы
Для C-приложения один sdbg_server.py владеет одним процессом MAME. VS Code
подключается к нему через DAP, Codex и Claude — через MCP, в том числе без
запущенного VS Code. Агент получает возможности нынешнего raw MCP, сохраняя
проверку C-карты, банки, события и владение точками. Один процесс MAME не
загружает одновременно mamebridge и sdbgbridge.
MAME.HT/plugins/mamebridge вместе с src/mame_mcp.py публикует 30
raw-инструментов. Удалённый на этапе А src/mame_bridge.lua использовал тот
же Python frontend, но обрабатывал только 16 команд и не отвечал при hard-stop.
toolchain/sdbg_mcp.py публикует 29 C-инструментов через общую с DAP сессию
(31 в автономном режиме с командами start/stop).
Цель — покрыть возможности всех 30 raw-инструментов в C-сессии, а не
механически скопировать имена и небезопасную семантику. Полный raw MCP
остаётся отдельным режимом для задач без C-пакета и других машин MAME.
Матрица покрытия должна для каждого raw-инструмента содержать имя/аргументы, адресное пространство, состояние CPU, владельца операции, эквивалент в C-MCP, ограничения совместной работы с DAP и живой тест. «Покрыт» означает проверенное поведение, а не только регистрацию инструмента в MCP SDK.
А. Проверить и убрать mame_bridge.lua
- Зафиксировать 16 команд старого Lua-dispatch:
regs,mem,setmem,bp,bpclr,bplist,wp,wpclr,step,over,out,cont,pause,status,dasm,cmd. Проверять именно Lua-обработчики: Python frontend показывает 30 инструментов и со старым скриптом, хотя 14 из них получаютunknown command. - Расширить
MAME.HT/scripts/sprinter/probe-raw-mcp.pyотдельным воспроизводимым профилем. Запускать legacy script и plugin по очереди с одинаковым BIOS, ROM, fixture и изолированными каталогами IPC/дисков. Для чтения сравнивать нормализованные результаты, дляsetmemписать лишь в выделенный scratch-буфер и восстанавливать байты. У legacy проверять создание/перечень/очистку не срабатывающих точек при running CPU; у plugin дополнительно проверять реальное попадание и очистку после stop.cmdв сравнении ограничить читающей командой, напримерprint pc. Не выполнять reset/state load ради сравнения: безопасный reset ещё не подтверждён. - Старый frame notifier не обслуживает команды при остановленном CPU.
Поэтому stop-dependent
step/over/outи интерактивную работу точек нельзя требовать от legacy в этом состоянии. Для них сверить трансляцию команд по коду и проверить поведение plugin при stop→step→inspect. Ошибка или timeout старого скрипта при hard-stop — его известное ограничение, а не недостающая функция plugin. - Отдельно прогнать 30 инструментов plugin через настоящий MCP stdio-клиент
с корректными предусловиями: running для ввода, stopped для шагов,
изолированные пути для снимков. Для каждой команды проверить содержимое
ответа, а не только отсутствие
unknown command; после ввода и точек проверить очистку состояния. - Если все 16 legacy-команд покрыты plugin и нет внешнего обязательного
потребителя
-autoboot_script mame_bridge.lua, удалить файл из активного дерева MAME.HT. Сохранить его в Git-истории; поправить ссылки вMAME_MCP_GUIDE.md,src/CLAUDE.md,scripts/sprinter/README.md,src/mame_mcp.pyи заголовок plugin, где сейчас заявлено, что отличается только способ опроса. Зафиксировать отдельным коммитом MAME.HT.
Выход А: plugin обеспечивает весь прежний набор и 30 своих инструментов; проверяемых ссылок на удалённый script в текущем способе запуска нет. До этого шага считать legacy устаревшим, но не удалённым.
Статус А, 2026-09-17 — выполнено. Сравнение Lua-dispatch показало 16/16
общих команд. Последовательные MCP stdio-прогоны на MAME.HT проверили
регистры, память, дизассемблирование, консоль, точки/watchpoints и
resume/pause у legacy при running CPU и plugin при stop. На реальном hello
plugin прошёл DSS→main, запись и восстановление errno, попадание в
watchpoint, ввод x через getchar() и машинные шаги over/out. Старый
frame notifier не смог обслужить запрос после service-stop в main, что
подтвердило известное ограничение. Файл удалён из активного дерева MAME.HT;
после удаления оба plugin-пробника повторно прошли. Повторный прогон hello
с -video soft -window показал вывод программы и Press any key to exit...
на снимке работающего экрана; затем x довёл PC до строки после getchar().
Пробник tests/sdbg/run_raw_mcp_hello_probe.py теперь по умолчанию использует
видимое окно, а --proof сохраняет снимок вне временного каталога сессии.
Б. Подготовить общую C-сессию к расширению
- Ввести control lease между DAP и MCP для
continue,pause, шагов, записи памяти, ввода и raw-команд. Читающие операции допускают нескольких клиентов; конфликтующие изменения получают явный отказ или передачу управления. У каждой точки, watchpoint и удерживаемой клавиши есть owner; heartbeat и закрытие соединения освобождают их даже после аварии клиента. - Все ответы и запросы связывать с session ID, build ID и generation. При
reset/load/exit или замене EXE инвалидировать старые операции и снимки.
Ограничить размеры запросов, очередь событий и время ожидания; после
timeout не повторять изменяющую команду вслепую. Для дополнительных
возможностей расширять один
sdbgbridge, а не загружать рядом raw plugin. - Определить единый контракт адресов: logical Z80, raw program/data/io,
VRAM/share и текущие страницы банков — разные типы адреса. Существующий
C-инструмент
read_memoryоставляет значение «logical Z80»; raw-чтение получает другое имя, напримерread_program_memory. Аналогичноclear_breakpointпродолжает принимать логический ID владельца, а не незащищённый MAME ID. Совпадение имени не должно менять старую семантику.
Выход Б: два MCP-клиента и DAP читают одну сессию; конкурентные мутации сериализованы, а потеря клиента не оставляет его точек или нажатых клавиш.
Промежуточный статус Б, 2026-09-17. В session server добавлен 30-секундный
control lease: команды CPU и ввод принимаются от текущего владельца, чужие
получают явный отказ. MCP предоставляет claim_control/release_control и
продлевает lease фоновым heartbeat; DAP продлевает его при опросе событий и
освобождает при disconnect. Каждый процесс session server выдаёт новый
session_id. Unit-тест проверил конфликт, освобождение и истечение lease;
живой DAP+MCP-прогон проверил передачу управления обратно VS Code и попадание
в его точку. Сервер теперь также удаляет личные MCP-точки и отпускает
удерживаемые клавиши после истечения heartbeat. Живой прогон с владельцем
без heartbeat подтвердил событие owner_expired, удаление точки и отсутствие
ложной остановки в hello.c:62 после возобновления DAP.
RPC теперь привязывает последующие запросы DAP/MCP/CLI к session ID и build ID;
мутации CPU и точек также требуют актуальную generation. Смена сессии/socket
или устаревшая generation дают явный отказ, без автоматического повтора.
Этап Б не закрыт: ещё нужны проверка reset/load на живом MAME, контроль
новых типов мутаций и политика передачи lease между долгоживущими клиентами.
В. Перенести 30 возможностей raw MCP по группам
| Raw-инструменты | Реализация в общей сессии и проверка |
|---|---|
status, read_registers, step, step_over, step_out, resume, pause |
Сохранить C-позицию и события; добавить ограниченный счётчик машинных шагов там, где raw его принимает. Проверить F10/F11/Shift+F11, остановку на пользовательской точке, ожидание getchar() и управление из VS Code и MCP. |
read_memory, read_logical_memory, read_vram, read_share, list_shares, write_memory |
Явные пространства и пределы длины, чтение без side effects, маркировка банков. Запись — только с lease и при остановленном CPU, с проверкой диапазона и read-back. Изменение кода/банков инвалидирует или повторно проверяет C-карту; не выдавать изменённый EXE за исходный build. |
set_breakpoint, clear_breakpoint, list_breakpoints, set_watchpoint, clear_watchpoint |
Raw-адрес и условие вынести в отдельный управляемый API; сохранить C-точки по строке/функции. Учитывать owner, bank/window и физический alias, показывать чужие точки только для чтения. Для watchpoint доказать срабатывание на read/write/IO, корректный PC и отсутствие ложного попадания при загрузке DSS. |
disassemble, debugger_command, screenshot, read_screen_pixels |
Дизассемблировать с явным пространством/банком. Снимок и пиксели отдавать с размером, форматом, временем кадра и ограничением объёма; при hard-stop сообщать, что кадр может быть старым. debugger_command в общей DAP-сессии сначала поддерживает проверенные читающие команды; полный pass-through — только под эксклюзивным raw lease с переоценкой/инвалидацией состояния после команды. Недопустимую команду отклонять явно, не выдавать частичный результат за поддержку. |
list_ports, press_key, type_string, type_text, move_mouse, click_mouse, press_input, set_input |
Одна очередь ввода с владельцем, временем удержания и гарантированным release при stop/disconnect/error. Вводить лишь после запуска EXE при running CPU; автоматический ввод DSS оставить launcher. Проверить getchar(), графическое приложение, мышь, прямую физическую клавиатуру MAME и отсутствие перехвата фокуса другой программы. |
Для каждой группы сначала добавить ограниченный метод sdbgbridge, затем
проверку/событие в sdbg_server.py, затем MCP-инструмент и документацию.
Не переносить старые строковые Lua-команды напрямую через MCP: они обходят
проверку типов, владения и generation. Если точная семантика raw-инструмента
небезопасна при открытом DAP, сохранить его функцию в явно эксклюзивном
режиме, а в общей сессии вернуть объясняемое ограничение.
Выход В: матрица всех 30 строк закрыта живыми тестами; ограничения raw pass-through и особенности адресов перечислены поимённо. Число методов C-MCP может быть больше 30 из-за разных адресных пространств и C-операций.
Промежуточный статус В, 2026-09-17. Через существующий sdbgbridge
добавлены list_shares, read_share, read_vram, read_screen_pixels,
screenshot, read_program_memory, list_ports с пределами размеров,
точным tag share, изолированным каталогом PNG и признаком устаревшего кадра
при stop. Живой DAP+MCP-прогон подтвердил совпадение raw program/logical Z80
в main, VRAM/share, список портов и PNG; второй прогон снял экран работающего
hello во время getchar() и затем завершил шаг клавишей x. Полное
поимённое состояние — в матрице 30 raw-инструментов.
Одиночный публичный press_key теперь требует running CPU и control lease,
удерживает клавишу 1..60 кадров и отпускает её при stop/ошибке. Автономный
официальный MCP-клиент ввёл x в ожидающий getchar() и попал на следующую
C-строку. Асинхронная очередь событий, другие имена физических клавиш и мышь
ещё не реализованы; поэтому строка raw press_key покрыта частично.
Ограниченный type_string уже выполняет последовательность из 1..64
символов PC-раскладки с предварительной проверкой, покадровым удержанием и
отпусканием, прерыванием при stop. На tests/gets строка Ab9 и Enter
дошли до gets(); программа напечатала её и остановилась на следующей
C-строке. Это покрывает типовой строковый ввод, но не natural keyboard и
не асинхронную очередь произвольных событий мыши/портов.
Читающий list_breakpoints теперь показывает логические C-точки всех
владельцев, адреса и полученные от MAME условия банковской страницы;
удалять чужие точки он не позволяет. Живой MCP-прогон увидел личную точку
перед getchar(). Точки, вручную созданные в родном debugger, пока вне списка.
Дизассемблирование текущего logical Z80 окна добавлено как
disassemble_logical: фиксированная команда MAME с числовыми аргументами,
пределом 256 байт и изолированным временным файлом; ответ включает
bank_pages и generation. Живой MCP-прогон получил инструкции main с
адреса 0x8224; байты до и после операции совпали. Raw program вне
текущего отображения остаётся открытым.
Машинный step_instruction принимает ограниченный count=1..64; backend
передаёт это число в native cpu.debug:step(count), общий session server
сохраняет control lease и событие остановки. Живой автономный MCP-прогон
выполнил count=3 в main, затем продолжил hello до getchar() и вышел
через клавишу. DAP source-step не менялся.
Г. Сделать запуск пригодным для Codex и Claude без VS Code
Сейчас sdbg_mcp.py подключается лишь к уже работающему socket. Сам
sdbg_launcher.py умеет поднять MAME→DSS→EXE→main и sdbg_server.py
без VS Code, но это ещё не удобный жизненный цикл для MCP-клиента.
- Добавить supervisor/команду запуска C-сессии с параметрами build,
MAME_HOME/MAME_BIN/ROM/DSS/образов и стабильным session socket. MCP handshake должен завершаться быстро; запуск MAME выполняется асинхронным инструментом с событиями прогресса, чтобы долгий DSS boot не выглядел как зависший MCP-сервер. Предусмотреть attach к уже работающей VS Code-сессии и DAP attach к сессии, запущенной агентом. - Определить судьбу MAME при закрытии каждого клиента: отсоединение одного
агента не завершает VS Code-сессию; явно управляемая автономная сессия
живёт до
stop_sessionили закрытия её владельца согласно выбранной политике. Повторный MCP старт не должен подключаться к старому socket/PID. - Проверить реальные stdio-подключения Codex и Claude отдельно: агент
запускает C-приложение без VS Code, останавливается в
main, читает переменную и экран, вводит клавишу; затем VS Code подключается к той же сессии. Обратный порядок: F5 в VS Code, агент подключается по socket и действует без второго Lua-моста. Проверить смену lease и cleanup.
Выход Г: C-отладка полностью доступна агенту без VS Code, а подключение агента к VS Code-сессии не создаёт второй MAME и не теряет точки DAP.
Промежуточный статус Г, 2026-09-17. sdbg_mcp.py --build публикует
асинхронный start_session, фазовый session_status и stop_session.
Живой официальный MCP-клиент получил ответ старта за 5–7 мс, дождался
DSS→hello.exe→main, прочитал переменную и PNG. DAP подключился к тому же
socket; его disconnect оставил MAME работающим. Повторный MCP start был
отклонён, stop_session удалил socket и завершил свой MAME. При закрытии
MCP-процесса supervisor также завершает принадлежащий ему launcher.
Тем же официальным клиентом проверены установка C-точки перед getchar(),
ввод x через публичный MCP press_key и остановка на следующей C-строке.
Этап Г не закрыт: нужны реальные подключения из Codex и Claude,
проверка передачи control lease между их клиентами и DAP, а также устойчивость
к сбоям launcher/клиента на разных стадиях boot.
Проверки и ограничения на всём пути
- Живые прогоны выполнять на
MAME.HT/sprinter, собранном командойmake SUBTARGET=sprinter SOURCES=src/mame/sinclair/sprinter.cpp; отдельно проверить stock +osxи patched +sdbgтам, где функция не требует патча. Lua-изменения не требуют пересборки бинарника, но требуют нового запуска MAME. - Использовать локальный pyenv Python 3.12. Raw MCP пока требует SDK 1.x, C-MCP — SDK 2.x; окружения разделять до явной миграции frontend. Проверять оба MCP-клиента на реальном протоколе, а не только прямыми Python-вызовами.
- Тесты на macOS включают окно
osxкак опцию, отзывчивость MAME, ручной ввод и Debug Console. Linux проверять отдельным живым прогоном. Полный Windows-маршрут пока не поддерживается из-за Unix socket/host launcher. - Несколько процессов MAME с MCP остаются отдельной отложенной задачей: их одновременная работа не проверялась и не гарантируется. Не объявлять её рабочей по результату тестов нескольких клиентов одной сессии.
- Сохранять риск раннего фокуса MAME отдельной финальной задачей: защита от случайного ввода до старта EXE не должна ломать DSS bootstrap и прямой ввод после запуска приложения.