Files
Sprinter-SDCC/examples/mdview2/mdview2.h
T
snark13 fabbc8129c mdview2: percent на 16-битной арифметике (убрать __divulong)
calc_md_pct/calc_raw_pct тянули 32-битное деление __divulong (+__muluint2ulong)
ради показа процента в статусе. Оба дают операнды ≤16 бит (≤18432 / ≤1024),
переполняет только *100. Новый pct16() масштабирует оба вниз и считает долю
циклом-вычитанием — 66 Б, НОЛЬ подтянутых арифм-хелперов. Точность ±1%
(на границах точно), для индикатора прокрутки незаметно.

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

281 lines
20 KiB
C

/*
* mdview2.h — общие определения и интерфейс между модулями mdview2.
*
* Монолит mdview2.c расщеплён на ядро + модули: index (парсер), md/raw (виды),
* enc (кодировки), table, help. Здесь — разделяемые константы экрана/атрибутов,
* словари флагов/стилей, тип cache_rec_t, extern-объявления разделяемого
* состояния ядра и API каждого модуля (сгруппированы по секциям ниже).
*/
#ifndef MDVIEW2_H
#define MDVIEW2_H
#include <stdint.h>
#include <palette.h> /* COLOR(), COLOR_* */
#include "mdview2_conf.h" /* WITH_RAW / WITH_HEX — выбор опциональных модулей */
/* ---- Геометрия экрана -------------------------------------------- */
#define SCREEN_W 80
#define SCREEN_H 32
#define VIEW_TOP_ROW 1
#define VIEW_H 30 /* видимая область: строки 1..30 включительно */
#define MENU_ROW 31
#define TAB_STOP 4
#define HPAN_STEP 8u /* шаг горизонтального сдвига (←/→) */
/* ---- Геометрия статус-бара (строка 0): фиксированные поля и разделители ---- */
#define SPINNER_COL 8 /* слот спиннера загрузки */
#define DIV1_X 45 /* разделитель │ перед числами диапазона строк */
#define DIV2_X 71 /* разделитель │ перед процентом */
/* Числа/метка пишутся с DIVn_X+2; область между разделителями — [DIV1_X+1 .. DIV2_X-1]. */
/* ---- Параметры файла и памяти ------------------------------------ */
#define PAGE_BITS 14u
#define PAGE_SIZE (1u << PAGE_BITS) /* размер EMM-страницы: 16 КБ */
#define PAGE_MASK ((uint16_t)(PAGE_SIZE - 1u))
#define MAX_PAGES 16 /* 16 страниц × 16 КБ = 256 КБ */
#define MAX_FILE ((uint32_t)MAX_PAGES * PAGE_SIZE)
#define FILE_BUF ((char *)0xC000) /* окно W3, куда мапится текущая EMM-страница */
/* ---- Палитра атрибутов ------------------------------------------- */
#define ATTR_RESET COLOR(COLOR_LIGHTGRAY, COLOR_BLACK)
#define ATTR_TEXT COLOR(COLOR_LIGHTGRAY, COLOR_BLUE)
#define ATTR_TEXT_TITLE1 COLOR(COLOR_YELLOW, COLOR_BLUE)
#define ATTR_TEXT_TITLE2 COLOR(COLOR_LIGHTBLUE, COLOR_BLUE)
#define ATTR_TEXT_TITLE3 COLOR(COLOR_LIGHTGREEN, COLOR_BLUE)
#define ATTR_TEXT_TITLE4 COLOR(COLOR_LIGHTGREEN, COLOR_BLUE)
#define ATTR_TEXT_BOLD COLOR(COLOR_LIGHTRED, COLOR_BLUE)
#define ATTR_TEXT_ITALIC COLOR(COLOR_LIGHTGREEN, COLOR_BLUE)
#define ATTR_TEXT_UNDERSORE COLOR(COLOR_LIGHTMAGENTA, COLOR_BLUE)
#define ATTR_TEXT_CODE COLOR(COLOR_WHITE, COLOR_BLUE)
#define ATTR_TEXT_STRIKE COLOR(COLOR_DARKGRAY, COLOR_BLUE)
#define ATTR_LIST_MARKER COLOR(COLOR_LIGHTCYAN, COLOR_BLUE)
#define ATTR_QUOTE_MARKER COLOR(COLOR_CYAN, COLOR_BLUE)
#define ATTR_HR COLOR(COLOR_CYAN, COLOR_BLUE)
#define ATTR_BOX COLOR(COLOR_CYAN, COLOR_BLUE)
#define ATTR_TRUNC COLOR(COLOR_YELLOW, COLOR_BLUE)
#define ATTR_BAR COLOR(COLOR_BLACK, COLOR_LIGHTCYAN)
#define ATTR_BAR_SPINNER COLOR(COLOR_WHITE, COLOR_LIGHTCYAN)
#define ATTR_MENU_T COLOR(COLOR_BLACK, COLOR_LIGHTCYAN)
#define ATTR_MENU_K COLOR(COLOR_YELLOW, COLOR_BLACK)
#define ATTR_WARN COLOR(COLOR_YELLOW, COLOR_RED) /* строка-обрыв (исчерпан кэш/лимит) */
/* ---- Атрибуты диалога справки ------------------------------------ */
#define ATTR_HELP_BG COLOR(COLOR_LIGHTGRAY, COLOR_BLACK)
#define ATTR_HELP_BDR COLOR(COLOR_WHITE, COLOR_BLACK)
#define ATTR_HELP_TIT COLOR(COLOR_YELLOW, COLOR_BLACK)
#define ATTR_HELP_HDR COLOR(COLOR_WHITE, COLOR_BLACK)
/* ---- Режимы просмотра -------------------------------------------- */
#define VIEW_MD 0 /* markdown с форматированием (по умолчанию) */
#define VIEW_RAW_WRAP 1 /* RAW: длинные строки переносятся кратно 80 */
#define VIEW_RAW_HSCROLL 2 /* RAW: длинные строки в одну, гориз. скролл */
/* ---- Кодировки --------------------------------------------------- */
/* 8-битные (CP866/CP1251/KOI8R) различаются только ремапом глифов [128-255]
* на отрисовке; UTF-8 — отдельный декодированный набор. */
#define ENC_CP866 0
#define ENC_CP1251 1
#define ENC_KOI8R 2
#define ENC_UTF8 3
#define ENC_UNSUPPORTED -1 /* UTF16 / UTF32 */
#define CONV_MARGIN 4096u /* на сколько байт держать UTF-конвертацию впереди индексатора */
/* ---- Флаги сегмента индекса (IF_*) — общие для индексатора/кэша/вью ---- */
#define IF_CONT 0x01u /* сегмент является продолжением перенесённой строки */
#define IF_NOWRAP 0x02u /* строка не переносится (кодовый блок / HR / таблица) */
#define IF_BLANK 0x04u /* визуально пустая строка */
#define IF_CODE 0x08u /* тело fenced code-блока (verbatim-режим) */
#define IF_HSCROLL 0x10u /* блок горизонтально скроллируется ЦЕЛИКОМ (код/таблица),
* включая строки короче 80 — двигаются как единый блок.
* НЕ ставится на HR и границы fence (им нечего прятать). */
#define IF_TRUNC_MSG 0x20u /* строка-обрыв: рисуется ВЖИВУЮ фикс. текстом (без контент-кэша);
* cache_rec.reserved несёт код причины (TRUNC_*). */
/* ---- Причина обрыва индексации (cache_rec.reserved при IF_TRUNC_MSG) ---- */
#define TRUNC_CONTENT 1 /* исчерпан контент-кэш (MAX_CACHE_CONTENT_PAGES) */
#define TRUNC_LINES 2 /* исчерпана ёмкость индекса (max_lines) */
#define TRUNC_FILE 3 /* файл больше MAX_FILE — прочитаны первые 256 КБ */
/* ---- Словарь сегментов/стилей (общий для индексатора и таблиц) ---- */
/* Тип сегмента-продолжения, передаваемый в emit_seg()/inline_scan(). */
#define CK_PLAIN 0
#define CK_QUOTE 1
#define CK_LIST 2
#define CK_OTHER 3
/* Начальный inline-стиль сегмента (INIT_STYLE_*). */
#define INIT_STYLE_PLAIN 0x0
#define INIT_STYLE_BOLD 0x1
#define INIT_STYLE_ITALIC 0x2
#define INIT_STYLE_UNDER 0x3
#define INIT_STYLE_CODE 0x4
#define INIT_STYLE_STRIKE 0x5
/* ---- Таблицы: CP866 light box-drawing (общий для индексатора и модуля) ---- */
#define TBL_MAX_COLS 16
#define TBL_ATTR ATTR_BOX
#define TBL_H 0xC4 /* ─ */
#define TBL_V 0xB3 /* │ */
#define TBL_TL 0xDA /* ┌ */
#define TBL_TM 0xC2 /* ┬ */
#define TBL_TR 0xBF /* ┐ */
#define TBL_ML 0xC3 /* ├ */
#define TBL_MM 0xC5 /* ┼ */
#define TBL_MR 0xB4 /* ┤ */
#define TBL_BL 0xC0 /* └ */
#define TBL_BM 0xC1 /* ┴ */
#define TBL_BR 0xD9 /* ┘ */
/* ================================================================== *
* Разделяемые символы mdview2.c, используемые модулем RAW.
* ================================================================== */
extern uint8_t g_view; /* активный режим просмотра (VIEW_*) */
extern uint32_t file_size; /* размер активного буфера документа */
extern const uint8_t *g_remap; /* таблица ремапа [128-255] (0 = нет) */
extern uint8_t g_scratch_phys; /* scratch EMM-страница для win_rest */
char fb(uint32_t p); /* байт активного буфера (W3-маппинг) */
void win_rest(uint8_t row, uint8_t col, uint8_t h, uint8_t w, uint8_t page, uint16_t off);
void fill_row(uint8_t y, uint8_t attr);
uint8_t pct16(uint16_t num, uint16_t den); /* num*100/den (0..100), 16-бит, без __divulong */
uint32_t seg_off(uint16_t idx); /* offset исходника для логической строки idx */
/* ================================================================== *
* API модуля RAW (mdview2_raw.c).
* ================================================================== */
void raw_seed_from(uint16_t md_top_line); /* вход в RAW: позиция по top_line */
void raw_home(void); /* сброс в начало (смена буфера при F8 в/из UTF-8) */
void raw_draw(void); /* перерисовать область документа в RAW */
uint8_t raw_pct(void); /* % прокрутки по байтам (для render_raw_status_numbers) */
uint8_t raw_key(uint8_t scan); /* навигация RAW; 1 = обработано */
void raw_screen_init(void); /* очистка экрана с нужным аттрибутом */
void raw_renorm(void); /* выровнять позицию при смене под-режима (F3) */
/* ================================================================== *
* Запись директории рендер-кэша (одна на видимую строку, РОВНО 8 байт —
* cache_dir_get/put адресуют сдвигом idx<<3). Контент — len пар (char,attr).
* ================================================================== */
typedef struct cache_rec_s {
uint8_t page; /* EMM-страница рендер-кэша (физический номер) */
uint16_t off; /* смещение в странице, 0..16383 (байты, не ячейки) */
uint8_t len; /* длина контента в ЯЧЕЙКАХ (char,attr пар), 0..255; 0 = пустая/HR */
uint8_t flags; /* копия IF_NOWRAP/IF_CODE/IF_BLANK на момент рендера */
uint8_t reserved; /* не используется */
uint8_t pad[2]; /* явный резерв, добивка до 8 байт */
} cache_rec_t;
void cache_dir_get(uint16_t idx, cache_rec_t *r); /* читатель директории кэша (ядро) */
void put_str_attr(uint8_t x, uint8_t y, const char *s, uint8_t attr); /* печать строки с атрибутом */
/* ================================================================== *
* Состояние навигации/представления (определено в mdview2.c).
* ================================================================== */
extern uint16_t n_lines; /* всего строк в индексе текущего набора */
extern uint16_t top_line; /* верхняя видимая строка */
extern uint8_t viewport_x; /* горизонтальный сдвиг (nowrap-строки) */
extern char filename[]; /* имя файла для статус-бара */
extern uint8_t g_loading; /* 1 во время index_lines() */
extern uint8_t g_ready; /* 1 когда первичный документ построен (F2 RAW) */
extern uint8_t g_f8_enabled; /* можно ли сейчас переключать кодировку */
/* ================================================================== *
* Статус-бар (строка 0), меню (строка 31), спиннер — mdview2_status.c.
* Атомарные части: prerender (фикс. хром, один раз) / encoding (поле кодировки)
* / numbers (диапазон строк + %) / menu / spinner — обновляются по отдельности.
* ================================================================== */
void prerender_status(void); /* фикс. часть: фон, разделители, MDVIEW, имя файла (1 раз) */
void status_encoding(void); /* только поле кодировки (col 37) */
void render_md_status_numbers(void); /* числа MD: диапазон строк + % */
void render_raw_status(void); /* RAW mode: метка режима */
void render_full_status(void); /* encoding + numbers (MD) */
void render_menu(void); /* строка меню (по смене режима/готовности) */
uint16_t drawable_lines(void); /* число строк, готовых к показу */
void spinner_tick(void); /* кадр спиннера загрузки */
void spinner_show(uint8_t on); /* вкл/выкл спиннер */
void render_percent_progress(uint8_t pct); /* показать процентный прогресс просмотра */
/* ================================================================== *
* Отрисовка области MD-документа и прокрутка (mdview2_md.c).
* ================================================================== */
void draw_viewport_from_cache(void); /* перерисовать область документа из кэша */
void clamp_top(void); /* привести top_line к диапазону */
void md_scroll_up(uint16_t n);
void md_scroll_down(uint16_t n);
void md_scroll_horizon(int8_t delta); /* горизонтальный сдвиг nowrap-блоков */
uint8_t md_key(uint8_t scan); /* навигация MD (после загрузки); 1 = обработано */
/* ================================================================== *
* API модуля справки (mdview2_help.c).
* ================================================================== */
void show_help(void); /* модальный диалог F1 */
/* ================================================================== *
* Парсер/индексатор (mdview2_index.c) и разделяемое с ним состояние.
* Хранилище индекса (index_blk/pages/phys, n_lines/max_lines) — в ядре
* (аллокация/doc-slot), индексатор пишет в него; cache_*-писатели и
* progress_tick ядро экспортирует индексатору (spinner_tick — из status).
* ================================================================== */
#define INDEX_RECS_PER_PAGE 2048u /* 16384 / 8; запись всегда в одной странице */
#define MAX_CACHE_LINE_LEN 255u /* кап длины контента строки в ячейках (буфер g_cells) */
extern uint16_t max_lines; /* ёмкость индекса: index_pages * 2048 */
extern uint8_t index_blk, index_pages; /* дескриптор EMM-блока индекса */
extern uint8_t index_phys[]; /* физ. страницы индекса */
extern uint8_t index_truncated; /* индекс упёрся в ёмкость */
extern uint8_t g_abort; /* F10/Esc во время загрузки → прервать индексацию */
extern uint8_t g_file_clamped; /* файл был обрезан до MAX_FILE при загрузке (>256 КБ) */
uint8_t cache_reserve(uint16_t nbytes, uint8_t *out_page, uint16_t *out_off);
void cache_commit(uint8_t page, uint16_t off, const void *buf, uint16_t len);
void cache_dir_put(uint16_t idx, const cache_rec_t *r);
void progress_tick(void); /* кооперативный шаг loading-loop (рисует/листает) */
void index_lines(void); /* построить индекс по загруженному файлу */
/* ================================================================== *
* Буфер ячеек рендера (mdview2.c) — общий с модулем таблиц.
* Таблицы эмитят свои строки теми же примитивами, что и индексатор.
* ================================================================== */
extern uint8_t g_ncells; /* ячеек в текущем сегменте */
void gc_put(char ch, uint8_t attr); /* добавить ячейку в g_cells */
void gc_fill(char ch, uint8_t attr, uint8_t n); /* n одинаковых ячеек */
uint8_t inline_scan(uint32_t q, uint32_t q_end, uint8_t col,
uint8_t ckind, uint8_t line_style, uint8_t base_attr,
uint8_t nowrap);
/* ================================================================== *
* API модуля таблиц (mdview2_table.c) — вызывается из index_lines().
* ================================================================== */
uint32_t row_end(uint32_t p); /* offset завершающего '\n' (или file_size) */
uint32_t table_first_cell(uint32_t row_start); /* контент первой ячейки строки */
uint8_t table_next_cell(uint32_t *pp, uint32_t lineend, uint32_t *cs, uint32_t *ce);
uint8_t table_is_sep_row(uint32_t row_start, uint32_t lineend);
void table_border(const uint8_t *widths, uint8_t ncols, char left, char mid, char right);
void table_data_row(uint32_t row_start, uint32_t lineend, const uint8_t *widths, uint8_t ncols);
/* ================================================================== *
* Кодировки и UTF-8 конвертация (mdview2_enc.c).
* Состояние кодека владеется модулем; build_doc()/index_lines() ядра
* оркестрируют его (привязка UTF-буфера к живому file_*, сборка наборов).
* ================================================================== */
extern uint8_t g_encoding; /* активная кодировка (ENC_*) */
extern uint8_t g_utf_building; /* 1 пока UTF-конвертация не дошла до конца исходника */
extern uint8_t utf_avail; /* 1 = страницы UTF-8 выделены */
extern uint8_t utf_blk, utf_pages; /* дескриптор EMM-буфера UTF-набора */
extern uint8_t utf_phys[]; /* физ. страницы UTF-буфера */
extern uint32_t utf_size; /* размер сконвертированного UTF-потока */
/* Ядро-owned состояние, читаемое кодеком (определено в mdview2.c). */
extern uint8_t cur_page; /* текущая страница файла в W3 (инвалидация маппинга) */
extern uint8_t orig_file_phys[]; /* физ. страницы снимка исходного файла */
extern uint8_t orig_file_pages;
extern uint32_t orig_file_size;
void set_encoding(uint8_t enc); /* активная кодировка + таблица ремапа */
const char *enc_name(uint8_t enc); /* короткое имя для статус-бара */
uint8_t detect_encoding(void); /* автоопределение по сэмплу байт */
void win_rest_remap(uint8_t row, uint8_t w, uint8_t page, uint16_t off); /* вывод среза с ремапом */
void utf_convert_more(uint32_t target); /* инкрементальная конвертация до target байт */
uint8_t utf_alloc(void); /* выделить EMM-страницы под UTF-набор; 0 = нет EMM */
#endif /* MDVIEW2_H */