Sprinter: добавить отладку C-исходников и интеграцию VS Code

This commit is contained in:
2026-09-15 17:58:41 +03:00
parent 50c6e56b7b
commit e4695b8281
62 changed files with 7147 additions and 27 deletions
+272
View File
@@ -0,0 +1,272 @@
# 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 используется собственное расширение этого проекта —
`toolchain/vscode-sprinter-debug`. Только оно знает формат source-debug
пакета, загрузку приложения через DSS, банки Sprinter и DAP-сессию MAME.
Microsoft C/C++ или clangd можно поставить дополнительно ради completion,
переходов по исходникам и подсветки. Оба анализируют код как близкий к
обычному C и не являются точной моделью SDCC: параметры `__naked`, ABI и
часть target-заголовков потребуют отдельных defines/configuration. Ошибки
реальной сборки всегда определяет `sprinter-cc`.
## Подготовка программы
Соберите приложение с полной картой:
```sh
pyenv exec make -C tests/hello SRC_DEBUG=1
```
Рядом с EXE появится `.sprinter-cc-hello/manifest.json`. При изменении C,
заголовка или опций `make` пересоберёт пакет по хэшу содержимого.
## Загрузка development-расширения
Из корня репозитория откройте VS Code с распакованным расширением:
```sh
code --extensionDevelopmentPath="$PWD/toolchain/vscode-sprinter-debug" "$PWD"
```
Если команда `code` не добавлена в `PATH`, на macOS этого проекта доступен
полный путь:
```sh
"/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code" \
--new-window \
--extensionDevelopmentPath="$PWD/toolchain/vscode-sprinter-debug" \
"$PWD"
```
Расширение в режиме `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`:
```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.
`dssTimeout` задаёт предельное время ожидания prompt (30 эмулируемых секунд).
`launchAt` можно задать как необязательную нижнюю границу времени запуска;
наличие prompt всё равно обязательно. Перед вводом сохраняется диагностический
снимок DSS.
Дополнительные файлы на floppy задаются массивом `data`, путь к другому MAME —
полем `mame`.
## Ручная проверка VS Code
В репозитории локально подготовлены два профиля `.vscode/launch.json`:
`Sprinter: hello (VS Code)` и `Sprinter: hello (VS Code + MAME debugger)`.
Файл `.vscode` намеренно игнорируется Git и не содержит личных путей.
1. Соберите `hello` командой из раздела «Подготовка программы» и запустите
Extension Development Host одной из команд выше.
2. Откройте `tests/hello/hello.c` и поставьте breakpoint на строке 31,
вызове `puts`.
3. В 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`.
4. Проверьте Call Stack, scope Registers и Debug Console. После Continue
должна сработать подтверждённая точка строки 31 по адресу `0x824b`.
5. На остановке проверьте F11 и F10. Курсор должен переходить только после
фактической остановки CPU, а не сразу после отправки команды. На строке 62
(`getchar`) нажмите F10, щёлкните окно Sprinter MAME и нажмите латинскую `x`:
выполнение должно перейти на строку 63. Пока программа ждёт клавишу,
кнопка Pause в VS Code должна останавливать CPU.
6. Завершите сессию кнопкой 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](sdbg-log-macros.md):
```c
#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-конфигурацию:
```json
"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. Для локального
checkout достаточно:
```sh
make mame-sdbg
```
Команда идемпотентно применяет
`toolchain/mame-patches/0001-sdbg-debugger-backend.patch`, инкрементально
собирает MAME и устанавливает `mame/v306/mame.arm`.
## Logpoints
Обычная команда VS Code **Add Logpoint…** работает без пересборки. В сообщении
разрешены литералы и простые подстановки:
```text
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`, затем:
```json
{
"type": "sprinter-mame",
"request": "attach",
"name": "Sprinter MAME: Attach",
"socket": "/tmp/sprinter-sdbg.sock"
}
```
Команды server и диагностического CLI приведены в
[mame-source-debug-status.md](mame-source-debug-status.md).
## Ограничения MVP
- Reset/restart отключён после найденного crash MAME 0.287 на старом Lua
callback. Session завершается штатным terminate.
- Локальные, backtrace, setVariable, watchpoints и чтение неотображённого
bank-data ещё не реализованы.
- Если одна инструкция имеет несколько C-маркеров, frame помечается
`[ambiguous]`; адаптер не выбирает строку молча.