72ce66275e
Спрайтовая графика поверх 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>
98 lines
4.4 KiB
C
98 lines
4.4 KiB
C
/*
|
||
* spriteclip — ЭКСПЕРИМЕНТ (гипотеза пользователя 2026-07-12): режет ли
|
||
* ЖЕЛЕЗО спрайт, выведенный за ПРАВЫЙ/НИЖНИЙ край экрана, БЕЗ
|
||
* программного клипа? Или запись за x>=320 / y>=256 портит соседнюю
|
||
* память (следующую строку / страницу 1 / верх экрана)?
|
||
*
|
||
* Метод: блит 16×16 НЕПРОЗРАЧНОГО белого спрайта мимо клипа — прямым
|
||
* вызовом leaf'а _bgi_copy_rows_raw (в обход _gfx_blit_clip). Фон —
|
||
* синий; зелёные линии-маркеры на x=319 и y=255 (граница экрана);
|
||
* левый край (0..7) и верх (0..7) залиты красным-маркером, чтобы
|
||
* поймать «заворот» переполнения именно туда.
|
||
*
|
||
* Читать скриншот:
|
||
* - ПРАВЫЙ (спрайт в x=312): если видно только 8 левых колонок белого
|
||
* (x=312..319), а правых 8 нет И красная левая полоса цела →
|
||
* железо режет по правому краю. Если правые 8 колонок «завернулись»
|
||
* белым в x=0..7 (поверх красного) или появился мусор — НЕ режет,
|
||
* клип обязателен.
|
||
* - НИЖНИЙ (спрайт в y=250): если видно 6 строк белого (y=250..255),
|
||
* а строк за 256 нет И красная верхняя полоса цела → железо режет по
|
||
* нижнему. Если белое всплыло сверху (y=0..) — Port_Y завернулся.
|
||
* - УГОЛ (x=312,y=250): комбинация.
|
||
*
|
||
* НЕ для CI-регресса — разовый hardware-probe (и в MAME, и на железе).
|
||
*/
|
||
|
||
#include <graphics.h>
|
||
#include <gfx.h>
|
||
#include <conio.h>
|
||
#include <stdint.h>
|
||
|
||
/* Внутренние символы libbgi (глобальны в bgi256.lib) — тянем напрямую
|
||
* ради no-clip блита. Обычному коду так делать НЕ надо (есть gfx_blit). */
|
||
extern void _bgi_begin(void);
|
||
extern void _bgi_end(void);
|
||
extern 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);
|
||
extern uint16_t _gfx_addr_base;
|
||
|
||
#define BW 16
|
||
static uint8_t sprite[4 + BW * BW];
|
||
|
||
static void build_sprite(void)
|
||
{
|
||
uint8_t *p = sprite;
|
||
int i;
|
||
|
||
*p++ = BW; *p++ = 0; *p++ = BW; *p++ = 0;
|
||
for (i = 0; i < BW * BW; i++)
|
||
*p++ = WHITE; /* непрозрачный: каждый байт пишется */
|
||
}
|
||
|
||
/* Блит БЕЗ клипа: адрес = base + x, Port_Y стартует с y, строки шагают
|
||
* Port_Y'ем (dstride 0). x/y могут выходить за экран — в этом и суть. */
|
||
static void blit_noclip(int x, int y)
|
||
{
|
||
uint8_t *dst = (uint8_t *)(_gfx_addr_base + x);
|
||
|
||
gfx_set_bank(GFX_BANK_NORMAL);
|
||
_bgi_begin();
|
||
_bgi_copy_rows_raw(sprite + 4, dst, BW, BW, 0, BW, (uint8_t)y);
|
||
_bgi_end();
|
||
}
|
||
|
||
int main(void)
|
||
{
|
||
build_sprite();
|
||
initgraph();
|
||
|
||
/* Фон синий. */
|
||
setfillstyle(SOLID_FILL, BLUE);
|
||
bar(0, 0, 319, 255);
|
||
|
||
/* Красные маркер-полосы: левые 8 колонок и верхние 8 строк —
|
||
* сюда «завернётся» переполнение вправо/вниз, если железо не режет. */
|
||
setfillstyle(SOLID_FILL, RED);
|
||
bar(0, 0, 7, 255);
|
||
bar(0, 0, 319, 7);
|
||
|
||
/* Зелёные маркеры границы экрана. */
|
||
setcolor(GREEN);
|
||
line(319, 0, 319, 255);
|
||
line(0, 255, 319, 255);
|
||
|
||
/* Sanity: клипнутый putsprite тем же спрайтом в безопасном месте. */
|
||
putsprite(140, 120, sprite);
|
||
|
||
/* Эксперимент — три no-clip блита за края: */
|
||
blit_noclip(312, 100); /* правый: x+16=328, 8 колонок за 320 */
|
||
blit_noclip(100, 250); /* нижний: y+16=266, 10 строк за 256 */
|
||
blit_noclip(312, 250); /* угол: и вправо, и вниз */
|
||
|
||
getch();
|
||
closegraph();
|
||
return 0;
|
||
}
|