2c414da465
Семантика 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>
145 lines
5.5 KiB
C
145 lines
5.5 KiB
C
/*
|
||
* _gfx_rectfill256 — диспетчер заливки прямоугольника (x,y,w,h,color),
|
||
* mode 0x81: выбирает ориентацию с меньшим числом выстрелов и передаёт
|
||
* управление leaf-заливке (tail-jump, стек не трогается — ABI идентичен):
|
||
* w>h && w<=256 → _gfx_recthfill256 (h горизонтальных строк)
|
||
* иначе → _gfx_rectvfill256 (w вертикальных колонок)
|
||
*
|
||
* ---- ЦЕНА ПРОВЕРКИ и когда звать 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).
|
||
* Диспетчер сохраняет 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
|
||
.globl __gfx_addr_base
|
||
.globl __gfx_hfill256_segment
|
||
.globl __gfx_vfill256_segment
|
||
push ix
|
||
ld ix, #0
|
||
add ix, sp
|
||
ld bc, (__gfx_addr_base)
|
||
add hl, bc ; HL = base + x (бегущий addr)
|
||
ld a, 8 (ix)
|
||
ld c, a ; C = color
|
||
|
||
;; --- ветка: 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, rf_vbranch
|
||
or a, b
|
||
jr Z, rf_vbranch ; w==h -> не (w>h)
|
||
ld a, 5 (ix) ; w high
|
||
cp a, #2
|
||
jr NC, rf_vbranch ; w >= 512
|
||
dec a
|
||
jr NZ, rf_hbranch ; high==0 -> w<256
|
||
ld a, 4 (ix)
|
||
or a, a
|
||
jr NZ, rf_vbranch ; high==1,low!=0 -> w>256
|
||
|
||
rf_hbranch:
|
||
ld a, 4 (ix)
|
||
ld b, a ; B = len = w (256 -> 0)
|
||
rf_hloop:
|
||
ld a, 6 (ix)
|
||
or a, 7 (ix)
|
||
jr Z, rf_done ; h == 0
|
||
call __gfx_hfill256_segment
|
||
inc e ; y++
|
||
ld a, 6 (ix)
|
||
sub a, #1
|
||
ld 6 (ix), a
|
||
ld a, 7 (ix)
|
||
sbc a, #0
|
||
ld 7 (ix), a
|
||
jr rf_hloop
|
||
|
||
rf_vbranch:
|
||
ld a, 6 (ix)
|
||
ld b, a ; B = len = h (256 -> 0)
|
||
rf_vloop:
|
||
ld a, 4 (ix)
|
||
or a, 5 (ix)
|
||
jr Z, rf_done ; w == 0
|
||
call __gfx_vfill256_segment
|
||
inc hl ; addr++
|
||
ld a, 4 (ix)
|
||
sub a, #1
|
||
ld 4 (ix), a
|
||
ld a, 5 (ix)
|
||
sbc a, #0
|
||
ld 5 (ix), a
|
||
jr rf_vloop
|
||
|
||
rf_done:
|
||
pop ix
|
||
pop hl
|
||
pop af
|
||
pop af
|
||
inc sp
|
||
jp (hl)
|
||
__endasm;
|
||
}
|
||
#endif
|