4 Commits

Author SHA1 Message Date
snark13 a14f19b657 mdview2: поддержка кодировок CP866/CP1251/KOI8-R/UTF-8 (F8)
- Детекция при открытии (BOM + эвристика по первым 4 КБ).
- 8-битные (CP866/CP1251/KOI8-R) — общий индекс/кэш, переключение
  мгновенным ремапом глифов [128-255] на отрисовке по attr (структурные
  глифы — рамка/HR/маркеры — не ремапятся).
- UTF-8 — отдельный набор: декодирование в CP866 (кириллица + ходовые
  символы: стрелки/галка/буллет/тире/кавычки/box), свой индекс/кэш.
- Два набора (docset_t g_doc[2]) со свапом «живых» глобалов; второй
  строится ЛЕНИВО при первом F8-переходе в него (build-on-demand).
- F8: цикл CP866→CP1251→KOI8R→UTF8; метка в меню видна только когда
  переключение возможно; во время сборки 8-бит первичного F8 крутит 8-бит.
- F1-справка: секция Encoding; меню разбито на блоки (F1/F8/F10).
- Фикс: g_doc обязан быть инициализирован (SDCC z80 не обнуляет статики
  надёжно) — иначе мусорный built вёл к показу неинициализированного набора.
- Убрана отладка времени обработки из статус-бара.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 11:00:49 +03:00
snark13 a6e0aacc80 mdview2: план поддержки кодировок CP1251/KOI8-R/UTF-8 (ревизия 2)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 22:39:49 +03:00
snark13 13ef8d2fa5 mdview2: выровненная отрисовка таблиц с рамкой
Таблица обрабатывается как блок в два прохода:
- проход 1: границы блока + ОТРЕНДЕРЕННЫЕ ширины колонок (мерим тем же
  inline_scan, что и при отрисовке — невидимые маркеры стиля **/код не
  раздувают столбцы);
- проход 2: верхняя рамка ┌┬┐ → строки данных │ ячейка<pad> │ → разделитель
  заголовка ├┼┤ (из строки |---|) → нижняя рамка └┴┘.

Колонки выровнены по содержимому, рамка CP866 box-drawing. Широкая таблица
остаётся nowrap+hscroll. Ячейки разбиваются cell-итератором (без массивов
на стеке); пустой g_cells под измерение освобождается ручным флашем
pending-сегмента (флаг g_skip_flush в emit_seg, чтобы не флашить повторно).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 22:23:57 +03:00
snark13 68201dca44 mdview2: 4 улучшения рендеринга markdown
1. Экранирование пунктуации: \* \_ \` \[ … → литерал, маркером НЕ считается
   (CommonMark ASCII-punctuation). Напр. "**...FILE\***" → болд "...FILE*".
   Не действует внутри инлайн-кода.
2. [x] / [ ] — ровно один символ в квадратных скобках → болд (нестандартно,
   для читабельного отображения task-list checkbox-ов). Не внутри кода.
3. Соседние пункты списка: если следующий имеет МЕНЬШИЙ отступ (dedent),
   пустую строку между ними больше не подавляем — пункты визуально разделены.
4. Абзац с ведущим отступом: все его перенесённые строки получают такой же
   отступ (continuation-префикс из col пробелов, как у списка/цитаты).

Реализация — в обоих inline-сканерах (inline_scan/scan_join_stream) + ветках
index_lines; новый helper is_escapable() и leading_spaces().

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 21:38:54 +03:00
5 changed files with 976 additions and 76 deletions
+3 -1
View File
@@ -35,8 +35,10 @@ $(DISK_TMP):
$(README_DISK): README.MD | $(DISK_TMP) $(README_DISK): README.MD | $(DISK_TMP)
iconv -c -f UTF-8 -t CP866 README.MD > $@ || true iconv -c -f UTF-8 -t CP866 README.MD > $@ || true
# UTF8TEST.MD кладётся на диск КАК ЕСТЬ (в UTF-8, без перекодировки) —
# это тестовый вход для проверки UTF-8 рендеринга (Фаза 2 кодировок).
floppy: $(EXAMPLE).exe $(README_DISK) floppy: $(EXAMPLE).exe $(README_DISK)
python3 $(MAKE_DISK) $(FLOPPY_IMG) $(EXAMPLE).exe $(README_DISK) python3 $(MAKE_DISK) $(FLOPPY_IMG) $(EXAMPLE).exe $(README_DISK) UTF8TEST.MD
@echo @echo
@echo "Floppy ready: $(FLOPPY_IMG)" @echo "Floppy ready: $(FLOPPY_IMG)"
@echo "Run: cd $(MAME_DIR) && ./run_mame.sh" @echo "Run: cd $(MAME_DIR) && ./run_mame.sh"
+11 -10
View File
@@ -33,9 +33,16 @@ Home Начало документа
End Конец документа End Конец документа
Left Right Горизонтальный сдвиг (только nowrap-строки) Left Right Горизонтальный сдвиг (только nowrap-строки)
F1 Окно справки F1 Окно справки
F8 Кодировка: CP866 → CP1251 → KOI8-R → UTF-8 → ...
F10 / Esc Выход из программы F10 / Esc Выход из программы
``` ```
Кодировка определяется автоматически при открытии (BOM + эвристика по
первым 4 КБ); `F8` переключает её вручную, если детекция ошиблась.
8-битные кодировки (CP866/CP1251/KOI8-R) переключаются мгновенно (ремап на
отрисовке). Второй набор индекс/кэша (например UTF-8) строится лениво — при
первом переключении в него (короткая пауза со спиннером), дальше мгновенно.
## Синтаксис Markdown ## Синтаксис Markdown
### Заголовки ### Заголовки
@@ -102,7 +109,7 @@ int main(void) {
## Технические характеристики ## Технические характеристики
- **Платформа:** Sprinter, процессор Z80 @ 21 МГц - **Платформа:** Sprinter, процессор Z80 @ 21 МГц
- **Кодировка:** CP866 (DOS Cyrillic) - **Кодировки:** CP866 / CP1251 / KOI8-R / UTF-8 (автоопределение, `F8`)
- **Максимальный размер файла:** 128 КБ - **Максимальный размер файла:** 128 КБ
- **Максимальное число строк в индексе:** 16 384 - **Максимальное число строк в индексе:** 16 384
- **Режим памяти:** small - **Режим памяти:** small
@@ -115,15 +122,9 @@ int main(void) {
разрешить работать с большим кол-вом страниц памяти, пока оттестированно разрешить работать с большим кол-вом страниц памяти, пока оттестированно
на работе с 8-мю страницами по 16Кб. на работе с 8-мю страницами по 16Кб.
2. **Форматированные таблицы.** Разбирать строки вида `| ячейка | ячейка |` Сделано: форматированные таблицы с рамкой; поддержка кодировок
с автоматическим выравниванием столбцов и отрисовкой разделительных CP866 / CP1251 / KOI8-R / UTF-8 с автоопределением и переключением по `F8`
линий (строки `|---|---|`). На текущий момент таблицы отображаются (второй набор строится лениво, по первому переключению).
как обычные nowrap-строки без выравнивания.
3. **Поддержка кодировок CP1251 и UTF-8.** Автоопределение кодировки
по BOM, явное указание через аргумент командной строки (`--encoding cp1251`),
возможность переключения кодировки во время просмотра (`F8`).
Нужно прежде всего для документов на русском языке; CP866 кодировака уже поддерживается.
4. **Ускорение рендеринга.** Кэш строк экрана. Оптимизация цикла вывода 4. **Ускорение рендеринга.** Кэш строк экрана. Оптимизация цикла вывода
символов через BIOS WRCHAR (пакетный вывод, DMA). символов через BIOS WRCHAR (пакетный вывод, DMA).
+28
View File
@@ -0,0 +1,28 @@
# Тест UTF-8
Это **проверка** кодировки _UTF-8_ в `mdview2`.
Кириллица должна читаться: съешь же ещё этих мягких булок.
## Символы и пунктуация
Тире — длинное, и – короткое. Кавычки: «ёлочки» и “лапки”.
Многоточие… стрелки → ← ↑ ↓, галочка ✓ и крестик ✗.
Градус 25°, пункт списка • буллет, номер № 7.
## Список
- Первый пункт
- Второй пункт с длинным текстом, чтобы проверить перенос строки по словам на границе экрана восемьдесят символов
- Ёжик, ёлка, объём
> Цитата: «Краткость — сестра таланта».
## Таблица
| Язык | Привет | Число |
|----------|---------------|-------|
| Русский | Здравствуйте | 42 |
| English | Hello | 7 |
| Deutsch | Grüße | 100 |
Конец файла.
+144
View File
@@ -0,0 +1,144 @@
# mdview2 — поддержка кодировок (CP866 / CP1251 / KOI8-R / UTF-8)
Статус: РЕАЛИЗОВАНО (2026-06-25). Все фазы сделаны; Фаза 4 — eager-кооперативным
вариантом (см. ниже), а не idle-build. Осталась только проверка на железе и
возможная оптимизация «не строить заведомо бесполезный вторичный набор».
## Цель
- CP1251, KOI8-R — простой 1:1 маппинг байтов [128-255] в CP866 (кириллица
+ основная пунктуация). KOI8-R равнозначна, входит в общий цикл.
- UTF-8 — декодирование с маппингом кириллицы в CP866 + подстановка части
некириллических символов (стрелки, галочки, тире, буллеты, box-drawing) в
CP866/ASCII-глифы.
- Автоопределение кодировки при открытии (BOM + дешёвая эвристика).
- F8 (Codepage) — переключение по циклу CP866 → CP1251 → KOI8-R → UTF8.
## Ключевые наблюдения (определяют архитектуру)
1. **8-битные кодировки имеют ОДНУ структуру.** markdown-разметка вся в ASCII
(`# * | -` …); 8-битные различаются только глифами [128-255]. Значит индекс
и кэш (смещения, переносы, ширины таблиц) для CP866/CP1251/KOI8-R —
**общие**; переключение между ними = ремап глифов [128-255], БЕЗ
переиндексации.
2. **UTF-8 — другая структура** (кириллица 2 байта). Нужен отдельный
конвертированный CP866-буфер со своим индексом/кэшем.
3. **Проблема рамок таблиц / маркеров.** В кэше намешаны контентные глифы
(байты-из-источника [128-255] — НАДО ремапить) и вставленные нами CP866-
глифы (box `│─┼…`, маркер списка 0x07, цитата 0xB3, HR 0xC4 — ремапить
НЕЛЬЗЯ). По значению байта не различить → различаем по **атрибуту**.
## Архитектура
### Буферы
- `src_phys[]` — оригинальные байты файла (грузим как сейчас; храним всегда —
под ремап и raw-view, см. memory/mdview2_file_phys_preserve).
- 8-битный режим: `fb()` читает `src_phys` напрямую. Кэш контентных ячеек
хранит **исходный байт** (не пред-конвертированный).
- UTF-8 режим: отдельные EMM-страницы `utf_phys[]` = UTF8→CP866 конвертация
src; свой индекс/кэш; `fb()` читает `utf_phys`.
### Различение контент/структура по attr
Каждый структурный глиф получает НЕ-контентный attr:
- box-рамка таблиц → новый `ATTR_BOX` (сейчас TBL_ATTR=ATTR_TEXT — поменять);
- HR (0xC4) → ATTR_HR; маркер списка (0x07) → ATTR_LIST_MARKER; цитата
(0xB3) → ATTR_QUOTE_MARKER — уже различимы.
Правило ремапа: ремапить `char>=128` только если attr ∈ контентных
(TEXT/BOLD/ITALIC/UNDER/CODE/STRIKE/TITLE1-4). Структурные — как есть.
### Ремап на этапе отрисовки
`draw_line_from_cache`:
- encoding == CP866 или UTF-8: прямой `win_rest` из кэш-страницы (ремап не
нужен — CP866 identity; UTF-8-кэш уже CP866).
- encoding == CP1251/KOI8-R: читаем кэш-строку в near-буфер, ремапим
контентные байты [128-255] через активную таблицу (структурные пропускаем
по attr), пишем в scratch-страницу, `win_rest` из неё. Только видимые ~30
строк, на скролле — дёшево.
Итого: переключение между 8-битными — мгновенно (меняем активную таблицу +
redraw, draw ремапит). Переиндексация только при переходе в/из UTF-8.
## Детекция кодировки (дешёвый скан байтов, ДО построения)
1. **BOM**: первые 3 байта EF BB BF → UTF8 (и пропустить BOM).
2. **UTF-8 валидность** (если нет BOM): проход, проверка структуры
(лид-байты 0xC2-0xDF/0xE0-0xEF/0xF0-0xF4 + континюэйшны 0x80-0xBF;
одиночный 0x80-0xBF, 0xC0/0xC1, 0xF5+ → нарушение). 0 нарушений И есть
≥1 multibyte → UTF8. Любое нарушение → 8-бит.
3. **8-бит дизамбигуация** (CP866/CP1251/KOI8-R): счёт попаданий в байты
самых ходовых строчных русских букв (о е а и н т с р в л) каждой кодировки;
максимум выигрывает. (Предрасчёт байт-наборов по таблицам.)
4. **Фолбэк**: нет байт ≥0x80 или неоднозначно → CP866 (родная).
## Таблицы (static const, CODE/const-сегмент)
- `cp1251_to_866[256]`, `koi8r_to_866[256]` — байт→байт (ASCII identity;
кириллица по раскладкам; en/em-dash, «ёлочки», … → CP866-аналоги или '?').
- UTF-8: `utf_cyr_to_866[]` для U+0400..U+045F + компактная таблица символов
`utf_sym[]` (codepoint→CP866): U+2192→'>'/стрелка, U+2190→'<', U+2713/14 ✓
→'v'/box, U+2022 •→0x07/0xF9, U+2014/2013 —→'-', U+2026 …→"...",
U+00A0→' ', U+2500.. box→CP866 box; прочее → '?'.
## Поток загрузки
1. Грузим в `src_phys`. Детект-скан → кодировка E.
2. Если E ∈ {UTF8}: строим UTF-8-набор (utf_phys + конвертация + индекс),
показываем UTF-8. Иначе: показываем 8-битный (общий индекс на src,
активная таблица = E).
3. **Ленивый build второго набора в простое.** Главный цикл — НЕ блокирующий
getkey, а kbhit-поллинг: пока нет клавиш и второй набор (UTF-8 при
стартовом 8-бит, либо 8-бит при стартовом UTF-8) не построен — докручиваем
его инкрементально. После — F8 в любую сторону мгновенно.
## F8 — переключение
- Цикл g_encoding: CP866 → CP1251 → KOI8-R → UTF8 → CP866.
- 8-бит↔8-бит: сменить активную таблицу + redraw (без переиндексации).
- в/из UTF-8: переключить активный индекс/кэш на соответствующий набор
(если построен; иначе достроить — но при ленивом build обычно уже готов).
- Статус-бар: имя кодировки; меню (строка 31): «F8 Codepage».
## Фазы реализации (ИТОГ)
1.**Ядро 8-бит**: ATTR_BOX; кэш хранит исходный байт; таблицы CP1251/KOI8R;
ремап в draw (`win_rest_remap`).
2.**UTF-8 набор**: `alloc_and_convert_utf8` + `utf8_convert` (декодер 1/2/3-
байт, 4-байтные/битые → `?`); `utf_cyr_to_866[96]` + символьные подстановки
в `conv_emit_cp` (стрелки 0x18-0x1B, галка 0xFB, буллет 0xF9, тире/кавычки/
box/° и т.п.; «» → `<`/`>`, т.к. в CP866 гильеметов нет).
3.**Детекция** (BOM + эвристика), сэмпл — первые **4 КБ** (быстро); хвост-
обрезка multibyte на границе сэмпла не штрафуется.
4.**Сосуществование 2 наборов** (`docset_t g_doc[2]` + `doc_save/load/switch`,
свап «живых» глобалов). Сборка — **ленивая (build-on-demand)**: при старте
строится только первичный (показываемый) набор; UTF-8 конвертация тоже
ленивая (в `build_doc`, не до первого экрана → старт быстрый). Второй набор
достраивается `switch_encoding()` при первом F8-переходе в него (спиннер,
потом кэш). Eager-вариант отвергнут: удваивал старт и блокировал F8 на время
фоновой сборки.
5.**F8** полный цикл CP866→CP1251→KOI8R→UTF8→CP866 (внутри 8-бит — ремап,
на границе — `doc_switch`/ленивая сборка). Статус: `enc_name` кол.37. Меню
«F8 Codepage» показывается только когда переключение возможно (`g_f8_enabled`):
во время сборки 8-битного первичного F8 разрешён в `load_key` (только цикл
8-бит); во время сборки UTF-8 первичного метка F8 скрыта. F1-справка: секция
Encoding.
NB: меню-строка разбита на 10 блоков по 8 колонок, метки Fn кладутся в блок
(n-1)*8 (F1→0, F8→7, F10→9).
## Память
- 8-бит: src + scratch-страница для ремапа (1 стр.). Доп. индекса нет.
- UTF-8 набор: utf_phys (≤8 стр.) + свой индекс/кэш-контент. Строится лениво.
- Бюджет 215 свободных страниц (sprinter_emm_budget) — с запасом.
## Открытые вопросы (к реализации)
1. Имя кодировки в статусе — где (зона имени файла / отдельный слот).
2. Набор UTF-8-подстановок символов — приоритет (→ ← ✓ ✗ • — … « » box).
3. Глубина таблиц пунктуации CP1251/KOI8 (минимум кириллица+dash или полнее).
4. Объём детект-сэмпла (весь файл или первые N КБ).
## Риски
- Корректность различения контент/структура по attr — нужно, чтобы ВСЕ
вставленные глифы имели не-контентный attr (проверить box/markers).
- Два набора индекс/кэш (8-бит + UTF-8) + переключение активного — учёт
страниц, чтобы не течь и не путать.
- Точность таблиц (особенно UTF-8 символы) — итеративно по факту.
File diff suppressed because it is too large Load Diff