ffd179e064
Раунд 1 (−1411): --max-allocs 100000 в Makefile (−848 кода) + снятие
всех нулевых инициализаторов file-scope переменных (_INITIALIZER
583→24; _DATA теперь зануляется в crt0).
Раунд 2 (−1188, индексатор 11019→9839): дедупликации в mdview2_index.c:
- classify_line: копия HR-проверки → вызов is_hr_raw;
- next_line() поверх row_end() вместо 9 копий «домотать до \n»;
- inline_marker: emph_to_attr(ls, base) вычисляется один раз (at);
- set_{nowrap,blank,code,hscroll}_cur → set_cur_flags(mask): строка
code-блока делает один idx_put вместо трёх.
Проверено в MAME (README/UTF8TEST: маркеры, списки, цитаты, таблицы,
code-блоки, F8).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
316 lines
18 KiB
C
316 lines
18 KiB
C
/*
|
||
* mdview2_enc.c — кодировки и конвертация UTF-8 → CP866.
|
||
*
|
||
* Две связанные подсистемы:
|
||
* 1. 8-битные кодировки (CP866/CP1251/KOI8-R) — различаются только ремапом
|
||
* старших байтов [128-255] на ОТРИСОВКЕ (g_remap, win_rest_remap).
|
||
* Автоопределение — detect_encoding по частоте ходовых букв.
|
||
* 2. UTF-8 — отдельный набор: исходник инкрементально декодируется в
|
||
* utf_phys[] как чистый CP866 (utf_convert_more), после чего весь
|
||
* конвейер индексации/кэша работает по нему без ремапа.
|
||
*
|
||
* Владеет состоянием кодека (conv-буфер, позиция конвертации, UTF-страницы);
|
||
* описание разделяемых символов и API — в mdview2.h. Оркестрацией (привязка
|
||
* UTF-буфера к живому file_*, сборка наборов) занимается build_doc() ядра.
|
||
*/
|
||
|
||
#include <stdint.h>
|
||
#include <conio.h> /* COLOR()/COLOR_* для ATTR_* в attr_is_content */
|
||
#include <sprinter.h> /* sprinter_page_w3 */
|
||
#include <sprinter_mem.h> /* bank_read/bank_write/mem_alloc_pages/mem_get_page */
|
||
#include "mdview2.h"
|
||
|
||
/* ---- Состояние кодировки/UTF (владелец — этот модуль) ------------- */
|
||
uint8_t g_encoding = ENC_CP866; /* активная кодировка */
|
||
|
||
uint8_t utf_blk;
|
||
uint8_t utf_pages;
|
||
uint8_t utf_phys[MAX_PAGES];
|
||
uint32_t utf_size;
|
||
uint8_t utf_avail; /* 1 = страницы UTF-8 выделены */
|
||
uint8_t g_utf_building; /* 1 пока конвертация не дошла до конца исходника */
|
||
|
||
/* Приватное состояние конвертера (наружу не торчит — сброс через
|
||
* utf_conv_reset(), чтение оригинала через cv_read). */
|
||
static uint32_t utf_src; /* позиция чтения ОРИГИНАЛА (utf-8) */
|
||
static uint8_t cv_page = 0xFF; /* orig-страница в W3 для cv_read (своя от cur_page) */
|
||
|
||
/* Таблицы ремапа СТАРШИХ байтов [128-255] в CP866. Индексируются (ch-0x80):
|
||
* младшие 128 (ASCII) — identity, в ремапе не участвуют (win_rest_remap
|
||
* трогает только ch>=0x80), поэтому в таблицах их нет. Кириллица + ходовая
|
||
* пунктуация; неизвестное → '?' (0x3F). Сгенерированы Python codecs. */
|
||
static const uint8_t cp1251_to_866[128] = {
|
||
/* 80 */ 0x3F, 0x3F, 0x27, 0x3F, 0x22, 0x2E, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3C, 0x3F, 0x3F, 0x3F, 0x3F,
|
||
/* 90 */ 0x3F, 0x27, 0x27, 0x22, 0x22, 0x07, 0x2D, 0x2D, 0x3F, 0x3F, 0x3F, 0x3E, 0x3F, 0x3F, 0x3F, 0x3F,
|
||
/* A0 */ 0xFF, 0xF6, 0xF7, 0x3F, 0xFD, 0x3F, 0x3F, 0x3F, 0xF0, 0x63, 0xF2, 0x3C, 0x3F, 0x3F, 0x72, 0xF4,
|
||
/* B0 */ 0xF8, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0xFA, 0xF1, 0xFC, 0xF3, 0x3E, 0x3F, 0x3F, 0x3F, 0xF5,
|
||
/* C0 */ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
|
||
/* D0 */ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
|
||
/* E0 */ 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
|
||
/* F0 */ 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
|
||
};
|
||
static const uint8_t koi8r_to_866[128] = {
|
||
/* 80 */ 0xC4, 0xB3, 0xDA, 0xBF, 0xC0, 0xD9, 0xC3, 0xB4, 0xC2, 0xC1, 0xC5, 0xDF, 0xDC, 0xDB, 0xDD, 0xDE,
|
||
/* 90 */ 0xB0, 0xB1, 0xB2, 0x3F, 0xFE, 0xF9, 0xFB, 0x3F, 0x3F, 0x3F, 0xFF, 0x3F, 0xF8, 0x3F, 0xFA, 0x3F,
|
||
/* A0 */ 0xCD, 0xBA, 0xD5, 0xF1, 0xD6, 0xC9, 0xB8, 0xB7, 0xBB, 0xD4, 0xD3, 0xC8, 0xBE, 0xBD, 0xBC, 0xC6,
|
||
/* B0 */ 0xC7, 0xCC, 0xB5, 0xF0, 0xB6, 0xB9, 0xD1, 0xD2, 0xCB, 0xCF, 0xD0, 0xCA, 0xD8, 0xD7, 0xCE, 0x63,
|
||
/* C0 */ 0xEE, 0xA0, 0xA1, 0xE6, 0xA4, 0xA5, 0xE4, 0xA3, 0xE5, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE,
|
||
/* D0 */ 0xAF, 0xEF, 0xE0, 0xE1, 0xE2, 0xE3, 0xA6, 0xA2, 0xEC, 0xEB, 0xA7, 0xE8, 0xED, 0xE9, 0xE7, 0xEA,
|
||
/* E0 */ 0x9E, 0x80, 0x81, 0x96, 0x84, 0x85, 0x94, 0x83, 0x95, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E,
|
||
/* F0 */ 0x8F, 0x9F, 0x90, 0x91, 0x92, 0x93, 0x86, 0x82, 0x9C, 0x9B, 0x87, 0x98, 0x9D, 0x99, 0x97, 0x9A,
|
||
};
|
||
|
||
/* Устанавливает активную кодировку и таблицу ремапа (для 8-битных). */
|
||
void set_encoding(uint8_t enc)
|
||
{
|
||
g_encoding = enc;
|
||
g_remap = (enc == ENC_CP1251) ? cp1251_to_866 :
|
||
(enc == ENC_KOI8R) ? koi8r_to_866 : (const uint8_t *)0;
|
||
}
|
||
|
||
/* Самые ходовые строчные русские буквы (о е а и н т с р в л) — их байты
|
||
* различают 8-битные кодировки по частоте. */
|
||
// static const uint8_t common866 [10] = {0xAE,0xA5,0xA0,0xA8,0xAD,0xE2,0xE1,0xE0,0xA2,0xAB};
|
||
// static const uint8_t common1251[10] = {0xEE,0xE5,0xE0,0xE8,0xED,0xF2,0xF1,0xF0,0xE2,0xEB};
|
||
// static const uint8_t commonkoi [10] = {0xCF,0xC5,0xC1,0xC9,0xCE,0xD4,0xD3,0xD2,0xD7,0xCC};
|
||
// static uint8_t in_set10(const uint8_t *s, uint8_t b)
|
||
// {
|
||
// for (uint8_t i = 0; i < 10; i++) if (s[i] == b) return 1;
|
||
// return 0;
|
||
// }
|
||
|
||
/* Проверяем только пять самых популярных символов (о е а и н) */
|
||
static const uint8_t common866 [5] = {0xAE,0xA5,0xA0,0xA8,0xAD};
|
||
static const uint8_t common1251[5] = {0xEE,0xE5,0xE0,0xE8,0xED};
|
||
static const uint8_t commonkoi [5] = {0xCF,0xC5,0xC1,0xC9,0xCE};
|
||
static uint8_t in_set10(const uint8_t *s, uint8_t b)
|
||
{
|
||
for (uint8_t i = 0; i < 5; i++) if (s[i] == b) return 1;
|
||
return 0;
|
||
}
|
||
|
||
/* Автоопределение кодировки дешёвым сканом байтов (до построения индекса).
|
||
* BOM → UTF8; иначе валидность UTF-8 (структура multibyte); иначе 8-бит по
|
||
* частоте ходовых букв; фолбэк CP866. */
|
||
uint8_t detect_encoding(void)
|
||
{
|
||
if (file_size >= 3 && (uint8_t)fb(0) == 0xEF && (uint8_t)fb(1) == 0xBB && (uint8_t)fb(2) == 0xBF)
|
||
return ENC_UTF8;
|
||
|
||
uint32_t n = file_size;
|
||
if (n > 1024u) n = 1024u; /* сэмпл: первый 1 КБ — детекции хватает */
|
||
uint8_t utf_ok = 1, has_mb = 0, has_high = 0, cont = 0;
|
||
uint16_t s866 = 0, s1251 = 0, skoi = 0;
|
||
|
||
for (uint32_t p = 0; p < n; p++) {
|
||
uint8_t b = (uint8_t)fb(p);
|
||
if (b < 0x80) { if (cont) { utf_ok = 0; cont = 0; } continue; }
|
||
has_high = 1;
|
||
if (in_set10(common866, b)) s866++;
|
||
if (in_set10(common1251, b)) s1251++;
|
||
if (in_set10(commonkoi, b)) skoi++;
|
||
if (cont) {
|
||
if ((b & 0xC0) == 0x80) cont--;
|
||
else { utf_ok = 0; cont = 0; }
|
||
}
|
||
else if (b >= 0xC2 && b <= 0xDF) { cont = 1; has_mb = 1; }
|
||
else if (b >= 0xE0 && b <= 0xEF) { cont = 2; has_mb = 1; }
|
||
else if (b >= 0xF0 && b <= 0xF4) { cont = 3; has_mb = 1; }
|
||
else utf_ok = 0; /* битый лид/одиночный континюэйшн */
|
||
}
|
||
/* Незавершённая multibyte-последовательность на КОНЦЕ — нарушение только
|
||
* если это настоящий EOF; на границе сэмпла (n<file_size) это просто
|
||
* обрезка, не считаем за ошибку. */
|
||
if (cont && n == file_size) utf_ok = 0;
|
||
|
||
if (!has_high) return ENC_CP866; /* чистый ASCII */
|
||
if (utf_ok && has_mb) return ENC_UTF8;
|
||
if (s1251 >= s866 && s1251 >= skoi) return ENC_CP1251;
|
||
if (skoi >= s866) return ENC_KOI8R;
|
||
return ENC_CP866;
|
||
}
|
||
|
||
const char *enc_name(uint8_t enc)
|
||
{
|
||
return (enc == ENC_CP1251) ? "CP1251" :
|
||
(enc == ENC_KOI8R) ? "KOI8-R" :
|
||
(enc == ENC_UTF8) ? "UTF-8 " : "CP866 ";
|
||
}
|
||
|
||
/* Контентный глиф (ремапим при смене кодировки) vs структурный (рамка/HR/
|
||
* маркеры — уже CP866, не трогаем). Различаем по attr. */
|
||
static uint8_t attr_is_content(uint8_t a)
|
||
{
|
||
return (uint8_t)(a != ATTR_BOX && a != ATTR_HR &&
|
||
a != ATTR_LIST_MARKER && a != ATTR_QUOTE_MARKER);
|
||
}
|
||
|
||
/* Вывод среза строки кэша с ремапом контентных глифов [128-255] через
|
||
* g_remap (CP1251/KOI8). Без активной таблицы — прямой win_rest. */
|
||
void win_rest_remap(uint8_t row, uint8_t w, uint8_t page, uint16_t off)
|
||
{
|
||
if (!g_remap || w == 0) {
|
||
win_rest(row, 0, 1, w, page, off);
|
||
return;
|
||
}
|
||
uint8_t buf[SCREEN_W * 2];
|
||
bank_read(page, off, buf, (uint16_t)w * 2u);
|
||
for (uint8_t i = 0; i < w; i++) {
|
||
uint8_t ch = buf[(uint16_t)i * 2u];
|
||
if (ch >= 0x80 && attr_is_content(buf[(uint16_t)i * 2u + 1u]))
|
||
buf[(uint16_t)i * 2u] = g_remap[ch - 0x80]; /* таблицы хранят только старшие 128 */
|
||
}
|
||
bank_write(g_scratch_phys, 0, buf, (uint16_t)w * 2u);
|
||
win_rest(row, 0, 1, w, g_scratch_phys, 0);
|
||
}
|
||
|
||
/* ========================= UTF-8 → CP866 (Фаза 2) ======================== */
|
||
|
||
/* Кириллица U+0400..U+045F → CP866. Русский набор + Ё/ё и часть украинских
|
||
* (Є є Ї ї Ў ў), которые есть в CP866; пропуски → '?' (0x3F). */
|
||
static const uint8_t utf_cyr_to_866[96] = {
|
||
/* 0400 */ 0x3F, 0xF0, 0x3F, 0x3F, 0xF2, 0x3F, 0x3F, 0xF4, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0xF6, 0x3F,
|
||
/* 0410 */ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
|
||
/* 0420 */ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
|
||
/* 0430 */ 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
|
||
/* 0440 */ 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
|
||
/* 0450 */ 0x3F, 0xF1, 0x3F, 0x3F, 0xF3, 0x3F, 0x3F, 0xF5, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0xF7, 0x3F,
|
||
};
|
||
|
||
/* Поток вывода конвертера: пишем в utf_phys чанками по странице, буфер в
|
||
* near-памяти, флаш через один bank_write (не побайтовый swap W3). */
|
||
static uint8_t conv_buf[256];
|
||
static uint16_t conv_n; /* байт в буфере, ещё не сброшено */
|
||
static uint8_t conv_page; /* индекс текущей страницы в utf_phys */
|
||
static uint16_t conv_off; /* уже сброшенное смещение в этой странице */
|
||
|
||
static void conv_flush(void)
|
||
{
|
||
if (conv_n) {
|
||
bank_write(utf_phys[conv_page], conv_off, conv_buf, conv_n);
|
||
conv_off = (uint16_t)(conv_off + conv_n);
|
||
conv_n = 0;
|
||
}
|
||
}
|
||
|
||
/* Добавляет один CP866-байт в выходной поток. Запись никогда не пересекает
|
||
* границу страницы (256 делит 16384 нацело). */
|
||
static void conv_put(uint8_t b)
|
||
{
|
||
if ((uint16_t)(conv_off + conv_n) == PAGE_SIZE) { /* страница заполнена */
|
||
conv_flush();
|
||
conv_page++;
|
||
conv_off = 0;
|
||
}
|
||
conv_buf[conv_n++] = b;
|
||
if (conv_n == (uint16_t)sizeof(conv_buf)) conv_flush();
|
||
}
|
||
|
||
/* Подстановки одиночных не-кириллических символов UTF-8 → один CP866-байт.
|
||
* Таблица вместо switch (экономия кода); линейный поиск дёшев — вызывается
|
||
* лишь при конвертации на редких символьных кодпойнтах. Чтобы добавить новый
|
||
* символ — достаточно дописать одну строку {кодпойнт, байт-CP866}.
|
||
* (Символы с НЕ-1:1 заменой, напр. … -> "...", обрабатываются отдельно ниже.) */
|
||
typedef struct { uint16_t utf8; uint8_t cp866; } utf_sym_t;
|
||
static const utf_sym_t utf_sym[] = {
|
||
{0x00A0, 0x20}, {0x00AB, 0x3C}, {0x00BB, 0x3E}, /* nbsp « » → space < > */
|
||
{0x00B0, 0xF8}, {0x00B7, 0xFA}, /* ° · */
|
||
{0x2013, 0x2D}, {0x2014, 0x2D}, /* – — → '-' */
|
||
{0x2018, 0x27}, {0x2019, 0x27}, /* ‘ ’ → '\'' */
|
||
{0x201C, 0x22}, {0x201D, 0x22}, /* “ ” → '"' */
|
||
{0x2022, 0xF9}, {0x2116, 0xFC}, /* • → ∙ № */
|
||
{0x2190, 0x1B}, {0x2191, 0x18}, {0x2192, 0x1A}, {0x2193, 0x19}, /* ← ↑ → ↓ */
|
||
{0x2500, 0xC4}, {0x2502, 0xB3}, {0x250C, 0xDA}, {0x2510, 0xBF}, /* ─ │ ┌ ┐ */
|
||
{0x2514, 0xC0}, {0x2518, 0xD9}, {0x251C, 0xC3}, {0x2524, 0xB4}, /* └ ┘ ├ ┤ */
|
||
{0x252C, 0xC2}, {0x2534, 0xC1}, {0x253C, 0xC5}, /* ┬ ┴ ┼ */
|
||
{0x2580, 0xDF}, {0x2584, 0xDC}, {0x2588, 0xDB}, /* ▀ ▄ █ */
|
||
{0x2591, 0xB0}, {0x2592, 0xB1}, {0x2593, 0xB2}, {0x25A0, 0xFE}, /* ░ ▒ ▓ ■ */
|
||
{0x2713, 0xFB}, {0x2714, 0xFB}, /* ✓ ✔ → галка */
|
||
{0x2715, 0x78}, {0x2717, 0x78}, {0x2718, 0x78}, /* ✕ ✗ ✘ → 'x' */
|
||
};
|
||
#define UTF_SYM_N (uint8_t)(sizeof(utf_sym) / sizeof(utf_sym[0]))
|
||
|
||
/* Кодпойнт → CP866-глиф(ы). Кириллица по таблице; спецслучаи (…, BOM) явно;
|
||
* остальные ходовые символы по таблице utf_sym[]; прочее → '?'. */
|
||
static void conv_emit_cp(uint32_t cp)
|
||
{
|
||
if (cp < 0x80) { conv_put((uint8_t)cp); return; }
|
||
if (cp >= 0x0400 && cp <= 0x045F) { conv_put(utf_cyr_to_866[cp - 0x0400]); return; }
|
||
if (cp == 0x2026) { conv_put('.'); conv_put('.'); conv_put('.'); return; } /* … → "..." */
|
||
if (cp == 0xFEFF) return; /* BOM/ZWNBSP — выкинуть */
|
||
for (uint8_t i = 0; i < UTF_SYM_N; i++)
|
||
if (utf_sym[i].utf8 == (uint16_t)cp) {
|
||
conv_put(utf_sym[i].cp866);
|
||
return;
|
||
}
|
||
conv_put('?');
|
||
}
|
||
|
||
/* Последовательное чтение байта ОРИГИНАЛА (utf-8) конвертером. Оригинал лежит
|
||
* в orig_file_phys[]; мапим его страницу в W3 напрямую, со своей кэш-переменной
|
||
* cv_page (отдельной от cur_page индексатора, т.к. оба используют W3 и
|
||
* чередуются). На границе с fb()-чтением utf-буфера кэши взаимно сбрасываются. */
|
||
static uint8_t cv_read(uint32_t s)
|
||
{
|
||
uint8_t pg = (uint8_t)(s >> PAGE_BITS);
|
||
if (pg != cv_page) { sprinter_page_w3(orig_file_phys[pg]); cv_page = pg; }
|
||
return *((volatile uint8_t *)(0xC000u + (uint16_t)(s & PAGE_MASK)));
|
||
}
|
||
|
||
/* Возобновляемая конвертация: дописывает utf_phys из оригинала, пока
|
||
* сконвертированных (flushed) байт меньше target и не достигнут конец
|
||
* исходника. По выходу file_size = доступная (сконвертированная) часть; при
|
||
* достижении конца фиксирует utf_size и снимает g_utf_building. */
|
||
void utf_convert_more(uint32_t target)
|
||
{
|
||
cv_page = 0xFF; /* W3 был на utf (fb индексатора) — пере-смаппим orig */
|
||
uint32_t n = orig_file_size;
|
||
while (utf_src < n &&
|
||
(uint32_t)((uint32_t)conv_page * PAGE_SIZE + conv_off) < target) {
|
||
uint8_t b = (uint8_t)cv_read(utf_src++);
|
||
if (b < 0x80) {
|
||
conv_emit_cp(b);
|
||
continue;
|
||
}
|
||
|
||
uint32_t cp;
|
||
uint8_t need;
|
||
if ((b & 0xE0) == 0xC0) { cp = (uint32_t)(b & 0x1F); need = 1; }
|
||
else if ((b & 0xF0) == 0xE0) { cp = (uint32_t)(b & 0x0F); need = 2; }
|
||
else if ((b & 0xF8) == 0xF0) { cp = (uint32_t)(b & 0x07); need = 3; }
|
||
else { conv_put('?'); continue; }
|
||
|
||
uint8_t ok = 1;
|
||
for (uint8_t k = 0; k < need; k++) {
|
||
if (utf_src >= n) { ok = 0; break; }
|
||
uint8_t cb = (uint8_t)cv_read(utf_src);
|
||
if ((cb & 0xC0) != 0x80) { ok = 0; break; }
|
||
cp = (cp << 6) | (uint32_t)(cb & 0x3F);
|
||
utf_src++;
|
||
}
|
||
if (!ok || need == 3) { conv_put('?'); continue; }
|
||
conv_emit_cp(cp);
|
||
}
|
||
conv_flush();
|
||
file_size = (uint32_t)conv_page * PAGE_SIZE + conv_off; /* доступно индексатору */
|
||
if (utf_src >= n) { utf_size = file_size; g_utf_building = 0; }
|
||
cur_page = 0xFF; /* W3 был на orig — fb индексатора пере-смаппит utf */
|
||
}
|
||
|
||
/* Выделяет страницы под UTF-8 набор (конвертированный ≤ оригинала по размеру)
|
||
* и сбрасывает приватное состояние конвертера в начало (conv-буфер, utf_src,
|
||
* g_utf_building). Сама конвертация — инкрементальная, через utf_convert_more().
|
||
* Возврат: 1 — успех, 0 — нет EMM. */
|
||
uint8_t utf_alloc(void)
|
||
{
|
||
utf_pages = orig_file_pages;
|
||
utf_blk = mem_alloc_pages(utf_pages);
|
||
if (utf_blk == 0) return 0;
|
||
for (uint8_t i = 0; i < utf_pages; i++)
|
||
utf_phys[i] = mem_get_page(utf_blk, i);
|
||
conv_n = 0; conv_page = 0; conv_off = 0; /* converter reset (вызывается один раз) */
|
||
utf_src = 0;
|
||
g_utf_building = 1;
|
||
return 1;
|
||
}
|