Files
Sprinter-SDCC/examples/mdview2/mdview2_hex.c
T
snark13 ae23d2dea2 mdview2: HEX-режим (F4) — дамп оригинального файла; версия v1.0 (b1)
Новый модуль mdview2_hex.c (WITH_HEX в conf): формат
' 0x012340 │ 16×hex │ 16 print', 30 строк, один атрибут.

- Дамп всегда ОРИГИНАЛЬНОГО файла (orig_file_phys), не активного буфера;
  ряд выровнен на 16 → один bank_read на ряд (не пересекает EMM-страницу),
  fb()/W3 не используются.
- Printable по текущей кодировке: CP866 как есть, CP1251/KOI8 через
  g_remap, UTF-8 — глиф на позиции лид-байта (continuation → '.') через
  новый utf_cp_glyph(), выделенный из конвертера enc-модуля.
- Навигация: ±16 / ±480 / Home / End; одна строка — аппаратный scroll()
  + подрисовка одного ряда (как MD/RAW); процент в статусе.
- F4 — тумблер HEX ↔ прежний вид; F2 из HEX уводит в MD; позиция при
  всех переходах через view_pos/view_reanchor (map_off orig ↔ active).
- F8 в HEX: hex-колонка неизменна, printable перерисовывается в новой
  кодировке; позиция не двигается.
- Help: версия v1.0 (b1), строка F4.

exe 25725 → 27893 (+2168). Проверено в MAME.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 13:19:08 +03:00

210 lines
8.5 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 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 <stdint.h>
#include <string.h>
#include <conio.h>
#include <bios/text.h>
#include <sprinter_mem.h> /* 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 */