Files
Sprinter-SDCC/tests/blitw/blitw.c
T
snark13 72ce66275e libbgi: спрайтовый движок v2 + accel-блит/heal leaf'ы + noclip-путь
Спрайтовая графика поверх accel block-copy (docs/sprite-api-design.md):

- Ядро блиттинга: leaf'ы _bgi_blit_rows_raw (dst фикс, только src-страйд) /
  _bgi_copy_rows_raw (getimage) / _bgi_heal_rows_raw (src==dst). DI один на
  спрайт (санкция: малый спрайт под одним DI аудио не рвёт); src[0]-фикс
  снят (точная MAME подавляет CPU-байт триггера записи — на железе
  перепроверить; для heal был избыточен и снят безусловно).
- Общие bracket-free ядра _gfx_blit_full/_gfx_heal_full (полная ширина:
  клип по экрану + split >256 для putimage) + лин _gfx_blit_sprite/
  _gfx_heal_sprite (кадр ≤64, без split, 8-бит w/h) + noclip-варианты
  (клип-кода нет → полный codegen-win).  Имя *_full (не *_clip) — «clip»
  двусмысленно (sprite-ядра тоже клипуют; различитель — ширина/split).
- Движок retained-модели <sprite.h>: sprite_init/update/flip + inline
  move/frame/show/hide/touch; drawn[2] per-page внутри структуры; кадр —
  двухпроходно heal ВСЕ -> блит ВСЕ под одной W3-скобкой/банком на проход.
- Флаг gfx_sprite_clip(on/off): приложение, само следящее за границами,
  отключает клип (~+19% на анимации; диспетч пока через if — funcptr далее).
- putsprite/movesprite/gfx_blit/putimage(COPY)/getimage переведены на ядро.

Тесты: examples/balls (движок, дабл-буфер, boundary-тест клипа),
tests/sprites (RAM PASS, клип 4 края, атлас), tests/blitw (trig-leak),
tests/spriteclip (hardware-probe: железо НЕ режет за краем -> клип нужен),
tests/blitperf, tests/gfxbanks. size-baseline обновлён (53 программы).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 21:51:52 +03:00

137 lines
5.0 KiB
C

/*
* blitw — программный регресс ядра блиттинга gfx_blit_part по ширинам.
*
* Репро артефакта tests/sprites (клипнутые шары w=12 рисуются со
* сдвигом): блитим паттерн-образ 24×24 разными ширинами банком 0x50
* (запись идёт и в ОЗУ-копию → getpixel читает ровно то, что записано)
* и побайтно сверяем прямоугольник + рамку 1px вокруг (должна остаться
* фоном). Вердикт по каждой ширине печатается на экране:
* w=NN PASS | w=NN FAIL @x,y eXX gYY (exp/got первого расхождения)
*
* Запуск:
* python3 toolchain/mame_interactive.py tests/blitw/blitw.exe \
* --snap 14 --timeout 16
*/
#include <graphics.h>
#include <gfx.h>
#define IW 24
#define IH 24
#define TX 200 /* позиция тестового прямоугольника (правее текста) */
#define TY 30
static uint8_t img[4 + IW * IH];
static char msg[40];
static uint8_t pat(int x, int y)
{
return (uint8_t)(((x + 1) * (y + 3)) & 0x7F); /* != 0xFF, != 0 кроме редких */
}
static void build_img(void)
{
uint8_t *p = img;
int x, y;
*p++ = IW; *p++ = 0; *p++ = IH; *p++ = 0;
for (y = 0; y < IH; y++)
for (x = 0; x < IW; x++)
*p++ = pat(x, y);
}
static char hexd(uint8_t v)
{
v &= 15;
return v < 10 ? '0' + v : 'A' + v - 10;
}
/* Проверить блит шириной w в (tx,TY): весь rect = pat, рамка = 0. */
static void check_w(int w, int tx, int ty, int row)
{
int x, y;
uint8_t exp, got;
uint8_t fail = 0;
/* фон под областью + рамкой */
setfillstyle(SOLID_FILL, 0);
bar(tx - 2, ty - 2, tx + w + 1, ty + IH + 1);
gfx_blit_part(tx, ty, img, 0, 0, w, IH);
for (y = -1; y <= IH && !fail; y++) {
for (x = -1; x <= w && !fail; x++) {
exp = (y >= 0 && y < IH && x >= 0 && x < w) ? pat(x, y) : 0;
got = (uint8_t)getpixel(tx + x, ty + y);
if (got != exp) {
fail = 1;
msg[0] = 'w'; msg[1] = '0' + w / 10; msg[2] = '0' + w % 10;
msg[3] = ' '; msg[4] = 'F'; msg[5] = 'A'; msg[6] = 'I';
msg[7] = 'L'; msg[8] = ' ';
msg[9] = 'x'; msg[10] = (char)('0' + (x + 1) / 10);
msg[11] = (char)('0' + (x + 1) % 10);
msg[12] = 'y'; msg[13] = (char)('0' + (y + 1) / 10);
msg[14] = (char)('0' + (y + 1) % 10);
msg[15] = ' '; msg[16] = 'e'; msg[17] = hexd(exp >> 4);
msg[18] = hexd(exp);
msg[19] = ' '; msg[20] = 'g'; msg[21] = hexd(got >> 4);
msg[22] = hexd(got);
msg[23] = 0;
}
}
}
if (!fail) {
msg[0] = 'w'; msg[1] = '0' + w / 10; msg[2] = '0' + w % 10;
msg[3] = ' '; msg[4] = 'P'; msg[5] = 'A'; msg[6] = 'S';
msg[7] = 'S'; msg[8] = 0;
}
outtextxy(4, 40 + row * 10, msg);
}
int main(void)
{
static const uint8_t widths[] = { 24, 16, 12, 8, 4, 2, 1 };
uint8_t i;
build_img();
initgraph();
cleardevice();
setcolor(WHITE);
outtextxy(4, 4, "BLITW: gfx_blit_part width sweep");
for (i = 0; i < sizeof(widths); i++)
check_w(widths[i], TX, TY, i);
/* и правый край, как в sprites: dst у конца строки page 0 */
check_w(12, 308 - 12, TY, 7); /* dst близко к краю, но без клипа */
/* Регресс утечки CPU-байта write-триггера (см. шапку
* _bgi_copy_rows_raw). Программно (getpixel = ОЗУ) утечка на MAME
* 0.283 НЕ ловится: ОЗУ-ветка 0x58 пишет все байты, и burst
* перезаписывает утечку в ОЗУ; жертвой остаётся только VRAM
* (FF-скип). Поэтому проверка ВИЗУАЛЬНАЯ: блит образа с col0=0xFF
* и последним столбцом YELLOW через 0x5C поверх ЗЕЛЁНОЙ плашки.
* col0 (x=TX..) обязан остаться ЗЕЛЁНЫМ; до фикса он был YELLOW
* (CPU-байт = последний байт burst-чтения). */
{
static uint8_t timg[4 + 16 * 8];
uint8_t *p = timg;
int x, y;
*p++ = 16; *p++ = 0; *p++ = 8; *p++ = 0;
for (y = 0; y < 8; y++)
for (x = 0; x < 16; x++)
*p++ = (x == 0) ? 0xFF : (x == 15 ? YELLOW : LIGHTBLUE);
setfillstyle(SOLID_FILL, GREEN);
bar(TX - 4, 150 - 4, TX + 19, 150 + 11);
gfx_set_bank(GFX_BANK_SPRITE);
gfx_blit(TX, 150, timg);
gfx_set_bank(GFX_BANK_NORMAL);
outtextxy(4, 40 + 8 * 10 + 4, "trig leak: col0 @200,150 green?");
}
outtextxy(4, 40 + 9 * 10 + 6, "done");
for (;;) { }
}