/*
 * blitw — программный регресс ядра блиттинга gfx_blit_part по ширинам.
 *
 * Репро артефакта tests/sprites (клипнутые шары w=12 рисуются со
 * сдвигом): блитим паттерн-образ 24×24 разными ширинами банком 0x50
 * (запись идёт и в ОЗУ-копию → getpixel читает ровно то, что записано)
 * и побайтно сверяем прямоугольник + рамку 1px вокруг (должна остаться
 * фоном).  Вердикт по каждой ширине печатается на экране:
 *   w=NN PASS | w=NN FAIL @x,y eXX gYY (exp/got первого расхождения)
 *
 * Запуск:
 *   python3 toolchain/mame_interactive.py tests/blitw/blitw.exe \
 *       --snap 14 --timeout 16
 */

#include <graphics.h>
#include <gfx.h>

#define IW 24
#define IH 24
#define TX 200          /* позиция тестового прямоугольника (правее текста) */
#define TY 30

static uint8_t img[4 + IW * IH];
static char msg[40];

static uint8_t pat(int x, int y)
{
    return (uint8_t)(((x + 1) * (y + 3)) & 0x7F);   /* != 0xFF, != 0 кроме редких */
}

static void build_img(void)
{
    uint8_t *p = img;
    int x, y;

    *p++ = IW; *p++ = 0; *p++ = IH; *p++ = 0;
    for (y = 0; y < IH; y++)
        for (x = 0; x < IW; x++)
            *p++ = pat(x, y);
}

static char hexd(uint8_t v)
{
    v &= 15;
    return v < 10 ? '0' + v : 'A' + v - 10;
}

/* Проверить блит шириной w в (tx,TY): весь rect = pat, рамка = 0. */
static void check_w(int w, int tx, int ty, int row)
{
    int x, y;
    uint8_t exp, got;
    uint8_t fail = 0;

    /* фон под областью + рамкой */
    setfillstyle(SOLID_FILL, 0);
    bar(tx - 2, ty - 2, tx + w + 1, ty + IH + 1);

    gfx_blit_part(tx, ty, img, 0, 0, w, IH);

    for (y = -1; y <= IH && !fail; y++) {
        for (x = -1; x <= w && !fail; x++) {
            exp = (y >= 0 && y < IH && x >= 0 && x < w) ? pat(x, y) : 0;
            got = (uint8_t)getpixel(tx + x, ty + y);
            if (got != exp) {
                fail = 1;
                msg[0] = 'w'; msg[1] = '0' + w / 10; msg[2] = '0' + w % 10;
                msg[3] = ' '; msg[4] = 'F'; msg[5] = 'A'; msg[6] = 'I';
                msg[7] = 'L'; msg[8] = ' ';
                msg[9]  = 'x'; msg[10] = (char)('0' + (x + 1) / 10);
                msg[11] = (char)('0' + (x + 1) % 10);
                msg[12] = 'y'; msg[13] = (char)('0' + (y + 1) / 10);
                msg[14] = (char)('0' + (y + 1) % 10);
                msg[15] = ' '; msg[16] = 'e'; msg[17] = hexd(exp >> 4);
                msg[18] = hexd(exp);
                msg[19] = ' '; msg[20] = 'g'; msg[21] = hexd(got >> 4);
                msg[22] = hexd(got);
                msg[23] = 0;
            }
        }
    }
    if (!fail) {
        msg[0] = 'w'; msg[1] = '0' + w / 10; msg[2] = '0' + w % 10;
        msg[3] = ' '; msg[4] = 'P'; msg[5] = 'A'; msg[6] = 'S';
        msg[7] = 'S'; msg[8] = 0;
    }
    outtextxy(4, 40 + row * 10, msg);
}

int main(void)
{
    static const uint8_t widths[] = { 24, 16, 12, 8, 4, 2, 1 };
    uint8_t i;

    build_img();
    initgraph();
    cleardevice();
    setcolor(WHITE);
    outtextxy(4, 4, "BLITW: gfx_blit_part width sweep");

    for (i = 0; i < sizeof(widths); i++)
        check_w(widths[i], TX, TY, i);

    /* и правый край, как в sprites: dst у конца строки page 0 */
    check_w(12, 308 - 12, TY, 7);     /* dst близко к краю, но без клипа */

    /* Регресс утечки CPU-байта write-триггера (см. шапку
     * _bgi_copy_rows_raw).  Программно (getpixel = ОЗУ) утечка на MAME
     * 0.283 НЕ ловится: ОЗУ-ветка 0x58 пишет все байты, и burst
     * перезаписывает утечку в ОЗУ; жертвой остаётся только VRAM
     * (FF-скип).  Поэтому проверка ВИЗУАЛЬНАЯ: блит образа с col0=0xFF
     * и последним столбцом YELLOW через 0x5C поверх ЗЕЛЁНОЙ плашки.
     * col0 (x=TX..) обязан остаться ЗЕЛЁНЫМ; до фикса он был YELLOW
     * (CPU-байт = последний байт burst-чтения). */
    {
        static uint8_t timg[4 + 16 * 8];
        uint8_t *p = timg;
        int x, y;

        *p++ = 16; *p++ = 0; *p++ = 8; *p++ = 0;
        for (y = 0; y < 8; y++)
            for (x = 0; x < 16; x++)
                *p++ = (x == 0) ? 0xFF : (x == 15 ? YELLOW : LIGHTBLUE);

        setfillstyle(SOLID_FILL, GREEN);
        bar(TX - 4, 150 - 4, TX + 19, 150 + 11);
        gfx_set_bank(GFX_BANK_SPRITE);
        gfx_blit(TX, 150, timg);
        gfx_set_bank(GFX_BANK_NORMAL);
        outtextxy(4, 40 + 8 * 10 + 4, "trig leak: col0 @200,150 green?");
    }

    outtextxy(4, 40 + 9 * 10 + 6, "done");
    for (;;) { }
}
