Files
Sprinter-SDCC/tests/sprites/sprites.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

190 lines
6.8 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.
/*
* sprites — тест Фазы B спрайтового API (docs/sprite-api-design.md §3):
* putsprite / movesprite / gfx_heal / gfx_blit_part поверх accel-ядра.
*
* Сцена: полосатый фон (вертикальные полосы 8px RED/LIGHTGRAY, банк
* 0x50 → фон и в VRAM, и в ОЗУ-копии). Спрайт — шар 24×24 (YELLOW с
* RED-ободом), углы = 0xFF (GFX_TRANSPARENT).
*
* Стадии (паузы по RTC, скриншот после каждой):
* S1 фон + разметка.
* S2 putsprite:
* - в центре зоны (C) — прозрачные углы: полосы сквозь них;
* - 4 клип-позиции: слева x=-12, справа x=308, сверху y=-10,
* снизу y=244 — рисуется только видимая часть, соседняя
* память не портится;
* - heal-проба: putsprite в H=(60,130) и тут же gfx_heal того же
* rect — шара БЫТЬ НЕ ДОЛЖНО (первая проверка heal src==dst
* через акселератор — риск §10.4 дизайна);
* - атлас: gfx_blit_part кадра 1 (MAGENTA-шар) из ленты 2×24×24
* в (270,130) банком GFX_BANK_SPRITE.
* Проверка A (по ОЗУ, getpixel = ОЗУ-копия): полосы под C целы.
* S3 movesprite: шар из (16,60) 12 шагов по (+16,+8) до (208,156) —
* след обязан быть чистым (heal старой позиции на каждом шаге).
* Проверка B: ОЗУ вдоль пути — полосы.
*
* VRAM программно не читается (write-only) — корректность экрана
* подтверждают скриншоты; программные проверки ловят порчу ОЗУ-копии.
*
* Запуск:
* python3 toolchain/mame_interactive.py tests/sprites/sprites.exe \
* --snap 13,17,21 --timeout 23
*/
#include <graphics.h>
#include <gfx.h>
#include <time.h>
#define BW 24 /* сторона спрайта */
#define BG_Y0 40
#define BG_Y1 199
#define CX 150 /* центральный putsprite */
#define CY 100
static uint8_t ball[4 + BW * BW]; /* YELLOW шар */
static uint8_t strip[4 + 2 * BW * BW]; /* лента: кадр0 YELLOW, кадр1 MAGENTA */
static uint8_t stripe_color(int x)
{
return ((x >> 3) & 1) ? LIGHTGRAY : RED;
}
/* Шар: r<=9 — fill, r 9..11 — ring, дальше прозрачно (0xFF). */
static uint8_t ball_pixel(int x, int y, uint8_t fill, uint8_t ring)
{
int dx = x - BW / 2, dy = y - BW / 2;
int d2 = dx * dx + dy * dy;
if (((x == 0 || x == BW - 1) && ((y >=0 && y <= 3) || (y >=BW - 4 && y <= BW - 1))) ||
((y == 0 || y == BW - 1) && ((x >=0 && x <= 3) || (x >=BW - 4 && x <= BW - 1)))) return GREEN;
if (x==0) return GFX_TRANSPARENT;
if (d2 <= 81) return fill;
if (d2 <= 121) return ring;
return GFX_TRANSPARENT;
}
static void build_images(void)
{
uint8_t *p;
int x, y, f;
p = ball;
*p++ = BW; *p++ = 0; *p++ = BW; *p++ = 0;
for (y = 0; y < BW; y++)
for (x = 0; x < BW; x++)
*p++ = ball_pixel(x, y, YELLOW, RED);
/* Лента 48×24: кадры лежат рядом по x (кадр N: sx = N*BW). */
p = strip;
*p++ = 2 * BW; *p++ = 0; *p++ = BW; *p++ = 0;
for (y = 0; y < BW; y++)
for (f = 0; f < 2; f++)
for (x = 0; x < BW; x++)
*p++ = ball_pixel(x, y, f ? LIGHTMAGENTA : YELLOW,
f ? BLUE : RED);
}
/* ОЗУ-копия (getpixel) в трёх точках зоны 24×24 — нетронутые полосы? */
static uint8_t ram_is_stripes(int zx, int zy)
{
static const uint8_t off[3][2] = { {12, 12}, {8, 16}, {16, 6} };
uint8_t i;
for (i = 0; i < 3; i++) {
int x = zx + off[i][0];
if (getpixel(x, zy + off[i][1]) != stripe_color(x))
return 0;
}
return 1;
}
static void pause_sec(uint8_t n)
{
datetime_t dt;
uint8_t last;
getdatetime(&dt);
last = dt.second;
while (n) {
getdatetime(&dt);
if (dt.second != last) {
last = dt.second;
n--;
}
}
}
static void say(int y, const char *s)
{
outtextxy(4, y, s);
}
int main(void)
{
uint8_t chkA, chkB;
int x, i;
build_images();
initgraph();
cleardevice();
gfx_sprite_clip(1);
setcolor(WHITE);
/* ---- S1: фон -------------------------------------------------- */
say(4, "SPRITES: putsprite/movesprite/heal");
for (x = 0; x < 320; x += 8) {
setfillstyle(SOLID_FILL, stripe_color(x));
bar(x, BG_Y0, x + 7, BG_Y1);
}
say(208, "S1 bg");
(void)getchar();
/* ---- S2: putsprite + клипы + heal-проба + атлас ---------------- */
putsprite(CX, CY, ball); /* центр: прозрачные углы */
(void)getchar();
putsprite(-12, 52, ball); /* клип слева */
(void)getchar();
putsprite(308, 82, ball); /* клип справа */
(void)getchar();
putsprite(297, 162, ball); /* клип справа */
(void)getchar();
putsprite(60, -10, ball); /* клип сверху */
(void)getchar();
putsprite(290, 244, ball); /* клип снизу */
(void)getchar();
putsprite(60, 130, ball); /* heal-проба: нарисовать... */
(void)getchar();
gfx_heal(60, 130, BW, BW); /* ...и стереть (шара НЕТ) */
(void)getchar();
gfx_set_bank(GFX_BANK_SPRITE); /* атлас: кадр 1 (MAGENTA) */
(void)getchar();
gfx_blit_part(270, 130, strip, BW, 0, BW, BW);
(void)getchar();
gfx_set_bank(GFX_BANK_NORMAL);
(void)getchar();
chkA = ram_is_stripes(CX, CY);
say(218, "S2 putsprite+clip, heal@60,130");
say(228, chkA ? "A RAM under sprite: PASS"
: "A RAM under sprite: FAIL");
pause_sec(4);
/* ---- S3: movesprite — след обязан быть чистым ------------------ */
x = 16;
putsprite(x, 60, ball);
(void)getchar();
for (i = 0; i < 12; i++) {
movesprite(x, 60 + (x - 16) / 2, x + 16, 60 + (x - 16 + 16) / 2,
ball);
x += 16;
(void)getchar();
}
chkB = ram_is_stripes(16, 60) && ram_is_stripes(112, 108);
say(238, chkB ? "B RAM along path: PASS"
: "B RAM along path: FAIL");
say(248, "S3 moved 16,60 -> 208,156 done");
(void)getchar();
}