Files
Sprinter-SDCC/libbgi/common/gfx_blit_cols_part_wx_op.c
T
snark13 a823e7ee9c 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>
2026-08-11 15:03:47 +03:00

96 lines
5.5 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* gfx_blit_cols_part_wx_op — блит спрайта column-major ЛОГИЧЕСКОЙ ОПЕРАЦИЕЙ:
* приёмник = приёмник <op> источник (op = GFX_OP_AND / GFX_OP_OR /
* GFX_OP_XOR). Полный близнец gfx_blit_cols_part_wx: те же клип-параметры
* (skip/rows сверху, skipw/maxw по колонкам), тот же бесплатный флип; вся
* разница — вызов op-ядра вместо копирующего.
*
* ПОЧЕМУ ОТДЕЛЬНОЕ ТЕЛО, а не флаг в gfx_blit_cols_part_wx. Копирующий
* путь — самый горячий в движке персонажей (PoP зовёт его на каждого Char
* каждый кадр); ветка «а не операция ли?» в его хвосте тянула бы op-ядро в
* ЛЮБУЮ программу, использующую блит колонками (~+180 Б _CODE при нулевой
* пользе). Клип-математика здесь дублируется сознательно — правки клипа
* вносить в ОБА файла (близнец: common/gfx_blit_cols_part_wx.c).
*
* ПРОЗРАЧНОСТЬ — ГЛАВНОЕ ОТЛИЧИЕ ОТ КОПИИ. Аппаратная прозрачность (банк
* 0x58/0x5C: байт #FF не пишется) смотрит на РЕЗУЛЬТАТ операции, а не на
* источник. Отсюда:
* GFX_OP_AND — прозрачный #FF нейтрален сам по себе (#FF & bg = bg);
* банк любой.
* GFX_OP_OR — #FF | bg = #FF, запись подавляется банком 0x58/0x5C;
* работает с нашими обычными атласами КАК ЕСТЬ. Оговорка:
* подавится и «настоящий» результат, случайно равный #FF.
* GFX_OP_XOR — с прозрачностью #FF НЕ РАБОТАЕТ: #FF ^ bg = ~bg, то есть
* инверсия фона по всему прямоугольнику спрайта. Источник
* для XOR обязан хранить прозрачный пиксель как 0x00 —
* нейтральный элемент и для XOR, и для OR (так же, как в
* оригинале DOS-игр, где прозрачный индекс = 0).
*
* ЧТО ЧИТАЕТ ОПЕРАЦИЯ. Чтение страниц #50..#5F всегда отдаёт ОЗУ-копию
* экрана (memory/sprinter_vram_transparency), поэтому при банке 0x54/0x5C
* (не писать в копию) операция считает ЧИСТЫЙ ФОН, а не то, что уже
* нарисовано поверх. Нужен результат поверх нарисованного — рисовать
* банком GFX_BANK_NORMAL (0x50), у которого копия и видео-ОЗУ совпадают;
* платой будет потеря дарового heal из копии.
*
* Цена: три accel-burst'а на колонку вместо двух (+50% времени accel);
* подготовка/клип — как у копии.
*/
#include <stdint.h>
#include "../_bgi.h"
void gfx_blit_cols_part_wx_op(int x, int y, const void *img, uint8_t flip,
int skip, int rows, uint8_t skipw, uint8_t maxw,
uint8_t op)
{
const uint8_t *p = (const uint8_t *)img;
int full_w = p[0] | (p[1] << 8);
int full_h = p[2] | (p[3] << 8);
const uint8_t *px = p + 4;
int w = full_w, h;
int sstride = flip ? -full_h : full_h; /* шаг между колонками в ОЗУ */
int sy; /* пропуск строк сверху колонки */
int cx = 0; /* пропуск колонок слева */
const uint8_t *src;
uint8_t *dst;
if (full_w <= 0 || full_h <= 0)
return;
if (skipw) { /* обрезка СЛЕВА */
if ((int)skipw >= w) return; /* срезано целиком */
cx = (int)skipw;
x += (int)skipw;
w -= (int)skipw;
}
if (maxw && (int)maxw < w)
w = (int)maxw; /* обрезка справа */
if (skip < 0) skip = 0;
if (skip >= full_h)
return; /* обрезано целиком */
sy = skip;
h = full_h - skip;
if (rows > 0 && rows < h)
h = rows;
/* Клип по экрану — только если хоть один край вылезает. */
if (x < 0 || y < 0 || x + w > _bgi_maxx + 1 || y + h > _bgi_maxy + 1) {
if (x < 0) { cx -= x; w += x; x = 0; }
if (y < 0) { sy -= y; h += y; y = 0; }
if (x + w > _bgi_maxx + 1) w = _bgi_maxx + 1 - x;
if (y + h > _bgi_maxy + 1) h = _bgi_maxy + 1 - y;
if (w <= 0 || h <= 0) return;
}
/* Первая ВИДИМАЯ колонка (с учётом флипа) + первая видимая строка. */
src = flip ? (px + (uint16_t)(full_w - 1) * (uint16_t)full_h) : px;
if (cx)
src += cx * sstride;
src += sy;
_bgi_begin();
dst = (uint8_t *)(_gfx_addr_base + (uint16_t)x);
_bgi_blit_cols_op_raw(src, dst, (uint8_t)w, (uint8_t)h, sstride,
(uint8_t)y, op);
_bgi_end();
}