# Sprinter Commander: платформенные пробы P1 Статус: MAME 0.287 / BIOS 3.06 — `PASS`; проверка на реальном Sprinter ожидается Связанные документы: [требования](commander-requirements.md), [архитектура](commander-architecture.md), [клавиши](commander-keymap.md), [roadmap](commander-roadmap.md), [экранный backend P2](p2-screen-backend.md) ## 1. Назначение Программа `p1probe.exe` объединяет платформенные проверки, которые должны быть завершены до реализации UI Commander. Она не является ранней версией Commander и не определяет его пользовательский интерфейс. Исходники находятся в `tests/p1_platform/`. Вторая программа, `p1child.exe`, используется только для проверки ESTEX `EXEC`, возврата к родителю и кода завершения. ## 2. Ограничения безопасности - Пробы не создают, не изменяют и не удаляют файлы. - Каталожная проба выполняет только `getcwd`, `chdir(".")` и `ffirst/fnext`. - EMM-проба пишет только в выделенный ей блок из трёх страниц. - Screen-проба пишет только в отдельную выделенную EMM-страницу. - Дочерняя программа меняет видеорежим, но не пишет на диск. - Проба не открывает файловые дескрипторы приложения напрямую. - MAME не запускается целями `make` и `make all`. Первый MAME-прогон выполнен 6 сентября 2026 года после отдельного согласования. Все запуски использовали автоматически пересоздаваемый `mc.img`; исходные дисковые образы не изменялись программами-пробами. ## 3. Состав | Файл | Назначение | |---|---| | `p1probe.c` | меню, общие сообщения, последовательность тестов | | `p1_sys.c` | app-local wrappers DSKINFO, EXEC, WAIT, WINREST | | `p1_emm.c` | выделение трёх страниц и проверка границ | | `p1_dir.c` | CURDIR/CHDIR/F_FIRST/F_NEXT и DSKINFO output | | `p1_screen.c` | полный и частичный WINREST с ненулевым offset | | `p1_keyboard.c` | вывод `ascii/scan/live modifiers` | | `p1_exec_test.c` | два режима EXEC и проверка состояния родителя | | `p1child.c` | дочерний EXE, меняющий режим на 40x32 и возвращающий `0x5A` | | `mame_dump_keys.lua` | вывод карты полей AT-клавиатуры MAME | | `mame_p1_keyboard.lua` | воспроизводимый ввод стрелок, F1–F10 и сочетаний | | `Makefile` | сборка обоих EXE | ## 4. Сборка ```sh cd applications/Volkov/tests/p1_platform make ``` Диагностическая программа собирается в режиме `small --safe`. Это сделано намеренно: P1 проверяет платформенные ABI и не должен одновременно отлаживать банковую topology Commander. Сборка `big --safe` является критерием P2. Дочерняя программа собирается как `tiny --safe`. ### Результат текущей сборки `p1probe.exe`: - `_CODE`: 11 062 байта; - данные: 2 741 байт; - файл EXE: 11 835 байт; - heap после статики: 17 429 байт; - стек: 1 279 байт. `p1child.exe`: - `_CODE`: 3 907 байт; - данные: 849 байт; - файл EXE: 4 453 байта; - heap после статики: 10 092 байта; - стек: 1 279 байт. Компиляция выполнена без ошибок. Значения относятся к текущей диагностической сборке и не являются бюджетом Commander. ## 5. Статическая проверка ABI ### 5.1. DSKINFO Сигнатура: ```c int p1_disk_info(uint8_t disk, P1DiskInfo *out); ``` Сгенерированный вызов передаёт `disk` в A, `out` в DE. Wrapper сохраняет IX, а после RST сохраняет: - A — sectors per cluster; - HL — total clusters; - DE — free clusters; - BC — bytes per sector. Размер `P1DiskInfo` проверяется typedef-assert и равен семи байтам. ### 5.2. EXEC Сигнатура намеренно имеет 8-битный аргумент первым: ```c int p1_exec(uint8_t path_mode, const char *path); ``` Для этого порядка SDCC передаёт `path_mode` в A и `path` в DE. Wrapper перекладывает их в B и HL соответственно. Вариант `p1_exec(const char *path, uint8_t mode)` был отвергнут после просмотра сгенерированного ASM: второй 8-битный аргумент SDCC помещал на стек, а не в DE. На успехе A сохраняется как `p1_exec_exit_code`; на ошибке код из A проходит через `__errno_set`. ### 5.3. WINREST Wrapper повторяет уже подтверждённую раскладку `tests/winrest` и `mdview2`: ```text A row L column SP+2 height SP+3 width SP+4 physical page SP+5..6 offset in 16-KB page ``` IX сохраняется, перед RST устанавливается в `0xC000 + offset`. Сгенерированный вызов проверен для полного экрана `32x80` и окна `2x20` с offset `0x2000`. ## 6. Меню `p1probe.exe` | Клавиша | Проба | Автоматическая оценка | |---|---|---| | `1` | Каталог и F_FIRST/F_NEXT | да, плюс наблюдаемые коды | | `2` | EMM: три страницы | да | | `3` | WINREST full/partial | только завершение; изображение оценивается визуально | | `4` | Клавиатурные коды | сбор наблюдений | | `5` | EXEC и возврат | да для режима B=1 | | `6` | DSKINFO текущего диска | да | | `A` | Пробы 1, 2 и 6 | да | | `0`/`Esc` | Выход | — | ## 7. Ожидаемые проверки ### 7.1. Каталог Проба дважды перечисляет каталог по маскам `*.*` и `*`, печатает первые 20 записей и итоговые значения: - число записей; - наличие `.`; - наличие `..`; - `errno` последнего `fnext()`; - совпадение результатов двух масок; - сохранение CWD после `chdir(".")`. Фактический результат DSS различается: `*.*` вернул обе записи с расширением, а `*` завершился как пустой список с `ENOENT`. Поэтому `*.*` принят как обязательный шаблон полного scan; совпадение масок не предполагается. MAME-прогон установил фактическое завершение перечисления: `fnext()` возвращает ошибку с `errno=3` (`ENOENT`). Код `0x0F` из старого описания DSS для этой операции не подтверждён. Проба после измерения принимает только `ENOENT`, чтобы возможная смена поведения не осталась незамеченной. ### 7.2. EMM Проверяются: - `mem_alloc_pages(3)`; - три различных физических номера; - первые 64 байта каждой страницы; - последние 64 байта каждой страницы; - независимые patterns страниц; - восстановление `mem_info.free_pages` после освобождения блока. ### 7.3. Screen/WINREST Первый вызов должен показать полный экран 80x32 с рамкой и цветными строками. После клавиши второй вызов должен наложить окно 20x2 в позиции row 14, column 30. Данные второго окна лежат по offset `0x2000`, поэтому тест одновременно проверяет корректность IX. Тест не открывает окно через BIOS: ESTEX WINREST здесь только копирует прямоугольник в текущий текстовый экран. Повтор со снимками в `t=20` и `t=24` без промежуточных вызовов подтвердил, что фон вне прямоугольника сохранён. Первая версия pattern использовала арифметически полученные атрибуты `0x11..0x17`. Верхняя граница `0x11` была обычным blue-on-blue, а часть остальных сочетаний выглядела неоднозначно в разных моментах палитрового цикла. Замечание о синих символах на синем фоне подтвердилось; raw-арифметика атрибутов в UI запрещается. После замены на именованные `COLOR(LIGHTBLUE..WHITE, BLUE)` полный экран и частичное окно были видимы на кадрах `t=20` и `t=24`. Повторная проверка исходных PNG установила, что их декодированные RGB-пиксели полностью идентичны. Прежнее сообщение о чередовании P2 было ошибкой встроенного предпросмотра, а не поведением MAME. Отдельная проба P2.2 сохранила, установила, прочитала обратно и восстановила шесть атрибутов во всех четырёх планах; многоцветная сцена осталась стабильной. Строка P1-SCR-03 ниже подтверждена как `PASS`. Детали — в [результатах P2.2](p2-palette-results.md). ### 7.4. Keyboard Для 24 событий печатаются: ```text ascii, scan, полное kbd_mod_state, compact Shift/Ctrl/Alt ``` `Esc` печатается последним и завершает сбор. В автоматическом MAME-сценарии использован следующий набор из 24 событий: - стрелки, Home/End, PgUp/PgDn; - F1–F10; - Insert/Delete; - Ctrl+R; - Ctrl+PgUp; - Alt+F1; - Esc. ### 7.5. EXEC Выполняются два случая: 1. `B=0`, `P1CHILD.EXE` — наблюдение short-name/PATH semantics. 2. `B=1`, `.\\P1CHILD.EXE` — обязательный случай PoC. Дочерняя программа: - переключается в 40x32; - показывает свои W1/W2/W3 и CWD; - ждёт клавишу; - возвращает `0x5A`. После возврата родитель проверяет: - результат непосредственного EXEC; - результат WAIT; - восстановление W1/W2/W3; - восстановление CWD; - возможность вернуть 80x32 и продолжить работу. Ошибка случая B=0 записывается как наблюдение и не проваливает PoC. Ошибка B=1 проваливает тест. ## 8. Выполненный порядок первого прогона 1. `2` — EMM. 2. `6` — DSKINFO. 3. `1` — каталог. 4. `3` — WINREST. 5. `4` — клавиатура. 6. `5` — EXEC последним. 7. Повторно `2` в том же процессе после EXEC, чтобы проверить EMM. 8. Возврат в меню; независимые запуски дополнительно подтвердили повторный старт программы. Такой порядок сначала проверяет неразрушительные примитивы и оставляет наиболее сложное переключение контекста на конец. ## 9. Таблица runtime-результатов Окружение MAME: `mame.arm` 0.287 (`b0c4527c`), машина `sprinter`, BIOS `v3.06`, запуск `p1probe.exe` с дискеты A:. `PENDING` в аппаратной колонке означает только отсутствие прогона на реальном Sprinter и не отменяет результата MAME. | ID | Проверка | MAME 0.287 / BIOS 3.06 | Реальный Sprinter | Артефакт | |---|---|---|---|---| | P1-DIR-01 | CWD и `chdir(".")` | PASS | PENDING | [результат](../artifacts/mame-p1/directory/strict-enoent.png) | | P1-DIR-02 | `*.*` против `*` | PASS: `2` против `0`; полный scan использует только `*.*` | PENDING | [результат](../artifacts/mame-p1/directory/strict-enoent.png) | | P1-DIR-03 | Код конца F_NEXT | PASS, `ENOENT=3` | PENDING | [строгая проверка](../artifacts/mame-p1/directory/strict-enoent.png) | | P1-EMM-01 | Блок 3 страницы | PASS, страницы `EF/F0/F1` | PENDING | [результат](../artifacts/mame-p1/emm/result.png) | | P1-EMM-02 | Границы страниц | PASS | PENDING | [результат](../artifacts/mame-p1/emm/result.png) | | P1-EMM-03 | Счётчик после free | PASS, `221 → 221` | PENDING | [результат](../artifacts/mame-p1/emm/result.png) | | P1-SCR-01 | Полный WINREST 80x32 | PASS, визуально | PENDING | [контрастный экран](../artifacts/mame-p1/winrest/full-contrast.png) | | P1-SCR-02 | Offset WINREST | PASS, row 14 / col 30 / `0x2000` | PENDING | [фон и окно вместе](../artifacts/mame-p1/winrest/partial-contrast-stable.png) | | P1-SCR-03 | Цветные атрибуты в двух моментах цикла | PASS, RGB кадров идентичен; P2.2 подтвердил 6 атрибутов × 4 плана | PENDING | [кадр 1](../artifacts/mame-p1/winrest/partial-flash-phase.png), [кадр 2](../artifacts/mame-p1/winrest/partial-contrast-stable.png), [P2.2](p2-palette-results.md) | | P1-SCR-04 | Raw-атрибуты `0x11..0x17` | OBSERVED, raw-арифметика запрещена | PENDING | [неудачный исходный pattern](../artifacts/mame-p1/winrest/partial-phase-hidden.png) | | P1-KBD-01 | Базовые scan-коды | PASS | PENDING | [таблица](../artifacts/mame-p1/keyboard-commander/sprinter/0002.png) | | P1-KBD-02 | Ctrl/Alt modifiers | PASS | PENDING | [сочетания](../artifacts/mame-p1/keyboard-commander/sprinter/0003.png) | | P1-EXE-01 | EXEC B=0 | PASS | PENDING | [результат](../artifacts/mame-p1/exec/result.png) | | P1-EXE-02 | EXEC B=1 | PASS | PENDING | [результат](../artifacts/mame-p1/exec/result.png) | | P1-EXE-03 | Exit/WAIT `0x5A` | PASS / PASS | PENDING | [результат](../artifacts/mame-p1/exec/result.png) | | P1-EXE-04 | Восстановление W1/W2/W3 | PASS, `F3/F2/FF` | PENDING | [результат](../artifacts/mame-p1/exec/result.png) | | P1-EXE-05 | EMM после двух EXEC | PASS | PENDING | [контроль](../artifacts/mame-p1/exec-then-emm/emm-result.png) | | P1-DSK-01 | DSKINFO | PASS | PENDING | [результат](../artifacts/mame-p1/dskinfo/result.png) | ### 9.1. Зафиксированные значения - DSKINFO: 1 сектор на кластер, 512 байт на сектор, 2847 кластеров всего, 2814 свободно. - EMM до выделения: 256 страниц всего, 221 свободна; после освобождения снова 221. - Оба режима EXEC: `rc=0`, `errno=0`, непосредственный exit=`5A`, WAIT=`5A`. - CWD до и после child: `\`; страницы W1/W2/W3 до и после: `F3/F2/FF`. - Модифицированная клавиша приходит с установленным битом 7 scan-кода: Ctrl+R=`93`, Ctrl+PgUp=`D9`, Alt+F1=`BB`. После `scan & 0x7F` получаются базовые коды `13`, `59`, `3B`; compact modifiers равны `02`, `02`, `04`. - Esc: `ascii=1B`, `scan=01`. ## 10. Условия завершения P1 MAME-часть P1 завершена: все обязательные строки имеют `PASS`, фактические клавиатурные коды перенесены в keymap, завершение `fnext()` уточнено, `EXEC B=1`/WAIT и EMM после child подтверждены, артефакты сохранены. Проверка на реальном Sprinter остаётся желательной аппаратной валидацией, но не блокирует переход к P2 и реализацию platform API PoC. Если реальное железо даст расхождение, оно оформляется отдельным репро и не маскируется адаптацией UI.