/* * _bgi_blit_rows_op_raw — accel-блит СТРОКАМИ с ЛОГИЧЕСКОЙ ОПЕРАЦИЕЙ: * приёмник = приёмник источник, где = 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 — не режим направления, а переключатель * внутреннего автомата с «буфер := память» на «буфер = память»; сам * 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 * (HL) ; op-burst : буфер = строка экрана * 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 в ); проверку значения делает вызывающий. * * ЧТО ЧИТАЕТ 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 ;; Операция существует только в форме (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 — буфер = строка экрана 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; }