/* * mdview2_hex.c — HEX-дамп ОРИГИНАЛЬНОГО файла (F4). * * Показывает исходные байты файла (orig_file_phys), а не активный буфер: * hex-дамп декодированного UTF-набора был бы искажением. Printable-колонка * интерпретируется ТЕКУЩЕЙ кодировкой: CP866 как есть, CP1251/KOI8 через * g_remap, UTF-8 — глиф на позиции лид-байта (continuation-байты → '.'). * * Формат ряда (79 колонок, один атрибут ATTR_TEXT — как RAW): * 0x012340 │ XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX │ 16 символов * * Ряд выровнен на 16 байт и никогда не пересекает границу EMM-страницы * (16 делит 16384 нацело) — один bank_read на ряд, fb()/W3-маппинг не нужны. * Позиция (hex_top) живёт в байтах ОРИГИНАЛА; конвертацию в/из байт-позиции * активного буфера делает ядро (view_pos/view_reanchor через map_off). */ #include #include #include #include #include /* bank_read (чтение оригинала по страницам EMM) */ #include "mdview2.h" #if WITH_HEX /* весь модуль опционален (mdview2_conf.h); при 0 пустой объектник */ #define HEX_BPR 16u /* байт на ряд */ #define HEX_SPAN ((uint32_t)(VIEW_H - 1) * HEX_BPR) /* Геометрия ряда (колонки). */ #define HX_OFF 1u /* "0x012340" */ #define HX_SEP1 10u /* │ */ #define HX_DUMP 12u /* пары hex-цифр */ #define HX_SEP2 61u /* │ */ #define HX_PRINT 63u /* printable */ static uint32_t hex_top; /* offset верхнего ряда (кратен 16) */ uint32_t hex_pos(void) { return hex_top; } /* Максимальный hex_top: последний ряд файла на нижней строке экрана. */ static uint32_t hex_max_top(void) { uint32_t last = (orig_file_size == 0) ? 0 : ((orig_file_size - 1u) & ~(uint32_t)(HEX_BPR - 1u)); return (last > HEX_SPAN) ? (last - HEX_SPAN) : 0; } void hex_reanchor(uint32_t orig_off) { uint32_t mt = hex_max_top(); orig_off &= ~(uint32_t)(HEX_BPR - 1u); hex_top = (orig_off > mt) ? mt : orig_off; } void hex_screen_init(void) { bios_clearwin(VIEW_TOP_ROW, 0, VIEW_H, SCREEN_W, ATTR_TEXT); } /* ---- отрисовка ----------------------------------------------------- */ static uint8_t hexdig(uint8_t v) { v &= 15u; return (uint8_t)((v < 10u) ? ('0' + v) : ('A' - 10 + v)); } /* Один байт оригинала (для UTF-lookahead за границей ряда — редкий путь). */ static uint8_t fbo(uint32_t off) { uint8_t b; bank_read(orig_file_phys[off >> PAGE_BITS], (uint16_t)(off & PAGE_MASK), &b, 1); return b; } /* Printable-глиф для n байт ряда b[] (off — offset b[0] в файле) → out[]. */ static void hex_print_cells(const uint8_t *b, uint32_t off, uint8_t n, uint8_t *out) { for (uint8_t i = 0; i < n; i++) { uint8_t c = b[i]; uint8_t g; if (c < 0x20u) { g = '.'; } else if (c < 0x80u || g_encoding != ENC_UTF8) { g = (uint8_t)((c >= 0x80u && g_remap) ? g_remap[c - 0x80u] : c); } else if ((c & 0xC0u) == 0x80u) { g = '.'; /* continuation-байт */ } else { /* Лид-байт: собрать кодпойнт (хвост может выйти за ряд → fbo). * 4-байтовые (за пределами CP866) и битые последовательности → '.'. */ uint32_t cp; uint8_t need, ok = 1; if ((c & 0xE0u) == 0xC0u) { cp = (uint32_t)(c & 0x1Fu); need = 1; } else if ((c & 0xF0u) == 0xE0u) { cp = (uint32_t)(c & 0x0Fu); need = 2; } else { out[i] = '.'; continue; } for (uint8_t k = 1; k <= need; k++) { uint32_t p = off + i + k; if (p >= orig_file_size) { ok = 0; break; } uint8_t cb = (uint8_t)((i + k < n) ? b[i + k] : fbo(p)); if ((cb & 0xC0u) != 0x80u) { ok = 0; break; } cp = (cp << 6) | (uint32_t)(cb & 0x3Fu); } g = ok ? utf_cp_glyph(cp) : '.'; if (g < 0x20u) g = '.'; /* overlong мог дать управляющий */ } out[i] = g; } } static uint8_t hex_line[SCREEN_W]; /* собранный ряд (только символы, единый attr) */ static void hex_draw_row(uint32_t off, uint8_t row) { uint8_t buf[HEX_BPR]; uint8_t n = 0; memset(hex_line, ' ', SCREEN_W); if (off < orig_file_size) { uint32_t rem = orig_file_size - off; n = (rem >= HEX_BPR) ? (uint8_t)HEX_BPR : (uint8_t)rem; bank_read(orig_file_phys[off >> PAGE_BITS], (uint16_t)(off & PAGE_MASK), buf, n); } if (n) { /* ряды за EOF — пустые */ hex_line[HX_OFF] = '0'; hex_line[HX_OFF + 1] = 'x'; hex_line[HX_OFF + 2] = hexdig((uint8_t)(off >> 20)); hex_line[HX_OFF + 3] = hexdig((uint8_t)(off >> 16)); hex_line[HX_OFF + 4] = hexdig((uint8_t)(off >> 12)); hex_line[HX_OFF + 5] = hexdig((uint8_t)(off >> 8)); hex_line[HX_OFF + 6] = hexdig((uint8_t)(off >> 4)); hex_line[HX_OFF + 7] = hexdig((uint8_t)off); hex_line[HX_SEP1] = TBL_V; for (uint8_t i = 0; i < n; i++) { uint8_t x = (uint8_t)(HX_DUMP + i * 3u + ((i >= 8u) ? 1u : 0u)); hex_line[x] = hexdig((uint8_t)(buf[i] >> 4)); hex_line[x + 1] = hexdig(buf[i]); } hex_line[HX_SEP2] = TBL_V; hex_print_cells(buf, off, n, hex_line + HX_PRINT); } bios_set_place(row, 0); bios_write_until((const char *)hex_line, SCREEN_W, 0); } void hex_draw(void) { uint32_t off = hex_top; for (uint8_t r = 0; r < VIEW_H; r++) { hex_draw_row(off, (uint8_t)(VIEW_TOP_ROW + r)); off += HEX_BPR; } } /* ---- статус-бар: % по байтам (как calc_raw_pct) --------------------- */ static uint8_t calc_hex_pct(void) { return pct16((uint16_t)(hex_top >> 8), (uint16_t)(orig_file_size >> 8)); } /* ---- навигация (вызывается из главного цикла при g_view == VIEW_HEX) */ uint8_t hex_key(uint8_t scan) { uint32_t mt = hex_max_top(); switch (scan) { /* Одна строка — аппаратный скролл окна + подрисовка одного ряда * (как в MD/RAW), без перерисовки всего экрана. */ case KEY_DOWN: if (hex_top < mt) { hex_top += HEX_BPR; scroll(0, VIEW_TOP_ROW, SCREEN_W, VIEW_H, 1, 0); /* содержимое вверх */ hex_draw_row(hex_top + HEX_SPAN, (uint8_t)(VIEW_TOP_ROW + VIEW_H - 1)); } break; case KEY_UP: if (hex_top) { hex_top -= HEX_BPR; scroll(0, VIEW_TOP_ROW, SCREEN_W, VIEW_H, 2, 0); /* содержимое вниз */ hex_draw_row(hex_top, VIEW_TOP_ROW); } break; case KEY_PGDN: { uint32_t nt = hex_top + (uint32_t)VIEW_H * HEX_BPR; if (nt > mt) nt = mt; if (nt != hex_top) { hex_top = nt; hex_draw(); } break; } case KEY_PGUP: { uint32_t d = (uint32_t)VIEW_H * HEX_BPR; uint32_t nt = (hex_top >= d) ? hex_top - d : 0; if (nt != hex_top) { hex_top = nt; hex_draw(); } break; } case KEY_HOME: if (hex_top) { hex_top = 0; hex_draw(); } break; case KEY_END: if (hex_top != mt) { hex_top = mt; hex_draw(); } break; default: return 0; /* не наша клавиша */ } render_percent_progress(calc_hex_pct()); return 1; } #endif /* WITH_HEX */