libbgi: chunked-заливки на мульти-триггере акселератора + сплит rectfill
Семантика FSM акселератора вскрыта по драйверу MAME (sprinter.cpp) и подтверждена экспериментами в MAME (tests/accfill): армирование живёт до следующего accel-опкода (мульти-триггер работает); под армированием триггерит ЛЮБОЙ non-M1 доступ к памяти (fetch операнда djnz/out!); вертикальный Fill двигает Port_Y и не возвращает; размер блока переживает LD B,B. Детали: memory/accel_multitrigger_fill. - _bgi_clear_raw: 20 DI-скобок по 16 колонок вместо полного брекета на каждую из 320 колонок; армирование размера один раз на скобку. - _gfx_rectfill256 разделён: диспетчер (проверка ориентации ~160-245Т, break-even |w-h| >= ~4) + leaf'ы _gfx_recthfill256/_gfx_rectvfill256 для прямого вызова, когда форма известна заранее. - Leaf'ы: чанки <=16 линий одной скобкой (~54-56Т/линию против ~250Т у per-line варианта); счётчик чанков precompute'ится в байт-регистр (dec e/jr nz ~46Т/чанк вместо 16-бит арифметики в IX-слотах ~173Т/чанк); хвост — отдельная скобка со своим армированием (CBL-ISR в окне EI армирует акселератор своим размером — не выносить). - getpixel/putpixel/_bgi_read: уборка мёртвого закомментированного кода. - tests/accfill: регресс chunked-заливок (B0 clear, B1 vert 1+3 чанка, B2 horz чанк+хвост, полноширинный bar 320x8 — путь w>=256). Прежние реализации сохранены под #if 0 для отката/сравнения. Проверено в MAME (все PASS); семантика эмуляции — перепроверить на реальном Sprinter. size-baseline: accfill добавлен, gfx_demo +54 Б (обвязка диспетчера), остальные без роста. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+8
-2
@@ -102,9 +102,15 @@ void _gfx_hfill256_segment(void);
|
||||
void _gfx_vfill256_segment(void);
|
||||
|
||||
/* Заливка прямоугольника цветом: x, y, w, h, color.
|
||||
* Механизм: один раз задаётся Horizontal_Size, затем в цикле по Y
|
||||
* акселератор копирует байт цвета на ширину строки (docs/part2/accelerator_doc.txt). */
|
||||
* _gfx_rectfill256 — диспетчер: выбирает ориентацию с меньшим числом
|
||||
* выстрелов (~160-245Т на проверку) и tail-jump'ит в leaf. Если форма
|
||||
* известна заранее или |w-h| < ~4, выгоднее звать leaf напрямую:
|
||||
* _gfx_recthfill256 — h горизонтальных строк, ТРЕБУЕТ 1 <= w <= 256;
|
||||
* _gfx_rectvfill256 — w вертикальных колонок, ТРЕБУЕТ 1 <= h <= 256.
|
||||
* Контракты/расчёт break-even — в шапках bgi256/_gfx_rect*fill256.c. */
|
||||
void _gfx_rectfill256(int x, int y, int w, int h, uint8_t color);
|
||||
void _gfx_recthfill256(int x, int y, int w, int h, uint8_t color);
|
||||
void _gfx_rectvfill256(int x, int y, int w, int h, uint8_t color);
|
||||
|
||||
/* ---- Скретч 16-цветных примитивов (bgi16/_gfx_g16_state.c) ------- */
|
||||
extern uint8_t _gfx_g16_y;
|
||||
|
||||
@@ -1,12 +1,59 @@
|
||||
/*
|
||||
* _bgi_clear_raw — залить всё поле 320×256 цветом color, mode 0x81.
|
||||
* 320 вертикальных Fill-burst'ов (по колонке 256 строк), Port_Y=0.
|
||||
*
|
||||
* Chunked-схема (v2, 2026-07-10): 20 DI-скобок по 16 колонок (256 строк
|
||||
* каждая) — армирование размера один раз на скобку, на колонку только
|
||||
* ре-арм LD E,E + выстрел (см. _gfx_rectfill256.c и
|
||||
* memory/accel_multitrigger_fill; проверено в MAME tests/accfill).
|
||||
* Окно DI на чанк ≈ 0.6 мс (16×256 байт) — CBL/IM2 живут.
|
||||
* Вертикальный Fill двигает Port_Y — re-OUT 0 перед каждой колонкой.
|
||||
*
|
||||
* Register-only asm, без глобального скретча. W3-naive: вызывающий
|
||||
* обрамляет _bgi_begin/_bgi_end. color → A (__sdcccall(1)).
|
||||
*/
|
||||
|
||||
#include "../_bgi.h"
|
||||
|
||||
void _bgi_clear_raw(uint8_t color) __naked
|
||||
{
|
||||
__asm
|
||||
.globl __gfx_addr_base
|
||||
ld c, a ; C = цвет
|
||||
ld hl, (__gfx_addr_base) ; HL = база draw-страницы
|
||||
ld d, #0 ; y0 = 0
|
||||
ld e, #20 ; 20 чанков × 16 колонок = 320
|
||||
|
||||
clr_chunk:
|
||||
di
|
||||
;; Армирование размера НЕ выносить из цикла (экономия ~0.1%):
|
||||
;; в окне EI между чанками CBL-ISR сам армирует акселератор
|
||||
;; своим размером блока — каждый чанк обязан армировать заново.
|
||||
ld d, d ; 0x52 — режим размера блока (no-op для D)
|
||||
ld a, #0 ; размер 256 (конвенция акселератора)
|
||||
ld b, b ; выключить — размер сохранён
|
||||
ld b, #16 ; колонок в чанке
|
||||
clr_col:
|
||||
ld a, d
|
||||
out (#0x89), a ; Port_Y = 0 (vertical Fill его сдвинул)
|
||||
ld e, e ; 0x5B — вертикальный Fill (no-op для E)
|
||||
ld a, c
|
||||
ld (hl), a ; выстрел: колонка 256 строк
|
||||
ld b, b ; выключить (перед djnz — fetch смещения!)
|
||||
inc hl ; следующая колонка
|
||||
djnz clr_col
|
||||
ei ; окно для прерываний между чанками
|
||||
dec e
|
||||
jr NZ, clr_chunk
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* ================================================================
|
||||
* Старый вариант (до 2026-07-10): полный брекет (DI+арм размера+
|
||||
* режим+выстрел+выкл+EI) на КАЖДУЮ из 320 колонок. Оставлен
|
||||
* закомментированным для отката/сравнения.
|
||||
* ================================================================ */
|
||||
void _bgi_clear_raw(uint8_t color) __naked
|
||||
{
|
||||
__asm
|
||||
@@ -39,3 +86,4 @@ void _bgi_clear_raw(uint8_t color) __naked
|
||||
jr _clear_cyc0
|
||||
__endasm;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,19 +1,71 @@
|
||||
/*
|
||||
* _gfx_rectfill256 — заливка прямоугольника (x,y,w,h) цветом color, mode 0x81.
|
||||
* _gfx_rectfill256 — диспетчер заливки прямоугольника (x,y,w,h,color),
|
||||
* mode 0x81: выбирает ориентацию с меньшим числом выстрелов и передаёт
|
||||
* управление leaf-заливке (tail-jump, стек не трогается — ABI идентичен):
|
||||
* w>h && w<=256 → _gfx_recthfill256 (h горизонтальных строк)
|
||||
* иначе → _gfx_rectvfill256 (w вертикальных колонок)
|
||||
*
|
||||
* Выбор ориентации burst'а: если w>h и w<=256 — гоним h горизонтальных
|
||||
* Fill'ов шириной w (y++ на каждой строке); иначе w вертикальных Fill'ов
|
||||
* высотой h (addr++ на каждой колонке). Инварианты (color=C, len=B) и
|
||||
* бегущий addr=HL/y=E живут в регистрах через весь цикл — _gfx_*_segment
|
||||
* их сохраняет; счётчик другой размерности крутится в его стек-слоте.
|
||||
* ---- ЦЕНА ПРОВЕРКИ и когда звать leaf'ы напрямую -------------------
|
||||
* Проверка + tail-jump: ~160Т (быстрый путь w<h) … ~245Т (w>256),
|
||||
* типично ~200Т ≈ 9.5 мкс @21МГц. Один выстрел-линия стоит ~55-65Т
|
||||
* CPU-оверхеда, т.е. проверка окупается, когда выбор ориентации
|
||||
* экономит |w−h| >= ~4 линии. На мелких/почти квадратных
|
||||
* прямоугольниках (|w−h| < 4) или когда форма известна заранее —
|
||||
* выгоднее прямой вызов _gfx_recthfill256 / _gfx_rectvfill256
|
||||
* (контракты — в их шапках: recthfill требует 1<=w<=256).
|
||||
*
|
||||
* __sdcccall(1): x→HL, y→DE(E=y); w→4/5(ix), h→6/7(ix), color→8(ix).
|
||||
* Pre: W3 замаплен, DI активен вокруг выстрелов внутри segment'ов.
|
||||
* Вызывающий управляет _bgi_begin/_bgi_end. w,h предполагаются >= 0.
|
||||
* Диспетчер сохраняет HL/DE и стек как есть; leaf делает свой
|
||||
* push ix и callee-pops. Pre/контракты — как у leaf'ов (W3 замаплен,
|
||||
* без клиппинга, w,h >= 0).
|
||||
*/
|
||||
|
||||
#include "../_bgi.h"
|
||||
|
||||
void _gfx_rectfill256(int x, int y, int w, int h, uint8_t color) __naked
|
||||
{
|
||||
__asm
|
||||
.globl __gfx_recthfill256
|
||||
.globl __gfx_rectvfill256
|
||||
push ix
|
||||
ld ix, #0
|
||||
add ix, sp ; только чтобы прочитать w/h; HL/DE не трогаем
|
||||
|
||||
;; --- w>h && w<=256 ? (unsigned, w/h >= 0) ---
|
||||
ld a, 4 (ix)
|
||||
sub a, 6 (ix)
|
||||
ld b, a ; (w-h) низкий
|
||||
ld a, 5 (ix)
|
||||
sbc a, 7 (ix) ; CF=1 если w<h
|
||||
jr C, disp_v
|
||||
or a, b
|
||||
jr Z, disp_v ; w==h -> не (w>h)
|
||||
ld a, 5 (ix) ; w high
|
||||
cp a, #2
|
||||
jr NC, disp_v ; w >= 512
|
||||
dec a
|
||||
jr NZ, disp_h ; high==0 -> w<256
|
||||
ld a, 4 (ix)
|
||||
or a, a
|
||||
jr NZ, disp_v ; high==1,low!=0 -> w>256
|
||||
; w==256 -> горизонталь (низкий байт 0 = «256»)
|
||||
disp_h:
|
||||
pop ix
|
||||
jp __gfx_recthfill256 ; tail-jump: аргументы на стеке как есть
|
||||
disp_v:
|
||||
pop ix
|
||||
jp __gfx_rectvfill256
|
||||
__endasm;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* ================================================================
|
||||
* Старый вариант (до 2026-07-10): полный брекет (арм+выстрел+выкл)
|
||||
* на КАЖДУЮ линию через call _gfx_*fill256_segment — ~100Т+call на
|
||||
* линию. Оставлен закомментированным для отката/сравнения.
|
||||
* (Промежуточный monolith-вариант с chunked-ветками жил здесь
|
||||
* 2026-07-10 и разъехался по _gfx_recthfill256.c/_gfx_rectvfill256.c.)
|
||||
* ================================================================ */
|
||||
void _gfx_rectfill256(int x, int y, int w, int h, uint8_t color) __naked
|
||||
{
|
||||
__asm
|
||||
@@ -89,3 +141,4 @@ void _gfx_rectfill256(int x, int y, int w, int h, uint8_t color) __naked
|
||||
jp (hl)
|
||||
__endasm;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* _gfx_recthfill256 — заливка прямоугольника (x,y,w,h) ГОРИЗОНТАЛЬНЫМИ
|
||||
* строками (h выстрелов шириной w), mode 0x81. Chunked-схема: скобки
|
||||
* DI/EI по ≤16 строк, армирование размера один раз на скобку, на строку —
|
||||
* OUT Port_Y + LD C,C + выстрел (семантика мульти-триггера:
|
||||
* memory/accel_multitrigger_fill, проверено в MAME tests/accfill).
|
||||
*
|
||||
* Счётчик чанков — precompute (v3): один раз на входе E = h>>4 (полных
|
||||
* чанков, ≤16 при h<=256) и h&15 — хвост (SMC в ld b,#n хвостовой
|
||||
* скобки). Заголовок чанка = dec e/jr nz (~46Т на чанк ≈ 2.9Т/линию
|
||||
* против ~173Т/чанк у 16-бит арифметики через IX-слоты); precompute
|
||||
* ~170Т однократно. Итог ~54Т/линию. Хвост — отдельная скобка со
|
||||
* СВОИМ армированием (второй SMC-слот размера): армирование обязано
|
||||
* быть в каждой скобке — в окне EI CBL-ISR армирует акселератор своим
|
||||
* размером блока.
|
||||
*
|
||||
* Прямой вызов оправдан, когда форма известна заранее (широкие/низкие
|
||||
* прямоугольники, w > h) — экономит ~160-245Т проверки диспетчера
|
||||
* _gfx_rectfill256 (см. там же расчёт break-even).
|
||||
*
|
||||
* ---- КОНТРАКТ (ответственность вызывающего) ------------------------
|
||||
* - 1 <= w <= 256 (w == 256 кодируется младшим байтом 0 — конвенция
|
||||
* акселератора; w == 0 даст 256!). Для w > 256 использовать
|
||||
* _gfx_rectvfill256 (или диспетчер). 0 <= h <= 256 (h == 0 — тихий
|
||||
* выход; старший байт h интерпретируется как «+16 чанков», т.е.
|
||||
* h > 511 — UB). Клиппинга НЕТ, координаты обязаны быть валидны.
|
||||
* - W3 замаплен (_bgi_begin у вызывающего).
|
||||
*
|
||||
* __sdcccall(1): x→HL, y→DE(E=y); w→4/5(ix), h→6/7(ix), color→8(ix);
|
||||
* __naked → callee-pops 5 байт. Инварианты в регистрах: HL=addr
|
||||
* (константа — строка задаётся Port_Y), C=цвет, D=бегущий y,
|
||||
* E=счётчик чанков, B=djnz внутри скобки.
|
||||
*/
|
||||
|
||||
#include "../_bgi.h"
|
||||
|
||||
void _gfx_recthfill256(int x, int y, int w, int h, uint8_t color) __naked
|
||||
{
|
||||
__asm
|
||||
.globl __gfx_addr_base
|
||||
push ix
|
||||
ld ix, #0
|
||||
add ix, sp
|
||||
ld bc, (__gfx_addr_base)
|
||||
add hl, bc ; HL = base + x (константа)
|
||||
ld d, e ; D = бегущий y
|
||||
ld a, 8 (ix)
|
||||
ld c, a ; C = color
|
||||
ld a, 4 (ix)
|
||||
ld (rh_len), a ; SMC: размер блока <- w (0 = 256)
|
||||
ld (rh_len2), a ; ...и в хвостовую скобку
|
||||
|
||||
;; --- precompute (один раз): E = h>>4, хвост h&15 -> SMC ---
|
||||
ld a, 6 (ix)
|
||||
and #0x0F
|
||||
ld (rh_tail), a ; SMC: хвост <- h & 15
|
||||
ld a, 6 (ix)
|
||||
rrca
|
||||
rrca
|
||||
rrca
|
||||
rrca
|
||||
and #0x0F
|
||||
ld e, a ; E = (h низкий) >> 4
|
||||
ld a, 7 (ix)
|
||||
or a, a
|
||||
jr Z, rh_go ; h < 256
|
||||
ld a, e
|
||||
add a, #16 ; старший байт: h==256 => ровно +16 чанков
|
||||
ld e, a
|
||||
rh_go:
|
||||
ld a, e
|
||||
or a, a
|
||||
jr Z, rh_tail_go ; полных чанков нет
|
||||
|
||||
;; --- E полных чанков по 16 строк ---
|
||||
;; (армирование НЕ выносить из цикла: в окне EI CBL-ISR
|
||||
;; армирует акселератор своим размером блока)
|
||||
rh_chunk:
|
||||
ld b, #16 ; строк в чанке
|
||||
di
|
||||
ld d, d ; 0x52 — режим размера блока (no-op для D=y)
|
||||
ld a, #0
|
||||
rh_len = . - 1
|
||||
ld b, b ; выключить — размер сохранён
|
||||
rh_row:
|
||||
ld a, d
|
||||
out (#0x89), a ; Port_Y = y (акселератор ВЫКЛЮЧЕН)
|
||||
ld c, c ; 0x49 — горизонтальный Fill (no-op для C)
|
||||
ld a, c
|
||||
ld (hl), a ; выстрел: строка w байт
|
||||
ld b, b ; выключить (перед djnz — fetch смещения!)
|
||||
inc d ; y++
|
||||
djnz rh_row
|
||||
ei ; окно для прерываний между чанками
|
||||
dec e
|
||||
jr NZ, rh_chunk
|
||||
|
||||
;; --- хвост h&15 строк: отдельная скобка со СВОИМ армированием ---
|
||||
rh_tail_go:
|
||||
ld a, #0
|
||||
rh_tail = . - 1
|
||||
or a, a
|
||||
jr Z, rh_done ; хвоста нет
|
||||
ld b, a
|
||||
di
|
||||
ld d, d
|
||||
ld a, #0
|
||||
rh_len2 = . - 1
|
||||
ld b, b
|
||||
rh_trow:
|
||||
ld a, d
|
||||
out (#0x89), a
|
||||
ld c, c
|
||||
ld a, c
|
||||
ld (hl), a
|
||||
ld b, b
|
||||
inc d
|
||||
djnz rh_trow
|
||||
ei
|
||||
rh_done:
|
||||
pop ix
|
||||
pop hl
|
||||
pop af
|
||||
pop af
|
||||
inc sp
|
||||
jp (hl)
|
||||
__endasm;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* ================================================================
|
||||
* Предыдущий вариант (v2, 2026-07-10, до precompute): решение
|
||||
* «16 или хвост» и вычитание h -= n на КАЖДЫЙ чанк через 16-бит
|
||||
* арифметику в IX-слотах (~141-189Т на чанк ≈ 11Т/линию).
|
||||
* Оставлен закомментированным для отката/сравнения.
|
||||
* ================================================================ */
|
||||
void _gfx_recthfill256(int x, int y, int w, int h, uint8_t color) __naked
|
||||
{
|
||||
__asm
|
||||
.globl __gfx_addr_base
|
||||
push ix
|
||||
ld ix, #0
|
||||
add ix, sp
|
||||
ld bc, (__gfx_addr_base)
|
||||
add hl, bc ; HL = base + x (константа)
|
||||
ld d, e ; D = бегущий y
|
||||
ld a, 8 (ix)
|
||||
ld c, a ; C = color
|
||||
ld a, 4 (ix)
|
||||
ld (rh_len), a ; SMC: размер блока <- w (0 = 256)
|
||||
rh_chunk:
|
||||
ld a, 7 (ix)
|
||||
or a, a
|
||||
jr NZ, rh_full ; h >= 256 -> чанк 16
|
||||
ld a, 6 (ix)
|
||||
or a, a
|
||||
jr Z, rh_done ; h == 0 -> готово
|
||||
cp a, #16
|
||||
jr NC, rh_full
|
||||
ld b, a ; хвост: n = h
|
||||
jr rh_go
|
||||
rh_full:
|
||||
ld b, #16 ; полный чанк
|
||||
rh_go:
|
||||
ld a, 6 (ix) ; h -= n
|
||||
sub a, b
|
||||
ld 6 (ix), a
|
||||
ld a, 7 (ix)
|
||||
sbc a, #0
|
||||
ld 7 (ix), a
|
||||
;; --- скобка: n строк одним армированием размера ---
|
||||
;; (армирование НЕ выносить из цикла: в окне EI CBL-ISR
|
||||
;; армирует акселератор своим размером блока)
|
||||
di
|
||||
ld d, d ; 0x52 — режим размера блока (no-op для D=y)
|
||||
ld a, #0
|
||||
rh_len = . - 1
|
||||
ld b, b ; выключить — размер сохранён
|
||||
rh_row:
|
||||
ld a, d
|
||||
out (#0x89), a ; Port_Y = y (акселератор ВЫКЛЮЧЕН)
|
||||
ld c, c ; 0x49 — горизонтальный Fill (no-op для C)
|
||||
ld a, c
|
||||
ld (hl), a ; выстрел: строка w байт
|
||||
ld b, b ; выключить (перед djnz — fetch смещения!)
|
||||
inc d ; y++
|
||||
djnz rh_row
|
||||
ei ; окно для прерываний между чанками
|
||||
jr rh_chunk
|
||||
rh_done:
|
||||
pop ix
|
||||
pop hl
|
||||
pop af
|
||||
pop af
|
||||
inc sp
|
||||
jp (hl)
|
||||
__endasm;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* _gfx_rectvfill256 — заливка прямоугольника (x,y,w,h) ВЕРТИКАЛЬНЫМИ
|
||||
* колонками (w выстрелов высотой h), mode 0x81. Chunked-схема: скобки
|
||||
* DI/EI по ≤16 колонок, армирование размера один раз на скобку, на
|
||||
* колонку — re-OUT Port_Y + LD E,E + выстрел (семантика мульти-триггера:
|
||||
* memory/accel_multitrigger_fill, проверено в MAME tests/accfill).
|
||||
*
|
||||
* Счётчик чанков — precompute (v3): один раз на входе E = w>>4 (полных
|
||||
* чанков, ≤20 при w<=320) и w&15 — хвост (SMC в ld b,#n хвостовой
|
||||
* скобки). Заголовок чанка = dec e/jr nz (~46Т на чанк ≈ 2.9Т/линию
|
||||
* против ~173Т/чанк у 16-бит арифметики через IX-слоты); precompute
|
||||
* ~170Т однократно. Итог ~56Т/линию. Хвост — отдельная скобка со
|
||||
* СВОИМ армированием (второй SMC-слот размера): армирование обязано
|
||||
* быть в каждой скобке — в окне EI CBL-ISR армирует акселератор своим
|
||||
* размером блока.
|
||||
*
|
||||
* Прямой вызов оправдан, когда форма известна заранее (высокие/узкие
|
||||
* прямоугольники, w <= h) — экономит ~160-245Т проверки диспетчера
|
||||
* _gfx_rectfill256 (см. там же расчёт break-even).
|
||||
*
|
||||
* ---- КОНТРАКТ (ответственность вызывающего) ------------------------
|
||||
* - 1 <= h <= 256 (h == 256 кодируется младшим байтом 0 — конвенция
|
||||
* акселератора; h == 0 даст 256!). 0 <= w <= 320 (w == 0 — тихий
|
||||
* выход; старший байт w интерпретируется как «+16 чанков», т.е.
|
||||
* w > 511 — UB). Клиппинга НЕТ, координаты обязаны быть валидны.
|
||||
* - W3 замаплен (_bgi_begin у вызывающего).
|
||||
*
|
||||
* __sdcccall(1): x→HL, y→DE(E=y); w→4/5(ix), h→6/7(ix), color→8(ix);
|
||||
* __naked → callee-pops 5 байт. Инварианты в регистрах: HL=бегущий addr,
|
||||
* C=цвет, D=y0 (константа; vertical Fill двигает Port_Y — re-OUT
|
||||
* обязателен), E=счётчик чанков, B=djnz внутри скобки.
|
||||
*/
|
||||
|
||||
#include "../_bgi.h"
|
||||
|
||||
void _gfx_rectvfill256(int x, int y, int w, int h, uint8_t color) __naked
|
||||
{
|
||||
__asm
|
||||
.globl __gfx_addr_base
|
||||
push ix
|
||||
ld ix, #0
|
||||
add ix, sp
|
||||
ld bc, (__gfx_addr_base)
|
||||
add hl, bc ; HL = base + x (бегущий)
|
||||
ld d, e ; D = y0 (константа)
|
||||
ld a, 8 (ix)
|
||||
ld c, a ; C = color
|
||||
ld a, 6 (ix)
|
||||
ld (rv_len), a ; SMC: размер блока <- h (0 = 256)
|
||||
ld (rv_len2), a ; ...и в хвостовую скобку
|
||||
|
||||
;; --- precompute (один раз): E = w>>4, хвост w&15 -> SMC ---
|
||||
ld a, 4 (ix)
|
||||
and #0x0F
|
||||
ld (rv_tail), a ; SMC: хвост <- w & 15
|
||||
ld a, 4 (ix)
|
||||
rrca
|
||||
rrca
|
||||
rrca
|
||||
rrca
|
||||
and #0x0F
|
||||
ld e, a ; E = (w низкий) >> 4
|
||||
ld a, 5 (ix)
|
||||
or a, a
|
||||
jr Z, rv_go ; w < 256
|
||||
ld a, e
|
||||
add a, #16 ; старший байт: w<=320 => ровно +16 чанков
|
||||
ld e, a
|
||||
rv_go:
|
||||
ld a, e
|
||||
or a, a
|
||||
jr Z, rv_tail_go ; полных чанков нет
|
||||
|
||||
;; --- E полных чанков по 16 колонок ---
|
||||
;; (армирование НЕ выносить из цикла: в окне EI CBL-ISR
|
||||
;; армирует акселератор своим размером блока)
|
||||
rv_chunk:
|
||||
ld b, #16 ; колонок в чанке
|
||||
di
|
||||
ld d, d ; 0x52 — режим размера блока (no-op для D=y0)
|
||||
ld a, #0
|
||||
rv_len = . - 1
|
||||
ld b, b ; выключить — размер сохранён
|
||||
rv_col:
|
||||
ld a, d
|
||||
out (#0x89), a ; Port_Y = y0 (vertical Fill его сдвинул)
|
||||
ld e, e ; 0x5B — вертикальный Fill (no-op для E-счётчика)
|
||||
ld a, c
|
||||
ld (hl), a ; выстрел: колонка h строк
|
||||
ld b, b ; выключить (перед djnz — fetch смещения!)
|
||||
inc hl ; следующая колонка
|
||||
djnz rv_col
|
||||
ei ; окно для прерываний между чанками
|
||||
dec e
|
||||
jr NZ, rv_chunk
|
||||
|
||||
;; --- хвост w&15 колонок: отдельная скобка со СВОИМ армированием ---
|
||||
rv_tail_go:
|
||||
ld a, #0
|
||||
rv_tail = . - 1
|
||||
or a, a
|
||||
jr Z, rv_done ; хвоста нет
|
||||
ld b, a
|
||||
di
|
||||
ld d, d
|
||||
ld a, #0
|
||||
rv_len2 = . - 1
|
||||
ld b, b
|
||||
rv_tcol:
|
||||
ld a, d
|
||||
out (#0x89), a
|
||||
ld e, e
|
||||
ld a, c
|
||||
ld (hl), a
|
||||
ld b, b
|
||||
inc hl
|
||||
djnz rv_tcol
|
||||
ei
|
||||
rv_done:
|
||||
pop ix
|
||||
pop hl
|
||||
pop af
|
||||
pop af
|
||||
inc sp
|
||||
jp (hl)
|
||||
__endasm;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* ================================================================
|
||||
* Предыдущий вариант (v2, 2026-07-10, до precompute): решение
|
||||
* «16 или хвост» и вычитание w -= n на КАЖДЫЙ чанк через 16-бит
|
||||
* арифметику в IX-слотах (~141-189Т на чанк ≈ 11Т/линию).
|
||||
* Оставлен закомментированным для отката/сравнения.
|
||||
* ================================================================ */
|
||||
void _gfx_rectvfill256(int x, int y, int w, int h, uint8_t color) __naked
|
||||
{
|
||||
__asm
|
||||
.globl __gfx_addr_base
|
||||
push ix
|
||||
ld ix, #0
|
||||
add ix, sp
|
||||
ld bc, (__gfx_addr_base)
|
||||
add hl, bc ; HL = base + x (бегущий)
|
||||
ld d, e ; D = y0 (константа)
|
||||
ld a, 8 (ix)
|
||||
ld c, a ; C = color
|
||||
ld a, 6 (ix)
|
||||
ld (rv_len), a ; SMC: размер блока <- h (0 = 256)
|
||||
rv_chunk:
|
||||
ld a, 5 (ix)
|
||||
or a, a
|
||||
jr NZ, rv_full ; w >= 256 -> чанк 16
|
||||
ld a, 4 (ix)
|
||||
or a, a
|
||||
jr Z, rv_done ; w == 0 -> готово
|
||||
cp a, #16
|
||||
jr NC, rv_full
|
||||
ld b, a ; хвост: n = w
|
||||
jr rv_go
|
||||
rv_full:
|
||||
ld b, #16 ; полный чанк
|
||||
rv_go:
|
||||
ld a, 4 (ix) ; w -= n
|
||||
sub a, b
|
||||
ld 4 (ix), a
|
||||
ld a, 5 (ix)
|
||||
sbc a, #0
|
||||
ld 5 (ix), a
|
||||
;; --- скобка: n колонок одним армированием размера ---
|
||||
;; (армирование НЕ выносить из цикла: в окне EI CBL-ISR
|
||||
;; армирует акселератор своим размером блока)
|
||||
di
|
||||
ld d, d ; 0x52 — режим размера блока (no-op для D=y0)
|
||||
ld a, #0
|
||||
rv_len = . - 1
|
||||
ld b, b ; выключить — размер сохранён
|
||||
rv_col:
|
||||
ld a, d
|
||||
out (#0x89), a ; Port_Y = y0 (vertical Fill его сдвинул)
|
||||
ld e, e ; 0x5B — вертикальный Fill (no-op для E)
|
||||
ld a, c
|
||||
ld (hl), a ; выстрел: колонка h строк
|
||||
ld b, b ; выключить (перед djnz — fetch смещения!)
|
||||
inc hl ; следующая колонка
|
||||
djnz rv_col
|
||||
ei ; окно для прерываний между чанками
|
||||
jr rv_chunk
|
||||
rv_done:
|
||||
pop ix
|
||||
pop hl
|
||||
pop af
|
||||
pop af
|
||||
inc sp
|
||||
jp (hl)
|
||||
__endasm;
|
||||
}
|
||||
#endif
|
||||
@@ -18,22 +18,4 @@ color_t getpixel(int x, int y)
|
||||
register uint8_t _gp_color = _bgi_read_raw(x, y);
|
||||
_bgi_end();
|
||||
return _gp_color;
|
||||
// _gp_y = (uint8_t)y;
|
||||
// _gp_addr = (uint16_t)(_gfx_addr_base + (unsigned)x);
|
||||
// __asm
|
||||
// di
|
||||
// in a, (#0xE2)
|
||||
// ld (__gfx_saved_w3), a
|
||||
// ld a, (__gfx_bank)
|
||||
// out (#0xE2), a
|
||||
// ld a, (_gp_y)
|
||||
// out (#0x89), a
|
||||
// ld hl, (_gp_addr)
|
||||
// ld a, (hl)
|
||||
// ld (_gp_color), a
|
||||
// ld a, (__gfx_saved_w3)
|
||||
// out (#0xE2), a
|
||||
// ei
|
||||
// __endasm;
|
||||
// return _gp_color;
|
||||
}
|
||||
|
||||
@@ -24,22 +24,4 @@ void putpixel(int x, int y, color_t color)
|
||||
_bgi_plot_raw(x, y, color);
|
||||
_bgi_end();
|
||||
return;
|
||||
// _pp_y = (uint8_t)y;
|
||||
// _pp_addr = (uint16_t)(_gfx_addr_base + (unsigned)x);
|
||||
// _pp_color = (uint8_t)color;
|
||||
// __asm
|
||||
// di
|
||||
// in a, (#0xE2)
|
||||
// ld (__gfx_saved_w3), a
|
||||
// ld a, (__gfx_bank)
|
||||
// out (#0xE2), a
|
||||
// ld a, (_pp_y)
|
||||
// out (#0x89), a
|
||||
// ld hl, (_pp_addr)
|
||||
// ld a, (_pp_color)
|
||||
// ld (hl), a
|
||||
// ld a, (__gfx_saved_w3)
|
||||
// out (#0xE2), a
|
||||
// ei
|
||||
// __endasm;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
unsigned _bgi_read(int x, int y)
|
||||
{
|
||||
unsigned c;
|
||||
register unsigned c;
|
||||
_bgi_begin();
|
||||
c = _bgi_read_raw(x, y);
|
||||
_bgi_end();
|
||||
|
||||
Reference in New Issue
Block a user