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

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

271 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.