/* * 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 #include #include #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(); }