Files
Sprinter-SDCC/applications/Volkov/docs/p5-stability-results.md
T

216 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Sprinter Commander: результаты стабилизации P5
Дата: 8 сентября 2026 года.
Статус: `PASS` в MAME 0.288 / BIOS 3.06 / DSS 1.71.57 на HDD `D:`.
Release gate PoC 0.1 закрыт в эмуляторе: пройдены граничные размеры, серия из
20 копирований, 100 refresh, повторный lifecycle, расширенная матрица ошибок,
смена носителя и запуск реальной внешней программы. Проверка на настоящем
Sprinter остаётся отдельным аппаратным smoke-тестом.
## 20 copy и граничные размеры
`tests/p5_fixture.py` создаёт двадцать файлов `F00.BIN..F19.BIN`. В набор
обязательно входят размеры:
```text
0, 1, 4095, 4096, 65535, 65536, 1048613 байт
```
Остальные тринадцать файлов проверяют повторное использование copy job,
временных имён и файловых дескрипторов. Lua-сценарий последовательно вызывает
F5 двадцать раз, не меняя активную панель. После остановки MAME
`tests/check_p5_hdd.sh` извлекает оба дерева и сравнивает каждую пару
побайтно:
```text
PASS: 20/20 файлов совпали; все граничные размеры и >1 МБ пройдены.
```
Первый вариант расписания успел выполнить только 17 команд: кадр показывал
активный прогресс `F16.BIN`, когда Lua уже отпустил следующие клавиши. Это не
трактовалось как ошибка Commander. Финальный сценарий разносит команды с
учётом числа 4-КБ блоков и получает все двадцать результатов на CHD.
## Refresh и EMM lifecycle
После copy тот же процесс получает 100 отдельных `Ctrl+R` с интервалом 0,5
секунды. Затем Commander штатно завершается и ещё дважды запускается и
завершается на том же HDD.
`sc_app_cleanup()` сравнивает `mem_info()` до выделения и после возврата общего
четырёхстраничного EMM-блока. При несовпадении программа печатает
`SPRCMD cleanup did not restore EMM free pages.` и возвращает код 4. Во всех
трёх контрольных кадрах присутствует только чистый prompt `D:\>`; повторные
инициализации также состоялись.
Артефакты:
- [прогресс файла больше 1 МБ](../artifacts/mame-p5/stability/copy-large-progress.png);
- [20 файлов после ста refresh](../artifacts/mame-p5/stability/twenty-copy-hundred-refresh.png);
- [cleanup-цикл 1](../artifacts/mame-p5/stability/cleanup-cycle-1.png);
- [cleanup-цикл 2](../artifacts/mame-p5/stability/cleanup-cycle-2.png);
- [cleanup-цикл 3](../artifacts/mame-p5/stability/cleanup-cycle-3.png).
## Реальный ENOSPC
Отдельный образ содержит `SOURCE/FULL.BIN` размером 262181 байт и filler
размером 32850000 байт. До запуска копирования на FAT16 свободно 104448 байт:
temp успевает создаться и частично вырасти, но не может вместить исходник.
Commander показал `Copy failed (errno 10); temporary file removed.` и остался
управляемым. После выхода валидатор подтвердил:
```text
PASS: ENOSPC не изменил source/filler и не оставил target/temp.
```
То есть проверен именно путь ошибки записи после создания temp, а не ранний
отказ `open()`.
Артефакты:
- [начало копирования на почти полном диске](../artifacts/mame-p5/enospc/copy-progress.png);
- [управляемый errno 10 и пустая target-панель](../artifacts/mame-p5/enospc/enospc-cleanup.png);
- [диалог после ошибки](../artifacts/mame-p5/enospc/quit-after-error.png);
- [чистый возврат в DSS](../artifacts/mame-p5/enospc/clean-dss-return.png).
## Точная граница видимой области 27/28
`N27` содержит 26 физических файлов плюс синтетический `..`, а `N28` — 27
файлов плюс `..`. Первый кадр одновременно показывает `027 entries` и
`028 entries`. После `End` панель N28 имеет `cursor=27`, первая видимая запись
становится `B0000000.TXT`, то есть `top=1`. Для N27 `End` даёт `cursor=26`,
`A0000000.TXT` остаётся первой строкой, то есть `top=0`.
- [обе точные границы](../artifacts/mame-p5/panel-edges/counts-27-28.png);
- [28-я запись включает прокрутку](../artifacts/mame-p5/panel-edges/count-28-scrolls.png);
- [27 записей помещаются без прокрутки](../artifacts/mame-p5/panel-edges/count-27-fits.png);
- [cleanup после проверки](../artifacts/mame-p5/panel-edges/clean-dss-return.png).
## Детерминированная матрица отказов copy/EXEC
`CPERR.EXE` вызывает то же банковое ядро `sc_copy_job.c`, но останавливает его
в точно известных состояниях. Это позволило проверить случаи, которые нельзя
надёжно поймать Lua-клавишей между двумя соседними 4-КБ блоками:
- отсутствующий EXE возвращает `ENOENT` (`errno 3`);
- отсутствующий source не оставляет уже созданный temp;
- отмена до первого read не создаёт target;
- после записи последнего блока job останавливается в отдельной точке
pre-commit, и отмена удаляет temp;
- принудительная ошибка read удаляет temp;
- temp, повторно открытый с `O_RDONLY`, даёт реальный отказ write
`EROFS` (`errno 8`) и удаляется;
- исчезновение temp непосредственно перед rename даёт управляемую ошибку;
- target, появившийся между begin и commit, сохраняет содержимое `KEEP`;
- после всей матрицы число свободных EMM-страниц полностью восстановлено.
Проверка выявила ошибку общего libc: исторические Sprinter-флаги имеют
значения `O_WRONLY=1`, `O_RDONLY=2`, `O_RDWR=3`, а `open()` трактовал младшие
биты как POSIX-нумерацию. Исправлены `libc/include/fcntl.h` и `libc/io/open.c`,
добавлен постоянный режимный тест в `tests/openenv`. После пересборки fast и
safe libc матрица прошла целиком, а основной F5-сценарий был повторён.
Результат внутри CHD проверяется не только по экрану: `RESULT.TXT` обязан
содержать все строки `PASS`, в `TARGET` разрешён только неизменённый
`RACE.BIN`, временные `~SC*.TMP` запрещены.
- [вся fault-matrix: ALL PASS](../artifacts/mame-p5/copy-faults/fault-matrix-all-pass.png);
- [возврат CPERR в DSS](../artifacts/mame-p5/copy-faults/return-to-dss.png).
## Смена HDD во время копирования
Lua физически выгружает только тестовый `hard2` во время первого F5 и через
несколько секунд подключает тот же CHD обратно. На DSS 1.71 активный системный
вызов ожидает возврата устройства; после подключения Commander получает
управляемый `errno 3`, удаляет temp и продолжает принимать команды. Повторный
F5 без перезапуска приложения успешно копирует `DATA.BIN` размером 262181 байт.
Посмертная проверка CHD подтверждает точное совпадение source/target и
отсутствие temp:
```text
PASS: HDD removal дал управляемый отказ; после возврата DATA совпал, temp нет.
```
- [управляемый отказ первого F5](../artifacts/mame-p5/media-change/managed-error.png);
- [прогресс повторного F5](../artifacts/mame-p5/media-change/retry-progress.png);
- [восстановленный target](../artifacts/mame-p5/media-change/recovered-copy.png);
- [чистый возврат в DSS](../artifacts/mame-p5/media-change/clean-dss-return.png).
## Реальный viewer и восстановление окружения
Внешним приложением служит не специальный test child, а полноэкранный
`examples/mdview2`. Commander перед EXEC устанавливает CWD активной панели,
поэтому viewer без передачи аргументов открывает лежащий рядом `README.MD`.
Проверены обычный вид, raw-режим, PageDown, выход и возврат в Commander с
восстановленными режимом, палитрой, CWD и обеими панелями.
Тест обнаружил в самом `mdview2` утечку scratch-блока EMM. Viewer переведён
на явное владение блоком: результат `mem_alloc_pages()` проверяется, а блок
освобождается в `unload_file()`. После исправления Commander штатно завершает
собственную lifecycle-проверку EMM.
- [главный экран mdview2](../artifacts/mame-p5/viewer/mdview-main.png);
- [mdview2 после PageDown](../artifacts/mame-p5/viewer/mdview-pagedown.png);
- [Commander после возврата](../artifacts/mame-p5/viewer/commander-restored.png);
- [чистый возврат в DSS](../artifacts/mame-p5/viewer/clean-dss-return.png).
## Финальный размер PoC
Конфигурация: `--memory big --safe --max-allocs 3000`.
| Область | Размер |
|---|---:|
| `_CODE` W2 | 8870 байт |
| данные W2 | 3240 байт |
| свободная куча W2 | 2738 байт |
| стек | 1279 байт |
| BANK1: scan/sort/copy job | 4242 / 16384 байт |
| BANK2: draw/copy/EXEC UI | 5973 / 16384 байт |
| `SPRCMD.EXE` | 42447 байт |
Проверка межбанковых вызовов чистая. Общий `make size-check` ожидаемо сообщает
рост `openenv` на 188 байт: это не скрытый рост libc, а добавленный постоянный
тест `write()` через `O_RDONLY` вместе с исправленной веткой mode mapping.
Эталон размеров автоматически не обновлялся, поскольку в общем дереве также
присутствует посторонний новый `hello3`.
## Команды воспроизведения
```sh
make hdd-p5 ALLOCS=3000
tests/run_mame_hdd.sh build/hdd/p5_stability.chd \
tests/mame_p5_stability.lua artifacts/mame-p5/local 205
tests/check_p5_hdd.sh build/hdd/p5_stability.chd
make hdd-p5-enospc ALLOCS=3000
tests/run_mame_hdd.sh build/hdd/p5_enospc.chd \
tests/mame_p5_enospc.lua artifacts/mame-p5/enospc-local 55
tests/check_p5_enospc_hdd.sh build/hdd/p5_enospc.chd
make hdd-p5-panels ALLOCS=3000
tests/run_mame_hdd.sh build/hdd/p5_panel_edges.chd \
tests/mame_p5_panel_edges.lua artifacts/mame-p5/panels-local 50
make hdd-p5-copy-faults ALLOCS=3000
tests/run_mame_hdd.sh build/hdd/p5_copy_faults.chd \
tests/mame_p5_copy_faults.lua artifacts/mame-p5/faults-local 180
tests/check_p5_copy_faults_hdd.sh build/hdd/p5_copy_faults.chd
make hdd-p5-viewer ALLOCS=3000
tests/run_mame_hdd.sh build/hdd/p5_viewer.chd \
tests/mame_p5_viewer.lua artifacts/mame-p5/viewer-local 65
make hdd-p5-media ALLOCS=3000
tests/run_mame_hdd.sh build/hdd/p5_media.chd \
tests/mame_p5_media_change.lua artifacts/mame-p5/media-local 50
tests/check_p5_media_hdd.sh build/hdd/p5_media.chd
```
Перед каждым запуском должен отсутствовать другой процесс MAME Sprinter.
`run_mame_hdd.sh` проверяет это сам. Единственный незакрытый пункт за пределами
эмуляторного release gate — повторить короткий smoke-сценарий на реальном
Sprinter Sp2000.