Files
Sprinter-SDCC/libbgi/bgi256/_gfx_rectfill256.c
T
snark13 2c414da465 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>
2026-07-10 17:41:49 +03:00

145 lines
5.5 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* _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