Files
Sprinter-SDCC/applications/Volkov/docs/p5-stability-results.md
T
snark13 8e389c03f8 Volkov: добавить Sprinter Commander
Реализовать двухпанельный Commander от платформенного PoC до этапов P6-P20: EMM-каталог, сортировку и выбор, операции с файлами и деревьями, транзакционное копирование, метаданные, политику конфликтов и предварительную проверку свободного места.

Добавить проектную документацию, HDD/MAME-сценарии и проверенные артефакты. Расширить libc операцией bank_write_page, исправлением режима O_RDONLY и связанными регрессионными проверками.
2026-09-10 10:45:30 +03:00

13 KiB
Raw Blame History

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. В набор обязательно входят размеры:

0, 1, 4095, 4096, 65535, 65536, 1048613 байт

Остальные тринадцать файлов проверяют повторное использование copy job, временных имён и файловых дескрипторов. Lua-сценарий последовательно вызывает F5 двадцать раз, не меняя активную панель. После остановки MAME tests/check_p5_hdd.sh извлекает оба дерева и сравнивает каждую пару побайтно:

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:\>; повторные инициализации также состоялись.

Артефакты:

Реальный ENOSPC

Отдельный образ содержит SOURCE/FULL.BIN размером 262181 байт и filler размером 32850000 байт. До запуска копирования на FAT16 свободно 104448 байт: temp успевает создаться и частично вырасти, но не может вместить исходник.

Commander показал Copy failed (errno 10); temporary file removed. и остался управляемым. После выхода валидатор подтвердил:

PASS: ENOSPC не изменил source/filler и не оставил target/temp.

То есть проверен именно путь ошибки записи после создания temp, а не ранний отказ open().

Артефакты:

Точная граница видимой области 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.

Детерминированная матрица отказов 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 запрещены.

Смена HDD во время копирования

Lua физически выгружает только тестовый hard2 во время первого F5 и через несколько секунд подключает тот же CHD обратно. На DSS 1.71 активный системный вызов ожидает возврата устройства; после подключения Commander получает управляемый errno 3, удаляет temp и продолжает принимать команды. Повторный F5 без перезапуска приложения успешно копирует DATA.BIN размером 262181 байт.

Посмертная проверка CHD подтверждает точное совпадение source/target и отсутствие temp:

PASS: HDD removal дал управляемый отказ; после возврата DATA совпал, temp нет.

Реальный 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.

Финальный размер 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.

Команды воспроизведения

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 35
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.