Files
Sprinter-SDCC/examples/space/space.c
T
snark13 6dea6955c2 libbgi: gfx_pal_sync + палитра ↔ файл (gfx_pal_fsave/fload); все PASS
- gfx_pal_sync(): палитра страницы 1 := палитре 0 (все 256 записей,
  чанками по 64 через общий буфер _gfx_pal_buf в W2) — обязательный
  шаг дабл-буфера (грабли examples/space: без него флип на страницу 1
  чёрный).  examples/space переведён на хелпер.
- gfx_pal_fsave(pal, path): 256 записей × 4 Б (B,G,R,0 — родной формат
  BIOS $A4) = 1024 Б.
- gfx_pal_fload(pal, path): принимает и усечённый файл (64 Б = палитра
  16 цветов) — грузит сколько есть, остальные слоты не трогает;
  возвращает число записей.

tests/palfile (MAME dev, все PASS): fsave; fload восстанавливает
испорченные слоты (n=256); усечённый файл на 2 записи чинит только их
(n=2, слот 2 не тронут); sync чинит испорченный слот палитры 1.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 16:51:49 +03:00

190 lines
6.3 KiB
C
Raw Permalink 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.
/*
* space — демо всего спрайтового стека libbgi (2026-07-13):
*
* .atl-атлас с диска (mkatlas.py) → EMM-страница, подключаемая в W0
* (atlas_load: ISR-стаб, ESTEX READ в W3) → движок sprite_t.page
* (sprite_update сам мапит страницу на блит-проход) → авто-анимации:
*
* - 5 астероидов: кадровая ANIM_LOOP (вращение, 4 кадра) +
* sprite_moveto к случайным целям с разными скоростями;
* по прибытии (sprite_moving()==0) — ВЗРЫВ в точке + новая цель;
* - 5 взрывов: ANIM_ONCE (4 кадра); по SPR_ANIM_DONE — sprite_hide;
* - маяк: ANIM_PINGPONG (пульс 0..3..0);
* - дабл-буфер: фон (звёзды + планеты) на обеих страницах, кадр =
* sprite_update на скрытой + vsync + флип; FPS-метр.
*
* Цели держатся в пределах экрана → gfx_sprite_clip(0) (noclip-ядра).
* ESC — выход.
*/
#include <graphics.h>
#include <gfx.h>
#include <sprite.h>
#include <conio.h>
#include <time.h>
#include <stdlib.h>
#define NAST 5 /* астероиды: sp[0..4] */
#define NEX NAST /* взрывы: sp[5..9] */
#define IBEAC (NAST + NEX) /* маяк: sp[10] */
#define NSPR (IBEAC + 1)
#define MAXX (320 - 16)
#define MAXY (256 - 16)
static atlas_t at;
static sprite_t sp[NSPR];
/* Случайная точка в пределах экрана (модуль — не горячий путь). */
static int rnd_x(void) { return rand() % (MAXX - 32) + 16; }
static int rnd_y(void) { return rand() % (MAXY - 48) + 32; }
/* Новая цель астероиду i: скорость по номеру (шаг 1..3, интервал 1..2). */
static void new_target(uint8_t i)
{
sprite_moveto(&sp[i], rnd_x(), rnd_y(),
(uint8_t)(1 + i % 3), (uint8_t)(1 + (i & 1)));
}
static void draw_space(void)
{
int i;
/* Фон обязан быть ИДЕНТИЧЕН на обеих страницах (иначе флип 25 Гц
* мерцает разными звёздами) — фиксированный seed на отрисовку. */
srand(1234);
cleardevice();
setfillstyle(SOLID_FILL, BLUE); /* планеты */
fillellipse(60, 200, 26, 26);
setfillstyle(SOLID_FILL, LIGHTBLUE);
fillellipse(60, 200, 18, 18);
setfillstyle(SOLID_FILL, BROWN);
fillellipse(270, 190, 14, 14);
for (i = 0; i < 90; i++) /* звёзды */
putpixel(rand() % 320, rand() % 256,
(i & 3) ? DARKGRAY : WHITE);
setcolor(WHITE);
outtextxy(36, 4, "SPACE: atlas W0 + auto-anim, ESC");
}
#define FPS_METER 1
#if FPS_METER
static void show_fps(uint8_t v)
{
char buf[4];
buf[0] = (char)('0' + (v / 100) % 10);
buf[1] = (char)('0' + (v / 10) % 10);
buf[2] = (char)('0' + v % 10);
buf[3] = 0;
setcolor(WHITE);
setfillstyle(SOLID_FILL, BLACK);
bar(0, 0, 27, 9);
outtextxy(2, 1, buf);
}
#endif
int main(void)
{
uint8_t i, page, hidden;
datetime_t dt;
#if FPS_METER
uint8_t frames = 0, last_sec, fps_val = 0, fps_draw = 0;
#endif
if (atlas_load(&at, "space.atl") != 0 &&
atlas_load(&at, "a:\\space.atl") != 0)
return 1;
getdatetime(&dt);
srand((unsigned)(dt.second * 77u + dt.minute));
initgraph();
gfx_sprite_clip(0); /* цели в пределах */
gfx_pal_sync(); /* обязательный шаг дабл-буфера (см. gfx.h) */
/* Спрайты: астероиды (LOOP + цель), взрывы (скрыты), маяк (PINGPONG). */
for (i = 0; i < NAST; i++) {
atlas_sprite_init(&sp[i], &at, 0);
sp[i].x = rnd_x();
sp[i].y = rnd_y();
sprite_anim(&sp[i], 0, 3, (uint8_t)(4 + i), ANIM_LOOP);
new_target(i);
sprite_show(&sp[i]);
}
for (i = 0; i < NEX; i++)
atlas_sprite_init(&sp[NAST + i], &at, 1);
atlas_sprite_init(&sp[IBEAC], &at, 2);
sp[IBEAC].x = 292; sp[IBEAC].y = 20;
sprite_anim(&sp[IBEAC], 0, 3, 6, ANIM_PINGPONG);
sprite_show(&sp[IBEAC]);
/* Фон на обеих страницах (ОЗУ-копия = бэкап для heal). */
for (page = 0; page < 2; page++) {
gfx_set_draw_page(page);
draw_space();
sprite_update(sp, NSPR);
}
gfx_set_visible_page(0);
getdatetime(&dt);
srand((unsigned)(dt.second * 77u + dt.minute)); /* цели — случайные */
#if FPS_METER
getdatetime(&dt);
last_sec = dt.second;
#endif
for (;;) {
if (kbhit() && getch() == 27)
break;
/* Прибывшие астероиды: взрыв в точке + новая цель. */
for (i = 0; i < NAST; i++) {
if (!sprite_moving(&sp[i])) {
sprite_t *ex = &sp[NAST + i];
sprite_move(ex, sp[i].x, sp[i].y);
sprite_anim(ex, 0, 3, 4, ANIM_ONCE);
sprite_show(ex);
new_target(i);
}
}
/* Отгоревшие взрывы — спрятать. */
for (i = 0; i < NEX; i++) {
sprite_t *ex = &sp[NAST + i];
if ((sprite_anim_status(ex) & SPR_ANIM_DONE) &&
(ex->flags & SPR_VISIBLE))
sprite_hide(ex);
}
hidden = gfx_get_visible_page() ^ 1;
gfx_set_draw_page(hidden);
#if FPS_METER
/* Свежую цифру рисовать на ОБЕИХ страницах (fps_draw = 2 кадра
* подряд) — иначе она живёт на одной и мерцает при флипе. */
frames++;
getdatetime(&dt);
if (dt.second != last_sec) {
last_sec = dt.second;
fps_val = frames;
frames = 0;
fps_draw = 2;
}
if (fps_draw) {
fps_draw--;
show_fps(fps_val);
}
#endif
sprite_update(sp, NSPR);
gfx_wait_vsync();
gfx_set_visible_page(hidden);
}
closegraph();
atlas_free(&at);
return 0;
}