mdview2: ускорение препроцессинга — asm fb() + инлайн рендер-цикла
Профилирование (52КБ README, раздельный замер): препроцессинг 24с, из них bank I/O ~0с, cont-walk ~1с — всё время в посимвольных скан-циклах (индексация 8с + рендер 13с, два прохода по файлу). - fb() переписана на ассемблере (была закомментированная заготовка): убран вызов функции и 32-битная арифметика на каждый байт. Раскладка __sdcccall(1) для uint32 аргумента (p=HLDE) и возврат char в A сверены через sdcc -S; координация маппинга с bank_read держится на том, что _io_page_w3 — порт (__sfr 0xE2), пишем OUT — bank_read читает IN. - render_line_to_cache: горячий путь обычного текста больше не вызывает handle_inline_marker (6 аргументов) на каждый символ — только на маркерах ` * _ ~; cc_put заинлайнен. Поведение идентично. Итого 24→18с. Индекс-скан (8с) лёгкого инлайна не имеет (нет per-char вызовов). Следующий шаг — merge двух проходов в один. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+52
-44
@@ -350,60 +350,61 @@ static inline void map_page(uint8_t page)
|
|||||||
/* Читает один байт логического файла по смещению p.
|
/* Читает один байт логического файла по смещению p.
|
||||||
* При переходе между страницами автоматически переключает W3.
|
* При переходе между страницами автоматически переключает W3.
|
||||||
* Проверка границ выполняется вызывающей стороной.
|
* Проверка границ выполняется вызывающей стороной.
|
||||||
*/
|
*
|
||||||
|
* Эквивалент C-кода (оставлен для справки):
|
||||||
static char fb(uint32_t p)
|
* uint8_t *pb = (uint8_t *)&p;
|
||||||
|
* uint8_t page = (pb[1] >> 6) | (pb[2] << 2);
|
||||||
|
* uint16_t off = ((pb[1] & 0x3F) << 8) | pb[0];
|
||||||
|
* map_page(page); // if (page!=cur_page){cur_page=page; OUT W3,file_phys[page];}
|
||||||
|
* return FILE_BUF[off]; // *(0xC000 + off)
|
||||||
|
*
|
||||||
|
* Ручной asm — это горячая примитивная операция (вызывается сотни тысяч раз
|
||||||
|
* при индексации 50КБ-файла); C-версия тратила вызов функции + 32-битную
|
||||||
|
* арифметику на КАЖДЫЙ байт. Раскладка __sdcccall(1): аргумент uint32_t в
|
||||||
|
* HLDE (E=pb[0], D=pb[1], L=pb[2], H=pb[3] — проверено sdcc -S), возврат
|
||||||
|
* char в A. Координация маппинга с bank_read/bank_write держится на том,
|
||||||
|
* что _io_page_w3 — это ПОРТ (__sfr 0xE2): здесь пишем его через OUT,
|
||||||
|
* bank_read читает через IN — общий аппаратный регистр, без теневой RAM.
|
||||||
|
* NB: __naked обязателен (внутри собственный ret); IX/IY не трогаем
|
||||||
|
* (callee-saved), BC/DE/HL/AF — caller-saved, можно клобберить. */
|
||||||
|
static char fb(uint32_t p) __naked
|
||||||
{
|
{
|
||||||
uint8_t *pb = (uint8_t *)&p;
|
(void)p; /* p = HLDE; доступ только из asm */
|
||||||
uint8_t page = (uint8_t)((pb[1] >> 6) | (pb[2] << 2));
|
__asm
|
||||||
uint16_t off = (uint16_t)(((uint16_t)(pb[1] & 0x3F) << 8) | pb[0]);
|
; off -> BC = 0xC000 + ((pb[1]&0x3F)<<8 | pb[0]) ; D=pb[1], E=pb[0]
|
||||||
map_page(page);
|
|
||||||
return FILE_BUF[off];
|
|
||||||
/*
|
|
||||||
// p = HLDE
|
|
||||||
(void)p;
|
|
||||||
|
|
||||||
__asm
|
|
||||||
; uint16_t off = (uint16_t)(((uint16_t)(pb[1] | 0xC0) << 8) | pb[0]);
|
|
||||||
ld a, d
|
ld a, d
|
||||||
or a, #0xC0
|
or a, #0xC0 ; (D|0xC0) == (D&0x3F)|0xC0 — старший байт окна W3
|
||||||
ld b, a
|
ld b, a
|
||||||
ld c, e
|
ld c, e ; C = pb[0]
|
||||||
|
|
||||||
; uint8_t page = (uint8_t)((pb[1] >> 6) | (pb[2] << 2));
|
|
||||||
ld a, l ;; pb[2] << 2
|
|
||||||
add a, a
|
|
||||||
add a, a
|
|
||||||
ld e, a ;; e = pb[2] << 2
|
|
||||||
|
|
||||||
ld a, d ;; pb[1] >> 6
|
|
||||||
rlca
|
|
||||||
rlca
|
|
||||||
and a, #0x03
|
|
||||||
|
|
||||||
or a, e ;; a = (pb[2] << 2) | (pb[1] >> 6)
|
; page = (pb[1]>>6) | (pb[2]<<2) ; L=pb[2]
|
||||||
ld e, a
|
ld a, l
|
||||||
; map_page(page);
|
add a, a
|
||||||
; if (page != cur_page) {
|
add a, a ; A = pb[2]<<2
|
||||||
|
ld e, a ; E свободен (pb[0] уже сохранён в C)
|
||||||
|
|
||||||
|
ld a, d
|
||||||
|
rlca
|
||||||
|
rlca
|
||||||
|
and a, #0x03 ; A = pb[1]>>6
|
||||||
|
or a, e ; A = page
|
||||||
|
ld e, a ; E = page
|
||||||
|
|
||||||
|
; map_page(page)
|
||||||
ld a, (_cur_page)
|
ld a, (_cur_page)
|
||||||
cp e
|
cp e
|
||||||
jr Z, __fbl0
|
jr Z, fb_same
|
||||||
; cur_page = page;
|
|
||||||
ld a, e
|
ld a, e
|
||||||
ld (_cur_page), a
|
ld (_cur_page), a
|
||||||
; sprinter_page_w3(file_phys[page]);
|
|
||||||
ld hl, #_file_phys
|
ld hl, #_file_phys
|
||||||
ld d, #0x00
|
ld d, #0x00
|
||||||
add hl, de
|
add hl, de ; hl = &file_phys[page]
|
||||||
ld a, (hl)
|
ld a, (hl)
|
||||||
out (__io_page_w3), a
|
out (#0xE2), a ; OUT PORT_PAGE_W3 (== _io_page_w3 sfr)
|
||||||
; }
|
fb_same:
|
||||||
__fbl0:
|
ld a, (bc) ; A = *(0xC000 + off)
|
||||||
; return FILE_BUF[off];
|
|
||||||
ld a, (bc)
|
|
||||||
ret
|
ret
|
||||||
__endasm;
|
__endasm;
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ESTEX WINREST (5Ah, RST 0x10) — копирует готовый буфер (char,attr)
|
/* ESTEX WINREST (5Ah, RST 0x10) — копирует готовый буфер (char,attr)
|
||||||
@@ -1663,7 +1664,10 @@ static void render_line_to_cache(uint16_t line_idx)
|
|||||||
ch = ' ';
|
ch = ' ';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parse_inline) {
|
/* handle_inline_marker реагирует только на ` * _ ~ — для остальных
|
||||||
|
* символов (подавляющее большинство) она возвращает 0 без side-эффектов.
|
||||||
|
* Пропускаем вызов функции (6 аргументов) на горячем пути обычного текста. */
|
||||||
|
if (parse_inline && (ch == '`' || ch == '*' || ch == '_' || ch == '~')) {
|
||||||
char lit_ch = 0;
|
char lit_ch = 0;
|
||||||
uint8_t mk = handle_inline_marker(ch, &p, seg_end, prev_ch, &emph, &lit_ch);
|
uint8_t mk = handle_inline_marker(ch, &p, seg_end, prev_ch, &emph, &lit_ch);
|
||||||
if (mk == 1) {
|
if (mk == 1) {
|
||||||
@@ -1689,7 +1693,11 @@ static void render_line_to_cache(uint16_t line_idx)
|
|||||||
cc = tgt;
|
cc = tgt;
|
||||||
prev_ch = ' ';
|
prev_ch = ' ';
|
||||||
} else {
|
} else {
|
||||||
cc_put(cellbuf, &col, ch, cur_attr);
|
/* инлайн cc_put: col < MAX_CACHE_LINE_LEN гарантирован условием while */
|
||||||
|
uint16_t ci = (uint16_t)col * 2u;
|
||||||
|
cellbuf[ci] = (uint8_t)ch;
|
||||||
|
cellbuf[ci + 1] = cur_attr;
|
||||||
|
col++;
|
||||||
cc++;
|
cc++;
|
||||||
prev_ch = ch;
|
prev_ch = ch;
|
||||||
}
|
}
|
||||||
@@ -1744,7 +1752,7 @@ static void draw_line_from_cache(uint16_t line_idx, uint8_t row)
|
|||||||
* перерисовки видимой области после загрузки файла (Фаза 3-4). */
|
* перерисовки видимой области после загрузки файла (Фаза 3-4). */
|
||||||
static void draw_viewport_from_cache(void)
|
static void draw_viewport_from_cache(void)
|
||||||
{
|
{
|
||||||
for (uint8_t i = 0; i < VIEW_H - 1; i++) {
|
for (uint8_t i = 0; i < VIEW_H; i++) {
|
||||||
draw_line_from_cache((uint16_t)(top_line + i), (uint8_t)(VIEW_TOP_ROW + i));
|
draw_line_from_cache((uint16_t)(top_line + i), (uint8_t)(VIEW_TOP_ROW + i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user