Профилирование (52КБ README, раздельный замер): препроцессинг 24с, из них
bank I/O ~0с, cont-walk ~1с — всё время в посимвольных скан-циклах
(индексация 8с + рендер 13с, два прохода по файлу).
- fb() переписана на ассемблере (была закомментированная заготовка): убран
вызов функции и 32-битная арифметика на каждый байт. Раскладка __sdcccall(1)
для uint32 аргумента (p=HLDE) и возврат char в A сверены через sdcc -S;
координация маппинга с bank_read держится на том, что _io_page_w3 — порт
(__sfr 0xE2), пишем OUT — bank_read читает IN.
- render_line_to_cache: горячий путь обычного текста больше не вызывает
handle_inline_marker (6 аргументов) на каждый символ — только на маркерах
` * _ ~; cc_put заинлайнен. Поведение идентично.
Итого 24→18с. Индекс-скан (8с) лёгкого инлайна не имеет (нет per-char
вызовов). Следующий шаг — merge двух проходов в один.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Горизонтальный скролл (Фаза 5, финал):
- Скроллим по ТИПУ, а не по длине: новый флаг IF_HSCROLL ставится только
на код и таблицы; HR и границы fence (IF_NOWRAP без IF_HSCROLL) не
двигаются. Блок едет целиком, включая строки короче 80.
- Обход бага кодогенерации SDCC z80: `if (n!=g) g=n;` пишет (n-g) вместо n
(SUB сравнения затирает A, store переиспользует испорченный A). Лечится
записью viewport_x ДО сравнения. Минимальный репродьюсер и оба описания
для трекера — в docs/bugs/sdcc-z80-cmp-store-a/ (воспроизводится на чистом
sdcc 4.5, в т.ч. с --no-peep → это кодогенератор, не peephole).
Рендеринг:
- Отступленный fence (```c внутри списка) теперь распознаётся: is_fence_raw
пропускает ведущие пробелы/табы; то же в рендере прячет строку-границу.
- Строки-разделители (HR, ровно 80) больше не участвуют в скролле.
Чистка: удалён мёртвый код (is_fence_delim, get_init_style[_raw], is_cont,
seg_flags). Makefile (mdview/mdview2): iconv UTF-8→CP866 завершается ненулевым
кодом при отбрасывании символов — игнорируем (|| true).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New examples/mdview2 — render-cache version of mdview: each logical
line is rendered into an EMM (char,attr) buffer once during file load
(interleaved with index_lines()), then scrolling draws straight from
the cache via ESTEX WINREST, with no re-parsing or fb() access in the
steady state. Horizontal scroll still uses the old live-render path
(Phase 5, not yet migrated).
Format and budget were derisked empirically first (tests/winrest):
confirmed ESTEX WINCOPY/WINREST buffer layout and measured EMM free
space, both folded into the implementation.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>