libbgi: блочные AND/OR/XOR акселератора (блит строками и колонками)

Акселератор умеет не только копировать блок, но и совмещать его с
приёмником: опкод `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>
This commit is contained in:
2026-08-11 15:03:47 +03:00
parent d154cb452c
commit a823e7ee9c
15 changed files with 829 additions and 23 deletions
+25 -20
View File
@@ -8,11 +8,17 @@
* с учётом текущего банка (gfx_set_bank: 0x58 даёт аппаратную
* прозрачность 0xFF, 0x54/0x5C — временный вывод).
*
* XOR/OR/AND — комбинируем с экраном по-пиксельно (raw-чтение +
* raw-плот в одной W3-скобке); NOT — пишем ~src. Ускорение этих op
* блочными режимами акселератора — «следующая версия» (см.
* docs/sprite-api-design.md §9). Клиппинга на этом пути НЕТ
* (per-pixel bounds-check остаётся только в safe-версии).
* XOR/OR/AND_PUT — тоже через акселератор (gfx_blit_part_op, 2026-08-11;
* закрыт пункт 2d-1 docs/TODO.md): акселератор умеет блочные AND/OR/XOR
* между своим буфером и памятью приёмника, попиксельного цикла CPU больше
* нет. Клиппинг у них теперь ТОТ ЖЕ, что у COPY_PUT (раньше на этом пути
* клипа не было вовсе). Оговорки — в шапке common/_gfx_blit_full_op.c:
* операция читает ОЗУ-копию экрана (при банках 0x54/0x5C это чистый фон),
* а прозрачность #FF совместима с AND/OR, но не с XOR.
*
* NOT_PUT — по-прежнему по-пиксельно (пишем ~src): это не операция с
* приёмником, а инверсия источника, у акселератора такого режима нет.
* Клиппинга на этом пути НЕТ (per-pixel bounds-check только в safe).
*
* Буфер bitmap обязан лежать вне W3 (< 0xC000) — на время операции W3
* замаплен на видеобанк.
@@ -23,31 +29,30 @@ void putimage(int left, int top, const void *bitmap, int op)
{
const uint8_t *p = (const uint8_t *)bitmap;
int w, h, x, y;
uint8_t src, dst;
uint8_t gop;
w = p[0] | (p[1] << 8);
h = p[2] | (p[3] << 8);
if (w <= 0 || h <= 0) return;
if (op == COPY_PUT) {
switch (op) {
case COPY_PUT:
gfx_blit_part(left, top, bitmap, 0, 0, w, h);
return;
case XOR_PUT: gop = GFX_OP_XOR; break;
case OR_PUT: gop = GFX_OP_OR; break;
case AND_PUT: gop = GFX_OP_AND; break;
default: gop = 0; break; /* NOT_PUT — попиксельно ниже */
}
if (gop) {
gfx_blit_part_op(left, top, bitmap, 0, 0, w, h, gop);
return;
}
p += 4;
_bgi_begin();
for (y = 0; y < h; y++) {
for (x = 0; x < w; x++) {
src = *p++;
switch (op) {
case XOR_PUT: dst = (uint8_t)_bgi_read_raw(left + x, top + y) ^ src; break;
case OR_PUT: dst = (uint8_t)_bgi_read_raw(left + x, top + y) | src; break;
case AND_PUT: dst = (uint8_t)_bgi_read_raw(left + x, top + y) & src; break;
case NOT_PUT:
default: dst = (uint8_t)~src; break;
}
_bgi_plot_raw(left + x, top + y, dst);
}
}
for (y = 0; y < h; y++)
for (x = 0; x < w; x++)
_bgi_plot_raw(left + x, top + y, (uint8_t)~(*p++));
_bgi_end();
}