Files
Sprinter-SDCC/libbgi/bgi256/_gfx_rectvfill256.c
T
snark13 580837d2ee docs: бюджет тактов/размеров chunked-заливок + идеи дальнейших упрощений
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>
2026-07-10 17:58:17 +03:00

205 lines
9.0 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_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-слоты). Хвост —
* отдельная скобка со СВОИМ армированием (второй SMC-слот размера):
* армирование обязано быть в каждой скобке — в окне EI CBL-ISR
* армирует акселератор своим размером блока.
*
* ---- БЮДЖЕТ (полный разбор: docs/accel-fill-budget.md) -------------
* подготовка ~340Т один раз на прямоугольник (пролог 102 + SMC 45 +
* precompute ~110 + эпилог 54); чанк 16 колонок — 46Т; колонка — 53Т.
* T(n) ≈ 340 + 46*(n/16) + 53*n; ~107 байт кода. Break-even против
* простейшего per-line di/ei (djnz, 72Т/линию, ~63 байта): n ≈ 9 —
* при n <= 8 линий простейший быстрее (максимум +140Т на n=1).
*
* Прямой вызов оправдан, когда форма известна заранее (высокие/узкие
* прямоугольники, 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