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:
@@ -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
|
||||
Reference in New Issue
Block a user