19 KiB
Source-debug Sprinter в VS Code
Статус: MVP для разработки самого отладчика. Поддержаны автоматический launch, ручной attach, точки по строкам и функциям, logpoints, один C-frame, регистры, простые global/static, continue/pause, instruction step и шаги по исходнику F10/F11/Shift+F11.
Warning
Windows пока не поддерживает полный цикл отладки. Значение
debugger: "windows"включает только штатное окно debugger MAME. Текущие launcher, owner lock и DAP/session transport используют Unix shell,fcntlи Unix domain sockets. Поэтому native Windows launch/attach из VS Code пока не считается рабочим. Поддержка потребует отдельного Windows transport и end-to-end проверки.
Какие расширения нужны
Для build/run/debug используется отдельный проект VSCode-Sprinter. Только
его расширение знает формат source-debug
пакета, загрузку приложения через DSS, банки Sprinter и DAP-сессию MAME.
Microsoft C/C++ или clangd можно поставить дополнительно ради completion,
переходов по исходникам и подсветки. Оба анализируют код как близкий к
обычному C и не являются точной моделью SDCC: параметры __naked, ABI и
часть target-заголовков потребуют отдельных defines/configuration. Ошибки
реальной сборки всегда определяет sprinter-cc.
Подготовка программы
Соберите приложение с полной картой:
pyenv exec make -C tests/hello SRC_DEBUG=1
Рядом с EXE появится .sprinter-cc-hello/manifest.json. При изменении C,
заголовка или опций make пересоберёт пакет по хэшу содержимого.
Загрузка development-расширения
Из корня репозитория откройте VS Code с распакованным расширением:
code --extensionDevelopmentPath="/путь/к/VSCode-Sprinter" "$PWD"
Если команда code не добавлена в PATH, на macOS этого проекта доступен
полный путь:
"/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code" \
--new-window \
--extensionDevelopmentPath="/путь/к/VSCode-Sprinter" \
"$PWD"
В локальных настройках workspace задайте sprinterDebugger.sdkRoot и
sprinterDebugger.mameHome. Первый указывает на установленный C-Compiler,
второй — на среду MAME/runtime с бинарником, ROM и DSS/CHD. Для выбора
stock/sdbg или нестандартной установки используются поля mameBin,
mameRompath, mameDssImage, mameSystemHddImage, mameBios профиля launch.
Расширение в режиме auto использует ~/.pyenv/shims/python при наличии
local .python-version; для внешнего workspace ищет установленный
~/.pyenv/versions/3.12*/bin/python, затем .venv/bin/python и известные
абсолютные пути Python 3.12. Local-версия передаётся задаче сборки через
PYENV_VERSION, даже если приложение лежит вне workspace. Это не зависит от PATH процесса VS Code,
который мог быть открыт из Dock. Выбор можно переопределить абсолютным путём
в sprinterDebugger.pythonCommand; дополнительные аргументы задаются через
sprinterDebugger.pythonArguments.
Автоматический launch
Добавьте в локальный .vscode/launch.json:
{
"version": "0.2.0",
"configurations": [
{
"type": "sprinter-mame",
"request": "launch",
"name": "Sprinter MAME: hello",
"build": "${workspaceFolder}/tests/hello/.sprinter-cc-hello"
}
]
}
Launcher создаёт временные floppy/HDD/state-каталоги и видимое окно MAME.
В отдельный cfg/sprinter.cfg он записывает только включение обеих клавиатур
Sprinter: : и :kbd:ms_naturl. MAME по умолчанию активирует лишь первую,
поэтому без этой настройки физические клавиши не доходят до DSS getchar().
Действующая пользовательская конфигурация MAME при этом не копируется.
Он читает символы текстового экрана из VRAM, находит пустой prompt X:…> и
требует, чтобы тот оставался готовым 0,25 секунды. Только затем ставится
service-точка main и вводится a:\\HELLO.EXE. После совпадения PC и
сигнатуры кода запускается session server, DAP принимает редакторские точки,
а VS Code показывает остановку на entry. Завершение debug session останавливает
только созданные ей MAME/server.
При reset/state load или потере MAME остановленная сессия становится
недействительной и DAP завершает её; для новой отладки запускайте F5 заново.
Проверен сценарий внезапного завершения собственного MAME во время остановки
в main. Отдельный живой тест штатного закрытия окна и загрузки state ещё
предстоит выполнить.
dssTimeout задаёт предельное время ожидания prompt (30 эмулируемых секунд).
launchAt можно задать как необязательную нижнюю границу времени запуска;
наличие prompt всё равно обязательно. Перед вводом сохраняется диагностический
снимок DSS.
Для перенесённого SprPoP его workspace содержит локальный профиль F5:
{
"type": "sprinter-mame",
"request": "launch",
"name": "Sprinter: SprPoP (VS Code)",
"build": "${workspaceFolder}/build/.sprinter-cc-sprpop",
"buildTarget": "hdd",
"appHdd": "${workspaceFolder}/build/hdd/sprpop.chd",
"launchPath": "d:\\games\\sprpop\\sprpop.exe"
}
Расширение перед F5 запускает make SRC_DEBUG=1 hdd в каталоге игры,
затем загружает EXE с её локального диска. Для такого профиля нужен
SPRINTER_ROOT только на этапе сборки и MAME_HOME при запуске.
Дополнительные файлы на floppy задаются массивом data; для приложения с
собственным CHD задайте appHdd, launchPath и buildTarget: "hdd".
Launcher монтирует временную копию CHD как -hard2, а DSS вводит путь EXE
из launchPath. Сам debug EXE остаётся также на временной A: для проверки
сигнатуры; несовпадение кода на диске с пакетом отладки останавливает launch.
Ручная проверка VS Code
В репозитории локально подготовлены два профиля .vscode/launch.json:
Sprinter: hello (VS Code) и Sprinter: hello (VS Code + MAME debugger).
Файл .vscode намеренно игнорируется Git и не содержит личных путей.
- Соберите
helloкомандой из раздела «Подготовка программы» и запустите Extension Development Host одной из команд выше. - Откройте
tests/hello/hello.cи поставьте breakpoint на строке 31, вызовеputs. - В Run and Debug выберите
Sprinter: hello (VS Code)и нажмите F5. Расширение сначала выполнитmake SRC_DEBUG=1вtests/helloкак задачу Sprinter Build. После успешной сборки должно появиться окно Sprinter; launcher дождётся prompt DSS, введётA:\\HELLO.EXEи VS Code остановится вmain. - Проверьте Call Stack, scope Registers и Debug Console. После Continue должна сработать подтверждённая точка строки 31.
- На остановке проверьте F11 и F10. Курсор должен переходить только после
фактической остановки CPU, а не сразу после отправки команды. На строке 62
(
getchar) нажмите F10, щёлкните окно Sprinter MAME и нажмите латинскуюx: выполнение должно перейти на строку 63. Пока программа ждёт клавишу, кнопка Pause в VS Code должна останавливать CPU. - Завершите сессию кнопкой Stop. Затем повторите профиль с
osx: вместе с тем же VS Code-сеансом должно открыться штатное Cocoa-окно debugger MAME.
Команда палитры Sprinter: Build Active Project собирает приложение по
Makefile открытого C-файла. Задачи Sprinter: Build ... доступны и в
Tasks: Run Task. Для launch автоматическая сборка включена по умолчанию,
если из build можно найти Makefile с app.mk; нестандартный каталог задаётся
полем project, а уже собранный пакет запускается с "autoBuild": false.
Ошибка SDCC с файлом и строкой видна в Problems; ненулевой код задачи
останавливает F5 до запуска MAME. Явный preLaunchTask использует стандартное
поведение VS Code и отключает автоматическую сборку расширения.
Если F5 сообщает, что тип sprinter-mame неизвестен, окно запущено без
--extensionDevelopmentPath. Сообщение Couldn't find a debug adapter descriptor означает, что extension manifest загружен, но расширение не
активировалось; после изменения package.json выполните Developer: Reload Window. Канал Output → Sprinter MAME Debug показывает выбранные Python и
DAP. При раннем завершении MAME ответ launch включает последние строки
MAME log. Если сборочный пакет устарел, адаптер должен отказать до запуска
программы и показать причину, а не использовать старую карту.
Логи из исходника без вывода в DSS задаются в C через <sdbg.h>;
полный синтаксис и таблица поддержанных типов — в
руководстве по SDBG_LOG:
#include <sdbg.h>
volatile int total;
/* tag уникален в этом .c; total читает debugger, не приложение. */
SDBG_LOG(after_increment, "total={total}");
При F5 debug-сборка привяжет нулевой asm-якорь к адресу и session server
поставит logpoint после загрузки DSS. Попадание напишет total=... в
Debug Console VS Code и в debugger console MAME, затем продолжит выполнение.
В обычной сборке макрос пуст. Поддерживаются только global/static переменные
доказанного типа, литералы и {name}; локальные, выражения и %d пока нет.
SDBG_LOGIF(tag, flag, "...") выводит сообщение, если поддержанная
global/static flag ненулевая. Аргументы не исполняются на Sprinter, поэтому
выражения с побочными эффектами не имеют здесь смысла. В режиме sdbg
журнал MAME существует, но его штатное окно не открывается; для видимого
окна выберите osx ниже. Горячий logpoint может заметно замедлить эмуляцию:
CPU останавливается на каждом попадании. Если DAP не успевает забрать
события из ограниченного журнала, Debug Console сообщает число пропусков.
Текст в VS Code сохраняется точно; MAME console заменяет двойные кавычки
апострофами и управляющие символы пробелами из-за синтаксиса printf.
Родной debugger MAME вместе с VS Code
По умолчанию используется project backend sdbg: видимо окно Sprinter,
а отдельное Cocoa-окно debugger не создаётся. Для регистров, дизассемблера,
памяти и console штатного MAME добавьте в launch-конфигурацию:
"debugger": "osx"
Bridge и DAP продолжают работать параллельно. Ручные go, изменение точек и
reset в native console обходят модель состояния VS Code; reset пока нельзя
использовать из-за lifecycle-crash MAME 0.287.
Для MAME без project patch выберите штатный provider:
| Платформа/сборка | debugger |
|---|---|
| macOS | osx |
| Windows native | windows |
| Linux с Qt debugger | qt |
| Linux/SDL без Qt | imgui |
| Автоматический выбор доступного provider | auto |
imgui использует основное графическое окно MAME; launcher уже запускает его
с -video soft -window. none немедленно продолжает остановленный CPU, а
gdbstub ждёт протокол GDB, поэтому они не подходят для sdbgbridge.
Текущая host-часть sdbg использует fcntl и Unix domain sockets. Она работает
на macOS/Linux. Для native Windows нужны отдельные реализации owner lock,
локального RPC и launcher; выбор windows решает только сторону MAME и не
обеспечивает работу VS Code-интеграции.
Backend sdbg входит как воспроизводимый patch к MAME 0.287. Исходники и
рецепты теперь принадлежат самостоятельному проекту MAME:
cd /путь/к/MAME
scripts/sprinter/build-variants.sh stock
scripts/sprinter/build-variants.sh sdbg
В fork patch уже зафиксирован в Git; для чистого baseline сохранён
scripts/sprinter/apply-sdbg-patch.sh. Каждый вариант копируется в
MAME_HOME/bin/stock/mame.arm или MAME_HOME/bin/sdbg/mame.arm без подмены
активного MAME_HOME/mame.arm.
Logpoints
Обычная команда VS Code Add Logpoint… работает без пересборки. В сообщении разрешены литералы и простые подстановки:
frame={frame_counter} state={state}
Подстановка принимает только имя поддержанной переменной. Выражения, format specifier и conversion отклоняются. Если logpoint и обычная точка разрешились в один адрес, сообщение печатается, а CPU остаётся остановленным.
Шаги по исходнику
F11 выполняет Z80-инструкции до следующей отличающейся C-позиции. F10 на
каждом машинном шаге использует MAME over, поэтому банковский вызов проходит
целиком. Shift+F11 сначала делает MAME out, затем проходит служебный bank
trampoline до первой C-позиции вызывающей функции. Instruction granularity
остаётся доступна для точного одиночного шага.
Автомат ограничен 512 машинными операциями. Команда шага отвечает VS Code
сразу; пока getchar() или другой вызов ждёт внешнего ввода, MAME продолжает
работать, а пользователь может нажать клавишу в его окне либо выполнить Pause
в VS Code. Время ожидания ввода не ограничивается таймаутом исходного шага.
При достижении предела CPU остаётся остановленным, а Debug Console получает
сообщение.
Пользовательская точка внутри шага имеет приоритет; logpoint печатается и шаг
продолжается с прежней семантикой.
Ручной attach
Для общего MAME между CLI и VS Code сначала запустите sdbg_server.py, затем:
{
"type": "sprinter-mame",
"request": "attach",
"name": "Sprinter MAME: Attach",
"socket": "/tmp/sprinter-sdbg.sock"
}
Команды server и диагностического CLI приведены в mame-source-debug-status.md.
Ограничения MVP
- Reset/restart отключён после найденного crash MAME 0.287 на старом Lua callback. Session завершается штатным terminate.
- Локальные, backtrace, setVariable, watchpoints и чтение неотображённого bank-data ещё не реализованы.
- Если одна инструкция имеет несколько C-маркеров, frame помечается
[ambiguous]; адаптер не выбирает строку молча.