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

103 lines
5.1 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_not_raw — accel-блит СТРОКАМИ операцией NOT: приёмник =
* ~источник (приёмник в операции НЕ участвует). Пара к
* _bgi_blit_rows_op_raw, вынесена отдельно, потому что цепочка другая.
*
* ПОЧЕМУ НЕ `CPL`. Буфер акселератора меняется только на ЧТЕНИИ и только
* опкодами AND/OR/XOR (HL) (драйвер MAME, update_accel_buffer); `CPL`
* (#2F) автомат не распознаёт вовсе — инвертируется регистр CPU, а не
* буфер, и запись выгрузит неизменённый блок. Зато ~src = src XOR #FF,
* поэтому NOT собирается из имеющегося: XOR-burst по константному блоку
* единиц (common/_bgi_ones256.c).
*
* ЦЕПОЧКА НА СТРОКУ (три burst'а, все горизонтальные):
* OUT Port_Y = y
* LD L,L · LD A,(DE) ; буфер := src
* LD B,B ; СТОП — разоружить перед сменой HL
* LD HL,#ones · LD L,L · XOR (HL) ; буфер ^= #FF... = ~src
* LD B,B ; СТОП
* LD HL,#dst · LD L,L · LD (HL),A ; запись строки
* LD B,B
* СТОП перед каждой загрузкой HL обязателен: под армированным accel fetch
* операнда `LD HL,nn` перезапустил бы burst (memory/
* accel_operand_fetch_retrigger). Буфер и размер блока переживают LD B,B
* (docs/new/06-accel.md §6.3) — на этом всё и держится.
* Port_Y в горизонтальном режиме не шагает, сбрасывать его между
* burst'ами не нужно; dst фиксирован (строку выбирает Port_Y) и потому
* кладётся SMC-immediate'ом один раз на вызов.
*
* Прозрачности у NOT нет по определению (инвертируется КАЖДЫЙ байт, в том
* числе #FF -> 0x00) — это ровно семантика NOT_PUT из Turbo C BGI.
*
* ВХОД (__sdcccall(1)): src→HL, dst→DE (фикс); стек: w 4(ix), h 5(ix),
* sstride 6/7(ix), y0 8(ix). Callee-pop 5 Б. Raw: без клипа, W3 замаплен
* снаружи, банк ставит вызывающий, src ВНЕ W3. Клоббер: AF/BC/DE/HL.
*/
#include "../_bgi.h"
void _bgi_blit_rows_not_raw(const uint8_t *src, uint8_t *dst,
uint8_t w, uint8_t h,
int sstride, uint8_t y0) __naked
{
(void)src; (void)dst; (void)w; (void)h; (void)sstride; (void)y0;
__asm
push ix
ld ix, #0
add ix, sp
;; SMC: размер блока <- w
ld a, 4 (ix)
ld (bn_len_imm), a
;; SMC: src-страйд между строками
ld a, 6 (ix)
ld (bn_slo_imm), a
ld a, 7 (ix)
ld (bn_shi_imm), a
;; SMC: адрес приёмника фиксирован на весь блит (строку задаёт
;; Port_Y), а HL по очереди занят то блоком единиц, то им
ld (bn_dst_imm), de
ld b, 5 (ix) ; B = счётчик строк (0 => 256: djnz)
ld c, 8 (ix) ; C = y
ex de, hl ; DE = src (читается LD A,(DE))
di ; один DI на весь блит
ld d, d ; 0x52 режим размера блока
ld a, #0
bn_len_imm = . - 1
ld b, b ; 0x40 стоп (B=счётчик не тронут)
bn_row:
ld a, c
out (#0x89), a ; Port_Y = y
inc c
ld l, l ; 0x6D гориз. режим
ld a, (de) ; read-burst : src -> буфер
ld b, b ; 0x40 стоп (разоружить перед LD HL)
ld hl, #__bgi_ones256
ld l, l ; 0x6D гориз. режим
xor a, (hl) ; op-burst : буфер ^= #FF... = ~src
ld b, b ; 0x40 стоп
ld hl, #0
bn_dst_imm = . - 2
ld l, l ; 0x6D гориз. режим
ld (hl), a ; write-burst: буфер -> строка экрана
ld b, b ; 0x40 стоп
;; src += sstride
ld a, e
add a, #0
bn_slo_imm = . - 1
ld e, a
ld a, d
adc a, #0
bn_shi_imm = . - 1
ld d, a
djnz bn_row
ei
pop ix
;; callee-pop 5 байт (w,h,sstride:2,y0)
pop hl ; ret-адрес
pop af
pop af
inc sp
jp (hl)
__endasm;
}