# 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`. ## Подготовка программы Соберите приложение с полной картой: ```sh pyenv exec make -C tests/hello SRC_DEBUG=1 ``` Рядом с EXE появится `.sprinter-cc-hello/manifest.json`. При изменении C, заголовка или опций `make` пересоберёт пакет по хэшу содержимого. ## Загрузка development-расширения Из корня репозитория откройте VS Code с распакованным расширением: ```sh code --extensionDevelopmentPath="/путь/к/VSCode-Sprinter" "$PWD" ``` Если команда `code` не добавлена в `PATH`, на macOS этого проекта доступен полный путь: ```sh "/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`: ```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, штатный `machine:exit()` и реальная загрузка state при остановке в `main`: каждый раз DAP получает `terminated`, после load старые точки удалены. Закрытие SDL3-окна MAME вызывает тот же `schedule_exit()`, что `machine:exit()`; прямой UI-клик в пробнике не выполнялся. Эти проверки прошли с `sdbg` и опциональным `osx` debugger provider. `dssTimeout` задаёт предельное время ожидания prompt (30 эмулируемых секунд). `launchAt` можно задать как необязательную нижнюю границу времени запуска; наличие prompt всё равно обязательно. Перед вводом сохраняется диагностический снимок DSS. Для перенесённого SprPoP его workspace содержит локальный профиль F5: ```json { "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 и не содержит личных путей. 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. 5. На остановке проверьте F11 и F10. Курсор должен переходить только после фактической остановки CPU, а не сразу после отправки команды. На строке 62 (`getchar`) нажмите F10, щёлкните окно Sprinter MAME и нажмите латинскую `x`: выполнение должно перейти на строку 63. Пока программа ждёт клавишу, кнопка Pause в VS Code должна останавливать CPU. 6. Завершите сессию кнопкой Stop. Затем повторите профиль с `osx`: вместе с тем же VS Code-сеансом должно открыться штатное Cocoa-окно debugger MAME. Stop отправляет DAP `disconnect`; launcher завершает запущенный им MAME. Чтобы SDL3 не перехватывал `SIGTERM` в необрабатываемое MAME событие Quit, launcher задаёт этому процессу `SDL_NO_SIGNAL_HANDLERS=1`, если переменная не была задана вручную. Прямой `SIGTERM` и Stop проверены живыми пробниками на остановке в `main`; они не требуют отдельного патча 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_LOG](sdbg-log-macros.md): ```c #include 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. Исходники и рецепты теперь принадлежат самостоятельному проекту MAME: ```sh 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…** работает без пересборки. В сообщении разрешены литералы и простые подстановки: ```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]`; адаптер не выбирает строку молча.