830 lines
73 KiB
Markdown
830 lines
73 KiB
Markdown
# Отладка C-приложений Sprinter в MAME и VS Code
|
||
|
||
Редакция: **2026-09-14**, после технического ревью плана от 2026-09-13.
|
||
Статус: **ЧАСТИЧНАЯ РЕАЛИЗАЦИЯ**. Сборка/оффлайновая карта, транспорт,
|
||
live C-сессия, постоянный server, DAP и VS Code launch MVP реализованы; результаты —
|
||
[mame-source-debug-status.md](mame-source-debug-status.md).
|
||
Полный lifecycle/restart и расширенная отладка ещё не реализованы.
|
||
Основной маршрут включает CLI/MCP и VS Code через DAP;
|
||
GDB — самостоятельное расширение по отдельной потребности.
|
||
|
||
### Статус платформ
|
||
|
||
- **macOS** — полный текущий цикл build → DSS → MAME → DAP → VS Code
|
||
проверен живыми запусками. Доступны backend `sdbg` и совместный режим
|
||
VS Code + штатное окно debugger через `debugger: "osx"`.
|
||
- **Linux** — архитектура и используемые host-механизмы совместимы; для
|
||
штатного окна MAME выбирается `qt` либо `imgui`. Полный живой прогон на
|
||
Linux ещё требуется, поэтому статус остаётся экспериментальным.
|
||
- **Windows** — полный функционал пока **не поддерживается**. Значение
|
||
`debugger: "windows"` выбирает только штатное окно debugger MAME, но
|
||
текущие owner lock (`fcntl`), Unix domain socket, shell-команды запуска и
|
||
тесты рассчитаны на macOS/Linux. Нужны Windows-реализации блокировки и
|
||
локального RPC, переносимый launcher и отдельные end-to-end тесты. До
|
||
этого launch/attach из VS Code на native Windows не считаются рабочими.
|
||
|
||
Прежние фазы 0–5A/5B заменены этапами §10. IDE учитывается в архитектуре
|
||
с самого начала; прежняя «опциональная фаза 4» теперь распределена между
|
||
этапами IDE и расширенной диагностики. Это изменение состава плана,
|
||
а не разрешение выполнять ранее отложенную реализацию.
|
||
|
||
## 1. Цель и критерий доверия
|
||
|
||
Целевой цикл: редактирование C → сборка sprinter-cc → упаковка приложения
|
||
и данных → запуск MAME/DSS → остановка на main → отладка из VS Code,
|
||
CLI или MCP.
|
||
|
||
Планируемые возможности:
|
||
|
||
- Точки по функции/строке C, условия, счётчики попаданий, логпоинты без
|
||
пересборки, watchpoint на поддержанные объекты.
|
||
- Текущий исходник, сгенерированный `.asm`, реальные инструкции, регистры.
|
||
- Чтение/изменение глобалов и статиков с учётом типов, банков и окон.
|
||
- Шаг по инструкции и исходнику; затем next/stepOut, проверенные локальные
|
||
переменные и восстановление стека в поддержанных случаях.
|
||
- Структурированные логи для автотестов/IDE, комментарии в родном
|
||
дизассемблере; позднее покрытие и профилирование.
|
||
- Общая семантика адресов и одна сессия для всех интерфейсов.
|
||
|
||
**Критерий доверия:** неизвестная строка, неподтверждённая точка или
|
||
недоступная переменная лучше правдоподобного неверного результата.
|
||
Карта оптимизированного кода не обещает отдельную инструкцию для каждого
|
||
оператора C и наличие всех переменных в любой момент исполнения.
|
||
|
||
Связанные файлы: [mame-autotest.md](mame-autotest.md),
|
||
[sprinter-cc](../bin/sprinter-cc), [app.mk](../app.mk),
|
||
[mame_interactive.py](../toolchain/mame_interactive.py),
|
||
[bank.s](../runtime/bank.s), [crt0_banked.s](../runtime/crt0_banked.s).
|
||
MAME fork теперь самостоятельный проект: `MAME/plugins/mamebridge/init.lua`,
|
||
`MAME/src/mame_mcp.py`; среда запуска задаётся `MAME_HOME`, а исходники
|
||
fork приложению не нужны. Расширение VS Code находится в отдельном
|
||
`VSCode-Sprinter`; Python DAP backend остаётся в SDK.
|
||
|
||
## 2. Доказательства и пределы выводов
|
||
|
||
### 2.1 Результаты первоначальной разведки
|
||
|
||
Ниже сохранены результаты редакции 2026-09-13. При обновлении плана
|
||
сборочные эксперименты не повторялись. Перед реализацией их требуется
|
||
воспроизвести с сохранением репро, команд и версий.
|
||
|
||
| Эксперимент | Зафиксированный результат | Предел вывода |
|
||
|---|---|---|
|
||
| `sdcc --debug`, probe | `.exe` идентичен; `_CODE` 3354 → 3354 | Один пример, не гарантия для всех программ |
|
||
| `tests/banked`, huge, 3 TU, 2 банка | `.ihx` идентичен сборке без debug | rc=1 из-за C$; корректность карты не доказана |
|
||
| `.globl _dbg_x` и `_dbg_x:` в inline asm | Ошибка локальных меток `NNNNN$` в проверенных циклах/ветвлениях | Такую форму якоря не используем |
|
||
| `.globl _dbg_x` и `_dbg_x = .` | Релоцируемый символ; в probe байты идентичны, якорь у `ret` | Отдельно проверить влияние inline asm на оптимизацию |
|
||
| `;;SDBG x` | Код идентичен, адреса в `.map` нет | Для адреса нужен листинг/дополнительная привязка |
|
||
| SDCC 4.5.0 z80, `--out-fmt-elf` | Неизвестная опция | Готового ELF/DWARF этого target ожидать нельзя |
|
||
|
||
SDCC сам использует `sym = .`. Это подтверждает пригодность ассемблерной
|
||
формы, но не доказывает отсутствие влияния пользовательского inline asm
|
||
на оптимизатор C и peephole. Раздельно проверяем идентичность бинарника,
|
||
точность карты и скорость программы под debugger.
|
||
|
||
Примеры `.cdb` из первоначальной разведки:
|
||
|
||
```text
|
||
L:C$probe.c$18$3_0$48:8236 C-строка → адрес линковщика
|
||
L:A$probe$111:8236 строка .asm → адрес
|
||
L:G$main$0$0:8222 начало функции
|
||
L:Fprobe$counter$0_0$0:8E40 статик модуля
|
||
L:G$total$0_0$0:8E42 глобал
|
||
S:G$total$0_0$0({2}SI:S),E,0,0 описание типа
|
||
S:Lprobe.add$s$1_0$44({2}SI:S),R,0,0,[e,d] описание регистровой локальной
|
||
```
|
||
|
||
Парсер разбирает семейства `L:`, `S:`, `F:` и диагностирует неизвестные
|
||
записи. Построчная форма не делает семантику типов, scope, инлайнинга
|
||
и границ функций тривиальной. Семантику конечных адресов установить репро,
|
||
внутренние диапазоны нормализовать к `[start, end)`.
|
||
|
||
### 2.2 Проверено чтением текущих локальных исходников
|
||
|
||
- MAME поддерживает breakpoint с condition/action, printf/logerror/tracelog,
|
||
source/debugscript, comadd и debugger без окна (`-debugger none`).
|
||
`none` автоматически вызывает go() при остановке: он пригоден для
|
||
автономных logpoint actions, но не для ожидания интерактивных команд.
|
||
Подтверждено исходниками и живым репро; прототип использует osx debugger.
|
||
- `device_debug::compute_opcode_crc32` в `src/emu/debug/debugcpu.cpp`
|
||
считает CRC **одной инструкции**. Одинаковый `ret` по одному адресу
|
||
в двух банках имеет одинаковый ключ комментария. CRC не определяет банк.
|
||
- `execute_trace` в `src/emu/debug/debugcmd.cpp` включает трассировку CPU,
|
||
а не просто открывает файл для сообщений.
|
||
- Мост содержит `clog` и обслуживает `register_periodic` при stopped.
|
||
Комментарий у `do_step` прямо указывает: инструкция выполняется после
|
||
возврата callback, поэтому чтение PC сразу после step даст старый PC.
|
||
- `emu.symbol_table` в `luaengine_debug.cpp` создаёт отдельную таблицу;
|
||
готового symadd для консоли нет. C-имена разрешаем на своей стороне.
|
||
- В штатных debug views нет окна C-исходника. Для родного UI используем
|
||
комментарии, для полноценного исходника — IDE.
|
||
- sprinter-cc размещает банки по `(bank << 16) | 0xC000` в huge и
|
||
`(bank << 16) | 0x4000` в big. Есть `--bank-data`: банки содержат и данные.
|
||
- `_bank_pages[1..N]` заполняется crt0 при загрузке банков, а не до entry.
|
||
Драйвер экспортирует PG0..PG3 и другие состояния отображения.
|
||
- `bootstrap_r/w` в sprinter.cpp при обычном исполнении перенаправляют
|
||
логический адрес в `0x10000 | addr`; загрузочный режим отличается.
|
||
Адрес CPU и адрес пространства MAME нельзя смешивать.
|
||
|
||
### 2.3 Пакет доказательств этапа 0
|
||
|
||
Сохранить небольшие исходники-репро, команды, полные диагностики,
|
||
версии SDCC/ассемблера/линкера, commit MAME и патчи, хеши бинарников,
|
||
выбранные фрагменты `.asm/.map/.cdb`, протокол живой проверки MAME.
|
||
Большие артефакты допускается хранить вне Git с командой воспроизведения.
|
||
Ссылки на меняющиеся номера строк MAME заменять именами функций и
|
||
закреплённой ревизией в отчёте эксперимента.
|
||
|
||
## 3. Реестр решений: что заменено и почему
|
||
|
||
| ID | Риск / прежнее предложение | Решение и обоснование |
|
||
|---|---|---|
|
||
| R01 | Игнорировать rc=1 при C$, если есть `.ihx` | Устранить конфликт debug-имён; исправность кода не доказывает карту, старый `.ihx` может пережить ошибку |
|
||
| R02 | «Полная карта, ноль влияния» | Раздельные регрессии бинарника, карты и overhead; гарантия ограничена проверенными конфигурациями |
|
||
| R03 | Адрес — одно число, банки только у кода | Типизированные адреса и snapshot отображения, включая bank-data |
|
||
| R04 | Первый адрес строки / следующая строка вместо якоря | Все доказанные позиции, фактическое разрешение и unverified; другой путь исполнения не заменяет удалённую точку |
|
||
| R05 | Арминг по совпадению PC с entry | Проверка образа, готовности runtime/банков и выхода; DSS использует те же адреса |
|
||
| R06 | Цикл step внутри Lua | Асинхронный автомат с лимитами/отменой: шаг исполняется после callback |
|
||
| R07 | Разные ID файлов достаточно для нескольких клиентов | Общая сессия и арбитраж; ID не устраняют гонки run/stop |
|
||
| R08 | Безусловный `g` в логпоинте, clear-all | Реестр владельцев и диспетчер попаданий; логи не отменяют остановку |
|
||
| R09 | `trace` по умолчанию для логов | Отдельный журнал; instruction trace имеет другое назначение и стоимость |
|
||
| R10 | Формат C printf можно передать MAME | Ограниченная грамматика, типы, знак, длина строк и CP866; форматы различаются |
|
||
| R11 | CRC скрывает чужой банк | Сначала резидентные комментарии; затем обновление при remap или расширение ключа |
|
||
| R12 | Каталог сборки достаточен для attach | Manifest, хеши и фиксированный пакет сессии; исключить смешение сборок |
|
||
| R13 | Все DAP-запросы сразу, стек эвристикой | Честные capabilities и один достоверный frame в MVP; сложные функции отдельно |
|
||
| R14 | GDB не имеет логов/банков | Учесть dprintf/overlays; DAP выбран за модель Sprinter и общую сессию |
|
||
| R15 | Сокет автоматически ускорит шаг | Сначала измерить RTT и execution latency; callback сокетом не исправляется |
|
||
| R16 | Ручные правки в игнорируемом mame/ | Версионируемые исходники/патчи и установщик с проверкой расхождений |
|
||
| R17 | Библиотеки/локальные автоматически следуют из CDB | Эксперимент архивной линковки и доказанные location ranges |
|
||
| R18 | Один launch.json завершает интеграцию | Сборка, диагностики, язык, упаковка данных, запуск DSS и повторный F5 |
|
||
| R19 | debugger none держит stopped для IDE | В none wait_for_debugger вызывает go(); использовать родной debugger, для режима без окон реализовать отдельный backend ожидания |
|
||
| R20 | `debugger: "windows"` означает поддержку всей цепочки на Windows | Разделить MAME provider и host-инструменты: Windows provider существует, но Python/DAP launch/attach остаются неподдержанными до замены `fcntl`/Unix sockets, переноса launcher и живых тестов |
|
||
| R21 | Короткий sleep в `sdbg` достаточно для окна MAME | При stopped CPU обычный frame loop не обрабатывает события GUI, и macOS помечает приложение «не отвечает». В `wait_for_debugger` периодически вызывать event pump выбранного OSD (SDL3: `input_update` + `process_events`, native macOS: штатный poll), ограничив частоту; проверить Cmd-Tab/Dock при длительной остановке и сохранение DAP/клавиатурного ввода |
|
||
| R22 | CDB pointee type достаточен для выбора строки или скаляра | SDCC 4.5 кодирует проверенные `char *` и `uint8_t *` одинаково (`DG,SC:U`). В финальном debug-пакете сохранять исходный declared type/typedef chain, сверять его с CDB и размером; при неопределённости явно `unavailable` либо запросить типовую аннотацию. Отдельный fixture должен доказать, что `char *` читается как строка, а `int8_t *`/`uint8_t *` — как один 8-битный объект |
|
||
|
||
## 4. Архитектура, сборка и пакет
|
||
|
||
```text
|
||
sprinter-cc → .exe + отладочный пакет + manifest
|
||
│
|
||
sdbg: карта и типы
|
||
│
|
||
CLI ────────┐ │
|
||
MCP ────────┼──→ общая сессия отладки ──→ мост MAME ──→ CPU/debugger
|
||
VS Code/DAP ┘ состояние, точки,
|
||
события, загрузка
|
||
```
|
||
|
||
### 4.1 Ответственность компонентов
|
||
|
||
- `toolchain/sdbg.py` — публичный CLI; реализацию разделить на модули
|
||
пакета/парсера, адресов, типов, точек, сессии и транспорта по мере роста.
|
||
- Карта — чистые преобразования артефактов без команд MAME.
|
||
- Общая сессия — процесс для подключения CLI/MCP/DAP, хранит build ID,
|
||
generation, состояние, владельца управления и реестр точек.
|
||
- Мост — низкоуровневые действия, согласованные снимки, асинхронные
|
||
операции и события; второго парсера CDB в Lua нет.
|
||
- DAP/MCP — тонкие адаптеры; существующие input/screenshots сохраняются,
|
||
команды изменения исполнения проходят арбитраж.
|
||
- `.dbgs` — ограниченный автономный экспорт, не второй менеджер сессии.
|
||
Неподдержанную семантику экспорт отклоняет с объяснением.
|
||
|
||
### 4.2 Флаги и manifest
|
||
|
||
`--src-debug` включает debug для всех пользовательских TU;
|
||
повторяемый `--src-debug-file FILE` — для выбранных. Режимы взаимоисключающие,
|
||
неизвестный FILE — ошибка. Необязательный аргумент прежнего флага убран,
|
||
поскольку он неоднозначен рядом с позиционными `.c`.
|
||
`--debug` остаётся DEBUG_RT. В app.mk: `SRC_DEBUG := 1` либо
|
||
`SRC_DEBUG_FILES := a.c b.c`. Обычные сборки прежние; профиль IDE явно
|
||
включает карту, а не меняет defaults всего проекта.
|
||
|
||
Пакет в `.sprinter-cc-<name>/`: `.cdb/.map/.noi/.ihx/.asm`, нужные листинги,
|
||
manifest и индекс `.sdbg.json`. Конфигурация и содержимое входов входят
|
||
в ключ пересборки: смена debug/fast/safe/memory не оставляет stale artifacts.
|
||
Параллельные сборки одного output сериализуются или используют разные
|
||
каталоги. Пакет публикуется только после успешных проверок, атомарно.
|
||
|
||
Manifest: schema version, build ID, хеш `.exe` и артефактов, версии
|
||
инструментов, команды/флаги, библиотеки, TU и исходники/заголовки с хешами,
|
||
соответствующие `.asm`, entry, секции, окна, банки и полнота debug-покрытия.
|
||
Адрес `>=0x10000` не объявляется банком без проверки секции.
|
||
Неподдержанное размещение диагностируется, не угадывается по имени режима.
|
||
|
||
Пути относительно корня сборки, уникальный TU ID, поддержка одинаковых
|
||
basename и source path mapping при переносе проекта. IDE проверяет хеши;
|
||
при расхождении показывает stale source или сохранённый снимок через DAP
|
||
source. Сессия фиксирует пакет: новая сборка не меняет карту старого образа.
|
||
|
||
### 4.3 Ошибки CDB
|
||
|
||
Основное решение R01: проверить уникализацию отладочных имён по TU до
|
||
линковки в `.asm` с согласованным преобразованием всех связанных CDB-записей.
|
||
Если формат не позволяет надёжный внешний проход, подготовить патч SDCC.
|
||
Не изменять публичные C/asm-символы и инструкции. Выбор способа — результат
|
||
репро этапа 0, а не утверждение, что простое переименование уже достаточно.
|
||
|
||
До исправления допустим пер-модульный режим только при успешной линковке;
|
||
manifest перечисляет отсутствующие TU. Неполный выбранный набор отличается
|
||
от повреждённой карты. Повреждённая карта не публикуется.
|
||
Автоматического превращения rc=1 в успех по наличию `.ihx` не будет.
|
||
Неуспешные артефакты можно сохранять для исследования отдельно, но обычный
|
||
attach их не принимает. Полные диагностики сохраняются, включая pipefail.
|
||
|
||
### 4.4 Библиотеки
|
||
|
||
Проверить извлечение `S/F/L` и путей из архивов libc/libbgi. После успеха
|
||
добавить явный режим debug-артефактов библиотек с отдельным ключом кэша,
|
||
сохранив fast/safe и «одна функция — один модуль». Проверить DCE, типы,
|
||
строки, отсутствие коллизий и идентичность кода. До этого библиотечный
|
||
код доступен как asm/символы без выдуманных C-строк.
|
||
|
||
## 5. Адреса, исходники, переменные и выражения
|
||
|
||
### 5.1 Адресная модель
|
||
|
||
`CodeLocation/DataLocation`: build ID, секция, адрес линковщика, logical
|
||
CPU address, bank ID приложения при наличии, окно и offset.
|
||
Физическая страница — свойство сессии, не константа пакета.
|
||
`MappingSnapshot`: generation остановки, PC/SP, регистры, PG0..PG3,
|
||
остальные нужные биты отображения, готовность `_bank_pages`.
|
||
|
||
```text
|
||
line_locations(source_id, line, function_id=None) → список CodeLocation
|
||
resolve_pc(pc, mapping_snapshot) → SourceLocation | Unknown
|
||
asm_at(code_location) → номер/текст asm и происхождение
|
||
function_at(code_location) → функция | Unknown
|
||
resolve_symbol(name, module_id=None) → Symbol | Ambiguous | Unknown
|
||
read_variable(symbol, snapshot) → TypedValue | Unavailable
|
||
```
|
||
|
||
Backend различает logical CPU memory, пространство MAME и physical RAM.
|
||
Преобразование `0x10000 | addr` инкапсулировано в драйверном backend.
|
||
Bootstrap/configuration имеет отдельную семантику и не допускает обычный
|
||
attach приложения.
|
||
|
||
Банковая точка проверяет страницу окна против `_bank_pages[N]` после
|
||
подтверждения таблицы и резидентного контекста приложения. huge — W3,
|
||
big — W1. Проверить достаточность PGn с учётом CNF и других битов драйвера;
|
||
простое равенство — базовый случай, не доказанный полный контракт.
|
||
Manual-размещения допускаются по фактической карте и поддержке backend.
|
||
|
||
### 5.2 Разрешение строк
|
||
|
||
Хранить все позиции строки и диапазоны инструкции/функции/секции.
|
||
Breakpoint строки по умолчанию покрывает все доказанные позиции исполнения;
|
||
дополнительно можно выбрать функцию/экземпляр. Resolver возвращает реальные
|
||
адреса, фактическую строку и причины неоднозначности/переноса.
|
||
Для пустой или удалённой строки можно предложить ближайшую позицию в том
|
||
же контексте, но не подтверждать её молча как точное совпадение.
|
||
Без допустимого соответствия — unverified с объяснением.
|
||
|
||
addr2line не распространяет предыдущую метку через конец функции, дыру,
|
||
секцию или банк. Inline-экземпляры и неоднозначности сохраняются, вместо
|
||
выбора первого TU. Привязка точного макроса рассматривается отдельно (§7.2).
|
||
|
||
### 5.3 Типы, память и watchpoint
|
||
|
||
MVP: доказанные целочисленные типы, указатели, глобалы/статики. Одинаковые
|
||
имена требуют квалификации модулем. Затем массивы, структуры, битовые поля
|
||
и другие типы с fixtures. Размеры, знак, byte order и ABI задаёт target
|
||
SDCC, не хост. Неподдержанный тип отображается raw bytes с диагностикой.
|
||
|
||
Регистры/память/mapping читаются в одной остановке, пакетные запросы
|
||
привязаны к generation. После resume старые handles и snapshots недействительны.
|
||
Запись требует stopped, актуальную generation, проверку диапазона/типа
|
||
и известного отображения. Порты показывать по экспортированному состоянию;
|
||
отладочные чтения отключают side effects там, где backend это поддерживает.
|
||
|
||
Неотображённый банк читается через проверенный доступ к physical RAM без
|
||
переключения страниц приложения; до реализации — Unavailable. Один
|
||
16-битный указатель не содержит достаточного bank ID: его нельзя выдумывать.
|
||
Чтение/запись через границу окна обрабатывается явно.
|
||
|
||
Для watchpoint экспериментом установить пространство/alias реального пути
|
||
CPU и момент остановки до/после записи. PC-источник, старое и новое значение
|
||
показывать лишь при достоверном получении: текущий PC может отличаться от
|
||
адреса записавшей инструкции. Банковый watchpoint учитывает mapping;
|
||
до доказательства не объявлять его поддержанным.
|
||
|
||
### 5.4 Выражения
|
||
|
||
Один парсер для condition/logMessage/evaluate/watches: имена, integer
|
||
literals, `$`-регистры, ограниченные арифметические/битовые/сравнительные
|
||
операции. Документировать грамматику, приоритеты, знаковость и ошибки.
|
||
Поля/индексы/разыменование добавлять по поддержке типов. По умолчанию
|
||
evaluate без присваиваний, вызовов C и других побочных эффектов.
|
||
|
||
Raw MAME expressions/commands — отдельный явно обозначенный режим;
|
||
это не C. Изменяющие команды также требуют управления и синхронизации.
|
||
Команды генерировать из проверенного AST с экранированием строк/путей,
|
||
не конкатенацией произвольного текста в action.
|
||
|
||
## 6. Общая сессия и управление исполнением
|
||
|
||
### 6.1 Жизненный цикл
|
||
|
||
```text
|
||
disconnected → waiting_load → verifying_image → runtime_initializing
|
||
→ stopped ↔ running
|
||
→ exited
|
||
reset / state load / потеря связи → invalidated → повторная проверка
|
||
```
|
||
|
||
Совпадение PC с entry — только кандидат загрузки. Подтверждение сочетает
|
||
build ID выбранного файла, протокол запуска и сравнение неизменяемых
|
||
участков RAM по карте. Хеш `.exe` на хосте сам по себе RAM не проверяет.
|
||
Изменяемые crt0 данные/таблицы исключаются; сигнатуры и точки проверки
|
||
определяются для конкретного runtime.
|
||
|
||
На entry активируются только доказанные резидентные точки. Банковые —
|
||
после загрузки и проверки `_bank_pages`. На main runtime должен быть готов.
|
||
Отладка crt0 — отдельный режим с asm и постепенным появлением областей.
|
||
Частичная ошибка загрузки не переводит сессию в ready.
|
||
|
||
Выход через runtime/ESTEX и возврат в DSS деактивируют точки приложения.
|
||
Проверить обычный/аварийный выход и обход штатного exit. Если контекст
|
||
невозможно уверенно распознать — invalidated, а не продолжение со старой картой.
|
||
Reset/state load/restart сбрасывают mapping, temporary points, handles,
|
||
generation. Attach к работающей программе сначала останавливает CPU,
|
||
проверяет образ и runtime; неизвестную сборку не принимает молча.
|
||
|
||
### 6.2 Управление и реестр точек
|
||
|
||
Один клиент владеет run/step/write/input; остальные наблюдают согласованные
|
||
данные. Передача управления явная. Потеря клиента снимает lease и отменяет
|
||
его незавершённые операции согласно политике сессии. Screenshots доступны
|
||
наблюдателям; ввод влияет на приложение и арбитрируется. Изменения run/stop
|
||
из родного UI MAME отражаются событиями.
|
||
|
||
Реестр: logical breakpoint ID, MAME IDs, owner, build ID/generation,
|
||
вид (user/log/temporary/service), адреса/условия. Clear-all ограничен owner.
|
||
Повторная загрузка файла точек заменяет его набор, не дублирует его.
|
||
|
||
Совпавшие точки обслуживает диспетчер: вычислить условия, записать логи,
|
||
собрать причины остановки, продолжить только если их нет. User breakpoint,
|
||
pause и ошибка шага имеют приоритет над автоматическим `g`.
|
||
Изменения точек в родном UI требуют сверки; если backend не может
|
||
гарантировать совместное управление, сообщить конфликт.
|
||
|
||
### 6.3 Протокол и транспорт
|
||
|
||
Protocol version/capabilities, session ID, request ID, generation,
|
||
ответы и упорядоченные события stop/run/output/reset/exit/error.
|
||
Ответ на установку шага значит «принят», не «CPU уже шагнул».
|
||
Нужны timeout/cancel, snapshot/varbatch, EOF/disconnect и защита от повторного
|
||
исполнения: write/continue после timeout не повторяется вслепую.
|
||
|
||
Сначала измерить файловый IPC: RTT, pause→snapshot, step→stopped,
|
||
periodic при running/stopped. Файловый backend: отдельный каталог сессии,
|
||
один писатель backend, атомарная публикация запросов/ответов, очистка stale
|
||
и журнал событий с курсором. Разделение диапазонов ID независимых MCP
|
||
заменяется одним владельцем backend: оно не решало гонки состояния.
|
||
|
||
Сокет реализует тот же протокол при подтверждённом выигрыше. MAME имеет
|
||
пример `emu.file` socket в plugins/gdbstub, но неблокирующий ввод/вывод
|
||
при stop проверяется отдельно. Framing, частичные сообщения, лимит очереди,
|
||
reconnect, loopback по умолчанию обязательны. Callback не ждёт клиента
|
||
блокирующим чтением. Сокет не исправляет задержку исполнения инструкции.
|
||
|
||
### 6.4 Шаги
|
||
|
||
Сначала надёжный instruction step. Source-step — асинхронный автомат:
|
||
задать действие, вернуть управление MAME, дождаться фактической остановки,
|
||
получить snapshot, решить следующий шаг. Для скорости применить temporary
|
||
breakpoints на доказанных границах либо C++ hook, если Lua periodic
|
||
недостаточен по измерениям.
|
||
|
||
StepIn идёт к следующей доступной позиции исходника с заходом в вызов.
|
||
Next обходит вызов только при распознанной семантике; stepOut требует
|
||
достоверного контекста возврата. Сравнивать source/TU/function/bank и
|
||
исполняемую позицию, не один номер строки. Повтор строки в цикле не должен
|
||
вечно ждать смены номера.
|
||
|
||
Составной шаг имеет предел инструкций/wall time, отмену и итоговую причину.
|
||
Код без исходников, HALT, ISR, рекурсия, tail call, bank trampolines,
|
||
BIOS rst 08 и ESTEX rst 10 имеют явную политику. Для неизвестного случая
|
||
допустим отказ или переход к asm с объяснением; stepIn не выдаётся за next.
|
||
Системный код пропускается только при безопасном способе дождаться возврата.
|
||
|
||
## 7. Логи, макросы и расширенная диагностика
|
||
|
||
### 7.1 Внешние точки и журнал
|
||
|
||
Основной путь — версионируемый внешний файл: ID, source/function/location,
|
||
condition, hit condition, сообщение и выражения. Тот же resolver и реестр,
|
||
что у VS Code; пересборка не требуется. Адреса пересчитываются для новой
|
||
сборки из сохранённой исходной привязки. DAP logMessage переводится в эту
|
||
модель, не образует отдельный движок.
|
||
|
||
Журнал: sequence, build/session ID, host time, доступное emulated time,
|
||
PC, bank/page, location, tag и типизированные значения. JSONL — автотестам,
|
||
текст — CLI, DAP output — IDE, курсор — MCP. Ограничить объём/частоту,
|
||
предусмотреть ротацию или bounded buffer, счётчик потерь, flush на stop/exit.
|
||
clog остаётся для сообщений MAME, но хвост консоли не считается полным
|
||
надёжным журналом приложения.
|
||
|
||
Форматирование — задача финального этапа §10, а не свойство текущего
|
||
`{name}`. Целевой ограниченный набор включает decimal/hex, ширину,
|
||
символ и строку; MAME `%d/%x/%X/%c/%s/%%` может быть форматом совместимого
|
||
экспорта, но не передаётся напрямую как C-макрос. Принято это разделение,
|
||
потому что formatter обязан проверять тип/знак, размер, pointer против
|
||
массива, границы и кодировку, а CDB не отличает `char *` от `uint8_t *`
|
||
(R22). Для строк нужны bounded read до NUL и проверка CP866→UTF-8;
|
||
raw bytes имеют отдельное представление. Значения собираются до resume.
|
||
Горячие логи могут исполняться в мосте по скомпилированному описанию;
|
||
стоимость измеряется отдельно.
|
||
|
||
Console/logerror допустимы для совместимого экспорта. Trace/tracelog —
|
||
явный режим instruction tracing, не default для логов. Экспорт `.dbgs`
|
||
указывает ограничения и не ставит безусловный resume в общей сессии.
|
||
|
||
### 7.2 SDBG_LOG и SDBG_LOGIF
|
||
|
||
Практический синтаксис, типы, ограничения и примеры описаны в отдельном
|
||
[руководстве по SDBG_LOG](sdbg-log-macros.md). Этот документ — источник
|
||
истины для пользовательского контракта макросов: при изменении грамматики,
|
||
типов, регистров, чтения указателей или вывода в MAME/DAP обновлять его
|
||
вместе с кодом и проверками, затем синхронизировать краткие описания здесь.
|
||
|
||
Первый поддержанный вариант реализован в `<sdbg.h>`: `SDBG_LOG(tag,
|
||
"total={total}")` и `SDBG_LOGIF(tag, flag, "total={total}")`.
|
||
Обычная сборка получает `((void)0)`; выбранный `--src-debug` TU —
|
||
символ `sym = .` без инструкции. `tag` обязан быть уникальным в TU,
|
||
условие пока только имя поддержанной global/static переменной, а
|
||
`{name}` соответствует ограниченной грамматике DAP logMessage.
|
||
Сборщик извлекает только активные вызовы из отдельного препроцессорного
|
||
metadata-прохода, проверяет единственный asm-якорь и linked address.
|
||
Автоматически установленный logpoint пишет в журнал debugger MAME и DAP
|
||
`output`, затем продолжает CPU; совпавшая обычная точка сохраняет остановку.
|
||
Событийный буфер DAP ограничен 1024 записями и сообщает разрыв курсора с
|
||
числом пропусков; overhead горячих macro-logpoints ещё требуется измерить.
|
||
Десятичный/hex formatter и чтение типизированного значения по указателю
|
||
отложены до финального этапа §10: сначала нужны доказанные тип, контекст
|
||
банка, границы и безопасное чтение памяти, иначе лог может показать неверный
|
||
объект. Текущий `{name}` выводит десятичное число и никогда не разыменовывает
|
||
указатель.
|
||
Если якорь не совпал с началом доказанной инструкции, он помечен unverified
|
||
и не активируется. В проверенном fixture EXE совпал побайтово с обычной
|
||
сборкой; это доказательство конкретного случая, не гарантия для всех
|
||
оптимизаций SDCC и inline asm.
|
||
|
||
Макросы остаются расширением для авторских устойчивых точек. Без
|
||
anchors — `((void)0)`, с anchors — символ `sym = .` без инструкции.
|
||
Аргументы логирования не исполняются приложением и не имеют C-побочных
|
||
эффектов; это явно документируется, чтобы counter++ не считался кодом C.
|
||
|
||
Точная точка требует найденного и проверенного якоря. Без него — missing/
|
||
unverified; приблизительная привязка выбирается отдельно с показом
|
||
фактической позиции. Якорь задаёт машинную границу перед инструкцией,
|
||
но не гарантирует материализацию локальной или порядок всех вычислений C.
|
||
|
||
ID включает TU и tag. Повторные/inline экземпляры имеют отдельные ID либо
|
||
отклоняются до линковки; уникальный tag в тексте программы не предотвращает
|
||
повторное разворачивание inline.
|
||
|
||
Реализованный препроцессорный проход учитывает активные #if, include,
|
||
wrappers, многострочные вызовы, комментарии и склейку литералов; связывает
|
||
SDBG-описания с единственным реально собранным якорем. Текущая грамматика
|
||
намеренно отклоняет вычисляемый tag/condition, нестроковое сообщение и
|
||
повторные tag; поддержку сложных C-выражений и локальных добавлять только
|
||
после доказанного location range и ограниченного парсера.
|
||
|
||
Приёмка сравнивает полные бинарники с/без anchors и без макроса на циклах,
|
||
ветках, inline и multi-TU. Если оптимизация меняется, внешние логпоинты
|
||
остаются путём без пересборки, anchors обозначаются инструментированным
|
||
режимом с измеренной дельтой. Без доказательства «ноль влияния» не обещать.
|
||
При добавлении sdbg.h обновить публичный справочник API.
|
||
|
||
### 7.3 Комментарии, стек, локальные, покрытие
|
||
|
||
Резидентные comadd ставятся после проверки образа и обновляются при смене
|
||
сборки. Банковые включаются после поддержки remap-обновления с удалением
|
||
старых либо патча ключа `(address, page/context, opcode CRC)`.
|
||
Оффлайновый cmt допустим для проверенного резидентного образа; CRC из ihx
|
||
не решает банковые коллизии. Учитывать владение пользовательскими комментариями.
|
||
|
||
Backtrace сначала даёт текущий достоверный frame. Затем поддержать
|
||
распознанные прологи/эпилоги, SDCC __sdcccall(1), callee-pops, IX,
|
||
trampolines и ISR. Сканирование стека на похожие адреса — отдельная
|
||
маркированная эвристика, не основание для stepOut/локальных. Возможная
|
||
альтернатива — история call/return, но attach посреди исполнения не знает
|
||
прошлого, а нестандартные переходы требуют инвалидирования истории.
|
||
|
||
Локальные доступны лишь при доказанном location range (регистр/стек/память).
|
||
Лексический scope не равен live-range. Неизвестные/оптимизированные
|
||
значения — unavailable/optimized out. CFI/location lists из asm — отдельное
|
||
исследование, а не автоматически доступная возможность CDB.
|
||
|
||
Покрытие различает посещение адреса, число исполнений и время. Trackpc
|
||
сам по себе не даёт времени и требует проверки банковых коллизий.
|
||
Ключ покрытия включает build ID/bank/location, знаменатель — доказанные
|
||
исполняемые позиции. Профиль использует измеренный источник cycles/time
|
||
или sampling с указанной погрешностью и overhead.
|
||
|
||
## 8. MCP, DAP и полный цикл VS Code
|
||
|
||
### 8.1 CLI/MCP
|
||
|
||
Интерфейсы: attach/status/detach, where, disassemble_src, break_at,
|
||
clear_owned_breakpoints, read_var/write_var, watch_var, registers/memory,
|
||
step_instruction/step_in/next/step_out/pause/continue, logs с курсором,
|
||
console_log и загрузка набора точек. Возвращать build ID/generation,
|
||
фактическое разрешение и ограничения там, где они нужны для интерпретации.
|
||
Неподдержанное — явная ошибка. Старые низкоуровневые команды интегрируются
|
||
в арбитраж, а не обходят его.
|
||
|
||
### 8.2 DAP MVP и развитие
|
||
|
||
MVP: initialize, attach, configurationDone, disconnect, setBreakpoints,
|
||
setFunctionBreakpoints, threads, stackTrace, scopes, variables,
|
||
ограниченный evaluate, continue/pause, проверенный instruction step,
|
||
disassemble и logMessage/output. Один Z80 — один thread; stackTrace
|
||
сначала содержит один текущий frame. Source-step, если ещё не готов,
|
||
отвечает отказом с объяснением; instruction granularity включается
|
||
только при реализации.
|
||
|
||
Соблюдать initialize→initialized→configurationDone; stopped содержит
|
||
причину/ID точек, continued сообщает внешний resume, breakpoint — новое
|
||
разрешение. Terminated означает конец сессии; exited выдаётся только при
|
||
установленном завершении приложения и достоверном коде выхода.
|
||
SetBreakpoints заменяет набор данного source, включая очистку пустым
|
||
списком, а не добавляет точки бесконечно.
|
||
|
||
Capabilities отражают реальную поддержку. Расширения: breakpointLocations,
|
||
instruction/conditional/hit breakpoints, readMemory/writeMemory, setVariable,
|
||
dataBreakpointInfo вместе с setDataBreakpoints, source-step/next/stepOut,
|
||
cancel и сложные типы. Frame/variables references привязаны к остановке,
|
||
memory references различают банк/пространство. Предусмотреть пагинацию.
|
||
Stdout адаптера содержит только DAP, диагностики — stderr/log.
|
||
|
||
### 8.3 Разработка и запуск из редактора
|
||
|
||
**Архитектурное решение:** Sprinter-специфичный цикл реализуется собственным
|
||
расширением `VSCode-Sprinter`. Готовые C/C++ или clangd можно
|
||
использовать для подсветки, completion и навигации, а VS Code Tasks и Debug UI
|
||
— как стандартные интерфейсы. Они не знают ABI SDCC/Z80, пакет
|
||
`.sprinter-cc-*`, DSS, банковую адресацию и протокол MAME, поэтому не могут
|
||
заменить project extension и не считаются источником истины для диагностики
|
||
компилятора или отладки.
|
||
|
||
Мини-расширение уже содержит `contributes.debuggers`, точки для C, схему и
|
||
шаблоны конфигурации. Следующий уровень переносит в него build/run/debug
|
||
оркестрацию: `DebugConfigurationProvider` проверяет конфигурацию и при
|
||
необходимости запускает выбранную build task; команды расширения выбирают
|
||
target/profile/EXTRA_DATA; TaskProvider и problem matcher переводят ошибки
|
||
SDCC/линкера в Problems. Сборку всё равно выполняют `make`/`sprinter-cc`, а
|
||
запуск — общий Python launcher: расширение не дублирует их логику.
|
||
|
||
Установка через VSIX, разработка через Extension Development Host. Наличие
|
||
VS Code, pyenv Python и необязательного языкового расширения проверяется при
|
||
настройке, а не считается постоянным свойством конкретной машины.
|
||
|
||
Включить в поставку:
|
||
|
||
- TaskProvider и/или tasks.json: make/sprinter-cc и problem matcher
|
||
SDCC/ассемблера/линкера; debug не стартует после неуспешной сборки.
|
||
- Языковые настройки: include/defines/gfx/safe/memory из той же сборочной
|
||
конфигурации. SDCC-расширения вроде __naked требуют совместимых редакторских
|
||
определений; generic clang/GCC-анализ не равен полному анализу SDCC.
|
||
- launch.json: приложение/пакет, MAME/ROM/media, EXTRA_DATA, timeout,
|
||
stopOnEntry/stopOnMain, source mappings; без личных абсолютных путей.
|
||
- Launch: успешная сборка → упаковка → MAME → загрузка DSS → проверка образа
|
||
→ готовность runtime → main. Переиспользовать текущую упаковку/launcher,
|
||
не дублировать сценарии ввода клавиатуры.
|
||
- Attach к подготовленной сессии без второго MAME; restart с новым build ID
|
||
и пересчётом точек после новой сборки/загрузки.
|
||
- Disconnect не закрывает чужой MAME; terminate запущенного адаптером
|
||
процесса имеет явную политику. Выход приложения отличается от выхода
|
||
MAME. Ошибки сборки/ROM/media показываются в редакторе с причиной.
|
||
|
||
## 9. Альтернативный маршрут GDB
|
||
|
||
DAP выбран за прямую модель Sprinter, reuse сессии MCP и отсутствие
|
||
обязательного DWARF-писателя/target GDB. GDB полезен для его скриптов и
|
||
фронтендов; маршрут сохранён как самостоятельное расширение.
|
||
|
||
Первоначальная разведка: OSD gdbstub знает z84c015, но объявляет mame.z80
|
||
и другой порядок регистров; исследованный GDB ожидает org.gnu.gdb.z80.cpu
|
||
и набор с объединённым IR. Повторить проверку выбранной пары версий.
|
||
Lua gdbstub с i386-картой не является готовым backend Z80. Оценка патча
|
||
«20 строк» заменяется проверкой полного контракта.
|
||
|
||
1. Собрать/закрепить target GDB; проверить handshake. Патч feature/регистров
|
||
и IR проходит round-trip всех регистров, включая альтернативные,
|
||
byte order и семантику R.
|
||
2. Проверить RSP step/continue/interrupt, logical memory read/write,
|
||
break/watch и отсутствие патчинга инструкций. Разведка сообщала, что
|
||
Z0/Z2..Z4 используют точки MAME; включить это в регрессию.
|
||
3. sdbg_elf.py на общей карте: ELF32 EM_Z80, секции по реальному размещению,
|
||
symtab, debug_line, затем CU/subprogram/variable и базовые типы DWARF.
|
||
Дыры/банки не склеивать в ложный text. Сначала binutils/GDB offline,
|
||
затем live. ELF с symtab остаётся полезным самостоятельным экспортом.
|
||
4. debug_frame/CFI и locations — только по доказанным данным; строки DWARF
|
||
сами по себе не исправляют unwinder SDCC.
|
||
5. VS Code cppdbg/target GDB/app.elf — отдельная MI-конфигурация. Банки
|
||
сначала вручную/ограниченно; затем исследовать overlays и синхронизацию
|
||
с runtime, а не обещать автоматическую поддержку.
|
||
|
||
GDB имеет dprintf без пересборки и поддержку overlays. Редакторский
|
||
logMessage зависит от frontend; overlays требуют интеграции Sprinter.
|
||
Поэтому прежние «логпоинтов нет» и «банки невозможны» заменены конкретными
|
||
ограничениями. Области Registers/Ports также возможны поверх GDB,
|
||
но в DAP непосредственно используют нашу target-модель.
|
||
|
||
RSP и DAP не управляют исполнением независимо одновременно. Режим GDB
|
||
получает отдельного владельца; наблюдающие функции MCP проверяются отдельно.
|
||
Общую карту можно переиспользовать без совместного run/stop.
|
||
|
||
## 10. Этапы реализации и условия перехода
|
||
|
||
Календарные оценки прежнего плана считаются оценками демонстрационного
|
||
прототипа: надёжность lifecycle/банков/DAP ими не покрыта. После этапа 0
|
||
оценить каждый этап по репро и измерениям, раздельно прототип, поддержанный
|
||
выпуск и исследовательские функции. Проход этапа определяется приёмкой.
|
||
|
||
### Этап 0 — проверка предположений
|
||
|
||
Сохранить §2.3; воспроизвести общий inline в двух вызываемых TU и проверить
|
||
адреса обоих экземпляров/решение R01. Проверить anchors, huge/big/bank-data,
|
||
библиотечную CDB, memory aliases/watchpoint PC, lifecycle crt0, async step,
|
||
задержки моста и CRC банков. Закрепить версии и baseline производительности.
|
||
|
||
**Выход:** доказанный способ R01 либо явный ограниченный пер-модульный
|
||
режим; таблица проверенных/непроверенных возможностей. Общую карту не
|
||
объявлять готовой при скрытом конфликте.
|
||
|
||
### Этап 1 — сборка, manifest и карта
|
||
|
||
Флаги/app.mk, публикация пакета, исправление CDB, типизированные адреса,
|
||
resolver и CLI map/addr2line/line2addr/vars/asm. Debug-библиотеки добавляются
|
||
после успешного эксперимента. Обновить документацию сборки/mame-autotest.
|
||
|
||
**Выход:** fixtures, одинаковые basename, inline multi-TU, huge/big и
|
||
bank-data разрешаются достоверно; unknown явен. Полный cmp debug/non-debug;
|
||
make size-check без необъяснённой дельты. Baseline не обновляется ради
|
||
скрытия регрессии.
|
||
|
||
### Этап 2 — сессия и мост
|
||
|
||
Lifecycle/image verification, snapshot/generation, арбитраж, реестр точек,
|
||
событийный протокол. Сначала файловый backend/измерения, затем socket
|
||
по необходимости. Pause/continue/instruction step и ограниченный async
|
||
stepIn. Версионировать мост/патчи и установщик (§12). Новый самостоятельный плагин
|
||
можно загружать прямо из toolchain/mcp через pluginspath без копирования
|
||
в vendor. Для интерактивного режима без окон требуется решение R19.
|
||
|
||
**Выход:** нет ложных точек DSS, банки активируются после готовности,
|
||
exit/reset/reload инвалидируют состояние; потеря клиента не блокирует MAME,
|
||
конкурентные операции не читают смешанный snapshot.
|
||
|
||
Текущая реализация опрашивает backend и при остановленном CPU: потеря MAME
|
||
переводит DAP в `terminated` (проверено живым пробником с внезапным выходом).
|
||
Проверенные живые lifecycle-пробники вызывают штатный `schedule_exit()` через
|
||
Lua `machine:exit()` и реальный save/load state при остановке в `main`:
|
||
оба завершают DAP, post-load удаляет старые точки. SDL3-событие закрытия
|
||
окна использует тот же `schedule_exit()`; прямой UI-клик не автоматизировался.
|
||
Reset по-прежнему требует безопасного отдельного репро. `SIGTERM`
|
||
остановленному процессу MAME пока не дал DAP `terminated` за 15 секунд;
|
||
это отдельный OS-signal путь, не проверка штатного выхода.
|
||
|
||
### Этап 3 — полезная CLI/MCP-отладка и логи
|
||
|
||
Внешние точки/условия/логи, диспетчер совпадений, базовые типы, чтение/запись
|
||
резидентных объектов, доказанные watchpoint, where/disassemble_src/clog.
|
||
Интегрировать пакет/журнал в автотестовый launcher; проверить headless debugger.
|
||
|
||
**Выход:** значения совпадают с эталонными байтами, логи воспроизводимы
|
||
и ограничены, breakpoint не проглатывается логпоинтом, повторная загрузка
|
||
набора не дублирует точки.
|
||
|
||
### Этап 4 — VS Code MVP
|
||
|
||
DAP §8.2 и мини-расширение, сначала attach. Source/asm, break/logMessage,
|
||
globals/registers, pause/continue/instruction step. Source-step включить
|
||
после его приёмки, другие запросы не рекламировать заранее.
|
||
|
||
**Выход:** редакторская точка показывает фактическое разрешение,
|
||
остановка — верные строку/банк/значения; lifecycle DAP, удаление точек,
|
||
disconnect и stale references проверены протокольными тестами.
|
||
|
||
### Этап 5 — полный цикл разработки и расширенная отладка
|
||
|
||
Собственное расширение получает команды Build/Run/Debug, TaskProvider,
|
||
problem matcher, выбор target/profile/EXTRA_DATA и проверку инструментов.
|
||
Языковой сервис C/C++ либо clangd остаётся необязательной внешней
|
||
зависимостью с генерируемыми include/defines; его диагностика не подменяет
|
||
SDCC. Добавить launch/restart/stopOnMain и упаковку VSIX. Довести next,
|
||
доступные stepOut, memory/setVariable, data breakpoints, physical bank-data,
|
||
массивы/структуры и резидентные комментарии.
|
||
|
||
**Выход:** новый рабочий каталог проходит документированную настройку/F5
|
||
на обычной и банковой программе; restart не использует старую карту.
|
||
У каждой расширенной функции свой тест/capability; недоступная функция
|
||
не мешает использовать поддержанные.
|
||
|
||
### Этап 6 — якоря и исследовательские функции
|
||
|
||
Первый `SDBG_LOG` и ограниченный `SDBG_LOGIF` уже реализованы; продолжить
|
||
§7.2 проверкой сложных inline/оптимизаций и условных выражений только после
|
||
появления соответствующего evaluator. Независимые подэтапы: банковые комментарии,
|
||
доказанные stack frames/локальные, coverage/profiling. GDB §9 — отдельный
|
||
подэтап по потребности.
|
||
|
||
Финальные задачи для макросов, **без реализации в текущем этапе**:
|
||
|
||
- Добавить явные десятичные и hex-форматы для 8/16/32-битных signed/unsigned
|
||
объектов и адресов, с проверкой типа, знака, ширины и ограниченной общей
|
||
грамматикой для SDBG_LOG, внешних logpoints и DAP. Проверить значения на
|
||
реальных SDCC-сборках; не передавать Python format specifier в MAME `printf`.
|
||
- Для указателя выводить его собственное значение (логический/банковый адрес)
|
||
в decimal/hex и отдельно значение по адресу только если указатель не `NULL`.
|
||
`char *` трактовать как строку с bounded read до NUL и проверенной кодировкой;
|
||
`int8_t *`/`uint8_t *` — как один 8-битный signed/unsigned объект (для
|
||
16/32-битных typed pointers — соответствующий размер). Не выбирать режим
|
||
по одному CDB: `char *` и `uint8_t *` там неразличимы (R22); сохранить
|
||
объявленный тип/typedef chain из исходника и сверить с CDB. Чтение только
|
||
без side effects, после проверки размера, доступного банка, границ и image
|
||
identity; `NULL`, закрытая страница, отсутствие NUL в лимите и неизвестный
|
||
pointee дают явный `unavailable`/truncated, а не неверное значение.
|
||
|
||
**Выход:** доказательства и ограничения каждой функции опубликованы.
|
||
Эвристика не выдаётся за стек, anchors — за гарантированную идентичность,
|
||
посещения адресов — за время CPU.
|
||
|
||
## 11. Матрица приёмки и регрессий
|
||
|
||
| Сценарий | Проверяемое свойство | Этап / риски |
|
||
|---|---|---|
|
||
| hello/probe debug и обычный | Полный cmp exe, размеры, карта main | 0–1, R02 |
|
||
| Два TU с общим вызываемым inline | Нет коллизий, адреса/строки обоих экземпляров | 0–1, R01/R04 |
|
||
| Ошибка линковки при старом ihx | Ошибка сохранена, новый пакет не опубликован | 1, R01/R12 |
|
||
| Два utils.c и перенесённый проект | TU identity и source mapping | 1, R12 |
|
||
| Цикл/ветка/удалённая строка/много адресов | Все позиции или unverified, без ложного переноса | 1–4, R04 |
|
||
| Два банка с одинаковыми PC и ret | Верная строка/точка, нет чужого комментария | 0–6, R03/R11 |
|
||
| huge W3, big W1, поддержанные manual | Адреса/guards из фактической карты | 1–5, R03 |
|
||
| bank-data, невключённый банк, граница окна | Physical чтение или Unavailable | 1–5, R03 |
|
||
| Watchpoint через CPU/alias | Адрес, момент и PC-источник корректны | 0–5, R03 |
|
||
| Entry до банков, ошибка loader | Нет ранней активации/ложного ready | 2, R05 |
|
||
| DSS/exit/restart/reset/state load | Нет чужих попаданий и stale handles | 2–5, R05/R12 |
|
||
| Изменённый source или чужой exe | Явное расхождение карты | 1–5, R12 |
|
||
| Log + user + temporary по одному PC | Лог есть, остановка сохранена | 2–4, R08 |
|
||
| Два клиента, ручной resume, reconnect | Арбитраж/события, нет повторных mutations | 2–4, R07/R15 |
|
||
| Одна строка в цикле, HALT/ISR/рекурсия/trampoline | Ограниченный отменяемый шаг | 2–5, R06/R13 |
|
||
| Signed 8/16, pointer/array, CP866, нет NUL | Знак/тип/кодировка, bounded read | 3–5, R10 |
|
||
| Горячий лог, переполнение, выход | Overhead, счётчик потерь, flush | 3, R09 |
|
||
| Debug libc/libbgi fast/safe, DCE | Строки/типы, нет роста кода | 0–1, R17 |
|
||
| Anchors с #if/include/wrappers/inline | Метаданные совпадают, unsupported отклонён | 6, R02/R04 |
|
||
| DAP replace/empty/events/references | Протокол и честные capabilities | 4, R13 |
|
||
| F5 с данными, ошибка ROM/build, restart | Полный цикл и диагностика | 5, R18 |
|
||
| MAME update с ручными изменениями | Установщик не затирает расхождение | 2, R16 |
|
||
|
||
Тесты карты/протокола — небольшие fixtures без MAME; CPU/банки/lifecycle —
|
||
живые интеграционные сценарии. Golden-карта сверяется независимо с
|
||
листингом, байтами и фактическими остановками, не только выводом парсера.
|
||
Tests/hello и tests/banked — стартовые кандидаты, не вся приёмка.
|
||
|
||
Замеры: без debugger, debugger без точек, resident/bank breakpoints,
|
||
горячий лог, instruction trace, transport RTT, end-to-end step.
|
||
Записывать хост/версию, emulation speed, объём журнала, latency p50/p95.
|
||
Пороги зафиксировать после baseline этапа 0 до выбора backend;
|
||
«21 МГц терпимо» не критерий приёмки.
|
||
|
||
## 12. Размещение и воспроизводимость
|
||
|
||
Исходники моста/MCP — `toolchain/mcp/`, патчи MAME —
|
||
`toolchain/mame-patches/`; патч SDCC при необходимости отдельно с версией.
|
||
`toolchain/install-mame-bridge.sh` проверяет upstream revision/хеши,
|
||
показывает diff при расхождении и не затирает неизвестные ручные изменения.
|
||
Повторная установка идемпотентна; protocol version проверяется handshake.
|
||
|
||
Самостоятельный sdbgbridge загружается прямо из toolchain/mcp через
|
||
pluginspath: это устраняет необходимость копировать его в vendor и риск
|
||
потери изменений. Установщик остаётся нужен для патчей существующего
|
||
MAME/моста, если они потребуются. Игнорируемое mame/ не источник истины. Документировать сборку/применение
|
||
патчей, проверку установленного бинарника и откат. Не хранить личные пути,
|
||
ROM и большие образы в исходниках расширения.
|
||
|
||
Defaults вместо прежних открытых вопросов: source debug явный/профиль IDE;
|
||
CDB errors не подавляются; отдельный журнал; общий ограниченный язык;
|
||
DAP — основной IDE-путь; одна сессия владеет backend; socket по измерениям;
|
||
debug-библиотеки после эксперимента. Технически открыты: способ R01,
|
||
наблюдение загрузки/выхода, полная mapping-формула, physical RAM и стоимость
|
||
hooks. У каждого — репро этапа 0 и условие допуска, а не молчаливое
|
||
предположение следующих этапов.
|
||
|
||
## 13. Внешние спецификации
|
||
|
||
Локальные версии исходников и репро первичны для конкретной сборки.
|
||
Online-документация задаёт общую семантику; при реализации фиксировать
|
||
использованную версию.
|
||
|
||
- [DAP specification](https://microsoft.github.io/debug-adapter-protocol/specification): запросы, события, capabilities и references.
|
||
- [DAP specification source](https://github.com/microsoft/debug-adapter-protocol/blob/main/specification.md): полный контракт.
|
||
- [VS Code debugger extension](https://code.visualstudio.com/api/extension-guides/debugger-extension): регистрация/упаковка адаптера.
|
||
- [MAME general commands](https://docs.mamedev.org/debugger/general.html): printf/tracelog/source/trackpc.
|
||
- [MAME execution commands](https://docs.mamedev.org/debugger/execution.html): шаги и трассировка.
|
||
- [MAME Lua debugger classes](https://docs.mamedev.org/luascript/ref-debugger.html): низкоуровневый API.
|
||
- [GDB Dynamic Printf](https://sourceware.org/gdb/current/onlinedocs/gdb.html/Dynamic-Printf.html): логирование без пересборки.
|
||
- [GDB Overlays](https://sourceware.org/gdb/current/onlinedocs/gdb.html/Overlays.html): перекрывающиеся размещения и target-интеграция.
|