Files
Sprinter-SDCC/examples/mdview2/mdview2_raw.c
T
snark13 68d5be6e47 mdview2: RAW-просмотр исходника (F2/F3) + инкрементальная UTF-8 конвертация
Новый модуль mdview2_raw.c (+ mdview2.h с общими константами/атрибутами и
extern'ами) — первый шаг разбиения монолита. Опционален через mdview2_conf.h
(#define WITH_RAW): при 0 — пустой объектник, нулевой расход (проверено:
размер как до RAW).

RAW-просмотр (без markdown-форматирования):
- работает по активному буферу (8-бит как есть / UTF-8 декодированный), 1 байт
  = 1 ячейка, \t→пробел, ремап CP1251/KOI8 на отрисовке;
- два под-режима: wrap (перенос кратно 80) и hscroll (одна строка + ←/→);
- прокрутка на 1 строку через аппаратный scroll + отрисовка одной строки;
  вывод char-буфером (bios_write_until по фону ATTR_TEXT), без win_rest/scratch;
- индекс/кэш markdown не используются, 0 доп. EMM.

Клавиши/меню:
- F2 — тумблер RAW↔MD (запоминает под-режим RAW);
- F3 — Wrap/Unwrap (только в RAW), метка показывает целевой режим;
- меню перестроено: блоки по 8 кол (col i*8), номера всех 10 клавиш без 'F'
  (' 1'..' 9','10'), текст-функция 6 симв. сразу за номером и только когда
  функция доступна; F8 сокращён до CodePg.

Инкрементальная UTF-8→CP866 конвертация: вместо полного прохода перед
индексацией — чанками впереди позиции чтения (CONV_MARGIN), первый экран
появляется быстро. Конвертер читает оригинал через cv_read (своя W3-страница),
index_lines докручивает конвертацию; progress_tick рисует по текущему виду
(MD/RAW), без мелькания чужого вида.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 23:31:54 +03:00

335 lines
12 KiB
C

/*
* mdview2_raw.c — RAW-просмотр исходного текста (без markdown-форматирования).
*
* Работает по АКТИВНОМУ буферу документа через fb() (8-бит = исходные байты,
* UTF-8 = декодированный в CP866 буфер), ремап ≥0x80 (CP1251/KOI8) — на
* отрисовке. Индекс/кэш markdown не используются: позиция — байт-offset,
* 1 байт = 1 ячейка, \t показываем пробелом. Два режима:
* VIEW_RAW_WRAP — длинные строки переносятся кратно 80 (рвём слова);
* VIEW_RAW_HSCROLL — строка в один ряд, горизонтальный скролл.
*
* Память: 0 доп. EMM; near-буфер строки + переиспользование g_scratch_phys.
*/
#include <stdint.h>
#include <stdio.h>
#include <conio.h>
#include <bios/text.h>
#include "mdview2.h"
#if WITH_RAW /* весь модуль — опционален (mdview_conf.h); при 0 пустой объектник */
static uint32_t raw_top = 0; /* байт-offset верхней строки экрана */
static uint32_t raw_bot = 0; /* offset ряда сразу ПОД экраном (для прокрутки) */
static uint16_t raw_hpan = 0; /* гориз. сдвиг (только HSCROLL) */
#define HPAN_STEP 8u
/* ---- примитивы навигации по физическим строкам / переносам -------- */
static uint32_t raw_line_start(uint32_t off)
{
while (off && fb(off - 1) != '\n') off--;
return off;
}
/* WRAP: начало следующего ряда от off (\n завершает строку; иначе +80). */
static uint32_t raw_next_wrap(uint32_t off)
{
uint32_t e = off, lim = off + SCREEN_W;
while (e < file_size) {
if (fb(e) == '\n') return e + 1;
e++;
if (e >= lim) { /* прошли 80 без \n */
if (e < file_size && fb(e) == '\n') return e + 1; /* ровно 80 + \n */
return e; /* длинная строка → продолжение */
}
}
return e; /* EOF */
}
/* WRAP: начало предыдущего ряда. */
// static uint32_t raw_prev_wrap(uint32_t off)
// {
// if (off == 0) return 0;
// uint32_t ls = raw_line_start(off);
// if (off > ls) /* тот же абзац: на 80 назад */
// return off - SCREEN_W;
// /* off == начало строки: последний ряд предыдущей физической строки.
// * Последнюю 80-границу ищем циклом (без 32-битного деления). */
// uint32_t pls = raw_line_start(off - 1); /* off-1 — это \n пред. строки */
// uint32_t last = pls;
// while (last + SCREEN_W < (off - 1)) last += SCREEN_W;
// return last;
// }
static uint32_t raw_prev_wrap(uint32_t off)
{
if (off == 0) return 0;
uint32_t pls = raw_line_start(off - 1); /* off-1 — это \n пред. строки */
while (pls + SCREEN_W < (off - 1)) pls += SCREEN_W;
return pls;
}
/* HSCROLL: начало следующей/предыдущей физической строки. */
static uint32_t raw_next_line(uint32_t off)
{
while (off < file_size && fb(off) != '\n') off++;
return (off < file_size) ? off + 1 : off;
}
// static uint32_t raw_prev_line(uint32_t off)
// {
// if (off == 0) return 0;
// uint32_t p = off - 1; /* \n, завершивший пред. строку */
// if (p == 0) return 0;
// p--;
// while (p != 0 && fb(p) != '\n') p--;
// return (fb(p) == '\n') ? p + 1 : 0;
// }
static uint32_t raw_prev_line(uint32_t off)
{
if (off == 0) return 0;
return raw_line_start(off - 1);
}
/* ---- отрисовка ряда (общий near-буфер → g_scratch_phys → win_rest) - */
static uint8_t raw_buf[SCREEN_W + 1]; /* только символы (атрибут единый, задаётся при выводе) */
/* В RAW атрибут единый (ATTR_TEXT): буфер — только 80 символов, без пар
* (char,attr). Кладём один байт-глиф (\t→пробел, ремап ≥0x80). */
static void raw_cell(uint8_t i, uint8_t ch)
{
if (ch >= 0x80 && g_remap) ch = g_remap[ch - 0x80];
raw_buf[i] = ch;
}
/* Вывод готового 80-символьного буфера в строку экрана одним BIOS-вызовом
* (char-буфер + единый attr — без WINREST/scratch-страницы). */
static void raw_flush(uint8_t row)
{
bios_set_place(row, 0);
// bios_writeattr((const char *)raw_buf, SCREEN_W, ATTR_TEXT);
// bios_writeattr_until((const char *)raw_buf, SCREEN_W, ATTR_TEXT, 0);
bios_write_until((const char *)raw_buf, SCREEN_W, 0);
}
static uint32_t raw_draw_wrap_row(uint32_t off, uint8_t row)
{
uint8_t i = 0;
uint32_t e = off, lim = off + SCREEN_W;
while (i < SCREEN_W && e < file_size) {
char c = fb(e);
if (c == '\n') {
raw_cell(i, 0);
break;
}
raw_cell(i, (uint8_t)(c == '\t' ? ' ' : c));
i++; e++;
}
raw_flush(row);
return raw_next_wrap(off);
}
// static uint32_t raw_draw_hscroll_row(uint32_t off, uint8_t row)
// {
// uint32_t e = off;
// while (e < file_size && fb(e) != '\n') e++; /* e = конец строки */
// uint16_t len = e - off;
// uint8_t i = 0;
// uint32_t p = off + raw_hpan;
// while (i < SCREEN_W && p < e) {
// char c = fb(p);
// raw_cell(i, (uint8_t)(c == '\t' ? ' ' : c));
// i++; p++;
// }
// for (; i < SCREEN_W; i++) raw_buf[i] = ' ';
// if (raw_hpan > 0) raw_buf[0] = '<';
// if (len > (uint16_t)raw_hpan + SCREEN_W) raw_buf[SCREEN_W - 1] = '>';
// raw_flush(row);
// return (e < file_size) ? e + 1 : e;
// }
static uint32_t raw_draw_hscroll_row(uint32_t off, uint8_t row)
{
uint32_t e = off;
while (e < file_size && fb(e) != '\n') e++; /* e = конец строки */
uint16_t len = e - off;
uint8_t i = 0;
uint32_t p = off + raw_hpan;
while (i < SCREEN_W && p < e) {
char c = fb(p);
raw_cell(i, (uint8_t)(c == '\t' ? ' ' : c));
i++; p++;
}
// raw_buf[i] = 0;
if (raw_hpan > 0)
raw_buf[0] = '<';
if (len > (uint16_t)raw_hpan + SCREEN_W)
raw_buf[SCREEN_W - 1] = '>';
if (i < SCREEN_W)
raw_buf[i] = 0;
raw_flush(row);
return (e < file_size) ? e + 1 : e;
}
/* Рисует один ряд от off (по режиму) и возвращает offset следующего ряда. */
static uint32_t raw_draw_one(uint32_t off, uint8_t row)
{
if (off >= file_size) { fill_row(row, ATTR_TEXT); return off; }
return (g_view == VIEW_RAW_WRAP) ? raw_draw_wrap_row(off, row)
: raw_draw_hscroll_row(off, row);
}
void raw_draw(void)
{
uint32_t off = raw_top;
for (uint8_t r = 0; r < VIEW_H; r++)
off = raw_draw_one(off, (uint8_t)(VIEW_TOP_ROW + r));
raw_bot = off; /* offset первого ряда НИЖЕ экрана */
}
/* Прокрутка на одну строку: аппаратный scroll окна документа + отрисовка
* ОДНОЙ новой строки (как в MD-режиме). raw_bot — offset ряда сразу под
* экраном, ведётся инкрементально. */
static uint32_t raw_next(uint32_t off)
{
return (g_view == VIEW_RAW_WRAP) ? raw_next_wrap(off) : raw_next_line(off);
}
static uint32_t raw_prev(uint32_t off)
{
return (g_view == VIEW_RAW_WRAP) ? raw_prev_wrap(off) : raw_prev_line(off);
}
static void raw_scroll_down1(void)
{
if (raw_bot >= file_size) return; /* уже внизу */
scroll(0, VIEW_TOP_ROW, SCREEN_W, VIEW_H, 1, 0); /* содержимое вверх */
raw_bot = raw_draw_one(raw_bot, (uint8_t)(VIEW_TOP_ROW + VIEW_H - 1));
raw_top = raw_next(raw_top);
}
static void raw_scroll_up1(void)
{
if (raw_top == 0) return;
uint32_t nt = raw_prev(raw_top);
scroll(0, VIEW_TOP_ROW, SCREEN_W, VIEW_H, 2, 0); /* содержимое вниз */
(void)raw_draw_one(nt, VIEW_TOP_ROW);
raw_bot = raw_prev(raw_bot);
raw_top = nt;
}
/* ---- статус-бар (без подсчёта строк: процент по байтам + режим) ---- */
/* Процент по байтам без 32-битного деления: масштабируем offset'ы в единицы
* по 1 КБ (>>10), дальше 16-битная арифметика (a*100 ≤ 12800). */
static uint8_t raw_pct(void)
{
uint16_t a = (uint16_t)(raw_top >> 10);
uint16_t b = (uint16_t)(file_size >> 10);
if (b == 0) return 0; /* файл < 1 КБ */
return (uint8_t)(a * 100u / b);
}
void raw_status_numbers(void)
{
uint8_t pct = raw_pct();
textattr(ATTR_BAR);
gotoxy(47, 0);
bios_writeattr_until(g_view == VIEW_RAW_WRAP ? "RAW wrap" : "RAW pan",
20, ATTR_BAR, 0);
gotoxy(73, 0);
dec8(pct);
cputs("% ");
}
/* ---- посев позиции / пересев при смене активного буфера ----------- */
void raw_seed_from(uint16_t md_top_line)
{
uint32_t off = seg_off(md_top_line);
if (off > file_size) off = 0;
raw_top = raw_line_start(off);
raw_hpan = 0;
}
/* Сброс в начало — при смене активного буфера (F8 в/из UTF-8: размеры/offset'ы
* разные, старый raw_top невалиден). Без percent-математики. */
void raw_home(void)
{
raw_top = 0;
raw_hpan = 0;
}
void raw_screen_init(void) {
bios_clearwin(1, 0, 30, 80, ATTR_TEXT);
}
/* Нормализация позиции при смене под-режима RAW (F3 Wrap/Unwrap): выровнять
* raw_top на начало физической строки и снять горизонтальный сдвиг. */
void raw_renorm(void)
{
raw_top = raw_line_start(raw_top);
raw_hpan = 0;
}
/* ---- навигация (вызывается из главного цикла при g_view != VIEW_MD) - */
uint8_t raw_key(uint8_t scan)
{
uint32_t nt;
switch (scan) {
case KEY_DOWN: raw_scroll_down1(); raw_status_numbers(); return 1;
case KEY_UP: raw_scroll_up1(); raw_status_numbers(); return 1;
case KEY_PGDN:
nt = raw_top;
for (uint8_t k = 0; k < VIEW_H; k++) {
uint32_t n2 = raw_next(nt);
if (n2 >= file_size) break;
nt = n2;
}
break;
case KEY_PGUP:
nt = raw_top;
for (uint8_t k = 0; k < VIEW_H; k++) nt = raw_prev(nt);
break;
case KEY_HOME:
nt = 0; raw_hpan = 0;
break;
case KEY_END:
nt = file_size; /* отмотать VIEW_H рядов назад */
for (uint8_t k = 0; k < VIEW_H; k++) nt = raw_prev(nt);
raw_hpan = 0;
break;
case KEY_LEFT:
if (g_view == VIEW_RAW_HSCROLL && raw_hpan) {
raw_hpan = (raw_hpan >= HPAN_STEP) ? (uint16_t)(raw_hpan - HPAN_STEP) : 0;
raw_draw();
raw_status_numbers();
}
return 1;
case KEY_RIGHT:
if (g_view == VIEW_RAW_HSCROLL) {
if (raw_hpan < 248u) raw_hpan = (uint16_t)(raw_hpan + HPAN_STEP);
raw_draw();
raw_status_numbers();
}
return 1;
default:
return 0; /* не наша клавиша */
}
raw_top = nt;
raw_draw(); /* PgUp/PgDn/Home/End — полный экран */
raw_status_numbers();
return 1;
}
#endif /* WITH_RAW */