Compare commits
4 Commits
e1450ba7b4
...
e5866d6ba4
| Author | SHA1 | Date | |
|---|---|---|---|
| e5866d6ba4 | |||
| adf667c087 | |||
| ffd179e064 | |||
| 961cfb786d |
@@ -34,6 +34,9 @@
|
|||||||
# --bank N=FILE.c compile FILE.c as bank N; repeatable; pulls crt0_banked
|
# --bank N=FILE.c compile FILE.c as bank N; repeatable; pulls crt0_banked
|
||||||
# automatically and adds -Wl-b_BANKN=0x{N}C000
|
# automatically and adds -Wl-b_BANKN=0x{N}C000
|
||||||
# --mkexe FLAG extra mkexe flag (repeatable; e.g. --mkexe -p --mkexe 0)
|
# --mkexe FLAG extra mkexe flag (repeatable; e.g. --mkexe -p --mkexe 0)
|
||||||
|
# --max-allocs N SDCC --max-allocs-per-node (default: SDCC's 3000).
|
||||||
|
# Higher values give smaller/faster code at the cost of
|
||||||
|
# compile time; 10000..100000 is a reasonable range.
|
||||||
# --debug enable runtime diagnostics — defines DEBUG_RT for both
|
# --debug enable runtime diagnostics — defines DEBUG_RT for both
|
||||||
# sdcc (-DDEBUG_RT) and the crt0 assembly (prepended
|
# sdcc (-DDEBUG_RT) and the crt0 assembly (prepended
|
||||||
# `DEBUG_RT = 1`). Exposes runtime introspection symbols
|
# `DEBUG_RT = 1`). Exposes runtime introspection symbols
|
||||||
@@ -82,6 +85,7 @@ SOURCES=()
|
|||||||
LD_EXTRA=()
|
LD_EXTRA=()
|
||||||
MKEXE_EXTRA=()
|
MKEXE_EXTRA=()
|
||||||
BANK_SPECS=() # entries like "1=engine.c"
|
BANK_SPECS=() # entries like "1=engine.c"
|
||||||
|
MAX_ALLOCS="" # if set, passed to sdcc as --max-allocs-per-node
|
||||||
|
|
||||||
# ------- Parse args ----------------------------------------------------------
|
# ------- Parse args ----------------------------------------------------------
|
||||||
usage() {
|
usage() {
|
||||||
@@ -105,6 +109,7 @@ while [[ $# -gt 0 ]]; do
|
|||||||
-Wl) LD_EXTRA+=("$2"); shift 2;;
|
-Wl) LD_EXTRA+=("$2"); shift 2;;
|
||||||
--bank) BANK_SPECS+=("$2"); shift 2;;
|
--bank) BANK_SPECS+=("$2"); shift 2;;
|
||||||
--mkexe) MKEXE_EXTRA+=("$2"); shift 2;;
|
--mkexe) MKEXE_EXTRA+=("$2"); shift 2;;
|
||||||
|
--max-allocs) MAX_ALLOCS="$2"; shift 2;;
|
||||||
--debug) DEBUG_RT=1; shift;;
|
--debug) DEBUG_RT=1; shift;;
|
||||||
-v) VERBOSE=1; shift;;
|
-v) VERBOSE=1; shift;;
|
||||||
-h|--help) usage 0;;
|
-h|--help) usage 0;;
|
||||||
@@ -272,6 +277,7 @@ run "$SDASZ80" -o "$HEAP_TOP_REL" "$HEAP_TOP_SRC"
|
|||||||
USER_RELS=()
|
USER_RELS=()
|
||||||
CC_FLAGS=(-mz80 --no-std-crt0 --std-c99 --opt-code-size -I "$INC_DIR" "${USER_INCS[@]}")
|
CC_FLAGS=(-mz80 --no-std-crt0 --std-c99 --opt-code-size -I "$INC_DIR" "${USER_INCS[@]}")
|
||||||
[[ $DEBUG_RT -eq 1 ]] && CC_FLAGS+=(-DDEBUG_RT)
|
[[ $DEBUG_RT -eq 1 ]] && CC_FLAGS+=(-DDEBUG_RT)
|
||||||
|
[[ -n "$MAX_ALLOCS" ]] && CC_FLAGS+=(--max-allocs-per-node "$MAX_ALLOCS")
|
||||||
for src in "${SOURCES[@]}"; do
|
for src in "${SOURCES[@]}"; do
|
||||||
rel="$WORK/$(basename "$src" .c).rel"
|
rel="$WORK/$(basename "$src" .c).rel"
|
||||||
run "$SDCC" "${CC_FLAGS[@]}" -c -o "$rel" "$src"
|
run "$SDCC" "${CC_FLAGS[@]}" -c -o "$rel" "$src"
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
|||||||
EXAMPLE := mdview2
|
EXAMPLE := mdview2
|
||||||
EXTRA_SRCS := mdview2_raw.c mdview2_help.c mdview2_table.c mdview2_enc.c mdview2_md.c mdview2_status.c mdview2_index.c
|
EXTRA_SRCS := mdview2_raw.c mdview2_help.c mdview2_table.c mdview2_enc.c mdview2_md.c mdview2_status.c mdview2_index.c
|
||||||
MEMORY := small
|
MEMORY := small
|
||||||
|
# --max-allocs 100000: агрессивнее аллокация регистров SDCC — минус ~850 байт
|
||||||
|
# кода на всём приложении (замер 2026-07-04) ценой более долгой компиляции.
|
||||||
|
EXTRA_FLAGS := --max-allocs 100000
|
||||||
|
|
||||||
include $(PROJ_ROOT)/app.mk
|
include $(PROJ_ROOT)/app.mk
|
||||||
|
|
||||||
|
|||||||
+99
-39
@@ -101,15 +101,15 @@ static const uint8_t md_pallete[16 * 4] = {
|
|||||||
/* MAX_CACHE_LINE_LEN — в mdview2.h (буфер ячеек g_cells живёт в mdview2_index.c). */
|
/* MAX_CACHE_LINE_LEN — в mdview2.h (буфер ячеек g_cells живёт в mdview2_index.c). */
|
||||||
#define MAX_CACHE_CONTENT_PAGES 40u /* кап контента на набор: 40 * 16 КБ = 640 КБ (80 на оба набора) */
|
#define MAX_CACHE_CONTENT_PAGES 40u /* кап контента на набор: 40 * 16 КБ = 640 КБ (80 на оба набора) */
|
||||||
|
|
||||||
static uint8_t cache_dir_blk = 0;
|
static uint8_t cache_dir_blk;
|
||||||
static uint8_t cache_dir_pages = 0;
|
static uint8_t cache_dir_pages;
|
||||||
static uint8_t cache_dir_phys[MAX_CACHE_DIR_PAGES] = {0};
|
static uint8_t cache_dir_phys[MAX_CACHE_DIR_PAGES];
|
||||||
|
|
||||||
static uint8_t cache_content_blk[MAX_CACHE_CONTENT_PAGES] = {0}; /* id блока на каждую выделенную страницу */
|
static uint8_t cache_content_blk[MAX_CACHE_CONTENT_PAGES]; /* id блока на каждую выделенную страницу */
|
||||||
static uint8_t cache_content_phys[MAX_CACHE_CONTENT_PAGES] = {0};
|
static uint8_t cache_content_phys[MAX_CACHE_CONTENT_PAGES];
|
||||||
static uint8_t cache_content_n_pages = 0; /* сколько страниц уже выделено */
|
static uint8_t cache_content_n_pages; /* сколько страниц уже выделено */
|
||||||
static uint8_t cache_cur_page_idx = 0; /* индекс текущей страницы записи в cache_content_phys */
|
static uint8_t cache_cur_page_idx; /* индекс текущей страницы записи в cache_content_phys */
|
||||||
static uint16_t cache_cur_off = 0; /* текущее смещение записи в этой странице */
|
static uint16_t cache_cur_off; /* текущее смещение записи в этой странице */
|
||||||
|
|
||||||
/* Доступ к директории рендер-кэша — тот же приём, что у idx_get/idx_put. */
|
/* Доступ к директории рендер-кэша — тот же приём, что у idx_get/idx_put. */
|
||||||
void cache_dir_get(uint16_t idx, cache_rec_t *r)
|
void cache_dir_get(uint16_t idx, cache_rec_t *r)
|
||||||
@@ -175,41 +175,41 @@ void cache_commit(uint8_t page, uint16_t off, const void *buf, uint16_t len)
|
|||||||
/* emph_to_attr определена ниже (рядом с inline-логикой), нужна в inline_scan. */
|
/* emph_to_attr определена ниже (рядом с inline-логикой), нужна в inline_scan. */
|
||||||
/* emph_to_attr — в mdview2_index.c. */
|
/* emph_to_attr — в mdview2_index.c. */
|
||||||
|
|
||||||
uint16_t n_lines = 0; /* extern: всего строк в индексе текущего набора */
|
uint16_t n_lines; /* extern: всего строк в индексе текущего набора */
|
||||||
uint16_t max_lines = 0; /* extern: ёмкость индекса: index_pages * 2048 */
|
uint16_t max_lines; /* extern: ёмкость индекса: index_pages * 2048 */
|
||||||
uint16_t top_line = 0; /* extern: верхняя видимая строка */
|
uint16_t top_line; /* extern: верхняя видимая строка */
|
||||||
uint32_t file_size = 0; /* extern: размер активного буфера документа */
|
uint32_t file_size; /* extern: размер активного буфера документа */
|
||||||
static uint8_t file_blk = 0;
|
static uint8_t file_blk;
|
||||||
static uint8_t file_pages = 0;
|
static uint8_t file_pages;
|
||||||
static uint8_t file_phys[MAX_PAGES] = {0};
|
static uint8_t file_phys[MAX_PAGES];
|
||||||
uint8_t index_blk = 0; /* extern: дескриптор EMM-блока индекса */
|
uint8_t index_blk; /* extern: дескриптор EMM-блока индекса */
|
||||||
uint8_t index_pages = 0; /* */
|
uint8_t index_pages; /* */
|
||||||
uint8_t index_phys[MAX_INDEX_PAGES] = {0}; /* extern: физ. страницы индекса */
|
uint8_t index_phys[MAX_INDEX_PAGES]; /* extern: физ. страницы индекса */
|
||||||
uint8_t index_truncated = 0; /* выставляется при исчерпании ёмкости индекса */
|
uint8_t index_truncated; /* выставляется при исчерпании ёмкости индекса */
|
||||||
uint8_t g_file_clamped = 0; /* extern: файл >256 КБ обрезан до MAX_FILE при загрузке */
|
uint8_t g_file_clamped; /* extern: файл >256 КБ обрезан до MAX_FILE при загрузке */
|
||||||
|
|
||||||
/* cur_seg_off/cur_rec (near-состояние индексатора) — в mdview2_index.c. */
|
/* cur_seg_off/cur_rec (near-состояние индексатора) — в mdview2_index.c. */
|
||||||
uint8_t cur_page = 0xFF; /* extern: 0xFF = в W3 ещё не показана ни одна страница (extern: кодек инвалидирует) */
|
uint8_t cur_page = 0xFF; /* extern: 0xFF = в W3 ещё не показана ни одна страница (extern: кодек инвалидирует) */
|
||||||
uint8_t viewport_x = 0; /* extern: горизонтальный сдвиг для nowrap-строк */
|
uint8_t viewport_x; /* extern: горизонтальный сдвиг для nowrap-строк */
|
||||||
char filename[64] = {0}; /* extern: статус-бар */
|
char filename[64]; /* extern: статус-бар */
|
||||||
|
|
||||||
/* Прогрессивная загрузка: рисуем первый экран и пускаем скролл, пока
|
/* Прогрессивная загрузка: рисуем первый экран и пускаем скролл, пока
|
||||||
* index_lines() ещё досканирует файл (кооперативно, без прерываний). */
|
* index_lines() ещё досканирует файл (кооперативно, без прерываний). */
|
||||||
uint8_t g_view = VIEW_MD; /* extern: активный режим просмотра (существует даже когда доступен только MD режим) */
|
uint8_t g_view = VIEW_MD; /* extern: активный режим просмотра (существует даже когда доступен только MD режим) */
|
||||||
#if WITH_RAW
|
#if WITH_RAW
|
||||||
uint8_t g_ready = 0; /* extern: 1 когда первичный документ построен — F2 (RAW) рабочий */
|
uint8_t g_ready; /* extern: 1 когда первичный документ построен — F2 (RAW) рабочий */
|
||||||
static uint8_t g_raw_last = VIEW_RAW_WRAP; /* последний под-режим RAW (возврат по F2) */
|
static uint8_t g_raw_last = VIEW_RAW_WRAP; /* последний под-режим RAW (возврат по F2) */
|
||||||
#endif
|
#endif
|
||||||
uint8_t g_loading = 0; /* extern: 1 во время index_lines() */
|
uint8_t g_loading; /* extern: 1 во время index_lines() */
|
||||||
static uint8_t g_first_drawn = 0; /* первый экран уже нарисован */
|
static uint8_t g_first_drawn; /* первый экран уже нарисован */
|
||||||
uint8_t g_f8_enabled = 0; /* extern: можно ли сейчас переключать кодировку */
|
uint8_t g_f8_enabled; /* extern: можно ли сейчас переключать кодировку */
|
||||||
static uint8_t g_utf_failed = 0; /* UTF-8 набор не удалось построить (нет EMM) — пропускаем UTF-8 в цикле F8 */
|
static uint8_t g_utf_failed; /* UTF-8 набор не удалось построить (нет EMM) — пропускаем UTF-8 в цикле F8 */
|
||||||
uint8_t g_abort = 0; /* extern: F10/Esc во время загрузки → прервать индексацию */
|
uint8_t g_abort; /* extern: F10/Esc во время загрузки → прервать индексацию */
|
||||||
static uint16_t g_last_prog = 0; /* n_lines на последнем обновлении статуса */
|
static uint16_t g_last_prog; /* n_lines на последнем обновлении статуса */
|
||||||
|
|
||||||
/* Кодировки (ENC_*) и g_encoding — в mdview2.h / mdview2_enc.c. */
|
/* Кодировки (ENC_*) и g_encoding — в mdview2.h / mdview2_enc.c. */
|
||||||
const uint8_t *g_remap = 0; /* extern: активная таблица ремапа (CP1251/KOI8), 0 = без ремапа (CP866)*/
|
const uint8_t *g_remap; /* extern: активная таблица ремапа (CP1251/KOI8), 0 = без ремапа (CP866)*/
|
||||||
uint8_t g_scratch_phys = 0; /* extern: EMM-страница под ремап-буфер строки для win_rest */
|
uint8_t g_scratch_phys; /* extern: EMM-страница под ремап-буфер строки для win_rest */
|
||||||
|
|
||||||
/* UTF-8 набор (utf_blk/utf_pages/utf_phys/utf_size/utf_avail, конвертер и его
|
/* UTF-8 набор (utf_blk/utf_pages/utf_phys/utf_size/utf_avail, конвертер и его
|
||||||
* состояние) — в mdview2_enc.c; разделяемые символы и API — в mdview2.h.
|
* состояние) — в mdview2_enc.c; разделяемые символы и API — в mdview2.h.
|
||||||
@@ -218,10 +218,10 @@ uint8_t g_scratch_phys = 0; /* extern: EMM-страни
|
|||||||
|
|
||||||
/* Снимок исходного (8-битного) файлового буфера — набор DOC_8BIT читает его
|
/* Снимок исходного (8-битного) файлового буфера — набор DOC_8BIT читает его
|
||||||
* как есть, набор DOC_UTF8 читает utf_phys. */
|
* как есть, набор DOC_UTF8 читает utf_phys. */
|
||||||
static uint8_t orig_file_blk = 0;
|
static uint8_t orig_file_blk;
|
||||||
uint8_t orig_file_pages = 0; /* extern: читает кодек (utf_alloc) */
|
uint8_t orig_file_pages; /* extern: читает кодек (utf_alloc) */
|
||||||
uint32_t orig_file_size = 0; /* extern: читает кодек (utf_convert_more) */
|
uint32_t orig_file_size; /* extern: читает кодек (utf_convert_more) */
|
||||||
uint8_t orig_file_phys[MAX_PAGES] = {0}; /* extern: читает кодек (cv_read) */
|
uint8_t orig_file_phys[MAX_PAGES]; /* extern: читает кодек (cv_read) */
|
||||||
|
|
||||||
/* ----------------- Два сосуществующих набора индекс/кэш -----------------
|
/* ----------------- Два сосуществующих набора индекс/кэш -----------------
|
||||||
* DOC_8BIT — исходные байты (CP866/CP1251/KOI8R, различаются ремапом на
|
* DOC_8BIT — исходные байты (CP866/CP1251/KOI8R, различаются ремапом на
|
||||||
@@ -250,9 +250,9 @@ typedef struct {
|
|||||||
|
|
||||||
#define DOC_8BIT 0
|
#define DOC_8BIT 0
|
||||||
#define DOC_UTF8 1
|
#define DOC_UTF8 1
|
||||||
static docset_t g_doc[2] = {{0}, {0}}; /* ОБЯЗАТЕЛЬНО инициализировать: built=0 для обоих
|
static docset_t g_doc[2]; /* built=0 для обоих: _DATA зануляется в crt0 gsinit
|
||||||
* (SDCC z80 не обнуляет неинициализированные статики
|
* (с 2026-07-04), явный {0}-инициализатор больше
|
||||||
* надёжно — см. memory/sdcc_static_storage_gotcha) */
|
* не нужен — он стоил ~260 байт _INITIALIZER */
|
||||||
static uint8_t g_active_doc = DOC_8BIT; /* какой набор сейчас в живых глобалах */
|
static uint8_t g_active_doc = DOC_8BIT; /* какой набор сейчас в живых глобалах */
|
||||||
static uint8_t g_disp_doc = DOC_8BIT; /* какой набор показан пользователю */
|
static uint8_t g_disp_doc = DOC_8BIT; /* какой набор показан пользователю */
|
||||||
|
|
||||||
@@ -826,6 +826,43 @@ static int build_doc(uint8_t slot, uint8_t enc, uint8_t visible)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Пропорциональный перенос байт-позиции между буферами разного размера
|
||||||
|
* (8bit ↔ UTF-8 при F8): off * to / from БЕЗ __mullong/__divulong. Один
|
||||||
|
* цикл restoring-деления: бит k дроби off/from (сравнение с from/2^k)
|
||||||
|
* сразу добавляет to/2^k в результат — сумма to/2^k по установленным
|
||||||
|
* битам и есть off*to/from. Точность from/65536 (≤4 байта на максимальном
|
||||||
|
* файле); ляжет на границу строки через line_at_off / raw_reanchor. */
|
||||||
|
static uint32_t map_off(uint32_t off, uint32_t from, uint32_t to)
|
||||||
|
{
|
||||||
|
if (from == to || from == 0) return off;
|
||||||
|
uint32_t res = 0;
|
||||||
|
for (uint8_t i = 0; i < 16; i++) {
|
||||||
|
from >>= 1;
|
||||||
|
to >>= 1;
|
||||||
|
if (off >= from) { off -= from; res += to; }
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Байт-позиция текущего вида (верх экрана) — единая «валюта» для переноса
|
||||||
|
* позиции между видами/наборами. */
|
||||||
|
static uint32_t view_pos(void)
|
||||||
|
{
|
||||||
|
#if WITH_RAW
|
||||||
|
if (g_view != VIEW_MD) return raw_pos();
|
||||||
|
#endif
|
||||||
|
return seg_off(top_line);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Поставить текущий вид на байт-позицию off (после doc_switch). */
|
||||||
|
static void view_reanchor(uint32_t off)
|
||||||
|
{
|
||||||
|
#if WITH_RAW
|
||||||
|
if (g_view != VIEW_MD) { raw_reanchor(off); return; }
|
||||||
|
#endif
|
||||||
|
top_line = line_at_off(off);
|
||||||
|
}
|
||||||
|
|
||||||
/* Переключает кодировку на `next` (вызывается из главного цикла по F8).
|
/* Переключает кодировку на `next` (вызывается из главного цикла по F8).
|
||||||
* 8-бит↔8-бит внутри одного набора — мгновенный ремап; переход в другой набор
|
* 8-бит↔8-бит внутри одного набора — мгновенный ремап; переход в другой набор
|
||||||
* — мгновенный doc_switch, если он построен, иначе ЛЕНИВАЯ сборка по месту
|
* — мгновенный doc_switch, если он построен, иначе ЛЕНИВАЯ сборка по месту
|
||||||
@@ -842,13 +879,22 @@ static void switch_encoding(uint8_t next)
|
|||||||
set_encoding(next);
|
set_encoding(next);
|
||||||
g_doc[target].enc = next;
|
g_doc[target].enc = next;
|
||||||
} else if (g_doc[target].built) { /* набор готов — мгновенный свап */
|
} else if (g_doc[target].built) { /* набор готов — мгновенный свап */
|
||||||
|
/* Позицию переносим пропорцией байтов (буферы наборов разного
|
||||||
|
* размера), а не из сохранённой в g_doc копии — иначе каждый набор
|
||||||
|
* скачет по своей старой позиции. */
|
||||||
|
uint32_t old_off = view_pos();
|
||||||
|
uint32_t old_size = file_size;
|
||||||
doc_switch(target);
|
doc_switch(target);
|
||||||
g_disp_doc = target;
|
g_disp_doc = target;
|
||||||
set_encoding(next);
|
set_encoding(next);
|
||||||
g_doc[target].enc = next;
|
g_doc[target].enc = next;
|
||||||
|
view_reanchor(map_off(old_off, old_size, file_size));
|
||||||
} else { /* строим набор лениво */
|
} else { /* строим набор лениво */
|
||||||
doc_save_live(g_active_doc); /* сохранить текущий (позиция и т.п.) */
|
doc_save_live(g_active_doc); /* сохранить текущий (позиция и т.п.) */
|
||||||
#if WITH_RAW
|
#if WITH_RAW
|
||||||
|
uint32_t raw_save = raw_pos();
|
||||||
|
if (g_view != VIEW_MD)
|
||||||
|
raw_reanchor(0); /* прогрессивный показ нового буфера — с начала */
|
||||||
g_ready = 0;
|
g_ready = 0;
|
||||||
render_menu(); /* идёт индексация — прячем F2 */
|
render_menu(); /* идёт индексация — прячем F2 */
|
||||||
#endif
|
#endif
|
||||||
@@ -864,6 +910,10 @@ static void switch_encoding(uint8_t next)
|
|||||||
if (target == DOC_UTF8)
|
if (target == DOC_UTF8)
|
||||||
g_utf_failed = 1;
|
g_utf_failed = 1;
|
||||||
doc_load_live(g_active_doc);
|
doc_load_live(g_active_doc);
|
||||||
|
#if WITH_RAW
|
||||||
|
if (g_view != VIEW_MD)
|
||||||
|
raw_reanchor(raw_save); /* буфер прежний — вернуть позицию RAW */
|
||||||
|
#endif
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
g_disp_doc = target; /* build_doc уже выставил active/enc */
|
g_disp_doc = target; /* build_doc уже выставил active/enc */
|
||||||
@@ -1036,6 +1086,7 @@ int main(int argc, char **argv)
|
|||||||
} else {
|
} else {
|
||||||
g_raw_last = g_view; /* запомнить под-режим RAW */
|
g_raw_last = g_view; /* запомнить под-режим RAW */
|
||||||
g_view = VIEW_MD;
|
g_view = VIEW_MD;
|
||||||
|
top_line = line_at_off(raw_pos()); /* позиция из RAW */
|
||||||
clamp_top();
|
clamp_top();
|
||||||
render_full_status(); render_menu();
|
render_full_status(); render_menu();
|
||||||
draw_viewport_from_cache();
|
draw_viewport_from_cache();
|
||||||
@@ -1052,7 +1103,16 @@ int main(int argc, char **argv)
|
|||||||
switch_encoding(next);
|
switch_encoding(next);
|
||||||
if (g_abort)
|
if (g_abort)
|
||||||
exit(0); /* Esc/F10 во время ленивой досборки */
|
exit(0); /* Esc/F10 во время ленивой досборки */
|
||||||
render_full_status(); /* перерисовка — в MD-режиме */
|
/* Перерисовка — по текущему виду (switch_encoding сам не рисует). */
|
||||||
|
#if WITH_RAW
|
||||||
|
if (g_view != VIEW_MD) {
|
||||||
|
status_encoding();
|
||||||
|
render_raw_status();
|
||||||
|
raw_draw();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
render_full_status();
|
||||||
draw_viewport_from_cache();
|
draw_viewport_from_cache();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -139,12 +139,14 @@ void win_rest(uint8_t row, uint8_t col, uint8_t h, uint8_t w, uint8_t page,
|
|||||||
void fill_row(uint8_t y, uint8_t attr);
|
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 */
|
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 */
|
uint32_t seg_off(uint16_t idx); /* offset исходника для логической строки idx */
|
||||||
|
uint16_t line_at_off(uint32_t off); /* обратно: последняя строка с seg_off <= off */
|
||||||
|
|
||||||
/* ================================================================== *
|
/* ================================================================== *
|
||||||
* API модуля RAW (mdview2_raw.c).
|
* API модуля RAW (mdview2_raw.c).
|
||||||
* ================================================================== */
|
* ================================================================== */
|
||||||
void raw_seed_from(uint16_t md_top_line); /* вход в RAW: позиция по top_line */
|
void raw_seed_from(uint16_t md_top_line); /* вход в RAW: позиция по top_line */
|
||||||
void raw_home(void); /* сброс в начало (смена буфера при F8 в/из UTF-8) */
|
uint32_t raw_pos(void); /* текущий байт-offset верха экрана RAW */
|
||||||
|
void raw_reanchor(uint32_t off); /* поставить RAW на строку с байтом off */
|
||||||
void raw_draw(void); /* перерисовать область документа в RAW */
|
void raw_draw(void); /* перерисовать область документа в RAW */
|
||||||
uint8_t raw_pct(void); /* % прокрутки по байтам (для render_raw_status_numbers) */
|
uint8_t raw_pct(void); /* % прокрутки по байтам (для render_raw_status_numbers) */
|
||||||
uint8_t raw_key(uint8_t scan); /* навигация RAW; 1 = обработано */
|
uint8_t raw_key(uint8_t scan); /* навигация RAW; 1 = обработано */
|
||||||
|
|||||||
@@ -23,16 +23,16 @@
|
|||||||
/* ---- Состояние кодировки/UTF (владелец — этот модуль) ------------- */
|
/* ---- Состояние кодировки/UTF (владелец — этот модуль) ------------- */
|
||||||
uint8_t g_encoding = ENC_CP866; /* активная кодировка */
|
uint8_t g_encoding = ENC_CP866; /* активная кодировка */
|
||||||
|
|
||||||
uint8_t utf_blk = 0;
|
uint8_t utf_blk;
|
||||||
uint8_t utf_pages = 0;
|
uint8_t utf_pages;
|
||||||
uint8_t utf_phys[MAX_PAGES] = {0};
|
uint8_t utf_phys[MAX_PAGES];
|
||||||
uint32_t utf_size = 0;
|
uint32_t utf_size;
|
||||||
uint8_t utf_avail = 0; /* 1 = страницы UTF-8 выделены */
|
uint8_t utf_avail; /* 1 = страницы UTF-8 выделены */
|
||||||
uint8_t g_utf_building = 0; /* 1 пока конвертация не дошла до конца исходника */
|
uint8_t g_utf_building; /* 1 пока конвертация не дошла до конца исходника */
|
||||||
|
|
||||||
/* Приватное состояние конвертера (наружу не торчит — сброс через
|
/* Приватное состояние конвертера (наружу не торчит — сброс через
|
||||||
* utf_conv_reset(), чтение оригинала через cv_read). */
|
* utf_conv_reset(), чтение оригинала через cv_read). */
|
||||||
static uint32_t utf_src = 0; /* позиция чтения ОРИГИНАЛА (utf-8) */
|
static uint32_t utf_src; /* позиция чтения ОРИГИНАЛА (utf-8) */
|
||||||
static uint8_t cv_page = 0xFF; /* orig-страница в W3 для cv_read (своя от cur_page) */
|
static uint8_t cv_page = 0xFF; /* orig-страница в W3 для cv_read (своя от cur_page) */
|
||||||
|
|
||||||
/* Таблицы ремапа СТАРШИХ байтов [128-255] в CP866. Индексируются (ch-0x80):
|
/* Таблицы ремапа СТАРШИХ байтов [128-255] в CP866. Индексируются (ch-0x80):
|
||||||
@@ -180,9 +180,9 @@ static const uint8_t utf_cyr_to_866[96] = {
|
|||||||
/* Поток вывода конвертера: пишем в utf_phys чанками по странице, буфер в
|
/* Поток вывода конвертера: пишем в utf_phys чанками по странице, буфер в
|
||||||
* near-памяти, флаш через один bank_write (не побайтовый swap W3). */
|
* near-памяти, флаш через один bank_write (не побайтовый swap W3). */
|
||||||
static uint8_t conv_buf[256];
|
static uint8_t conv_buf[256];
|
||||||
static uint16_t conv_n = 0; /* байт в буфере, ещё не сброшено */
|
static uint16_t conv_n; /* байт в буфере, ещё не сброшено */
|
||||||
static uint8_t conv_page = 0; /* индекс текущей страницы в utf_phys */
|
static uint8_t conv_page; /* индекс текущей страницы в utf_phys */
|
||||||
static uint16_t conv_off = 0; /* уже сброшенное смещение в этой странице */
|
static uint16_t conv_off; /* уже сброшенное смещение в этой странице */
|
||||||
|
|
||||||
static void conv_flush(void)
|
static void conv_flush(void)
|
||||||
{
|
{
|
||||||
|
|||||||
+114
-155
@@ -9,17 +9,25 @@
|
|||||||
* позиции чтения. Разделяемые символы и API — в mdview2.h.
|
* позиции чтения. Разделяемые символы и API — в mdview2.h.
|
||||||
*
|
*
|
||||||
* Приватно (не торчит в .h): idx_rec_t, cur_rec/cur_seg_off, буфер ячеек
|
* Приватно (не торчит в .h): idx_rec_t, cur_rec/cur_seg_off, буфер ячеек
|
||||||
* g_cells, styles_map, idx_get/idx_put и все классификаторы/сканеры.
|
* g_cells, idx_get/idx_put и все классификаторы/сканеры.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <conio.h> /* COLOR()/COLOR_* для ATTR_* в styles_map */
|
#include <conio.h> /* COLOR()/COLOR_* для ATTR_* в сканерах */
|
||||||
#include <sprinter.h>
|
#include <sprinter.h>
|
||||||
#include <sprinter_mem.h> /* bank_read/bank_write (idx_get/idx_put) */
|
#include <sprinter_mem.h> /* bank_read/bank_write (idx_get/idx_put) */
|
||||||
#include "mdview2.h"
|
#include "mdview2.h"
|
||||||
|
|
||||||
static uint8_t emph_to_attr(uint8_t emph, uint8_t base_attr); /* forward (нужна в inline-сканере) */
|
static uint8_t emph_to_attr(uint8_t emph, uint8_t base_attr); /* forward (нужна в inline-сканере) */
|
||||||
|
static uint8_t is_hr_raw(uint32_t p); /* forward (нужна в classify_line) */
|
||||||
|
|
||||||
|
/* Начало следующей физической строки (байт после '\n', либо file_size). */
|
||||||
|
static uint32_t next_line(uint32_t p)
|
||||||
|
{
|
||||||
|
p = row_end(p);
|
||||||
|
return (p < file_size) ? p + 1 : p;
|
||||||
|
}
|
||||||
|
|
||||||
typedef struct idx_rec_s {
|
typedef struct idx_rec_s {
|
||||||
uint32_t off; /* смещение первого байта исходного текста для сегмента */
|
uint32_t off; /* смещение первого байта исходного текста для сегмента */
|
||||||
@@ -31,7 +39,7 @@ typedef struct idx_rec_s {
|
|||||||
|
|
||||||
#define INDEX_REC_SIZE 8u
|
#define INDEX_REC_SIZE 8u
|
||||||
|
|
||||||
static uint32_t cur_seg_off = 0; /* смещение последнего emit_seg (кэш в near-памяти) */
|
static uint32_t cur_seg_off; /* смещение последнего emit_seg (кэш в near-памяти) */
|
||||||
static idx_rec_t cur_rec; /* near-копия последней записанной idx-записи */
|
static idx_rec_t cur_rec; /* near-копия последней записанной idx-записи */
|
||||||
|
|
||||||
/* Для проверки границ выделения считаем пробел/таб/конец строки/0 пробельными. */
|
/* Для проверки границ выделения считаем пробел/таб/конец строки/0 пробельными. */
|
||||||
@@ -90,12 +98,23 @@ uint32_t seg_off(uint16_t idx)
|
|||||||
return r.off;
|
return r.off;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Установка флагов всегда идёт в последний emit_seg (n_lines-1).
|
/* Последняя строка индекса с исходным offset <= off — обратный перевод
|
||||||
|
* байт-позиции в MD-строку (позиционирование при F2/F8). Оффсеты сегментов
|
||||||
|
* монотонны, поэтому бинарный поиск (~15 bank-чтений). */
|
||||||
|
uint16_t line_at_off(uint32_t off)
|
||||||
|
{
|
||||||
|
uint16_t lo = 0, hi = n_lines;
|
||||||
|
while (lo < hi) {
|
||||||
|
uint16_t mid = (uint16_t)((lo + hi) >> 1);
|
||||||
|
if (seg_off(mid) <= off) lo = (uint16_t)(mid + 1);
|
||||||
|
else hi = mid;
|
||||||
|
}
|
||||||
|
return (lo > 0) ? (uint16_t)(lo - 1) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Установка флагов (маска IF_*) всегда идёт в последний emit_seg (n_lines-1).
|
||||||
* Используется near-копия cur_rec, чтобы не делать обратное чтение из банка. */
|
* Используется near-копия cur_rec, чтобы не делать обратное чтение из банка. */
|
||||||
static void set_nowrap_cur(void) { cur_rec.flags |= IF_NOWRAP; idx_put((uint16_t)(n_lines - 1), &cur_rec); }
|
static void set_cur_flags(uint8_t f) { cur_rec.flags |= f; idx_put((uint16_t)(n_lines - 1), &cur_rec); }
|
||||||
static void set_blank_cur(void) { cur_rec.flags |= IF_BLANK; idx_put((uint16_t)(n_lines - 1), &cur_rec); }
|
|
||||||
static void set_code_cur(void) { cur_rec.flags |= IF_CODE; idx_put((uint16_t)(n_lines - 1), &cur_rec); }
|
|
||||||
static void set_hscroll_cur(void){ cur_rec.flags |= IF_HSCROLL; idx_put((uint16_t)(n_lines - 1), &cur_rec); }
|
|
||||||
|
|
||||||
/* ==================================================================
|
/* ==================================================================
|
||||||
* Индексация строк
|
* Индексация строк
|
||||||
@@ -103,12 +122,6 @@ static void set_hscroll_cur(void){ cur_rec.flags |= IF_HSCROLL; idx_put((uint16_
|
|||||||
|
|
||||||
/* INIT_STYLE_* — в mdview2.h (общие с модулем таблиц). */
|
/* INIT_STYLE_* — в mdview2.h (общие с модулем таблиц). */
|
||||||
|
|
||||||
/* Таблица сопоставления init-style -> атрибут.
|
|
||||||
* Порядок должен совпадать с INIT_STYLE_* / EM_* ниже. */
|
|
||||||
static const uint8_t styles_map[] = {
|
|
||||||
ATTR_TEXT, ATTR_TEXT_BOLD, ATTR_TEXT_ITALIC, ATTR_TEXT_UNDERSORE, ATTR_TEXT_CODE, ATTR_TEXT_STRIKE
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Тип строки, который возвращает classify_line(). */
|
/* Тип строки, который возвращает classify_line(). */
|
||||||
#define LK_PLAIN 0
|
#define LK_PLAIN 0
|
||||||
#define LK_H1 1
|
#define LK_H1 1
|
||||||
@@ -157,22 +170,7 @@ static uint8_t classify_line(uint32_t p_start, uint32_t *out_content)
|
|||||||
|
|
||||||
/* Горизонтальный разделитель.
|
/* Горизонтальный разделитель.
|
||||||
* Проверяется до списков, чтобы `- - -` не стало маркером списка. */
|
* Проверяется до списков, чтобы `- - -` не стало маркером списка. */
|
||||||
if (c0 == '-' || c0 == '*' || c0 == '_') {
|
if (is_hr_raw(p_start)) return LK_HR;
|
||||||
char marker = 0;
|
|
||||||
uint8_t count = 0;
|
|
||||||
uint8_t ok = 1;
|
|
||||||
uint32_t p = p_start;
|
|
||||||
while (p < file_size) {
|
|
||||||
char ch = fb(p++);
|
|
||||||
if (ch == '\n' || ch == '\r') break;
|
|
||||||
if (ch == ' ' || ch == '\t') continue;
|
|
||||||
if (ch != '-' && ch != '*' && ch != '_') { ok = 0; break; }
|
|
||||||
if (marker == 0) marker = ch;
|
|
||||||
else if (ch != marker) { ok = 0; break; }
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
if (ok && count >= 3) return LK_HR;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Для списков/цитат дополнительно допускаем ведущие пробелы
|
/* Для списков/цитат дополнительно допускаем ведущие пробелы
|
||||||
* (базовая поддержка вложенности). */
|
* (базовая поддержка вложенности). */
|
||||||
@@ -237,15 +235,15 @@ static uint8_t marker_visible_col(uint8_t kind, uint32_t p_start, uint32_t conte
|
|||||||
* g_ncells_at_space — снимок длины на последнем пробеле для усечения буфера
|
* g_ncells_at_space — снимок длины на последнем пробеле для усечения буфера
|
||||||
* при переносе строки (отбрасываем уехавшее за границу слово). */
|
* при переносе строки (отбрасываем уехавшее за границу слово). */
|
||||||
static uint8_t g_cells[MAX_CACHE_LINE_LEN * 2];
|
static uint8_t g_cells[MAX_CACHE_LINE_LEN * 2];
|
||||||
uint8_t g_ncells = 0; /* ячеек в текущем сегменте (общий с таблицами) */
|
uint8_t g_ncells; /* ячеек в текущем сегменте (общий с таблицами) */
|
||||||
static uint8_t g_ncells_at_space = 0; /* снимок g_ncells на последнем пробеле (для усечения при переносе) */
|
static uint8_t g_ncells_at_space; /* снимок g_ncells на последнем пробеле (для усечения при переносе) */
|
||||||
static uint8_t g_skip_flush = 0; /* следующий emit_seg пропустит lag-флаш (pending уже зафлашен вручную, напр. перед таблицей) */
|
static uint8_t g_skip_flush; /* следующий emit_seg пропустит lag-флаш (pending уже зафлашен вручную, напр. перед таблицей) */
|
||||||
|
|
||||||
/* Обрыв индексации: причина (0 / TRUNC_CONTENT / TRUNC_LINES) и строка, на
|
/* Обрыв индексации: причина (0 / TRUNC_CONTENT / TRUNC_LINES) и строка, на
|
||||||
* которой упёрлись. Выставляются ОДИН раз (первая причина побеждает); по ним
|
* которой упёрлись. Выставляются ОДИН раз (первая причина побеждает); по ним
|
||||||
* index_lines() в конце пишет строку-сообщение (IF_TRUNC_MSG, рисуется вживую). */
|
* index_lines() в конце пишет строку-сообщение (IF_TRUNC_MSG, рисуется вживую). */
|
||||||
static uint8_t g_trunc_cause = 0;
|
static uint8_t g_trunc_cause;
|
||||||
static uint16_t g_trunc_line = 0;
|
static uint16_t g_trunc_line;
|
||||||
|
|
||||||
/* Пишет одну (char,attr)-ячейку в g_cells, молча игнорируя переполнение
|
/* Пишет одну (char,attr)-ячейку в g_cells, молча игнорируя переполнение
|
||||||
* MAX_CACHE_LINE_LEN. Оставлена ФУНКЦИЕЙ намеренно: инлайн в сильно
|
* MAX_CACHE_LINE_LEN. Оставлена ФУНКЦИЕЙ намеренно: инлайн в сильно
|
||||||
@@ -383,17 +381,18 @@ static uint8_t is_table_raw(uint32_t p)
|
|||||||
* маркер обработан (вызывающий делает continue), 0 — символ обычный (пробел/
|
* маркер обработан (вызывающий делает continue), 0 — символ обычный (пробел/
|
||||||
* текст), его кладёт сам вызывающий (и проверяет перенос). Меняет
|
* текст), его кладёт сам вызывающий (и проверяет перенос). Меняет
|
||||||
* *pq/*pls/*pseg/*pprev. attr берётся как emph_to_attr(line_style, base) —
|
* *pq/*pls/*pseg/*pprev. attr берётся как emph_to_attr(line_style, base) —
|
||||||
* для join base=ATTR_TEXT, что тождественно styles_map[line_style]. */
|
* для join base=ATTR_TEXT (атрибут «обычного» текста). */
|
||||||
static uint8_t inline_marker(uint32_t *pq, uint32_t bound, uint8_t *pls,
|
static uint8_t inline_marker(uint32_t *pq, uint32_t bound, uint8_t *pls,
|
||||||
uint8_t *pseg, char *pprev, uint8_t base)
|
uint8_t *pseg, char *pprev, uint8_t base)
|
||||||
{
|
{
|
||||||
uint32_t q = *pq;
|
uint32_t q = *pq;
|
||||||
uint8_t ls = *pls;
|
uint8_t ls = *pls;
|
||||||
char ch = fb(q);
|
char ch = fb(q);
|
||||||
|
uint8_t at = emph_to_attr(ls, base); /* атрибут литералов текущего стиля */
|
||||||
|
|
||||||
if (ls != INIT_STYLE_CODE && ch == '\\' && (q + 1) < bound && is_escapable(fb(q + 1))) {
|
if (ls != INIT_STYLE_CODE && ch == '\\' && (q + 1) < bound && is_escapable(fb(q + 1))) {
|
||||||
char nb = fb(q + 1);
|
char nb = fb(q + 1);
|
||||||
gc_put(nb, emph_to_attr(ls, base));
|
gc_put(nb, at);
|
||||||
*pseg += 1; *pq = q + 2; *pprev = nb; return 1;
|
*pseg += 1; *pq = q + 2; *pprev = nb; return 1;
|
||||||
}
|
}
|
||||||
if (ls != INIT_STYLE_CODE && ch == '[' && (q + 2) < bound && fb(q + 2) == ']') {
|
if (ls != INIT_STYLE_CODE && ch == '[' && (q + 2) < bound && fb(q + 2) == ']') {
|
||||||
@@ -403,7 +402,7 @@ static uint8_t inline_marker(uint32_t *pq, uint32_t bound, uint8_t *pls,
|
|||||||
if (ch == '`') {
|
if (ch == '`') {
|
||||||
if (ls == INIT_STYLE_PLAIN) { *pls = INIT_STYLE_CODE; *pq = q + 1; return 1; }
|
if (ls == INIT_STYLE_PLAIN) { *pls = INIT_STYLE_CODE; *pq = q + 1; return 1; }
|
||||||
if (ls == INIT_STYLE_CODE) { *pls = INIT_STYLE_PLAIN; *pq = q + 1; return 1; }
|
if (ls == INIT_STYLE_CODE) { *pls = INIT_STYLE_PLAIN; *pq = q + 1; return 1; }
|
||||||
gc_put('`', emph_to_attr(ls, base)); *pseg += 1; *pq = q + 1; *pprev = '`'; return 1;
|
gc_put('`', at); *pseg += 1; *pq = q + 1; *pprev = '`'; return 1;
|
||||||
}
|
}
|
||||||
if (ch == '*' && (q + 1) < bound && fb(q + 1) == '*') {
|
if (ch == '*' && (q + 1) < bound && fb(q + 1) == '*') {
|
||||||
char nx = ((q + 2) < bound) ? fb(q + 2) : '\n';
|
char nx = ((q + 2) < bound) ? fb(q + 2) : '\n';
|
||||||
@@ -411,7 +410,7 @@ static uint8_t inline_marker(uint32_t *pq, uint32_t bound, uint8_t *pls,
|
|||||||
if (ls == INIT_STYLE_PLAIN && ws_or_eol(*pprev)) { *pls = INIT_STYLE_BOLD; *pq = q + 2; return 1; }
|
if (ls == INIT_STYLE_PLAIN && ws_or_eol(*pprev)) { *pls = INIT_STYLE_BOLD; *pq = q + 2; return 1; }
|
||||||
if (ls == INIT_STYLE_BOLD && ws_or_eol_or_delim(nx)) { *pls = INIT_STYLE_PLAIN; *pq = q + 2; return 1; }
|
if (ls == INIT_STYLE_BOLD && ws_or_eol_or_delim(nx)) { *pls = INIT_STYLE_PLAIN; *pq = q + 2; return 1; }
|
||||||
}
|
}
|
||||||
gc_put('*', emph_to_attr(ls, base)); gc_put('*', emph_to_attr(ls, base));
|
gc_put('*', at); gc_put('*', at);
|
||||||
*pseg += 2; *pq = q + 2; *pprev = '*'; return 1;
|
*pseg += 2; *pq = q + 2; *pprev = '*'; return 1;
|
||||||
}
|
}
|
||||||
if (ch == '~' && (q + 1) < bound && fb(q + 1) == '~') {
|
if (ch == '~' && (q + 1) < bound && fb(q + 1) == '~') {
|
||||||
@@ -420,7 +419,7 @@ static uint8_t inline_marker(uint32_t *pq, uint32_t bound, uint8_t *pls,
|
|||||||
if (ls == INIT_STYLE_PLAIN && ws_or_eol(*pprev)) { *pls = INIT_STYLE_STRIKE; *pq = q + 2; return 1; }
|
if (ls == INIT_STYLE_PLAIN && ws_or_eol(*pprev)) { *pls = INIT_STYLE_STRIKE; *pq = q + 2; return 1; }
|
||||||
if (ls == INIT_STYLE_STRIKE && ws_or_eol_or_delim(nx)) { *pls = INIT_STYLE_PLAIN; *pq = q + 2; return 1; }
|
if (ls == INIT_STYLE_STRIKE && ws_or_eol_or_delim(nx)) { *pls = INIT_STYLE_PLAIN; *pq = q + 2; return 1; }
|
||||||
}
|
}
|
||||||
gc_put('~', emph_to_attr(ls, base)); gc_put('~', emph_to_attr(ls, base));
|
gc_put('~', at); gc_put('~', at);
|
||||||
*pseg += 2; *pq = q + 2; *pprev = '~'; return 1;
|
*pseg += 2; *pq = q + 2; *pprev = '~'; return 1;
|
||||||
}
|
}
|
||||||
if (ch == '*' || ch == '_') {
|
if (ch == '*' || ch == '_') {
|
||||||
@@ -430,99 +429,57 @@ static uint8_t inline_marker(uint32_t *pq, uint32_t bound, uint8_t *pls,
|
|||||||
if (ls == INIT_STYLE_PLAIN && ws_or_eol(*pprev)) { *pls = ns; *pq = q + 1; return 1; }
|
if (ls == INIT_STYLE_PLAIN && ws_or_eol(*pprev)) { *pls = ns; *pq = q + 1; return 1; }
|
||||||
if (ls != INIT_STYLE_PLAIN && ws_or_eol_or_delim(nx)) { *pls = INIT_STYLE_PLAIN; *pq = q + 1; return 1; }
|
if (ls != INIT_STYLE_PLAIN && ws_or_eol_or_delim(nx)) { *pls = INIT_STYLE_PLAIN; *pq = q + 1; return 1; }
|
||||||
}
|
}
|
||||||
gc_put(ch, emph_to_attr(ls, base)); *pseg += 1; *pq = q + 1; *pprev = ch; return 1;
|
gc_put(ch, at); *pseg += 1; *pq = q + 1; *pprev = ch; return 1;
|
||||||
}
|
}
|
||||||
if (ch == '\t') {
|
if (ch == '\t') {
|
||||||
uint8_t old_sc = *pseg;
|
uint8_t old_sc = *pseg;
|
||||||
uint8_t tgt_full = (uint8_t)((old_sc & (uint8_t)~(TAB_STOP - 1)) + TAB_STOP);
|
uint8_t tgt_full = (uint8_t)((old_sc & (uint8_t)~(TAB_STOP - 1)) + TAB_STOP);
|
||||||
uint8_t tgt = tgt_full; if (tgt > SCREEN_W) tgt = SCREEN_W;
|
uint8_t tgt = tgt_full; if (tgt > SCREEN_W) tgt = SCREEN_W;
|
||||||
gc_fill(' ', emph_to_attr(ls, base), (uint8_t)(tgt_full - old_sc));
|
gc_fill(' ', at, (uint8_t)(tgt_full - old_sc));
|
||||||
*pseg = tgt; *pq = q + 1; *pprev = ' '; return 1;
|
*pseg = tgt; *pq = q + 1; *pprev = ' '; return 1;
|
||||||
}
|
}
|
||||||
return 0; /* пробел или обычный символ — обрабатывает вызывающий */
|
return 0; /* пробел или обычный символ — обрабатывает вызывающий */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Общий сканер inline-форматирования и переносов.
|
#define JOIN_MODE_NONE 0u
|
||||||
* Идёт от q до q_end, учитывает стартовую колонку col и при необходимости
|
|
||||||
* эмитит continuation-сегменты. Возвращает итоговый line_style. */
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
uint32_t last_space = 0xFFFFFFFFu;
|
|
||||||
uint8_t last_space_style = line_style; /* снимок line_style на момент last_space */
|
|
||||||
char prev_ch = ' ';
|
|
||||||
uint8_t seg_col = col;
|
|
||||||
/* base — атрибут «обычного» текста сегмента. Для заголовка первый сегмент
|
|
||||||
* имеет title-attr, а continuation-сегменты render рисует как PLAIN
|
|
||||||
* (ATTR_TEXT, kind=LK_PLAIN при cont) — переключаем base после 1-го переноса. */
|
|
||||||
uint8_t base = base_attr;
|
|
||||||
|
|
||||||
while (q < q_end && n_lines < max_lines) {
|
|
||||||
char ch = fb(q);
|
|
||||||
|
|
||||||
if (ch == '\n') {
|
|
||||||
/* Жёсткий перенос (уже проверен снаружи) или конец потока. */
|
|
||||||
q++;
|
|
||||||
emit_seg(q, line_style, ckind, (ckind == CK_LIST || ckind == CK_QUOTE) ? 1 : 0);
|
|
||||||
base = ATTR_TEXT;
|
|
||||||
last_space = 0xFFFFFFFFu; prev_ch = ' '; seg_col = col;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Inline-маркеры (\X [x] ` ** ~~ * _ \t) — общий разбор. */
|
|
||||||
if (inline_marker(&q, q_end, &line_style, &seg_col, &prev_ch, base)) continue;
|
|
||||||
|
|
||||||
if (ch == ' ') {
|
|
||||||
gc_put(' ', emph_to_attr(line_style, base));
|
|
||||||
last_space = q + 1;
|
|
||||||
last_space_style = line_style;
|
|
||||||
g_ncells_at_space = g_ncells;
|
|
||||||
seg_col++; q++; prev_ch = ' ';
|
|
||||||
} else {
|
|
||||||
gc_put(ch, emph_to_attr(line_style, base));
|
|
||||||
seg_col++; q++; prev_ch = ch;
|
|
||||||
}
|
|
||||||
if (!nowrap && seg_col >= SCREEN_W) {
|
|
||||||
uint8_t use_ls = (uint8_t)(last_space != 0xFFFFFFFFu && last_space > cur_seg_off);
|
|
||||||
uint32_t wrap_at = use_ls ? last_space : q;
|
|
||||||
if (wrap_at >= q_end || wrap_at == cur_seg_off) break;
|
|
||||||
uint8_t wrap_style = use_ls ? last_space_style : line_style;
|
|
||||||
if (use_ls) g_ncells = g_ncells_at_space;
|
|
||||||
emit_seg(wrap_at, wrap_style, ckind, 1);
|
|
||||||
base = ATTR_TEXT; /* continuation заголовка → PLAIN */
|
|
||||||
seg_col = col; last_space = 0xFFFFFFFFu; prev_ch = ' '; q = wrap_at;
|
|
||||||
line_style = wrap_style;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return line_style;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define JOIN_MODE_LIST 1u
|
#define JOIN_MODE_LIST 1u
|
||||||
#define JOIN_MODE_QUOTE 2u
|
#define JOIN_MODE_QUOTE 2u
|
||||||
#define JOIN_MODE_PLAIN 3u
|
#define JOIN_MODE_PLAIN 3u
|
||||||
|
|
||||||
/* Общий потоковый сканер для index_lines().
|
/* ================== Единый сканер сегмента ================================
|
||||||
* Убирает дублирование между ветками list/quote/plain:
|
* Общий цикл inline-форматирования, переносов и (в join-режимах) склейки
|
||||||
* - mode задаёт правила обработки перевода строки,
|
* физических строк — бывшие inline_scan + scan_join_stream. mode:
|
||||||
* - остальная inline-логика (emphasis/таб/перенос) едина.
|
* JOIN_MODE_NONE — сканировать РОВНО [q, bound) без склейки; '\n' в
|
||||||
* Возвращает позицию q, на которой сканер остановился, и обновляет стиль. */
|
* диапазоне не ожидается (вызывающие дают строку/ячейку до EOL);
|
||||||
static uint32_t scan_join_stream(uint32_t q, uint32_t para_start, uint8_t col,
|
* активны base_attr (title-attr заголовка) и nowrap (замер таблиц);
|
||||||
uint8_t ckind, uint8_t *io_line_style, uint8_t mode)
|
* ckind всегда CK_OTHER (continuation-префикс не эмитится).
|
||||||
|
* JOIN_MODE_LIST/QUOTE/PLAIN — правила '\n'-склейки; bound == file_size,
|
||||||
|
* base_attr == ATTR_TEXT (emph_to_attr(ls, ATTR_TEXT) тождественно
|
||||||
|
* бывшему styles_map[ls]), nowrap == 0.
|
||||||
|
* Возвращает позицию останова; итоговый стиль — через *io_line_style. */
|
||||||
|
static uint32_t scan_stream(uint32_t q, uint32_t bound, uint32_t para_start,
|
||||||
|
uint8_t col, uint8_t ckind, uint8_t *io_line_style,
|
||||||
|
uint8_t base_attr, uint8_t nowrap, uint8_t mode)
|
||||||
{
|
{
|
||||||
uint32_t last_space = 0xFFFFFFFFu;
|
uint32_t last_space = 0xFFFFFFFFu;
|
||||||
uint8_t last_space_style = *io_line_style; /* снимок line_style на момент last_space */
|
uint8_t last_space_style = *io_line_style; /* снимок line_style на момент last_space */
|
||||||
char prev_ch = ' ';
|
char prev_ch = ' ';
|
||||||
uint8_t seg_col = col;
|
uint8_t seg_col = col;
|
||||||
uint8_t line_style = *io_line_style;
|
uint8_t line_style = *io_line_style;
|
||||||
|
/* base — атрибут «обычного» текста сегмента. Для заголовка первый сегмент
|
||||||
|
* имеет title-attr, а continuation-сегменты render рисует как PLAIN —
|
||||||
|
* переключаем base после 1-го переноса (в join-режимах base == ATTR_TEXT
|
||||||
|
* и присваивание ничего не меняет). */
|
||||||
|
uint8_t base = base_attr;
|
||||||
|
|
||||||
while (q < file_size && n_lines < max_lines) {
|
while (q < bound && n_lines < max_lines) {
|
||||||
char ch = fb(q);
|
char ch = fb(q);
|
||||||
uint8_t soft_break = 0;
|
uint8_t soft_break = 0;
|
||||||
|
|
||||||
if (ch == '\n') {
|
if (ch == '\n') {
|
||||||
uint32_t next = q + 1;
|
uint32_t next = q + 1;
|
||||||
if (next >= file_size) break;
|
if (mode == JOIN_MODE_NONE) break; /* NONE: '\n' в [q,bound) не ожидается */
|
||||||
|
if (next >= bound) break;
|
||||||
|
|
||||||
if (mode == JOIN_MODE_LIST) {
|
if (mode == JOIN_MODE_LIST) {
|
||||||
if (is_line_blank(next)) break;
|
if (is_line_blank(next)) break;
|
||||||
@@ -538,7 +495,7 @@ static uint32_t scan_join_stream(uint32_t q, uint32_t para_start, uint8_t col,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (next < file_size) {
|
while (next < bound) {
|
||||||
char ws = fb(next);
|
char ws = fb(next);
|
||||||
if (ws == ' ' || ws == '\t') next++;
|
if (ws == ' ' || ws == '\t') next++;
|
||||||
else break;
|
else break;
|
||||||
@@ -556,8 +513,7 @@ static uint32_t scan_join_stream(uint32_t q, uint32_t para_start, uint8_t col,
|
|||||||
if (next_kind != LK_QUOTE) break;
|
if (next_kind != LK_QUOTE) break;
|
||||||
|
|
||||||
{
|
{
|
||||||
uint32_t next_line_end = next_content;
|
uint32_t next_line_end = row_end(next_content);
|
||||||
while (next_line_end < file_size && fb(next_line_end) != '\n') next_line_end++;
|
|
||||||
|
|
||||||
{
|
{
|
||||||
uint8_t empty_quote = 1;
|
uint8_t empty_quote = 1;
|
||||||
@@ -622,7 +578,7 @@ static uint32_t scan_join_stream(uint32_t q, uint32_t para_start, uint8_t col,
|
|||||||
q++;
|
q++;
|
||||||
/* как render: пропускаем ведущие пробелы/табы строки-продолжения,
|
/* как render: пропускаем ведущие пробелы/табы строки-продолжения,
|
||||||
* склейка даёт РОВНО один пробел (иначе merge эмитит отступ как лишние ячейки) */
|
* склейка даёт РОВНО один пробел (иначе merge эмитит отступ как лишние ячейки) */
|
||||||
while (q < file_size && (fb(q) == ' ' || fb(q) == '\t')) q++;
|
while (q < bound && (fb(q) == ' ' || fb(q) == '\t')) q++;
|
||||||
soft_break = 1;
|
soft_break = 1;
|
||||||
ch = ' ';
|
ch = ' ';
|
||||||
}
|
}
|
||||||
@@ -631,39 +587,39 @@ static uint32_t scan_join_stream(uint32_t q, uint32_t para_start, uint8_t col,
|
|||||||
/* Inline-маркеры (\X [x] ` ** ~~ * _ \t) — общий разбор; при
|
/* Inline-маркеры (\X [x] ` ** ~~ * _ \t) — общий разбор; при
|
||||||
* soft_break ch==' ' (синтетический пробел склейки) — маркеры
|
* soft_break ch==' ' (синтетический пробел склейки) — маркеры
|
||||||
* пропускаем, его кладёт ветка пробела ниже. */
|
* пропускаем, его кладёт ветка пробела ниже. */
|
||||||
if (!soft_break && inline_marker(&q, file_size, &line_style, &seg_col, &prev_ch, ATTR_TEXT))
|
if (!soft_break && inline_marker(&q, bound, &line_style, &seg_col, &prev_ch, base))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (ch == ' ') {
|
if (ch == ' ') {
|
||||||
gc_put(' ', styles_map[line_style]);
|
gc_put(' ', emph_to_attr(line_style, base));
|
||||||
if (soft_break) {
|
if (soft_break) {
|
||||||
last_space = q;
|
last_space = q; /* синтетический пробел склейки */
|
||||||
last_space_style = line_style;
|
|
||||||
seg_col++;
|
|
||||||
} else {
|
} else {
|
||||||
last_space = q + 1;
|
last_space = q + 1;
|
||||||
last_space_style = line_style;
|
|
||||||
seg_col++;
|
|
||||||
q++;
|
q++;
|
||||||
}
|
}
|
||||||
|
last_space_style = line_style;
|
||||||
g_ncells_at_space = g_ncells; /* снимок для усечения */
|
g_ncells_at_space = g_ncells; /* снимок для усечения */
|
||||||
|
seg_col++;
|
||||||
prev_ch = ' ';
|
prev_ch = ' ';
|
||||||
} else {
|
} else {
|
||||||
gc_put(ch, styles_map[line_style]);
|
gc_put(ch, emph_to_attr(line_style, base));
|
||||||
seg_col++;
|
seg_col++;
|
||||||
q++; /* soft_break здесь всегда 0 (ch != ' ') */
|
q++; /* soft_break здесь всегда 0 (ch != ' ') */
|
||||||
prev_ch = ch;
|
prev_ch = ch;
|
||||||
}
|
}
|
||||||
if (seg_col >= SCREEN_W) {
|
if (!nowrap && seg_col >= SCREEN_W) {
|
||||||
uint8_t use_ls = (uint8_t)(last_space != 0xFFFFFFFFu && last_space > cur_seg_off);
|
uint8_t use_ls = (uint8_t)(last_space != 0xFFFFFFFFu && last_space > cur_seg_off);
|
||||||
uint32_t wrap_at = use_ls ? last_space : q;
|
uint32_t wrap_at = use_ls ? last_space : q;
|
||||||
if (wrap_at >= file_size || wrap_at == cur_seg_off) break;
|
if (wrap_at >= bound || wrap_at == cur_seg_off) break;
|
||||||
uint8_t wrap_style = use_ls ? last_space_style : line_style;
|
uint8_t wrap_style = use_ls ? last_space_style : line_style;
|
||||||
if (use_ls) g_ncells = g_ncells_at_space; /* усечь до пробела */
|
if (use_ls) g_ncells = g_ncells_at_space; /* усечь до пробела */
|
||||||
emit_seg(wrap_at, wrap_style, ckind, 1);
|
emit_seg(wrap_at, wrap_style, ckind, 1);
|
||||||
|
base = ATTR_TEXT; /* continuation заголовка → PLAIN (в join base и так ATTR_TEXT) */
|
||||||
/* Префикс continuation-сегмента: список и отступленный абзац (#4) —
|
/* Префикс continuation-сегмента: список и отступленный абзац (#4) —
|
||||||
* col пробелов выравнивания; цитата — отступ + маркер 0xB3 + пробел.
|
* col пробелов выравнивания; цитата — отступ + маркер 0xB3 + пробел.
|
||||||
* Для обычного абзаца без отступа col==0 → префикса нет. */
|
* Для обычного абзаца без отступа col==0 → префикса нет; CK_OTHER
|
||||||
|
* (заголовки/таблицы, NONE-режим) — префикса нет. */
|
||||||
if (ckind == CK_LIST || ckind == CK_PLAIN) {
|
if (ckind == CK_LIST || ckind == CK_PLAIN) {
|
||||||
gc_fill(' ', ATTR_TEXT, col);
|
gc_fill(' ', ATTR_TEXT, col);
|
||||||
} else if (ckind == CK_QUOTE) {
|
} else if (ckind == CK_QUOTE) {
|
||||||
@@ -692,6 +648,25 @@ static uint32_t scan_join_stream(uint32_t q, uint32_t para_start, uint8_t col,
|
|||||||
return q;
|
return q;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Тонкая обёртка прежнего API: скан ровно [q, q_end) без склейки строк.
|
||||||
|
* Вызывается для заголовков и ячеек таблиц (всегда CK_OTHER, col=0). */
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
(void)scan_stream(q, q_end, q, col, ckind, &line_style, base_attr, nowrap,
|
||||||
|
JOIN_MODE_NONE);
|
||||||
|
return line_style;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Потоковый сканер для index_lines(): mode задаёт правила '\n'-склейки. */
|
||||||
|
static uint32_t scan_join_stream(uint32_t q, uint32_t para_start, uint8_t col,
|
||||||
|
uint8_t ckind, uint8_t *io_line_style, uint8_t mode)
|
||||||
|
{
|
||||||
|
return scan_stream(q, file_size, para_start, col, ckind, io_line_style,
|
||||||
|
ATTR_TEXT, 0, mode);
|
||||||
|
}
|
||||||
|
|
||||||
/* Отрисовка таблиц (row_end/table_*) — в модуле mdview2_table.c. */
|
/* Отрисовка таблиц (row_end/table_*) — в модуле mdview2_table.c. */
|
||||||
|
|
||||||
/* Строит индекс сегментов рендера по загруженному файлу. */
|
/* Строит индекс сегментов рендера по загруженному файлу. */
|
||||||
@@ -722,12 +697,11 @@ void index_lines(void)
|
|||||||
/* Пустая строка — разделитель параграфов.
|
/* Пустая строка — разделитель параграфов.
|
||||||
* В индекс добавляем не более одной пустой экранной строки подряд. */
|
* В индекс добавляем не более одной пустой экранной строки подряд. */
|
||||||
if (is_line_blank(p)) {
|
if (is_line_blank(p)) {
|
||||||
while (p < file_size && fb(p) != '\n') p++;
|
p = next_line(p);
|
||||||
if (p < file_size) p++;
|
|
||||||
/* Серия пустых строк схлопывается в одну визуальную строку. */
|
/* Серия пустых строк схлопывается в одну визуальную строку. */
|
||||||
if (n_lines == 0 || !(cur_rec.flags & IF_BLANK)) {
|
if (n_lines == 0 || !(cur_rec.flags & IF_BLANK)) {
|
||||||
emit_seg(p, INIT_STYLE_PLAIN, CK_PLAIN, 0);
|
emit_seg(p, INIT_STYLE_PLAIN, CK_PLAIN, 0);
|
||||||
set_blank_cur();
|
set_cur_flags(IF_BLANK);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -736,16 +710,13 @@ void index_lines(void)
|
|||||||
if (is_fence_raw(p)) {
|
if (is_fence_raw(p)) {
|
||||||
in_block = !in_block;
|
in_block = !in_block;
|
||||||
emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0);
|
emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0);
|
||||||
set_nowrap_cur();
|
set_cur_flags(IF_NOWRAP);
|
||||||
while (p < file_size && fb(p) != '\n') p++;
|
p = next_line(p);
|
||||||
if (p < file_size) p++;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (in_block) {
|
if (in_block) {
|
||||||
emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0);
|
emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0);
|
||||||
set_nowrap_cur();
|
set_cur_flags(IF_NOWRAP | IF_CODE | IF_HSCROLL); /* код скроллится целым блоком */
|
||||||
set_code_cur();
|
|
||||||
set_hscroll_cur(); /* код скроллится целым блоком */
|
|
||||||
{
|
{
|
||||||
uint8_t ccx = 0;
|
uint8_t ccx = 0;
|
||||||
uint32_t pp = p;
|
uint32_t pp = p;
|
||||||
@@ -763,18 +734,16 @@ void index_lines(void)
|
|||||||
pp++;
|
pp++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (p < file_size && fb(p) != '\n') p++;
|
p = next_line(p);
|
||||||
if (p < file_size) p++;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- горизонтальный разделитель ---- */
|
/* ---- горизонтальный разделитель ---- */
|
||||||
if (is_hr_raw(p)) {
|
if (is_hr_raw(p)) {
|
||||||
emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0);
|
emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0);
|
||||||
set_nowrap_cur();
|
set_cur_flags(IF_NOWRAP);
|
||||||
gc_fill(0xC4, ATTR_HR, SCREEN_W);
|
gc_fill(0xC4, ATTR_HR, SCREEN_W);
|
||||||
while (p < file_size && fb(p) != '\n') p++;
|
p = next_line(p);
|
||||||
if (p < file_size) p++;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -814,21 +783,21 @@ void index_lines(void)
|
|||||||
|
|
||||||
/* Проход 2: верхняя рамка → строки/разделитель → нижняя рамка. */
|
/* Проход 2: верхняя рамка → строки/разделитель → нижняя рамка. */
|
||||||
emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0);
|
emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0);
|
||||||
set_nowrap_cur(); set_hscroll_cur();
|
set_cur_flags(IF_NOWRAP | IF_HSCROLL);
|
||||||
table_border(widths, ncols, TBL_TL, TBL_TM, TBL_TR);
|
table_border(widths, ncols, TBL_TL, TBL_TM, TBL_TR);
|
||||||
|
|
||||||
r = p;
|
r = p;
|
||||||
while (r < tbl_end) {
|
while (r < tbl_end) {
|
||||||
uint32_t le = row_end(r);
|
uint32_t le = row_end(r);
|
||||||
emit_seg(r, INIT_STYLE_PLAIN, CK_OTHER, 0);
|
emit_seg(r, INIT_STYLE_PLAIN, CK_OTHER, 0);
|
||||||
set_nowrap_cur(); set_hscroll_cur();
|
set_cur_flags(IF_NOWRAP | IF_HSCROLL);
|
||||||
if (table_is_sep_row(r, le)) table_border(widths, ncols, TBL_ML, TBL_MM, TBL_MR);
|
if (table_is_sep_row(r, le)) table_border(widths, ncols, TBL_ML, TBL_MM, TBL_MR);
|
||||||
else table_data_row(r, le, widths, ncols);
|
else table_data_row(r, le, widths, ncols);
|
||||||
r = (le < file_size) ? le + 1 : le;
|
r = (le < file_size) ? le + 1 : le;
|
||||||
}
|
}
|
||||||
|
|
||||||
emit_seg(tbl_end, INIT_STYLE_PLAIN, CK_OTHER, 0);
|
emit_seg(tbl_end, INIT_STYLE_PLAIN, CK_OTHER, 0);
|
||||||
set_nowrap_cur(); set_hscroll_cur();
|
set_cur_flags(IF_NOWRAP | IF_HSCROLL);
|
||||||
table_border(widths, ncols, TBL_BL, TBL_BM, TBL_BR);
|
table_border(widths, ncols, TBL_BL, TBL_BM, TBL_BR);
|
||||||
|
|
||||||
p = tbl_end;
|
p = tbl_end;
|
||||||
@@ -849,11 +818,9 @@ void index_lines(void)
|
|||||||
default: hbase = ATTR_TEXT_TITLE4; break;
|
default: hbase = ATTR_TEXT_TITLE4; break;
|
||||||
}
|
}
|
||||||
emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0);
|
emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0);
|
||||||
uint32_t line_end = content_off;
|
uint32_t line_end = row_end(content_off);
|
||||||
while (line_end < file_size && fb(line_end) != '\n') line_end++;
|
|
||||||
(void)inline_scan(content_off, line_end, 0, CK_OTHER, INIT_STYLE_PLAIN, hbase, 0);
|
(void)inline_scan(content_off, line_end, 0, CK_OTHER, INIT_STYLE_PLAIN, hbase, 0);
|
||||||
while (p < file_size && fb(p) != '\n') p++;
|
p = next_line(p);
|
||||||
if (p < file_size) p++;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -882,10 +849,7 @@ void index_lines(void)
|
|||||||
q = scan_join_stream(q, p, col, CK_LIST, &line_style, JOIN_MODE_LIST);
|
q = scan_join_stream(q, p, col, CK_LIST, &line_style, JOIN_MODE_LIST);
|
||||||
|
|
||||||
/* Переходим к началу следующей физической строки после пункта. */
|
/* Переходим к началу следующей физической строки после пункта. */
|
||||||
if (q < file_size) {
|
q = next_line(q);
|
||||||
while (q < file_size && fb(q) != '\n') q++;
|
|
||||||
if (q < file_size) q++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Одна пустая строка между соседними маркерами списка подавляется.
|
/* Одна пустая строка между соседними маркерами списка подавляется.
|
||||||
* Две и более пустых строки оставляют видимый разделитель. */
|
* Две и более пустых строки оставляют видимый разделитель. */
|
||||||
@@ -893,8 +857,7 @@ void index_lines(void)
|
|||||||
uint32_t t = q;
|
uint32_t t = q;
|
||||||
uint8_t blanks = 0;
|
uint8_t blanks = 0;
|
||||||
while (t < file_size && is_line_blank(t)) {
|
while (t < file_size && is_line_blank(t)) {
|
||||||
while (t < file_size && fb(t) != '\n') t++;
|
t = next_line(t);
|
||||||
if (t < file_size) t++;
|
|
||||||
blanks++;
|
blanks++;
|
||||||
// if (blanks >= 2) break;
|
// if (blanks >= 2) break;
|
||||||
}
|
}
|
||||||
@@ -934,9 +897,7 @@ void index_lines(void)
|
|||||||
}
|
}
|
||||||
q = scan_join_stream(q, p, col, CK_QUOTE, &line_style, JOIN_MODE_QUOTE);
|
q = scan_join_stream(q, p, col, CK_QUOTE, &line_style, JOIN_MODE_QUOTE);
|
||||||
|
|
||||||
while (q < file_size && fb(q) != '\n') q++;
|
p = next_line(q);
|
||||||
if (q < file_size) q++;
|
|
||||||
p = q;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -962,9 +923,7 @@ void index_lines(void)
|
|||||||
/* q стоит на завершающем newline параграфа.
|
/* q стоит на завершающем newline параграфа.
|
||||||
* Переходим на следующую строку, чтобы внешняя петля её
|
* Переходим на следующую строку, чтобы внешняя петля её
|
||||||
* корректно переклассифицировала. */
|
* корректно переклассифицировала. */
|
||||||
while (q < file_size && fb(q) != '\n') q++;
|
p = next_line(q);
|
||||||
if (q < file_size) q++;
|
|
||||||
p = q;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,8 +19,8 @@
|
|||||||
|
|
||||||
#if WITH_RAW /* весь модуль — опционален (mdview_conf.h); при 0 пустой объектник */
|
#if WITH_RAW /* весь модуль — опционален (mdview_conf.h); при 0 пустой объектник */
|
||||||
|
|
||||||
static uint32_t raw_top = 0; /* байт-offset верхней строки экрана */
|
static uint32_t raw_top; /* байт-offset верхней строки экрана */
|
||||||
static uint16_t raw_hpan = 0; /* гориз. сдвиг (только HSCROLL) */
|
static uint16_t raw_hpan; /* гориз. сдвиг (только HSCROLL) */
|
||||||
|
|
||||||
/* ---- примитивы навигации по физическим строкам / переносам -------- */
|
/* ---- примитивы навигации по физическим строкам / переносам -------- */
|
||||||
|
|
||||||
@@ -173,17 +173,27 @@ uint8_t calc_raw_pct(void)
|
|||||||
|
|
||||||
/* ---- посев позиции / пересев при смене активного буфера ----------- */
|
/* ---- посев позиции / пересев при смене активного буфера ----------- */
|
||||||
|
|
||||||
void raw_seed_from(uint16_t md_top_line)
|
uint32_t raw_pos(void)
|
||||||
{
|
{
|
||||||
uint32_t off = seg_off(md_top_line);
|
return raw_top;
|
||||||
if (off > file_size) off = 0;
|
}
|
||||||
|
|
||||||
|
/* Поставить RAW на начало строки, содержащей байт off (позиционирование
|
||||||
|
* при F2 MD→RAW и F8-переносе позиции между наборами). */
|
||||||
|
void raw_reanchor(uint32_t off)
|
||||||
|
{
|
||||||
|
if (off > file_size) off = file_size;
|
||||||
raw_top = raw_line_start(off);
|
raw_top = raw_line_start(off);
|
||||||
raw_hpan = 0;
|
raw_hpan = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Сброс в начало — при смене активного буфера (F8 в/из UTF-8: размеры/offset'ы
|
void raw_seed_from(uint16_t md_top_line)
|
||||||
* разные, старый raw_top невалиден). Без percent-математики. */
|
{
|
||||||
void raw_home(void)
|
raw_reanchor(seg_off(md_top_line));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Сброс в начало (клавиша Home). */
|
||||||
|
static void raw_home(void)
|
||||||
{
|
{
|
||||||
raw_top = 0;
|
raw_top = 0;
|
||||||
raw_hpan = 0;
|
raw_hpan = 0;
|
||||||
|
|||||||
+31
-15
@@ -27,6 +27,8 @@
|
|||||||
.globl s__INITIALIZER
|
.globl s__INITIALIZER
|
||||||
.globl l__INITIALIZER
|
.globl l__INITIALIZER
|
||||||
.globl s__INITIALIZED
|
.globl s__INITIALIZED
|
||||||
|
.globl s__DATA
|
||||||
|
.globl l__DATA
|
||||||
.globl s__BSS
|
.globl s__BSS
|
||||||
.globl l__BSS
|
.globl l__BSS
|
||||||
|
|
||||||
@@ -300,10 +302,13 @@ gpn_skip:
|
|||||||
;; =========================================================================
|
;; =========================================================================
|
||||||
;; Runtime data (HOME's _DATA / _BSS)
|
;; Runtime data (HOME's _DATA / _BSS)
|
||||||
;; =========================================================================
|
;; =========================================================================
|
||||||
.area _DATA
|
.area _CODE
|
||||||
|
;; Written BEFORE gsinit runs (gsinit zeroes _DATA), so these live in
|
||||||
|
;; _CODE — RAM on the Sprinter, always mapped.
|
||||||
_estex_startup_ix::
|
_estex_startup_ix::
|
||||||
.ds 2
|
.ds 2
|
||||||
|
|
||||||
|
.area _DATA
|
||||||
_argc::
|
_argc::
|
||||||
.ds 2
|
.ds 2
|
||||||
_argv::
|
_argv::
|
||||||
@@ -326,32 +331,43 @@ progname_buf:
|
|||||||
.ds 128 ; ESTEX APPINFO app_path target
|
.ds 128 ; ESTEX APPINFO app_path target
|
||||||
|
|
||||||
;; =========================================================================
|
;; =========================================================================
|
||||||
;; gsinit — copy _INITIALIZER -> _INITIALIZED, then zero _BSS.
|
;; gsinit — zero _DATA + _BSS, then copy _INITIALIZER -> _INITIALIZED.
|
||||||
|
;; _DATA is zeroed because C guarantees zero-valued statics — this lets
|
||||||
|
;; code drop explicit `= 0` initializers that burn _INITIALIZER bytes.
|
||||||
;; =========================================================================
|
;; =========================================================================
|
||||||
.area _GSINIT
|
.area _GSINIT
|
||||||
gsinit::
|
.area _CODE
|
||||||
ld bc, #l__INITIALIZER
|
;; zero_area — fill BC bytes at HL with 0 (BC may be zero).
|
||||||
|
zero_area:
|
||||||
ld a, b
|
ld a, b
|
||||||
or a, c
|
or a, c
|
||||||
jr Z, gsinit_bss
|
ret Z
|
||||||
ld de, #s__INITIALIZED
|
|
||||||
ld hl, #s__INITIALIZER
|
|
||||||
ldir
|
|
||||||
gsinit_bss:
|
|
||||||
ld bc, #l__BSS
|
|
||||||
ld a, b
|
|
||||||
or a, c
|
|
||||||
jr Z, gsinit_done
|
|
||||||
ld hl, #s__BSS
|
|
||||||
ld (hl), #0
|
ld (hl), #0
|
||||||
dec bc
|
dec bc
|
||||||
ld a, b
|
ld a, b
|
||||||
or a, c
|
or a, c
|
||||||
jr Z, gsinit_done
|
ret Z
|
||||||
ld d, h
|
ld d, h
|
||||||
ld e, l
|
ld e, l
|
||||||
inc de
|
inc de
|
||||||
ldir
|
ldir
|
||||||
|
ret
|
||||||
|
|
||||||
|
.area _GSINIT
|
||||||
|
gsinit::
|
||||||
|
ld hl, #s__DATA
|
||||||
|
ld bc, #l__DATA
|
||||||
|
call zero_area
|
||||||
|
ld hl, #s__BSS
|
||||||
|
ld bc, #l__BSS
|
||||||
|
call zero_area
|
||||||
|
ld bc, #l__INITIALIZER
|
||||||
|
ld a, b
|
||||||
|
or a, c
|
||||||
|
jr Z, gsinit_done
|
||||||
|
ld de, #s__INITIALIZED
|
||||||
|
ld hl, #s__INITIALIZER
|
||||||
|
ldir
|
||||||
gsinit_done:
|
gsinit_done:
|
||||||
|
|
||||||
.area _GSFINAL
|
.area _GSFINAL
|
||||||
|
|||||||
+32
-15
@@ -35,6 +35,8 @@
|
|||||||
.globl s__INITIALIZER
|
.globl s__INITIALIZER
|
||||||
.globl l__INITIALIZER
|
.globl l__INITIALIZER
|
||||||
.globl s__INITIALIZED
|
.globl s__INITIALIZED
|
||||||
|
.globl s__DATA
|
||||||
|
.globl l__DATA
|
||||||
.globl s__BSS
|
.globl s__BSS
|
||||||
.globl l__BSS
|
.globl l__BSS
|
||||||
|
|
||||||
@@ -398,7 +400,9 @@ gpn_skip:
|
|||||||
;; =========================================================================
|
;; =========================================================================
|
||||||
;; Runtime data
|
;; Runtime data
|
||||||
;; =========================================================================
|
;; =========================================================================
|
||||||
.area _DATA
|
.area _CODE
|
||||||
|
;; Written BEFORE gsinit runs (gsinit zeroes _DATA), so these live in
|
||||||
|
;; _CODE — RAM on the Sprinter, always mapped.
|
||||||
_estex_startup_ix::
|
_estex_startup_ix::
|
||||||
.ds 2
|
.ds 2
|
||||||
_estex_file_handle::
|
_estex_file_handle::
|
||||||
@@ -407,6 +411,8 @@ _estex_block_id::
|
|||||||
.ds 1
|
.ds 1
|
||||||
_bank_block_id::
|
_bank_block_id::
|
||||||
.ds 1
|
.ds 1
|
||||||
|
|
||||||
|
.area _DATA
|
||||||
_argc::
|
_argc::
|
||||||
.ds 2
|
.ds 2
|
||||||
_argv::
|
_argv::
|
||||||
@@ -427,32 +433,43 @@ _w2_self_allocated::
|
|||||||
.endif
|
.endif
|
||||||
|
|
||||||
;; =========================================================================
|
;; =========================================================================
|
||||||
;; gsinit — same as crt0.s (copy INITIALIZER, zero BSS)
|
;; gsinit — same as crt0.s (zero _DATA + _BSS, copy INITIALIZER)
|
||||||
;; =========================================================================
|
;; =========================================================================
|
||||||
.area _GSINIT
|
.area _GSINIT
|
||||||
gsinit::
|
.area _CODE
|
||||||
ld bc, #l__INITIALIZER
|
;; zero_area — fill BC bytes at HL with 0 (BC may be zero).
|
||||||
|
zero_area:
|
||||||
ld a, b
|
ld a, b
|
||||||
or a, c
|
or a, c
|
||||||
jr Z, gsinit_bss
|
ret Z
|
||||||
ld de, #s__INITIALIZED
|
|
||||||
ld hl, #s__INITIALIZER
|
|
||||||
ldir
|
|
||||||
gsinit_bss:
|
|
||||||
ld bc, #l__BSS
|
|
||||||
ld a, b
|
|
||||||
or a, c
|
|
||||||
jr Z, gsinit_done
|
|
||||||
ld hl, #s__BSS
|
|
||||||
ld (hl), #0
|
ld (hl), #0
|
||||||
dec bc
|
dec bc
|
||||||
ld a, b
|
ld a, b
|
||||||
or a, c
|
or a, c
|
||||||
jr Z, gsinit_done
|
ret Z
|
||||||
ld d, h
|
ld d, h
|
||||||
ld e, l
|
ld e, l
|
||||||
inc de
|
inc de
|
||||||
ldir
|
ldir
|
||||||
|
ret
|
||||||
|
|
||||||
|
.area _GSINIT
|
||||||
|
gsinit::
|
||||||
|
;; --- Zero _DATA + _BSS (C guarantees zero-valued statics) ---
|
||||||
|
ld hl, #s__DATA
|
||||||
|
ld bc, #l__DATA
|
||||||
|
call zero_area
|
||||||
|
ld hl, #s__BSS
|
||||||
|
ld bc, #l__BSS
|
||||||
|
call zero_area
|
||||||
|
;; --- Copy _INITIALIZER -> _INITIALIZED if non-empty ---
|
||||||
|
ld bc, #l__INITIALIZER
|
||||||
|
ld a, b
|
||||||
|
or a, c
|
||||||
|
jr Z, gsinit_done
|
||||||
|
ld de, #s__INITIALIZED
|
||||||
|
ld hl, #s__INITIALIZER
|
||||||
|
ldir
|
||||||
gsinit_done:
|
gsinit_done:
|
||||||
|
|
||||||
.area _GSFINAL
|
.area _GSFINAL
|
||||||
|
|||||||
+30
-17
@@ -20,6 +20,8 @@
|
|||||||
.globl s__INITIALIZER
|
.globl s__INITIALIZER
|
||||||
.globl l__INITIALIZER
|
.globl l__INITIALIZER
|
||||||
.globl s__INITIALIZED
|
.globl s__INITIALIZED
|
||||||
|
.globl s__DATA
|
||||||
|
.globl l__DATA
|
||||||
.globl s__BSS
|
.globl s__BSS
|
||||||
.globl l__BSS
|
.globl l__BSS
|
||||||
|
|
||||||
@@ -70,40 +72,51 @@ _start::
|
|||||||
;; =========================================================================
|
;; =========================================================================
|
||||||
;; Runtime data
|
;; Runtime data
|
||||||
;; =========================================================================
|
;; =========================================================================
|
||||||
.area _DATA
|
.area _CODE
|
||||||
|
;; Written BEFORE gsinit runs (gsinit zeroes _DATA), so these live in
|
||||||
|
;; _CODE — RAM on the Sprinter, always mapped.
|
||||||
_estex_startup_ix::
|
_estex_startup_ix::
|
||||||
.ds 2
|
.ds 2
|
||||||
|
|
||||||
;; =========================================================================
|
;; =========================================================================
|
||||||
;; gsinit — copy _INITIALIZER -> _INITIALIZED, then zero _BSS.
|
;; gsinit — zero _DATA + _BSS, then copy _INITIALIZER -> _INITIALIZED.
|
||||||
;; SDCC appends per-unit init code between this label and the ret in _GSFINAL.
|
;; SDCC appends per-unit init code between this label and the ret in _GSFINAL.
|
||||||
;; =========================================================================
|
;; =========================================================================
|
||||||
.area _GSINIT
|
.area _GSINIT
|
||||||
gsinit::
|
.area _CODE
|
||||||
;; --- Copy _INITIALIZER -> _INITIALIZED if non-empty ---
|
;; zero_area — fill BC bytes at HL with 0 (BC may be zero).
|
||||||
ld bc, #l__INITIALIZER
|
zero_area:
|
||||||
ld a, b
|
ld a, b
|
||||||
or a, c
|
or a, c
|
||||||
jr Z, gsinit_bss
|
ret Z
|
||||||
ld de, #s__INITIALIZED
|
|
||||||
ld hl, #s__INITIALIZER
|
|
||||||
ldir
|
|
||||||
gsinit_bss:
|
|
||||||
;; --- Zero _BSS if non-empty ---
|
|
||||||
ld bc, #l__BSS
|
|
||||||
ld a, b
|
|
||||||
or a, c
|
|
||||||
jr Z, gsinit_done
|
|
||||||
ld hl, #s__BSS
|
|
||||||
ld (hl), #0
|
ld (hl), #0
|
||||||
dec bc
|
dec bc
|
||||||
ld a, b
|
ld a, b
|
||||||
or a, c
|
or a, c
|
||||||
jr Z, gsinit_done
|
ret Z
|
||||||
ld d, h
|
ld d, h
|
||||||
ld e, l
|
ld e, l
|
||||||
inc de
|
inc de
|
||||||
ldir
|
ldir
|
||||||
|
ret
|
||||||
|
|
||||||
|
.area _GSINIT
|
||||||
|
gsinit::
|
||||||
|
;; --- Zero _DATA + _BSS (C guarantees zero-valued statics) ---
|
||||||
|
ld hl, #s__DATA
|
||||||
|
ld bc, #l__DATA
|
||||||
|
call zero_area
|
||||||
|
ld hl, #s__BSS
|
||||||
|
ld bc, #l__BSS
|
||||||
|
call zero_area
|
||||||
|
;; --- Copy _INITIALIZER -> _INITIALIZED if non-empty ---
|
||||||
|
ld bc, #l__INITIALIZER
|
||||||
|
ld a, b
|
||||||
|
or a, c
|
||||||
|
jr Z, gsinit_done
|
||||||
|
ld de, #s__INITIALIZED
|
||||||
|
ld hl, #s__INITIALIZER
|
||||||
|
ldir
|
||||||
gsinit_done:
|
gsinit_done:
|
||||||
|
|
||||||
.area _GSFINAL
|
.area _GSFINAL
|
||||||
|
|||||||
+35
-19
@@ -38,6 +38,8 @@
|
|||||||
.globl s__INITIALIZER
|
.globl s__INITIALIZER
|
||||||
.globl l__INITIALIZER
|
.globl l__INITIALIZER
|
||||||
.globl s__INITIALIZED
|
.globl s__INITIALIZED
|
||||||
|
.globl s__DATA
|
||||||
|
.globl l__DATA
|
||||||
.globl s__BSS
|
.globl s__BSS
|
||||||
.globl l__BSS
|
.globl l__BSS
|
||||||
|
|
||||||
@@ -119,11 +121,11 @@ w2_join:
|
|||||||
ld sp, #0xBFFE
|
ld sp, #0xBFFE
|
||||||
.ifdef DEBUG_RT
|
.ifdef DEBUG_RT
|
||||||
;; Save the self-alloc flag across gsinit (which clobbers BC/DE/HL and
|
;; Save the self-alloc flag across gsinit (which clobbers BC/DE/HL and
|
||||||
;; zeroes BSS). SP is in W2 now, push is safe.
|
;; zeroes _DATA/_BSS). SP is in W2 now, push is safe.
|
||||||
push af
|
push af
|
||||||
.endif
|
.endif
|
||||||
|
|
||||||
;; ----- Step 5: save IX prefix in its canonical W2 slot. -------------
|
;; ----- Step 5: save IX prefix in its canonical slot (_CODE, W1). ----
|
||||||
ld (_estex_startup_ix), ix
|
ld (_estex_startup_ix), ix
|
||||||
|
|
||||||
;; ----- Step 6: standard gsinit + argv flow (identical to crt0.s). --
|
;; ----- Step 6: standard gsinit + argv flow (identical to crt0.s). --
|
||||||
@@ -336,10 +338,13 @@ gpn_skip:
|
|||||||
;; Runtime data — same layout as crt0.s. Lives in DATA/BSS which the
|
;; Runtime data — same layout as crt0.s. Lives in DATA/BSS which the
|
||||||
;; linker places in W2 (--data-loc 0x8000).
|
;; linker places in W2 (--data-loc 0x8000).
|
||||||
;; =========================================================================
|
;; =========================================================================
|
||||||
.area _DATA
|
.area _CODE
|
||||||
|
;; _estex_startup_ix is written BEFORE gsinit runs (gsinit zeroes _DATA),
|
||||||
|
;; so it lives in _CODE — RAM on the Sprinter, always mapped.
|
||||||
_estex_startup_ix::
|
_estex_startup_ix::
|
||||||
.ds 2
|
.ds 2
|
||||||
|
|
||||||
|
.area _DATA
|
||||||
_argc::
|
_argc::
|
||||||
.ds 2
|
.ds 2
|
||||||
_argv::
|
_argv::
|
||||||
@@ -362,33 +367,44 @@ _w2_self_allocated::
|
|||||||
.endif
|
.endif
|
||||||
|
|
||||||
;; =========================================================================
|
;; =========================================================================
|
||||||
;; gsinit — copy _INITIALIZER -> _INITIALIZED, then zero _BSS. Identical
|
;; gsinit — zero _DATA + _BSS, then copy _INITIALIZER -> _INITIALIZED.
|
||||||
;; to crt0.s; runs AFTER W2 is mapped so writes to _INITIALIZED/_BSS land.
|
;; Identical to crt0.s; runs AFTER W2 is mapped so the writes land.
|
||||||
|
;; _DATA is zeroed because C guarantees zero-valued statics — initializing
|
||||||
|
;; `static uint8_t x;` here lets code drop explicit `= 0` initializers
|
||||||
|
;; that would otherwise burn _INITIALIZER bytes in the EXE image.
|
||||||
;; =========================================================================
|
;; =========================================================================
|
||||||
.area _GSINIT
|
.area _CODE
|
||||||
gsinit::
|
;; zero_area — fill BC bytes at HL with 0 (BC may be zero).
|
||||||
ld bc, #l__INITIALIZER
|
zero_area:
|
||||||
ld a, b
|
ld a, b
|
||||||
or a, c
|
or a, c
|
||||||
jr Z, gsinit_bss
|
ret Z
|
||||||
ld de, #s__INITIALIZED
|
|
||||||
ld hl, #s__INITIALIZER
|
|
||||||
ldir
|
|
||||||
gsinit_bss:
|
|
||||||
ld bc, #l__BSS
|
|
||||||
ld a, b
|
|
||||||
or a, c
|
|
||||||
jr Z, gsinit_done
|
|
||||||
ld hl, #s__BSS
|
|
||||||
ld (hl), #0
|
ld (hl), #0
|
||||||
dec bc
|
dec bc
|
||||||
ld a, b
|
ld a, b
|
||||||
or a, c
|
or a, c
|
||||||
jr Z, gsinit_done
|
ret Z
|
||||||
ld d, h
|
ld d, h
|
||||||
ld e, l
|
ld e, l
|
||||||
inc de
|
inc de
|
||||||
ldir
|
ldir
|
||||||
|
ret
|
||||||
|
|
||||||
|
.area _GSINIT
|
||||||
|
gsinit::
|
||||||
|
ld hl, #s__DATA
|
||||||
|
ld bc, #l__DATA
|
||||||
|
call zero_area
|
||||||
|
ld hl, #s__BSS
|
||||||
|
ld bc, #l__BSS
|
||||||
|
call zero_area
|
||||||
|
ld bc, #l__INITIALIZER
|
||||||
|
ld a, b
|
||||||
|
or a, c
|
||||||
|
jr Z, gsinit_done
|
||||||
|
ld de, #s__INITIALIZED
|
||||||
|
ld hl, #s__INITIALIZER
|
||||||
|
ldir
|
||||||
gsinit_done:
|
gsinit_done:
|
||||||
|
|
||||||
.area _GSFINAL
|
.area _GSFINAL
|
||||||
|
|||||||
Reference in New Issue
Block a user