Volkov: добавить Sprinter Commander

Реализовать двухпанельный Commander от платформенного PoC до этапов P6-P20: EMM-каталог, сортировку и выбор, операции с файлами и деревьями, транзакционное копирование, метаданные, политику конфликтов и предварительную проверку свободного места.

Добавить проектную документацию, HDD/MAME-сценарии и проверенные артефакты. Расширить libc операцией bank_write_page, исправлением режима O_RDONLY и связанными регрессионными проверками.
This commit is contained in:
2026-09-10 10:45:30 +03:00
parent 05bcd8197e
commit 8e389c03f8
870 changed files with 21310 additions and 25 deletions
@@ -0,0 +1,117 @@
# Sprinter Commander: рекурсивный F5 одного каталога 0.2/P15
Дата: 9 сентября 2026 года.
Статус: основной и отрицательные сценарии `PASS` в MAME 0.288 / BIOS 3.06 /
DSS 1.71.57 на HDD `D:`.
## Реализованный контракт
Если в активной панели нет выбранной группы и cursor стоит на обычном
каталоге, F5 рекурсивно создаёт одноимённое дерево в каталоге passive panel:
- обход breadth-first, без рекурсии C-стека;
- каждый файл проходит прежний crash-safe `temp -> commit` и общий 4-КБ
copy-buffer в выделенной странице W3;
- одновременно открыты не более source и temp;
- пустые каталоги создаются;
- existing target не объединяется и не перезаписывается;
- Esc проверяется между каталогами и на каждом 4-КБ шаге файла;
- при cancel/error уже committed-файлы и созданные каталоги остаются, temp
текущего файла удаляется;
- CWD процесса восстанавливается и passive panel перечитывается после
изменения target.
Начиная с P16 выбранные каталоги также добавляются как несколько roots в ту
же очередь. Этот документ фиксирует более узкий single-root срез P15.
## Память обхода
`sc_tree` на время одной операции выделяет одну дополнительную EMM-страницу.
Первые 256 байт сохраняют исходный CWD. Остаток содержит 1008 узлов по 16
байт: `parent index + DOS 8.3 component + depth`. Абсолютный путь строится
перед операцией по цепочке parent; фиксированный предел глубины — 32, а общий
предел пути остаётся 255 символов.
4-КБ данные файла не смешиваются с traversal queue и не размещаются в W2.
W3 отображается только внутри `bank_read/write` и `bank_read_page/
bank_write_page`; вызовы сами восстанавливают прежнюю страницу.
До добавления root проверяется, что каталог passive panel не совпадает с
source root и не лежит внутри него. Это запрещает саморастущее копирование.
## Проверка
Fixture включает пустой каталог, файлы размером 0, 15, 16 и 4097 байт и
цепочку из 16 вложенных уровней. MAME сообщил точный итог:
```text
Tree copied: 4 files; 19 dirs.
```
После выхода source и target извлечены из CHD и сравнены как полные деревья:
```text
PASS: recursive tree copied exactly; depth=16; no temp.
```
Повторный F5 остановился до изменения файлов: DSS вернул `EISDIR` (`errno
15`) для уже существующего target-каталога. Контрольные состояния:
- [source TREE и пустой target](../artifacts/mame-p15/tree-copy/before.png);
- [успех 4 files / 19 dirs](../artifacts/mame-p15/tree-copy/success.png);
- [repeat остановлен на existing target](../artifacts/mame-p15/tree-copy/repeat-existing-target.png);
- [passive panel перечитана](../artifacts/mame-p15/tree-copy/passive-refreshed.png);
- [Esc удалил temp большого файла](../artifacts/mame-p15/tree-copy/cancel-clean.png);
- [target внутри source отклонён до mutation](../artifacts/mame-p15/tree-copy/self-target-refused.png);
- [чистый возврат в DSS](../artifacts/mame-p15/tree-copy/clean-dss-return.png).
Отдельный cancel-fixture начал копирование `BIG.BIN` размером 4 МБ и подал
Esc до commit. Target root остался как определённый частичный результат,
файл и temp отсутствуют, source не изменён:
```text
PASS: tree cancel removed active temp; source intact; no BIG commit.
```
Self-target fixture расположил passive panel в `SOURCE\TREE\INNER`. Операция
остановилась с `errno 14` (`EINVAL` отображается libc на родной DSS
`EUNKOP`) до первого `mkdir`; `INNER` остался пустым:
```text
PASS: target-inside-source refused before mutation.
```
## Компоновка и размер
Конфигурация `--memory big --safe --max-allocs 3000`:
| Область | После P14 | После P15 | Изменение |
|---|---:|---:|---:|
| `_CODE` W2 | 9503 | 9503 | 0 |
| данные W2 | 3268 | 3524 | +256 |
| свободная куча W2 | 2077 | 1821 | -256 |
| BANK1 | 6477 | 9687 | +3210 |
| BANK2 | 13388 | 14547 | +1159 |
| `SPRCMD.EXE` | 43080 | 43080 | 0 |
256 байт W2 — единственный `ffblk_t` traversal engine. Крупная очередь
динамическая и в эти числа не входит. BANK1/BANK2 имеют соответственно 6697
и 1837 байт запаса; межбанковый аудит чист.
## Воспроизведение
```sh
make hdd-p15-tree-copy ALLOCS=3000
tests/run_mame_hdd.sh build/hdd/p15_tree_copy.chd \
tests/mame_p15_tree_copy.lua artifacts/mame-p15/local 60
tests/check_p15_tree_copy_hdd.sh build/hdd/p15_tree_copy.chd
make hdd-p15-tree-cancel hdd-p15-tree-self ALLOCS=3000
tests/run_mame_hdd.sh build/hdd/p15_tree_cancel.chd \
tests/mame_p15_tree_cancel.lua artifacts/mame-p15/cancel 48
tests/check_p15_tree_cancel_hdd.sh build/hdd/p15_tree_cancel.chd
tests/run_mame_hdd.sh build/hdd/p15_tree_self.chd \
tests/mame_p15_tree_self.lua artifacts/mame-p15/self 48
tests/check_p15_tree_self_hdd.sh build/hdd/p15_tree_self.chd
```