/* * gfxbanks — Фаза 0 спрайтового дизайна: верификация подрежимов * видеостраниц #50..#5F (docs/sprite-api-design.md, «Фаза 0»). * * Семантика по доку (Иван Мак §4.3): * bit3 (0x58/0x5C) — запись байта 0xFF НЕ производится (прозрачность); * bit2 (0x54/0x5C) — запись только в видео-ОЗУ, ОЗУ-копия не трогается; * чтение из #50..#5F всегда возвращает ОЗУ-копию (getpixel = ОЗУ). * * ВНИМАНИЕ: вердикт MAME НЕ финален — тест прогоняется и в MAME, и на * железе. Известное расхождение: в MAME 0.283 (наш v306) FF-скип bit3 * гасит ТОЛЬКО запись в VRAM, а теневое ОЗУ при bit2=0 всё равно * получает FF (ram_w: `if (BIT(~page,2)) ram[...]=data; * if (!(BIT(page,3) && data==0xff)) vram_w(...)`); в master-драйвере * уже полное подавление (`return`) — по доку. Через 0x5C расхождение * не проявляется (bit2 гасит ОЗУ-ветку) — поэтому putsprite планово * работает через 0x5C. На банке 0x58 зона SKIP это расхождение * ловит: chkC = FAIL на 0.283, PASS по доку/master. * * Сценарий (4 стадии, скриншот после каждой; палитра[255] = синий; * паузы — по RTC getdatetime, т.к. delayms в --memory small неработосп. * (irq_install → EINVAL), а скорость CPU в паузах не участвует): * S1 фон: вертикальные полосы RED/LIGHTGRAY через 0x50 (VRAM+ОЗУ). * S2 «спрайты» (после Фазы A putimage(COPY) идёт через акселератор, * поэтому CPU-путь в ZL — честный цикл putpixel): * ZL (CPU-путь): банк 0x5C, putpixel-ями круг GREEN с полями * 0xFF — ожидание: круг поверх полос, поля прозрачны; * ZR (accel-путь): банк 0x5C, bar 0xFF 64×64 (accel-fill: * ожидание no-op) + putimage того же круга (accel-COPY: * прозрачность полей на burst-пути); * ZB (bit3-skip): банк 0x58, putimage 0xFF (accel-copy, верх) + * bar 0xFF (accel-fill, низ) — ожидание по доку: no-op * экрана и ОЗУ. * Проверки по ОЗУ (getpixel): A = ОЗУ под ZL/ZR не тронуто (bit2); * C = ОЗУ под ZB не тронуто (полное подавление FF-записи). * Hex-пробы ОЗУ печатаются рядом (ожидание — цвета полос 04/07). * S3 КЛЮЧЕВОЙ ШАГ — 0xFF реально в VRAM: банк 0x54 (скипа нет), * квадрат 0xFF 32×32 поверх круга (CPU putpixel-ями в ZL, * accel-copy putimage'ем в ZR). * Экран: СИНИЙ квадрат → FF отображается как цвет 255 * (семантика дока полная, стирание = heal); * ПОЛОСЫ → видеокарта подставляет ОЗУ-копию * (дешёвое стирание FF-заливкой работает); * круг/зелёный → 0x54 не эмулируется. * Проверка B = ОЗУ всё ещё полосы (bit2 у 0x54). * S4 heal: чтение+запись через 0x50 ЛЕВЫХ половин всех зон — * левые половины возвращаются к фону ИЗ ОЗУ-КОПИИ, правые * сохраняют артефакты S2/S3. NB: если ОЗУ под зоной испорчено * (chkC FAIL на 0.283), heal честно перенесёт порчу в VRAM — * зона SKIP посинеет на этой стадии. * * Запуск: * python3 toolchain/mame_interactive.py tests/gfxbanks/gfxbanks.exe \ * --snap 13,17,21,25 --timeout 27 */ #include #include #include #define ZY 56 /* верх зон */ #define ZH 64 /* зоны 64×64 */ #define ZLX 16 /* 0x5C, CPU-путь */ #define ZRX 120 /* 0x5C, accel-путь */ #define ZBX 224 /* 0x58, bit3-skip */ #define STRIPE_Y0 48 #define STRIPE_Y1 135 /* Точки-пробы внутри области, куда S2 писал НЕ-FF байты (круг r=28 и * квадрат +16..+47 содержат все три), — общие для проверок и hex-дампа. */ static const uint8_t probe[3][2] = { {32, 32}, {20, 28}, {40, 36} }; /* Полосы шириной 8: цвет по абсолютному x */ static uint8_t stripe_color(int x) { return ((x >> 3) & 1) ? LIGHTGRAY : RED; } /* Спрайт 64×64: круг GREEN r=28, вокруг — прозрачный 0xFF */ static uint8_t spr[4 + 64 * 64]; /* Квадрат 32×32 сплошного 0xFF */ static uint8_t ffsq[4 + 32 * 32]; static void build_images(void) { uint8_t *p = spr; int x, y; *p++ = 64; *p++ = 0; *p++ = 64; *p++ = 0; for (y = 0; y < 64; y++) for (x = 0; x < 64; x++) { int dx = x - 32, dy = y - 32; *p++ = (dx * dx + dy * dy <= 28 * 28) ? GREEN : 0xFF; } p = ffsq; *p++ = 32; *p++ = 0; *p++ = 32; *p++ = 0; for (x = 0; x < 32 * 32; x++) *p++ = 0xFF; } /* ОЗУ-копия зоны — нетронутые полосы? */ static uint8_t ram_is_stripes(int zx) { uint8_t i; for (i = 0; i < 3; i++) { int x = zx + probe[i][0]; if (getpixel(x, ZY + probe[i][1]) != stripe_color(x)) return 0; } return 1; } static char hexd(uint8_t v) { v &= 15; return v < 10 ? '0' + v : 'A' + v - 10; } /* Hex-дамп трёх ОЗУ-проб зоны: "ZX 04 04 07" в колонке x=216 */ static void show_probes(const char *tag, int zx, int ty) { char buf[12]; uint8_t i, v; buf[0] = tag[0]; buf[1] = tag[1]; for (i = 0; i < 3; i++) { v = (uint8_t)getpixel(zx + probe[i][0], ZY + probe[i][1]); buf[2 + i * 3] = ' '; buf[3 + i * 3] = hexd(v >> 4); buf[4 + i * 3] = hexd(v); } buf[11] = 0; outtextxy(216, ty, buf); } /* Пауза n секунд по RTC (гранулярность 1 с, не зависит от CPU/режима) */ static void pause_sec(uint8_t n) { datetime_t dt; uint8_t last; getdatetime(&dt); last = dt.second; while (n) { getdatetime(&dt); if (dt.second != last) { last = dt.second; n--; } } } /* CPU-путь: вывести rect w×h из img (getimage-формат) в (x0,y0) * по-пиксельно через putpixel (LD (HL),A — НЕ акселератор). */ static void blit_cpu(int x0, int y0, const void *img, int w, int h) { const uint8_t *p = (const uint8_t *)img + 4; int x, y; for (y = 0; y < h; y++) for (x = 0; x < w; x++) putpixel(x0 + x, y0 + y, *p++); } /* CPU-путь: залить rect цветом по-пиксельно. */ static void fill_cpu(int x0, int y0, int w, int h, uint8_t color) { int x, y; for (y = 0; y < h; y++) for (x = 0; x < w; x++) putpixel(x0 + x, y0 + y, color); } /* heal: прочитать (вернёт ОЗУ-копию) и записать (уйдёт в VRAM+ОЗУ) * каждый пиксель прямоугольника банком 0x50. Прообраз gfx_heal. */ static void heal(int x0, int y0, int w, int h) { int x, y; for (y = y0; y < y0 + h; y++) for (x = x0; x < x0 + w; x++) putpixel(x, y, getpixel(x, y)); } static void say(int y, const char *s) { outtextxy(4, y, s); } int main(void) { uint8_t chkA, chkB, chkC; int x; build_images(); initgraph(); gfx_pal_set(0, 255, 0, 0, 255); /* цвет 255 = ярко-синий */ cleardevice(); setcolor(WHITE); /* ---- S1: фон ------------------------------------------------ */ say(4, "GFXBANKS: page submodes 50/54/58/5C"); for (x = 0; x < 320; x += 8) { setfillstyle(SOLID_FILL, stripe_color(x)); bar(x, STRIPE_Y0, x + 7, STRIPE_Y1); } outtextxy(ZLX, 44, "5C CPU"); outtextxy(ZRX, 44, "5C ACC"); outtextxy(ZBX, 44, "58 SKIP"); say(148, "S1 stripes via 50"); pause_sec(4); /* ---- S2: спрайты через 0x5C / no-op через 0x58 --------------- */ gfx_set_bank(0x5C); blit_cpu(ZLX, ZY, spr, 64, 64); /* CPU: круг + FF-поля */ setfillstyle(SOLID_FILL, 255); bar(ZRX, ZY, ZRX + 63, ZY + 63); /* accel-fill FF — ждём no-op */ putimage(ZRX, ZY, spr, COPY_PUT); /* accel-COPY: круг + FF-поля */ gfx_set_bank(0x58); putimage(ZBX, ZY, ffsq, COPY_PUT); /* accel-copy FF, верх зоны */ setfillstyle(SOLID_FILL, 255); bar(ZBX, ZY + 32, ZBX + 31, ZY + 63); /* accel-fill FF, низ зоны */ gfx_set_bank(0x50); chkA = ram_is_stripes(ZLX) && ram_is_stripes(ZRX); chkC = ram_is_stripes(ZBX); say(158, "S2 sprites 5C, ff-skip 58"); say(168, chkA ? "A bit2 RAM intact: PASS" : "A bit2 RAM intact: FAIL"); show_probes("ZL", ZLX, 168); say(178, chkC ? "C bit3 skip (RAM): PASS" : "C bit3 skip (RAM): FAIL"); show_probes("ZB", ZBX, 178); pause_sec(4); /* ---- S3: 0xFF реально в VRAM (банк 0x54, скипа нет) ---------- */ gfx_set_bank(0x54); fill_cpu(ZLX + 16, ZY + 16, 32, 32, 0xFF); /* CPU-путь */ putimage(ZRX + 16, ZY + 16, ffsq, COPY_PUT); /* accel-copy путь */ gfx_set_bank(0x50); chkB = ram_is_stripes(ZLX) && ram_is_stripes(ZRX); say(188, "S3 KEY: ff into vram via 54"); say(198, chkB ? "B 54 RAM intact: PASS" : "B 54 RAM intact: FAIL"); show_probes("ZR", ZRX, 198); say(208, "S3 look: BLUE=doc STRIPES=subst"); pause_sec(4); /* ---- S4: heal левых половин зон ------------------------------ */ heal(ZLX, ZY, 32, ZH); heal(ZRX, ZY, 32, ZH); heal(ZBX, ZY, 32, ZH); say(218, "S4 healed left halves, done"); for (;;) { } }