mdview2: статус-бар в отдельный модуль, упростить alloc_set_storage

- mdview2_status.c: вынести статус-бар/меню/спиннер из ядра
- mdview2.c: убрать retry-цикл в alloc_set_storage (fail-fast вместо
  ложной устойчивости — при нехватке EMM под индекс контент тоже не влезет)
- mdview2.h: дополнить экспортами статус-модуля
- mdview2_md.c / mdview2_raw.c: зачистка после расщепления
- mdview/mdview.c: переименовать scroll_* → md_scroll_* (симметрия)
- docs/fast_ram.md, docs/turboc.txt: добавить справочные доки
- examples/mdview2/README.MD, READMEBG.MD: обновить описание

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-26 23:19:00 +03:00
parent 05916a3cc6
commit 1b78dda125
12 changed files with 1802 additions and 443 deletions
+75 -117
View File
@@ -45,21 +45,6 @@ static uint32_t raw_next_wrap(uint32_t off)
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;
@@ -74,15 +59,6 @@ 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)
{
@@ -128,27 +104,6 @@ static uint32_t raw_draw_wrap_row(uint32_t off, uint8_t 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;
@@ -203,54 +158,18 @@ 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)
{
/* Скроллим вниз ТОЛЬКО если контент уходит за нижний край. Проходим VIEW_H
* рядов от raw_top: если контент кончился раньше (экран не полон) — внизу
* была бы пустая строка, не скроллим; если ровно по нижний ряд (off==EOF) —
* ниже пусто, тоже не скроллим. Иначе off = ряд сразу под экраном. */
uint32_t off = raw_top;
for (uint8_t r = 0; r < VIEW_H; r++) {
if (off >= file_size) return; /* экран не заполнен контентом */
off = raw_next(off);
}
if (off >= file_size) return; /* контент ровно по экран, ниже пусто */
/* ---- статус-бар: % по байтам (отрисовка — render_raw_status_numbers
* в mdview2_status.c, читает позицию через этот raw_pct) ---- */
scroll(0, VIEW_TOP_ROW, SCREEN_W, VIEW_H, 1, 0); /* содержимое вверх */
raw_draw_one(off, (uint8_t)(VIEW_TOP_ROW + VIEW_H - 1)); /* новый нижний ряд */
raw_top = raw_next(raw_top);
}
static void raw_scroll_up1(void)
/* Процент по байтам без 32-битного деления: масштабируем offset'ы (>>8),
* дальше 32-битная арифметика. raw_top приватен модулю, поэтому % считаем
* здесь и отдаём готовым числом в статус-модуль. */
uint8_t calc_raw_pct(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_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);
uint16_t a = (uint16_t)(raw_top >> 8);
uint16_t b = (uint16_t)(file_size >> 8);
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("% ");
return (uint8_t)(a * 100UL / b);
}
/* ---- посев позиции / пересев при смене активного буфера ----------- */
@@ -271,6 +190,14 @@ void raw_home(void)
raw_hpan = 0;
}
void raw_end(void)
{
raw_top = file_size; /* отмотать VIEW_H рядов назад */
for (uint8_t k = 0; k < VIEW_H; k++)
raw_top = raw_prev(raw_top);
raw_hpan = 0;
}
void raw_screen_init(void) {
bios_clearwin(1, 0, 30, 80, ATTR_TEXT);
}
@@ -283,58 +210,89 @@ void raw_renorm(void)
raw_hpan = 0;
}
void raw_scroll_down(uint8_t delta) {
uint32_t off = raw_top;
for (uint8_t r = 0; r < VIEW_H; r++) {
if (off >= file_size)
return; /* экран не заполнен контентом */
off = raw_next(off);
}
if (off >= file_size)
return; /* контент ровно по экран, ниже пусто */
if(delta ==1) {
scroll(0, VIEW_TOP_ROW, SCREEN_W, VIEW_H, 1, 0); /* содержимое вверх */
off = raw_draw_one(off, (uint8_t)(VIEW_TOP_ROW + VIEW_H - 1)); /* новый нижний ряд */
raw_top = raw_next(raw_top);
} else {
for (uint8_t k = 0; k < delta; k++) {
uint32_t n2 = raw_next(raw_top);
if (n2 >= file_size)
break;
raw_top = n2;
}
raw_draw(); /* PgUp/PgDn/Home/End — полный экран */
}
}
void raw_scroll_up(uint8_t delta) {
if (raw_top == 0)
return;
if (delta == 1) {
raw_top = raw_prev(raw_top);
scroll(0, VIEW_TOP_ROW, SCREEN_W, VIEW_H, 2, 0); /* содержимое вниз */
(void)raw_draw_one(raw_top, VIEW_TOP_ROW);
} else {
for (uint8_t k = 0; k < delta; k++)
raw_top = raw_prev(raw_top);
raw_draw(); /* PgUp/PgDn/Home/End — полный экран */
}
}
/* ---- навигация (вызывается из главного цикла при 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_DOWN:
raw_scroll_down(1);
break;
case KEY_UP:
raw_scroll_up(1);
break;
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;
}
raw_scroll_down(VIEW_H);
break;
case KEY_PGUP:
nt = raw_top;
for (uint8_t k = 0; k < VIEW_H; k++) nt = raw_prev(nt);
raw_scroll_up(VIEW_H);
break;
case KEY_HOME:
nt = 0; raw_hpan = 0;
raw_home();
raw_draw();
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;
raw_end();
raw_draw();
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;
break;
case KEY_RIGHT:
if (g_view == VIEW_RAW_HSCROLL) {
if (raw_hpan < 248u) raw_hpan = (uint16_t)(raw_hpan + HPAN_STEP);
if (g_view == VIEW_RAW_HSCROLL && raw_hpan < 248u) {
raw_hpan = (uint16_t)(raw_hpan + HPAN_STEP);
raw_draw();
raw_status_numbers();
}
return 1;
break;
default:
return 0; /* не наша клавиша */
}
raw_top = nt;
raw_draw(); /* PgUp/PgDn/Home/End — полный экран */
raw_status_numbers();
// render_raw_status_numbers();
render_percent_progress(calc_raw_pct());
return 1;
}