a823e7ee9c
Акселератор умеет не только копировать блок, но и совмещать его с приёмником: опкод `and/or/xor (hl)` между триггерами чтения и записи переводит внутренний автомат из «буфер := память» в «буфер <op>= память». Операция ортогональна направлению (гориз. LD L,L / верт. LD A,A), значит одинаково работает и для row-major картинок, и для column-major спрайтов персонажей с бесплатным флипом. Полный набор, по образцу существующего копирующего: - ядра bgi256/_bgi_blit_rows_op_raw.c и _bgi_blit_cols_op_raw.c; - строками: gfx_blit_op / gfx_blit_part_op (через _gfx_blit_full_op); - колонками: gfx_blit_cols_op / gfx_blit_cols_part_wx_op (skip/rows/ skipw/maxw и flip — как у копирующего близнеца); - putimage(XOR/OR/AND_PUT) переведён на accel, попиксельным остался только NOT_PUT — закрыт пункт 2d-1 docs/TODO.md. Одна функция на три операции: опкод патчится SMC, как размер блока и страйд, — ветвления в цикле нет. Колоночный путь дороже строкового на один OUT Port_Y: вертикальный op-burst шагает Port_Y, и перед записью его надо вернуть на верх колонки (STOP обязателен — иначе fetch операнда OUT перезапустит burst, memory/accel_operand_fetch_retrigger). Две оговорки (в шапках модулей): - операция ЧИТАЕТ ОЗУ-копию экрана, а не видео-ОЗУ, поэтому при банках 0x54/0x5C совмещается с чистым фоном, а не с нарисованным поверх; - аппаратная прозрачность #FF совместима с AND (нейтраль) и OR (#FF|bg = #FF, запись подавляется), но НЕ с XOR: там источник обязан хранить прозрачный пиксель как 0x00. Проверка: tests/accop (новый) — 8/8 PASS в MAME, побайтно, источник с разными значениями по строкам И колонкам + рамка вокруг; tests/bgi_img получил две байтовые самопроверки (COPY==источник, XOR дважды == фон), обе PASS. make size-check: роста нет (эталон принят заново — kbdpoll +63 Б приехал из прошлой сессии, воспроизводится и без этих правок). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
155 lines
6.4 KiB
C
155 lines
6.4 KiB
C
/*
|
||
* bgi_img — тест спрайтов BGI: getimage / putimage / imagesize
|
||
* (режим 320×256×256, --gfx 256).
|
||
*
|
||
* Что проверяем:
|
||
* 1. imagesize() совпадает с реальным размером буфера (self-check —
|
||
* при расхождении рисуется красная плашка "SIZE MISMATCH").
|
||
* 2. getimage() снимает прямоугольник экрана в буфер (заголовок
|
||
* w,h + w*h байт).
|
||
* 3. putimage() кладёт образ всеми пятью операциями COPY/XOR/OR/AND/
|
||
* NOT поверх серого фона — визуально видно разницу операций.
|
||
* 4. XOR дважды по одному месту восстанавливает фон (классический
|
||
* признак корректного спрайтового XOR).
|
||
*
|
||
* Прогон в MAME визуальный: см. что источник и его COPY-копия
|
||
* идентичны (в т.ч. красный маркер в левом-верхнем углу — ловит
|
||
* зеркалирование), а XOR-дважды не оставляет следа на фоне.
|
||
*
|
||
* 2026-08-11: XOR/OR/AND_PUT переведены с попиксельного цикла на блочные
|
||
* операции акселератора (gfx_blit_part_op), поэтому к визуальной проверке
|
||
* добавлены ДВЕ БАЙТОВЫЕ (PASS/FAIL на экране) — глазами тут промах на
|
||
* пиксель или уехавший Port_Y не поймать:
|
||
* P1 COPY == источник побайтно (клип/адресация не сдвинули образ);
|
||
* P2 XOR дважды == чистый фон побайтно (op-burst читает ровно тот байт
|
||
* приёмника, поверх которого потом пишет).
|
||
* Механика самих блочных операций проверяется отдельно — tests/accop.
|
||
*/
|
||
|
||
#include <graphics.h>
|
||
#include <stdint.h>
|
||
|
||
/* Габариты спрайта-источника. Из них же считаем и рект, и буфер —
|
||
* так imagesize() и sizeof(buf) обязаны сойтись. */
|
||
#define SPR_L 16
|
||
#define SPR_T 28
|
||
#define SPR_W 40
|
||
#define SPR_H 32
|
||
|
||
/* Буфер образа: 4 байта заголовка (w,h) + по байту на пиксель. */
|
||
static uint8_t sprite[4 + SPR_W * SPR_H];
|
||
|
||
/* Нарисовать узнаваемый ассиметричный образ в (SPR_L,SPR_T). */
|
||
static void draw_source(void)
|
||
{
|
||
int l = SPR_L, t = SPR_T;
|
||
int r = l + SPR_W - 1, b = t + SPR_H - 1;
|
||
|
||
setfillstyle(SOLID_FILL, LIGHTBLUE);
|
||
bar(l, t, r, b); /* фон образа */
|
||
|
||
setcolor(YELLOW);
|
||
circle(l + SPR_W / 2, t + SPR_H / 2, 10);
|
||
|
||
setcolor(WHITE);
|
||
line(l, t, r, b); /* диагональ */
|
||
|
||
/* Красный маркер в левом-верхнем углу — детектор ориентации. */
|
||
setfillstyle(SOLID_FILL, LIGHTRED);
|
||
bar(l, t, l + 5, t + 5);
|
||
|
||
setcolor(WHITE);
|
||
rectangle(l - 1, t - 1, r + 1, b + 1); /* рамка вокруг источника */
|
||
}
|
||
|
||
/* Прямоугольник (x0,y0,w,h) совпадает с прямоугольником (x1,y1,w,h)? */
|
||
static uint8_t rect_eq(int x0, int y0, int x1, int y1, int w, int h)
|
||
{
|
||
int cx, cy;
|
||
|
||
for (cy = 0; cy < h; cy++)
|
||
for (cx = 0; cx < w; cx++)
|
||
if (getpixel(x0 + cx, y0 + cy) != getpixel(x1 + cx, y1 + cy))
|
||
return 0;
|
||
return 1;
|
||
}
|
||
|
||
static void say(int x, int y, const char *s, uint8_t ok)
|
||
{
|
||
setcolor(ok ? LIGHTGREEN : LIGHTRED);
|
||
outtextxy(x, y, s);
|
||
outtextxy(x + textwidth((char *)s) + 4, y, ok ? "PASS" : "FAIL");
|
||
}
|
||
|
||
int main(void)
|
||
{
|
||
static const int ops[5] = { COPY_PUT, XOR_PUT, OR_PUT, AND_PUT, NOT_PUT };
|
||
static const char *names[5] = { "COPY", "XOR", "OR", "AND", "NOT" };
|
||
uint8_t p_copy, p_xor2;
|
||
int i, x;
|
||
int sl = SPR_L, st = SPR_T;
|
||
int sr = SPR_L + SPR_W - 1, sb = SPR_T + SPR_H - 1;
|
||
|
||
initgraph();
|
||
cleardevice();
|
||
|
||
setcolor(WHITE);
|
||
settextstyle(DEFAULT_FONT, HORIZ_DIR, 1);
|
||
outtextxy(80, 6, "getimage/putimage test");
|
||
|
||
/* --- источник --- */
|
||
outtextxy(SPR_L, 18, "src");
|
||
draw_source();
|
||
|
||
/* --- imagesize() self-check --- */
|
||
if (imagesize(sl, st, sr, sb) != (unsigned)sizeof(sprite)) {
|
||
setfillstyle(SOLID_FILL, LIGHTRED);
|
||
bar(80, 90, 240, 100);
|
||
setcolor(WHITE);
|
||
outtextxy(84, 92, "SIZE MISMATCH");
|
||
}
|
||
|
||
/* --- снимаем образ --- */
|
||
getimage(sl, st, sr, sb, sprite);
|
||
|
||
/* --- пять операций putimage поверх серого фона --- */
|
||
for (i = 0; i < 5; i++) {
|
||
x = 16 + i * 60;
|
||
setcolor(WHITE);
|
||
outtextxy(x, 108, names[i]);
|
||
|
||
setfillstyle(SOLID_FILL, LIGHTGRAY); /* общий фон под все ops */
|
||
bar(x, 120, x + SPR_W - 1, 120 + SPR_H - 1);
|
||
|
||
putimage(x, 120, sprite, ops[i]);
|
||
}
|
||
|
||
/* P1: COPY-копия (x = 16, первая в ряду) обязана совпасть с источником
|
||
* побайтно — ловит сдвиг на пиксель и зеркалирование. */
|
||
p_copy = rect_eq(16, 120, sl, st, SPR_W, SPR_H);
|
||
|
||
/* --- XOR дважды = восстановление фона --- */
|
||
setcolor(WHITE);
|
||
outtextxy(16, 176, "XOR x2 -> restores bg (patch stays clean):");
|
||
|
||
setfillstyle(SOLID_FILL, GREEN);
|
||
bar(16, 190, 16 + SPR_W - 1, 190 + SPR_H - 1);
|
||
putimage(16, 190, sprite, XOR_PUT); /* наложили */
|
||
putimage(16, 190, sprite, XOR_PUT); /* сняли */
|
||
|
||
/* Рядом — тот же зелёный фон нетронутым, для сравнения. */
|
||
setfillstyle(SOLID_FILL, GREEN);
|
||
bar(80, 190, 80 + SPR_W - 1, 190 + SPR_H - 1);
|
||
setcolor(WHITE);
|
||
outtextxy(130, 202, "<- these two must match ->");
|
||
|
||
/* P2: наложенный и снятый XOR не оставил следа — сравниваем с
|
||
* нетронутым зелёным эталоном справа. */
|
||
p_xor2 = rect_eq(16, 190, 80, 190, SPR_W, SPR_H);
|
||
|
||
say(16, 228, "P1 COPY==src:", p_copy);
|
||
say(16, 240, "P2 XORx2==bg:", p_xor2);
|
||
|
||
for (;;) { }
|
||
}
|