8e389c03f8
Реализовать двухпанельный Commander от платформенного PoC до этапов P6-P20: EMM-каталог, сортировку и выбор, операции с файлами и деревьями, транзакционное копирование, метаданные, политику конфликтов и предварительную проверку свободного места. Добавить проектную документацию, HDD/MAME-сценарии и проверенные артефакты. Расширить libc операцией bank_write_page, исправлением режима O_RDONLY и связанными регрессионными проверками.
271 lines
18 KiB
Markdown
271 lines
18 KiB
Markdown
# 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.
|