Volkov: добавить Sprinter Commander
Реализовать двухпанельный Commander от платформенного PoC до этапов P6-P20: EMM-каталог, сортировку и выбор, операции с файлами и деревьями, транзакционное копирование, метаданные, политику конфликтов и предварительную проверку свободного места. Добавить проектную документацию, HDD/MAME-сценарии и проверенные артефакты. Расширить libc операцией bank_write_page, исправлением режима O_RDONLY и связанными регрессионными проверками.
This commit is contained in:
@@ -0,0 +1,678 @@
|
||||
# Sprinter Commander: архитектура
|
||||
|
||||
Статус: P1/P2 подтверждены; одностраничная модель панели P3 реализована,
|
||||
проходит граничные проверки DSS
|
||||
|
||||
Связанные документы:
|
||||
[требования](commander-requirements.md),
|
||||
[клавиши и команды](commander-keymap.md),
|
||||
[план разработки](commander-roadmap.md),
|
||||
[платформенные пробы P1](p1-platform-probes.md),
|
||||
[экранный backend P2](p2-screen-backend.md),
|
||||
[результаты P2.1](p2-screen-probe-results.md),
|
||||
[результаты skeleton P2](p2-skeleton-results.md)
|
||||
|
||||
## 1. Цели архитектуры
|
||||
|
||||
Архитектура должна позволить получить небольшой технический PoC, не создавая
|
||||
тупиков для полной версии. Главные ограничения задаются не исходным VC, а
|
||||
платформой Sprinter:
|
||||
|
||||
- Z80 имеет 64-КБ адресное пространство;
|
||||
- W0 занят DSS/BIOS;
|
||||
- стек обязан находиться в W2;
|
||||
- полный список двух панелей не помещается вместе с кодом в near-память;
|
||||
- код полной версии потребует банков;
|
||||
- DSS имеет критический предел в восемь открытых файлов;
|
||||
- текстовый экран удобно восстанавливать непосредственно из EMM-страницы.
|
||||
|
||||
## 2. Принятые архитектурные решения
|
||||
|
||||
### ADR-001. Новая реализация, а не перенос
|
||||
|
||||
Commander пишется на C для SDCC. VC 4.05/4.99 используются как описание
|
||||
поведения и состава функций, Dos Navigator — как источник архитектурных
|
||||
идей. Зависимости от DOS interrupt API, сегментной модели x86, PSP, DTA,
|
||||
TSR, overlays, XMS и EMS в код проекта не переносятся.
|
||||
|
||||
### ADR-002. Лёгкий цикл событий вместо оконного framework
|
||||
|
||||
Приложение имеет один цикл событий, набор символьных команд и явное состояние
|
||||
экрана. Иерархия объектов наподобие Turbo Vision не используется.
|
||||
|
||||
Причины:
|
||||
|
||||
- меньше резидентного кода и данных;
|
||||
- нет виртуальных методов и потоковой сериализации объектов;
|
||||
- проще анализировать банковские вызовы;
|
||||
- легче гарантировать освобождение ресурсов.
|
||||
|
||||
### ADR-003. Целевой режим памяти — `big`
|
||||
|
||||
PoC собирается с `--memory big --safe`:
|
||||
|
||||
- W0 `0x0000–0x3FFF` — DSS/BIOS;
|
||||
- W1 `0x4000–0x7FFF` — переключаемые банки кода;
|
||||
- W2 `0x8000–0xBFFF` — HOME, DATA/BSS, heap и стек;
|
||||
- W3 `0xC000–0xFFFF` — EMM-страницы данных.
|
||||
|
||||
Если первоначальный skeleton временно собирается без банков, это не меняет
|
||||
контрактов модулей. До принятия PoC должна быть проверена именно сборка `big`.
|
||||
|
||||
### ADR-004. Панели, экран и рабочая область копирования хранятся в EMM
|
||||
|
||||
Начиная с P4 PoC выделяет один блок из четырёх последовательных 16-КБ страниц.
|
||||
P2/P3 использовали первые три страницы; четвёртая заранее резервируется текущим
|
||||
`ScApp`, чтобы копирование не увеличивало W2 и не меняло схему памяти во время
|
||||
операции:
|
||||
|
||||
| Логическая страница | Назначение | Использование |
|
||||
|---:|---|---|
|
||||
| 0 | записи левой панели | до 640 `ScDirEntry`, 1024 байта резерва |
|
||||
| 1 | записи правой панели | до 640 `ScDirEntry`, 1024 байта резерва |
|
||||
| 2 | экранный буфер | первые 5120 байт |
|
||||
| 3 | рабочая область copy job | первые 4096 байт в PoC |
|
||||
|
||||
Допускается выделение четырёх отдельных блоков, если эксперимент покажет
|
||||
проблему с последовательным блоком, но внешний интерфейс хранилища от этого
|
||||
не меняется.
|
||||
|
||||
### ADR-005. Запрещены долгоживущие указатели на W3
|
||||
|
||||
Указатель в диапазон `0xC000–0xFFFF` действителен только внутри функции,
|
||||
которая явно владеет отображением W3. После выхода из неё указатель считается
|
||||
недействительным.
|
||||
|
||||
Ни `PanelState`, ни `ScApp`, ни длительная операция не содержат near-указатель
|
||||
на содержимое EMM. Они хранят физическую страницу и смещение.
|
||||
|
||||
### ADR-006. Экран строится как модель ячеек
|
||||
|
||||
Каждая экранная ячейка представлена двумя байтами `(символ, атрибут)`. Формат
|
||||
совпадает с подтверждённым форматом ESTEX `WINREST`:
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
uint8_t ch;
|
||||
uint8_t attr;
|
||||
} ScCell;
|
||||
```
|
||||
|
||||
Полный экран занимает `80 * 32 * 2 = 5120` байт. Renderer изменяет EMM-буфер,
|
||||
помечает строки грязными, а backend выводит либо диапазон строк, либо весь
|
||||
экран. Backend использует координатные ESTEX/BIOS-функции; основной пакетный
|
||||
путь — проверенный `WINREST`. Прямой доступ к VRAM не используется.
|
||||
|
||||
Renderer использует смысловые роли атрибутов (`frame`, `title`, `active`,
|
||||
`passive`, `error`), а не числа палитры. Модуль `sc_theme`, подтверждённый
|
||||
пробой P2.2, назначает ролям разные атрибуты, сохраняет затрагиваемые записи
|
||||
четырёх планов и восстанавливает их при cleanup. Это не меняет формат
|
||||
`ScCell` или API renderer.
|
||||
Системные BIOS/ESTEX-вызовы палитры разрешены; запрещён только обход системного
|
||||
экранного API через прямую адресацию VRAM и оконные дескрипторы BIOS.
|
||||
|
||||
Дескрипторы BIOS `WIN_OPEN/WIN_CLOSE/WIN_*_WIN` не используются: BIOS хранит
|
||||
только один описатель окна 0. Слово `WIN` в ESTEX `WINREST` означает область
|
||||
экрана и не делает эту функцию частью BIOS window manager.
|
||||
|
||||
### ADR-007. Источник файлов отделён от панели
|
||||
|
||||
Панель не вызывает `ffirst/fnext` напрямую. Она работает через модуль
|
||||
источника. В PoC реализован только `SC_SOURCE_REAL_FS`.
|
||||
|
||||
Вместо таблицы виртуальных функций используется `kind` и обычный диспетчер:
|
||||
|
||||
```c
|
||||
typedef enum {
|
||||
SC_SOURCE_REAL_FS = 0,
|
||||
SC_SOURCE_SEARCH,
|
||||
SC_SOURCE_ARCHIVE
|
||||
} ScSourceKind;
|
||||
```
|
||||
|
||||
Такой подход не создаёт near/far function pointers и упрощает размещение
|
||||
реализаций по банкам. `SEARCH` и `ARCHIVE` до соответствующих этапов должны
|
||||
возвращать внутреннюю ошибку `SC_ERR_UNSUPPORTED`, которая на границе
|
||||
платформы при необходимости отображается как ESTEX `EUNKOP`.
|
||||
|
||||
### ADR-008. Длительные операции являются jobs
|
||||
|
||||
Копирование, а позднее удаление, поиск и подсчёт размеров, оформляются как
|
||||
конечные автоматы:
|
||||
|
||||
```text
|
||||
INIT -> PREPARE -> RUN_CHUNK -> PRE_COMMIT -> COMMIT -> DONE
|
||||
| | |
|
||||
+-> CANCEL ----+-----------+
|
||||
+-> ERROR -----+-----------+
|
||||
```
|
||||
|
||||
Один шаг не должен надолго захватывать управление. Между блоками приложение
|
||||
может обновить прогресс и проверить отмену. Даже последний успешно записанный
|
||||
блок завершает только `RUN_CHUNK`: отдельный вызов переводит job через
|
||||
`PRE_COMMIT` к атомарному rename. Поэтому `Esc`, полученный после отображения
|
||||
`N/N`, всё ещё успевает закрыть и удалить temp до появления целевого файла.
|
||||
|
||||
### ADR-009. Viewer, editor и setup сначала отдельные программы
|
||||
|
||||
Основной Commander отвечает за панели и операции с файлами. Просмотрщик,
|
||||
редактор и настройка первоначально запускаются через `EXEC` как отдельные
|
||||
`.EXE`. Это уменьшает HOME, число банков и последствия ошибки компонента.
|
||||
|
||||
После 1.0 они могут быть интегрированы банками, если измерения покажут
|
||||
реальную пользу.
|
||||
|
||||
## 3. Слои системы
|
||||
|
||||
```text
|
||||
+------------------------------------------------------+
|
||||
| app: main loop, mode, active panel, command dispatch |
|
||||
+----------------------+-------------------------------+
|
||||
| panels/model | UI/dialogs/status |
|
||||
+----------------------+-------------------------------+
|
||||
| sources | renderer/screen buffer |
|
||||
+----------------------+-------------------------------+
|
||||
| jobs: copy/... | storage: EMM/page access |
|
||||
+----------------------+-------------------------------+
|
||||
| platform: ESTEX, keyboard, exec, screen, filesystem |
|
||||
+------------------------------------------------------+
|
||||
| libc + Sprinter DSS/BIOS |
|
||||
+------------------------------------------------------+
|
||||
```
|
||||
|
||||
Зависимости направлены вниз. `platform` не знает о панелях; `storage` не
|
||||
показывает сообщения; renderer не читает файловую систему.
|
||||
|
||||
## 4. Предлагаемая структура исходников
|
||||
|
||||
```text
|
||||
Makefile
|
||||
src/
|
||||
sprcmd.c точка входа и аварийная очистка
|
||||
sc_app.c главный цикл и состояние приложения
|
||||
sc_command.c преобразование событий в команды и dispatch
|
||||
sc_input.c клавиатурные события
|
||||
sc_screen.c EMM framebuffer и dirty rows
|
||||
sc_video_system.c координатный вывод ESTEX/BIOS без оконных ID
|
||||
sc_draw.c рамки, строки, числа, сокращение путей
|
||||
sc_panel.c состояние и поведение панели
|
||||
sc_panel_draw.c представление панели
|
||||
sc_dir_store.c записи каталога в EMM
|
||||
sc_sort.c сравнение и сортировка записей
|
||||
sc_source.c диспетчер источников
|
||||
sc_source_fs.c настоящий каталог DSS
|
||||
sc_copy_job.c PoC-копирование одного файла
|
||||
sc_copy_ui.c банковая оркестрация F5 и прогресс
|
||||
sc_dialog.c сообщения и подтверждения
|
||||
sc_path.c безопасная работа с путями 8.3
|
||||
sc_error.c нормализация errno/ESTEX ошибок
|
||||
sc_platform_exec.c ESTEX EXEC/WAIT
|
||||
sc_platform_screen.c wrappers WINREST/WRCHAR/SCROLL и screen mode
|
||||
include/
|
||||
sc_app.h
|
||||
sc_command.h
|
||||
sc_glyphs.h raw-коды CP866 для рамок и стрелок
|
||||
sc_input.h
|
||||
sc_screen.h
|
||||
sc_panel.h
|
||||
sc_store.h
|
||||
sc_source.h
|
||||
sc_job.h
|
||||
sc_path.h
|
||||
sc_platform.h
|
||||
banks/
|
||||
bank1_panel.c чтение/сортировка/обновление панелей
|
||||
bank2_fileops.c копирование и диалоги операции
|
||||
tests/
|
||||
... target-тесты и данные для MAME
|
||||
docs/
|
||||
...
|
||||
```
|
||||
|
||||
Граница файлов может уточняться по карте линковки. Нельзя объединять всё в
|
||||
один большой модуль: SDCC/линкер должны иметь возможность не тянуть лишний
|
||||
код и разнести холодные функции по банкам.
|
||||
|
||||
## 5. Состояние приложения
|
||||
|
||||
### 5.1. `ScApp`
|
||||
|
||||
В W2 хранится единственный объект состояния:
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
ScPanel panels[2];
|
||||
uint8_t active_panel;
|
||||
uint8_t running;
|
||||
uint8_t video_mode_saved;
|
||||
uint8_t original_video_mode;
|
||||
uint8_t emm_block;
|
||||
uint8_t panel_page[2];
|
||||
uint8_t screen_page;
|
||||
ScJob job;
|
||||
int16_t last_error;
|
||||
} ScApp;
|
||||
```
|
||||
|
||||
Фактическое определение может отличаться, но следующие инварианты обязательны:
|
||||
|
||||
- `active_panel` равен 0 или 1;
|
||||
- обе панели ссылаются на разные EMM-страницы;
|
||||
- `job` либо неактивен, либо является единственным владельцем своих fd;
|
||||
- номера блоков и страниц обнуляются сразу после освобождения;
|
||||
- cleanup можно вызвать после любой частично завершённой стадии init.
|
||||
|
||||
### 5.2. `ScPanel`
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
char path[256];
|
||||
uint16_t count;
|
||||
uint16_t cursor;
|
||||
uint16_t top;
|
||||
uint32_t total_size;
|
||||
uint8_t source_kind;
|
||||
uint8_t phys_page;
|
||||
uint8_t truncated;
|
||||
uint8_t dirty;
|
||||
} ScPanel;
|
||||
```
|
||||
|
||||
Ограничения:
|
||||
|
||||
- `cursor < count`, если `count != 0`;
|
||||
- `cursor == 0`, если `count == 0`;
|
||||
- `top <= cursor`;
|
||||
- видимый курсор лежит в диапазоне `top..top+26`;
|
||||
- `count <= 640` для всей линейки 0.1–1.0;
|
||||
- `path` всегда завершён нулём.
|
||||
|
||||
## 6. Хранилище каталогов
|
||||
|
||||
Публичный интерфейс не возвращает указатель в W3:
|
||||
|
||||
```c
|
||||
int sc_store_get(const ScPanel *panel, uint16_t index, ScDirEntry *out);
|
||||
int sc_store_put(ScPanel *panel, uint16_t index, const ScDirEntry *entry);
|
||||
void sc_store_clear(ScPanel *panel);
|
||||
```
|
||||
|
||||
Для пакетных операций допускается закрытая функция, которая отображает
|
||||
страницу на короткое время. Она должна:
|
||||
|
||||
1. Сохранить текущую страницу W3.
|
||||
2. Подключить страницу панели.
|
||||
3. Выполнить только вычисления над памятью.
|
||||
4. Не вызывать ESTEX, renderer, банковский dispatch или пользовательский
|
||||
callback.
|
||||
5. Восстановить W3 перед возвратом.
|
||||
|
||||
Сортировка PoC должна быть нерекурсивной и не использовать callback `qsort`,
|
||||
чтобы избежать лишнего кода и банковских переходов. Предпочтительный первый
|
||||
вариант — Shell sort над одной отображённой страницей.
|
||||
|
||||
## 7. Чтение настоящего каталога
|
||||
|
||||
Последовательность `sc_source_fs_scan()`:
|
||||
|
||||
1. Проверить и нормализовать путь.
|
||||
2. Сохранить имя записи под курсором, если панель уже заполнена.
|
||||
3. Очистить счётчики панели, но не освобождать её страницу.
|
||||
4. При необходимости добавить синтетическую запись `..`.
|
||||
5. Выполнить `ffirst("*.*")/fnext` с одним общим 256-байтовым near-буфером.
|
||||
Шаблон `*` запрещён: P1 и P3 подтвердили, что DSS считает его именем без
|
||||
расширения и не возвращает обычные `NAME.EXT`.
|
||||
6. Пропустить `.` и `..` от DSS.
|
||||
7. Преобразовать `ffblk_t` в `ScDirEntry` и последовательно записать в EMM.
|
||||
8. При достижении 640 записей установить `truncated` и немедленно закончить
|
||||
scan: следующий `F_FIRST` сам переинициализирует глобальный итератор DSS.
|
||||
9. На стабильном DSS 1.71 код 35 означает, что закончилась единственная
|
||||
16-КБ страница кэша каталога, а не весь каталог. Принять уже прочитанные
|
||||
записи, установить `truncated` и добавить последней красную виртуальную
|
||||
запись `>>> MORE...` с флагом `SC_ENTRY_DSS_LIMIT`. Она занимает один
|
||||
слот, не является файлом и не участвует в файловых операциях.
|
||||
10. Отсортировать записи: `..`, каталоги, файлы, маркер ограничения; внутри
|
||||
обычных групп применить ключ и направление конкретной панели.
|
||||
11. Найти прежнее имя и восстановить курсор.
|
||||
12. Пометить панель и её экранные строки грязными.
|
||||
|
||||
Переборы двух панелей никогда не активны одновременно. `ffblk_t` не хранится
|
||||
в EMM и не является частью состояния панели. Причина и варианты снятия
|
||||
предела DSS разобраны в [dss-large-directories.md](dss-large-directories.md).
|
||||
|
||||
Флаг `SC_ENTRY_SELECTED` хранится в самой 24-байтовой EMM-записи и переезжает
|
||||
вместе с ней при сортировке. Резидентная `ScPanel` содержит только счётчик и
|
||||
32-битную сумму выбранных обычных файлов. `..` и виртуальные маркеры никогда
|
||||
не выбираются; refresh в текущем срезе 0.2 явно сбрасывает выбор и статистику.
|
||||
Одиночный `Insert` может менять флаг каталога, но все групповые операции
|
||||
`+`, `-`, `*` сначала отбрасывают `FA_DIREC` и никогда не меняют ранее
|
||||
установленный на каталоге флаг.
|
||||
|
||||
Маски `+`/`-` используют ASCII case-insensitive matcher с `*` и `?` только
|
||||
для файлов; специальный DOS-шаблон `*.*` совпадает также с именами файлов
|
||||
без точки. Редактор
|
||||
маски является общей координатной модальной поверхностью в BANK2: он рисует
|
||||
поле в экранной EMM-модели, поддерживает insertion, Backspace, Delete,
|
||||
Left/Right/Home/End и не создаёт BIOS-окно.
|
||||
|
||||
F6/F7 используют тот же editor и один validator компонента DOS 8.3. Локальный
|
||||
F6 не передаёт полные пути в DSS `RENAME`: отдельный банковый helper сохраняет
|
||||
CWD в резидентный scratch, входит в каталог панели, проверяет отсутствие
|
||||
target, вызывает `rename(old_basename, new_basename)` и восстанавливает CWD.
|
||||
Перезапись существующей записи запрещена до системного вызова.
|
||||
|
||||
F8 без выбора удаляет один объект после подтверждения: `unlink` для файла и
|
||||
`rmdir` для пустого каталога. Непустой каталог и виртуальная запись не
|
||||
запускают разрушительную операцию. При наличии выбора F8 одним
|
||||
подтверждением сначала удаляет выбранные файлы верхнего уровня, затем
|
||||
обходит все выбранные directory roots общей BFS-очередью, удаляет найденные
|
||||
файлы и выполняет `rmdir` каталогов обратным проходом. При ошибке или Esc
|
||||
уже выполненные действия не откатываются; CWD и EMM-страница освобождаются.
|
||||
|
||||
Групповой F5 проходит EMM-store по индексам и передаёт `sc_copy_begin/step`
|
||||
только записи с одновременно установленным `SC_ENTRY_SELECTED` и снятым
|
||||
`FA_DIREC`. Между файлами долговременный указатель W3 не сохраняется. При
|
||||
ошибке уже committed-файлы остаются, активный temp удаляется прежней job;
|
||||
это первая зафиксированная политика частично успешной группы.
|
||||
|
||||
F5 одного текущего каталога использует `sc_tree`: на время операции он
|
||||
выделяет отдельную EMM-страницу с BFS-очередью до 1008 каталогов. Узел хранит
|
||||
только parent-index, компонент DOS 8.3 и depth; путь восстанавливается перед
|
||||
действием. Первый 256-байтовый диапазон страницы хранит исходный CWD.
|
||||
Глубина ограничена 32, а путь — 255 символами. Файлы по-прежнему проходят
|
||||
через единственную 4-КБ copy-page. Existing target не merge-ится; target
|
||||
внутри source запрещается до создания root. Групповой F5 добавляет все
|
||||
выбранные каталоги верхнего уровня как независимые roots той же очереди
|
||||
после завершения выбранных top-level файлов.
|
||||
|
||||
## 8. Renderer
|
||||
|
||||
### 8.1. Интерфейс
|
||||
|
||||
Минимальные операции:
|
||||
|
||||
```c
|
||||
void sc_screen_clear(uint8_t attr);
|
||||
void sc_screen_cell(uint8_t x, uint8_t y, uint8_t ch, uint8_t attr);
|
||||
void sc_screen_text(uint8_t x, uint8_t y, const char *s, uint8_t attr,
|
||||
uint8_t max_width);
|
||||
void sc_screen_fill(uint8_t x, uint8_t y, uint8_t width,
|
||||
uint8_t ch, uint8_t attr);
|
||||
int sc_screen_scroll_rows(uint8_t x, uint8_t y, uint8_t width,
|
||||
uint8_t height, int8_t delta);
|
||||
void sc_screen_mark_rows(uint8_t first, uint8_t count);
|
||||
int sc_screen_flush(void);
|
||||
```
|
||||
|
||||
Экранный буфер — источник истины. Диалог рисуется в тот же буфер поверх
|
||||
панелей. Для PoC допустима перерисовка панелей после закрытия диалога вместо
|
||||
отдельного стека сохранённых окон.
|
||||
|
||||
### 8.2. Обновление
|
||||
|
||||
- Dirty mask занимает 32 бита в W2.
|
||||
- Изменение курсора помечает старую и новую строку.
|
||||
- Прокрутка панели помечает строки 1–27 её половины экрана.
|
||||
- При изменении `top` ровно на одну строку `sc_screen_scroll_rows()` сдвигает
|
||||
одновременно EMM-модель и экранный прямоугольник, затем renderer строит
|
||||
только открытую строку. PgUp/PgDn и большой скачок делают полный redraw
|
||||
области панели.
|
||||
- Смена каталога перерисовывает соответствующую половину и общие строки
|
||||
29–31.
|
||||
- Полный `sc_video_present_full()` используется после запуска, возврата из
|
||||
`.EXE` и крупных изменений.
|
||||
- `sc_video_present_rect()` группирует соседние грязные строки.
|
||||
|
||||
App-local WINREST wrapper проверен P1-пробой Commander: полный экран 80x32 и
|
||||
окно 20x2 из offset `0x2000` восстановились корректно. P2.1 затем подтвердил
|
||||
полную сверку через `RDCHAR`, оба направления прямоугольного scroll и 1000
|
||||
операций. Координатный backend принят как основной пакетный способ вывода;
|
||||
ограничения и артефакты описаны в
|
||||
[результатах P2.1](p2-screen-probe-results.md).
|
||||
|
||||
## 9. Ввод и команды
|
||||
|
||||
`sc_input.c` преобразует `getkey()` и `kbd_mod_state()` в `ScKeyEvent`:
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
uint8_t ascii;
|
||||
uint8_t scan;
|
||||
uint8_t modifiers;
|
||||
} ScKeyEvent;
|
||||
```
|
||||
|
||||
Модификаторы хранят только Shift, Ctrl и Alt; состояния CapsLock, NumLock и
|
||||
Rus/Lat не участвуют в выборе команды.
|
||||
|
||||
P1 установил особенность DSS: при модифицированной клавише бит 7 raw
|
||||
scan-кода установлен. Например, Ctrl+R даёт `93`, Ctrl+PgUp — `D9`, Alt+F1
|
||||
— `BB`. `sc_input` нормализует их в `13`, `59`, `3B` через `scan & 0x7F`,
|
||||
но только после чтения compact modifiers. Таблица команд никогда не содержит
|
||||
raw-коды с битом 7.
|
||||
|
||||
`sc_command.c` превращает событие в `ScCommand`. Остальные модули не должны
|
||||
сравнивать scan-коды непосредственно. Команды описаны в
|
||||
[commander-keymap.md](commander-keymap.md).
|
||||
|
||||
В MAME `kbd_mod_state()` в момент возврата `getkey()` сохранил активный
|
||||
модификатор: compact Ctrl=`02`, Alt=`04`, Shift=`01`. Для переносимости
|
||||
управляющие ASCII-коды (`Ctrl+R` и подобные) всё равно имеют явные алиасы,
|
||||
не зависящие от live-состояния.
|
||||
|
||||
## 10. Копирование как конечный автомат
|
||||
|
||||
Состояния PoC:
|
||||
|
||||
```text
|
||||
IDLE
|
||||
-> VALIDATE
|
||||
-> CREATE_TEMP
|
||||
-> OPEN_SOURCE
|
||||
-> COPY_CHUNK <----+
|
||||
-> CLOSE_FILES |
|
||||
-> RENAME_TEMP |
|
||||
-> REFRESH |
|
||||
-> DONE |
|
||||
|
|
||||
COPY_CHUNK -----------+ пока done < total
|
||||
|-> CANCEL -> CLEANUP -> DONE
|
||||
+-> ERROR -> CLEANUP -> DONE
|
||||
```
|
||||
|
||||
Правила:
|
||||
|
||||
- конечное имя проверяется до создания temp;
|
||||
- temp создаётся в целевом каталоге с `O_CREAT|O_EXCL`;
|
||||
- имя temp имеет форму `~SCxxxx.TMP` и перебирается с ограниченным числом
|
||||
попыток;
|
||||
- рабочая область находится в отдельной EMM-странице и временно отображается
|
||||
в W3; 4-КБ массива в W2 и изменяемого буфера в банке кода нет;
|
||||
- начальный логический размер блока — 4096 байт, чтобы между блоками часто
|
||||
обновлять прогресс и проверять отмену; после измерений он может быть увеличен
|
||||
вплоть до размера страницы;
|
||||
- чтение выполняется существующим `bank_read_page()` прямо из открытого fd в
|
||||
страницу; для записи нужен симметричный `bank_write_page()`, который сохраняет
|
||||
и восстанавливает отображение W3;
|
||||
- открыто не более двух fd;
|
||||
- успешность `write()` определяется его библиотечным контрактом, а не
|
||||
сырым DE ESTEX;
|
||||
- при short write оставшаяся часть блока дописывается или операция завершается
|
||||
ошибкой;
|
||||
- после успешного закрытия temp конечное имя проверяется повторно, и только
|
||||
затем temp переименовывается в него;
|
||||
- FAT date/time задаются temp через `PUT_D_T` перед close; после rename
|
||||
конечное имя получает исходные R/H/S/A через `ATTRIB`;
|
||||
- ошибка после rename помечается как ошибка финализации уже committed-файла,
|
||||
а не как полное отсутствие результата;
|
||||
- любое завершение проходит через единый cleanup;
|
||||
- после успеха перечитывается пассивная панель.
|
||||
|
||||
P19 добавляет policy существующего файла. Safe overwrite сначала переводит
|
||||
старый target в уникальный `~SBxxxx.BAK`, затем commit-ит temp. При отказе
|
||||
второго rename backup возвращается на место вместе с исходными атрибутами;
|
||||
после успешных метаданных нового target backup удаляется. `overwrite all` и
|
||||
`skip all` — локальное состояние одного F5. Readonly не наследует молча
|
||||
`overwrite all`, а повторно показывает choice-dialog.
|
||||
|
||||
Для версии 0.2 этот автомат расширяется очередью файлов, обходом каталогов,
|
||||
политиками overwrite/skip/rename и сохранением метаданных.
|
||||
|
||||
## 11. Запуск дочерней программы
|
||||
|
||||
Последовательность:
|
||||
|
||||
1. Завершить или запретить активный job.
|
||||
2. Сформировать проверенный абсолютный путь `.EXE`.
|
||||
3. Запомнить активную панель, курсоры и ожидаемый CWD; временно сделать путь
|
||||
активной панели текущим каталогом child.
|
||||
4. Выполнить синхронный ESTEX `EXEC` с `B=1` и сразу сохранить его результат
|
||||
до других вызовов libc.
|
||||
5. Восстановить ожидаемые диск и CWD.
|
||||
6. Повторно установить текстовый режим 80x32 и палитру Commander, не меняя
|
||||
сохранённую палитру для финального shutdown.
|
||||
7. Перечитать обе панели: внешняя программа потенциально могла изменить оба
|
||||
их каталога.
|
||||
8. Полностью вывести экран из screen page.
|
||||
9. При ошибке показать её штатной строкой состояния Commander.
|
||||
|
||||
P1 подтвердил контракт wrapper: C-сигнатура
|
||||
`p1_exec(uint8_t path_mode, const char *path)` получает mode в A и path в DE,
|
||||
затем перекладывает их в B и HL для ESTEX. `B=0` с коротким именем и `B=1`
|
||||
с явным относительным путём работают; probe получил `0x5A`. В Commander
|
||||
низкоуровневый `sc_platform_exec(path)` фиксирует `B=1`, принимает путь в HL
|
||||
по `__sdcccall(1)` и возвращает код завершения как `int` в DE.
|
||||
|
||||
### 11.1. Командная строка и `Ctrl+O` в 1.0
|
||||
|
||||
В VC 4.05/4.99 `Ctrl+O` переключает панели и user screen, а редактор `DosBuf`
|
||||
работает вместе с prompt и историей. Dos Navigator дополнительно показывает
|
||||
самостоятельный `TCommandLine` с горизонтальной прокруткой и историей. На Sprinter
|
||||
родительская DSS-shell приостановлена, поэтому её нельзя превратить в интерактивный
|
||||
фон простым скрытием панелей.
|
||||
|
||||
Поэтому 1.0 использует собственный pseudo-shell:
|
||||
|
||||
- однострочный prompt на экране панелей и полноэкранный режим делят один
|
||||
редактор, историю и модель текущего каталога;
|
||||
- `Ctrl+O` переключает режимы; возврат к панелям полностью строит их из
|
||||
`ScPanel`, поэтому отдельная копия экрана панелей не нужна;
|
||||
- builtin-команды минимум `cd`, смена диска, `dir`, `cls`, `pwd`, `help`;
|
||||
- внешняя команда запускается через подтверждённый ESTEX `EXEC`; после возврата
|
||||
pseudo-shell восстанавливает видеорежим, CWD и свою экранную модель;
|
||||
- вывод и скролл идут через тот же безоконный screen backend; прямой VRAM и BIOS
|
||||
window manager не используются;
|
||||
- полноэкранные ячейки могут повторно использовать текущую screen page. До
|
||||
фиксации поведения внешнего `.EXE` отдельной пробой проверяется `WINCOPY`.
|
||||
|
||||
## 12. Ошибки и владение ресурсами
|
||||
|
||||
Низкий уровень возвращает `0/-1` и устанавливает `errno`. UI не анализирует
|
||||
Carry Flag или регистры DSS.
|
||||
|
||||
Каждый ресурс имеет одного владельца:
|
||||
|
||||
| Ресурс | Владелец |
|
||||
|---|---|
|
||||
| общий EMM-блок PoC | `ScApp` |
|
||||
| физическая страница панели | `ScPanel`, без права освобождения блока |
|
||||
| экранная страница | `sc_screen`, без права освобождения блока |
|
||||
| рабочая страница копирования | `ScApp`/copy job, без права освобождения блока |
|
||||
| fd источника и temp | активный `ScCopyJob` |
|
||||
| исходный видеорежим | `ScApp` |
|
||||
| временно отображённая W3 | текущая функция storage/screen |
|
||||
|
||||
Cleanup выполняется в обратном порядке инициализации. Нельзя прятать
|
||||
владение fd или EMM-блоком в локальном static-состоянии модуля.
|
||||
|
||||
## 13. Предварительная карта кода
|
||||
|
||||
HOME/W2 содержит только постоянно необходимое:
|
||||
|
||||
- crt0, банковские трамплины и используемую libc;
|
||||
- init/cleanup;
|
||||
- главный цикл;
|
||||
- ввод и базовый dispatch;
|
||||
- EMM accessors;
|
||||
- screen flush;
|
||||
- критическую обработку ошибок.
|
||||
|
||||
Предварительные банки:
|
||||
|
||||
| Банк | PoC | Полная версия |
|
||||
|---:|---|---|
|
||||
| 1 | panel scan, sort, redraw model | панели, сортировки, режимы панели |
|
||||
| 2 | copy job, operation dialog | копирование/перемещение/удаление |
|
||||
| 3 | не нужен | меню, конфигурация, история |
|
||||
| 4 | не нужен | поиск и дерево |
|
||||
| 5 | не нужен | архивные/виртуальные источники |
|
||||
|
||||
Номер банка является деталью сборки, а не публичным ABI. Банковские модули
|
||||
не должны хранить долгоживущие payload-данные в локальном сегменте банка.
|
||||
Краткоживущий `ffblk_t` является обычным near-буфером; закреплять его в хвосте
|
||||
W1 бессмысленно, поскольку граница больших каталогов находится внутри DSS.
|
||||
|
||||
После каждой сборки проверяются:
|
||||
|
||||
- размер HOME вместе с DATA/BSS и heap;
|
||||
- запас каждого банка до 16 КБ;
|
||||
- отсутствие прямых вызовов в чужой банк;
|
||||
- расположение трамплинов;
|
||||
- стек W2 при всех вызовах ESTEX.
|
||||
|
||||
## 14. Эволюция после PoC
|
||||
|
||||
### Версия 0.2
|
||||
|
||||
- одностраничное хранилище остаётся ограничено 640 записями;
|
||||
- выбранные файлы и маски;
|
||||
- очередь операций;
|
||||
- рекурсивный обход;
|
||||
- local rename, mkdir, recursive group copy/delete выполнены; group move и
|
||||
защитная policy остаются;
|
||||
- транзакционная перезапись файлов и conflict-policy выполнены; directory
|
||||
merge и delete/move policy остаются;
|
||||
- атрибуты и время обычных файлов выполнены; метаданные каталогов — позже.
|
||||
|
||||
### Версия 0.4
|
||||
|
||||
- командная строка, история и полноэкранный pseudo-shell по `Ctrl+O`;
|
||||
- associations/user menu;
|
||||
- внешний viewer/editor/setup;
|
||||
- конфигурационный файл;
|
||||
- переключение дисков.
|
||||
|
||||
### Версия 0.6
|
||||
|
||||
- quick view и info source;
|
||||
- дерево и поиск;
|
||||
- фильтры и сравнение каталогов;
|
||||
- подсчёт размеров каталогов;
|
||||
- описания файлов.
|
||||
|
||||
### Версия 0.8+
|
||||
|
||||
- `SC_SOURCE_SEARCH` как виртуальная панель;
|
||||
- `SC_SOURCE_ARCHIVE`;
|
||||
- протокол внешних helper-программ;
|
||||
- мышь, помощь, локализация;
|
||||
- измеренная оптимизация кэшей и renderer.
|
||||
|
||||
### Версия 2.0+
|
||||
|
||||
- динамическое выделение EMM-страниц панели;
|
||||
- предел 1024/2048 или отсутствие продуктового предела после измерений;
|
||||
- межстраничная сортировка без полной копии каталога в W2;
|
||||
- деградация при нехватке EMM и тесты частично выделенных цепочек страниц.
|
||||
|
||||
## 15. Архитектурные запреты
|
||||
|
||||
- Нельзя вызывать DSS при стеке вне W2.
|
||||
- Нельзя держать одновременно восемь файловых дескрипторов.
|
||||
- Нельзя сохранять W3 pointer в состоянии приложения.
|
||||
- Нельзя менять W1 вручную из банковской функции.
|
||||
- Нельзя вызывать чужой код при оставленной временной странице W3.
|
||||
- Нельзя выполнять разрушительную операцию без известного cleanup-path.
|
||||
- Нельзя смешивать вывод через `printf` с экранной моделью во время работы UI.
|
||||
- Нельзя добавлять функцию DN только потому, что она присутствует в DN;
|
||||
каждая функция должна соответствовать roadmap Commander.
|
||||
@@ -0,0 +1,324 @@
|
||||
# Sprinter Commander: клавиши и команды
|
||||
|
||||
Статус: PoC и первые команды 0.2 подтверждены в MAME
|
||||
|
||||
Связанные документы:
|
||||
[требования](commander-requirements.md),
|
||||
[архитектура](commander-architecture.md),
|
||||
[план разработки](commander-roadmap.md)
|
||||
|
||||
## 1. Назначение
|
||||
|
||||
Документ отделяет физические клавиши Sprinter от логических команд
|
||||
Commander. Только модуль `sc_input.c` знает scan-коды, а только
|
||||
`sc_command.c` знает таблицу их назначения.
|
||||
|
||||
Панели и операции получают значения `ScCommand`, а не результаты `getkey()`.
|
||||
Это позволяет позднее добавить мышь, переназначение клавиш и альтернативную
|
||||
клавиатуру без изменений файловых функций.
|
||||
|
||||
## 2. Формат события
|
||||
|
||||
```c
|
||||
enum {
|
||||
SC_MOD_SHIFT = 0x01,
|
||||
SC_MOD_CTRL = 0x02,
|
||||
SC_MOD_ALT = 0x04
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint8_t ascii;
|
||||
uint8_t scan;
|
||||
uint8_t modifiers;
|
||||
} ScKeyEvent;
|
||||
```
|
||||
|
||||
Правила нормализации:
|
||||
|
||||
- обычный символ записывается в `ascii`;
|
||||
- расширенная клавиша записывается в `scan`, `ascii == 0`;
|
||||
- при активном Ctrl/Alt/Shift DSS может установить бит 7 scan-кода;
|
||||
диагностическая трассировка показывает raw-значение, а `sc_input`
|
||||
передаёт командам базовый `scan & 0x7F`;
|
||||
- левый и правый Shift объединяются в `SC_MOD_SHIFT`;
|
||||
- левый и правый Ctrl объединяются в `SC_MOD_CTRL`;
|
||||
- левый и правый Alt объединяются в `SC_MOD_ALT`;
|
||||
- CapsLock, NumLock, ScrollLock, Insert и Rus/Lat не являются модификаторами
|
||||
команды;
|
||||
- управляющий ASCII-код имеет приоритет над live-состоянием модификатора;
|
||||
- неизвестная комбинация преобразуется в `SC_CMD_NONE`.
|
||||
|
||||
## 3. Подтверждённые расширенные scan-коды
|
||||
|
||||
Значения уже объявлены в `<conio.h>`:
|
||||
|
||||
| Клавиша | Scan-код | Макрос |
|
||||
|---|---:|---|
|
||||
| F1 | `0x3B` | `KEY_F1` |
|
||||
| F2 | `0x3C` | `KEY_F2` |
|
||||
| F3 | `0x3D` | `KEY_F3` |
|
||||
| F4 | `0x3E` | `KEY_F4` |
|
||||
| F5 | `0x3F` | `KEY_F5` |
|
||||
| F6 | `0x40` | `KEY_F6` |
|
||||
| F7 | `0x41` | `KEY_F7` |
|
||||
| F8 | `0x42` | `KEY_F8` |
|
||||
| F9 | `0x43` | `KEY_F9` |
|
||||
| F10 | `0x44` | `KEY_F10` |
|
||||
| End | `0x51` | `KEY_END` |
|
||||
| Down | `0x52` | `KEY_DOWN` |
|
||||
| PgDn | `0x53` | `KEY_PGDN` |
|
||||
| Left | `0x54` | `KEY_LEFT` |
|
||||
| Delete | `0x55` | `KEY_DEL` |
|
||||
| Right | `0x56` | `KEY_RIGHT` |
|
||||
| Home | `0x57` | `KEY_HOME` |
|
||||
| Up | `0x58` | `KEY_UP` |
|
||||
| PgUp | `0x59` | `KEY_PGUP` |
|
||||
| Insert | `0x50` | `KEY_INS` |
|
||||
|
||||
Все значения таблицы, включая `Insert` и `Delete`, подтверждены P1-пробой в
|
||||
MAME. `F11` и `F12` в P1 не проверялись и остаются неподтверждёнными до
|
||||
появления команды, которая их использует.
|
||||
|
||||
Фактическая последовательность MAME для навигации была:
|
||||
`Up=58`, `Down=52`, `Left=54`, `Right=56`, `Home=57`, `End=51`,
|
||||
`PgUp=59`, `PgDn=53`, `Insert=50`, `Delete=55`. F1–F10 дали непрерывный
|
||||
диапазон `3B..44`.
|
||||
|
||||
## 4. Логические команды
|
||||
|
||||
Численные значения enum не являются стабильным ABI до версии 0.2.
|
||||
|
||||
```c
|
||||
typedef enum {
|
||||
SC_CMD_NONE = 0,
|
||||
|
||||
SC_CMD_CURSOR_UP,
|
||||
SC_CMD_CURSOR_DOWN,
|
||||
SC_CMD_CURSOR_LEFT,
|
||||
SC_CMD_CURSOR_RIGHT,
|
||||
SC_CMD_PAGE_UP,
|
||||
SC_CMD_PAGE_DOWN,
|
||||
SC_CMD_HOME,
|
||||
SC_CMD_END,
|
||||
SC_CMD_PANEL_SWITCH,
|
||||
SC_CMD_OPEN,
|
||||
SC_CMD_PARENT,
|
||||
SC_CMD_ROOT,
|
||||
SC_CMD_REFRESH,
|
||||
SC_CMD_CANCEL,
|
||||
|
||||
SC_CMD_SELECT,
|
||||
SC_CMD_SELECT_MASK,
|
||||
SC_CMD_UNSELECT_MASK,
|
||||
SC_CMD_INVERT_SELECTION,
|
||||
|
||||
SC_CMD_HELP,
|
||||
SC_CMD_USER_MENU,
|
||||
SC_CMD_VIEW,
|
||||
SC_CMD_EDIT,
|
||||
SC_CMD_COPY,
|
||||
SC_CMD_MOVE_RENAME,
|
||||
SC_CMD_MKDIR,
|
||||
SC_CMD_DELETE,
|
||||
SC_CMD_MAIN_MENU,
|
||||
SC_CMD_QUIT,
|
||||
|
||||
SC_CMD_LEFT_DRIVE,
|
||||
SC_CMD_RIGHT_DRIVE,
|
||||
SC_CMD_SWAP_PANELS,
|
||||
SC_CMD_TOGGLE_SHELL,
|
||||
SC_CMD_QUICK_VIEW,
|
||||
SC_CMD_INFO_PANEL,
|
||||
SC_CMD_FIND,
|
||||
SC_CMD_TREE
|
||||
} ScCommand;
|
||||
```
|
||||
|
||||
Дополнительные команды вводятся при появлении соответствующей функции, а не
|
||||
заранее в обработчиках-заглушках.
|
||||
|
||||
## 5. Клавиши PoC 0.1
|
||||
|
||||
### 5.1. Навигация панели
|
||||
|
||||
| Клавиша | Команда | Поведение PoC |
|
||||
|---|---|---|
|
||||
| `Up` | `SC_CMD_CURSOR_UP` | На одну запись вверх |
|
||||
| `Down` | `SC_CMD_CURSOR_DOWN` | На одну запись вниз |
|
||||
| `PgUp` | `SC_CMD_PAGE_UP` | На 27 записей вверх |
|
||||
| `PgDn` | `SC_CMD_PAGE_DOWN` | На 27 записей вниз |
|
||||
| `Home` | `SC_CMD_HOME` | Первая запись |
|
||||
| `End` | `SC_CMD_END` | Последняя запись |
|
||||
| `Left` | `SC_CMD_NONE` | Не используется в одноколоночной панели PoC |
|
||||
| `Right` | `SC_CMD_NONE` | Не используется в одноколоночной панели PoC |
|
||||
| `Tab` | `SC_CMD_PANEL_SWITCH` | Переключить активную панель |
|
||||
| `Enter` | `SC_CMD_OPEN` | Войти в каталог или запустить `.EXE` |
|
||||
| `Ctrl+PgUp` | `SC_CMD_PARENT` | Перейти в родительский каталог |
|
||||
| `Backspace` | `SC_CMD_PARENT` | То же, пока командной строки нет |
|
||||
| `Ctrl+R` | `SC_CMD_REFRESH` | Перечитать активную панель |
|
||||
| `Ctrl+F3` | `SC_CMD_SORT_NAME` | Имя; повторное нажатие меняет направление |
|
||||
| `Ctrl+F4` | `SC_CMD_SORT_EXTENSION` | Расширение; повтор меняет направление |
|
||||
| `Ctrl+F5` | `SC_CMD_SORT_DATE` | Дата/время; повтор меняет направление |
|
||||
| `Ctrl+F6` | `SC_CMD_SORT_SIZE` | Размер; повтор меняет направление |
|
||||
| `Insert` | `SC_CMD_SELECT` | Выбрать/снять текущую запись и перейти вниз |
|
||||
| `*` | `SC_CMD_INVERT_SELECTION` | Инвертировать только файлы панели; каталоги не менятся |
|
||||
|
||||
`SC_CMD_CURSOR_LEFT/RIGHT` сохраняются в enum для будущего многоколоночного
|
||||
режима, но PoC их не генерирует.
|
||||
|
||||
### 5.2. Функциональные клавиши PoC
|
||||
|
||||
| Клавиша | Команда | Состояние |
|
||||
|---|---|---|
|
||||
| `F1` | `SC_CMD_HELP` | Зарезервирована, неактивна |
|
||||
| `F2` | `SC_CMD_USER_MENU` | Зарезервирована, неактивна |
|
||||
| `F3` | `SC_CMD_VIEW` | Зарезервирована, неактивна |
|
||||
| `F4` | `SC_CMD_EDIT` | Зарезервирована, неактивна |
|
||||
| `F5` | `SC_CMD_COPY` | Один файл/каталог либо группа выбранных файлов и directory roots |
|
||||
| `F6` | `SC_CMD_MOVE_RENAME` | Локальный rename одной записи; move/group позже в 0.2 |
|
||||
| `F7` | `SC_CMD_MKDIR` | Создать каталог; 0.2, выполнено |
|
||||
| `F8` | `SC_CMD_DELETE` | Одна запись либо выбранная группа; каталоги группы удаляются рекурсивно после одного подтверждения |
|
||||
| `F9` | `SC_CMD_MAIN_MENU` | Зарезервирована, неактивна |
|
||||
| `F10` | `SC_CMD_QUIT` | Открыть подтверждение штатного выхода |
|
||||
|
||||
PoC не должен выполнять разрушительное действие по неактивной клавише.
|
||||
|
||||
### 5.3. Общие клавиши PoC
|
||||
|
||||
| Клавиша | Команда | Контекст |
|
||||
|---|---|---|
|
||||
| `Esc` | `SC_CMD_CANCEL` | Закрыть сообщение или отменить job |
|
||||
| `Esc` | `SC_CMD_NONE` | Обычный режим панелей |
|
||||
| `Enter` | подтвердить | Диалог сообщения |
|
||||
| `Y`/`N` | да/нет | Диалог подтверждения, если он появится |
|
||||
|
||||
## 6. Контекстная обработка
|
||||
|
||||
Одна клавиша может иметь разное действие в зависимости от режима. Приоритет
|
||||
обработчиков фиксирован:
|
||||
|
||||
1. Активная аварийная ошибка.
|
||||
2. Модальный диалог.
|
||||
3. Активный job.
|
||||
4. Командная строка или полноэкранный pseudo-shell версии 1.0.
|
||||
5. Активная панель.
|
||||
6. Глобальные команды приложения.
|
||||
|
||||
Примеры:
|
||||
|
||||
- Во время копирования `Esc` запрашивает отмену, но `F10` не завершает
|
||||
процесс немедленно.
|
||||
- В диалоге `Enter` подтверждает кнопку, а не открывает файл под курсором.
|
||||
- После закрытия диалога то же событие не передаётся панели повторно.
|
||||
- `F10` во время job игнорируется с подсказкой отменять операцию через `Esc`;
|
||||
подтверждение выхода открывается только после завершения или отмены job.
|
||||
- В режиме панелей печатный символ передаётся однострочному редактору;
|
||||
`Enter` выполняет непустую строку, а при пустой строке открывает запись.
|
||||
- В pseudo-shell `Up/Down` листают историю команд, а не панель; `Ctrl+O`
|
||||
возвращает панели с сохранёнными путями и курсорами.
|
||||
- `Esc` сначала очищает непустую команду, затем закрывает полноэкранный режим;
|
||||
событие не передаётся следующему контексту повторно.
|
||||
|
||||
## 7. Нормализация управляющих ASCII-кодов
|
||||
|
||||
На части клавиатур `Ctrl+буква` может приходить как ASCII `0x01–0x1A` без
|
||||
надёжного сохранения состояния Ctrl. Поэтому ввод должен распознавать
|
||||
следующие значения независимо от `kbd_mod_state()`:
|
||||
|
||||
| ASCII | Комбинация | Команда |
|
||||
|---:|---|---|
|
||||
| `0x12` | `Ctrl+R` | `SC_CMD_REFRESH` |
|
||||
| `0x15` | `Ctrl+U` | `SC_CMD_SWAP_PANELS`, после PoC |
|
||||
| `0x0F` | `Ctrl+O` | `SC_CMD_TOGGLE_SHELL`, обязательно к 1.0 |
|
||||
| `0x11` | `Ctrl+Q` | `SC_CMD_QUICK_VIEW`, после PoC |
|
||||
| `0x0C` | `Ctrl+L` | `SC_CMD_INFO_PANEL`, после PoC |
|
||||
|
||||
`Tab` (`0x09`), `Enter` (`0x0D`), `Esc` (`0x1B`) и `Backspace` (`0x08`)
|
||||
обрабатываются как обычные управляющие ASCII-клавиши.
|
||||
|
||||
P1 в MAME показал второй, фактически основной для текущего DSS путь:
|
||||
|
||||
| Комбинация | ASCII | Raw scan | Base scan | `kbd_mod_state()` | Compact |
|
||||
|---|---:|---:|---:|---:|---:|
|
||||
| `Ctrl+R` | `00` | `93` | `13` | `0028` | `02` |
|
||||
| `Ctrl+PgUp` | `00` | `D9` | `59` | `0028` | `02` |
|
||||
| `Alt+F1` | `00` | `BB` | `3B` | `0014` | `04` |
|
||||
| `Esc` | `1B` | `01` | `01` | `0000` | `00` |
|
||||
|
||||
Таким образом, `sc_input` обязан поддерживать оба представления: сначала
|
||||
управляющий ASCII-код, затем пару `(base scan, modifiers)`. Проверять raw
|
||||
scan `93/D9/BB` прямо в таблице команд нельзя: базовый scan с модификатором
|
||||
получается снятием бита 7. Скриншоты пробы находятся в
|
||||
[`artifacts/mame-p1/keyboard-commander`](../artifacts/mame-p1/keyboard-commander/sprinter/0003.png).
|
||||
|
||||
## 8. Предварительная карта полной версии
|
||||
|
||||
Эта таблица резервирует привычные сочетания VC, но не является требованием
|
||||
PoC.
|
||||
|
||||
| Клавиша | Команда | Планируемая версия |
|
||||
|---|---|---:|
|
||||
| `Insert` | Выделить/снять выделение и перейти вниз | 0.2, выполнено |
|
||||
| `Gray +` или `+` | Выделить по маске | 0.2, обычный `+` выполнен |
|
||||
| `Gray -` или `-` | Снять выделение по маске | 0.2, обычный `-` выполнен |
|
||||
| `Gray *` или `*` | Инвертировать выделение | 0.2, `*` выполнено |
|
||||
| `F1` | Помощь | 0.4 |
|
||||
| `F2` | Пользовательское меню | 0.4 |
|
||||
| `F3` | Просмотр | 0.4 |
|
||||
| `F4` | Редактор | 0.4 |
|
||||
| `F5` | Копирование | 0.1/0.2 |
|
||||
| `F6` | Перемещение/переименование | 0.2, локальный rename выполнен |
|
||||
| `F7` | Создать каталог | 0.2, выполнено |
|
||||
| `F8` | Удалить | 0.2, single и recursive group выполнены |
|
||||
| `F9` | Главное меню | 0.4 |
|
||||
| `F10` | Выход | 0.1 |
|
||||
| `Alt+F1` | Диск левой панели | 0.4 |
|
||||
| `Alt+F2` | Диск правой панели | 0.4 |
|
||||
| `Alt+F7` | Поиск файлов | 0.6 |
|
||||
| `Alt+F10` | Дерево каталогов | 0.6 |
|
||||
| `Ctrl+R` | Перечитать панель | 0.1 |
|
||||
| `Ctrl+F3..F6` | Name/ext/date/size, повтор меняет направление | 0.2 |
|
||||
| `Ctrl+U` | Обменять панели местами | 0.4 |
|
||||
| `Ctrl+O` | Панели / интерактивный полноэкранный pseudo-shell | 0.4/1.0 |
|
||||
| `Ctrl+PgUp` | Родительский каталог | 0.1 |
|
||||
| `Ctrl+\` | Корень текущего диска | 0.2 |
|
||||
| `Ctrl+Q` | Quick view | 0.6 |
|
||||
| `Ctrl+L` | Информационная панель | 0.6 |
|
||||
|
||||
Shift-варианты функциональных клавиш не резервируются до появления
|
||||
конкретной необходимости. Это предотвращает преждевременное копирование всей
|
||||
таблицы VC без соответствующей функции.
|
||||
|
||||
## 9. Нижняя строка клавиш
|
||||
|
||||
Строка 31 содержит десять сегментов шириной по восемь символов. В PoC:
|
||||
|
||||
```text
|
||||
1 Help 2 Menu 3 View 4 Edit 5 Copy 6 Ren 7 Mkdir 8 Delete9 Menu 10 Quit
|
||||
```
|
||||
|
||||
Это логическая схема, а не точная строка байтов. Названия сокращаются до
|
||||
доступной ширины. Поддерживаемые действия (`F5`, `F10`) рисуются активным
|
||||
цветом, остальные — неактивным.
|
||||
|
||||
После добавления модификатора строка клавиш может временно показывать
|
||||
назначения `Alt`/`Ctrl`, но это не входит в PoC.
|
||||
|
||||
## 10. Проверки клавиатуры P1/P2
|
||||
|
||||
В P1 на MAME подтверждены F1–F10, восемь навигационных клавиш,
|
||||
Insert/Delete, Esc, Ctrl+R, Ctrl+PgUp и Alt+F1. Отдельная проба также
|
||||
подтвердила compact-состояния Shift=`01`, Ctrl=`02`, Alt=`04`.
|
||||
|
||||
В P2 остаются поведенческие проверки уже внутри UI:
|
||||
|
||||
1. `Tab`, `Enter` и `Backspace` во всех контекстах.
|
||||
2. Одновременное удержание нескольких модификаторов.
|
||||
3. CapsLock, NumLock и Rus/Lat.
|
||||
4. Отсутствие повторного выполнения события после закрытия диалога.
|
||||
5. Keyboard repeat на границах списка.
|
||||
6. Приём `Esc` во время копирования большого файла.
|
||||
7. Повтор P1-набора на реальном Sprinter.
|
||||
|
||||
Любое расхождение с таблицей сначала оформляется отдельным репро и только
|
||||
затем учитывается в Commander.
|
||||
@@ -0,0 +1,427 @@
|
||||
# Sprinter Commander: требования к PoC и границы проекта
|
||||
|
||||
Статус: требования PoC зафиксированы; gate P1 и ядро P2.1 пройдены в MAME
|
||||
|
||||
Целевая платформа: Sprinter Sp2000, Z80, DSS 1.71.57
|
||||
|
||||
Инструментарий: SDCC 4.5, `sprinter-cc`, MAME 0.287 / BIOS 3.06
|
||||
|
||||
Связанные документы:
|
||||
[архитектура](commander-architecture.md),
|
||||
[клавиши и команды](commander-keymap.md),
|
||||
[план разработки](commander-roadmap.md),
|
||||
[экранный backend P2](p2-screen-backend.md),
|
||||
[результаты P2.1](p2-screen-probe-results.md),
|
||||
[результаты skeleton P2](p2-skeleton-results.md),
|
||||
[результаты файловых панелей P3](p3-panel-results.md)
|
||||
|
||||
## 1. Назначение документа
|
||||
|
||||
Документ фиксирует проверяемые требования к первой работающей версии
|
||||
файлового менеджера для Sprinter. Рабочее имя проекта — **Sprinter
|
||||
Commander**, рабочее имя исполняемого файла — `SPRCMD.EXE`.
|
||||
|
||||
PoC не является портом Volkov Commander. Это новая программа на C,
|
||||
повторяющая привычную модель двухпанельного файлового менеджера с учётом
|
||||
архитектуры памяти и API Sprinter.
|
||||
|
||||
## 2. Референсы и правила их использования
|
||||
|
||||
### 2.1. Volkov Commander 4.05
|
||||
|
||||
Используется как минимальный поведенческий образец:
|
||||
|
||||
- компоновка двух панелей;
|
||||
- клавиатурная навигация;
|
||||
- назначение функциональных клавиш;
|
||||
- общий сценарий работы пользователя.
|
||||
|
||||
### 2.2. Volkov Commander 4.99
|
||||
|
||||
Используется как образец декомпозиции и источник функций для дальнейших
|
||||
версий:
|
||||
|
||||
- отдельные подсистемы панелей, экрана, клавиатуры и диалогов;
|
||||
- отдельные операции копирования, удаления и изменения атрибутов;
|
||||
- просмотрщик, редактор, поиск и дерево каталогов;
|
||||
- описания файлов и архивные панели;
|
||||
- разделение резидентного ядра и загружаемых частей.
|
||||
|
||||
### 2.3. Dos Navigator 1.51
|
||||
|
||||
Используется только как архитектурный референс:
|
||||
|
||||
- единый цикл событий и команды приложения;
|
||||
- отделение модели панели от её представления;
|
||||
- абстракция источника файлов;
|
||||
- длительные операции с прогрессом и отменой;
|
||||
- виртуальные панели для архивов и результатов поиска;
|
||||
- история, конфигурация и внешние инструменты.
|
||||
|
||||
Объектная система Turbo Vision, DOS overlays, XMS/EMS-код и прикладные
|
||||
модули DN не переносятся.
|
||||
|
||||
Исходный код, строки интерфейса и ресурсы референсов не копируются без
|
||||
отдельного решения по лицензированию. Реализация проекта должна оставаться
|
||||
собственной реализацией по описанному поведению.
|
||||
|
||||
## 3. Определения версий
|
||||
|
||||
**PoC 0.1** доказывает техническую реализуемость панелей, EMM-хранилища,
|
||||
экранного буфера, копирования и запуска программ.
|
||||
|
||||
**Рабочая версия** пригодна для повседневной работы с файлами и включает
|
||||
безопасные групповые и рекурсивные операции.
|
||||
|
||||
**Полная версия 1.0** соответствует основным возможностям VC 4.99, но не
|
||||
пытается повторить весь набор утилит Dos Navigator.
|
||||
|
||||
## 4. Пользовательский сценарий PoC
|
||||
|
||||
После запуска программа переключается в текстовый режим 80x32, выделяет
|
||||
необходимую память и показывает две панели. Обе панели первоначально
|
||||
открывают текущий каталог запуска.
|
||||
|
||||
Пользователь может:
|
||||
|
||||
1. Перемещать курсор по активной панели.
|
||||
2. Переключать активную панель клавишей `Tab`.
|
||||
3. Входить в каталог клавишей `Enter`.
|
||||
4. Возвращаться в родительский каталог.
|
||||
5. Перечитывать содержимое активной панели.
|
||||
6. Копировать один обычный файл в каталог пассивной панели.
|
||||
7. Запускать выбранный `.EXE` и возвращаться в Commander.
|
||||
8. Выйти по `F10` с освобождением всех ресурсов.
|
||||
|
||||
## 5. Компоновка экрана PoC
|
||||
|
||||
Экран имеет 80 столбцов и 32 строки.
|
||||
|
||||
| Строки | Назначение |
|
||||
|---:|---|
|
||||
| 0 | Заголовки и текущие пути обеих панелей |
|
||||
| 1–27 | Списки файлов, по 27 видимых записей |
|
||||
| 28 | Итоги панелей: число записей и общий размер |
|
||||
| 29 | Информация о текущей записи активной панели |
|
||||
| 30 | Строка состояния, ошибок и прогресса |
|
||||
| 31 | Подсказки функциональных клавиш |
|
||||
|
||||
Левая панель занимает столбцы 0–39, правая — 40–79. Точные символы рамок
|
||||
и палитра не являются контрактом PoC, но все символы должны корректно
|
||||
отображаться в CP866.
|
||||
|
||||
Renderer задаёт смысловые роли атрибутов независимо от конкретной палитры.
|
||||
P2.2 подтвердил многоцветные планы в MAME: тема сохраняет исходные записи,
|
||||
задаёт обе FLASH-пары и восстанавливает палитру при выходе. До аппаратного
|
||||
подтверждения активная и пассивная панели всё равно различаются не только
|
||||
цветом, но и глифами/маркерами.
|
||||
|
||||
Полная перерисовка выполняется из собственного экранного буфера через
|
||||
`sc_video`. Commander не использует дескрипторы окон BIOS: панели, меню и
|
||||
диалоги — логические прямоугольники приложения. `sc_video` выводит их в
|
||||
координаты глобального экрана через ESTEX/BIOS-функции без идентификатора
|
||||
окна; прямой доступ к VRAM не используется.
|
||||
Изменение курсора или одной строки панели не должно требовать повторного
|
||||
чтения каталога.
|
||||
|
||||
Прокрутка списка на одну строку может использовать прямоугольный ESTEX
|
||||
`SCROLL 55h`, как `mdview2`, но рамка и соседняя панель остаются неподвижны,
|
||||
а экранный EMM-буфер обновляется синхронно. Эта служба не является BIOS-
|
||||
дескриптором окна; её аппаратная реализация исходниками `mdview2` не
|
||||
доказана. Псевдографика задаётся raw-кодами CP866 из единого `sc_glyphs.h`.
|
||||
|
||||
## 6. Функциональные требования PoC
|
||||
|
||||
### REQ-BOOT: запуск и завершение
|
||||
|
||||
- **REQ-BOOT-01.** Программа должна явно установить текстовый режим 80x32.
|
||||
- **REQ-BOOT-02.** Перед изменением видеорежима и текущего каталога должны
|
||||
быть сохранены исходные значения, если DSS позволяет их получить.
|
||||
- **REQ-BOOT-03.** При штатном выходе освобождаются все EMM-блоки и закрываются
|
||||
все открытые файлы.
|
||||
- **REQ-BOOT-04.** Ошибка инициализации не должна оставлять изменённый
|
||||
видеорежим или выделенную память.
|
||||
- **REQ-BOOT-05.** Если обязательные четыре EMM-страницы выделить нельзя,
|
||||
программа выводит понятную ошибку и завершает работу.
|
||||
- **REQ-BOOT-06.** `F10` в обычном режиме открывает подтверждение выхода;
|
||||
положительный ответ запускает штатный cleanup, отрицательный возвращает к
|
||||
панелям.
|
||||
|
||||
### REQ-PANEL: панели
|
||||
|
||||
- **REQ-PANEL-01.** Панели имеют независимые пути, курсор и позицию прокрутки.
|
||||
- **REQ-PANEL-02.** Активная панель визуально отличается от пассивной.
|
||||
- **REQ-PANEL-03.** Каталоги отображаются перед обычными файлами.
|
||||
- **REQ-PANEL-04.** Внутри групп записи сортируются по имени без учёта
|
||||
регистра ASCII.
|
||||
- **REQ-PANEL-05.** Для записи отображаются имя, размер, дата, время и
|
||||
атрибут каталога/файла в объёме, который помещается в ширину панели.
|
||||
- **REQ-PANEL-06.** После перечитывания каталог по возможности сохраняет
|
||||
курсор на прежнем имени; если оно исчезло — на ближайшей допустимой
|
||||
позиции.
|
||||
- **REQ-PANEL-07.** Полный scan DSS использует `*.*`; шаблон `*` не считается
|
||||
эквивалентным, поскольку не возвращает обычные имена с расширением.
|
||||
- **REQ-PANEL-08.** Пустой каталог отображается корректно и остаётся
|
||||
управляемым.
|
||||
- **REQ-PANEL-09.** Родительский каталог представлен синтетической записью
|
||||
`..`, кроме корня диска.
|
||||
- **REQ-PANEL-10.** Записи `.` и `..`, возвращённые DSS, не должны
|
||||
дублировать синтетическую запись.
|
||||
- **REQ-PANEL-11.** Во всей линейке 0.1–1.0 панель хранит не более
|
||||
640 записей, включая синтетическую `..`. При превышении лимита
|
||||
показывается предупреждение; запись за границу EMM-страницы
|
||||
запрещена. Многостраничные панели и предел более 640 записей
|
||||
относятся только к версии 2.0+.
|
||||
- **REQ-PANEL-12.** Если DSS завершил `F_FIRST/F_NEXT` кодом 35
|
||||
(`TOO_MANY_FILES_IN_DIR`), уже прочитанные записи остаются доступны, а
|
||||
последней добавляется красная виртуальная запись `>>> MORE...`. Она
|
||||
сообщает о недоступном остатке каталога, не сортируется вместе с файлами
|
||||
и не может быть целью файловой операции.
|
||||
|
||||
### REQ-NAV: навигация
|
||||
|
||||
- **REQ-NAV-01.** Поддерживаются `Up`, `Down`, `PgUp`, `PgDn`, `Home`, `End`.
|
||||
- **REQ-NAV-02.** `Tab` переключает активную панель без изменения каталогов.
|
||||
- **REQ-NAV-03.** `Enter` на каталоге открывает его в активной панели.
|
||||
- **REQ-NAV-04.** `Ctrl+PgUp` и `Backspace` открывают родительский каталог.
|
||||
- **REQ-NAV-05.** `Ctrl+R` перечитывает активную панель.
|
||||
- **REQ-NAV-06.** Навигация внутри уже прочитанной панели не обращается к
|
||||
файловой системе.
|
||||
- **REQ-NAV-07.** Операция с активной панелью не должна неожиданно менять
|
||||
путь пассивной панели.
|
||||
|
||||
### REQ-FS: работа с DSS и путями
|
||||
|
||||
- **REQ-FS-01.** PoC работает с именами DOS 8.3. Длинные имена не входят в
|
||||
требования до отдельного подтверждения поддержки DSS.
|
||||
- **REQ-FS-02.** Максимальный внутренний путь — 255 байт плюс завершающий
|
||||
ноль.
|
||||
- **REQ-FS-03.** Склейка пути и имени проверяет переполнение до обращения к
|
||||
DSS.
|
||||
- **REQ-FS-04.** Контекст текущего каталога DSS считается глобальным
|
||||
ресурсом. После временной смены каталога модуль обязан восстановить
|
||||
ожидаемый каталог приложения.
|
||||
- **REQ-FS-05.** Одновременно приложение держит не более трёх файловых
|
||||
дескрипторов; штатная операция копирования использует два.
|
||||
- **REQ-FS-06.** Конец перебора каталога отличается от настоящей ошибки
|
||||
`fnext()` по `errno` и контексту операции.
|
||||
|
||||
### REQ-COPY: безопасное копирование одного файла
|
||||
|
||||
- **REQ-COPY-01.** `F5` копирует выбранный обычный файл в текущий каталог
|
||||
пассивной панели.
|
||||
- **REQ-COPY-02.** Копирование каталогов и групп файлов в PoC запрещено с
|
||||
явным сообщением.
|
||||
- **REQ-COPY-03.** Если конечное имя уже существует, PoC отказывается от
|
||||
операции и не изменяет существующий файл.
|
||||
- **REQ-COPY-04.** Данные копируются блоками, а общий размер и позиция имеют
|
||||
32-битный тип.
|
||||
- **REQ-COPY-05.** Поддерживаются файлы больше 64 КБ.
|
||||
- **REQ-COPY-06.** Целевой файл сначала создаётся под уникальным временным
|
||||
именем через семантику `O_EXCL`, после успешного закрытия переименовывается
|
||||
в конечное имя.
|
||||
- **REQ-COPY-07.** При ошибке или отмене временный файл закрывается и
|
||||
удаляется.
|
||||
- **REQ-COPY-08.** Между блоками проверяется `Esc`, а строка состояния
|
||||
обновляет прогресс.
|
||||
- **REQ-COPY-09.** Нулевой файл копируется корректно.
|
||||
- **REQ-COPY-10.** В PoC сохранение времени и атрибутов файла не обязательно,
|
||||
но должно быть предусмотрено в интерфейсе операции.
|
||||
- **REQ-COPY-11.** Непосредственно перед переименованием временного файла
|
||||
конечное имя проверяется повторно. Если оно появилось, операция завершается
|
||||
без его изменения.
|
||||
- **REQ-COPY-12.** Данные между открытыми файлами проходят непосредственно
|
||||
через выделенную EMM-страницу, отображаемую в W3. Полноразмерный блоковый
|
||||
буфер в W2 и изменяемый буфер внутри банка кода запрещены.
|
||||
- **REQ-COPY-13.** Начиная с P18 успешно скопированный обычный файл получает
|
||||
исходные FAT date/time и атрибуты readonly/hidden/system/archive. Время
|
||||
задаётся temp до `close`, атрибуты — конечному имени после commit.
|
||||
- **REQ-COPY-14.** Начиная с P19 существующий файл обрабатывается policy
|
||||
overwrite/skip/rename; варианты overwrite-all и skip-all ограничены одним
|
||||
F5. Overwrite использует backup в том же каталоге и rollback при отказе
|
||||
commit, а readonly требует явного повторного решения.
|
||||
|
||||
### REQ-EXEC: запуск программ
|
||||
|
||||
- **REQ-EXEC-01.** `Enter` на имени с расширением `.EXE` запускает программу
|
||||
через ESTEX `EXEC`.
|
||||
- **REQ-EXEC-02.** Перед запуском Commander сохраняет логическое состояние
|
||||
панелей и закрывает временные ресурсы операции.
|
||||
- **REQ-EXEC-03.** После возврата восстанавливаются видеорежим, экран и
|
||||
ожидаемый текущий каталог.
|
||||
- **REQ-EXEC-04.** Активная панель перечитывается после возврата; пассивная
|
||||
перечитывается, если запущенная программа могла изменить её каталог.
|
||||
- **REQ-EXEC-05.** Ошибка загрузки `.EXE` показывается без завершения
|
||||
Commander.
|
||||
- **REQ-EXEC-06.** Обычный файл неизвестного типа в PoC не запускается.
|
||||
|
||||
### REQ-UI: сообщения и состояние
|
||||
|
||||
- **REQ-UI-01.** Ошибки не выводятся поверх структуры панели случайными
|
||||
вызовами `printf`; они проходят через единый модуль сообщений.
|
||||
- **REQ-UI-02.** Для каждой ошибки показываются действие, имя объекта и
|
||||
текст/код ошибки, если они доступны.
|
||||
- **REQ-UI-03.** `Esc` закрывает сообщение или отменяет текущую длительную
|
||||
операцию; в обычном состоянии он не завершает программу.
|
||||
- **REQ-UI-04.** Неподдерживаемые в PoC функциональные клавиши либо не
|
||||
показаны, либо показаны неактивным цветом.
|
||||
- **REQ-UI-05.** F6 local rename и F7 mkdir принимают только один проверенный
|
||||
компонент DOS 8.3; `..`, маркер DSS и неоднозначная группа не передаются
|
||||
файловой системе.
|
||||
- **REQ-UI-06.** Rename обязан проверить отсутствие нового имени до DSS
|
||||
`RENAME`, сохранить CWD и восстановить его после успеха или ошибки.
|
||||
- **REQ-UI-05.** После закрытия сообщения восстанавливается экран из
|
||||
логического экранного буфера.
|
||||
|
||||
### REQ-MEM: память
|
||||
|
||||
- **REQ-MEM-01.** Целевая конфигурация PoC — `--memory big --safe`.
|
||||
- **REQ-MEM-02.** W2 содержит резидентное ядро, обычные данные, heap и стек;
|
||||
банковские функции размещаются в W1; W3 используется только для EMM-
|
||||
данных.
|
||||
- **REQ-MEM-03.** PoC выделяет четыре 16-КБ страницы: две для панелей, одну
|
||||
для экранного буфера и одну для рабочей области копирования.
|
||||
- **REQ-MEM-04.** Прикладной код не хранит указатели в диапазон
|
||||
`0xC000–0xFFFF` между вызовами хранилища.
|
||||
- **REQ-MEM-05.** Любой модуль, временно меняющий W3, восстанавливает прежнюю
|
||||
страницу до возврата и до вызова чужого кода.
|
||||
- **REQ-MEM-06.** Повторное чтение каталогов не выделяет новые блоки.
|
||||
- **REQ-MEM-07.** Освобождение частично созданного приложения идемпотентно на
|
||||
уровне состояния приложения: каждый реально выделенный блок освобождается
|
||||
ровно один раз.
|
||||
- **REQ-MEM-08.** Рабочая EMM-страница копирования принадлежит общему блоку
|
||||
`ScApp`; copy job не выделяет и не освобождает её на каждый файл.
|
||||
|
||||
## 7. Формат записи каталога PoC
|
||||
|
||||
Логический формат записи фиксирован как 24 байта:
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
char name[13];
|
||||
uint32_t size;
|
||||
uint16_t date;
|
||||
uint16_t time;
|
||||
uint8_t attr;
|
||||
uint8_t flags;
|
||||
uint8_t reserved;
|
||||
} ScDirEntry;
|
||||
```
|
||||
|
||||
На этапе реализации обязательна compile-time или build-time проверка
|
||||
`sizeof(ScDirEntry) == 24`.
|
||||
|
||||
Флаги PoC:
|
||||
|
||||
- `SC_ENTRY_PARENT` — синтетическая запись `..`;
|
||||
- `SC_ENTRY_SELECTED` — выбор обычной записи в версии 0.2;
|
||||
- `SC_ENTRY_DSS_LIMIT` — виртуальная запись о недоступном остатке каталога;
|
||||
- остальные биты должны быть нулевыми.
|
||||
|
||||
В одной странице используется не более 640 записей; последние 1024 байта
|
||||
остаются резервом и не входят в массив записей. Служебное
|
||||
состояние панели хранится в W2, а не в начале EMM-страницы.
|
||||
|
||||
## 8. Нефункциональные требования
|
||||
|
||||
- **REQ-NF-01.** Исходный код и комментарии проекта ведутся на русском.
|
||||
- **REQ-NF-02.** Сборка выполняется штатным `sprinter-cc` без отдельной
|
||||
проприетарной цепочки.
|
||||
- **REQ-NF-03.** После каждого этапа сохраняется отчёт размеров HOME и
|
||||
банков; переполнение банка является ошибкой сборки.
|
||||
- **REQ-NF-04.** Реакция на навигационную клавишу в уже прочитанной панели не
|
||||
должна визуально зависать; целевой ориентир в MAME — менее 100 мс.
|
||||
- **REQ-NF-05.** Операция дольше 500 мс должна показывать состояние или
|
||||
прогресс.
|
||||
- **REQ-NF-06.** Все воспроизводимые дефекты DSS или SDCC подтверждаются
|
||||
отдельным тестом, `.asm`, дампом или другим артефактом.
|
||||
- **REQ-NF-07.** Рабочая версия должна проверяться в MAME и на реальном
|
||||
Sprinter; PoC может быть принят по MAME с обязательным последующим
|
||||
hardware smoke-test.
|
||||
|
||||
## 9. Что не входит в PoC 0.1
|
||||
|
||||
- удаление файлов;
|
||||
- перезапись существующих файлов;
|
||||
- перемещение и переименование пользователем;
|
||||
- создание каталогов;
|
||||
- рекурсивные операции;
|
||||
- групповое выделение и маски;
|
||||
- командная строка и история;
|
||||
- встроенные viewer/editor;
|
||||
- дерево и поиск;
|
||||
- архивы и виртуальные панели;
|
||||
- мышь;
|
||||
- конфигурационный файл, темы и локализация;
|
||||
- длинные имена;
|
||||
- фоновые операции и многозадачность.
|
||||
|
||||
## 10. Критерии приёмки PoC
|
||||
|
||||
PoC считается завершённым, если одновременно выполнены условия:
|
||||
|
||||
1. Программа стабильно запускается в MAME с двухпанельным экраном.
|
||||
2. Обе панели независимо проходят дерево каталогов и корректно работают с
|
||||
пустым каталогом.
|
||||
3. Хранилище панели проверено синтетическим source на границах 640/641 без
|
||||
выхода за EMM-страницу; на DSS 1.71 превышение его границы 512 физических
|
||||
FAT-записей даёт управляемый частичный список с красным маркером.
|
||||
Оба случая подтверждены MAME-пробами в
|
||||
[результатах P3](p3-panel-results.md).
|
||||
4. Сто последовательных перечитываний и переходов не меняют число свободных
|
||||
EMM-страниц.
|
||||
5. Копируются и побайтно совпадают файлы размеров 0, 1, 4095, 4096, 65535,
|
||||
65536 байт и более 1 МБ.
|
||||
6. Отмена и ошибка записи не оставляют временный файл.
|
||||
7. Существующий целевой файл никогда не меняется.
|
||||
8. Тестовый `.EXE` запускается, завершается, после чего Commander полностью
|
||||
восстанавливает экран и продолжает работу.
|
||||
9. Все пути ошибок возвращают приложение в управляемое состояние.
|
||||
10. На выходе восстановлен видеорежим, закрыты дескрипторы и освобождены
|
||||
выделенные EMM-блоки.
|
||||
|
||||
## 11. Граница полной версии 1.0
|
||||
|
||||
Версия 1.0 должна дополнительно включать:
|
||||
|
||||
- групповые и рекурсивные копирование, перемещение и удаление;
|
||||
- безопасную политику перезаписи;
|
||||
- сохранение атрибутов и времени;
|
||||
- независимую для каждой панели сортировку по имени, расширению,
|
||||
размеру и дате файла в обоих направлениях;
|
||||
- переключение дисков;
|
||||
- командную строку, историю, интерактивный полноэкранный
|
||||
режим псевдооболочки по `Ctrl+O` и ассоциации;
|
||||
- настраиваемую подсветку имён по расширению;
|
||||
- просмотрщик и редактор, допустимо отдельными `.EXE`;
|
||||
- меню, диалоги, помощь и конфигурацию;
|
||||
- быстрый поиск, дерево и поиск файлов;
|
||||
- информационную и quick-view панели;
|
||||
- базовую работу с архивами;
|
||||
- мышь.
|
||||
|
||||
Обязательные детали этих функций:
|
||||
|
||||
- групповые `+`, `-` и `*` работают только над файлами и не меняют флаг
|
||||
каталога, даже если он был установлен отдельным `Insert`;
|
||||
- `Ctrl+O` не просто скрывает панели, а переключает между ними и
|
||||
управляемым полноэкранным режимом командной строки;
|
||||
- полноэкранный режим и однострочный prompt используют один редактор строки и
|
||||
общую историю; внешние `.EXE` запускаются через ESTEX `EXEC`;
|
||||
- синтетическая `..` всегда первая, каталоги остаются перед файлами,
|
||||
а направление применяется внутри этих групп;
|
||||
- пункт сортировки «дата» использует метку создания/модификации, которую
|
||||
реально возвращает файловая система; DSS `F_FIRST/F_NEXT` сейчас даёт
|
||||
метку last-write, поэтому отдельный выбор creation не имитируется
|
||||
дубликатом даты и требует отдельной платформенной пробы;
|
||||
- при отсутствии файла цветов каталоги и `..` имеют белый цвет, `.EXE` —
|
||||
жёлтый, остальные файлы — светло-серый;
|
||||
- предел 640 записей не снимается ни одной из этих функций.
|
||||
|
||||
Многостраничное/динамическое хранилище панели, межстраничная сортировка и
|
||||
показ 1024/2048 или неограниченного числа файлов не входят в 1.0 и
|
||||
резервируются для 2.0+.
|
||||
|
||||
Терминал, модем, CD-плеер, форматирование, восстановление дисков, электронная
|
||||
таблица, игры и многооконный desktop Dos Navigator не входят в определение
|
||||
Commander 1.0.
|
||||
@@ -0,0 +1,751 @@
|
||||
# Sprinter Commander: план разработки
|
||||
|
||||
Статус: рабочий план
|
||||
|
||||
Связанные документы:
|
||||
[требования](commander-requirements.md),
|
||||
[архитектура](commander-architecture.md),
|
||||
[клавиши и команды](commander-keymap.md),
|
||||
[платформенные пробы P1](p1-platform-probes.md),
|
||||
[экранный backend P2](p2-screen-backend.md),
|
||||
[результаты P2.1](p2-screen-probe-results.md),
|
||||
[результаты P2.2](p2-palette-results.md),
|
||||
[результаты skeleton P2](p2-skeleton-results.md),
|
||||
[результаты файловых панелей P3](p3-panel-results.md),
|
||||
[результаты копирования P4.1](p4-copy-results.md),
|
||||
[результаты запуска EXE P4.2](p4-exec-results.md),
|
||||
[результаты P5](p5-stability-results.md),
|
||||
[результаты F7/P9](p9-mkdir-results.md),
|
||||
[результаты F6/P10](p10-rename-results.md),
|
||||
[результаты F8/P11](p11-delete-results.md),
|
||||
[результаты group-copy/P12](p12-group-copy-results.md),
|
||||
[результаты group-delete/P13](p13-group-delete-results.md),
|
||||
[результаты стандартных цветов/P14](p14-default-colors-results.md),
|
||||
[результаты recursive copy/P15](p15-tree-copy-results.md),
|
||||
[результаты recursive group-copy/P16](p16-group-tree-copy-results.md),
|
||||
[результаты recursive group-delete/P17](p17-tree-delete-results.md),
|
||||
[результаты сохранения метаданных/P18](p18-metadata-results.md),
|
||||
[результаты conflict-policy/P19](p19-copy-policy-results.md),
|
||||
[UI-ориентир VC](ui-reference.md)
|
||||
|
||||
Текущее состояние:
|
||||
|
||||
- P0 — завершена первая редакция документации;
|
||||
- P1 — обязательные платформенные примитивы подтверждены в MAME 0.287;
|
||||
проверка на реальном Sprinter ожидается;
|
||||
- P2.1 — ядро координатного экранного backend прошло MAME 0.287;
|
||||
- P2 — многоцветный двухпанельный skeleton прошёл полный MAME-сценарий,
|
||||
включая scroll, диалоги и повторный cleanup; аппаратная проверка ожидается;
|
||||
- P2.2 — системная многоцветная палитра прошла отдельный MAME-тест;
|
||||
аппаратная проверка ожидается;
|
||||
- P3 — реальные две панели, EMM-store с границей 640/641, сортировка,
|
||||
навигация, синтетическая `..` и предел DSS 35 прошли диагностические и
|
||||
интеграционные MAME-тесты; аппаратная проверка ожидается.
|
||||
- P4 — безопасное однофайловое копирование через EMM/W3 и запуск `.EXE` с
|
||||
полным восстановлением окружения прошли HDD-сценарии MAME; аппаратная
|
||||
проверка ожидается.
|
||||
- P5 — завершён в MAME: пройдены 20 последовательных copy со всеми граничными
|
||||
размерами, 100 refresh, три lifecycle, ENOSPC, readonly, ошибки
|
||||
read/rename/ENOENT, отмена перед commit, смена носителя, отсутствующий EXE и
|
||||
реальный `mdview2`. Аппаратный smoke-тест остаётся внешней проверкой.
|
||||
- 0.2 — начат: независимые сортировки name/ext/size/date и reverse, выбор
|
||||
`Insert`, `*`, `+`/`-` с масками, F7 mkdir и локальный F6 rename
|
||||
реализованы и прошли target/UI-сценарии MAME; следующим идёт расширение
|
||||
файловой job и рекурсивная обработка каталогов. Групповые F5 и F8 уже
|
||||
обрабатывают файлы и выбранные directory roots; для F8 каталоги удаляются
|
||||
после файлов обратным проходом очереди. F5 сохраняет FAT date/time и
|
||||
файловые атрибуты R/H/S/A. Conflict-policy overwrite/skip/rename и all,
|
||||
readonly re-prompt, backup/rollback выполнены; P5/P18 повторно пройдены.
|
||||
Также досрочно реализован стандартный fallback цветов имён без файла
|
||||
конфигурации.
|
||||
|
||||
## 1. Целевые результаты
|
||||
|
||||
Проект делится на три продуктовых рубежа:
|
||||
|
||||
1. **PoC 0.1** — доказана работа архитектуры на Sprinter.
|
||||
2. **Рабочая версия 0.4** — Commander пригоден для повседневных файловых
|
||||
операций.
|
||||
3. **Полная версия 1.0** — реализован основной набор возможностей VC 4.99 с
|
||||
адаптацией к Sprinter.
|
||||
|
||||
Dos Navigator задаёт идеи для расширяемости, но не объём версии 1.0.
|
||||
|
||||
Вся линейка до 1.0 включительно использует одну EMM-страницу на панель и
|
||||
жёсткий предел 640 показываемых записей. Его снятие — отдельная архитектурная
|
||||
задача 2.0+.
|
||||
|
||||
## 2. Допущения для оценки
|
||||
|
||||
Оценки приведены для одного разработчика, знакомого с текущим Sprinter
|
||||
C-Compiler и имеющего доступ к MAME. Один рабочий день означает день
|
||||
сосредоточенной разработки, а не календарный день.
|
||||
|
||||
В оценку входят:
|
||||
|
||||
- реализация;
|
||||
- целевые тесты;
|
||||
- анализ `.asm`/`.map` при платформенной проблеме;
|
||||
- MAME-прогон;
|
||||
- обновление документации.
|
||||
|
||||
В оценку не входят:
|
||||
|
||||
- исправление неизвестных дефектов DSS;
|
||||
- разработка отсутствующего архиватора;
|
||||
- длительное ожидание доступа к реальному железу;
|
||||
- перенос стороннего editor/viewer;
|
||||
- создание графического режима интерфейса.
|
||||
|
||||
Ожидаемая точность оценки:
|
||||
|
||||
- PoC: `-20% / +40%`;
|
||||
- этапы после PoC: `-25% / +60%`;
|
||||
- архивы и editor: оцениваются повторно после исследования доступных программ.
|
||||
|
||||
## 3. Сводный план
|
||||
|
||||
| Этап | Результат | Оценка |
|
||||
|---|---|---:|
|
||||
| P0 | Спецификация и архитектура | 2–3 дня |
|
||||
| P1 | Проверенные платформенные примитивы | 3–5 дней |
|
||||
| P2 | Запускаемый двухпанельный skeleton | 4–5 дней |
|
||||
| P3 | Полноценная навигация по двум панелям | 5–7 дней |
|
||||
| P4 | Копирование одного файла и EXEC | 5–7 дней |
|
||||
| P5 | Стабилизация PoC | 3–5 дней |
|
||||
| 0.2 | Безопасное файловое ядро | 5–7 недель |
|
||||
| 0.4 | Рабочая оболочка и внешние инструменты | 4–6 недель |
|
||||
| 0.6 | Поиск, дерево и расширенные панели | 5–7 недель |
|
||||
| 0.8 | Виртуальные источники и архивы | 6–10 недель |
|
||||
| 1.0 | Надёжность, производительность, релиз | 5–8 недель |
|
||||
|
||||
PoC требует ориентировочно 22–32 рабочих дня. Полная версия 1.0 — примерно
|
||||
29–44 рабочих недели с учётом PoC. После исследования P1 оценки должны быть
|
||||
пересмотрены по фактической скорости разработки.
|
||||
|
||||
## 4. Зависимости этапов
|
||||
|
||||
```text
|
||||
P0 documentation
|
||||
|
|
||||
v
|
||||
P1 platform probes
|
||||
|
|
||||
+------> EXEC probe -------------------+
|
||||
| |
|
||||
v v
|
||||
P2 app/screen/input skeleton P4 EXEC
|
||||
| ^
|
||||
v |
|
||||
P3 panel/store/filesystem --------> P4 copy
|
||||
| |
|
||||
+------------------+-------------------+
|
||||
v
|
||||
P5 PoC hardening
|
||||
|
|
||||
v
|
||||
0.2 safe file core
|
||||
|
|
||||
+----------+-----------+
|
||||
v v
|
||||
0.4 shell/helpers 0.6 search/tree
|
||||
+----------+-----------+
|
||||
v
|
||||
0.8 virtual sources
|
||||
|
|
||||
v
|
||||
1.0
|
||||
|
|
||||
v
|
||||
2.0+ dynamic panel store
|
||||
```
|
||||
|
||||
0.6 допускается начинать после стабилизации интерфейса панелей 0.2, но релиз
|
||||
0.8 требует законченных 0.4 и 0.6.
|
||||
|
||||
## 5. Фаза P0 — проектирование
|
||||
|
||||
### Цель
|
||||
|
||||
Превратить общую идею Commander в набор контрактов, по которым можно писать
|
||||
и проверять код без постоянного возврата к исходникам VC.
|
||||
|
||||
### Работы
|
||||
|
||||
- зафиксировать границы PoC и версии 1.0;
|
||||
- определить компоновку экрана;
|
||||
- определить `ScDirEntry`, `ScPanel`, события и команды;
|
||||
- выбрать режим памяти и владение EMM;
|
||||
- описать безопасное копирование;
|
||||
- описать запуск дочерней программы;
|
||||
- определить клавиши PoC и будущий keymap;
|
||||
- составить acceptance tests;
|
||||
- зафиксировать лицензионное правило для референсов.
|
||||
|
||||
### Артефакты
|
||||
|
||||
- `docs/commander-requirements.md`;
|
||||
- `docs/commander-architecture.md`;
|
||||
- `docs/commander-keymap.md`;
|
||||
- `docs/commander-roadmap.md`.
|
||||
|
||||
### Критерий выхода
|
||||
|
||||
Нет неизвестного продуктового решения, мешающего начать платформенные тесты.
|
||||
Непроверенные технические детали перечислены как задачи P1, а не скрыты в
|
||||
предположениях архитектуры.
|
||||
|
||||
## 6. Фаза P1 — платформенные пробы
|
||||
|
||||
### Цель
|
||||
|
||||
Получить минимальные воспроизводимые программы для всех примитивов, от
|
||||
которых зависит PoC.
|
||||
|
||||
### P1.1. Каталоги и пути
|
||||
|
||||
Проверить:
|
||||
|
||||
- `getcwd`, абсолютные и относительные пути;
|
||||
- `chdir` между двумя каталогами и дисками;
|
||||
- шаблон для перечисления всех файлов;
|
||||
- возвращает ли DSS `.` и `..`;
|
||||
- конец `fnext()` и значение `errno`;
|
||||
- имя корня и переход к родителю;
|
||||
- максимальную практическую длину пути;
|
||||
- поведение при смене/отсутствии носителя.
|
||||
|
||||
Результат: таблица фактического поведения и тестовый `.exe`.
|
||||
|
||||
### P1.2. EMM
|
||||
|
||||
Проверить:
|
||||
|
||||
- выделение блока из трёх страниц;
|
||||
- получение всех физических страниц;
|
||||
- чтение/запись начала и конца каждой страницы;
|
||||
- сохранение W3 вокруг `bank_read/bank_write`;
|
||||
- повторное выделение/освобождение;
|
||||
- понятную ошибку при нехватке памяти.
|
||||
|
||||
Результат: подтверждённая схема трёх страниц PoC.
|
||||
|
||||
### P1.3. Экран
|
||||
|
||||
На базе существующего теста `winrest` проверить app-local wrapper:
|
||||
|
||||
- полный экран 80x32;
|
||||
- вывод с ненулевого offset;
|
||||
- частичный диапазон строк;
|
||||
- все используемые атрибуты;
|
||||
- восстановление после смены видеорежима;
|
||||
- сохранность IX и W3.
|
||||
|
||||
Результат: утверждённый ABI `sc_platform_winrest()`.
|
||||
|
||||
### P1.4. Клавиатура
|
||||
|
||||
Записать фактические `ascii`, `scan`, `kbd_mod_state` для клавиш из
|
||||
`commander-keymap.md`. Отдельно проверить быстрое отпускание Ctrl после
|
||||
`Ctrl+PgUp` и `Ctrl+R`.
|
||||
|
||||
Результат: тестовая таблица и список подтверждённых комбинаций.
|
||||
|
||||
### P1.5. EXEC
|
||||
|
||||
Проверить:
|
||||
|
||||
- формат параметров ESTEX `EXEC`;
|
||||
- требуется ли отдельный `WAIT`;
|
||||
- возврат к вызывающей программе;
|
||||
- состояние W1/W2/W3 после возврата;
|
||||
- состояние видеорежима и CWD;
|
||||
- коды ошибок отсутствующего и повреждённого `.EXE`;
|
||||
- взаимодействие с открытым загрузочным fd multi-bank программы.
|
||||
|
||||
Результат: app-local wrapper и отдельный repro.
|
||||
|
||||
### P1.6. Информация о диске и метаданные
|
||||
|
||||
`DSKINFO` подтверждён в P1. `ATTRIB`, получение и установка даты/времени не
|
||||
блокируют базовый PoC и проверяются перед реализацией соответствующих
|
||||
операций версии 0.2.
|
||||
|
||||
### Критерий выхода P1
|
||||
|
||||
- Все обязательные wrappers имеют воспроизводимый тест.
|
||||
- Необъяснённых регистровых соглашений нет.
|
||||
- Известно минимальное число требуемых страниц и fd.
|
||||
- Архитектура обновлена по результатам проб.
|
||||
|
||||
Критерий достигнут для MAME 0.287 / BIOS 3.06. Подтверждены EMM, DSKINFO,
|
||||
F_FIRST/F_NEXT, полный и частичный WINREST, клавиши PoC, EXEC обоих режимов,
|
||||
WAIT=`0x5A`, восстановление W1/W2/W3 и повторный EMM-тест после child.
|
||||
Фактические значения и скриншоты приведены в
|
||||
[p1-platform-probes.md](p1-platform-probes.md).
|
||||
|
||||
Расширенные отрицательные случаи не блокируют skeleton P2 и перенесены к
|
||||
этапам, где появится использующий их код:
|
||||
|
||||
- переходы между дисками, корень, длинный путь и смена носителя — P3/P5;
|
||||
- исчерпание EMM — P5;
|
||||
- отсутствующий/повреждённый EXE и multi-bank loader fd — P4/P5;
|
||||
- ATTRIB и изменение даты/времени — версия 0.2.
|
||||
|
||||
## 7. Фаза P2 — skeleton приложения
|
||||
|
||||
### Цель
|
||||
|
||||
Получить запускаемое приложение с двумя пустыми панелями, экранной моделью и
|
||||
управлением, но без чтения каталога.
|
||||
|
||||
### Работы
|
||||
|
||||
1. Создать `Makefile`, `src/`, `include/`, `banks/`, `tests/`.
|
||||
2. Добавить `ScApp` и init/cleanup.
|
||||
3. Настроить сборку `--memory big --safe`.
|
||||
4. Выделить EMM-блок и получить три страницы.
|
||||
5. Зафиксировать прошедший P2.1 coordinate-screen probe: полный и частичный
|
||||
`WINREST`, `RDCHAR`, оба направления прямоугольного `SCROLL`, 1000
|
||||
операций и сохранение W3.
|
||||
6. Реализовать `sc_video_system` на `WINREST`/`WRCHAR`/`SCROLL`; не
|
||||
использовать BIOS window descriptors и прямой доступ к VRAM.
|
||||
7. Реализовать screen buffer и полный/частичный present через `sc_video`.
|
||||
8. Добавить `sc_glyphs.h` с CP866-глифами и test рамок, стрелок и block-
|
||||
элементов.
|
||||
9. Реализовать прямоугольный scroll одной панели в обоих направлениях;
|
||||
синхронно сдвигать экранную EMM-модель и проверять границы прямоугольника.
|
||||
10. Нарисовать две панели и нижнюю строку клавиш.
|
||||
11. Реализовать `ScKeyEvent`, `ScCommand` и главный цикл.
|
||||
12. Реализовать `Tab`, навигационные команды-заглушки и подтверждение `F10`.
|
||||
13. Добавить единый message dialog.
|
||||
14. Настроить первый интерактивный MAME-тест со скриншотом.
|
||||
15. Подключить прошедший P2.2 модуль темы: сохранить затрагиваемые записи
|
||||
четырёх планов, задать одинаковые normal/FLASH-пары и восстановить их при
|
||||
cleanup. Повторить тот же тест на реальном Sprinter.
|
||||
|
||||
### Критерий выхода P2
|
||||
|
||||
- `SPRCMD.EXE` собирается в `big`.
|
||||
- Экран соответствует схеме 80x32.
|
||||
- `Tab` меняет активную рамку.
|
||||
- Однострочный scroll не двигает рамку, соседнюю панель и общие строки.
|
||||
- Все соединения рамок, стрелки и block-элементы отображаются ожидаемыми
|
||||
CP866-глифами.
|
||||
- Экран остаётся читаемым во времени с подключённым многоцветным профилем.
|
||||
- Диалог F10 работает в обоих направлениях.
|
||||
- Выход освобождает EMM; повторные запуски не уменьшают свободную память.
|
||||
- В `.map` есть зафиксированный запас HOME и банков.
|
||||
|
||||
MAME-часть этих критериев достигнута 6 сентября 2026 года. Текущий skeleton
|
||||
имеет 7 276 байт heap в HOME, пока не использует W1-банки и дважды подряд
|
||||
восстановил число свободных EMM-страниц после выхода. Отдельный `sc_theme`
|
||||
сохранил/установил/восстановил четыре палитровых плана. Подробный сценарий и
|
||||
кадры: [p2-skeleton-results.md](p2-skeleton-results.md).
|
||||
|
||||
Аппаратный прогон остаётся внешним gate. Многоцветная тема P2.2 прошла MAME;
|
||||
на реальном Sprinter она проверяется вместе с остальными критериями P2.
|
||||
|
||||
## 8. Фаза P3 — панели и каталог
|
||||
|
||||
### Цель
|
||||
|
||||
Реализовать независимую навигацию по настоящей файловой системе.
|
||||
|
||||
### Работы
|
||||
|
||||
- `ScDirEntry` и build-time проверка размера 24 байта;
|
||||
- EMM store get/put/clear;
|
||||
- scan настоящего каталога;
|
||||
- синтетический `..`;
|
||||
- сортировка каталогов и файлов;
|
||||
- panel renderer;
|
||||
- cursor/top и постраничная навигация;
|
||||
- вход в каталог, parent, refresh;
|
||||
- сохранение позиции по имени;
|
||||
- отдельные пути панелей;
|
||||
- предупреждение о пределе 640 записей и отдельный красный маркер
|
||||
ограничения DSS после ошибки 35;
|
||||
- отображение ошибок носителя.
|
||||
|
||||
### Порядок реализации
|
||||
|
||||
1. Одна панель, маленький каталог.
|
||||
2. Пустой каталог и корень.
|
||||
3. Сортировка.
|
||||
4. Прокрутка за 27 строк.
|
||||
5. Вторая независимая панель.
|
||||
6. Большой каталог и предел страницы.
|
||||
7. Ошибки и восстановление CWD.
|
||||
|
||||
### Критерий выхода P3
|
||||
|
||||
- Выполнены `REQ-PANEL`, `REQ-NAV` и `REQ-FS` PoC.
|
||||
- Сто переходов/refresh не меняют EMM free pages.
|
||||
- Каталог на пределе не повреждает screen page.
|
||||
- Перемещение курсора не вызывает `ffirst/fnext`.
|
||||
- После ошибки обе панели остаются управляемыми.
|
||||
|
||||
## 9. Фаза P4 — копирование и запуск
|
||||
|
||||
### P4.1. Копирование
|
||||
|
||||
Порядок работ:
|
||||
|
||||
1. Проверка выбранной записи и построение путей.
|
||||
2. Проверка отсутствия конечного имени.
|
||||
3. Генерация temp с `O_EXCL`.
|
||||
4. Зарезервировать четвёртую EMM-страницу как рабочую область copy job.
|
||||
5. Добавить симметричные операции `bank_read_page()`/`bank_write_page()` для
|
||||
файлового обмена через W3 без полноразмерного буфера в W2.
|
||||
6. Копирование логическими блоками 4096 байт; возможное увеличение блока —
|
||||
только после измерения отзывчивости и скорости.
|
||||
5. 32-битный прогресс.
|
||||
6. Отмена через `Esc`.
|
||||
7. Cleanup на каждой точке ошибки.
|
||||
8. Повторная проверка конечного имени.
|
||||
9. Rename temp и refresh пассивной панели.
|
||||
10. Тесты размеров и побайтовое сравнение.
|
||||
|
||||
### P4.2. EXEC
|
||||
|
||||
Порядок работ:
|
||||
|
||||
1. Запуск минимального дочернего `.EXE`.
|
||||
2. Восстановление видеорежима и экрана.
|
||||
3. Восстановление CWD.
|
||||
4. Refresh панелей.
|
||||
5. Обработка ошибки запуска.
|
||||
6. Запуск существующего viewer как интеграционный тест.
|
||||
|
||||
### Критерий выхода P4
|
||||
|
||||
- Выполнены `REQ-COPY` и `REQ-EXEC`.
|
||||
- Большой файл копируется без 16-битного переполнения.
|
||||
- Cancel/error не оставляют temp.
|
||||
- Существующий файл не изменяется.
|
||||
- После дочерней программы Commander продолжает принимать команды.
|
||||
|
||||
## 10. Фаза P5 — стабилизация PoC 0.1
|
||||
|
||||
### Обязательная матрица
|
||||
|
||||
| Область | Сценарии |
|
||||
|---|---|
|
||||
| Каталог | пустой, один файл, 27/28, synthetic 640/641, DSS >512 физических записей |
|
||||
| Навигация | границы, repeat, две разные глубины каталогов |
|
||||
| Файлы | 0, 1, 4095, 4096, 65535, 65536, более 1 МБ |
|
||||
| Ошибки | ENOENT, EACCES/readonly, ENOSPC, смена носителя |
|
||||
| Copy cleanup | cancel в начале/середине/конце, ошибка read/write/rename |
|
||||
| EXEC | успех, отсутствующий файл, плохой EXE, возврат с другим экраном |
|
||||
| Ресурсы | 100 refresh, 20 copy, повторные запуск/выход |
|
||||
| Экран | полный redraw, dirty rows, диалог, возврат из EXE |
|
||||
|
||||
### Артефакты релиза PoC
|
||||
|
||||
- `SPRCMD.EXE`;
|
||||
- Makefile с `floppy`, `hdd` и `run`;
|
||||
- MAME-тесты и контрольные скриншоты;
|
||||
- test files с известными размерами/checksum;
|
||||
- отчёт размеров HOME/банков;
|
||||
- список известных ограничений;
|
||||
- инструкция запуска.
|
||||
|
||||
### Release gate 0.1
|
||||
|
||||
Все десять критериев раздела 10 требований выполнены. Наличие известной
|
||||
ошибки, способной повредить чужой файл или потерять EMM-блок, блокирует релиз.
|
||||
|
||||
Release gate выполнен в MAME 0.288 / BIOS 3.06 / DSS 1.71.57. Сводка,
|
||||
команды воспроизведения и ограничения зафиксированы в
|
||||
[p5-stability-results.md](p5-stability-results.md) и
|
||||
[poc-0.1-release.md](poc-0.1-release.md). Проверка на реальном Sprinter не
|
||||
подменяется MAME и остаётся обязательным последующим smoke-тестом.
|
||||
|
||||
## 11. Версия 0.2 — безопасное файловое ядро
|
||||
|
||||
### Цель
|
||||
|
||||
Сделать Commander пригодным для реальной работы с файлами без viewer/editor.
|
||||
|
||||
### Функции
|
||||
|
||||
- одна EMM-страница на панель и предел 640 записей сохраняются;
|
||||
- `Insert`, `*`, `+`/`-` и маски выбора — выполнены;
|
||||
- сортировки name/ext/size/date и reverse — выполнены и проверены в MAME;
|
||||
- последовательная очередь выбранных файлов F5 — выполнена;
|
||||
- рекурсивное копирование одного каталога и группы directory roots —
|
||||
выполнено; traversal hardening для больших деревьев остаётся;
|
||||
- F6 local rename — выполнено; межкаталожный/group move остаётся в работе;
|
||||
- F7 mkdir — выполнено и проверено в MAME;
|
||||
- F8 single file/empty-directory delete и recursive group delete выбранных
|
||||
файлов и каталогов — выполнены, включая runtime-cancel; защитная policy
|
||||
остаётся;
|
||||
- cancel между файлами и блоками;
|
||||
- overwrite/skip/rename и варианты all — выполнены для файлов, включая
|
||||
backup/rollback и readonly re-prompt; merge существующих каталогов позже;
|
||||
- проверка свободного места;
|
||||
- сохранение даты, времени и атрибутов обычных файлов — выполнено и
|
||||
проверено exact FAT-тестом; метаданные каталогов не входят в P18;
|
||||
- защита readonly при overwrite — выполнена; delete/move policy остаётся;
|
||||
- подсчёт итогов выбранной группы;
|
||||
- переключение дисков минимум через диалог.
|
||||
|
||||
### Внутренние работы
|
||||
|
||||
- сохранить одностраничный store и добавить к `ScPanel` ключ и направление сортировки;
|
||||
- расширить `ScJob` очередью и стеком каталогов;
|
||||
- сохранить crash-safe temp/commit модель;
|
||||
- добавить единый policy object для ошибок;
|
||||
- определить поведение при частично успешной группе.
|
||||
|
||||
### Критерий выхода 0.2
|
||||
|
||||
- Неделя dogfood без повреждения данных.
|
||||
- Рекурсивные операции проходят дерево глубиной не менее 16 уровней.
|
||||
- Рекурсивная job обрабатывает более 640 файлов суммарно в нескольких
|
||||
каталогах, не превращая саму панель в многостраничную.
|
||||
- Cancel оставляет уже завершённые файлы в определённом документированном
|
||||
состоянии и не оставляет temp.
|
||||
- Все разрушительные команды требуют подтверждения.
|
||||
|
||||
## 12. Версия 0.4 — рабочая оболочка
|
||||
|
||||
### Функции
|
||||
|
||||
- однострочная командная строка;
|
||||
- история команд и каталогов;
|
||||
- интерактивный полноэкранный pseudo-shell по `Ctrl+O`;
|
||||
- вставка имени/пути активной и пассивной панели;
|
||||
- запуск `.EXE` с параметрами;
|
||||
- file associations;
|
||||
- пользовательское меню;
|
||||
- F3 через отдельный viewer;
|
||||
- F4 через отдельный editor;
|
||||
- F9 и базовое меню;
|
||||
- режимы панелей `Brief`/`Details` по образцу VC;
|
||||
- конфигурационный файл;
|
||||
- сохранение путей, sort mode и палитры;
|
||||
- цветовые правила по расширениям; без файла: каталоги белые, EXE жёлтые,
|
||||
остальные файлы светло-серые;
|
||||
- `Ctrl+U`, `Ctrl+O`, root;
|
||||
- полноценный drive dialog;
|
||||
- краткая встроенная помощь.
|
||||
|
||||
### Протокол helper-программ
|
||||
|
||||
До реализации F3/F4 фиксируется небольшой контракт:
|
||||
|
||||
- полный путь передаётся аргументом;
|
||||
- код возврата различает успех, отмену и ошибку;
|
||||
- helper не обязан восстанавливать экран;
|
||||
- Commander всегда восстанавливает режим и перечитывает затронутую панель;
|
||||
- для editor предусматривается признак изменения файла.
|
||||
|
||||
### Критерий выхода 0.4
|
||||
|
||||
Commander можно использовать как основную оболочку DSS для навигации,
|
||||
операций, запуска программ, просмотра и редактирования файлов.
|
||||
|
||||
## 13. Версия 0.6 — возможности VC-класса
|
||||
|
||||
### Функции
|
||||
|
||||
- quick search по текущей панели;
|
||||
- постоянные фильтры/маски панели;
|
||||
- quick-view panel;
|
||||
- info panel;
|
||||
- размеры каталогов;
|
||||
- сравнение каталогов;
|
||||
- поиск файлов по имени;
|
||||
- поиск текста в файлах, если скорость приемлема;
|
||||
- виртуальная панель результатов поиска;
|
||||
- дерево каталогов;
|
||||
- история посещённых каталогов;
|
||||
- descriptions (`description.ion`/настраиваемое имя), если формат выбран;
|
||||
- изменение атрибутов и времени через UI.
|
||||
|
||||
### Архитектурный gate
|
||||
|
||||
Перед началом поиска должен быть стабилен интерфейс `ScSource`. Результаты
|
||||
поиска не должны притворяться настоящим каталогом с помощью специальных
|
||||
проверок по всему UI: они реализуются как отдельный source kind.
|
||||
|
||||
### Критерий выхода 0.6
|
||||
|
||||
Поиск и дерево работают на объёмах, превышающих near-memory, а возврат из
|
||||
виртуальной панели не теряет прежнее состояние настоящей панели.
|
||||
|
||||
## 14. Версия 0.8 — виртуальные источники и архивы
|
||||
|
||||
### Обязательные исследования
|
||||
|
||||
- какие архиваторы реально существуют для Sprinter;
|
||||
- можно ли получить листинг без распаковки;
|
||||
- достаточно ли внешнего процесса и list-файла;
|
||||
- какие форматы стоит поддерживать первыми;
|
||||
- сколько памяти требует нативный decoder;
|
||||
- разрешает ли лицензия включать выбранную реализацию.
|
||||
|
||||
### Предпочтительная последовательность
|
||||
|
||||
1. Внешний архиватор по шаблону команды.
|
||||
2. Просмотр архива как read-only `SC_SOURCE_ARCHIVE`.
|
||||
3. Извлечение выбранных файлов.
|
||||
4. Копирование файла в архив, если внешний инструмент поддерживает.
|
||||
5. Только затем — нативный decoder для одного востребованного формата.
|
||||
|
||||
### Дополнительные функции
|
||||
|
||||
- мышь;
|
||||
- локализация строк через resource file;
|
||||
- расширенная помощь;
|
||||
- bookmarks;
|
||||
- protocol внешних plugin/helper-программ;
|
||||
- сохранённые наборы конфигурации.
|
||||
|
||||
### Критерий выхода 0.8
|
||||
|
||||
Архивная и поисковая панели используют тот же panel UI без специальных
|
||||
ветвей в renderer и навигации. Ошибка внешнего архиватора не повреждает
|
||||
исходный архив.
|
||||
|
||||
## 15. Версия 1.0 — релизная стабилизация
|
||||
|
||||
К этому этапу уже должны быть закончены все назначенные функции 0.2–0.8.
|
||||
Отдельно проверяются три обязательных релизных контракта:
|
||||
|
||||
- командная строка и интерактивный полноэкранный pseudo-shell по `Ctrl+O`;
|
||||
- сортировка каждой панели по имени, расширению, размеру и дате, по
|
||||
возрастанию и убыванию;
|
||||
- подсветка по типу файла с настраиваемыми цветами и заданным поведением
|
||||
при отсутствии конфигурации.
|
||||
|
||||
Предел панели 640 записей в 1.0 остаётся штатным документированным
|
||||
ограничением.
|
||||
|
||||
### Надёжность
|
||||
|
||||
- проверка всех cleanup-path;
|
||||
- защита от двойного освобождения;
|
||||
- тесты нехватки EMM;
|
||||
- тесты предела файловых дескрипторов;
|
||||
- смена и извлечение носителя;
|
||||
- read-only и disk full;
|
||||
- повреждённые каталоги/архивы;
|
||||
- отмена каждой длительной операции;
|
||||
- безопасное обновление конфигурации через temp/rename.
|
||||
|
||||
### Производительность
|
||||
|
||||
- профилирование чтения больших каталогов;
|
||||
- профилирование сортировок EMM;
|
||||
- минимизация переключений W3;
|
||||
- группировка dirty rows;
|
||||
- размер блока copy по фактическим измерениям;
|
||||
- размер HOME и каждого банка;
|
||||
- время холодного запуска и возврата из helper.
|
||||
|
||||
### Совместимость
|
||||
|
||||
- MAME 0.287 / BIOS 3.06;
|
||||
- реальный Sprinter Sp2000;
|
||||
- DSS 1.71.57;
|
||||
- floppy и HDD;
|
||||
- минимум две конфигурации доступной EMM;
|
||||
- CP866 и Rus/Lat состояние клавиатуры.
|
||||
|
||||
### Документация релиза
|
||||
|
||||
- руководство пользователя;
|
||||
- полный keymap;
|
||||
- описание конфигурации;
|
||||
- восстановление после прерванной операции;
|
||||
- ограничения файловой системы;
|
||||
- лицензии и благодарности;
|
||||
- руководство сборки;
|
||||
- архитектурная памятка для добавления source/job/bank.
|
||||
|
||||
### Release gate 1.0
|
||||
|
||||
- Нет известных дефектов потери или скрытой порчи данных.
|
||||
- Все обязательные функции требований 1.0 реализованы; перенос любой из них
|
||||
требует нового явного решения о составе релиза.
|
||||
- Пройдены автоматические MAME-тесты и ручной hardware checklist.
|
||||
- Размеры HOME/банков имеют документированный запас.
|
||||
- Чистая сборка воспроизводима штатной командой проекта.
|
||||
|
||||
## 16. Версия 2.0+
|
||||
|
||||
Первый архитектурный рубеж после 1.0 — просмотр каталогов, содержащих более
|
||||
640 записей. Перед реализацией сравниваются два варианта.
|
||||
|
||||
**Вариант A: потоковые логические страницы.** Рекомендуемый первый шаг:
|
||||
|
||||
- в EMM по-прежнему находится только текущая страница и не более 640 строк;
|
||||
- виртуальные `<<< PAGE n` и `>>> PAGE n` занимают места в этом лимите;
|
||||
- при переходе каталог сканируется заново до требуемой логической позиции;
|
||||
- каталоги и файлы отбираются двумя проходами, чтобы каталоги оставались
|
||||
первыми во всём потоке;
|
||||
- сортировка выполняется только внутри текущей страницы и так обозначается
|
||||
в UI;
|
||||
- изменение каталога между проходами сбрасывает просмотр на первую страницу.
|
||||
|
||||
Текущий DSS API `F_FIRST/F_NEXT` не принимает ключ или направление
|
||||
сортировки. Незавершённые итераторы двух панелей не сохраняются: каждый
|
||||
переход заканчивает перебор либо начинает новый.
|
||||
|
||||
Платформенная предпосылка варианта A: стабильный DSS 1.71 не умеет перейти
|
||||
за 512-ю физическую FAT-запись и возвращает код 35. Потоковые страницы
|
||||
становятся реализуемыми только после перехода на расширенный режим
|
||||
`F_FIRST` (`B=0x80/0x81`) из экспериментальной ветки `beta_cdfs` либо после
|
||||
эквивалентного исправления DSS. До этого `>>> MORE...` является только
|
||||
информирующим маркером, а не переходом на страницу 2. Детали:
|
||||
[dss-large-directories.md](dss-large-directories.md).
|
||||
|
||||
**Вариант B: динамический EMM-store.** Он нужен, только если потребуются
|
||||
глобальная сортировка и произвольный переход между страницами:
|
||||
|
||||
- выбор модели 1024, 2048 или динамического предела по результатам замеров;
|
||||
- выделение и освобождение динамической цепочки EMM-страниц;
|
||||
- межстраничная сортировка или индекс без полной копии списка в W2;
|
||||
- определённое поведение при нехватке EMM на части каталога;
|
||||
- нагрузочные тесты многостраничных списков и отмены во время scan/sort.
|
||||
|
||||
Для обоих вариантов виртуальные записи имеют отдельный тип и не участвуют в
|
||||
выделении, файловых операциях и сортировке. Точный номер версии после 1.0
|
||||
фиксируется отдельным release gate; базовый релиз 1.0 от этого не зависит.
|
||||
|
||||
Прочий backlog, не влияющий на релиз 1.0:
|
||||
|
||||
- нативные архивные codecs;
|
||||
- FTP/serial/network source при наличии транспорта;
|
||||
- disk image source;
|
||||
- сравнение и синхронизация деревьев;
|
||||
- макросы;
|
||||
- очередь нескольких jobs;
|
||||
- background copy, только если появится безопасная модель многозадачности;
|
||||
- интеграция viewer/editor в банки Commander;
|
||||
- дополнительные режимы экрана.
|
||||
|
||||
DOS-специфические функции DN — CD player, modem terminal, disk recovery,
|
||||
spreadsheet, games и desktop окон — остаются отдельными приложениями.
|
||||
|
||||
## 17. Правило изменения плана
|
||||
|
||||
После каждого release gate обновляются:
|
||||
|
||||
1. Фактическое время этапа.
|
||||
2. Размер HOME и банков.
|
||||
3. Число обязательных EMM-страниц.
|
||||
4. Открытые платформенные риски.
|
||||
5. Состав следующей версии.
|
||||
|
||||
Новая функция не добавляется в текущий этап без одного из решений:
|
||||
|
||||
- она устраняет блокирующий риск текущего release gate;
|
||||
- она дешевле сейчас из-за уже изменяемого интерфейса;
|
||||
- другая функция того же объёма явно исключается из этапа.
|
||||
|
||||
Такой обмен фиксируется в этом документе, чтобы PoC и 1.0 не расширялись
|
||||
неуправляемо.
|
||||
@@ -0,0 +1,100 @@
|
||||
# DSS: большие каталоги и `F_FIRST/F_NEXT`
|
||||
|
||||
## Вывод
|
||||
|
||||
Стабильный DSS 1.71 не может перечислить каталог за пределами первых 512
|
||||
физических FAT-записей. Это ограничение внутреннего 16-КБ кэша DSS, а не
|
||||
размера `ffblk_t`, адреса DTA, W2, W3 или EMM-хранилища Commander.
|
||||
|
||||
Commander хранит до 640 элементов в EMM, но на стабильном DSS источник
|
||||
может закончиться раньше. Код ошибки 35 принимается как частичный успех:
|
||||
полученные элементы сохраняются, а последней добавляется красная виртуальная
|
||||
строка `>>> MORE...`.
|
||||
|
||||
## Подтверждение исходниками DSS
|
||||
|
||||
Исходники находятся в `../../docs/sources/Estex-DSS` и закодированы в CP866.
|
||||
Для чтения комментариев они просматривались через `iconv`, без изменения
|
||||
оригинальных файлов.
|
||||
|
||||
Стабильная ветка `master`, релизный срез `6078563`:
|
||||
|
||||
- `DSS/defines.inc`: `DIRPAGE.buffer = 0xC000`;
|
||||
- `DSS/FS/FAT.asm:LOADDIR`: за один раз читается ровно `0x4000` байт;
|
||||
- размер `FAT_DIRECTORY_RECORD` равен 32 байтам;
|
||||
- `SEARCH.Custom` начинает с `IX=0xC000` и прибавляет 32;
|
||||
- после 512 записей IX переходит через `0xFFFF`, а
|
||||
`SEARCH.error_too_many_files` возвращает
|
||||
`DSS_Error.sys.TOO_MANY_FILES_IN_DIR`;
|
||||
- `DSS/API/Find.asm:F_NEXT` отдельно проверяет `IX==0` и направляет выполнение
|
||||
на ту же ошибку;
|
||||
- комментарии `TODO` прямо требуют record index и загрузку другой части
|
||||
каталога размером более `0x4000` байт.
|
||||
|
||||
В системном `SYSTEM.DOS`, реально используемом MAME, найдены обе характерные
|
||||
последовательности стабильной реализации: проверка `LD IX,0` и цикл
|
||||
`ADD IX,32` до переноса. Бинарник на HDD и `dss171u.img` совпадают по SHA-256.
|
||||
|
||||
## Почему старый тест показывал 682
|
||||
|
||||
Один ранний экран `P3DIR` показывал `count=682, errno=0`, но поле `last`
|
||||
содержало `B0000599.DAT`. Проверка физического порядка тестового FAT-образа
|
||||
показала:
|
||||
|
||||
- записи 0 и 1 — `.` и `..`;
|
||||
- `B0000599.DAT` — запись 511, то есть 512-я физическая запись;
|
||||
- запись 512 уже имеет другое имя.
|
||||
|
||||
Следовательно, DSS завершил реальный перебор точно на системной границе, а
|
||||
старый диагностический `.EXE` исказил локальный счётчик и `errno`. Этот экран
|
||||
не является доказательством чтения 682 записей. Исправленная проба хранит
|
||||
счётчик и контрольные имена 511/512/513 в статической памяти.
|
||||
|
||||
Матрица `tests/p3_enum_matrix` независимо получила для всех проверенных
|
||||
вариантов `count=512, end_errno=35`: обычный wrapper, wrapper и цикл в W1,
|
||||
чтение W3 и запись в EMM.
|
||||
|
||||
## Семантика Commander 0.1–1.0
|
||||
|
||||
При `fnext() == -1 && errno == 35`:
|
||||
|
||||
1. Ошибка не откатывает уже прочитанный каталог.
|
||||
2. `panel.truncated` устанавливается в 1.
|
||||
3. В конец добавляется `ScDirEntry` с флагом `SC_ENTRY_DSS_LIMIT` и именем
|
||||
`>>> MORE...`.
|
||||
4. Маркер всегда сортируется после файлов, рисуется красным и занимает один
|
||||
из 640 слотов панели.
|
||||
5. `Enter` и файловые команды не трактуют маркер как файл или каталог.
|
||||
|
||||
Число видимых файлов может быть меньше 510: в 512 физических записей входят
|
||||
`.`/`..`, удалённые записи и служебные записи длинных имён, которые DSS или
|
||||
Commander отфильтровывают.
|
||||
|
||||
## Расширенная ветка DSS
|
||||
|
||||
В `origin/beta_cdfs` существует незавершённая реализация больших каталогов:
|
||||
|
||||
- `B=0x80` — короткое имя и обход без старого предела;
|
||||
- `B=0x81` — DOS-имя и обход без старого предела;
|
||||
- хранится record index;
|
||||
- при окончании страницы вызывается `LOAD_NEXT_DIR_PART_TO_DIR_CACHE`;
|
||||
- старые режимы `B=0/1` намеренно ограничиваются 512 результатами для
|
||||
совместимости.
|
||||
|
||||
Это хороший upstream-прототип для версии 2.0, но не контракт стабильного DSS
|
||||
1.71. До отдельной сборки и тестирования Commander не должен зависеть от
|
||||
него.
|
||||
|
||||
## Следствие для логических страниц Commander
|
||||
|
||||
Предложенные `<<< PAGE n`/`>>> PAGE n` реализуемы без дополнительной памяти,
|
||||
если DSS способен продолжить физический перебор после первой 16-КБ части.
|
||||
На стабильном DSS повторный `F_FIRST/F_NEXT` снова остановится на тех же 512
|
||||
записях, поэтому настоящее переключение на PAGE2 требует одного из вариантов:
|
||||
|
||||
1. стабилизировать расширенный режим `B=0x80/0x81`;
|
||||
2. добавить эквивалентный API DSS с record index/offset;
|
||||
3. как нежелательный запасной путь — реализовать raw FAT source вне DSS.
|
||||
|
||||
Выбор делается после релиза 1.0; текущий красный маркер уже имеет отдельный
|
||||
тип и сможет позднее стать переходом без изменения формата `ScDirEntry`.
|
||||
@@ -0,0 +1,324 @@
|
||||
# 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.
|
||||
@@ -0,0 +1,77 @@
|
||||
# Sprinter Commander: локальный rename 0.2/P10
|
||||
|
||||
Дата: 8 сентября 2026 года.
|
||||
|
||||
Статус: `PASS` в MAME 0.288 / BIOS 3.06 / DSS 1.71.57 на HDD `D:`.
|
||||
|
||||
## Реализованный срез F6
|
||||
|
||||
`F6` переименовывает одну текущую обычную запись внутри активного каталога.
|
||||
Поддержаны как файлы, так и каталоги. Диалог начинает с текущего имени и
|
||||
использует общий редактор; новое имя проходит тот же строгий validator одного
|
||||
компонента DOS 8.3, что и F7.
|
||||
|
||||
Это пока локальный `Rename`, а не полный VC `Move/Rename`:
|
||||
|
||||
- межкаталожный move в пассивную панель ещё не выполняется;
|
||||
- если в панели есть неоднозначная группа, команда явно отказывается от
|
||||
операции; единственная выбранная текущая запись допустима;
|
||||
- `..` и маркер предела DSS никогда не передаются файловой системе;
|
||||
- совпадающее без учёта ASCII-регистра имя считается no-op.
|
||||
|
||||
Низкоуровневый `sc_rename_local()` учитывает подтверждённое ограничение DSS
|
||||
1.71: `RENAME` принимает два basename относительно CWD. Helper сохраняет CWD,
|
||||
временно входит в активный каталог, проверяет отсутствие target через
|
||||
`F_FIRST`, выполняет `rename()` и восстанавливает CWD. Таким образом
|
||||
существующая запись не перезаписывается даже при иной семантике конкретной
|
||||
версии DSS.
|
||||
|
||||
## Проверка
|
||||
|
||||
`RENRT.EXE` вызывает тот же helper и проверяет:
|
||||
|
||||
- rename файла с побайтно неизменным содержимым;
|
||||
- отказ при существующем target и сохранность обоих файлов;
|
||||
- rename непустого каталога с сохранностью вложенного файла;
|
||||
- CWD после успеха, collision и ошибки входа в каталог.
|
||||
|
||||
После UI-сценария внешний checker извлекает HDD и подтверждает окончательные
|
||||
имена и содержимое:
|
||||
|
||||
```text
|
||||
PASS: file/dir rename, no-overwrite, CWD restore и UI F6.
|
||||
```
|
||||
|
||||
Контрольные состояния UI:
|
||||
|
||||
- [диалог с исходным именем](../artifacts/mame-p10/rename/dialog-default.png);
|
||||
- [`FINAL.BIN` создан и остался под cursor](../artifacts/mame-p10/rename/renamed-focused.png);
|
||||
- [неизменное имя обработано как no-op](../artifacts/mame-p10/rename/unchanged-name.png);
|
||||
- [существующий target отвергнут](../artifacts/mame-p10/rename/collision.png);
|
||||
- [`..` не переименовывается](../artifacts/mame-p10/rename/virtual-guard.png);
|
||||
- [неоднозначная группа не превращается в rename cursor](../artifacts/mame-p10/rename/group-guard.png);
|
||||
- [штатный возврат в DSS](../artifacts/mame-p10/rename/clean-dss-return.png).
|
||||
|
||||
## Размер
|
||||
|
||||
Конфигурация `--memory big --safe --max-allocs 3000`:
|
||||
|
||||
| Область | После P9 + UI footer | После P10 | Изменение |
|
||||
|---|---:|---:|---:|
|
||||
| `_CODE` W2 | 9449 | 9463 | +14 |
|
||||
| данные W2 | 3256 | 3256 | 0 |
|
||||
| свободная куча W2 | 2143 | 2129 | -14 |
|
||||
| BANK1 | 6477 | 6477 | 0 |
|
||||
| BANK2 | 8303 | 10008 | +1705 |
|
||||
| `SPRCMD.EXE` | 43026 | 43040 | +14 |
|
||||
|
||||
В BANK2 остаётся 6376 байт, в BANK1 — 9907 байт. Межбанковый аудит чист.
|
||||
|
||||
## Воспроизведение
|
||||
|
||||
```sh
|
||||
make hdd-p10-rename ALLOCS=3000
|
||||
tests/run_mame_hdd.sh build/hdd/p10_rename.chd \
|
||||
tests/mame_p10_rename.lua artifacts/mame-p10/local 65
|
||||
tests/check_p10_rename_hdd.sh build/hdd/p10_rename.chd
|
||||
```
|
||||
@@ -0,0 +1,71 @@
|
||||
# Sprinter Commander: однообъектный delete 0.2/P11
|
||||
|
||||
Дата: 9 сентября 2026 года.
|
||||
|
||||
Статус: `PASS` в MAME 0.288 / BIOS 3.06 / DSS 1.71.57 на HDD `D:`.
|
||||
|
||||
## Реализованный срез F8
|
||||
|
||||
`F8` удаляет одну текущую обычную запись только после модального
|
||||
подтверждения:
|
||||
|
||||
- обычный файл удаляется через `unlink()`;
|
||||
- пустой каталог — через `rmdir()`;
|
||||
- непустой каталог сохраняется и даёт управляемую ошибку;
|
||||
- `N`/`Esc` отменяют команду без изменения носителя;
|
||||
- `..` и маркер предела DSS не передаются файловой системе;
|
||||
- в срезе P11 неоднозначная выбранная группа отклонялась; начиная с P13
|
||||
выбранные файлы удаляются группой, а выбранные каталоги пропускаются.
|
||||
Это исторический контракт P11/P13; начиная с P17 каталоги выбранной группы
|
||||
удаляются рекурсивно.
|
||||
|
||||
Рекурсивное и групповое удаление намеренно не имитируются циклом вокруг
|
||||
текущего cursor. Они будут реализованы общей файловой job с определённой
|
||||
политикой частичного успеха и cancel.
|
||||
|
||||
## Проверка
|
||||
|
||||
Target-проба `DELRT.EXE` проверяет удаление обычного файла и пустого каталога
|
||||
по полным путям, отказ для непустого каталога, сохранность соседнего файла и
|
||||
неизменность CWD. UI-сценарий затем проверяет обе стороны подтверждения и
|
||||
защитные ветки. После выхода дерево извлекается из CHD и проверяется
|
||||
побайтно:
|
||||
|
||||
```text
|
||||
PASS: file/empty-dir delete, cancel, guards, nonempty и CWD.
|
||||
```
|
||||
|
||||
Контрольные состояния:
|
||||
|
||||
- [подтверждение удаления файла](../artifacts/mame-p11/delete/file-confirm.png);
|
||||
- [cancel сохранил файл](../artifacts/mame-p11/delete/cancel-preserves.png);
|
||||
- [файл удалён после подтверждения](../artifacts/mame-p11/delete/file-deleted.png);
|
||||
- [пустой каталог удалён](../artifacts/mame-p11/delete/empty-dir-deleted.png);
|
||||
- [непустой каталог сохранён](../artifacts/mame-p11/delete/nonempty-preserved.png);
|
||||
- [`..` заблокирован](../artifacts/mame-p11/delete/virtual-guard.png);
|
||||
- [неоднозначная группа заблокирована](../artifacts/mame-p11/delete/group-guard.png);
|
||||
- [штатный возврат в DSS](../artifacts/mame-p11/delete/clean-dss-return.png).
|
||||
|
||||
## Размер
|
||||
|
||||
Конфигурация `--memory big --safe --max-allocs 3000`:
|
||||
|
||||
| Область | После P10 | После P11 | Изменение |
|
||||
|---|---:|---:|---:|
|
||||
| `_CODE` W2 | 9463 | 9496 | +33 |
|
||||
| данные W2 | 3256 | 3256 | 0 |
|
||||
| свободная куча W2 | 2129 | 2096 | -33 |
|
||||
| BANK1 | 6477 | 6477 | 0 |
|
||||
| BANK2 | 10008 | 10942 | +934 |
|
||||
| `SPRCMD.EXE` | 43040 | 43073 | +33 |
|
||||
|
||||
В BANK2 остаётся 5442 байта, в BANK1 — 9907 байт. Межбанковый аудит чист.
|
||||
|
||||
## Воспроизведение
|
||||
|
||||
```sh
|
||||
make hdd-p11-delete ALLOCS=3000
|
||||
tests/run_mame_hdd.sh build/hdd/p11_delete.chd \
|
||||
tests/mame_p11_delete.lua artifacts/mame-p11/local 67
|
||||
tests/check_p11_delete_hdd.sh build/hdd/p11_delete.chd
|
||||
```
|
||||
@@ -0,0 +1,78 @@
|
||||
# Sprinter Commander: групповое копирование файлов 0.2/P12
|
||||
|
||||
Дата: 9 сентября 2026 года.
|
||||
|
||||
Статус: `PASS` в MAME 0.288 / BIOS 3.06 / DSS 1.71.57 на HDD `D:`.
|
||||
|
||||
Примечание: документ фиксирует исторический срез P12, где directory roots
|
||||
ещё пропускались. Начиная с P16 текущий F5 копирует их рекурсивно; fixture и
|
||||
checker P12 обновлены как регрессия нового контракта, а исходные кадры ниже
|
||||
оставлены для истории этапа.
|
||||
|
||||
## Контракт первого группового среза
|
||||
|
||||
Если в активной панели нет выбора, F5 сохраняет проверенное поведение
|
||||
однофайлового PoC. При ненулевом выборе F5:
|
||||
|
||||
1. Считает выбранные обычные файлы и отдельно выбранные каталоги.
|
||||
2. Последовательно копирует только файлы в порядке текущей сортировки панели.
|
||||
3. Для каждого файла использует прежний crash-safe `temp -> commit` и общую
|
||||
4-КБ EMM-страницу W3.
|
||||
4. Проверяет Esc между блоками и файлами.
|
||||
5. При cancel/error удаляет только незавершённый temp; уже завершённые файлы
|
||||
сохраняются.
|
||||
6. Останавливается на первой ошибке и сообщает `copied/total`, число
|
||||
пропущенных каталогов и errno.
|
||||
7. Перечитывает passive panel один раз после завершённых копий.
|
||||
|
||||
Выбранные каталоги намеренно игнорируются до появления рекурсивной job. Это
|
||||
не меняет их флаг выбора и согласуется с правилом: групповые `+`, `-`, `*`
|
||||
работают только над файлами.
|
||||
|
||||
Overwrite по-прежнему запрещён. Повторный запуск поверх уже скопированных
|
||||
файлов останавливается на первом `EEXIST`; существующие targets не меняются.
|
||||
|
||||
## Проверка
|
||||
|
||||
HDD-fixture содержит выбранный вручную `ADIR` и три файла размером 10, 4097
|
||||
и 0 байт. После первого F5 внешний checker подтверждает три побайтно точные
|
||||
копии, отсутствие каталога и temp. Второй F5 проверяет управляемую остановку
|
||||
на существующем `A.BIN`.
|
||||
|
||||
```text
|
||||
PASS: 3 selected files copied; selected directory skipped; no temp.
|
||||
```
|
||||
|
||||
Контрольные состояния:
|
||||
|
||||
- [каталог и три файла выбраны](../artifacts/mame-p12/group-copy/selected-files-and-dir.png);
|
||||
- [скопировано 3/3, один каталог пропущен](../artifacts/mame-p12/group-copy/copied-and-skipped-dir.png);
|
||||
- [повтор остановлен на EEXIST](../artifacts/mame-p12/group-copy/repeat-stops-eexist.png);
|
||||
- [passive panel перечитана](../artifacts/mame-p12/group-copy/passive-refreshed.png);
|
||||
- [штатный возврат в DSS](../artifacts/mame-p12/group-copy/clean-dss-return.png).
|
||||
|
||||
## Компоновка и размер
|
||||
|
||||
Конфигурация `--memory big --safe --max-allocs 3000`:
|
||||
|
||||
| Область | После P11 | После P12 | Изменение |
|
||||
|---|---:|---:|---:|
|
||||
| `_CODE` W2 | 9496 | 9496 | 0 |
|
||||
| данные W2 | 3256 | 3256 | 0 |
|
||||
| свободная куча W2 | 2096 | 2096 | 0 |
|
||||
| BANK1 | 6477 | 6477 | 0 |
|
||||
| BANK2 | 10942 | 12256 | +1314 |
|
||||
| `SPRCMD.EXE` | 43073 | 43073 | 0 |
|
||||
|
||||
Пробный перенос обработчика в новый BANK3 увеличивал EXE ровно на 16384
|
||||
байта при 2823 байтах полезного кода. Поэтому P12 оставлен в BANK2: там ещё
|
||||
4128 байт запаса, а размер файла не вырос. Межбанковый аудит чист.
|
||||
|
||||
## Воспроизведение
|
||||
|
||||
```sh
|
||||
make hdd-p12-group-copy ALLOCS=3000
|
||||
tests/run_mame_hdd.sh build/hdd/p12_group_copy.chd \
|
||||
tests/mame_p12_group_copy.lua artifacts/mame-p12/local 57
|
||||
tests/check_p12_group_copy_hdd.sh build/hdd/p12_group_copy.chd
|
||||
```
|
||||
@@ -0,0 +1,78 @@
|
||||
# Sprinter Commander: групповое удаление файлов 0.2/P13
|
||||
|
||||
Дата: 9 сентября 2026 года.
|
||||
|
||||
Статус: `PASS` в MAME 0.288 / BIOS 3.06 / DSS 1.71.57 на HDD `D:`.
|
||||
|
||||
Это исторический контракт P13. Начиная с P17 выбранные каталоги не
|
||||
пропускаются, а удаляются рекурсивно после одного общего подтверждения; новый
|
||||
контракт и актуальные артефакты описаны в
|
||||
[p17-tree-delete-results.md](p17-tree-delete-results.md). Текущий P13 fixture
|
||||
оставлен как регрессия file-only группы.
|
||||
|
||||
## Контракт первого группового среза F8
|
||||
|
||||
Если в активной панели есть выбранные записи, F8:
|
||||
|
||||
1. Считает выбранные обычные файлы и выбранные каталоги отдельно.
|
||||
2. Если выбранных файлов нет, не открывает подтверждение и сообщает, что
|
||||
рекурсивное удаление каталогов ещё не реализовано.
|
||||
3. Показывает одно подтверждение для всей группы файлов.
|
||||
4. После подтверждения проходит EMM-store в порядке панели и удаляет только
|
||||
выбранные записи без `FA_DIREC`.
|
||||
5. Останавливается на первой ошибке. Уже удалённые файлы не восстанавливаются;
|
||||
итог сообщает `deleted/total`, число пропущенных каталогов и errno.
|
||||
6. После хотя бы одного успешного удаления перечитывает активную панель.
|
||||
|
||||
Выбранные каталоги не удаляются и не обходятся рекурсивно. Это согласуется с
|
||||
контрактом групповых `+`, `-`, `*`: групповые файловые команды отделяют файлы
|
||||
от каталогов. При отсутствии выбора сохранён однообъектный F8 из P11: файл
|
||||
удаляется через `unlink()`, пустой каталог — через `rmdir()`.
|
||||
|
||||
## Проверка
|
||||
|
||||
HDD-fixture содержит два выбранных файла, один невыбранный файл, выбранный
|
||||
пустой каталог и выбранный непустой каталог. Сценарий сначала отменяет
|
||||
групповое подтверждение, затем повторяет операцию с подтверждением. После
|
||||
refresh отдельно выбирается только каталог: F8 обязан отказать без диалога и
|
||||
без изменения носителя.
|
||||
|
||||
После чистого выхода MAME дерево извлекается из CHD и проверяется побайтно:
|
||||
|
||||
```text
|
||||
PASS: selected files deleted; selected directories and KEEP preserved.
|
||||
```
|
||||
|
||||
Контрольные состояния:
|
||||
|
||||
- [два файла и два каталога выбраны](../artifacts/mame-p13/group-delete/selected-files-and-dirs.png);
|
||||
- [одно подтверждение группы](../artifacts/mame-p13/group-delete/group-confirm.png);
|
||||
- [отмена сохранила группу](../artifacts/mame-p13/group-delete/cancel-preserves.png);
|
||||
- [файлы удалены, каталоги сохранены](../artifacts/mame-p13/group-delete/files-deleted-dirs-preserved.png);
|
||||
- [directory-only группа отклонена](../artifacts/mame-p13/group-delete/directory-only-refused.png);
|
||||
- [штатный возврат в DSS](../artifacts/mame-p13/group-delete/clean-dss-return.png).
|
||||
|
||||
## Компоновка и размер
|
||||
|
||||
Конфигурация `--memory big --safe --max-allocs 3000`:
|
||||
|
||||
| Область | После P12 | После P13 | Изменение |
|
||||
|---|---:|---:|---:|
|
||||
| `_CODE` W2 | 9496 | 9496 | 0 |
|
||||
| данные W2 | 3256 | 3256 | 0 |
|
||||
| свободная куча W2 | 2096 | 2096 | 0 |
|
||||
| BANK1 | 6477 | 6477 | 0 |
|
||||
| BANK2 | 12256 | 13267 | +1011 |
|
||||
| `SPRCMD.EXE` | 43073 | 43073 | 0 |
|
||||
|
||||
В BANK2 остаётся 3117 байт. HOME и размер EXE не выросли; межбанковый аудит
|
||||
чист.
|
||||
|
||||
## Воспроизведение
|
||||
|
||||
```sh
|
||||
make hdd-p13-group-delete ALLOCS=3000
|
||||
tests/run_mame_hdd.sh build/hdd/p13_group_delete.chd \
|
||||
tests/mame_p13_group_delete.lua artifacts/mame-p13/local 58
|
||||
tests/check_p13_group_delete_hdd.sh build/hdd/p13_group_delete.chd
|
||||
```
|
||||
@@ -0,0 +1,62 @@
|
||||
# Sprinter Commander: стандартные цвета имён P14
|
||||
|
||||
Дата: 9 сентября 2026 года.
|
||||
|
||||
Статус: `PASS` в MAME 0.288 / BIOS 3.06 / DSS 1.71.57 на HDD `D:`.
|
||||
|
||||
## Реализованный профиль без конфигурации
|
||||
|
||||
Renderer выбирает атрибут обычной строки по типу записи:
|
||||
|
||||
- каталоги и синтетическая `..` — белые;
|
||||
- файлы с точным регистронезависимым расширением `.EXE` — жёлтые;
|
||||
- остальные файлы, в том числе без расширения, — светло-серые;
|
||||
- виртуальный маркер предела DSS остаётся красным;
|
||||
- cursor активной панели сохраняет отдельную контрастную cyan-подсветку.
|
||||
|
||||
Цвет применяется ко всей строке записи, поэтому имя, размер и атрибуты не
|
||||
расходятся визуально. `SC_ATTR_DIR` и `SC_ATTR_EXE` переиспользуют уже
|
||||
сохранённые палитровые атрибуты frame/title; новый атрибут потребовался только
|
||||
для обычного файла.
|
||||
|
||||
Настраиваемые правила по расширениям и файл конфигурации ещё не реализованы.
|
||||
Текущий профиль является обязательным fallback при отсутствии такого файла.
|
||||
|
||||
## Проверка
|
||||
|
||||
Отдельный HDD содержит `SUBDIR`, `APP.EXE`, `ARCHIVE.ZIP`, `README.TXT` и
|
||||
`NOEXT`. [Контрольный кадр](../artifacts/mame-p14/default-colors/file-types.png)
|
||||
показывает все три класса одновременно в активной и passive панелях. После
|
||||
F10 [палитра DSS восстановлена](../artifacts/mame-p14/default-colors/clean-dss-palette.png).
|
||||
Внешняя проверка извлечённого CHD подтверждает, что визуальный тест не изменил
|
||||
fixture:
|
||||
|
||||
```text
|
||||
PASS: colour fixture preserved.
|
||||
```
|
||||
|
||||
## Компоновка и размер
|
||||
|
||||
Конфигурация `--memory big --safe --max-allocs 3000`:
|
||||
|
||||
| Область | После P13 | После P14 | Изменение |
|
||||
|---|---:|---:|---:|
|
||||
| `_CODE` W2 | 9496 | 9503 | +7 |
|
||||
| данные W2 | 3256 | 3268 | +12 |
|
||||
| свободная куча W2 | 2096 | 2077 | -19 |
|
||||
| BANK1 | 6477 | 6477 | 0 |
|
||||
| BANK2 | 13267 | 13388 | +121 |
|
||||
| `SPRCMD.EXE` | 43073 | 43080 | +7 |
|
||||
|
||||
Двенадцать байт DATA/BSS — сохранённые RGB трёх компонент для четырёх
|
||||
палитровых планов нового атрибута. В BANK2 остаётся 2996 байт; межбанковый
|
||||
аудит чист.
|
||||
|
||||
## Воспроизведение
|
||||
|
||||
```sh
|
||||
make hdd-p14-default-colors ALLOCS=3000
|
||||
tests/run_mame_hdd.sh build/hdd/p14_default_colors.chd \
|
||||
tests/mame_p14_default_colors.lua artifacts/mame-p14/local 36
|
||||
tests/check_p14_default_colors_hdd.sh build/hdd/p14_default_colors.chd
|
||||
```
|
||||
@@ -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
|
||||
```
|
||||
@@ -0,0 +1,72 @@
|
||||
# Sprinter Commander: рекурсивный group F5 0.2/P16
|
||||
|
||||
Дата: 9 сентября 2026 года.
|
||||
|
||||
Статус: `PASS` в MAME 0.288 / BIOS 3.06 / DSS 1.71.57 на HDD `D:`.
|
||||
|
||||
## Реализованный контракт
|
||||
|
||||
При групповом F5 Commander сначала последовательно копирует выбранные файлы,
|
||||
а затем добавляет все выбранные каталоги текущего уровня как независимые
|
||||
roots в одну traversal queue P15. Для каждого root действует тот же контракт:
|
||||
|
||||
- breadth-first обход и пустые каталоги;
|
||||
- crash-safe temp/commit для файлов;
|
||||
- cancel между файлами, каталогами и 4-КБ блоками;
|
||||
- no-overwrite и self-target guard;
|
||||
- один динамический EMM-блок очереди на всю группу;
|
||||
- один refresh passive panel после операции.
|
||||
|
||||
Если top-level файл даёт ошибку или cancel до запуска обхода, directory roots
|
||||
остаются `unprocessed`. При ошибке одного tree root операция останавливается;
|
||||
уже committed-файлы и созданные каталоги сохраняются.
|
||||
|
||||
Это не меняет семантику `+`, `-`, `*`: групповые команды выделения по-прежнему
|
||||
игнорируют каталоги. Каталог может попасть в F5-группу только после отдельного
|
||||
`Insert`.
|
||||
|
||||
## Проверка
|
||||
|
||||
В active panel вручную выбраны `ADIR`, `BDIR` и `ROOT.BIN`; `KEEP.TXT`
|
||||
оставлен невыбранным. Два root-каталога содержат по файлу, а `ADIR` — ещё и
|
||||
пустой вложенный каталог. UI сообщил:
|
||||
|
||||
```text
|
||||
Tree copied: 3 files; 3 dirs.
|
||||
```
|
||||
|
||||
Внешний checker сравнил target с точным ожидаемым множеством и подтвердил
|
||||
отсутствие `KEEP.TXT` и temp:
|
||||
|
||||
```text
|
||||
PASS: selected file and two directory roots copied exactly.
|
||||
```
|
||||
|
||||
Контрольные состояния:
|
||||
|
||||
- [два каталога и файл выбраны](../artifacts/mame-p16/group-tree/selected-roots.png);
|
||||
- [группа скопирована](../artifacts/mame-p16/group-tree/copied.png);
|
||||
- [passive panel перечитана](../artifacts/mame-p16/group-tree/passive-refreshed.png);
|
||||
- [чистый возврат в DSS](../artifacts/mame-p16/group-tree/clean-dss-return.png).
|
||||
|
||||
## Компоновка
|
||||
|
||||
`_CODE` W2, DATA, BANK1 и размер EXE не изменились относительно P15. BANK2
|
||||
вырос с 14547 до 14918 байт (+371); остаётся 1466 байт. Итоговая сборка:
|
||||
|
||||
```text
|
||||
_CODE 9503; DATA 3524; heap 1821
|
||||
BANK1 9687/16384; BANK2 14918/16384
|
||||
SPRCMD.EXE 43080
|
||||
```
|
||||
|
||||
Межбанковый аудит чист.
|
||||
|
||||
## Воспроизведение
|
||||
|
||||
```sh
|
||||
make hdd-p16-group-tree-copy ALLOCS=3000
|
||||
tests/run_mame_hdd.sh build/hdd/p16_group_tree_copy.chd \
|
||||
tests/mame_p16_group_tree_copy.lua artifacts/mame-p16/local 60
|
||||
tests/check_p16_group_tree_copy_hdd.sh build/hdd/p16_group_tree_copy.chd
|
||||
```
|
||||
@@ -0,0 +1,101 @@
|
||||
# Sprinter Commander: рекурсивный group F8 0.2/P17
|
||||
|
||||
Дата: 10 сентября 2026 года.
|
||||
|
||||
Статус: `PASS` в MAME 0.288 / BIOS 3.06 / DSS 1.71.57 на HDD `D:`.
|
||||
|
||||
## Контракт
|
||||
|
||||
Если в панели есть выбранные записи, F8 показывает одно подтверждение для
|
||||
всей группы. После подтверждения:
|
||||
|
||||
1. выбранные обычные файлы верхнего уровня удаляются в порядке панели;
|
||||
2. выбранные каталоги становятся корнями одного BFS-job;
|
||||
3. содержимое каталогов перечисляется через `F_FIRST/F_NEXT`, файлы удаляются
|
||||
по мере обхода, а найденные подкаталоги добавляются в очередь;
|
||||
4. после полного scan очередь проходится в обратном порядке, поэтому каждый
|
||||
дочерний каталог удаляется раньше родителя;
|
||||
5. исходный CWD восстанавливается, динамическая EMM-страница освобождается,
|
||||
активная панель перечитывается.
|
||||
|
||||
Очередь та же, что у recursive F5: одна динамическая 16-КБ страница, 1008
|
||||
узлов по 16 байт, максимальная глубина 32. В W2 дерево не хранится. `Esc`
|
||||
проверяется между шагами; уже выполненные удаления при ошибке или отмене не
|
||||
откатываются, что явно отражается в статусе.
|
||||
|
||||
При отсутствии группового выбора однообъектный F8 сохраняет прежний контракт:
|
||||
файл удаляется, а каталог должен быть пустым.
|
||||
|
||||
## Проверка
|
||||
|
||||
Fixture содержит два выбранных дерева и выбранный файл, а также невыбранные
|
||||
`KEEPDIR/SAFE.BIN` и `KEEP.TXT`. Первое дерево имеет пустой каталог, четыре
|
||||
файла размеров 0/1/16/4097 и цепочку глубиной 12. Несколько файлов в одном
|
||||
каталоге отдельно проверяют продолжение `F_NEXT` после `unlink` текущей
|
||||
записи.
|
||||
|
||||
Сценарий сначала отменяет общее подтверждение и убеждается, что выбор и
|
||||
панель не изменились, затем подтверждает удаление. После выхода содержимое
|
||||
CHD извлекается и проверяется побайтно:
|
||||
|
||||
```text
|
||||
PASS: selected file and two directory trees deleted; KEEP preserved.
|
||||
```
|
||||
|
||||
Отдельный runtime-cancel fixture выбирает `TOP.BIN` и дерево из 180
|
||||
подкаталогов. `TOP.BIN` удаляется до начала обхода, затем Lua подаёт `Esc` во
|
||||
время заполнения очереди. После отмены дерево остаётся доступным, завершённое
|
||||
удаление не откатывается, `KEEP.TXT` не меняется:
|
||||
|
||||
```text
|
||||
PASS: runtime cancel kept remaining tree; completed TOP delete remains.
|
||||
```
|
||||
|
||||
Чтобы `Esc` не ждал завершения длинной серии `F_NEXT`, delete-итератор
|
||||
возвращает управление вызывающему коду после каждого добавленного каталога.
|
||||
|
||||
Контрольные кадры:
|
||||
|
||||
- [исходное дерево](../artifacts/mame-p17/tree-delete/before.png);
|
||||
- [два каталога и файл выбраны](../artifacts/mame-p17/tree-delete/selected-file-and-trees.png);
|
||||
- [общее подтверждение](../artifacts/mame-p17/tree-delete/group-confirm.png);
|
||||
- [отмена до mutation сохранила группу](../artifacts/mame-p17/tree-delete/cancel-preserves.png);
|
||||
- [после удаления остались только KEEP-объекты](../artifacts/mame-p17/tree-delete/deleted-keep-preserved.png);
|
||||
- [чистый возврат в DSS](../artifacts/mame-p17/tree-delete/clean-dss-return.png).
|
||||
|
||||
Runtime-cancel:
|
||||
|
||||
- [выбранное дерево и верхнеуровневый файл](../artifacts/mame-p17/tree-delete-cancel/selected-before-cancel.png);
|
||||
- [подтверждение](../artifacts/mame-p17/tree-delete-cancel/group-confirm.png);
|
||||
- [частичный результат после Esc](../artifacts/mame-p17/tree-delete-cancel/runtime-cancelled.png);
|
||||
- [чистый возврат в DSS](../artifacts/mame-p17/tree-delete-cancel/clean-dss-return.png).
|
||||
|
||||
## Компоновка
|
||||
|
||||
P17 остаётся в двух банках и не увеличивает образ EXE: каждый дополнительный
|
||||
банк добавил бы 16 КБ независимо от фактического заполнения. Текущая сборка
|
||||
`--memory big --safe --max-allocs 3000`:
|
||||
|
||||
```text
|
||||
_CODE 9503; DATA 3524; heap 1821
|
||||
BANK1 10562/16384; BANK2 15904/16384
|
||||
SPRCMD.EXE 43080
|
||||
```
|
||||
|
||||
В BANK2 остаётся 480 байт, поэтому следующий функциональный этап потребует
|
||||
осмысленной перекладки модулей в BANK3. Межбанковый аудит чист.
|
||||
|
||||
## Воспроизведение
|
||||
|
||||
```sh
|
||||
make hdd-p17-tree-delete ALLOCS=3000
|
||||
tests/run_mame_hdd.sh build/hdd/p17_tree_delete.chd \
|
||||
tests/mame_p17_tree_delete.lua artifacts/mame-p17/local 58
|
||||
tests/check_p17_tree_delete_hdd.sh build/hdd/p17_tree_delete.chd
|
||||
|
||||
make hdd-p17-tree-delete-cancel ALLOCS=3000
|
||||
tests/run_mame_hdd.sh build/hdd/p17_tree_delete_cancel.chd \
|
||||
tests/mame_p17_tree_delete_cancel.lua artifacts/mame-p17/cancel-local 50
|
||||
tests/check_p17_tree_delete_cancel_hdd.sh \
|
||||
build/hdd/p17_tree_delete_cancel.chd
|
||||
```
|
||||
@@ -0,0 +1,89 @@
|
||||
# Sprinter Commander: сохранение метаданных F5 0.2/P18
|
||||
|
||||
Дата: 10 сентября 2026 года.
|
||||
|
||||
Статус: `PASS` в MAME 0.288 / BIOS 3.06 / DSS 1.71.57 на HDD `D:`.
|
||||
|
||||
## Контракт
|
||||
|
||||
Одиночный, групповой и рекурсивный F5 сохраняют у каждого обычного файла:
|
||||
|
||||
- FAT date/time, включая двухсекундную точность;
|
||||
- атрибуты readonly, hidden, system и archive.
|
||||
|
||||
Date/time задаются вызовом DSS `PUT_D_T` на открытом temp-файле после
|
||||
последней записи и до `close()`. Атрибуты задаются через `ATTRIB` только
|
||||
после атомарного rename temp в конечное имя. Такой порядок не делает temp
|
||||
readonly до commit и сохраняет прежнюю защиту существующего target.
|
||||
|
||||
Если ошибка происходит до rename, общий cleanup закрывает дескрипторы и
|
||||
удаляет temp. Если `ATTRIB` или восстановление CWD завершаются ошибкой уже
|
||||
после rename, `ScCopyJob.committed` не позволяет выдавать результат за
|
||||
отсутствующий файл: UI сообщает `File committed; finalization failed`.
|
||||
|
||||
Метаданные каталогов на этом этапе не переносятся.
|
||||
|
||||
## Проверка
|
||||
|
||||
Fixture создаёт `MSRC/META.BIN` размером 4097 байт с контролируемым payload,
|
||||
датой `2001-02-03`, временем `04:05:06` и набором атрибутов `R/H/S/A`.
|
||||
Подготовленный raw FAT проверяется до MAME, чтобы текущая дата сборки не могла
|
||||
дать ложный положительный результат.
|
||||
|
||||
После F5 извлекается конечный CHD и собственным FAT16-reader проверяются обе
|
||||
32-байтовые directory entries и цепочка кластеров:
|
||||
|
||||
```text
|
||||
PASS: F5 preserved exact FAT date/time and R/H/S/A attributes.
|
||||
```
|
||||
|
||||
На экране обе панели показывают одинаковые размер, `RHSA` и
|
||||
`2001-02-03 04:05`; секунды и payload подтверждает raw-проверка.
|
||||
|
||||
Контрольные кадры:
|
||||
|
||||
- [исходные метаданные](../artifacts/mame-p18/metadata/source-metadata.png);
|
||||
- [файл скопирован с теми же полями](../artifacts/mame-p18/metadata/copied-metadata.png);
|
||||
- [пассивная панель после переключения](../artifacts/mame-p18/metadata/passive-metadata.png);
|
||||
- [чистый возврат в DSS](../artifacts/mame-p18/metadata/clean-dss-return.png).
|
||||
|
||||
После P18 повторно пройдены:
|
||||
|
||||
- P5 copy-fault matrix: `ALL PASS`, CHD-check `PASS`;
|
||||
- P16 recursive group F5: UI корректен, все выбранные деревья и файлы
|
||||
совпали побайтно.
|
||||
|
||||
## ABI DSS
|
||||
|
||||
`sc_metadata_set_datetime()` раскрывает packed FAT date/time в поля
|
||||
`PUT_D_T $18`. Низкоуровневый leaf получает handle в `A`, near-указатель на
|
||||
семибайтовую структуру в `DE`, загружает год в `IX` и сохраняет caller IX.
|
||||
|
||||
Для `ATTRIB $16` сгенерированный `.asm` подтвердил важную особенность
|
||||
`__sdcccall(1)`: второй однобайтовый аргумент banked-wrapper лежит на стеке,
|
||||
а `E` занят номером банка. Поэтому публичная banked-функция вызывает
|
||||
локальный raw-leaf с обычным ABI; leaf снимает однобайтовый аргумент и адрес
|
||||
возврата явно. Оба пути переводят `CF/A` в `errno`.
|
||||
|
||||
## Компоновка
|
||||
|
||||
Сборка `--memory big --safe --max-allocs 3000` остаётся двухбанковой и не
|
||||
увеличивает EXE за счёт пустого третьего банка:
|
||||
|
||||
```text
|
||||
_CODE 9503; DATA 3531; heap 1814
|
||||
BANK1 11011/16384; BANK2 16145/16384
|
||||
SPRCMD.EXE 43080
|
||||
```
|
||||
|
||||
В BANK2 остаётся 239 байт. Следующий заметный UI/policy-модуль следует
|
||||
размещать с осмысленной перекладкой в BANK3. Межбанковый аудит чист.
|
||||
|
||||
## Воспроизведение
|
||||
|
||||
```sh
|
||||
make hdd-p18-metadata ALLOCS=3000
|
||||
tests/run_mame_hdd.sh build/hdd/p18_metadata.chd \
|
||||
tests/mame_p18_metadata.lua artifacts/mame-p18/local 48
|
||||
tests/check_p18_metadata_hdd.sh build/hdd/p18_metadata.chd
|
||||
```
|
||||
@@ -0,0 +1,129 @@
|
||||
# Sprinter Commander: conflict-policy F5 0.2/P19
|
||||
|
||||
Дата: 10 сентября 2026 года.
|
||||
|
||||
Статус: `PASS` в MAME 0.288 / BIOS 3.06 / DSS 1.71.57 на HDD `D:`.
|
||||
|
||||
## Поведение
|
||||
|
||||
Если конечное имя файла существует, F5 показывает единый choice-dialog:
|
||||
|
||||
- `O` — безопасно заменить только этот файл;
|
||||
- `S` — пропустить только этот файл;
|
||||
- `R` — ввести другое DOS 8.3-имя;
|
||||
- `A` — заменить этот и все следующие конфликтующие файлы текущей группы;
|
||||
- `N` — пропустить этот и все следующие конфликты текущей группы;
|
||||
- `Esc` — отменить текущую операцию.
|
||||
|
||||
Policy `all` живёт только до завершения одного вызова F5 и распространяется
|
||||
на top-level группу и файлы рекурсивной job. Readonly target не заменяется
|
||||
молча даже после ранее выбранного `overwrite all`: для него снова появляется
|
||||
диалог `Read-only target`. Групповые `+/-/*` по-прежнему работают только над
|
||||
файлами; выбор каталогов через `Insert` не менялся.
|
||||
|
||||
`Rename` меняет только имя target, проверяет один компонент DOS 8.3 и не
|
||||
переименовывает source. Если новое имя тоже занято, policy запрашивается
|
||||
повторно.
|
||||
|
||||
## Транзакционная замена
|
||||
|
||||
Существующий target не удаляется перед копированием. После полной записи и
|
||||
закрытия нового temp выполняется последовательность:
|
||||
|
||||
1. выбирается отсутствующее имя `~SBxxxx.BAK` в том же каталоге;
|
||||
2. readonly временно снимается только после явного разрешения overwrite;
|
||||
3. старый target переименовывается в backup;
|
||||
4. новый temp переименовывается в target;
|
||||
5. новому target назначаются исходные date/time и R/H/S/A;
|
||||
6. backup удаляется.
|
||||
|
||||
Если шаг commit нового temp не проходит, старый target переименовывается
|
||||
обратно и его атрибуты восстанавливаются. Заранее существующие backup-имена
|
||||
пропускаются, а не перезаписываются. Ошибка после необратимого commit
|
||||
помечается `ScCopyJob.committed`, поэтому UI не сообщает, что target якобы
|
||||
отсутствует.
|
||||
|
||||
## Проверка
|
||||
|
||||
Отдельная target-проба прошла на реальном DSS-пути:
|
||||
|
||||
```text
|
||||
PASS default still refuses existing target
|
||||
PASS overwrite commits through backup
|
||||
PASS explicit readonly overwrite
|
||||
PASS pre-existing backup name skipped
|
||||
PASS replace policy accepts vanished target
|
||||
PASS distinct destination name
|
||||
PASS rename failure rolls original target back
|
||||
PASS EMM restored
|
||||
ALL PASS
|
||||
```
|
||||
|
||||
CHD-check побайтно подтвердил заменённые файлы, rollback исходного payload,
|
||||
сохранность заранее созданного `~SB0000.BAK` и отсутствие новых backup/temp:
|
||||
|
||||
```text
|
||||
PASS: transactional overwrite, readonly, collision and rollback.
|
||||
```
|
||||
|
||||
Полный UI-сценарий выбрал три файла для `overwrite all`, получил отдельный
|
||||
readonly prompt, затем применил `skip all` к двум файлам, скопировал
|
||||
`REN.BIN` как `NEXT.BIN` и отменил конфликт `CANCEL.BIN`. Итоговая проверка:
|
||||
|
||||
```text
|
||||
PASS: overwrite-all, readonly prompt, skip-all, rename and cancel.
|
||||
```
|
||||
|
||||
Контрольные кадры target-пробы:
|
||||
|
||||
- [ALL PASS](../artifacts/mame-p19/overwrite-core/all-pass.png);
|
||||
- [чистый возврат](../artifacts/mame-p19/overwrite-core/clean-dss-return.png).
|
||||
|
||||
UI policy:
|
||||
|
||||
- [выбор overwrite all](../artifacts/mame-p19/policy/overwrite-choice.png);
|
||||
- [повторный readonly prompt](../artifacts/mame-p19/policy/readonly-reprompt.png);
|
||||
- [три файла заменены](../artifacts/mame-p19/policy/overwrite-complete.png);
|
||||
- [выбор skip all](../artifacts/mame-p19/policy/skip-all-choice.png);
|
||||
- [оба файла пропущены](../artifacts/mame-p19/policy/skip-complete.png);
|
||||
- [rename-conflict](../artifacts/mame-p19/policy/rename-choice.png);
|
||||
- [`NEXT.BIN` создан](../artifacts/mame-p19/policy/renamed.png);
|
||||
- [отмена конфликта](../artifacts/mame-p19/policy/cancel-choice.png);
|
||||
- [диалог полностью убран после Esc](../artifacts/mame-p19/policy/cancel-cleared.png);
|
||||
- [итог пассивной панели](../artifacts/mame-p19/policy/target-result.png);
|
||||
- [чистый возврат в DSS](../artifacts/mame-p19/policy/clean-dss-return.png).
|
||||
|
||||
В процессе визуальная проверка нашла и устранила stale-dialog: после
|
||||
одиночных conflict `error/cancel/skip` теперь выполняется полный redraw, а не
|
||||
обновление только двух нижних строк.
|
||||
|
||||
После итоговой правки повторно пройдены P5 copy-fault matrix и P18 exact FAT
|
||||
metadata.
|
||||
|
||||
## Компоновка
|
||||
|
||||
EXEC-модуль перенесён из почти полного BANK2 в BANK1. Его строковые статусы
|
||||
теперь всегда копируются в W2 BSS, поэтому наружу не уходит указатель на
|
||||
bank-local literal. Это позволило оставить приложение двухбанковым:
|
||||
|
||||
```text
|
||||
_CODE 9503; DATA 3561; heap 1784
|
||||
BANK1 14374/16384; BANK2 15665/16384
|
||||
SPRCMD.EXE 43080
|
||||
```
|
||||
|
||||
Межбанковый аудит чист. В BANK1 остаётся 2010 байт, в BANK2 — 719.
|
||||
|
||||
## Воспроизведение
|
||||
|
||||
```sh
|
||||
make hdd-p19-overwrite ALLOCS=3000
|
||||
tests/run_mame_hdd.sh build/hdd/p19_overwrite.chd \
|
||||
tests/mame_p19_overwrite.lua artifacts/mame-p19/core-local 26
|
||||
tests/check_p19_overwrite_hdd.sh build/hdd/p19_overwrite.chd
|
||||
|
||||
make hdd-p19-policy ALLOCS=3000
|
||||
tests/run_mame_hdd.sh build/hdd/p19_policy.chd \
|
||||
tests/mame_p19_policy.lua artifacts/mame-p19/policy-local 106
|
||||
tests/check_p19_policy_hdd.sh build/hdd/p19_policy.chd
|
||||
```
|
||||
@@ -0,0 +1,81 @@
|
||||
# Sprinter Commander: результаты палитровой пробы P2.2
|
||||
|
||||
Дата: 6 сентября 2026 года.
|
||||
|
||||
Статус: `PASS` в MAME; проверка на реальном Sprinter ожидается.
|
||||
|
||||
Окружение: MAME 0.287 (`b0c4527c`), машина `sprinter`, BIOS `v3.06`.
|
||||
Исходник пробы находится в `tests/p2_palette/p2palette.c`, кадры — в
|
||||
[`artifacts/mame-p2/palette/`](../artifacts/mame-p2/palette/).
|
||||
|
||||
## 1. Проверенный контракт
|
||||
|
||||
Проба использует только публичные системные функции
|
||||
`text_pal_get_color()`/`text_pal_set_color()`:
|
||||
|
||||
- сохраняет шесть применяемых атрибутов во всех четырёх текстовых планах;
|
||||
- задаёт normal-paper и flash-paper одинаковыми;
|
||||
- задаёт normal-ink и flash-ink одинаковыми;
|
||||
- читает обратно все 24 записи и сравнивает каждый RGB-компонент;
|
||||
- показывает одновременно шесть контрастных цветовых сочетаний;
|
||||
- перед выходом восстанавливает каждую сохранённую запись.
|
||||
|
||||
Проверенные сочетания: white/blue, yellow/green, white/red,
|
||||
white/magenta, black/lightgray и black/cyan. Runtime-результат:
|
||||
|
||||
```text
|
||||
Readback of 6 attrs x 4 planes: PASS (0 mismatches)
|
||||
```
|
||||
|
||||
## 2. Стабильность изображения
|
||||
|
||||
Пять кадров одной неизменной сцены сняты в разных моментах. После
|
||||
декодирования PNG область экрана Sprinter во всех кадрах пиксельно идентична.
|
||||
Единственное различие между первым и остальными кадрами — 42 пикселя в
|
||||
прямоугольнике `x=0..13, y=42..44`: это внешний индикатор обращения к
|
||||
`Drive A` в layout MAME, а не видеовыход Sprinter. Его состояние в экранных
|
||||
тестах не учитывается.
|
||||
|
||||
Файлы `phase-b.png`–`phase-e.png` идентичны целиком. Их SHA-256:
|
||||
|
||||
```text
|
||||
5eda33a6289dcd0afbb10573b70bcd701c428d6ed8f2aaae55d16c8d32dbe9cb
|
||||
```
|
||||
|
||||
Артефакты:
|
||||
|
||||
- [phase-a.png](../artifacts/mame-p2/palette/phase-a.png) — горит индикатор
|
||||
Drive A;
|
||||
- [phase-b.png](../artifacts/mame-p2/palette/phase-b.png),
|
||||
[phase-c.png](../artifacts/mame-p2/palette/phase-c.png),
|
||||
[phase-d.png](../artifacts/mame-p2/palette/phase-d.png),
|
||||
[phase-e.png](../artifacts/mame-p2/palette/phase-e.png) — стабильная сцена;
|
||||
- [restored-dos.png](../artifacts/mame-p2/palette/restored-dos.png) — возврат
|
||||
в DSS после восстановления палитры.
|
||||
|
||||
## 3. Исправление прежней интерпретации
|
||||
|
||||
Ранее встроенный предпросмотр двух PNG ошибочно показал исчезновение части
|
||||
цветных строк. Проверка исходных файлов опровергла это:
|
||||
|
||||
- `partial-flash-phase.png` и `partial-contrast-stable.png` из P1 имеют
|
||||
идентичные декодированные RGB-пиксели;
|
||||
- все три исторически названных `flash-before-*.png` из P2.1 также полностью
|
||||
идентичны;
|
||||
- текущая P2.2-сцена различается только внешним LED дисковода MAME.
|
||||
|
||||
Следовательно, гипотеза о нестабильных FLASH-планах была артефактом
|
||||
предпросмотра. Она не является ограничением BIOS, libc, MAME или Commander.
|
||||
|
||||
## 4. Принятое решение
|
||||
|
||||
Многоцветная тема разрешена для Commander. Модуль `sc_theme`:
|
||||
|
||||
- владеет точными RGB смысловых атрибутов;
|
||||
- сохраняет только затрагиваемые записи всех четырёх планов;
|
||||
- устанавливает одинаковые normal/FLASH-пары;
|
||||
- восстанавливает исходные записи при любом штатном cleanup.
|
||||
|
||||
До аппаратного прогона `PASS` относится к MAME 0.287 / BIOS 3.06. На реальном
|
||||
Sprinter требуется проверить те же цвета и возврат исходной палитры после
|
||||
выхода.
|
||||
@@ -0,0 +1,270 @@
|
||||
# Sprinter Commander: экранный backend P2
|
||||
|
||||
Статус: ядро P2.1 и многоцветная тема P2.2 прошли MAME; аппаратная проверка,
|
||||
восстановление после child и измерение времени ожидаются.
|
||||
|
||||
Связанные документы:
|
||||
[архитектура](commander-architecture.md),
|
||||
[требования](commander-requirements.md),
|
||||
[roadmap](commander-roadmap.md),
|
||||
[результаты P1](p1-platform-probes.md),
|
||||
[результаты P2.1](p2-screen-probe-results.md),
|
||||
[результаты P2.2](p2-palette-results.md)
|
||||
|
||||
## 1. Решение
|
||||
|
||||
Commander не использует систему оконных дескрипторов BIOS (`WIN_OPEN`,
|
||||
`WIN_CLOSE`, `WIN_COPY_WIN`, `WIN_RESTORE_WIN`). Текущий BIOS хранит только
|
||||
один описатель с идентификатором 0; открытие нового окна уничтожает прежнее
|
||||
состояние. Диалоги, панели и меню поэтому являются прямоугольниками внутри
|
||||
собственной модели экрана Commander, а не окнами BIOS.
|
||||
|
||||
Целевой backend выводит в координаты полного экрана через системные функции,
|
||||
но не открывает и не переключает BIOS-окна. Основной пакетный примитив —
|
||||
ESTEX `WINREST`; для специальных операций доступны координатные `WRCHAR`,
|
||||
`CLEAR` и `SCROLL`. Прямой доступ к VRAM не используется.
|
||||
|
||||
## 2. Что уже доказано
|
||||
|
||||
P1 и `examples/mdview2` подтвердили формат логического буфера:
|
||||
|
||||
```text
|
||||
(char, attr), по строкам, stride = width * 2, без padding
|
||||
```
|
||||
|
||||
`WINREST` корректно выводит полный экран, отдельную строку и прямоугольник из
|
||||
ненулевого смещения EMM-страницы. Эта функция ESTEX не создаёт BIOS-окно и не
|
||||
использует его идентификатор; слово `WIN` здесь означает прямоугольную область
|
||||
текущего экрана.
|
||||
|
||||
`mdview2` — полезный образец полноэкранного приложения: состояние документа
|
||||
принадлежит программе, строки материала выводятся из собственного кэша, а
|
||||
status/menu рисуются координатными примитивами. Commander сохраняет этот
|
||||
принцип, но выносит способ вывода в отдельный backend.
|
||||
|
||||
## 3. Почему используем системный координатный вывод
|
||||
|
||||
- DSS/BIOS уже знают реальное устройство текстового режима, знакогенератора,
|
||||
активной screen page и системной палитры.
|
||||
- `WINREST` выводит сразу прямоугольник из EMM, поэтому не требуется RST на
|
||||
каждую ячейку.
|
||||
- Координатные ESTEX-функции не используют идентификатор глобального окна.
|
||||
- После возврата из child достаточно снова установить 80x32 и вывести всю
|
||||
экранную модель.
|
||||
- Один путь работает и в MAME, и на реальном Sprinter без предположений о
|
||||
внутренней раскладке VRAM.
|
||||
|
||||
## 4. Граница между оконными и безоконными функциями
|
||||
|
||||
Запрещены функции управления глобальными окнами, где передаётся window ID:
|
||||
`WIN_OPEN`, `WIN_CLOSE`, `WIN_COPY_WIN`, `WIN_RESTORE_WIN`, `WIN_MOVE_WIN`.
|
||||
Они зависят от единственного поддерживаемого описателя 0.
|
||||
|
||||
Разрешены:
|
||||
|
||||
- ESTEX `WINREST 5Ah` — прямоугольник `(row, col, height, width)`, EMM page
|
||||
и source address, без window ID;
|
||||
- ESTEX `WRCHAR 58h`, `CLEAR 56h`, `SCROLL 55h` — абсолютные координаты
|
||||
активной screen page;
|
||||
- BIOS `LP_SET_PLACE`/`LP_PRINT_*`/`LP_CLS_WIN*` — только как пакетные
|
||||
оптимизации для глобального экрана, без открытия новых окон.
|
||||
|
||||
Системные вызовы чтения и записи текстовой палитры также разрешены. Их
|
||||
отсутствие в P2.1 — способ изолировать экранный backend, а не архитектурный
|
||||
запрет. Выделенный модуль темы использует их для полного save/set/restore;
|
||||
контракт подтверждён P2.2.
|
||||
|
||||
Чтобы renderer не зависел от текущего cursor/place, основным остаётся
|
||||
`WINREST`. Если применяется BIOS `LP_*`, backend всегда сам устанавливает
|
||||
place непосредственно перед операцией и не оставляет его частью состояния UI.
|
||||
|
||||
## 5. Палитра и FLASH
|
||||
|
||||
У Sprinter атрибут — индекс в четыре текстовых палитровых плана: paper, ink,
|
||||
flash-paper и flash-ink. Сигнал FLASH переключает пары глобально. Чтобы тема
|
||||
Commander не мигала, для каждого используемого атрибута normal-paper и
|
||||
flash-paper задаются одинаково, как и normal-ink с flash-ink.
|
||||
|
||||
Первый pattern P1 с raw-атрибутами `0x11..0x17` действительно содержал
|
||||
blue-on-blue. Это ошибка выбора атрибутов, а не нестабильность палитры;
|
||||
арифметическое построение атрибутов для UI поэтому по-прежнему запрещено.
|
||||
|
||||
Прежняя гипотеза о пропадающих цветных строках была вызвана неверным
|
||||
предпросмотром PNG. Проверка декодированных файлов показала, что именованные
|
||||
цветные кадры P1 и три диагностических кадра P2.1 пиксельно идентичны.
|
||||
|
||||
P2.2 отдельно проверил шесть цветовых атрибутов:
|
||||
|
||||
- сохранены все 24 исходные записи (6 атрибутов × 4 плана);
|
||||
- установлены одинаковые normal/FLASH-пары;
|
||||
- обратное чтение дало `PASS (0 mismatches)`;
|
||||
- пять временных кадров имеют одинаковый viewport Sprinter;
|
||||
- перед выходом исходные записи восстановлены.
|
||||
|
||||
Единственное изменение между двумя полными PNG находилось в служебной панели
|
||||
MAME: 42 пикселя LED `Drive A`. Элементы layout вне viewport Sprinter в
|
||||
экранных сравнениях не учитываются. Подробности — в
|
||||
[результатах P2.2](p2-palette-results.md).
|
||||
|
||||
## 6. Интерфейс backend
|
||||
|
||||
Минимальный интерфейс не раскрывает ESTEX/BIOS остальным модулям:
|
||||
|
||||
```c
|
||||
int sc_video_init(void);
|
||||
void sc_video_shutdown(void);
|
||||
int sc_video_present_full(uint8_t emm_page);
|
||||
int sc_video_present_rect(uint8_t emm_page, uint16_t offset,
|
||||
uint8_t row, uint8_t col,
|
||||
uint8_t height, uint8_t width);
|
||||
int sc_video_scroll_rect(uint8_t row, uint8_t col,
|
||||
uint8_t height, uint8_t width,
|
||||
int8_t delta);
|
||||
```
|
||||
|
||||
`sc_screen` строит `(char, attr)` в EMM и вызывает backend. Он не вызывает
|
||||
`WIN_OPEN`, `WRCHAR`, `WINREST` или BIOS непосредственно. Единственная
|
||||
реализация PoC — `sc_video_system.c`; выбор конкретного системного примитива
|
||||
остаётся внутри неё.
|
||||
|
||||
## 7. Вертикальный текстовый scroll
|
||||
|
||||
`mdview2` использует эффективную схему для перемещения на одну строку:
|
||||
|
||||
```text
|
||||
Down: scroll(rect, UP) → нарисовать новую нижнюю строку
|
||||
Up: scroll(rect, DOWN) → нарисовать новую верхнюю строку
|
||||
PgUp/PgDn/Home/End → полный redraw области
|
||||
```
|
||||
|
||||
Его viewport — `(col=0, row=1, width=80, height=30)`. Вызов libc
|
||||
`scroll()` обращается к ESTEX `SCROLL 55h`, который сдвигает прямоугольник
|
||||
активной screen page на одну строку. Он не использует идентификатор окна
|
||||
BIOS и совместим с решением не применять window descriptors.
|
||||
|
||||
Исходники `mdview2` и контракт ESTEX не доказывают, что операция реализована
|
||||
аппаратным регистром видеоконтроллера: для документации это **системный
|
||||
прямоугольный scroll**, пока не появится измерение или исходник DSS. Возможный
|
||||
глобальный аппаратный сдвиг начала экрана для Commander всё равно неудобен —
|
||||
он одновременно сдвинет рамки и обе панели. Реализация
|
||||
`sc_video_scroll_rect()` делегирует прямоугольник ESTEX; это не создаёт
|
||||
зависимости от BIOS-окон.
|
||||
|
||||
Для Commander эта оптимизация применяется только когда `top` панели
|
||||
изменился ровно на один. Прямоугольники — внутренности панелей без рамок:
|
||||
|
||||
```text
|
||||
left: col=1, row=1, width=38, height=27
|
||||
right: col=41, row=1, width=38, height=27
|
||||
```
|
||||
|
||||
Точные размеры уточняются renderer-геометрией, но центральный разделитель,
|
||||
заголовок, footer и пассивная панель не должны входить в scroll rectangle.
|
||||
|
||||
Экранный EMM-буфер остаётся источником истины. Поэтому
|
||||
`sc_screen_scroll_rows()` сначала сдвигает соответствующие `(char, attr)` в
|
||||
модели и строит открытую строку, затем вызывает `sc_video_scroll_rect()` и
|
||||
выводит эту строку. Системный scroll без синхронизации модели запрещён:
|
||||
после диалога или EXEC полный redraw иначе вернул бы старые строки.
|
||||
|
||||
Fallback при любой неопределённости — present всех 27 строк активной панели.
|
||||
P2.1 должен проверить четыре границы прямоугольника, оба направления, верх и
|
||||
низ списка, 1000 последовательных scroll и неизменность соседней панели.
|
||||
|
||||
## 8. Псевдографика и CP866
|
||||
|
||||
`mdview2` подтверждает стандартный light box-drawing набор CP866/CP437:
|
||||
|
||||
| Имя | Код | Глиф | Имя | Код | Глиф |
|
||||
|---|---:|:---:|---|---:|:---:|
|
||||
| `SC_G_H` | `C4` | ─ | `SC_G_V` | `B3` | │ |
|
||||
| `SC_G_TL` | `DA` | ┌ | `SC_G_TR` | `BF` | ┐ |
|
||||
| `SC_G_BL` | `C0` | └ | `SC_G_BR` | `D9` | ┘ |
|
||||
| `SC_G_TM` | `C2` | ┬ | `SC_G_BM` | `C1` | ┴ |
|
||||
| `SC_G_ML` | `C3` | ├ | `SC_G_MR` | `B4` | ┤ |
|
||||
| `SC_G_X` | `C5` | ┼ | | | |
|
||||
|
||||
Стрелки для подсказок, если понадобятся: Up=`18`, Down=`19`, Right=`1A`,
|
||||
Left=`1B`. Это управляющий диапазон ASCII, поэтому такие байты нельзя
|
||||
передавать через API, интерпретирующий CR/LF/BEL/Esc. Они кладутся как raw
|
||||
glyph непосредственно в `ScCell`.
|
||||
|
||||
Правила Commander:
|
||||
|
||||
- все коды сосредоточены в `sc_glyphs.h`, чисел `0xB3` по renderer-модулям
|
||||
быть не должно;
|
||||
- исходная рамка строится из глифов, а не из UTF-8-литералов;
|
||||
- строки UI хранятся в CP866 или проходят явную build-time конвертацию;
|
||||
- border test рисует все 11 соединений и четыре стрелки;
|
||||
- атрибут каждого глифа задаётся через `COLOR(fg, bg)`;
|
||||
- при координатном raw-выводе символы `<0x20` считаются глифами, а не
|
||||
командами.
|
||||
|
||||
Для PoC достаточно одинарных рамок. Двойные рамки VC-класса добавляются
|
||||
после проверки полного набора соединений, чтобы не смешивать несовместимые
|
||||
single/double junctions.
|
||||
|
||||
Расширенный набор, уже подтверждённый таблицей конвертации `mdview2`, можно
|
||||
зарезервировать для прогресса, отметок и будущих тем:
|
||||
|
||||
| Имя | Код | Глиф | Назначение |
|
||||
|---|---:|:---:|---|
|
||||
| `SC_G_SHADE_LIGHT` | `B0` | ░ | светлая заливка |
|
||||
| `SC_G_SHADE_MEDIUM` | `B1` | ▒ | средняя заливка |
|
||||
| `SC_G_SHADE_DARK` | `B2` | ▓ | тёмная заливка |
|
||||
| `SC_G_BLOCK` | `DB` | █ | полный блок |
|
||||
| `SC_G_HALF_UP` | `DF` | ▀ | верхняя половина |
|
||||
| `SC_G_HALF_DOWN` | `DC` | ▄ | нижняя половина |
|
||||
| `SC_G_HALF_LEFT` | `DD` | ▌ | левая половина |
|
||||
| `SC_G_HALF_RIGHT` | `DE` | ▐ | правая половина |
|
||||
| `SC_G_BULLET` | `F9` | ∙ | маркер |
|
||||
| `SC_G_CHECK` | `FB` | √ | отметка |
|
||||
| `SC_G_SQUARE` | `FE` | ■ | квадрат |
|
||||
|
||||
`mdview2_help.c` рисует рамку этими кодами через `wrchar()` и BIOS fill-
|
||||
функции. Commander заимствует таблицу глифов и геометрию, но обращается к ним
|
||||
только через `ScCell`/`sc_video`: текущий cursor/place не становится частью
|
||||
модели интерфейса.
|
||||
|
||||
## 9. Coordinate-screen probe P2.1
|
||||
|
||||
Отдельный `tests/p2_screen/p2screen.exe` выполнен в MAME 0.287 / BIOS 3.06:
|
||||
|
||||
1. Режим 80x32 и одна EMM-страница экранной модели — `PASS`.
|
||||
2. Полный экран одним `WINREST` с полной сверкой через `RDCHAR` — `PASS`.
|
||||
3. Прямоугольник 20x2 из offset `0x2000` — `PASS`.
|
||||
4. Две рамки и таблица CP866-глифов — `PASS` визуально и по `RDCHAR`.
|
||||
5. Левая панель вверх и правая панель вниз — `PASS`; рамки и соседние клетки
|
||||
совпали с моделью.
|
||||
6. Синхронизация EMM-модели после системного `SCROLL` — `PASS`.
|
||||
7. 1000 чередующихся однострочных scroll с итоговой полной сверкой — `PASS`.
|
||||
8. Сохранение W3 вокруг `WINREST`, `RDCHAR` и `SCROLL` — `PASS`.
|
||||
9. Один системный атрибут стабилен в трёх временных фазах — `PASS`, кадры
|
||||
пиксельно идентичны.
|
||||
10. Шесть атрибутов и четыре палитровых плана — `PASS` отдельной пробой P2.2.
|
||||
|
||||
Остались отдельные проверки, не нужные для начала skeleton:
|
||||
|
||||
11. Повторить полную инициализацию после child, изменившего видеорежим.
|
||||
12. Сравнить время полного, однострочного и scroll-вывода.
|
||||
13. Повторить принятый сценарий на реальном Sprinter.
|
||||
|
||||
Подробные значения и кадры приведены в
|
||||
[результатах P2.1](p2-screen-probe-results.md).
|
||||
|
||||
## 10. Критерий выбора
|
||||
|
||||
Системный backend принят для реализации skeleton по результату MAME, потому
|
||||
что:
|
||||
|
||||
- совпадает с EMM-моделью по символам и атрибутам;
|
||||
- изолирует палитру в модуле темы и сохраняет отображение W3;
|
||||
- частичное обновление не затрагивает соседние клетки;
|
||||
- scroll прямоугольника не двигает рамку и пассивную панель;
|
||||
- все псевдографические соединения совпадают с CP866;
|
||||
- базовый системный атрибут стабилен во времени;
|
||||
- код не обращается к `RGADR`, `RGMOD` и VRAM-банкам напрямую.
|
||||
|
||||
До аппаратного принятия backend остаются прогон на реальном Sprinter и
|
||||
повторная инициализация после EXEC. Многоцветность подтверждена отдельным
|
||||
критерием P2.2 и не выводится из результата P2.1.
|
||||
@@ -0,0 +1,81 @@
|
||||
# Sprinter Commander: результаты экранной пробы P2.1
|
||||
|
||||
Дата: 6 сентября 2026 года.
|
||||
|
||||
Статус: `PASS` в MAME; проверка на реальном Sprinter ожидается.
|
||||
|
||||
Окружение: MAME 0.287 (`b0c4527c`), машина `sprinter`, BIOS `v3.06`.
|
||||
Исходники пробы находятся в `tests/p2_screen/`, сохранённые кадры — в
|
||||
[`artifacts/mame-p2/`](../artifacts/mame-p2/README.md).
|
||||
|
||||
## 1. Проверяемая граница
|
||||
|
||||
`p2screen.exe` проверяет системный координатный backend, а не интерфейс
|
||||
готового Commander:
|
||||
|
||||
- экранная модель 80x32 хранится в EMM как пары `(char, attr)`;
|
||||
- прямоугольники выводятся ESTEX `WINREST`;
|
||||
- фактические клетки читаются ESTEX `RDCHAR`;
|
||||
- области панелей сдвигаются ESTEX `SCROLL`;
|
||||
- BIOS window descriptors не открываются;
|
||||
- к `RGADR`, `RGMOD` и VRAM-банкам программа не обращается;
|
||||
- палитра в принятом варианте теста не читается и не изменяется.
|
||||
|
||||
Сборка: `--memory big --safe`.
|
||||
|
||||
| Область | Значение |
|
||||
|---|---:|
|
||||
| `_CODE` | 4 458 байт (`0x8100..0x926A`) |
|
||||
| данные | 581 байт (конец `0x94AF`) |
|
||||
| EXE | 5 004 байта |
|
||||
| heap после статики | 9 809 байт |
|
||||
| стек | 1 279 байт |
|
||||
|
||||
## 2. Runtime-результаты
|
||||
|
||||
| Этап | Результат | Артефакт |
|
||||
|---|---|---|
|
||||
| Полный `WINREST` 80x32 и полная сверка `RDCHAR` | PASS | [full.png](../artifacts/mame-p2/screen/full.png) |
|
||||
| Частичный `WINREST` 20x2 из offset `0x2000` | PASS | [partial-offset.png](../artifacts/mame-p2/screen/partial-offset.png) |
|
||||
| Scroll внутренности левой панели вверх | PASS | [left-scroll-up.png](../artifacts/mame-p2/screen/left-scroll-up.png) |
|
||||
| Scroll внутренности правой панели вниз | PASS | [right-scroll-down.png](../artifacts/mame-p2/screen/right-scroll-down.png) |
|
||||
| 1000 scroll, синхронная EMM-модель, итоговая сверка `RDCHAR` и W3 | PASS | [stress-final.png](../artifacts/mame-p2/screen/stress-final.png) |
|
||||
| Финальная сцена в трёх временных точках | PASS, файлы идентичны | [phase A](../artifacts/mame-p2/screen/stable-phase-a.png), [B](../artifacts/mame-p2/screen/stable-phase-b.png), [C](../artifacts/mame-p2/screen/stable-phase-c.png) |
|
||||
|
||||
SHA-256 каждого из трёх финальных кадров:
|
||||
|
||||
```text
|
||||
dabeb114f8a7574462aeeb0731363608c1afa940da66d518f734335b36fe8f3b
|
||||
```
|
||||
|
||||
Полная сверка после каждого функционального этапа сравнивала все 2560 клеток
|
||||
экрана с EMM-моделью, включая рамки, пассивную панель и общие строки. Поэтому
|
||||
`PASS` scroll означает не только появление новой строки, но и отсутствие
|
||||
побочного сдвига соседней области.
|
||||
|
||||
## 3. Граница палитры и исправление диагностики
|
||||
|
||||
P2.1 намеренно принят с одним системным атрибутом `0x30` и не вызывает
|
||||
палитровые функции: так координатный backend проверяется отдельно от темы.
|
||||
|
||||
Первичная визуальная интерпретация диагностических PNG ошибочно сообщала, что
|
||||
в одной временной фазе часть цветных символов исчезает. Проверка самих файлов
|
||||
показала обратное: три `flash-before-*.png` имеют одинаковые SHA-256 и
|
||||
одинаковые декодированные RGB-пиксели. Два именованных цветных кадра P1 также
|
||||
пиксельно идентичны.
|
||||
|
||||
Отдельная проба P2.2 затем подтвердила save/set/readback/restore всех четырёх
|
||||
планов и стабильную сцену с шестью атрибутами. Подробности и исправление
|
||||
ошибочной гипотезы приведены в
|
||||
[результатах P2.2](p2-palette-results.md).
|
||||
|
||||
## 4. Что ещё не проверено
|
||||
|
||||
- повторная инициализация backend после child, сменившего видеорежим;
|
||||
- измерение времени полного present, одной строки и системного scroll;
|
||||
- тот же сценарий на реальном Sprinter;
|
||||
- интеграционный прогон многоцветного `SPRCMD.EXE` после подключения темы.
|
||||
|
||||
Эти пункты не блокируют реализацию экранной модели и skeleton P2. Аппаратный
|
||||
прогон остаётся gate перед признанием backend полностью платформенно
|
||||
подтверждённым. Многоцветная тема уже прошла отдельный MAME-gate P2.2.
|
||||
@@ -0,0 +1,98 @@
|
||||
# Sprinter Commander: результаты двухпанельного skeleton P2
|
||||
|
||||
Дата: 6 сентября 2026 года.
|
||||
|
||||
Статус: многоцветный skeleton `PASS` в MAME; файловая система и аппаратная
|
||||
проверка ожидаются.
|
||||
|
||||
Связанные документы:
|
||||
[архитектура](commander-architecture.md),
|
||||
[roadmap](commander-roadmap.md),
|
||||
[экранный backend](p2-screen-backend.md),
|
||||
[результаты P2.1](p2-screen-probe-results.md)
|
||||
|
||||
## 1. Реализованная граница
|
||||
|
||||
`build/sprcmd.exe` — первая запускаемая форма Commander, а не очередная
|
||||
платформенная проба. В ней уже разделены:
|
||||
|
||||
- `ScApp`, инициализация, главный цикл и идемпотентное состояние cleanup;
|
||||
- три EMM-страницы: левая панель, правая панель и screen model;
|
||||
- `sc_screen` — единственный владелец модели 80x32 `(char, attr)`;
|
||||
- `sc_video_system` — `WINREST` и прямоугольный `SCROLL` без BIOS-окон;
|
||||
- renderer двух панелей, status и строки функциональных клавиш;
|
||||
- нормализованные `ScKeyEvent` и `ScCommand`;
|
||||
- навигация по синтетическим P2-записям, включая системный scroll;
|
||||
- единая модальная поверхность для сообщений и подтверждения выхода.
|
||||
|
||||
Синтетические строки нужны только для проверки cursor/top и scroll. Они не
|
||||
являются подменой каталога: `ffirst/fnext` skeleton не вызывает, настоящий
|
||||
источник файлов добавляется в P3.
|
||||
|
||||
## 2. Сборка
|
||||
|
||||
Команда: `make`, конфигурация `--memory big --safe`.
|
||||
|
||||
| Область | Значение |
|
||||
|---|---:|
|
||||
| `_CODE` | 7 129 байт (`0x8100..0x9CD9`) |
|
||||
| данные | 443 байта (конец `0x9E94`) |
|
||||
| EXE | 7 675 байт |
|
||||
| heap после статики | 7 276 байт |
|
||||
| стек | 1 279 байт |
|
||||
|
||||
Банков кода skeleton пока не требует. Контракты уже рассчитаны на `big`, а
|
||||
холодные функции будут переноситься в W1 по мере появления P3/P4-кода.
|
||||
|
||||
## 3. MAME-сценарий
|
||||
|
||||
Окружение: MAME 0.287 (`b0c4527c`), машина `sprinter`, BIOS `v3.06`.
|
||||
Сценарий воспроизводится файлом `tests/mame_p2_skeleton.lua`.
|
||||
|
||||
| Действие | Наблюдение | Результат |
|
||||
|---|---|---|
|
||||
| Первый экран | две многоцветные панели 80x32, левая `[ACTIVE]`, cursor/top `0/0` | [PASS](../artifacts/mame-p2/skeleton-color/initial-left-active.png) |
|
||||
| `Tab` | `[ACTIVE]` и `>` перешли на правую панель | [PASS](../artifacts/mame-p2/skeleton-color/tab-right-active.png) |
|
||||
| `PgDn`, `Down` | правая панель показывает строки 002–028, cursor/top `28/2`; сосед и рамки неподвижны | [PASS](../artifacts/mame-p2/skeleton-color/navigation-scroll.png) |
|
||||
| `F10` | красный диалог подтверждения поверх модели | [PASS](../artifacts/mame-p2/skeleton-color/quit-dialog.png) |
|
||||
| `N` | обе панели и тема полностью восстановлены из EMM | [PASS](../artifacts/mame-p2/skeleton-color/quit-cancelled-restored.png) |
|
||||
| `F1`, `Esc` | общий message dialog открылся и восстановил экран | [PASS](../artifacts/mame-p2/skeleton-color/reserved-command-dialog.png), [после закрытия](../artifacts/mame-p2/skeleton-color/message-closed-restored.png) |
|
||||
| повторный `F10`, `Enter` | палитра восстановлена, штатный выход в DSS | [PASS](../artifacts/mame-p2/skeleton-color/clean-exit-dos.png) |
|
||||
| второй запуск после выхода | приложение снова инициализировало EMM и тему | [PASS](../artifacts/mame-p2/skeleton-color/repeat-second-launch.png) |
|
||||
| второй cleanup | возврат в DSS без сообщения об утечке EMM | [PASS](../artifacts/mame-p2/skeleton-color/repeat-second-clean-exit.png) |
|
||||
|
||||
## 4. Проверка ресурсов
|
||||
|
||||
В начале `sc_app_init()` сохраняет `mem_info.total/free`, затем выделяет один
|
||||
блок из трёх страниц. `sc_app_cleanup()` освобождает блок, повторяет
|
||||
`mem_info()` и возвращает ошибку, если оба значения не восстановились. При
|
||||
ошибке основной модуль печатает отдельное сообщение и возвращает код 4.
|
||||
|
||||
В MAME этого сообщения не было ни после первого, ни после второго процесса.
|
||||
Второй процесс успешно выделил тот же объём и полностью отрисовался. Таким
|
||||
образом, проверка включает и внутреннее сравнение счётчиков, и повторный
|
||||
запуск с чистого состояния.
|
||||
|
||||
## 5. Экран и палитра
|
||||
|
||||
Skeleton не открывает BIOS window descriptors и не обращается к VRAM
|
||||
напрямую. Панели и диалоги — прямоугольники в собственной EMM-модели.
|
||||
|
||||
Системные BIOS/ESTEX-вызовы палитры **не запрещены**. Первый зафиксированный
|
||||
skeleton намеренно отображал все semantic attributes системным `0x30`, чтобы
|
||||
изолировать проверку приложения. После успешной отдельной пробы P2.2 добавлен
|
||||
`sc_theme`: разные смысловые атрибуты, save/set всех четырёх планов и restore
|
||||
при cleanup. Renderer и формат экранной модели при этом не изменились.
|
||||
|
||||
Интеграционный MAME-сценарий прошёл с белыми/жёлтыми/светло-голубыми
|
||||
элементами на синем фоне, чёрным на cyan/lightgray и белыми красными
|
||||
диалогами. Второй запуск подтвердил повторную инициализацию темы после её
|
||||
восстановления первым процессом.
|
||||
|
||||
## 6. Следующие работы
|
||||
|
||||
- повторить палитровую пробу P2.2 на реальном Sprinter;
|
||||
- повторная инициализация `sc_video` после child, сменившего видеорежим;
|
||||
- измерение full/row/scroll present;
|
||||
- аппаратный прогон P2.1 и skeleton;
|
||||
- P3: заменить синтетические строки настоящими независимыми каталогами.
|
||||
@@ -0,0 +1,146 @@
|
||||
# Sprinter Commander: результаты файловых панелей P3
|
||||
|
||||
Дата: 7 сентября 2026 года.
|
||||
|
||||
Статус: `PASS` в MAME для навигации, независимых панелей, EMM-store на
|
||||
границе 640/641, сортировки и явной обработки предела каталога DSS 1.71.
|
||||
|
||||
Связанные документы:
|
||||
[требования](commander-requirements.md),
|
||||
[архитектура](commander-architecture.md),
|
||||
[roadmap](commander-roadmap.md),
|
||||
[большие каталоги DSS](dss-large-directories.md)
|
||||
|
||||
## 1. Проверенная граница
|
||||
|
||||
В P3 синтетические записи заменены настоящим источником `F_FIRST/F_NEXT`.
|
||||
Каждая панель имеет собственные путь, cursor/top и физическую EMM-страницу.
|
||||
Запись панели занимает 24 байта, поэтому одна 16-КБ страница вмещает
|
||||
жёсткий продуктовый предел 640 записей и оставляет 1024 байта запаса.
|
||||
|
||||
Реализованы и проверены:
|
||||
|
||||
- шаблон перечисления `*.*` и имена DOS 8.3;
|
||||
- синтетическая `..` в некорневом каталоге без дублей `.`/`..` от DSS;
|
||||
- каталоги перед файлами и сортировка имён без учёта регистра ASCII;
|
||||
- вход в каталог, возврат через `..`/`Ctrl+PgUp`, refresh и независимость
|
||||
пассивной панели;
|
||||
- навигация `Up/Down/PgUp/PgDn/Home/End` без повторного scan;
|
||||
- частичный результат при ошибке DSS 35 и отдельная виртуальная запись
|
||||
`>>> MORE...`.
|
||||
|
||||
## 2. Сборка
|
||||
|
||||
Основная команда: `make`.
|
||||
|
||||
Конфигурация: `--memory big --safe --max-allocs 3000`.
|
||||
|
||||
| Область | Значение |
|
||||
|---|---:|
|
||||
| `_CODE` | 7728 байт (`0x8100..0x9F30`) |
|
||||
| данные | 2281 байт (конец `0xA819`) |
|
||||
| свободная куча W2 | 4839 байт |
|
||||
| стек | 1279 байт |
|
||||
| `_BANK1` — scan/sort | 2299 / 16384 байт (14,0%) |
|
||||
| `_BANK2` — renderer | 2902 / 16384 байт (17,7%) |
|
||||
| EXE | 41305 байт |
|
||||
|
||||
`check_bank_calls.py` не нашёл прямых вызовов в чужой банк или публикации
|
||||
указателей на данные банка.
|
||||
|
||||
## 3. Прямая диагностика DSS
|
||||
|
||||
Образ содержит `BIGDIR` из 680 обычных файлов. Исправленная проба `P3DIR`
|
||||
получила:
|
||||
|
||||
```text
|
||||
count=512
|
||||
errno=35
|
||||
511=B0000598.DAT
|
||||
512=B0000599.DAT
|
||||
513=
|
||||
```
|
||||
|
||||
Это подтверждает границу стабильного DSS и отсутствие продолжения за ней.
|
||||
Конкретные последние имена отражают физический порядок записей, созданный
|
||||
`mcopy`, и сами по себе не являются контрактом теста.
|
||||
|
||||
Артефакт: [экран P3DIR](../artifacts/mame-p3/panels/dss-enum-limit.png).
|
||||
|
||||
## 4. Диагностика общего пути Commander
|
||||
|
||||
Проба `P3SCAN` использует те же `sc_source_fs`, `sc_store` и `sc_sort`, что
|
||||
основная программа. Получен результат:
|
||||
|
||||
```text
|
||||
scan: rc=0 errno=35 count=512 trunc=1
|
||||
first: .. attr=10 flags=01
|
||||
last : >>> MORE... attr=00 flags=04
|
||||
```
|
||||
|
||||
Ошибка 35 сознательно остаётся в `errno` как причина усечения, но scan
|
||||
возвращает успешный частичный снимок. Красный маркер занимает последний
|
||||
элемент снимка и сортируется после файлов.
|
||||
|
||||
Артефакт: [экран P3SCAN](../artifacts/mame-p3/panels/scan-marker.png).
|
||||
|
||||
## 5. Интеграционный сценарий Commander
|
||||
|
||||
Исторические артефакты ниже сняты в MAME 0.287 с FDD `A:`. Текущий
|
||||
регрессионный сценарий предельного каталога переведён на HDD `D:`:
|
||||
`make hdd-stress` создаёт `build/hdd/p3_stress.chd`, а
|
||||
`tests/mame_p3_stress.lua` сначала переключает DSS на `D:`. FDD-сборщик
|
||||
`tests/make_p3_stress_floppy.sh` оставлен только для smoke-совместимости.
|
||||
|
||||
| Действие | Подтверждённое наблюдение | Результат |
|
||||
|---|---|---|
|
||||
| Корень | обе панели независимо открыли один реальный каталог | [PASS](../artifacts/mame-p3/panels/root-both-panels.png) |
|
||||
| Вход и `..` | левая панель вошла в `TESTDIR` и вернулась в корень | [PASS](../artifacts/mame-p3/panels/left-testdir-parent.png), [возврат](../artifacts/mame-p3/panels/enter-parent-return.png) |
|
||||
| `Ctrl+R` | после refresh сохранён выбор `INNER.TXT` | [PASS](../artifacts/mame-p3/panels/refresh-preserves-inner.png) |
|
||||
| `Tab` и навигация справа | пути и cursor/top панелей не смешиваются | [PASS](../artifacts/mame-p3/panels/independent-panel-state.png) |
|
||||
| `Ctrl+PgUp` слева | изменена только активная левая панель | [PASS](../artifacts/mame-p3/panels/ctrl-pgup-left-only.png) |
|
||||
| Вход в `EMPTY` | единственная запись — синтетическая `..`, count=1 | [PASS](../artifacts/mame-p3/panels/empty-parent.png) |
|
||||
| Вход в `MANY`, `PgDn`, `Down` | cursor/top меняются до 28/2 без rescan, рамки и соседняя панель сохраняются | [PASS](../artifacts/mame-p3/panels/navigation-scroll.png) |
|
||||
| Вход в `BIGDIR` | показан частичный снимок, count=512 и признак `entries+` | [PASS](../artifacts/mame-p3/panels/bigdir-partial.png) |
|
||||
| `End` | выбран последний красный `>>> MORE...`, info показывает `<DSS LIMIT>` | [PASS](../artifacts/mame-p3/panels/dss-marker-selected.png) |
|
||||
| `Enter` на маркере | выведено сообщение о недоступном остатке DSS; переход или EXEC не выполнен | [PASS](../artifacts/mame-p3/panels/dss-marker-message.png) |
|
||||
| `F10` | открыт диалог выхода поверх текущего состояния | [PASS](../artifacts/mame-p3/panels/dss-limit-quit-dialog.png) |
|
||||
| `Enter` в диалоге | выполнен cleanup и возврат в `A:\>` | [PASS](../artifacts/mame-p3/panels/dss-limit-clean-exit.png) |
|
||||
|
||||
Скриншот фиксирует только состояние изображения в конкретный момент и не
|
||||
используется в одиночку для вывода о причине возможного дефекта. Подозрение
|
||||
на ошибку должно дополнительно подтверждаться соседними кадрами, ожидаемым
|
||||
состоянием сценария и, при необходимости, отдельной диагностической пробой.
|
||||
|
||||
## 6. Синтетическая граница EMM-store
|
||||
|
||||
Проба `tests/p3_store_probe/p3store.c` не вызывает DSS и поэтому достигает
|
||||
продуктового предела, который нельзя получить из реального каталога DSS
|
||||
1.71. Сборка выполнена с `--max-allocs 3000`.
|
||||
|
||||
```text
|
||||
entry=24 capacity=640 bytes=15360 reserve=1024
|
||||
write: count=640 first=1 middle=1 last=1
|
||||
guard: put640=1 get640=1 tail=1
|
||||
cleanup: free_after=222 restored=1
|
||||
PASS: store boundary is intact.
|
||||
```
|
||||
|
||||
Проверены записи 0, 319 и 639. Запись и чтение индекса 640 отклонены,
|
||||
байты в начале и конце 1024-байтового резерва не изменились, EMM-страница
|
||||
освобождена без утечки.
|
||||
|
||||
Артефакт:
|
||||
[граница 640/641](../artifacts/mame-p3/panels/store-boundary-640-641.png).
|
||||
|
||||
## 7. Итог и следующая граница
|
||||
|
||||
Функциональный gate файловых панелей P3 закрыт в MAME. Аппаратный прогон на
|
||||
реальном Sprinter ещё нужен, но не блокирует начало P4.
|
||||
|
||||
Следующая работа PoC:
|
||||
|
||||
1. копирование одного файла через отдельную EMM-страницу в W3;
|
||||
2. безопасный временный файл, обработка ошибок и cleanup;
|
||||
3. запуск `.EXE` через ESTEX с восстановлением экрана после child;
|
||||
4. регрессионный MAME-сценарий для копирования, файлов больше 64 КБ и EXEC.
|
||||
@@ -0,0 +1,98 @@
|
||||
# Sprinter Commander: результаты копирования P4.1
|
||||
|
||||
Дата: 7 сентября 2026 года.
|
||||
|
||||
Статус: `PASS` в MAME 0.288 / BIOS 3.06 / DSS 1.71.57 на HDD `D:` для
|
||||
однофайлового F5, прогресса, отмены, запрета overwrite и нулевого файла.
|
||||
|
||||
Связанные документы:
|
||||
[требования](commander-requirements.md),
|
||||
[архитектура](commander-architecture.md),
|
||||
[roadmap](commander-roadmap.md),
|
||||
[носители тестов](test-media.md).
|
||||
|
||||
## Реализация
|
||||
|
||||
- F5 берёт обычный файл активной панели и каталог пассивной панели;
|
||||
- каталоги и виртуальный маркер DSS явно отклоняются;
|
||||
- source и уникальный temp открыты одновременно — не более двух fd;
|
||||
- блок 4096 байт находится только в четвёртой EMM-странице `ScApp` и
|
||||
временно отображается в W3 функциями `bank_read_page()` и
|
||||
`bank_write_page()`;
|
||||
- счётчики размера/позиции 32-битные;
|
||||
- temp создаётся через `O_CREAT|O_EXCL`, на ошибке или Esc закрывается и
|
||||
удаляется;
|
||||
- перед commit конечное имя проверяется повторно;
|
||||
- commit вызывает DSS `RENAME` двумя basename в каталоге пассивной панели и
|
||||
восстанавливает глобальный CWD;
|
||||
- после успеха пассивная панель перечитывается.
|
||||
|
||||
UI-оркестрация и форматирование прогресса вынесены из резидентного W2 в
|
||||
`sc_copy_ui.c` (BANK2). Полноразмерного статического буфера нет ни в W2, ни в
|
||||
банке кода.
|
||||
|
||||
## Найденная особенность DSS RENAME
|
||||
|
||||
Первый HDD-прогон перенёс все 131109 байт во временный файл, но завершился
|
||||
`errno 16` (`EINAME`), а валидатор CHD подтвердил отсутствие результата.
|
||||
Причина установлена не по одиночному кадру, а по сочетанию:
|
||||
|
||||
1. позднего прогресса `122880/131109`;
|
||||
2. отдельного итогового кадра с `errno 16`;
|
||||
3. извлечённого состояния HDD;
|
||||
4. исходника `DSS/API/Rename.asm`: операция переименовывает записи текущего
|
||||
каталога, а полный новый путь не является допустимым новым именем.
|
||||
|
||||
Исправление ограничено commit-фазой: job сохраняет CWD, временно входит в
|
||||
target-dir, передаёт два коротких имени и восстанавливает CWD на всех обычных
|
||||
путях возврата.
|
||||
|
||||
## Матрица MAME
|
||||
|
||||
Образ создаёт `tests/make_p4_copy_hdd.sh`, сценарий выполняет
|
||||
`tests/mame_p4_copy_hdd.lua`, итог проверяет `tests/check_p4_copy_hdd.sh`.
|
||||
|
||||
| Сценарий | Результат |
|
||||
|---|---|
|
||||
| `LARGE.BIN`, 131109 байт | скопирован, passive refresh показывает файл; содержимое побайтно совпало |
|
||||
| `EXIST.TXT` | F5 отказал до изменения существующего целевого файла |
|
||||
| `CANCEL.BIN`, 4194341 байт | виден промежуточный прогресс; Esc удалил temp и не создал target |
|
||||
| `EMPTY.ZRO`, 0 байт | создан корректный пустой файл и показан passive refresh |
|
||||
| cleanup/exit | временных `~SC*.TMP` нет; возврат в `D:\>` |
|
||||
|
||||
Артефакты:
|
||||
|
||||
- [успешный файл больше 64 КБ](../artifacts/mame-p4/copy/hdd-copy-success.png);
|
||||
- [отказ overwrite](../artifacts/mame-p4/copy/hdd-existing-refused.png);
|
||||
- [прогресс до Esc](../artifacts/mame-p4/copy/hdd-cancel-progress.png);
|
||||
- [состояние после отмены](../artifacts/mame-p4/copy/hdd-cancel-cleanup.png);
|
||||
- [успешный нулевой файл](../artifacts/mame-p4/copy/hdd-zero-copy-success.png);
|
||||
- [чистый выход на HDD](../artifacts/mame-p4/copy/hdd-clean-exit.png);
|
||||
- [низкоуровневый read/write EMM-page probe](../artifacts/mame-p4/copy/bank-page-io.png).
|
||||
|
||||
Финальный файловый валидатор сообщил:
|
||||
|
||||
```text
|
||||
PASS: LARGE/EMPTY скопированы; EXIST сохранён; cancel/temp отсутствуют.
|
||||
```
|
||||
|
||||
## Размер сборки
|
||||
|
||||
Конфигурация: `--memory big --safe --max-allocs 3000`.
|
||||
|
||||
| Область | Размер |
|
||||
|---|---:|
|
||||
| `_CODE` W2 | 8795 байт |
|
||||
| данные W2 | 3161 байт |
|
||||
| свободная куча W2 | 2892 байта |
|
||||
| стек | 1279 байт |
|
||||
| BANK1: scan/sort/copy job | 4327 / 16384 байт |
|
||||
| BANK2: draw/copy UI | 4411 / 16384 байт |
|
||||
| `SPRCMD.EXE` | 42372 байта |
|
||||
|
||||
`check_bank_calls.py` — чисто. Общий `make size-check` — 67 программ без
|
||||
роста; отдельно отмечается уже существующий новый тест `hello3`, не связанный
|
||||
с Commander.
|
||||
|
||||
P4.1 закрыт для требований PoC `REQ-COPY-01..12`; запуск `.EXE` и вся
|
||||
стабилизация PoC продолжены в отчётах P4.2 и P5.
|
||||
@@ -0,0 +1,100 @@
|
||||
# Sprinter Commander: результаты запуска EXE P4.2
|
||||
|
||||
Дата: 8 сентября 2026 года.
|
||||
|
||||
Статус: `PASS` в MAME 0.288 / BIOS 3.06 / DSS 1.71.57 на HDD `D:`.
|
||||
Выполнены требования `REQ-EXEC-01..06`: запуск по `Enter`, возврат в
|
||||
Commander после изменившей окружение программы, обработка плохого EXE и отказ
|
||||
для неподдерживаемого типа файла.
|
||||
|
||||
Связанные документы:
|
||||
[требования](commander-requirements.md),
|
||||
[архитектура](commander-architecture.md),
|
||||
[roadmap](commander-roadmap.md),
|
||||
[носители тестов](test-media.md).
|
||||
|
||||
## Реализация
|
||||
|
||||
- `Enter` запускает только обычную запись с расширением `.EXE` без учёта
|
||||
регистра; каталог, маркер предела DSS и иной тип файла не передаются EXEC;
|
||||
- полный путь строится в резидентной памяти, ESTEX `EXEC $40` вызывается с
|
||||
`B=1` и синхронно возвращает код завершения в диапазоне `00..FF`;
|
||||
- перед запуском сохраняются текущий диск и 256-байтный CWD;
|
||||
- child получает каталог активной панели как CWD, что позволяет программам
|
||||
без аргументов открывать соседние файлы; затем CWD Commander возвращается;
|
||||
- одновременный copy job запрещён; пути временно используют уже существующие
|
||||
строковые поля `ScCopyJob`, поэтому EXEC не добавил два новых буфера в W2;
|
||||
- после child восстанавливаются диск/CWD, режим 80x32 и восемь записей
|
||||
палитры Commander во всех четырёх planes;
|
||||
- повторная установка темы не перезаписывает снимок исходной системной
|
||||
палитры, который нужен штатному cleanup;
|
||||
- обе панели перечитываются, затем экран полностью строится из EMM-модели;
|
||||
- успешный статус показывает код завершения, ошибка загрузчика — имя и
|
||||
`errno`; Commander остаётся управляемым.
|
||||
|
||||
Низкоуровневая обёртка находится в `sc_platform_exec.c`, а холодная
|
||||
оркестрация и форматирование статуса — в `sc_exec_ui.c` (BANK2).
|
||||
|
||||
## Интеграционный child
|
||||
|
||||
`tests/p4_exec_child/p4child.exe` намеренно создаёт неблагоприятное для
|
||||
родителя состояние:
|
||||
|
||||
1. создаёт `EXECLEFT/CREATED.TXT` и `EXECRGHT/PASSIVE.TXT`;
|
||||
2. оставляет CWD в `EXECLEFT`;
|
||||
3. переключает экран в 40x32;
|
||||
4. заменяет используемые Commander цвета ярко-зелёной/пурпурной палитрой;
|
||||
5. ждёт `Enter` и возвращает код `5A`.
|
||||
|
||||
Имена каталогов теста строго соответствуют 8.3. Промежуточный вариант
|
||||
`EXECRIGHT` оказался VFAT long name и был виден DSS как `EXECRI~1`; отказ
|
||||
`chdir("EXECRIGHT")` был ошибкой fixture, а не доказательством дефекта DSS.
|
||||
|
||||
## Матрица MAME
|
||||
|
||||
Образ создаёт `tests/make_p4_exec_hdd.sh`, сценарий выполняет
|
||||
`tests/mame_p4_exec_hdd.lua`, итоговое содержимое проверяет
|
||||
`tests/check_p4_exec_hdd.sh`.
|
||||
|
||||
| Сценарий | Результат |
|
||||
|---|---|
|
||||
| `P4CHILD.EXE` | child показал `active=0 passive=0` и CWD `\\EXECLEFT` |
|
||||
| возврат из child | Commander снова в 80x32 с исходной темой; статус содержит `5A` |
|
||||
| refresh активной панели | появился `CREATED.TXT`, размер 19 |
|
||||
| refresh пассивной панели | появился `PASSIVE.TXT`, размер 27 |
|
||||
| `BROKEN.EXE` | показано `Cannot execute BROKEN.EXE (errno 17)`; приложение продолжило работу |
|
||||
| `NOTE.TXT` | показано имя объекта и явный отказ: поддерживаются только `.EXE` |
|
||||
| cleanup/exit | диалог работает; системная палитра восстановлена; возврат в `D:\>` |
|
||||
|
||||
После остановки MAME CHD был извлечён и оба marker-файла сравнены побайтно:
|
||||
|
||||
```text
|
||||
PASS: child создал active/passive markers с ожидаемым содержимым.
|
||||
```
|
||||
|
||||
Артефакты:
|
||||
|
||||
- [child после изменения режима, палитры и CWD](../artifacts/mame-p4/exec/child-mode-palette-cwd.png);
|
||||
- [Commander после восстановления и refresh обеих панелей](../artifacts/mame-p4/exec/commander-restored-both-panels.png);
|
||||
- [ошибка повреждённого EXE](../artifacts/mame-p4/exec/broken-exe-error.png);
|
||||
- [отказ для обычного TXT](../artifacts/mame-p4/exec/unsupported-file.png);
|
||||
- [диалог выхода после EXEC](../artifacts/mame-p4/exec/quit-after-exec.png);
|
||||
- [чистый возврат в DSS](../artifacts/mame-p4/exec/clean-dss-return.png).
|
||||
|
||||
## Размер сборки
|
||||
|
||||
Конфигурация: `--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 байт |
|
||||
|
||||
`check_bank_calls.py` — чисто. Таблица актуализирована по финальной сборке P5
|
||||
после исправления режима `O_RDONLY` в libc; P4.1 и P4.2 вместе закрывают
|
||||
критерий выхода P4.
|
||||
@@ -0,0 +1,215 @@
|
||||
# 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 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.
|
||||
@@ -0,0 +1,98 @@
|
||||
# Sprinter Commander: сортировки 0.2/P6
|
||||
|
||||
Дата: 8 сентября 2026 года.
|
||||
|
||||
Статус: `PASS` в MAME 0.288 / BIOS 3.06 / DSS 1.71.57 на HDD `D:`.
|
||||
|
||||
## Реализация
|
||||
|
||||
Каждая `ScPanel` хранит собственные `sort_key` и `sort_descending`. Доступны
|
||||
четыре ключа:
|
||||
|
||||
| Комбинация | Ключ |
|
||||
|---|---|
|
||||
| `Ctrl+F3` | имя |
|
||||
| `Ctrl+F4` | расширение |
|
||||
| `Ctrl+F5` | дата и время last-write |
|
||||
| `Ctrl+F6` | размер |
|
||||
|
||||
Выбор другого ключа включает прямой порядок; повтор текущей комбинации меняет
|
||||
направление. На нижней рамке каждой панели постоянно виден режим `N/E/D/S` и
|
||||
знак `+`/`-`. Refresh и переходы в каталог используют сохранённый режим этой
|
||||
же панели.
|
||||
|
||||
Порядок групп не зависит от reverse: синтетический `..` всегда первый,
|
||||
затем идут каталоги, затем обычные файлы, а красный маркер ограничения DSS
|
||||
остаётся последним. Направление применяется только внутри групп. При равном
|
||||
основном ключе детерминированным вторичным ключом служит имя без учёта регистра.
|
||||
Дата сравнивается как FAT date, затем FAT time; отдельная creation date не
|
||||
имитируется, потому что текущий `F_FIRST/F_NEXT` возвращает last-write.
|
||||
|
||||
Shell sort по-прежнему работает непосредственно с 24-байтовыми записями EMM
|
||||
через `sc_store_get/put` и не сохраняет указатели W3. Выбранная запись после
|
||||
пересортировки находится по имени; cursor/top нормализуются без повторного
|
||||
сканирования каталога.
|
||||
|
||||
## Автоматическая проверка
|
||||
|
||||
`SORTRT.EXE` заполняет две разные EMM-страницы намеренно перемешанными
|
||||
записями и пишет `SORTRES.TXT`. Посмертный валидатор подтвердил:
|
||||
|
||||
```text
|
||||
PASS: 4 sort keys, reverse, fixed groups, selection, two panels.
|
||||
```
|
||||
|
||||
Проверяются восемь точных порядков, неизменность `..` и `>>> MORE...`,
|
||||
сохранение `BETA.TXT` под курсором и отсутствие влияния режима первой панели
|
||||
на вторую.
|
||||
|
||||
Затем тот же MAME-сценарий запускает Commander, открывает `SORTL` и `SORTR`,
|
||||
подаёт реальные `Ctrl+F3..F6`, переключает активную панель и делает refresh.
|
||||
Два полных UI-прогона дали побайтно одинаковые контрольные кадры.
|
||||
|
||||
- [name ascending](../artifacts/mame-p6/sort/name-ascending.png);
|
||||
- [extension ascending](../artifacts/mame-p6/sort/extension-ascending.png);
|
||||
- [extension descending](../artifacts/mame-p6/sort/extension-descending.png);
|
||||
- [size ascending](../artifacts/mame-p6/sort/size-ascending.png);
|
||||
- [size descending](../artifacts/mame-p6/sort/size-descending.png);
|
||||
- [date ascending](../artifacts/mame-p6/sort/date-ascending.png);
|
||||
- [date descending](../artifacts/mame-p6/sort/date-descending.png);
|
||||
- [left `S-`, right `E+`](../artifacts/mame-p6/sort/independent-panels.png);
|
||||
- [refresh сохранил `E+`](../artifacts/mame-p6/sort/refresh-keeps-mode.png);
|
||||
- [штатный cleanup](../artifacts/mame-p6/sort/clean-dss-return.png).
|
||||
|
||||
В текущем общем HDD-builder файлы fixture получают одинаковую дату при
|
||||
копировании дерева, поэтому точный порядок date проверяет target-проба;
|
||||
UI-сценарий независимо подтверждает доставку `Ctrl+F5`, оба состояния `D+/-`
|
||||
и обратный redraw. Этот предел fixture не выдаётся за проверку разных FAT-дат
|
||||
по скриншоту.
|
||||
|
||||
## Размер
|
||||
|
||||
Конфигурация `--memory big --safe --max-allocs 3000`:
|
||||
|
||||
| Область | P5 | После сортировок | Изменение |
|
||||
|---|---:|---:|---:|
|
||||
| `_CODE` W2 | 8870 | 9125 | +255 |
|
||||
| данные W2 | 3240 | 3244 | +4 |
|
||||
| свободная куча W2 | 2738 | 2479 | -259 |
|
||||
| BANK1 | 4242 | 5137 | +895 |
|
||||
| BANK2 | 5973 | 6053 | +80 |
|
||||
| `SPRCMD.EXE` | 42447 | 42702 | +255 |
|
||||
|
||||
Рост BANK1 содержит компараторы и сохранение выбора, W2 — команды и один
|
||||
банковый вызов. Первоначальный вариант с восемью статусными строками давал
|
||||
рост W2 на 736 байт и был сокращён до общего статуса плюс компактный индикатор
|
||||
на рамке. Межбанковый аудит финальной сборки чистый.
|
||||
|
||||
## Воспроизведение
|
||||
|
||||
```sh
|
||||
make hdd-p6-sort ALLOCS=3000
|
||||
tests/run_mame_hdd.sh build/hdd/p6_sort.chd \
|
||||
tests/mame_p6_sort.lua artifacts/mame-p6/local 90
|
||||
tests/check_p6_sort_hdd.sh build/hdd/p6_sort.chd
|
||||
```
|
||||
|
||||
Перед запуском требуется убедиться, что другого MAME Sprinter нет;
|
||||
`run_mame_hdd.sh` выполняет эту проверку.
|
||||
@@ -0,0 +1,73 @@
|
||||
# Sprinter Commander: выбор записей 0.2/P7
|
||||
|
||||
Дата: 8 сентября 2026 года.
|
||||
|
||||
Статус: `PASS` в MAME 0.288 / BIOS 3.06 / DSS 1.71.57 на HDD `D:`.
|
||||
|
||||
## Контракт
|
||||
|
||||
- `Insert` меняет выбор текущей обычной записи и передвигает cursor вниз;
|
||||
- `*` инвертирует выбор только файлов текущей панели;
|
||||
- файлы и каталоги можно выбирать, синтетический `..` и виртуальный маркер
|
||||
DSS нельзя;
|
||||
- групповые `+`, `-` и `*` игнорируют каталоги и не снимают выбор,
|
||||
ранее поставленный с каталога через `Insert`;
|
||||
- выбранная запись отмечается `*` слева от имени;
|
||||
- footer активной панели показывает `<bytes> bytes in <count> selected`, а
|
||||
общая строка информации пока сохраняет компактное `sel размер/количество`;
|
||||
- размер каталога в этой статистике равен нулю: рекурсивный подсчёт появится
|
||||
вместе с групповой job;
|
||||
- сортировка переносит `SC_ENTRY_SELECTED` вместе с записью и сохраняет
|
||||
текущий файл под cursor;
|
||||
- refresh текущего среза явно очищает выбор; режим сортировки сохраняется;
|
||||
- панели имеют независимые EMM-страницы и независимую статистику.
|
||||
|
||||
Функции выбора находятся в BANK1 и работают только через
|
||||
`sc_store_get/put`; указатель на W3 не сохраняется. Для toggle и invert
|
||||
используется один банковый entry point, чтобы не плодить resident thunk’и.
|
||||
|
||||
## Проверка
|
||||
|
||||
Target-проба `SELRT.EXE` проверяет Insert двух файлов, точную сумму 30,
|
||||
отказы для `..`/DSS-маркера, две инверсии, выбор каталога, сброс статистики
|
||||
через `sc_store_clear()` и возврат EMM. Результат извлекается из CHD:
|
||||
|
||||
```text
|
||||
PASS: Insert/invert/masks, virtual guards, totals, refresh и EMM.
|
||||
```
|
||||
|
||||
UI-сценарий выбирает два каталога и `A.BIN`, меняет порядок на `N-`,
|
||||
инвертирует выбор и перечитывает каталог. Контрольные состояния:
|
||||
|
||||
- [виртуальная запись не выбирается](../artifacts/mame-p7/select/virtual-entry-guard.png);
|
||||
- [три записи, `sel 10/003`](../artifacts/mame-p7/select/insert-three.png);
|
||||
- [сортировка сохранила flags и cursor](../artifacts/mame-p7/select/sort-keeps-selection.png);
|
||||
- [инверсия файлов не изменила ранее выбранные каталоги, `sel 50/004`](../artifacts/mame-p7/select/inverted.png);
|
||||
- [refresh очистил выбор и сохранил `N-`](../artifacts/mame-p7/select/refresh-clears.png);
|
||||
- [штатный возврат в DSS](../artifacts/mame-p7/select/clean-dss-return.png).
|
||||
|
||||
## Размер
|
||||
|
||||
Конфигурация `--memory big --safe --max-allocs 3000`:
|
||||
|
||||
| Область | После P6 | После P7 | Изменение |
|
||||
|---|---:|---:|---:|
|
||||
| `_CODE` W2 | 9125 | 9434 | +309 |
|
||||
| данные W2 | 3244 | 3256 | +12 |
|
||||
| свободная куча W2 | 2479 | 2158 | -321 |
|
||||
| BANK1 | 5137 | 6033 | +896 |
|
||||
| BANK2 | 6053 | 6213 | +160 |
|
||||
| `SPRCMD.EXE` | 42702 | 43011 | +309 |
|
||||
|
||||
Первый вариант имел два банковых entry point и оставлял 2069 байт heap.
|
||||
После объединения и сокращения резидентных сообщений финальная сборка
|
||||
возвращает 2158 байт. Межбанковый аудит чистый.
|
||||
|
||||
## Воспроизведение
|
||||
|
||||
```sh
|
||||
make hdd-p7-select ALLOCS=3000
|
||||
tests/run_mame_hdd.sh build/hdd/p7_select.chd \
|
||||
tests/mame_p7_select.lua artifacts/mame-p7/local 65
|
||||
tests/check_p7_select_hdd.sh build/hdd/p7_select.chd
|
||||
```
|
||||
@@ -0,0 +1,69 @@
|
||||
# Sprinter Commander: выбор по маске 0.2/P8
|
||||
|
||||
Дата: 8 сентября 2026 года.
|
||||
|
||||
Статус: `PASS` в MAME 0.288 / BIOS 3.06 / DSS 1.71.57 на HDD `D:`.
|
||||
|
||||
## Реализация
|
||||
|
||||
Обычный `+` открывает `Select by mask`, обычный `-` — `Unselect by mask`.
|
||||
Начальное значение `*.*`; Enter применяет действие, Esc отменяет его без
|
||||
изменения панели. Доступны wildcard `*` и `?`, сравнение ASCII-букв не зависит
|
||||
от регистра. Как в DOS, `*.*` означает все имена файлов, включая имена без точки.
|
||||
Все групповые операции `+`, `-`, `*` игнорируют каталоги; синтетический `..` и виртуальный
|
||||
маркер DSS также не участвуют. Выбор, поставленный на каталог отдельным `Insert`, не меняется.
|
||||
|
||||
Модальный редактор не использует консольные окна или прямой VRAM. Диалог и
|
||||
поле находятся в общей EMM-модели Commander и выводятся координатными
|
||||
функциями. Реализованы insertion в позиции cursor, Backspace, Delete,
|
||||
Left/Right, Home/End, Enter и Esc. Для текущих DSS 8.3-масок ввод ограничен
|
||||
12 символами; тот же editor будет переиспользован командами F7/F6 и
|
||||
командной строкой.
|
||||
|
||||
Существующий `sc_dialog.c` вместе с новым editor перенесён из HOME в BANK2.
|
||||
Mask matcher находится в BANK1 рядом с выбором; приложение передаёт ему
|
||||
резидентный scratch-буфер copy job, пока файловая job не активна.
|
||||
|
||||
## Проверка
|
||||
|
||||
Target-часть проверяет:
|
||||
|
||||
- `*.bin` в нижнем регистре против имён верхнего регистра;
|
||||
- `?.b?n`;
|
||||
- `*.*` для файла без точки и неизменность каталога;
|
||||
- последовательное set/clear с точными count/size;
|
||||
- неизменность `..` и DSS-маркера.
|
||||
|
||||
UI-сценарий редактирует `*.*` в `*.bin`, получает три выбранных файла и
|
||||
`sel 60/003`, затем снимает `b.bin`, получая `sel 40/002`. Следующий диалог
|
||||
закрывается Esc, состояние не меняется.
|
||||
|
||||
- [диалог с `*.*`](../artifacts/mame-p8/masks/default-dialog.png);
|
||||
- [поле после редактирования в `*.bin`](../artifacts/mame-p8/masks/edited-bin-mask.png);
|
||||
- [три BIN выбраны](../artifacts/mame-p8/masks/bin-selected.png);
|
||||
- [`b.bin` снят](../artifacts/mame-p8/masks/b-unselected.png);
|
||||
- [Esc сохранил выбор](../artifacts/mame-p8/masks/cancel-preserves.png);
|
||||
- [штатный cleanup](../artifacts/mame-p8/masks/clean-dss-return.png).
|
||||
|
||||
## Размер
|
||||
|
||||
| Область | После P7 | После P8 | Изменение |
|
||||
|---|---:|---:|---:|
|
||||
| `_CODE` W2 | 9434 | 9416 | -18 |
|
||||
| данные W2 | 3256 | 3256 | 0 |
|
||||
| свободная куча W2 | 2158 | 2176 | +18 |
|
||||
| BANK1 | 6033 | 6451 | +418 |
|
||||
| BANK2 | 6213 | 7268 | +1055 |
|
||||
| `SPRCMD.EXE` | 43011 | 42993 | -18 |
|
||||
|
||||
Несмотря на новую функцию, HOME стал меньше благодаря переносу диалогов.
|
||||
Оба банка имеют более 9 КБ резерва; межбанковый аудит чистый.
|
||||
|
||||
## Воспроизведение
|
||||
|
||||
```sh
|
||||
make hdd-p8-masks ALLOCS=3000
|
||||
tests/run_mame_hdd.sh build/hdd/p7_select.chd \
|
||||
tests/mame_p7_select.lua artifacts/mame-p8/local 80
|
||||
tests/check_p7_select_hdd.sh build/hdd/p7_select.chd
|
||||
```
|
||||
@@ -0,0 +1,71 @@
|
||||
# Sprinter Commander: создание каталога 0.2/P9
|
||||
|
||||
Дата: 8 сентября 2026 года.
|
||||
|
||||
Статус: `PASS` в MAME 0.288 / BIOS 3.06 / DSS 1.71.57 на HDD `D:`.
|
||||
|
||||
## Контракт
|
||||
|
||||
- `F7` открывает общий координатный редактор и принимает один компонент
|
||||
DOS 8.3 длиной до 12 символов;
|
||||
- разрешены база до 8 и расширение до 3 символов, одна необязательная точка,
|
||||
ASCII-буквы/цифры, допустимая DOS-пунктуация и CP866-байты `80h..FFh`;
|
||||
- пустое имя, `.`/`..`, разделители пути, пробелы, wildcard, лишние точки и
|
||||
превышение 8.3 отвергаются до вызова DSS;
|
||||
- полный путь строится в уже принадлежащем `ScApp` scratch-буфере;
|
||||
- `mkdir()` получает полный путь и не меняет глобальный CWD DSS;
|
||||
- после успеха перечитывается только активная панель;
|
||||
- существующий каталог и невалидное имя дают управляемое сообщение, `Esc`
|
||||
закрывает редактор без файловой операции.
|
||||
|
||||
Валидатор вынесен в отдельный банковый модуль `sc_name.c`. Сам Commander
|
||||
размещает редактор, оркестрацию F7 и валидатор в BANK2; HOME/W2 содержит
|
||||
только dispatch и банковые трамплины.
|
||||
|
||||
## Проверка
|
||||
|
||||
Target-проба `MKRT.EXE` проверяет допустимые и запрещённые имена, построение
|
||||
полного пути, реальный цикл `mkdir`/duplicate/`rmdir` и неизменность CWD.
|
||||
После MAME дерево извлекается из CHD: внешний checker подтверждает созданный
|
||||
`MKTEST/NEWDIR`, неизменный `KEEP.TXT` и отсутствие пробных каталогов.
|
||||
|
||||
```text
|
||||
PASS: validator, mkdir/error/rmdir/CWD и F7 NEWDIR.
|
||||
```
|
||||
|
||||
UI-сценарий отдельно подтверждает:
|
||||
|
||||
- [пустой редактор F7](../artifacts/mame-p9/mkdir/dialog-empty.png);
|
||||
- [создание и refresh активной панели](../artifacts/mame-p9/mkdir/created.png);
|
||||
- [управляемую ошибку повторного имени](../artifacts/mame-p9/mkdir/duplicate-error.png);
|
||||
- [отказ для имени длиннее 8.3](../artifacts/mame-p9/mkdir/invalid-name.png);
|
||||
- [вход в созданный каталог](../artifacts/mame-p9/mkdir/entered-newdir.png);
|
||||
- [штатный возврат в DSS](../artifacts/mame-p9/mkdir/clean-dss-return.png).
|
||||
|
||||
## Размер
|
||||
|
||||
Конфигурация `--memory big --safe --max-allocs 3000`:
|
||||
|
||||
| Область | После P8 | После P9 и уточнения group select | Изменение |
|
||||
|---|---:|---:|---:|
|
||||
| `_CODE` W2 | 9416 | 9449 | +33 |
|
||||
| данные W2 | 3256 | 3256 | 0 |
|
||||
| свободная куча W2 | 2176 | 2143 | -33 |
|
||||
| BANK1 | 6451 | 6477 | +26 |
|
||||
| BANK2 | 7268 | 8156 | +888 |
|
||||
| `SPRCMD.EXE` | 42993 | 43026 | +33 |
|
||||
|
||||
Оба банка сохраняют более 8 КБ резерва. Межбанковый аудит чист.
|
||||
|
||||
Общий `size-check` по всему C-Compiler пока ожидаемо отмечает посторонний
|
||||
новый `hello3` и намеренный рост `openenv` после постоянной проверки
|
||||
`O_RDONLY`; эталон размеров из Commander не обновлялся.
|
||||
|
||||
## Воспроизведение
|
||||
|
||||
```sh
|
||||
make hdd-p9-mkdir ALLOCS=3000
|
||||
tests/run_mame_hdd.sh build/hdd/p9_mkdir.chd \
|
||||
tests/mame_p9_mkdir.lua artifacts/mame-p9/local 70
|
||||
tests/check_p9_mkdir_hdd.sh build/hdd/p9_mkdir.chd
|
||||
```
|
||||
@@ -0,0 +1,85 @@
|
||||
# Sprinter Commander PoC 0.1
|
||||
|
||||
Дата: 8 сентября 2026 года.
|
||||
|
||||
Статус: техническая архитектура подтверждена в MAME 0.288 / BIOS 3.06 /
|
||||
DSS 1.71.57. `SPRCMD.EXE` является proof of concept, а не готовым файловым
|
||||
менеджером для повседневной работы. Аппаратный smoke-тест на Sprinter Sp2000
|
||||
ещё требуется.
|
||||
|
||||
## Что уже работает
|
||||
|
||||
- полноэкранный двухпанельный интерфейс 80x32 через безоконные координатные
|
||||
функции BIOS/ESTEX, без прямой записи в VRAM;
|
||||
- независимые пути и навигация обеих панелей, синтетический `..`, Home/End,
|
||||
PgUp/PgDn, Tab и refresh;
|
||||
- одна EMM-страница на панель, максимум 640 записей без заполнения страницы
|
||||
целиком, красный `>>> MORE...` при известном ограничении DSS;
|
||||
- каталоги перед файлами и сортировка по имени;
|
||||
- безопасное копирование одного файла по F5 через отдельную EMM-страницу W3,
|
||||
4-КБ блоками и temp/rename commit;
|
||||
- отмена по Esc между блоками и в отдельной точке перед commit;
|
||||
- запрет перезаписи уже существующего target;
|
||||
- запуск `.EXE` по Enter с временным CWD активной панели и восстановлением
|
||||
экрана, палитры, CWD и панелей;
|
||||
- штатное освобождение EMM, закрытие fd и восстановление системного экрана
|
||||
при выходе.
|
||||
|
||||
## Сборка и запуск
|
||||
|
||||
Из каталога приложения:
|
||||
|
||||
```sh
|
||||
make ALLOCS=3000
|
||||
make hdd
|
||||
```
|
||||
|
||||
Результат — `build/sprcmd.exe`; образ для быстрого интеграционного запуска —
|
||||
`build/hdd/sprcmd.chd`. Для MAME штатно используются системный HDD как `C:` и
|
||||
отдельный тестовый `hard2` как `D:`. Одновременно разрешён только один процесс
|
||||
MAME Sprinter.
|
||||
|
||||
Полный перечень автоматизированных образов и команд находится в
|
||||
[test-media.md](test-media.md), доказательства P5 — в
|
||||
[p5-stability-results.md](p5-stability-results.md).
|
||||
|
||||
Финальная конфигурация PoC: `--memory big --safe --max-allocs 3000`;
|
||||
`SPRCMD.EXE` — 42447 байт, `_CODE` W2 — 8870 байт, свободная куча W2 —
|
||||
2738 байт, BANK1 — 4242 байта, BANK2 — 5973 байта.
|
||||
|
||||
## Клавиши PoC
|
||||
|
||||
| Клавиша | Действие |
|
||||
|---|---|
|
||||
| стрелки, Home/End, PgUp/PgDn | перемещение по активной панели |
|
||||
| `Tab` | сменить активную панель |
|
||||
| `Enter` | войти в каталог/`..` или запустить `.EXE` |
|
||||
| `Ctrl+R` | перечитать активную панель |
|
||||
| `F5` | скопировать текущий обычный файл в пассивную панель |
|
||||
| `Esc` | отменить активное копирование |
|
||||
| `F10` | подтверждение выхода |
|
||||
|
||||
Надписи F6/F7/F8 и меню пока являются зарезервированными командами следующих
|
||||
этапов, а не обещанием выполненной операции.
|
||||
|
||||
## Известные ограничения
|
||||
|
||||
- в панели показывается не более 640 записей; многостраничный режим относится
|
||||
только к 2.0+;
|
||||
- стабильный DSS 1.71 может остановить физическое перечисление около 512-й
|
||||
FAT-записи с ошибкой 35 — показывается информирующий красный маркер;
|
||||
- сортировка PoC только по имени; ext/size/date и обратный порядок входят в
|
||||
следующую функциональную фазу;
|
||||
- нет выбора нескольких файлов, масок, очереди и рекурсивных операций;
|
||||
- F6 move/rename, F7 mkdir, F8 delete и overwrite policy ещё не реализованы;
|
||||
- нет командной строки, `Ctrl+O`, истории, associations и передачи аргументов
|
||||
внешней программе;
|
||||
- нет конфигурационного файла цветов, viewer/editor вызываются только как
|
||||
обычные внешние `.EXE`;
|
||||
- нет длинных имён: DSS-контракт текущего слоя — 8.3;
|
||||
- floppy поддерживается лишь как медленный smoke-носитель; основная матрица
|
||||
выполнялась на HDD `D:`;
|
||||
- проверка на реальном Sprinter пока не выполнена.
|
||||
|
||||
Эти ограничения намеренно отделяют доказанную платформенную основу PoC от
|
||||
безопасного файлового ядра 0.2 и полного набора функций версии 1.0.
|
||||
@@ -0,0 +1,90 @@
|
||||
# Носители для тестов Commander
|
||||
|
||||
Дата: 8 сентября 2026 года.
|
||||
|
||||
## Решение
|
||||
|
||||
Стандартный носитель интеграционных и нагрузочных тестов Commander —
|
||||
отдельный FAT16 HDD-образ, подключаемый к MAME как `hard2`. DSS видит его как
|
||||
диск `D:`; системный `hard1` остаётся диском `C:`.
|
||||
|
||||
MAME качественно эмулирует механические задержки FDD. Это полезно для проверки
|
||||
совместимости, но делает повторяющиеся scan/copy-прогоны на порядок медленнее и
|
||||
не даёт пользы большинству функциональных тестов. Поэтому FDD `A:` сохраняется
|
||||
только для отдельных smoke-тестов чтения/запуска с дискеты.
|
||||
|
||||
## Образы
|
||||
|
||||
Все рабочие образы Commander лежат локально в `build/hdd/` и не подменяют
|
||||
общий `mame/v306/IMG/test_hdd.chd`:
|
||||
|
||||
```sh
|
||||
make hdd # только SPRCMD.EXE -> build/hdd/sprcmd.chd
|
||||
make hdd-stress # P3: EMPTY/MANY/BIGDIR -> build/hdd/p3_stress.chd
|
||||
make hdd-copy # P4: COPYFROM/COPYTO -> build/hdd/p4_copy.chd
|
||||
make hdd-exec # P4: EXECLEFT/EXECRGHT -> build/hdd/p4_exec.chd
|
||||
make hdd-p5 # P5: 20 copy + resource cycles -> p5_stability.chd
|
||||
make hdd-p5-enospc # P5: почти полный HDD -> p5_enospc.chd
|
||||
make hdd-p5-panels # P5: 27/28 записей -> p5_panel_edges.chd
|
||||
make hdd-p5-copy-faults # P5: copy/EXEC fault matrix -> p5_copy_faults.chd
|
||||
make hdd-p5-viewer # P5: SPRCMD + реальный mdview2 -> p5_viewer.chd
|
||||
make hdd-p5-media # P5: hot-unplug hard2 во время F5 -> p5_media.chd
|
||||
make hdd-p6-sort # 0.2: target/UI сортировки -> p6_sort.chd
|
||||
make hdd-p7-select # 0.2: target/UI выбора -> p7_select.chd
|
||||
make hdd-p8-masks # 0.2: расширение того же образа масками +/−
|
||||
make hdd-p9-mkdir # 0.2: validator и UI F7 mkdir -> p9_mkdir.chd
|
||||
make hdd-p10-rename # 0.2: file/dir rename и UI F6 -> p10_rename.chd
|
||||
make hdd-p11-delete # 0.2: confirm/cancel и UI F8 -> p11_delete.chd
|
||||
make hdd-p12-group-copy # 0.2: выбранные файлы -> p12_group_copy.chd
|
||||
make hdd-p13-group-delete # 0.2: выбранные файлы -> p13_group_delete.chd
|
||||
make hdd-p14-default-colors # UI: dir/EXE/file -> p14_default_colors.chd
|
||||
make hdd-p15-tree-copy # 0.2: recursive current dir -> p15_tree_copy.chd
|
||||
make hdd-p15-tree-cancel # 0.2: cancel/cleanup -> p15_tree_cancel.chd
|
||||
make hdd-p15-tree-self # 0.2: self-target guard -> p15_tree_self.chd
|
||||
make hdd-p16-group-tree-copy # 0.2: multi-root F5 -> p16_group_tree_copy.chd
|
||||
make hdd-p17-tree-delete # 0.2: recursive group F8 -> p17_tree_delete.chd
|
||||
make hdd-p17-tree-delete-cancel # runtime Esc -> p17_tree_delete_cancel.chd
|
||||
make hdd-p18-metadata # exact FAT metadata F5 -> p18_metadata.chd
|
||||
make hdd-p19-overwrite # backup/rollback probe -> p19_overwrite.chd
|
||||
make hdd-p19-policy # UI conflict policy -> p19_policy.chd
|
||||
```
|
||||
|
||||
Сборщик `toolchain/make_hdd.sh` принимает как отдельные файлы, так и готовые
|
||||
деревья каталогов. Рекурсивное копирование дерева одной командой особенно
|
||||
важно для `BIGDIR`: 680 файлов не требуют 680 отдельных запусков `mcopy`.
|
||||
|
||||
## Запуск и проверка
|
||||
|
||||
Lua-сценарий сначала вводит `D:`, а затем запускает `SPRCMD.EXE`. Благодаря
|
||||
этому текущий диск DSS и оба относительных контекста панелей находятся на
|
||||
тестовом HDD, а не на системном `C:` или медленном `A:`.
|
||||
|
||||
Пример полного теста F5:
|
||||
|
||||
```sh
|
||||
make hdd-copy ALLOCS=3000
|
||||
tests/run_mame_hdd.sh \
|
||||
build/hdd/p4_copy.chd tests/mame_p4_copy_hdd.lua \
|
||||
artifacts/mame-p4/copy-hdd-local 75
|
||||
tests/check_p4_copy_hdd.sh build/hdd/p4_copy.chd
|
||||
```
|
||||
|
||||
После пересборки CHD MAME требуется полностью перезапустить. Нельзя извлекать
|
||||
или проверять образ во время работающего MAME: окончательное состояние
|
||||
валидируется только после чистого выхода эмулятора.
|
||||
|
||||
Скриншоты подтверждают состояние интерфейса, но не заменяют файловую
|
||||
проверку. Для copy-теста после MAME CHD преобразуется обратно в RAW, файлы
|
||||
извлекаются через mtools и сравниваются с детерминированными шаблонами.
|
||||
|
||||
Для EXEC-теста аналогичная тройка команд использует
|
||||
`tests/mame_p4_exec_hdd.lua` и `tests/check_p4_exec_hdd.sh`. Все имена,
|
||||
которые должен открыть DSS, задаются в формате 8.3: VFAT long name на образе
|
||||
не является доступным DSS-именем и может отображаться как alias с `~1`.
|
||||
|
||||
Сценарий смены носителя управляет только тестовым `hard2`. Перед запуском
|
||||
любой автоматизации проверяется, что другого экземпляра MAME Sprinter нет:
|
||||
одновременный доступ двух процессов к CHD запрещён. После физического unload
|
||||
DSS может ожидать возврата устройства внутри системного вызова; Lua возвращает
|
||||
тот же образ, после чего Commander получает управляемую ошибку и продолжает
|
||||
работу.
|
||||
@@ -0,0 +1,53 @@
|
||||
# Sprinter Commander: направление развития UI
|
||||
|
||||
Дата: 8 сентября 2026 года.
|
||||
|
||||
## Референс
|
||||
|
||||
Volkov Commander используется как поведенческий и композиционный образец,
|
||||
но интерфейс не переносится побайтно. Цель — узнаваемая двухпанельная модель
|
||||
VC, адаптированная к текстовому экрану Sprinter 80x32 и к координатному
|
||||
BIOS/ESTEX backend без консольных окон и прямого доступа к VRAM.
|
||||
|
||||
По предоставленным контрольным экранам фиксируются следующие ориентиры:
|
||||
|
||||
- независимый режим панели `Brief` с несколькими колонками коротких имён;
|
||||
- режим `Details` с колонками `Name`, `Size`, `Date`, `Time`;
|
||||
- строка итога выбора внизу соответствующей панели вида
|
||||
`N bytes in M selected files`;
|
||||
- верхняя полоса меню, открываемая `F9`, с разделами панелей, файлов,
|
||||
команд и настроек;
|
||||
- однострочный prompt под панелями и полоса функциональных клавиш внизу;
|
||||
- путь активной панели должен читаться как единая строка и визуально
|
||||
отличаться от содержимого панели;
|
||||
- активность панели определяется cursor/title, а не лишними служебными
|
||||
словами внутри рамки.
|
||||
|
||||
## Поэтапное сближение
|
||||
|
||||
1. В 0.2 сохранить устойчивую текущую геометрию, но заменить диагностическую
|
||||
статистику выбора пользовательской строкой итога и привести подписи
|
||||
F-клавиш к реально работающим командам. Строка итога в footer панели уже
|
||||
реализована и подтверждена
|
||||
[кадром MAME](../artifacts/mame-ui/selected-footer-clean.png);
|
||||
окончательная геометрия появится вместе с режимами панелей.
|
||||
2. После безопасных F6/F7/F8 добавить модель режима панели и `Details` с
|
||||
заголовками колонок; затем `Brief` с расчётом числа колонок из ширины.
|
||||
3. В 0.4 добавить верхнее меню `F9`, настоящую командную строку и
|
||||
полноэкранную псевдооболочку `Ctrl+O` на общем редакторе строки.
|
||||
4. Стандартный fallback цветов уже добавлен: белые каталоги, жёлтые `.EXE`,
|
||||
светло-серые остальные файлы. После стабилизации режимов добавить загрузку
|
||||
конфигурируемых правил расширений и сохранение режима отдельно для левой и
|
||||
правой панели.
|
||||
|
||||
До появления отдельного счётчика файлов строка итога не должна называть
|
||||
каталоги файлами: `Insert` может выбирать каталог, тогда как групповые
|
||||
`+`, `-`, `*` его обязаны игнорировать. Это различие остаётся частью модели,
|
||||
а не только правилом renderer.
|
||||
|
||||
## Проверка
|
||||
|
||||
Каждый визуальный шаг принимается не по одному кадру, а по сценарию с
|
||||
известным содержимым панели, соседним контрольным кадрам и, где применимо,
|
||||
извлечённому состоянию HDD. Зона имитации индикаторов дисководов MAME не
|
||||
участвует в сравнении интерфейса Commander.
|
||||
Reference in New Issue
Block a user