/*
 * _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;
}
