/* * _bgi_scroll_cols_raw — accel-скролл КОЛОНКАМИ video->video (mode 0x81). * УНИВЕРСАЛЕН: делает и горизонтальный, и ВЕРТИКАЛЬНЫЙ сдвиг за один * проход, БЕЗ буфера-посредника. Ядро gfx_scroll_v (вертикаль); может * служить и горизонтали (gfx_scroll_h держит свой rows-вариант — он * на ~5-7% быстрее, т.к. на строку нужен один OUT Port_Y, а не два). * * КАК РАБОТАЕТ. Колонка = один вертикальный accel-burst (LD A,A, 0x7F — * «копия блока для вертикальных линий»): read набирает h байт колонки src * с Port_Y=y0 (Port_Y авто-инкрементится внутри burst'а: y0→y0+h), write * выгружает их в колонку dst с Port_Y=y1 (y1→y1+h). Вертикальный сдвиг = * разница y0/y1; горизонтальный = разница адресов src/dst (dx). Оба сразу * = произвольный (dx,dy). dst/src двигаются на +1 (следующий x) за колонку. * * ПОЧЕМУ РАБОТАЕТ смена Port_Y между read и write (в отличие от наивного * rows-варианта, где это ломало accel). Ключ — STOP (LD B,B) ПЕРЕД * OUT Port_Y=y1: он разоружает accel (m_acc_dir=0), поэтому fetch операнда * OUT (immediate байт 0x89 из программной памяти) НЕ перезапускает * read-burst. Затем LD A,A снова взводит вертикальный режим для записи. * Оба OUT'а (y0 и y1) выполняются только пока accel РАЗОРУЖЁН. Порядок на * колонку: {OUT y0 · arm · read · STOP · OUT y1 · arm · write · STOP}. * (Подробно про квирк перезапуска — memory/accel_operand_fetch_retrigger.) * * DI/EI — БАНДАМИ по 16 колонок ВНУТРИ (как _gfx_recthfill256): держать DI * на весь скролл нельзя (порвёт CBL-звук). Размер блока (h) армируется в * КАЖДОЙ скобке (в окне EI CBL-ISR мог бы переармировать accel). Единая * тело-петля для полных банд и хвоста (счётчик колонок decrement'ится в * стек-слоте); y0 держим в C (LD A,C 4Т вместо LD A,#imm 7Т на колонку), * y1 — SMC-immediate (грузится только пока разоружены — безопасно). * * ВХОД (__sdcccall(1)): src->HL, dst->DE; стек: w 4(ix) — число КОЛОНОК * 1..255 (0 = тихий выход; ровно 256 режет вызывающий), h 5(ix) — высота * колонки = размер accel-блока 1..256 (0 = 256, конвенция accel), * y0 6(ix) — Port_Y чтения, y1 7(ix) — Port_Y записи. Callee-pop 4 байта. * * Raw: клиппинга нет (вызывающий), W3 замаплен (_bgi_begin/end снаружи), * банк 0x50 (запись бьёт VRAM+тень — скролл И heal цели). Перекрытия * src/dst нет by design — стороны в РАЗНЫХ видеостраницах. Колонок >255 и * колонки >256 строк режет вызывающий. Клоббер: AF/BC; HL/DE/IX * сохраняются (LD A,A/LD D,D — accel-опкоды-no-op'ы, регистры не трогают). */ #include "../_bgi.h" void _bgi_scroll_cols_raw(uint16_t src, uint16_t dst, uint8_t w, uint8_t h, uint8_t y0, uint8_t y1) __naked { (void)src; (void)dst; (void)w; (void)h; (void)y0; (void)y1; __asm push ix ld ix, #0 add ix, sp ;; HL = src, DE = dst (адреса-колонки; строку задаёт Port_Y) ld a, 5 (ix) ld (sc_col_len), a ; SMC: размер accel-блока <- h (0 = 256) ld a, 7 (ix) ld (sc_col_y1), a ; SMC: Port_Y записи <- y1 ld c, 6 (ix) ; C = Port_Y чтения (y0) — LD A,C дешевле imm sc_col_band: ld a, 4 (ix) ; колонок осталось or a, a jr Z, sc_col_done ; 0 => выход cp a, #16 jr NC, sc_col_full ; >=16 — полная банда ld b, a ; <16 — хвост: B = остаток jr sc_col_go sc_col_full: ld b, #16 ; 16 колонок в банде sc_col_go: ld a, 4 (ix) sub a, b ld 4 (ix), a ; колонок -= B di ld d, d ; 0x52 — режим размера блока (no-op для D) ld a, #0 sc_col_len = . - 1 ld b, b ; выключить — размер сохранён (B=счётчик цел) sc_col_next: ld a, c ; Port_Y чтения (y0) out (#0x89), a ; accel РАЗОРУЖЁН — операнд OUT безопасен ld a, a ; 0x7F — арм вертикальной копии ld a, (hl) ; read-burst: колонка src -> буфер (Port_Y y0..) ld b, b ; 0x40 — STOP (разоружить перед OUT y1) ld a, #0 sc_col_y1 = . - 1 out (#0x89), a ; Port_Y записи (y1) — accel разоружён ld a, a ; 0x7F — арм вертикальной копии ld (de), a ; write-burst: буфер -> колонка dst (Port_Y y1..) ld b, b ; 0x40 — STOP inc hl ; src += 1 (следующий x) inc de ; dst += 1 djnz sc_col_next ei ; окно прерываний между бандами jr sc_col_band sc_col_done: pop ix ;; callee-pop 4 байта (w, h, y0, y1) pop hl ; ret-адрес pop af pop af jp (hl) __endasm; }