/*
 * _gfx_recthfill256 — заливка прямоугольника (x,y,w,h) ГОРИЗОНТАЛЬНЫМИ
 * строками (h выстрелов шириной w), mode 0x81.  Chunked-схема: скобки
 * DI/EI по ≤16 строк, армирование размера один раз на скобку, на строку —
 * OUT Port_Y + LD C,C + выстрел (семантика мульти-триггера:
 * memory/accel_multitrigger_fill, проверено в MAME tests/accfill).
 *
 * Счётчик чанков — precompute (v3): один раз на входе E = h>>4 (полных
 * чанков, ≤16 при h<=256) и h&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Т; строка — 51Т.
 *  T(n) ≈ 340 + 46*(n/16) + 51*n;  ~107 байт кода.  Break-even против
 *  простейшего per-line di/ei (djnz, 70Т/линию, ~53 байта): n ≈ 9 —
 *  при n <= 8 линий простейший быстрее (максимум +140Т на n=1).
 *
 * Прямой вызов оправдан, когда форма известна заранее (широкие/низкие
 * прямоугольники, w > h) — экономит ~160-245Т проверки диспетчера
 * _gfx_rectfill256 (см. там же расчёт break-even).
 *
 * ---- КОНТРАКТ (ответственность вызывающего) ------------------------
 *  - 1 <= w <= 256 (w == 256 кодируется младшим байтом 0 — конвенция
 *    акселератора; w == 0 даст 256!).  Для w > 256 использовать
 *    _gfx_rectvfill256 (или диспетчер).  0 <= h <= 256 (h == 0 — тихий
 *    выход; старший байт h интерпретируется как «+16 чанков», т.е.
 *    h > 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
 * (константа — строка задаётся Port_Y), C=цвет, D=бегущий y,
 * E=счётчик чанков, B=djnz внутри скобки.
 */

#include "../_bgi.h"

void _gfx_recthfill256(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 = бегущий y
        ld    a, 8 (ix)
        ld    c, a               ; C = color
        ld    a, 4 (ix)
        ld    (rh_len), a        ; SMC: размер блока <- w (0 = 256)
        ld    (rh_len2), a       ; ...и в хвостовую скобку

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

        ;; --- E полных чанков по 16 строк ---
        ;; (армирование НЕ выносить из цикла: в окне EI CBL-ISR
        ;;  армирует акселератор своим размером блока)
    rh_chunk:
        ld    b, #16             ; строк в чанке
        di
        ld    d, d               ; 0x52 — режим размера блока (no-op для D=y)
        ld    a, #0
    rh_len = . - 1
        ld    b, b               ; выключить — размер сохранён
    rh_row:
        ld    a, d
        out   (#0x89), a         ; Port_Y = y (акселератор ВЫКЛЮЧЕН)
        ld    c, c               ; 0x49 — горизонтальный Fill (no-op для C)
        ld    a, c
        ld    (hl), a            ; выстрел: строка w байт
        ld    b, b               ; выключить (перед djnz — fetch смещения!)
        inc   d                  ; y++
        djnz  rh_row
        ei                       ; окно для прерываний между чанками
        dec   e
        jr    NZ, rh_chunk

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

#if 0
/* ================================================================
 * Предыдущий вариант (v2, 2026-07-10, до precompute): решение
 * «16 или хвост» и вычитание h -= n на КАЖДЫЙ чанк через 16-бит
 * арифметику в IX-слотах (~141-189Т на чанк ≈ 11Т/линию).
 * Оставлен закомментированным для отката/сравнения.
 * ================================================================ */
void _gfx_recthfill256(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 = бегущий y
        ld    a, 8 (ix)
        ld    c, a               ; C = color
        ld    a, 4 (ix)
        ld    (rh_len), a        ; SMC: размер блока <- w (0 = 256)
    rh_chunk:
        ld    a, 7 (ix)
        or    a, a
        jr    NZ, rh_full        ; h >= 256 -> чанк 16
        ld    a, 6 (ix)
        or    a, a
        jr    Z, rh_done         ; h == 0 -> готово
        cp    a, #16
        jr    NC, rh_full
        ld    b, a               ; хвост: n = h
        jr    rh_go
    rh_full:
        ld    b, #16             ; полный чанк
    rh_go:
        ld    a, 6 (ix)          ; h -= n
        sub   a, b
        ld    6 (ix), a
        ld    a, 7 (ix)
        sbc   a, #0
        ld    7 (ix), a
        ;; --- скобка: n строк одним армированием размера ---
        ;; (армирование НЕ выносить из цикла: в окне EI CBL-ISR
        ;;  армирует акселератор своим размером блока)
        di
        ld    d, d               ; 0x52 — режим размера блока (no-op для D=y)
        ld    a, #0
    rh_len = . - 1
        ld    b, b               ; выключить — размер сохранён
    rh_row:
        ld    a, d
        out   (#0x89), a         ; Port_Y = y (акселератор ВЫКЛЮЧЕН)
        ld    c, c               ; 0x49 — горизонтальный Fill (no-op для C)
        ld    a, c
        ld    (hl), a            ; выстрел: строка w байт
        ld    b, b               ; выключить (перед djnz — fetch смещения!)
        inc   d                  ; y++
        djnz  rh_row
        ei                       ; окно для прерываний между чанками
        jr    rh_chunk
    rh_done:
        pop   ix
        pop   hl
        pop   af
        pop   af
        inc   sp
        jp    (hl)
    __endasm;
}
#endif
