Files
Sprinter-SDCC/libbgi/bgi256/_bgi_blit_rows_op_raw.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

119 lines
6.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.
/*
* _bgi_blit_rows_op_raw — accel-блит СТРОКАМИ с ЛОГИЧЕСКОЙ ОПЕРАЦИЕЙ:
* приёмник = приёмник <op> источник, где <op> = AND / OR / XOR. Клон
* _bgi_blit_rows_raw (dst фиксирован, строку выбирает Port_Y; src шагает
* на sstride), но между чтением источника и записью вставлен ТРЕТИЙ
* burst — чтение приёмника в режиме операции.
*
* КАК ЭТО РАБОТАЕТ (docs/new/06-accel.md §6.2 + пример «XOR блока данных»
* §6.3; сверено с драйвером MAME src/mame/sinclair/sprinter.cpp,
* accel_control_r/update_accel_buffer). Опкоды `AND (HL)` #A6,
* `OR (HL)` #B6, `XOR (HL)` #AE — не режим направления, а переключатель
* внутреннего автомата с «буфер := память» на «буфер <op>= память»; сам
* burst запускает операндное чтение (HL) в ТЕКУЩЕМ режиме направления.
* Следующее армирование (LD L,L / LD A,A / LD B,B) возвращает автомат к
* присваиванию.
*
* ЗДЕСЬ ВСЁ ГОРИЗОНТАЛЬНО — и источник (строка спрайта в ОЗУ), и приёмник
* (строка экрана), поэтому три burst'а идут ОДНОЙ цепочкой, ровно как в
* примере документации, без промежуточных STOP/OUT:
* LD L,L ; гориз. режим
* LD A,(DE) ; read-burst : w байт src -> буфер accel
* <op> (HL) ; op-burst : буфер <op>= строка экрана
* LD (HL),A ; write-burst: буфер -> строка экрана
* LD B,B ; стоп
* Port_Y в горизонтальном режиме не шагает (шагает внутренний адрес,
* который каждый burst берёт заново из регистра) — сбрасывать между
* burst'ами нечего. Это дешевле колоночного близнеца
* (_bgi_blit_cols_op_raw), где приёмник вертикальный и Port_Y после
* op-burst'а приходится возвращать.
*
* ОДНА функция на три операции: опкод патчится SMC при входе (байт op),
* ровно как размер блока и страйд. op — это и есть опкод (GFX_OP_AND/
* OR/XOR в <gfx.h>); проверку значения делает вызывающий.
*
* ЧТО ЧИТАЕТ op-burst: страницы #50..#5F на ЧТЕНИЕ всегда отдают ОЗУ-копию
* экрана, а не видео-ОЗУ (memory/sprinter_vram_transparency). При банке
* 0x54/0x5C (бит 2 — «не писать в ОЗУ-копию») операция считает ЧИСТЫЙ ФОН,
* а не то, что нарисовано поверх; нужна операция с видимой картинкой —
* рисовать банком 0x50 (GFX_BANK_NORMAL).
*
* ПРОЗРАЧНОСТЬ: аппаратная (банк 0x58/0x5C — не писать #FF) смотрит на
* РЕЗУЛЬТАТ, поэтому даром достаётся AND (#FF нейтрален) и OR (#FF | bg =
* #FF, запись подавляется), но НЕ XOR (#FF ^ bg = ~bg). Источник для XOR
* обязан хранить прозрачный пиксель как 0x00. Подробно — шапка
* common/_gfx_blit_full_op.c.
*
* ВХОД (__sdcccall(1)): src→HL, dst→DE (фикс); стек: w 4(ix), h 5(ix),
* sstride 6/7(ix), y0 8(ix), op 9(ix). Смысл w/h/sstride/y0 — как у
* _bgi_blit_rows_raw. Callee-pop 6 Б.
*
* Raw: без клиппинга (вызывающий); W3 замаплен (_bgi_begin/end снаружи);
* банк выставлен вызывающим. src ВНЕ W3. ОДИН DI на весь блит (втрое
* больше трафика, чем у копии — большие блиты режет вызывающий).
* Клоббер: AF/BC/DE/HL; IX сохраняется.
*/
#include "../_bgi.h"
void _bgi_blit_rows_op_raw(const uint8_t *src, uint8_t *dst,
uint8_t w, uint8_t h,
int sstride, uint8_t y0, uint8_t op) __naked
{
(void)src; (void)dst; (void)w; (void)h; (void)sstride; (void)y0; (void)op;
__asm
push ix
ld ix, #0
add ix, sp
;; SMC: размер блока <- w (immediate у ld a,#n требование FSM)
ld a, 4 (ix)
ld (bro_len_imm), a
;; SMC: src-страйд константа на вызов (8-битная adc-цепочка)
ld a, 6 (ix)
ld (bro_slo_imm), a
ld a, 7 (ix)
ld (bro_shi_imm), a
;; SMC: опкод операции and/or/xor (hl) (#A6/#B6/#AE)
ld a, 9 (ix)
ld (bro_op_imm), a
ld b, 5 (ix) ; B = счётчик строк (0 => 256: djnz)
ld c, 8 (ix) ; C = y
;; Операция существует только в форме <op> (HL) приёмник обязан
;; жить в HL; источник переезжает в DE (читается LD A,(DE)).
ex de, hl ; HL = dst (экран, фикс), DE = src (ОЗУ)
di ; один DI на весь блит
ld d, d ; 0x52 режим размера блока (no-op для D)
ld a, #0
bro_len_imm = . - 1
ld b, b ; 0x40 стоп (B=счётчик не тронут)
bro_row:
ld a, c
out (#0x89), a ; Port_Y = y
inc c
ld l, l ; 0x6D армировать гориз. копию строки
ld a, (de) ; read-burst : src -> буфер акселератора
xor a, (hl) ; SMC: op-burst буфер <op>= строка экрана
bro_op_imm = . - 1
ld (hl), a ; write-burst: буфер -> строка экрана
ld b, b ; 0x40 стоп
;; src += sstride (dst НЕ двигается строку выбирает Port_Y)
ld a, e
add a, #0
bro_slo_imm = . - 1
ld e, a
ld a, d
adc a, #0
bro_shi_imm = . - 1
ld d, a
djnz bro_row
ei ; закрыть DI-окно блита
pop ix
;; callee-pop 6 байт (w,h,sstride:2,y0,op)
pop hl ; ret-адрес
pop af
pop af
pop af
jp (hl)
__endasm;
}