Files
Sprinter-SDCC/libbgi/common/gfx_blit_cols_part_wx_op.c
T
snark13 860468f3c5 libbgi: NOT_PUT тоже через акселератор + изыскания по виду Тени
NOT.  У акселератора нет режима «инвертировать буфер»: буфер меняется
только на ЧТЕНИИ и только опкодами AND/OR/XOR (HL) (драйвер MAME,
update_accel_buffer), а `CPL` автомат вообще не распознаёт — инвертируется
регистр CPU, не буфер.  Зато ~src = src XOR #FF, поэтому NOT собирается из
уже имеющегося: буфер := src, XOR-burst по константному блоку единиц
(common/_bgi_ones256.c), запись.  Ядра _bgi_blit_{rows,cols}_not_raw.c;
между burst'ами меняется HL, поэтому каждая смена — под СТОПом (иначе fetch
операнда перезапустит burst).  В колоночном варианте второй OUT Port_Y не
нужен: op-burst идёт по блоку единиц горизонтально, а Port_Y шагает только
вертикальный.

Наружу — тем же op-параметром (GFX_OP_NOT), диспетчер один раз на вызов, в
цикл по полосам/колонкам не заходит.  putimage лишился попиксельного пути
ЦЕЛИКОМ: все пять операций BGI идут через акселератор и клиппируются
одинаково.  tests/accop дополнен T9/T10 (NOT строками и колонками) —
10/10 PASS в MAME; tests/bgi_img P1/P2 по-прежнему PASS.

tests/convbench (новый) — замер побайтной конвертации атласа
«прозрачный #FF -> 0x00» (источник для XOR-блита): 145.3 такта/байт, то
есть 2.5× от 59 номинальных T-states цикла.  0.11 с на страницу 16 КБ,
3.2 с на все 28 страниц, 1.3 с по фактическому объёму данных (186 КБ).

applications/PoP/docs/shadow_render.md — вид Тени (два блиттера OR+XOR)
ОТЛОЖЕН по решению пользователя: пока рисуем обычной копией атласами Кида.
В документе собрано, почему в лоб не выходит (XOR несовместим с
прозрачностью #FF; операция читает ОЗУ-копию, поэтому два прохода
оригинала вырождаются в один XOR — нужен однопроходный композит
s | bg ^ s(сдвиг)), замер стоимости источника и четыре варианта.  Ключ к
выбору — список кадров, которыми тень реально пользуется; снимать по факту,
когда пойдут уровни 5/6/12.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 15:41:06 +03:00

102 lines
5.9 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);
/* NOT — инверсия ИСТОЧНИКА, приёмник в ней не участвует: своя цепочка
* (XOR по блоку единиц) и своё ядро. Ветка одна на вызов. */
if (op == GFX_OP_NOT)
_bgi_blit_cols_not_raw(src, dst, (uint8_t)w, (uint8_t)h, sstride,
(uint8_t)y);
else
_bgi_blit_cols_op_raw(src, dst, (uint8_t)w, (uint8_t)h, sstride,
(uint8_t)y, op);
_bgi_end();
}