Volkov: добавить Sprinter Commander
Реализовать двухпанельный Commander от платформенного PoC до этапов P6-P20: EMM-каталог, сортировку и выбор, операции с файлами и деревьями, транзакционное копирование, метаданные, политику конфликтов и предварительную проверку свободного места. Добавить проектную документацию, HDD/MAME-сценарии и проверенные артефакты. Расширить libc операцией bank_write_page, исправлением режима O_RDONLY и связанными регрессионными проверками.
This commit is contained in:
@@ -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.
|
||||
Reference in New Issue
Block a user