2c414da465
Семантика 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>
96 lines
4.6 KiB
C
96 lines
4.6 KiB
C
/*
|
||
* 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 (;;) { }
|
||
}
|