/*
 * blitperf — микробенчмарк спрайтового пути: операций в секунду (RTC)
 * для каждого слоя цепочки putsprite/gfx_heal.  Диагностика разрыва
 * с asm-референсом (examples/balls ~1.5 мс/шар при теории ~0.3 мс):
 * отделяет стоимость leaf'ов от C-обвязки (клип/банк/заголовок) и
 * даёт опорные точки (bar — давно оптимизированный accel-путь).
 *
 * Методика: измеряется число вызовов за одну RTC-секунду (старт по
 * смене секунды; время проверяется раз в 8 операций).  Строка «empty»
 * — цена самого цикла (вызов пустышки + getdatetime/8) — вычитать из
 * остальных: t_op = 1/n_op - 1/n_empty.
 *
 * ВНИМАНИЕ: использует ВНУТРЕННИЕ символы libbgi (прототипы
 * продублированы ниже — сверять с libbgi/_bgi.h при изменениях ABI).
 *
 * Запуск:
 *   python3 toolchain/mame_interactive.py tests/blitperf/blitperf.exe \
 *       --snap 22 --timeout 24
 */

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

/* --- внутренности libbgi (копия из _bgi.h, НЕ публичный API) ------- */
extern uint16_t _gfx_addr_base;
void _bgi_begin(void);
void _bgi_end(void);
void _bgi_heal_rows_raw(uint8_t *scr, int y0, uint8_t w, uint8_t h);
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);
/* ------------------------------------------------------------------- */

#define BW 16
static uint8_t spr[4 + BW * BW];

static void build_sprite(void)
{
    uint8_t *p = spr;
    int i;

    *p++ = BW; *p++ = 0; *p++ = BW; *p++ = 0;
    for (i = 0; i < BW * BW; i++)
        *p++ = (uint8_t)(LIGHTRED + ((i & 7) == 0));  /* без 0xFF */
}

/* --- операции ------------------------------------------------------ */

static void op_empty(void) { }

static void op_leaf_heal(void)          /* leaf + W3-скобка, без C-обвязки */
{
    _bgi_begin();
    _bgi_heal_rows_raw((uint8_t *)(_gfx_addr_base + 100), 100, BW, BW);
    _bgi_end();
}

static void op_leaf_blit(void)          /* copy-leaf + скобка, без обвязки */
{
    _bgi_begin();
    _bgi_copy_rows_raw(spr + 4, (uint8_t *)(_gfx_addr_base + 100),
                       BW, BW, 0, BW, 100);
    _bgi_end();
}

static void op_gfx_heal(void)   { gfx_heal(100, 100, BW, BW); }
static void op_blit_part(void)  { gfx_blit_part(100, 100, spr, 0, 0, BW, BW); }
static void op_putsprite(void)  { putsprite(100, 100, spr); }
static void op_bar(void)                /* опорная точка: accel-fill 16×16 */
{
    bar(100, 100, 100 + BW - 1, 100 + BW - 1);
}

/* --- измеритель ---------------------------------------------------- */

static uint16_t bench(void (*op)(void))
{
    datetime_t dt;
    uint8_t s0, k;
    uint16_t n = 0;

    getdatetime(&dt);
    s0 = dt.second;
    do { getdatetime(&dt); } while (dt.second == s0);   /* синхр. к границе */
    s0 = dt.second;
    for (;;) {
        for (k = 0; k < 8; k++)
            op();
        n += 8;
        getdatetime(&dt);
        if (dt.second != s0)
            return n;
    }
}

static void say_num(int y, const char *tag, uint16_t v)
{
    char buf[16];
    uint8_t i;
    uint16_t d = 10000;

    for (i = 0; tag[i] && i < 9; i++)
        buf[i] = tag[i];
    while (i < 10) buf[i++] = ' ';
    for (; d; d /= 10)
        buf[i++] = (char)('0' + (v / d) % 10);
    buf[i] = 0;
    setcolor(WHITE);
    outtextxy(4, y, buf);
}

int main(void)
{
    build_sprite();
    initgraph();
    cleardevice();
    setcolor(WHITE);
    outtextxy(4, 4, "BLITPERF ops/sec (16x16)");

    say_num(20, "empty",     bench(op_empty));
    say_num(30, "leafheal",  bench(op_leaf_heal));
    say_num(40, "leafblit",  bench(op_leaf_blit));
    say_num(50, "gfx_heal",  bench(op_gfx_heal));
    say_num(60, "blitpart",  bench(op_blit_part));
    say_num(70, "putsprite", bench(op_putsprite));
    say_num(80, "bar",       bench(op_bar));
    outtextxy(4, 96, "done");

    for (;;) { }
}
