580837d2ee
docs/accel-fill-budget.md: полный разбор v3 (подготовка ~340Т на прямоугольник, 46Т на чанк 16 линий, 53Т/51Т на линию, ~107 байт/leaf) против per-line di/ei вариантов (djnz 72Т/линию ~63 байта; 16-бит IX 206Т/линию); break-even n≈9 линий, тотализатор, решение остаться на v3. Краткие выжимки — в шапки _gfx_rect*fill256. Идеи на будущее (там же): ограничить контракт стороной <=256 (широкие прямоугольники пользователь выводит в два приёма); реализовать оба варианта (поблочный/построчный) с переключением опцией сборки в духе GFX_NOCHECK. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
206 lines
9.0 KiB
C
206 lines
9.0 KiB
C
/*
|
||
* _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-слоты). Хвост —
|
||
* отдельная скобка со СВОИМ армированием (второй SMC-слот размера):
|
||
* армирование обязано быть в каждой скобке — в окне EI CBL-ISR
|
||
* армирует акселератор своим размером блока.
|
||
*
|
||
* ---- БЮДЖЕТ (полный разбор: docs/accel-fill-budget.md) -------------
|
||
* подготовка ~340Т один раз на прямоугольник (пролог 102 + SMC 45 +
|
||
* precompute ~110 + эпилог 54); чанк 16 строк — 46Т; строка — 51Т.
|
||
* T(n) ≈ 340 + 46*(n/16) + 51*n; ~107 байт кода. Break-even против
|
||
* простейшего per-line di/ei (djnz, 70Т/линию, ~53 байта): n ≈ 9 —
|
||
* при n <= 8 линий простейший быстрее (максимум +140Т на n=1).
|
||
*
|
||
* Прямой вызов оправдан, когда форма известна заранее (широкие/низкие
|
||
* прямоугольники, 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
|