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:
@@ -1,4 +1,5 @@
|
|||||||
# Эталон размеров _CODE (байт); обновление: python3 toolchain/size_check.py --update
|
# Эталон размеров _CODE (байт); обновление: python3 toolchain/size_check.py --update
|
||||||
|
accfill 3435
|
||||||
argv 3431
|
argv 3431
|
||||||
assrtest 3847
|
assrtest 3847
|
||||||
attrprob 4091
|
attrprob 4091
|
||||||
@@ -22,7 +23,7 @@ fdmax 6337
|
|||||||
filetest 11624
|
filetest 11624
|
||||||
gets 509
|
gets 509
|
||||||
gfx_dbuf 4770
|
gfx_dbuf 4770
|
||||||
gfx_demo 4207
|
gfx_demo 4261
|
||||||
hello 4176
|
hello 4176
|
||||||
hello2 4251
|
hello2 4251
|
||||||
irqtest 5881
|
irqtest 5881
|
||||||
|
|||||||
|
+8
-2
@@ -102,9 +102,15 @@ void _gfx_hfill256_segment(void);
|
|||||||
void _gfx_vfill256_segment(void);
|
void _gfx_vfill256_segment(void);
|
||||||
|
|
||||||
/* Заливка прямоугольника цветом: x, y, w, h, color.
|
/* Заливка прямоугольника цветом: x, y, w, h, color.
|
||||||
* Механизм: один раз задаётся Horizontal_Size, затем в цикле по Y
|
* _gfx_rectfill256 — диспетчер: выбирает ориентацию с меньшим числом
|
||||||
* акселератор копирует байт цвета на ширину строки (docs/part2/accelerator_doc.txt). */
|
* выстрелов (~160-245Т на проверку) и tail-jump'ит в leaf. Если форма
|
||||||
|
* известна заранее или |w-h| < ~4, выгоднее звать leaf напрямую:
|
||||||
|
* _gfx_recthfill256 — h горизонтальных строк, ТРЕБУЕТ 1 <= w <= 256;
|
||||||
|
* _gfx_rectvfill256 — w вертикальных колонок, ТРЕБУЕТ 1 <= h <= 256.
|
||||||
|
* Контракты/расчёт break-even — в шапках bgi256/_gfx_rect*fill256.c. */
|
||||||
void _gfx_rectfill256(int x, int y, int w, int h, uint8_t color);
|
void _gfx_rectfill256(int x, int y, int w, int h, uint8_t color);
|
||||||
|
void _gfx_recthfill256(int x, int y, int w, int h, uint8_t color);
|
||||||
|
void _gfx_rectvfill256(int x, int y, int w, int h, uint8_t color);
|
||||||
|
|
||||||
/* ---- Скретч 16-цветных примитивов (bgi16/_gfx_g16_state.c) ------- */
|
/* ---- Скретч 16-цветных примитивов (bgi16/_gfx_g16_state.c) ------- */
|
||||||
extern uint8_t _gfx_g16_y;
|
extern uint8_t _gfx_g16_y;
|
||||||
|
|||||||
@@ -1,12 +1,59 @@
|
|||||||
/*
|
/*
|
||||||
* _bgi_clear_raw — залить всё поле 320×256 цветом color, mode 0x81.
|
* _bgi_clear_raw — залить всё поле 320×256 цветом color, mode 0x81.
|
||||||
* 320 вертикальных Fill-burst'ов (по колонке 256 строк), Port_Y=0.
|
*
|
||||||
|
* Chunked-схема (v2, 2026-07-10): 20 DI-скобок по 16 колонок (256 строк
|
||||||
|
* каждая) — армирование размера один раз на скобку, на колонку только
|
||||||
|
* ре-арм LD E,E + выстрел (см. _gfx_rectfill256.c и
|
||||||
|
* memory/accel_multitrigger_fill; проверено в MAME tests/accfill).
|
||||||
|
* Окно DI на чанк ≈ 0.6 мс (16×256 байт) — CBL/IM2 живут.
|
||||||
|
* Вертикальный Fill двигает Port_Y — re-OUT 0 перед каждой колонкой.
|
||||||
|
*
|
||||||
* Register-only asm, без глобального скретча. W3-naive: вызывающий
|
* Register-only asm, без глобального скретча. W3-naive: вызывающий
|
||||||
* обрамляет _bgi_begin/_bgi_end. color → A (__sdcccall(1)).
|
* обрамляет _bgi_begin/_bgi_end. color → A (__sdcccall(1)).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "../_bgi.h"
|
#include "../_bgi.h"
|
||||||
|
|
||||||
|
void _bgi_clear_raw(uint8_t color) __naked
|
||||||
|
{
|
||||||
|
__asm
|
||||||
|
.globl __gfx_addr_base
|
||||||
|
ld c, a ; C = цвет
|
||||||
|
ld hl, (__gfx_addr_base) ; HL = база draw-страницы
|
||||||
|
ld d, #0 ; y0 = 0
|
||||||
|
ld e, #20 ; 20 чанков × 16 колонок = 320
|
||||||
|
|
||||||
|
clr_chunk:
|
||||||
|
di
|
||||||
|
;; Армирование размера НЕ выносить из цикла (экономия ~0.1%):
|
||||||
|
;; в окне EI между чанками CBL-ISR сам армирует акселератор
|
||||||
|
;; своим размером блока — каждый чанк обязан армировать заново.
|
||||||
|
ld d, d ; 0x52 — режим размера блока (no-op для D)
|
||||||
|
ld a, #0 ; размер 256 (конвенция акселератора)
|
||||||
|
ld b, b ; выключить — размер сохранён
|
||||||
|
ld b, #16 ; колонок в чанке
|
||||||
|
clr_col:
|
||||||
|
ld a, d
|
||||||
|
out (#0x89), a ; Port_Y = 0 (vertical Fill его сдвинул)
|
||||||
|
ld e, e ; 0x5B — вертикальный Fill (no-op для E)
|
||||||
|
ld a, c
|
||||||
|
ld (hl), a ; выстрел: колонка 256 строк
|
||||||
|
ld b, b ; выключить (перед djnz — fetch смещения!)
|
||||||
|
inc hl ; следующая колонка
|
||||||
|
djnz clr_col
|
||||||
|
ei ; окно для прерываний между чанками
|
||||||
|
dec e
|
||||||
|
jr NZ, clr_chunk
|
||||||
|
ret
|
||||||
|
__endasm;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
/* ================================================================
|
||||||
|
* Старый вариант (до 2026-07-10): полный брекет (DI+арм размера+
|
||||||
|
* режим+выстрел+выкл+EI) на КАЖДУЮ из 320 колонок. Оставлен
|
||||||
|
* закомментированным для отката/сравнения.
|
||||||
|
* ================================================================ */
|
||||||
void _bgi_clear_raw(uint8_t color) __naked
|
void _bgi_clear_raw(uint8_t color) __naked
|
||||||
{
|
{
|
||||||
__asm
|
__asm
|
||||||
@@ -39,3 +86,4 @@ void _bgi_clear_raw(uint8_t color) __naked
|
|||||||
jr _clear_cyc0
|
jr _clear_cyc0
|
||||||
__endasm;
|
__endasm;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -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 горизонтальных
|
* ---- ЦЕНА ПРОВЕРКИ и когда звать leaf'ы напрямую -------------------
|
||||||
* Fill'ов шириной w (y++ на каждой строке); иначе w вертикальных Fill'ов
|
* Проверка + tail-jump: ~160Т (быстрый путь w<h) … ~245Т (w>256),
|
||||||
* высотой h (addr++ на каждой колонке). Инварианты (color=C, len=B) и
|
* типично ~200Т ≈ 9.5 мкс @21МГц. Один выстрел-линия стоит ~55-65Т
|
||||||
* бегущий addr=HL/y=E живут в регистрах через весь цикл — _gfx_*_segment
|
* 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).
|
* __sdcccall(1): x→HL, y→DE(E=y); w→4/5(ix), h→6/7(ix), color→8(ix).
|
||||||
* Pre: W3 замаплен, DI активен вокруг выстрелов внутри segment'ов.
|
* Диспетчер сохраняет HL/DE и стек как есть; leaf делает свой
|
||||||
* Вызывающий управляет _bgi_begin/_bgi_end. w,h предполагаются >= 0.
|
* push ix и callee-pops. Pre/контракты — как у leaf'ов (W3 замаплен,
|
||||||
|
* без клиппинга, w,h >= 0).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "../_bgi.h"
|
#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
|
void _gfx_rectfill256(int x, int y, int w, int h, uint8_t color) __naked
|
||||||
{
|
{
|
||||||
__asm
|
__asm
|
||||||
@@ -89,3 +141,4 @@ void _gfx_rectfill256(int x, int y, int w, int h, uint8_t color) __naked
|
|||||||
jp (hl)
|
jp (hl)
|
||||||
__endasm;
|
__endasm;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -0,0 +1,199 @@
|
|||||||
|
/*
|
||||||
|
* _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-слоты); precompute
|
||||||
|
* ~170Т однократно. Итог ~54Т/линию. Хвост — отдельная скобка со
|
||||||
|
* СВОИМ армированием (второй SMC-слот размера): армирование обязано
|
||||||
|
* быть в каждой скобке — в окне EI CBL-ISR армирует акселератор своим
|
||||||
|
* размером блока.
|
||||||
|
*
|
||||||
|
* Прямой вызов оправдан, когда форма известна заранее (широкие/низкие
|
||||||
|
* прямоугольники, 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
|
||||||
@@ -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
|
||||||
@@ -18,22 +18,4 @@ color_t getpixel(int x, int y)
|
|||||||
register uint8_t _gp_color = _bgi_read_raw(x, y);
|
register uint8_t _gp_color = _bgi_read_raw(x, y);
|
||||||
_bgi_end();
|
_bgi_end();
|
||||||
return _gp_color;
|
return _gp_color;
|
||||||
// _gp_y = (uint8_t)y;
|
|
||||||
// _gp_addr = (uint16_t)(_gfx_addr_base + (unsigned)x);
|
|
||||||
// __asm
|
|
||||||
// di
|
|
||||||
// in a, (#0xE2)
|
|
||||||
// ld (__gfx_saved_w3), a
|
|
||||||
// ld a, (__gfx_bank)
|
|
||||||
// out (#0xE2), a
|
|
||||||
// ld a, (_gp_y)
|
|
||||||
// out (#0x89), a
|
|
||||||
// ld hl, (_gp_addr)
|
|
||||||
// ld a, (hl)
|
|
||||||
// ld (_gp_color), a
|
|
||||||
// ld a, (__gfx_saved_w3)
|
|
||||||
// out (#0xE2), a
|
|
||||||
// ei
|
|
||||||
// __endasm;
|
|
||||||
// return _gp_color;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,22 +24,4 @@ void putpixel(int x, int y, color_t color)
|
|||||||
_bgi_plot_raw(x, y, color);
|
_bgi_plot_raw(x, y, color);
|
||||||
_bgi_end();
|
_bgi_end();
|
||||||
return;
|
return;
|
||||||
// _pp_y = (uint8_t)y;
|
|
||||||
// _pp_addr = (uint16_t)(_gfx_addr_base + (unsigned)x);
|
|
||||||
// _pp_color = (uint8_t)color;
|
|
||||||
// __asm
|
|
||||||
// di
|
|
||||||
// in a, (#0xE2)
|
|
||||||
// ld (__gfx_saved_w3), a
|
|
||||||
// ld a, (__gfx_bank)
|
|
||||||
// out (#0xE2), a
|
|
||||||
// ld a, (_pp_y)
|
|
||||||
// out (#0x89), a
|
|
||||||
// ld hl, (_pp_addr)
|
|
||||||
// ld a, (_pp_color)
|
|
||||||
// ld (hl), a
|
|
||||||
// ld a, (__gfx_saved_w3)
|
|
||||||
// out (#0xE2), a
|
|
||||||
// ei
|
|
||||||
// __endasm;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
unsigned _bgi_read(int x, int y)
|
unsigned _bgi_read(int x, int y)
|
||||||
{
|
{
|
||||||
unsigned c;
|
register unsigned c;
|
||||||
_bgi_begin();
|
_bgi_begin();
|
||||||
c = _bgi_read_raw(x, y);
|
c = _bgi_read_raw(x, y);
|
||||||
_bgi_end();
|
_bgi_end();
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
||||||
|
EXAMPLE := accfill
|
||||||
|
EXTRA_FLAGS ?= --gfx 256
|
||||||
|
include $(PROJ_ROOT)/app.mk
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* accfill — регресс chunked-заливок libbgi на мульти-триггере акселератора
|
||||||
|
* (B0: cleardevice/_bgi_clear_raw; B1: bar → _gfx_rectvfill256, пути
|
||||||
|
* «1 чанк», «2 чанка + хвост»; B2: bar → _gfx_recthfill256, «чанк + хвост»;
|
||||||
|
* плюс полноширинный bar 320×8 — precompute-путь w>=256).
|
||||||
|
*
|
||||||
|
* Семантика FSM (из драйвера MAME src/mame/sinclair/sprinter.cpp;
|
||||||
|
* согласуется с примерами docs/part2/accelerator_doc.txt; сырые
|
||||||
|
* asm-эксперименты E1-E3, подтвердившие её, удалены — см. git history
|
||||||
|
* 2026-07-10 и memory/accel_multitrigger_fill):
|
||||||
|
* - армирование (LD C,C / LD E,E / ...) живёт до следующего accel-опкода —
|
||||||
|
* мульти-триггер поддерживается;
|
||||||
|
* - под армированием триггерит ЛЮБОЙ non-M1 доступ к памяти: в т.ч. fetch
|
||||||
|
* immediate/displacement (djnz/jr/ld a,#n) и стек → между триггерами
|
||||||
|
* допустимы ТОЛЬКО регистровые M1-опкоды, либо выключать (LD B,B);
|
||||||
|
* - вертикальный Fill двигает Port_Y (+len) и НЕ возвращает; адрес
|
||||||
|
* константен. Горизонтальный — двигает адрес, Port_Y на месте;
|
||||||
|
* - OUT — I/O, акселератор не триггерит (но fetch операнда порта — да,
|
||||||
|
* поэтому OUT делать при выключенном акселераторе);
|
||||||
|
* - размер блока хранится во внутреннем регистре и переживает LD B,B —
|
||||||
|
* армировать размер достаточно один раз на скобку.
|
||||||
|
*
|
||||||
|
* Верификация: getpixel по всем линиям прямоугольника + нетронутый фон
|
||||||
|
* в 1px вокруг. Результат (PASS/FAIL) печатается на экране.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <graphics.h>
|
||||||
|
|
||||||
|
|
||||||
|
/* Прямоугольник (x0,y0,w,h) залит col целиком, а 1px вокруг — фон (0)?
|
||||||
|
* Сканируем все колонки в трёх строках и все строки в трёх колонках —
|
||||||
|
* ловит пропуск отдельного триггера (дырку в колонке/строке). */
|
||||||
|
static uint8_t rect_ok(int x0, int y0, int w, int h, color_t col)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < w; i++) {
|
||||||
|
if (getpixel(x0 + i, y0) != col) return 0;
|
||||||
|
if (getpixel(x0 + i, y0 + h / 2) != col) return 0;
|
||||||
|
if (getpixel(x0 + i, y0 + h - 1) != col) return 0;
|
||||||
|
}
|
||||||
|
for (i = 0; i < h; i++) {
|
||||||
|
if (getpixel(x0, y0 + i) != col) return 0;
|
||||||
|
if (getpixel(x0 + w / 2, y0 + i) != col) return 0;
|
||||||
|
if (getpixel(x0 + w - 1, y0 + i) != col) return 0;
|
||||||
|
}
|
||||||
|
/* вокруг — нетронутый фон */
|
||||||
|
if (getpixel(x0 - 1, y0) != 0) return 0;
|
||||||
|
if (getpixel(x0 + w, y0) != 0) return 0;
|
||||||
|
if (getpixel(x0, y0 - 1) != 0) return 0;
|
||||||
|
if (getpixel(x0, y0 + h) != 0) return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
uint8_t b0;
|
||||||
|
|
||||||
|
initgraph();
|
||||||
|
cleardevice();
|
||||||
|
|
||||||
|
/* B0: cleardevice (chunked _bgi_clear_raw) — углы и центр чистые */
|
||||||
|
b0 = getpixel(0, 0) == 0 && getpixel(319, 0) == 0 &&
|
||||||
|
getpixel(0, 255) == 0 && getpixel(319, 255) == 0 &&
|
||||||
|
getpixel(160, 128) == 0;
|
||||||
|
|
||||||
|
setcolor(WHITE);
|
||||||
|
outtextxy(8, 8, "ACCEL multi-trigger fill test v3");
|
||||||
|
|
||||||
|
/* B1: bar → вертикальная ветка rectfill: 1 чанк (16 кол.) и 3 чанка (40) */
|
||||||
|
setfillstyle(SOLID_FILL, LIGHTBLUE);
|
||||||
|
bar(16, 60, 31, 123); /* 16×64 */
|
||||||
|
setfillstyle(SOLID_FILL, YELLOW);
|
||||||
|
bar(230, 60, 269, 119); /* 40×60 */
|
||||||
|
|
||||||
|
/* B2: bar → горизонтальная ветка rectfill: 2 чанка (20 строк) */
|
||||||
|
setfillstyle(SOLID_FILL, LIGHTMAGENTA);
|
||||||
|
bar(200, 18, 299, 37); /* 100×20 */
|
||||||
|
|
||||||
|
bar(0, 8, 319, 15); /* 320×8 */
|
||||||
|
|
||||||
|
outtextxy(8, 132, b0
|
||||||
|
? "B0 cleardevice chunked: PASS"
|
||||||
|
: "B0 cleardevice chunked: FAIL");
|
||||||
|
outtextxy(8, 144, (rect_ok(16, 60, 16, 64, LIGHTBLUE) &&
|
||||||
|
rect_ok(230, 60, 40, 60, YELLOW))
|
||||||
|
? "B1 bar vert 1+3 chunks: PASS"
|
||||||
|
: "B1 bar vert 1+3 chunks: FAIL");
|
||||||
|
outtextxy(8, 156, rect_ok(200, 18, 100, 20, LIGHTMAGENTA)
|
||||||
|
? "B2 bar horz 2 chunks: PASS"
|
||||||
|
: "B2 bar horz 2 chunks: FAIL");
|
||||||
|
|
||||||
|
outtextxy(8, 208, "done");
|
||||||
|
for (;;) { }
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user