/*
 * 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;
}
