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>
This commit is contained in:
2026-07-12 21:47:37 +03:00
parent 78161561e7
commit 72ce66275e
42 changed files with 2835 additions and 32 deletions
+66
View File
@@ -68,6 +68,30 @@ void _bgi_hspan_raw(int x, int y, int len, uint8_t color) __preserves_regs(d
void _bgi_vspan_raw(int x, int y, int len, uint8_t color) __preserves_regs(d,e); /* V-отрезок, без скобки */
void _bgi_clear_raw(uint8_t color); /* заливка экрана, без скобки (клоббер всех) */
/* Копирование h строк по w байт через accel block-copy (LD L,L) —
* ядро блиттинга (putimage, getimage, gfx_blit, gfx_heal). Без скобки и
* клиппинга; w,h: 0 = 256; stride 0 = адрес на месте (сторона экрана,
* строку выбирает Port_Y); y0 — стартовый Port_Y. Направление задают
* указатели (буфер обязан быть вне W3). Контракт/схема — в
* bgi256/_bgi_copy_rows_raw.c. */
void _bgi_copy_rows_raw(const uint8_t *src, uint8_t *dst,
uint8_t w, uint8_t h,
int dstride, int sstride, uint8_t y0);
/* Специализация для БЛИТА: dst фиксирован (dstride 0), src двигается на
* sstride (SMC-константа); dst-цепочка выкинута (~24Т/строку). Ядро
* gfx_blit_part/putsprite/спрайтового движка. Без скобки/клиппинга; банк
* ставит вызывающий (0x5C прозрачный). bgi256/_bgi_blit_rows_raw.c. */
void _bgi_blit_rows_raw(const uint8_t *src, uint8_t *dst,
uint8_t w, uint8_t h,
int sstride, uint8_t y0);
/* Специализация для heal: src == dst == адрес экрана, оба страйда 0
* (строку выбирает Port_Y) — адресной арифметики в цикле нет, строка
* 77Т + burst'ы. y0 — стартовый Port_Y; w,h: 0 = 256. Без скобки и
* клиппинга; банк ставит вызывающий (gfx_heal форсит 0x50). */
void _bgi_heal_rows_raw(uint8_t *scr, int y0, uint8_t w, uint8_t h);
/* Self-bracket версии (begin + raw + end) — для разовых вызовов из
* математики, которой не нужен пакетный режим (floodfill, cleardevice).
* Разовое чтение пикселя — публичный getpixel (та же композиция;
@@ -75,6 +99,48 @@ void _bgi_clear_raw(uint8_t color); /* заливка экр
void _bgi_hspan (int x, int y, int len, uint8_t color);
void _bgi_clearall(uint8_t color);
/* Ядро блиттинга gfx_blit_part/gfx_blit/gfx_heal (Фаза B) — публичные,
* прототипы в <gfx.h>; спрайтовые обёртки putsprite/movesprite — в
* <graphics.h>. Реализации: common/gfx_blit_part.c и соседи. */
/* Bracket-free общие ядра (полная ширина) (common/_gfx_blit_full.c, _gfx_heal_full.c):
* клип по экрану + нарезка полос ≤256 + accel-leaf, БЕЗ _bgi_begin/
* _bgi_end и БЕЗ смены банка. Вызывающий обязан замапить W3 (_bgi_begin)
* и выставить нужный банк ДО вызова. Общее ядро для разовых обёрток
* (gfx_blit_part/gfx_heal) и batch-пасса спрайтового движка v2, который
* держит одну скобку + один банк на весь кадр (docs/sprite-api-design.md
* §9.1). blit: банк 0x5C для прозрачности; heal: банк 0x50. */
void _gfx_blit_full(int x, int y, const void *img,
int sx, int sy, int w, int h);
void _gfx_heal_full(int x, int y, int w, int h);
/* Лин-ядра для СПРАЙТОВ (кадр ≤ 64×64, sprite.h): как *_full, но без
* split'а >256 и с 8-битными w/h (движок/putsprite — горячий путь;
* программа только со спрайтами не тянет общие *_full со split'ом).
* common/_gfx_blit_sprite.c, _gfx_heal_sprite.c. */
void _gfx_blit_sprite(int x, int y, const void *img,
int sx, int sy, uint8_t w, uint8_t h);
void _gfx_heal_sprite(int x, int y, uint8_t w, uint8_t h);
/* NOCLIP-ядра: клип-кода нет вообще (полный выигрыш от codegen, не только
* скип проверок). Зовутся при gfx_sprite_clip(0) — приложение
* гарантирует пределы. common/_gfx_blit_sprite_noclip.c, _gfx_heal_… */
void _gfx_blit_sprite_noclip(int x, int y, const void *img,
int sx, int sy, uint8_t w, uint8_t h);
void _gfx_heal_sprite_noclip(int x, int y, uint8_t w, uint8_t h);
/* Диспетчеризация clip/noclip через УКАЗАТЕЛИ (не ветка в горячем цикле):
* gfx_sprite_clip() перенаправляет их на clip- или noclip-ядро ОДИН раз,
* а sprite_update/putsprite/movesprite зовут через указатель — один
* call-site, без if в цикле → тесный codegen (полный выигрыш noclip,
* см. docs/sprite-api-design.md). Установка по умолчанию (clip on) — в
* initgraph. */
typedef void (*_gfx_blit_fp)(int, int, const void *, int, int,
uint8_t, uint8_t);
typedef void (*_gfx_heal_fp)(int, int, uint8_t, uint8_t);
extern _gfx_blit_fp _gfx_blit_fn;
extern _gfx_heal_fp _gfx_heal_fn;
/* ---- Fill-burst'ы акселератора 256 (register-ABI, asm-only) ------ *
* Вызываются ТОЛЬКО из asm raw-примитивов; аргументы в регистрах, без
* глобального скретча в _DATA: