/* * _bgi_heal_rows_raw — специализация accel-копии для heal (src == dst == * адрес экрана): h строк по w байт «прочитать ОЗУ-копию → записать в * VRAM+ОЗУ» на одном месте. Выделен из _bgi_copy_rows_raw по профилю * examples/balls (2026-07-12): у heal ОБА страйда нулевые по * определению (строку выбирает Port_Y, CPU-адрес не меняется), поэтому * адресной арифметики в цикле нет вообще — строка 77Т + burst'ы против * 204Т у универсального leaf'а до оптимизации. * * ВХОД (__sdcccall(1)): scr→HL (base + x), y0→DE (E — стартовый * Port_Y); стек: w 4(ix), h 5(ix) — 1..256, 0 = 256 (конвенция * акселератора). Callee-pops 2 байта. * * Raw: клиппинга нет, W3 замаплен вызывающим; банк выбирает * вызывающий (gfx_heal форсит 0x50 — иначе heal бессмыслен). * Полосы > 256 байт режет вызывающий. * * Квирк CPU-байта write-триггера: фикс (preread + EX AF,AF') СНЯТ * (2026-07-13). Для heal он был избыточен ВСЕГДА: банк 0x50 без * прозрачности — даже если CPU-байт утёк бы в dst[0], burst тут же * перезаписывает его тем же значением из ОЗУ-копии. Плюс точная * dev-MAME показала, что ПЛМ вообще подавляет CPU-байт (см. * _bgi_blit_rows_raw). Закомментированные строки ниже — след старого * фикса на случай отличий реального железа. * * Клоббер: AF/BC/DE/HL; IX сохраняется. */ #include "../_bgi.h" void _bgi_heal_rows_raw(uint8_t *scr, int y0, uint8_t w, uint8_t h) __naked { __asm push ix ld ix, #0 add ix, sp ;; SMC: размер блока <- w (immediate — требование FSM) ld a, 4 (ix) ld (hr_len_imm), a ld c, e ; C = y (взять ДО затирания DE) ld b, 5 (ix) ; B = счётчик строк (0 => 256) ld d, h ld e, l ; DE = HL = scr (src == dst) ;; ОДИН DI на весь спрайт (arm + все строки): убирает per-row ;; EI-окно, где CBL-ISR мог пере-армировать акселератор своим ;; размером блока (латентная реентерабельность), и тесней loop. ;; Санкция пользователя 2026-07-12: 16×16 = 256 байт под одним DI ;; (как 1×256 fill) на аудио не влияет. ВНИМАНИЕ: h ограничен ;; малыми спрайтами — для больших блитов (putimage 256 строк) ;; вызывающий обязан резать на чанки ≤16 строк (иначе долгий DI). di ld d, d ; 0x52 — режим размера блока ld a, #0 hr_len_imm = . - 1 ld b, b ; 0x40 — стоп (B=счётчик не тронут) hr_row: ld a, c out (#0x89), a ; Port_Y = y inc c ;; ld a, (hl) ; предчитать ОЗУ[0] (accel выключен) ;; ex af, af' ld l, l ; 0x6D — армировать копию строки ld a, (hl) ; burst: ОЗУ-копия -> память акселератора ;; ex af, af' ; A = ОЗУ[0] — CPU-байт триггера корректен ld (de), a ; burst: память акселератора -> VRAM+ОЗУ ld b, b ; 0x40 — стоп djnz hr_row ; адреса не меняются — только Port_Y ei ; закрыть DI-окно спрайта pop ix pop hl ; ret-адрес; callee-pop 2 байта (w,h) inc sp inc sp jp (hl) __endasm; }