/* * _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-опкоды. * * КВИРК ТРИГГЕРА ЗАПИСИ — ФИКС СНЯТ 2026-07-13. История: в стоковой * MAME 0.283 LD (DE),A вёл себя как реальная CPU-инструкция — её * собственный цикл записи клал байт A (последний байт строки после * burst-чтения) в dst[0], и при банке 0x58/0x5C со src[0] == 0xFF * оставалась вертикальная полоса (найдено tests/sprites). * Нейтрализация была: preread src[0] + EX AF,AF' перед триггером. * Точная dev-MAME (сборка разработчиков Sprinter) эмулирует ПЛМ, * подавляющую CPU-байт при активном burst'е — утечки нет и без фикса * (проверено tests/blitw: col0 @200,150 = GREEN, по байтам VRAM). * Закомментированные строки ниже — след фикса; восстановить, если * реальное железо поведёт себя как MAME 0.283. НА ЖЕЛЕЗЕ ПЕРЕПРОВЕРИТЬ. * * Клоббер: 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; }