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

136 lines
8.6 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_cols_op_raw — accel-блит КОЛОНКАМИ с ЛОГИЧЕСКОЙ ОПЕРАЦИЕЙ:
* приёмник = приёмник <op> источник, где <op> = AND / OR / XOR. Клон
* _bgi_blit_cols_raw (тот же column-major источник, тот же бесплатный
* горизонтальный флип), но между чтением источника и записью вставлен
* ТРЕТИЙ burst — чтение приёмника в режиме операции.
*
* КАК ЭТО РАБОТАЕТ (docs/new/06-accel.md §6.2, подтверждено драйвером MAME
* src/mame/sinclair/sprinter.cpp — accel_control_r/update_accel_buffer).
* Опкоды `AND (HL)` #A6, `OR (HL)` #B6, `XOR (HL)` #AE НЕ являются
* отдельным режимом акселератора: их M1-выборка переводит внутренний
* автомат из «буфер := память» в «буфер <op>= память», а сам burst
* запускается операндным чтением (HL) — в ТЕКУЩЕМ режиме направления
* (гориз. LD L,L / верт. LD A,A). То есть операция ОРТОГОНАЛЬНА
* направлению: для column-major спрайтов работает так же, как копия.
* Следующее армирование (LD L,L / LD A,A / LD B,B) возвращает автомат в
* «буфер := память».
*
* ОДНА функция на три операции: опкод операции патчится SMC при входе
* (байт op), ровно как размер блока и страйд. op — это и есть опкод
* (GFX_OP_AND/OR/XOR в <gfx.h>); проверку значения делает вызывающий.
*
* ПОСЛЕДОВАТЕЛЬНОСТЬ НА КОЛОНКУ (три burst'а вместо двух):
* OUT Port_Y = y0 ; accel разоружён — операнд OUT безопасен
* LD L,L / LD A,(DE) ; гориз. чтение: h смежных байт src -> буфер
* LD A,A / <op> (HL) ; верт. чтение: буфер <op>= видео-колонка
* LD B,B ; СТОП — разоружить перед вторым OUT
* OUT Port_Y = y0 ; вернуть Y: верт. burst сдвинул его на +h
* LD A,A / LD (HL),A ; верт. запись: буфер -> видео-колонка
* LD B,B
* Второй OUT обязателен: вертикальный burst ШАГАЕТ Port_Y (§6.1), и после
* op-чтения он стоит на y0+h. СТОП перед OUT обязателен по квирку
* memory/accel_operand_fetch_retrigger (fetch операнда под армированным
* accel перезапускает burst). Буфер и размер блока переживают LD B,B
* (§6.3) — это и позволяет разнести чтение и запись.
*
* ЧТО ЧИТАЕТ op-burst: страницы #50..#5F на ЧТЕНИЕ всегда отдают ОЗУ-копию
* экрана, а не видео-ОЗУ (memory/sprinter_vram_transparency). Значит при
* банке 0x54/0x5C (бит 2 — «не писать в ОЗУ-копию») операция считает
* ЧИСТЫЙ ФОН, а не то, что уже нарисовано поверх него. Нужна операция с
* реально видимым содержимым — вызывающий обязан рисовать банком 0x50
* (GFX_BANK_NORMAL), тогда копия и видео-ОЗУ совпадают.
*
* ПРОЗРАЧНОСТЬ. Аппаратная прозрачность (банки 0x58/0x5C — не писать байт
* #FF) действует на РЕЗУЛЬТАТ, а не на источник, поэтому даром достаётся
* только двум операциям:
* AND — #FF нейтрален сам по себе (#FF & bg = bg);
* OR — #FF | bg = #FF, и запись такого байта подавляется банком 0x58/0x5C;
* XOR — НЕ работает: #FF ^ bg = ~bg, инверсия фона на прозрачных пикселях.
* Для XOR источник обязан иметь прозрачный индекс 0x00 (нейтральный к XOR
* и к OR) и рисоваться банком без подавления. Подробно — шапка
* common/gfx_blit_cols_part_wx_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_cols_raw. Callee-pop 6 Б.
*
* Raw: без клиппинга (вызывающий); W3 замаплен (_bgi_begin/end снаружи);
* банк выставлен вызывающим. src ВНЕ W3. ОДИН DI на весь блит (втрое
* больше трафика, чем у копии — для больших спрайтов резать вызывающему).
* Клоббер: AF/BC/DE/HL; IX сохраняется.
*/
#include "../_bgi.h"
void _bgi_blit_cols_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: размер accel-блока <- h (высота колонки)
ld a, 5 (ix)
ld (bo_len_imm), a
;; SMC: src-страйд между колонками (8-бит adc-цепочка, ± на вызов)
ld a, 6 (ix)
ld (bo_slo_imm), a
ld a, 7 (ix)
ld (bo_shi_imm), a
;; SMC: опкод операции and/or/xor (hl) (#A6/#B6/#AE)
ld a, 9 (ix)
ld (bo_op_imm), a
ld b, 4 (ix) ; B = число колонок (0 => 256: djnz)
ld c, 8 (ix) ; C = y0 (Port_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
bo_len_imm = . - 1
ld b, b ; 0x40 стоп (B=счётчик не тронут)
bo_col:
ld a, c
out (#0x89), a ; Port_Y = y0 (верх колонки)
;; 1) ЧТЕНИЕ ИСТОЧНИКА ГОРИЗОНТАЛЬНОЙ копией (LD L,L): колонка
;; спрайта лежит в ОЗУ СМЕЖНО, шагает адрес, не Port_Y.
ld l, l ; 0x6D гориз. режим
ld a, (de) ; read-burst: h байт src -> буфер accel
;; 2) ОПЕРАЦИЯ с приёмником ВЕРТИКАЛЬНЫМ чтением (LD A,A):
;; буфер <op>= видео-колонка; Port_Y уезжает на y0+h.
ld a, a ; 0x7F верт. режим
xor a, (hl) ; SMC: and/or/xor (hl) op-burst
bo_op_imm = . - 1
ld b, b ; 0x40 стоп (разоружить перед OUT)
;; 3) ЗАПИСЬ снова с верха колонки.
ld a, c
out (#0x89), a ; Port_Y = y0 (accel разоружён)
ld a, a ; 0x7F верт. режим
ld (hl), a ; write-burst: буфер -> видео-колонка
ld b, b ; 0x40 стоп
;; src += sstride (следующая колонка источника)
ld a, e
add a, #0
bo_slo_imm = . - 1
ld e, a
ld a, d
adc a, #0
bo_shi_imm = . - 1
ld d, a
inc hl ; dst += 1 (следующий x)
djnz bo_col
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;
}