/*
 * _gfx_rectvfill256 — заливка прямоугольника (x,y,w,h) ВЕРТИКАЛЬНЫМИ
 * колонками (w выстрелов высотой h), mode 0x81.  Chunked-схема: скобки
 * DI/EI по ≤16 колонок, армирование размера один раз на скобку, на
 * колонку — re-OUT Port_Y + LD E,E + выстрел (семантика мульти-триггера:
 * memory/accel_multitrigger_fill, проверено в MAME tests/accfill).
 *
 * Счётчик чанков — precompute (v3): один раз на входе E = w>>4 (полных
 * чанков, ≤20 при w<=320) и w&15 — хвост (SMC в ld b,#n хвостовой
 * скобки).  Заголовок чанка = dec e/jr nz (~46Т на чанк ≈ 2.9Т/линию
 * против ~173Т/чанк у 16-бит арифметики через IX-слоты).  Хвост —
 * отдельная скобка со СВОИМ армированием (второй SMC-слот размера):
 * армирование обязано быть в каждой скобке — в окне EI CBL-ISR
 * армирует акселератор своим размером блока.
 *
 * ---- БЮДЖЕТ (полный разбор: docs/accel-fill-budget.md) -------------
 *  подготовка ~340Т один раз на прямоугольник (пролог 102 + SMC 45 +
 *  precompute ~110 + эпилог 54); чанк 16 колонок — 46Т; колонка — 53Т.
 *  T(n) ≈ 340 + 46*(n/16) + 53*n;  ~107 байт кода.  Break-even против
 *  простейшего per-line di/ei (djnz, 72Т/линию, ~63 байта): n ≈ 9 —
 *  при n <= 8 линий простейший быстрее (максимум +140Т на n=1).
 *
 * Прямой вызов оправдан, когда форма известна заранее (высокие/узкие
 * прямоугольники, w <= h) — экономит ~160-245Т проверки диспетчера
 * _gfx_rectfill256 (см. там же расчёт break-even).
 *
 * ---- КОНТРАКТ (ответственность вызывающего) ------------------------
 *  - 1 <= h <= 256 (h == 256 кодируется младшим байтом 0 — конвенция
 *    акселератора; h == 0 даст 256!).  0 <= w <= 320 (w == 0 — тихий
 *    выход; старший байт w интерпретируется как «+16 чанков», т.е.
 *    w > 511 — UB).  Клиппинга НЕТ, координаты обязаны быть валидны.
 *  - W3 замаплен (_bgi_begin у вызывающего).
 *
 * __sdcccall(1): x→HL, y→DE(E=y); w→4/5(ix), h→6/7(ix), color→8(ix);
 * __naked → callee-pops 5 байт.  Инварианты в регистрах: HL=бегущий addr,
 * C=цвет, D=y0 (константа; vertical Fill двигает Port_Y — re-OUT
 * обязателен), E=счётчик чанков, B=djnz внутри скобки.
 */

#include "../_bgi.h"

void _gfx_rectvfill256(int x, int y, int w, int h, uint8_t color) __naked
{
    __asm
        .globl __gfx_addr_base
        push  ix
        ld    ix, #0
        add   ix, sp
        ld    bc, (__gfx_addr_base)
        add   hl, bc             ; HL = base + x (бегущий)
        ld    d, e               ; D = y0 (константа)
        ld    a, 8 (ix)
        ld    c, a               ; C = color
        ld    a, 6 (ix)
        ld    (rv_len), a        ; SMC: размер блока <- h (0 = 256)
        ld    (rv_len2), a       ; ...и в хвостовую скобку

        ;; --- precompute (один раз): E = w>>4, хвост w&15 -> SMC ---
        ld    a, 4 (ix)
        and   #0x0F
        ld    (rv_tail), a       ; SMC: хвост <- w & 15
        ld    a, 4 (ix)
        rrca
        rrca
        rrca
        rrca
        and   #0x0F
        ld    e, a               ; E = (w низкий) >> 4
        ld    a, 5 (ix)
        or    a, a
        jr    Z, rv_go           ; w < 256
        ld    a, e
        add   a, #16             ; старший байт: w<=320 => ровно +16 чанков
        ld    e, a
    rv_go:
        ld    a, e
        or    a, a
        jr    Z, rv_tail_go      ; полных чанков нет

        ;; --- E полных чанков по 16 колонок ---
        ;; (армирование НЕ выносить из цикла: в окне EI CBL-ISR
        ;;  армирует акселератор своим размером блока)
    rv_chunk:
        ld    b, #16             ; колонок в чанке
        di
        ld    d, d               ; 0x52 — режим размера блока (no-op для D=y0)
        ld    a, #0
    rv_len = . - 1
        ld    b, b               ; выключить — размер сохранён
    rv_col:
        ld    a, d
        out   (#0x89), a         ; Port_Y = y0 (vertical Fill его сдвинул)
        ld    e, e               ; 0x5B — вертикальный Fill (no-op для E-счётчика)
        ld    a, c
        ld    (hl), a            ; выстрел: колонка h строк
        ld    b, b               ; выключить (перед djnz — fetch смещения!)
        inc   hl                 ; следующая колонка
        djnz  rv_col
        ei                       ; окно для прерываний между чанками
        dec   e
        jr    NZ, rv_chunk

        ;; --- хвост w&15 колонок: отдельная скобка со СВОИМ армированием ---
    rv_tail_go:
        ld    a, #0
    rv_tail = . - 1
        or    a, a
        jr    Z, rv_done         ; хвоста нет
        ld    b, a
        di
        ld    d, d
        ld    a, #0
    rv_len2 = . - 1
        ld    b, b
    rv_tcol:
        ld    a, d
        out   (#0x89), a
        ld    e, e
        ld    a, c
        ld    (hl), a
        ld    b, b
        inc   hl
        djnz  rv_tcol
        ei
    rv_done:
        pop   ix
        pop   hl
        pop   af
        pop   af
        inc   sp
        jp    (hl)
    __endasm;
}

#if 0
/* ================================================================
 * Предыдущий вариант (v2, 2026-07-10, до precompute): решение
 * «16 или хвост» и вычитание w -= n на КАЖДЫЙ чанк через 16-бит
 * арифметику в IX-слотах (~141-189Т на чанк ≈ 11Т/линию).
 * Оставлен закомментированным для отката/сравнения.
 * ================================================================ */
void _gfx_rectvfill256(int x, int y, int w, int h, uint8_t color) __naked
{
    __asm
        .globl __gfx_addr_base
        push  ix
        ld    ix, #0
        add   ix, sp
        ld    bc, (__gfx_addr_base)
        add   hl, bc             ; HL = base + x (бегущий)
        ld    d, e               ; D = y0 (константа)
        ld    a, 8 (ix)
        ld    c, a               ; C = color
        ld    a, 6 (ix)
        ld    (rv_len), a        ; SMC: размер блока <- h (0 = 256)
    rv_chunk:
        ld    a, 5 (ix)
        or    a, a
        jr    NZ, rv_full        ; w >= 256 -> чанк 16
        ld    a, 4 (ix)
        or    a, a
        jr    Z, rv_done         ; w == 0 -> готово
        cp    a, #16
        jr    NC, rv_full
        ld    b, a               ; хвост: n = w
        jr    rv_go
    rv_full:
        ld    b, #16             ; полный чанк
    rv_go:
        ld    a, 4 (ix)          ; w -= n
        sub   a, b
        ld    4 (ix), a
        ld    a, 5 (ix)
        sbc   a, #0
        ld    5 (ix), a
        ;; --- скобка: n колонок одним армированием размера ---
        ;; (армирование НЕ выносить из цикла: в окне EI CBL-ISR
        ;;  армирует акселератор своим размером блока)
        di
        ld    d, d               ; 0x52 — режим размера блока (no-op для D=y0)
        ld    a, #0
    rv_len = . - 1
        ld    b, b               ; выключить — размер сохранён
    rv_col:
        ld    a, d
        out   (#0x89), a         ; Port_Y = y0 (vertical Fill его сдвинул)
        ld    e, e               ; 0x5B — вертикальный Fill (no-op для E)
        ld    a, c
        ld    (hl), a            ; выстрел: колонка h строк
        ld    b, b               ; выключить (перед djnz — fetch смещения!)
        inc   hl                 ; следующая колонка
        djnz  rv_col
        ei                       ; окно для прерываний между чанками
        jr    rv_chunk
    rv_done:
        pop   ix
        pop   hl
        pop   af
        pop   af
        inc   sp
        jp    (hl)
    __endasm;
}
#endif
