a823e7ee9c
Акселератор умеет не только копировать блок, но и совмещать его с приёмником: опкод `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>
119 lines
6.9 KiB
C
119 lines
6.9 KiB
C
/*
|
||
* _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;
|
||
}
|