mdview2: статус-бар в отдельный модуль, упростить alloc_set_storage

- mdview2_status.c: вынести статус-бар/меню/спиннер из ядра
- mdview2.c: убрать retry-цикл в alloc_set_storage (fail-fast вместо
  ложной устойчивости — при нехватке EMM под индекс контент тоже не влезет)
- mdview2.h: дополнить экспортами статус-модуля
- mdview2_md.c / mdview2_raw.c: зачистка после расщепления
- mdview/mdview.c: переименовать scroll_* → md_scroll_* (симметрия)
- docs/fast_ram.md, docs/turboc.txt: добавить справочные доки
- examples/mdview2/README.MD, READMEBG.MD: обновить описание

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-26 23:19:00 +03:00
parent 05916a3cc6
commit 1b78dda125
12 changed files with 1802 additions and 443 deletions
+41
View File
@@ -112,10 +112,51 @@ int main(void) {
- **Кодировки:** CP866 / CP1251 / KOI8-R / UTF-8 (автоопределение, `F8`)
- **Максимальный размер файла:** 128 КБ
- **Максимальное число строк в индексе:** 16 384
- **Максимальная длина строки в рендер-кэше:** 255 ячеек (см. ограничение ниже)
- **Режим памяти:** small
- Код программы, cтек, данные, куча — окнa W1-W2 (32 КБ, адреса 0x40000xBFFF).
- Буфер файла — страницы EMM, отображаемые в W3 (0xC0000xFFFF)
## Известные ограничения
### Длина строки в MD-режиме — 255 ячеек
Рендер-кэш хранит каждую логическую строку как **не более 255 пар (символ, атрибут)**
— константа `MAX_CACHE_LINE_LEN`. Лимит задан типами: `g_ncells` и
`cache_rec_t.len``uint8_t`. Касается всех строк, но заметнее всего на
**горизонтально скроллируемых** строках (блоки кода и строки таблиц, флаг
`IF_HSCROLL`), которые в MD-режиме можно листать вправо.
**Что происходит с более длинной строкой:** при индексации `gc_put()` молча
отбрасывает каждую ячейку после 255-й (`if (g_ncells < MAX_CACHE_LINE_LEN)`).
В кэш попадают только первые 255 ячеек, остаток **теряется** — до него нельзя
доскроллить и **нет маркера обрезки** на 255-й позиции (маркер `>` означает лишь
«есть ещё в пределах кэша»). Переполнения буфера нет — `gc_put` проверяет границу.
Важно: «255 ячеек» — это **отрендеренная ширина**, не байты исходника. Табы в
коде разворачиваются в пробелы (до `TAB_STOP`), а ячейки таблицы добиваются
пробелами до ширины колонки + рамки `│` — поэтому кап достигается раньше, чем
255 «полезных» символов.
> **RAW-режим (`F2`) этого лимита не имеет** — он рисует прямо из файла
> побайтово, длинные строки видны целиком (через wrap `F3` или гориз. скролл).
**Идея снятия лимита** (оценка, не реализовано) — расширить длину до `uint16_t`:
- `cache_rec_t.len` `uint8_t→uint16_t` — структура остаётся **ровно 8 байт**
(len съедает один pad-байт), адресация `idx<<3` не меняется. Бесплатно.
- `g_ncells` / `g_ncells_at_space``uint16_t` — главная цена по **коду/скорости**:
16-битная арифметика на Z80 в горячем `gc_put` (вызов на каждую ячейку) и в
scan-циклах. Ориентир: **+0.2…0.4 КБ кода** + замедление индексации.
- Буфер `g_cells[MAX_CACHE_LINE_LEN*2]` в near-RAM (W2) — главная цена по
**памяти**: `2 × кап` байт. Сейчас 510 Б; кап 512 → +0.5 КБ, 1024 → +1.5 КБ.
EMM-кэш контента (1 МБ) длинные строки тянет легко — узкое место именно near.
- `viewport_x` (и копия в `docset_t`) + потолок `max_vx` (сейчас 248) → `uint16_t`,
иначе хранить >255 можно, а доскроллить нельзя. Плюс `widths[]`/`ccx` в таблицах/коде.
Реалистичный компромисс — кап 512–1023: хватит почти всем листингам/таблицам,
цена ~+0.3 КБ кода и +0.5…1.5 КБ near-RAM. **Дешёвая полумера без `uint16_t`**
ставить честный маркер обрезки на 255-й позиции, чтобы потеря была видна.
## TODO
1. **Увеличение размера документов.** Снять лимит 128 КБ: Достаточно