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:
2026-07-10 17:41:49 +03:00
parent e553e5e6c9
commit 2c414da465
11 changed files with 617 additions and 49 deletions
+61 -8
View File
@@ -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