mdview: move segment index to EMM bank; inline hot byte reads
- Index is now an 8-byte record per visible segment in a dedicated EMM block (idx_get/idx_put), freeing ~11 KB of near RAM and lifting the old 2048-line cap (dynamic max_lines = index_pages * 2048). - The per-byte scan keeps the previous segment offset in a near var (cur_seg_off) and mirrors the last record (cur_rec), so it never reads the index back from the bank. - fb()/map_page() are inlined now that there is code headroom, removing per-byte call + 32-bit argument marshalling overhead. Co-Authored-By: Oz <oz-agent@warp.dev>
This commit is contained in:
+136
-110
@@ -109,37 +109,51 @@ static const uint8_t md_pallete[16 * 4] = {
|
|||||||
* given time (FILE_BUF); fb() swaps pages on demand and tracks the
|
* given time (FILE_BUF); fb() swaps pages on demand and tracks the
|
||||||
* currently-mapped one in cur_page to avoid redundant OUTs.
|
* currently-mapped one in cur_page to avoid redundant OUTs.
|
||||||
*
|
*
|
||||||
* line_offset[]: one entry per VISIBLE viewport row. Segments created by
|
* The screen-segment index is a separate EMM block of 8-byte records
|
||||||
* word-wrap store the byte offset of the wrapped segment; continuation
|
* (one per VISIBLE viewport row), accessed through W3 via idx_get/idx_put.
|
||||||
* flag is in cont_flag[]. nowrap_flag[] marks code block / HR / table
|
* Each record holds the segment's start offset, flags (cont/nowrap/blank/
|
||||||
* rows that do NOT wrap and support horizontal scrolling.
|
* code) and the initial inline style; the right bound is the next record's
|
||||||
|
* offset. This frees ~11 KB of near RAM and lifts the old 2048-line cap.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* NOTE: explicit `= 0` initialisers are MANDATORY on SDCC z80.
|
/* NOTE: explicit `= 0` initialisers are MANDATORY on SDCC z80.
|
||||||
* Uninitialised static storage (`static T x;`) doesn't actually allocate
|
* Uninitialised static storage (`static T x;`) doesn't actually allocate
|
||||||
* space — multiple such declarations end up at the SAME address and
|
* space — multiple such declarations end up at the SAME address and
|
||||||
* silently overwrite each other. See memory/sdcc_static_storage_gotcha. */
|
* silently overwrite each other. See memory/sdcc_static_storage_gotcha. */
|
||||||
uint32_t line_offset[MAX_LINES]; // = {0};
|
/* Screen-segment index. One compact record per VISIBLE viewport row,
|
||||||
/* Bitmap: seg i is a continuation of the previous logical line. */
|
* stored in a dedicated EMM block (through W3) rather than scarce near
|
||||||
uint8_t cont_flag[MAX_LINES / 8]; // = {0};
|
* RAM. This both removes the old MAX_LINES near-array cap and frees
|
||||||
/* Bitmap: seg i (non-CONT) is inside a fenced code block. */
|
* ~11 KB of W2 for code/stack. */
|
||||||
uint8_t in_code[MAX_LINES / 8]; // = {0};
|
typedef struct idx_rec_s {
|
||||||
/* Bitmap: seg i should NOT be wrapped (code block, HR, table). */
|
uint32_t off; /* first source byte rendered by this segment */
|
||||||
uint8_t nowrap_flag[MAX_LINES / 8]; // = {0};
|
uint8_t flags; /* IF_* bits below */
|
||||||
/* Bitmap: seg i is a blank (empty visual line). */
|
uint8_t style; /* INIT_STYLE_* active at segment start */
|
||||||
uint8_t blank_flag[MAX_LINES / 8]; // = {0};
|
uint8_t pad0; /* padding → 8-byte record (2048 records / 16 KB page) */
|
||||||
/* 2-bit continuation kind per seg (4 lines/byte): 0=PLAIN, 1=QUOTE, 2=LIST, 3=OTHER.
|
uint8_t pad1;
|
||||||
* Used by render_line() to repeat prefix/indent on wrapped continuation rows. */
|
} idx_rec_t;
|
||||||
uint8_t line_kind[MAX_LINES / 4]; // = {0};
|
|
||||||
|
|
||||||
uint8_t init_style[MAX_LINES]; // = {0};
|
#define INDEX_REC_SIZE 8u
|
||||||
|
#define INDEX_RECS_PER_PAGE 2048u /* 16384 / 8 — records never straddle a page */
|
||||||
|
#define MAX_INDEX_PAGES 8u /* 8 * 2048 = 16384 segments max */
|
||||||
|
|
||||||
|
#define IF_CONT 0x01u /* continuation of a wrapped logical line */
|
||||||
|
#define IF_NOWRAP 0x02u /* must not wrap (code block / HR / table) */
|
||||||
|
#define IF_BLANK 0x04u /* visually blank row */
|
||||||
|
#define IF_CODE 0x08u /* fenced code-block body (verbatim style) */
|
||||||
|
|
||||||
static uint16_t n_lines = 0;
|
static uint16_t n_lines = 0;
|
||||||
|
static uint16_t max_lines = 0; /* index capacity = index_pages * 2048 */
|
||||||
static uint16_t top_line = 0;
|
static uint16_t top_line = 0;
|
||||||
static uint32_t file_size = 0;
|
static uint32_t file_size = 0;
|
||||||
static uint8_t file_blk = 0;
|
static uint8_t file_blk = 0;
|
||||||
static uint8_t file_pages = 0;
|
static uint8_t file_pages = 0;
|
||||||
static uint8_t file_phys[MAX_PAGES] = {0};
|
static uint8_t file_phys[MAX_PAGES] = {0};
|
||||||
|
static uint8_t index_blk = 0;
|
||||||
|
static uint8_t index_pages = 0;
|
||||||
|
static uint8_t index_phys[MAX_INDEX_PAGES] = {0};
|
||||||
|
static uint8_t index_truncated = 0; /* set when the index capacity is exhausted */
|
||||||
|
static uint32_t cur_seg_off = 0; /* off of the most-recent emitted seg (near; no bank read-back) */
|
||||||
|
static idx_rec_t cur_rec; /* mirror of the most-recent emitted record (near) */
|
||||||
static uint8_t cur_page = 0xFF; /* 0xFF = no page mapped yet */
|
static uint8_t cur_page = 0xFF; /* 0xFF = no page mapped yet */
|
||||||
static uint8_t viewport_x = 0; /* horizontal pan (for nowrap lines) */
|
static uint8_t viewport_x = 0; /* horizontal pan (for nowrap lines) */
|
||||||
static char filename[64] = {0};
|
static char filename[64] = {0};
|
||||||
@@ -197,7 +211,7 @@ static void spinner_show(uint8_t on)
|
|||||||
|
|
||||||
/* Map the EMM page that holds file offset >= page * PAGE_SIZE into W3.
|
/* Map the EMM page that holds file offset >= page * PAGE_SIZE into W3.
|
||||||
* No-op if already mapped. */
|
* No-op if already mapped. */
|
||||||
static void map_page(uint8_t page)
|
static inline void map_page(uint8_t page)
|
||||||
{
|
{
|
||||||
if (page != cur_page) {
|
if (page != cur_page) {
|
||||||
/* file_phys[] saves a BIOS mem_get_page() call on every page swap. */
|
/* file_phys[] saves a BIOS mem_get_page() call on every page swap. */
|
||||||
@@ -213,7 +227,7 @@ static void map_page(uint8_t page)
|
|||||||
* Hot path: index_lines() calls this for almost every input byte. On z80
|
* Hot path: index_lines() calls this for almost every input byte. On z80
|
||||||
* 32-bit shifts/masks are helper calls, so we decode page/offset from the
|
* 32-bit shifts/masks are helper calls, so we decode page/offset from the
|
||||||
* little-endian byte layout of `p`: page = bits 14..16, offset = bits 0..13. */
|
* little-endian byte layout of `p`: page = bits 14..16, offset = bits 0..13. */
|
||||||
static char fb(uint32_t p)
|
static inline char fb(uint32_t p)
|
||||||
{
|
{
|
||||||
uint8_t *pb = (uint8_t *)&p;
|
uint8_t *pb = (uint8_t *)&p;
|
||||||
uint8_t page = (uint8_t)((pb[1] >> 6) | (pb[2] << 2));
|
uint8_t page = (uint8_t)((pb[1] >> 6) | (pb[2] << 2));
|
||||||
@@ -244,54 +258,57 @@ static uint8_t is_emph_flanked(char prev_ch, char next_ch)
|
|||||||
return (uint8_t)(ws_or_eol(prev_ch) != ws_or_eol_or_delim(next_ch));
|
return (uint8_t)(ws_or_eol(prev_ch) != ws_or_eol_or_delim(next_ch));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Continuation-flag bitmap helpers (see cont_flag[] above). */
|
/* Continuation kind passed to emit_seg(); no longer stored (render derives
|
||||||
static uint8_t is_cont(uint16_t idx)
|
* the kind from classify_line()), kept only as call-site argument names. */
|
||||||
{
|
|
||||||
return (uint8_t)((cont_flag[idx >> 3] >> (idx & 7)) & 1u);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void set_cont(uint16_t idx)
|
|
||||||
{
|
|
||||||
cont_flag[idx >> 3] |= (uint8_t)(1u << (idx & 7));
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t is_nowrap(uint16_t idx)
|
|
||||||
{
|
|
||||||
return (uint8_t)((nowrap_flag[idx >> 3] >> (idx & 7)) & 1u);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void set_nowrap(uint16_t idx)
|
|
||||||
{
|
|
||||||
nowrap_flag[idx >> 3] |= (uint8_t)(1u << (idx & 7));
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t is_blank(uint16_t idx)
|
|
||||||
{
|
|
||||||
return (uint8_t)((blank_flag[idx >> 3] >> (idx & 7)) & 1u);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void set_blank(uint16_t idx)
|
|
||||||
{
|
|
||||||
blank_flag[idx >> 3] |= (uint8_t)(1u << (idx & 7));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Continuation kind: 0=PLAIN, 1=QUOTE, 2=LIST (ULIST/OLIST), 3=OTHER (HR/CODE/TABLE). */
|
|
||||||
#define CK_PLAIN 0
|
#define CK_PLAIN 0
|
||||||
#define CK_QUOTE 1
|
#define CK_QUOTE 1
|
||||||
#define CK_LIST 2
|
#define CK_LIST 2
|
||||||
#define CK_OTHER 3
|
#define CK_OTHER 3
|
||||||
|
|
||||||
static uint8_t get_ckind(uint16_t idx)
|
/* EMM index accessors. Records are 8 bytes and aligned within an index
|
||||||
|
* page, so bank_read/bank_write never split a record across a page. */
|
||||||
|
static void idx_get(uint16_t idx, idx_rec_t *r)
|
||||||
{
|
{
|
||||||
return (uint8_t)((line_kind[idx >> 2] >> ((idx & 3) << 1)) & 3u);
|
uint8_t page = (uint8_t)(idx >> 11); /* idx / 2048 */
|
||||||
|
uint16_t off = (uint16_t)((idx & 2047u) << 3); /* (idx % 2048) * 8 */
|
||||||
|
bank_read(index_phys[page], off, r, INDEX_REC_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void set_ckind(uint16_t idx, uint8_t kind)
|
static void idx_put(uint16_t idx, const idx_rec_t *r)
|
||||||
{
|
{
|
||||||
uint8_t shift = (uint8_t)((idx & 3) << 1);
|
uint8_t page = (uint8_t)(idx >> 11);
|
||||||
line_kind[idx >> 2] = (uint8_t)((line_kind[idx >> 2] & ~(3u << shift)) | ((kind & 3u) << shift));
|
uint16_t off = (uint16_t)((idx & 2047u) << 3);
|
||||||
|
bank_write(index_phys[page], off, r, INDEX_REC_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Field readers used by render & navigation (per-row, not per-byte). */
|
||||||
|
static uint8_t seg_flags(uint16_t idx)
|
||||||
|
{
|
||||||
|
idx_rec_t r;
|
||||||
|
if (idx >= n_lines) return 0;
|
||||||
|
idx_get(idx, &r);
|
||||||
|
return r.flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t seg_off(uint16_t idx)
|
||||||
|
{
|
||||||
|
idx_rec_t r;
|
||||||
|
if (idx >= n_lines) return file_size;
|
||||||
|
idx_get(idx, &r);
|
||||||
|
return r.off;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t is_cont(uint16_t idx) { return (uint8_t)((seg_flags(idx) & IF_CONT) != 0); }
|
||||||
|
static uint8_t is_nowrap(uint16_t idx) { return (uint8_t)((seg_flags(idx) & IF_NOWRAP) != 0); }
|
||||||
|
static uint8_t is_blank(uint16_t idx) { return (uint8_t)((seg_flags(idx) & IF_BLANK) != 0); }
|
||||||
|
static uint8_t is_code_body(uint16_t idx){ return (uint8_t)((seg_flags(idx) & IF_CODE) != 0); }
|
||||||
|
|
||||||
|
/* Flag setters target the MOST-RECENT emitted seg (n_lines-1) through the
|
||||||
|
* near mirror cur_rec, so the scan never reads the index back from the bank. */
|
||||||
|
static void set_nowrap_cur(void) { cur_rec.flags |= IF_NOWRAP; 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_pallete(void) {
|
static void set_pallete(void) {
|
||||||
uint8_t buff[64];
|
uint8_t buff[64];
|
||||||
for(int i = 0; i < 16; i++) {
|
for(int i = 0; i < 16; i++) {
|
||||||
@@ -361,6 +378,25 @@ static int load_file(const char *path)
|
|||||||
remaining -= chunk;
|
remaining -= chunk;
|
||||||
}
|
}
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
||||||
|
/* Allocate the EMM index block (8 B/segment, 2048 records per 16 KB
|
||||||
|
* page). Size it a little larger than the file so even dense files
|
||||||
|
* index fully; back off if EMM is tight. max_lines becomes the cap. */
|
||||||
|
{
|
||||||
|
uint8_t want = (uint8_t)(file_pages + 1u);
|
||||||
|
if (want > MAX_INDEX_PAGES) want = MAX_INDEX_PAGES;
|
||||||
|
index_blk = 0;
|
||||||
|
while (want >= 1u) {
|
||||||
|
index_blk = mem_alloc_pages(want);
|
||||||
|
if (index_blk) break;
|
||||||
|
want--;
|
||||||
|
}
|
||||||
|
if (index_blk == 0) return -5;
|
||||||
|
index_pages = want;
|
||||||
|
for (uint8_t i = 0; i < index_pages; i++)
|
||||||
|
index_phys[i] = mem_get_page(index_blk, i);
|
||||||
|
max_lines = (uint16_t)index_pages * INDEX_RECS_PER_PAGE;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -370,6 +406,10 @@ static void unload_file(void)
|
|||||||
mem_free_block(file_blk);
|
mem_free_block(file_blk);
|
||||||
file_blk = 0;
|
file_blk = 0;
|
||||||
}
|
}
|
||||||
|
if (index_blk) {
|
||||||
|
mem_free_block(index_blk);
|
||||||
|
index_blk = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ==================================================================
|
/* ==================================================================
|
||||||
@@ -383,19 +423,13 @@ static uint8_t is_fence_delim(uint16_t idx)
|
|||||||
{
|
{
|
||||||
if (idx >= n_lines) return 0;
|
if (idx >= n_lines) return 0;
|
||||||
if (is_cont(idx)) return 0;
|
if (is_cont(idx)) return 0;
|
||||||
uint32_t off = line_offset[idx];
|
uint32_t off = seg_off(idx);
|
||||||
if (off + 2 >= file_size) return 0;
|
if (off + 2 >= file_size) return 0;
|
||||||
return (uint8_t)(fb(off) == '`' &&
|
return (uint8_t)(fb(off) == '`' &&
|
||||||
fb(off + 1) == '`' &&
|
fb(off + 1) == '`' &&
|
||||||
fb(off + 2) == '`');
|
fb(off + 2) == '`');
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t is_code_body(uint16_t idx)
|
|
||||||
{
|
|
||||||
if (idx >= n_lines) return 0;
|
|
||||||
return (uint8_t)((in_code[idx >> 3] >> (idx & 7)) & 1u);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define INIT_STYLE_PLAIN 0x0
|
#define INIT_STYLE_PLAIN 0x0
|
||||||
#define INIT_STYLE_BOLD 0x1
|
#define INIT_STYLE_BOLD 0x1
|
||||||
#define INIT_STYLE_ITALIC 0x2
|
#define INIT_STYLE_ITALIC 0x2
|
||||||
@@ -408,19 +442,18 @@ static const uint8_t styles_map[] = {
|
|||||||
|
|
||||||
static uint8_t get_init_style(uint16_t idx)
|
static uint8_t get_init_style(uint16_t idx)
|
||||||
{
|
{
|
||||||
|
idx_rec_t r;
|
||||||
if (idx >= n_lines) return styles_map[INIT_STYLE_PLAIN];
|
if (idx >= n_lines) return styles_map[INIT_STYLE_PLAIN];
|
||||||
return styles_map[init_style[idx] & 7u];
|
idx_get(idx, &r);
|
||||||
|
return styles_map[r.style & 7u];
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t get_init_style_raw(uint16_t idx)
|
static uint8_t get_init_style_raw(uint16_t idx)
|
||||||
{
|
{
|
||||||
|
idx_rec_t r;
|
||||||
if (idx >= n_lines) return INIT_STYLE_PLAIN;
|
if (idx >= n_lines) return INIT_STYLE_PLAIN;
|
||||||
return init_style[idx] & 7u;
|
idx_get(idx, &r);
|
||||||
}
|
return (uint8_t)(r.style & 7u);
|
||||||
|
|
||||||
static void set_init_style_raw(uint16_t idx, uint8_t style)
|
|
||||||
{
|
|
||||||
if (idx < MAX_LINES) init_style[idx] = style & 7u;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns 1 iff the logical line at file offset `p_start` should NOT be
|
/* Returns 1 iff the logical line at file offset `p_start` should NOT be
|
||||||
@@ -579,14 +612,20 @@ static uint8_t marker_visible_col(uint8_t kind, uint32_t p_start, uint32_t conte
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Emit a new segment entry. */
|
/* Emit a new segment entry into the EMM index. cur_rec (near) mirrors the
|
||||||
|
* just-written record so set_*_cur() can add flags without a bank read-back,
|
||||||
|
* and cur_seg_off feeds the per-byte wrap logic without reading the index. */
|
||||||
static void emit_seg(uint32_t off, uint8_t style, uint8_t ckind, uint8_t cont)
|
static void emit_seg(uint32_t off, uint8_t style, uint8_t ckind, uint8_t cont)
|
||||||
{
|
{
|
||||||
if (n_lines >= MAX_LINES) return;
|
(void)ckind; /* kind is derived at render time */
|
||||||
line_offset[n_lines] = off;
|
if (n_lines >= max_lines) { index_truncated = 1; return; }
|
||||||
set_init_style_raw(n_lines, style);
|
cur_rec.off = off;
|
||||||
set_ckind(n_lines, ckind);
|
cur_rec.flags = (uint8_t)(cont ? IF_CONT : 0);
|
||||||
if (cont) set_cont(n_lines);
|
cur_rec.style = (uint8_t)(style & 7u);
|
||||||
|
cur_rec.pad0 = 0;
|
||||||
|
cur_rec.pad1 = 0;
|
||||||
|
idx_put(n_lines, &cur_rec);
|
||||||
|
cur_seg_off = off;
|
||||||
n_lines++;
|
n_lines++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -639,7 +678,7 @@ static uint8_t inline_scan(uint32_t q, uint32_t q_end, uint8_t col,
|
|||||||
char prev_ch = ' ';
|
char prev_ch = ' ';
|
||||||
uint8_t seg_col = col;
|
uint8_t seg_col = col;
|
||||||
|
|
||||||
while (q < q_end && n_lines < MAX_LINES) {
|
while (q < q_end && n_lines < max_lines) {
|
||||||
char ch = fb(q);
|
char ch = fb(q);
|
||||||
|
|
||||||
if (ch == '\n') {
|
if (ch == '\n') {
|
||||||
@@ -690,9 +729,9 @@ static uint8_t inline_scan(uint32_t q, uint32_t q_end, uint8_t col,
|
|||||||
seg_col++; q++; prev_ch = ch;
|
seg_col++; q++; prev_ch = ch;
|
||||||
}
|
}
|
||||||
if (seg_col >= SCREEN_W) {
|
if (seg_col >= SCREEN_W) {
|
||||||
uint32_t wrap_at = (last_space != 0xFFFFFFFFu && last_space > line_offset[n_lines - 1])
|
uint32_t wrap_at = (last_space != 0xFFFFFFFFu && last_space > cur_seg_off)
|
||||||
? last_space : q;
|
? last_space : q;
|
||||||
if (wrap_at >= q_end || wrap_at == line_offset[n_lines - 1]) break;
|
if (wrap_at >= q_end || wrap_at == cur_seg_off) break;
|
||||||
emit_seg(wrap_at, line_style, ckind, 1);
|
emit_seg(wrap_at, line_style, ckind, 1);
|
||||||
seg_col = col; last_space = 0xFFFFFFFFu; prev_ch = ' '; q = wrap_at;
|
seg_col = col; last_space = 0xFFFFFFFFu; prev_ch = ' '; q = wrap_at;
|
||||||
}
|
}
|
||||||
@@ -704,16 +743,12 @@ static void index_lines(void)
|
|||||||
{
|
{
|
||||||
uint8_t in_block = 0;
|
uint8_t in_block = 0;
|
||||||
n_lines = 0;
|
n_lines = 0;
|
||||||
memset(in_code, 0, sizeof(in_code));
|
index_truncated = 0;
|
||||||
memset(cont_flag, 0, sizeof(cont_flag));
|
cur_seg_off = 0xFFFFFFFFu; /* no segment emitted yet */
|
||||||
memset(nowrap_flag, 0, sizeof(nowrap_flag));
|
|
||||||
memset(blank_flag, 0, sizeof(blank_flag));
|
|
||||||
memset(line_kind, 0, sizeof(line_kind));
|
|
||||||
memset(init_style, 0, sizeof(init_style));
|
|
||||||
if (file_size == 0) return;
|
if (file_size == 0) return;
|
||||||
|
|
||||||
uint32_t p = 0;
|
uint32_t p = 0;
|
||||||
while (p < file_size && n_lines < MAX_LINES) {
|
while (p < file_size && n_lines < max_lines) {
|
||||||
if ((n_lines & 15) == 0) spinner_tick();
|
if ((n_lines & 15) == 0) spinner_tick();
|
||||||
|
|
||||||
/* Blank line = paragraph separator. Emit one empty screen row
|
/* Blank line = paragraph separator. Emit one empty screen row
|
||||||
@@ -721,9 +756,9 @@ static void index_lines(void)
|
|||||||
if (is_line_blank(p)) {
|
if (is_line_blank(p)) {
|
||||||
while (p < file_size && fb(p) != '\n') p++;
|
while (p < file_size && fb(p) != '\n') p++;
|
||||||
if (p < file_size) p++;
|
if (p < file_size) p++;
|
||||||
if (n_lines == 0 || line_offset[n_lines - 1] != p) {
|
if (n_lines == 0 || cur_seg_off != p) {
|
||||||
emit_seg(p, INIT_STYLE_PLAIN, CK_PLAIN, 0);
|
emit_seg(p, INIT_STYLE_PLAIN, CK_PLAIN, 0);
|
||||||
set_blank(n_lines - 1);
|
set_blank_cur();
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -732,15 +767,15 @@ static 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(n_lines - 1);
|
set_nowrap_cur();
|
||||||
while (p < file_size && fb(p) != '\n') p++;
|
while (p < file_size && fb(p) != '\n') p++;
|
||||||
if (p < file_size) 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(n_lines - 1);
|
set_nowrap_cur();
|
||||||
in_code[(n_lines - 1) >> 3] |= (uint8_t)(1u << ((n_lines - 1) & 7));
|
set_code_cur();
|
||||||
while (p < file_size && fb(p) != '\n') p++;
|
while (p < file_size && fb(p) != '\n') p++;
|
||||||
if (p < file_size) p++;
|
if (p < file_size) p++;
|
||||||
continue;
|
continue;
|
||||||
@@ -749,7 +784,7 @@ static void index_lines(void)
|
|||||||
/* ---- horizontal rule ---- */
|
/* ---- horizontal rule ---- */
|
||||||
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(n_lines - 1);
|
set_nowrap_cur();
|
||||||
while (p < file_size && fb(p) != '\n') p++;
|
while (p < file_size && fb(p) != '\n') p++;
|
||||||
if (p < file_size) p++;
|
if (p < file_size) p++;
|
||||||
continue;
|
continue;
|
||||||
@@ -804,7 +839,7 @@ static void index_lines(void)
|
|||||||
char prev_ch = ' ';
|
char prev_ch = ' ';
|
||||||
uint8_t seg_col = 0;
|
uint8_t seg_col = 0;
|
||||||
|
|
||||||
while (q < file_size && n_lines < MAX_LINES) {
|
while (q < file_size && n_lines < max_lines) {
|
||||||
char ch = fb(q);
|
char ch = fb(q);
|
||||||
uint8_t soft_break = 0;
|
uint8_t soft_break = 0;
|
||||||
|
|
||||||
@@ -901,9 +936,9 @@ static void index_lines(void)
|
|||||||
prev_ch = ch;
|
prev_ch = ch;
|
||||||
}
|
}
|
||||||
if (seg_col >= SCREEN_W) {
|
if (seg_col >= SCREEN_W) {
|
||||||
uint32_t wrap_at = (last_space != 0xFFFFFFFFu && last_space > line_offset[n_lines - 1])
|
uint32_t wrap_at = (last_space != 0xFFFFFFFFu && last_space > cur_seg_off)
|
||||||
? last_space : q;
|
? last_space : q;
|
||||||
if (wrap_at >= file_size || wrap_at == line_offset[n_lines - 1]) break;
|
if (wrap_at >= file_size || wrap_at == cur_seg_off) break;
|
||||||
emit_seg(wrap_at, line_style, CK_PLAIN, 1);
|
emit_seg(wrap_at, line_style, CK_PLAIN, 1);
|
||||||
seg_col = 0; last_space = 0xFFFFFFFFu; prev_ch = ' '; q = wrap_at;
|
seg_col = 0; last_space = 0xFFFFFFFFu; prev_ch = ' '; q = wrap_at;
|
||||||
}
|
}
|
||||||
@@ -940,13 +975,11 @@ static void render_line(uint16_t line_idx, uint8_t row)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (line_idx < n_lines) {
|
if (line_idx < n_lines) {
|
||||||
uint32_t p = line_offset[line_idx];
|
uint32_t p = seg_off(line_idx);
|
||||||
uint32_t content_off = p;
|
uint32_t content_off = p;
|
||||||
uint8_t cont = is_cont(line_idx);
|
uint8_t cont = is_cont(line_idx);
|
||||||
/* Right bound for this seg: start of next seg, or file end. */
|
/* Right bound for this seg: start of next seg, or file end. */
|
||||||
uint32_t seg_end = (line_idx + 1 < n_lines)
|
uint32_t seg_end = seg_off((uint16_t)(line_idx + 1));
|
||||||
? line_offset[line_idx + 1]
|
|
||||||
: file_size;
|
|
||||||
if (seg_end > file_size) seg_end = file_size;
|
if (seg_end > file_size) seg_end = file_size;
|
||||||
uint8_t kind;
|
uint8_t kind;
|
||||||
|
|
||||||
@@ -955,7 +988,7 @@ static void render_line(uint16_t line_idx, uint8_t row)
|
|||||||
if (cont) {
|
if (cont) {
|
||||||
uint16_t first = line_idx;
|
uint16_t first = line_idx;
|
||||||
while (first > 0 && is_cont((uint16_t)(first - 1))) first--;
|
while (first > 0 && is_cont((uint16_t)(first - 1))) first--;
|
||||||
uint32_t p_start = line_offset[first];
|
uint32_t p_start = seg_off(first);
|
||||||
uint32_t content_off = p_start;
|
uint32_t content_off = p_start;
|
||||||
uint8_t kind = classify_line(p_start, &content_off);
|
uint8_t kind = classify_line(p_start, &content_off);
|
||||||
uint8_t indent = marker_visible_col(kind, p_start, content_off);
|
uint8_t indent = marker_visible_col(kind, p_start, content_off);
|
||||||
@@ -975,7 +1008,7 @@ static void render_line(uint16_t line_idx, uint8_t row)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* start rendering from the wrapped segment offset */
|
/* start rendering from the wrapped segment offset */
|
||||||
p = line_offset[line_idx];
|
p = seg_off(line_idx);
|
||||||
content_off = p;
|
content_off = p;
|
||||||
/* For cont segs, inline parsing continues normally. */
|
/* For cont segs, inline parsing continues normally. */
|
||||||
}
|
}
|
||||||
@@ -1145,7 +1178,7 @@ static void render_line(uint16_t line_idx, uint8_t row)
|
|||||||
* content before the end of the line, overlay the last cell with '>'. */
|
* content before the end of the line, overlay the last cell with '>'. */
|
||||||
if (is_nowrap(line_idx) && col >= SCREEN_W) {
|
if (is_nowrap(line_idx) && col >= SCREEN_W) {
|
||||||
uint32_t pp = p;
|
uint32_t pp = p;
|
||||||
uint32_t left_bound = line_offset[line_idx];
|
uint32_t left_bound = seg_off(line_idx);
|
||||||
while (pp < seg_end) {
|
while (pp < seg_end) {
|
||||||
char c = fb(pp);
|
char c = fb(pp);
|
||||||
if (c == '\n' || c == '\r') break;
|
if (c == '\n' || c == '\r') break;
|
||||||
@@ -1357,14 +1390,6 @@ static void die(const char *msg)
|
|||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
memset(line_offset, 0, sizeof(line_offset));
|
|
||||||
memset(cont_flag, 0, sizeof(cont_flag));
|
|
||||||
memset(in_code, 0, sizeof(in_code));
|
|
||||||
memset(nowrap_flag, 0, sizeof(nowrap_flag));
|
|
||||||
memset(blank_flag, 0, sizeof(blank_flag));
|
|
||||||
memset(line_kind, 0, sizeof(line_kind));
|
|
||||||
memset(init_style, 0, sizeof(init_style));
|
|
||||||
|
|
||||||
const char *path;
|
const char *path;
|
||||||
if (argc >= 2) path = argv[1];
|
if (argc >= 2) path = argv[1];
|
||||||
else path = "SAMPLE.MD";
|
else path = "SAMPLE.MD";
|
||||||
@@ -1400,6 +1425,7 @@ int main(int argc, char **argv)
|
|||||||
case -2: puts("(size > 128K or seek failed)"); break;
|
case -2: puts("(size > 128K or seek failed)"); break;
|
||||||
case -3: puts("(mem_alloc_pages failed)"); break;
|
case -3: puts("(mem_alloc_pages failed)"); break;
|
||||||
case -4: puts("(short read)"); break;
|
case -4: puts("(short read)"); break;
|
||||||
|
case -5: puts("(index alloc failed)"); break;
|
||||||
}
|
}
|
||||||
puts("Press any key to exit...");
|
puts("Press any key to exit...");
|
||||||
(void)getkey();
|
(void)getkey();
|
||||||
|
|||||||
Reference in New Issue
Block a user