Files
Sprinter-SDCC/libbgi/bgi256/_bgi_copy_rows_raw.c
T
snark13 72ce66275e libbgi: спрайтовый движок v2 + accel-блит/heal leaf'ы + noclip-путь
Спрайтовая графика поверх accel block-copy (docs/sprite-api-design.md):

- Ядро блиттинга: leaf'ы _bgi_blit_rows_raw (dst фикс, только src-страйд) /
  _bgi_copy_rows_raw (getimage) / _bgi_heal_rows_raw (src==dst). DI один на
  спрайт (санкция: малый спрайт под одним DI аудио не рвёт); src[0]-фикс
  снят (точная MAME подавляет CPU-байт триггера записи — на железе
  перепроверить; для heal был избыточен и снят безусловно).
- Общие bracket-free ядра _gfx_blit_full/_gfx_heal_full (полная ширина:
  клип по экрану + split >256 для putimage) + лин _gfx_blit_sprite/
  _gfx_heal_sprite (кадр ≤64, без split, 8-бит w/h) + noclip-варианты
  (клип-кода нет → полный codegen-win).  Имя *_full (не *_clip) — «clip»
  двусмысленно (sprite-ядра тоже клипуют; различитель — ширина/split).
- Движок retained-модели <sprite.h>: sprite_init/update/flip + inline
  move/frame/show/hide/touch; drawn[2] per-page внутри структуры; кадр —
  двухпроходно heal ВСЕ -> блит ВСЕ под одной W3-скобкой/банком на проход.
- Флаг gfx_sprite_clip(on/off): приложение, само следящее за границами,
  отключает клип (~+19% на анимации; диспетч пока через if — funcptr далее).
- putsprite/movesprite/gfx_blit/putimage(COPY)/getimage переведены на ядро.

Тесты: examples/balls (движок, дабл-буфер, boundary-тест клипа),
tests/sprites (RAM PASS, клип 4 края, атлас), tests/blitw (trig-leak),
tests/spriteclip (hardware-probe: железо НЕ режет за краем -> клип нужен),
tests/blitperf, tests/gfxbanks. size-baseline обновлён (53 программы).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 21:51:52 +03:00

131 lines
7.4 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_copy_rows_raw — копирование h строк по w байт через block-copy
* акселератора (LD L,L), mode 0x81. Ядро блиттинга: putimage(COPY),
* getimage, gfx_blit*, gfx_heal — все операции блиттинга это один этот
* leaf с разными src/dst (docs/sprite-api-design.md §2, §4.1):
*
* блит src=буфер, sstride=w картинки; dst=экран, dstride=0
* grab src=экран, sstride=0; dst=буфер, dstride=w
* Для heal (src == dst == экран, оба страйда 0) — специализированный
* _bgi_heal_rows_raw (без адресной арифметики в цикле вообще).
* Страйды здесь — константы на весь вызов и патчатся SMC при входе
* (8-битные цепочки add/adc, 73Т/строку вместо 140Т у прежнего
* варианта с перечитыванием через IX; профиль examples/balls).
*
* ВХОД (__sdcccall(1)): src→HL, dst→DE; стек: w 4(ix), h 5(ix),
* dstride 6/7(ix), sstride 8/9(ix), y0 10(ix).
* w — байт в строке за burst, 1..256 (0 = 256 — конвенция акселератора);
* h — строк, 1..256 (0 = 256);
* dstride/sstride — шаг адреса между строками (0 = адрес на месте,
* строку выбирает Port_Y — сторона, живущая в видеоокне W3);
* y0 — стартовый Port_Y, инкремент на строку.
*
* Raw: клиппинга НЕТ (весь клип делает вызывающий), W3 уже замаплен
* (_bgi_begin/_bgi_end снаружи). Буферная сторона обязана быть ВНЕ W3
* (< 0xC000) — на время операции W3 занят видеобанком. Полосы > 256
* байт режет вызывающий (см. gfx_blit_part).
*
* Схема (референс Draw_Restangle_Data, accelerator_doc.txt): размер
* блока армируется ОДИН раз (SMC у LD A,#n — операнд ОБЯЗАН быть
* immediate, memory/sprinter_accelerator; размер переживает LD B,B —
* tests/accfill), далее на строку: OUT Port_Y, DI, LD L,L (армировать
* копию), LD A,(HL) (burst-чтение src в память акселератора),
* LD (DE),A (burst-запись из памяти акселератора в dst), LD B,B, EI.
* Между армированием и триггерами — только регистровые M1-опкоды.
*
* КВИРК ТРИГГЕРА ЗАПИСИ (найден tests/sprites — клипнутые спрайты):
* LD (DE),A — реальная CPU-инструкция: её собственный цикл записи
* кладёт байт A в dst[0], и лишь затем burst FSM пишет src[0..w-1]
* поверх. После burst-чтения в A остаётся ПОСЛЕДНИЙ байт строки —
* при банке 0x50 утечка невидима (src[0] тут же перезапишет), но при
* 0x58/0x5C, если src[0] == 0xFF, перезапись скипается и в dst[0]
* остаётся вертикальная полоса цвета последних байтов строк.
* Нейтрализация: src[0] предчитывается ДО армирования и подставляется
* в A через EX AF,AF' (регистровый M1-опкод — под armed разрешён)
* прямо перед триггером — CPU-байт становится src[0], что корректно
* при ЛЮБОМ банке и любой семантике первого цикла записи (в MAME
* 0.283 подтверждено tests/blitw+sprites; на железе перепроверить).
*
* Клоббер: AF/AF'/BC/DE/HL; IX сохраняется (callee-saved).
*/
#include "../_bgi.h"
void _bgi_copy_rows_raw(const uint8_t *src, uint8_t *dst,
uint8_t w, uint8_t h,
int dstride, int sstride, uint8_t y0) __naked
{
__asm
push ix
ld ix, #0
add ix, sp
;; SMC: размер блока <- w (immediate у ld a,#n требование FSM)
ld a, 4 (ix)
ld (cr_len_imm), a
;; SMC: страйды константы на весь вызов; патчим immediate
;; 8-битных цепочек сложения вместо перечитывания из стека
;; через IX на каждой строке (профиль examples/balls
;; 2026-07-12: было 140Т/строку, стало 73Т и BC не нужен,
;; push/pop и ex de,hl ушли).
ld a, 8 (ix)
ld (cr_slo_imm), a
ld a, 9 (ix)
ld (cr_shi_imm), a
ld a, 6 (ix)
ld (cr_dlo_imm), a
ld a, 7 (ix)
ld (cr_dhi_imm), a
ld b, 5 (ix) ; B = счётчик строк (0 => 256: djnz)
ld c, 10 (ix) ; C = y
;; ОДИН DI на весь блит (arm + все строки): убирает per-row
;; EI-окно (CBL-ISR не пере-армирует акселератор посреди спрайта)
;; и тесней loop. Санкция пользователя 2026-07-12: малый спрайт
;; под одним DI аудио не рвёт. ВНИМАНИЕ: для больших h (putimage
;; 256 строк) вызывающий обязан резать на чанки 16 строк.
di
ld d, d ; 0x52 режим размера блока
ld a, #0
cr_len_imm = . - 1
ld b, b ; 0x40 стоп (B=счётчик не тронут)
cr_row:
ld a, c
out (#0x89), a ; Port_Y = y
inc c
;; ld a, (hl) ; A = src[0] (accel выключен)
;; ex af, af' ; спрятать src[0] в A'
ld l, l ; 0x6D армировать копию строки
ld a, (hl) ; burst: src -> память акселератора
;; ex af, af' ; A = src[0] CPU-байт триггера записи
ld (de), a ; burst: память акселератора -> dst
ld b, b ; 0x40 стоп (no-op по регистрам)
;; src += sstride (SMC-immediates, 16-бит через adc)
ld a, l
add a, #0
cr_slo_imm = . - 1
ld l, a
ld a, h
adc a, #0
cr_shi_imm = . - 1
ld h, a
;; dst += dstride
ld a, e
add a, #0
cr_dlo_imm = . - 1
ld e, a
ld a, d
adc a, #0
cr_dhi_imm = . - 1
ld d, a
djnz cr_row
ei ; закрыть DI-окно блита
pop ix
;; callee-pop 7 байт стековых аргументов
pop hl ; ret-адрес
pop af
pop af
pop af
inc sp
jp (hl)
__endasm;
}