# Акселератор: бюджет тактов и размеров chunked-заливок (rectfill/clear)

2026-07-10, после рефактора 2c414da (chunked-заливки v3 на мульти-триггере
акселератора).  Семантика FSM акселератора — memory/accel_multitrigger_fill,
регресс-тест — tests/accfill.  Такты — стандартные Z80; пересчёт в мкс —
для 21 МГц без учёта wait-state'ов видеопамяти.

## Архитектура v3 (что считаем)

`_gfx_rectvfill256` / `_gfx_recthfill256`: заливка чанками ≤16 линий на одну
DI/EI-скобку.  В скобке размер блока армируется один раз; на линию — OUT
Port_Y (при выключенном акселераторе) + 1 опкод режима (LD E,E / LD C,C) +
выстрел + LD B,B.  Счётчик чанков precompute'ится один раз на входе в
байт-регистр E (`n>>4`), хвост `n&15` SMC-патчится в отдельную хвостовую
скобку со своим армированием (в окне EI между чанками CBL-ISR армирует
акселератор своим размером блока — поэтому армирование в каждой скобке).

## Бюджет v3

### 1) Подготовка — один раз на прямоугольник (~325–340Т)

| блок                                                  | тактов  |
|-------------------------------------------------------|---------|
| пролог: push ix / ld ix,#0 / add ix,sp + база+x + цвет | 102     |
| SMC размера в обе скобки (19+13+13)                    | 45      |
| precompute: хвост n&15 → SMC + чанки n>>4 → E          | 108–118 |
| проверка «есть ли полные чанки»                        | 15–20   |
| эпилог: pop ix / 3×pop / inc sp / jp (hl)              | 54      |

### 2) На блок 16 линий — 46Т

ld b,#16 (7) + di (4) + арм ld d,d / ld a,#n / ld b,b (15) + ei (4) +
dec e / jr nz (16) = 46Т ≈ 2.9Т/линию.  Хвостовая скобка ≈ 45Т (заход
22 + арм 23).

### 3) На линию — 53Т (vertical) / 51Т (horizontal)

ld a,d (4) + out (11) + режим (4) + ld a,c (4) + ld (hl),a (7) +
ld b,b (4) + inc hl (6) / inc d (4) + djnz (13).

**Формула:** T(n) ≈ 340 + 46·⌈n/16⌉ + 53·n  (vertical; horizontal 51·n).

## Альтернативы: каждая линия в своей DI/EI-скобке

**А — байтовый счётчик в B + djnz** (арм на линию, всё в одном цикле):
подготовка ~210Т, линия 72Т (h) / 74Т (v).  Ограничение: счётчик ≤ 256 —
для vertical с w до 320 нужен двухпроходный костыль (~+10 байт).
Бонус: DI-окно = 1 линия (≤37 мкс) — минимальная латентность прерываний.

**Б — 16-бит счётчик в IX-слотах** (стиль per-line v1, без call):
подготовка ~190Т, линия ~206Т (135Т из них — декремент+проверка 16-бит
числа через 19Т-обращения к IX-слотам).

### Тотализатор (vertical, тактов на прямоугольник n линий)

| линий | v3      | А (djnz)  | Б (IX)   |
|-------|---------|-----------|----------|
| 1     | ~420    | **~280**  | ~395     |
| 4     | ~580    | **~500**  | ~1015    |
| 8     | ~790    | **~785**  | ~1840    |
| 9     | ~845    | ~855      | ~2045    |
| 16    | ~1215   | ~1365     | ~3490    |
| 64    | ~3895   | ~4820     | ~13 375  |
| 256   | ~14 620 | ~18 640   | ~52 930  |

**Break-even v3 против А: n ≈ 9 линий** (340+53n+2.9n = 210+72n → n≈8.3).
При n ≤ 8 простейший А быстрее (максимум выигрыша +140Т ≈ 6.7 мкс на n=1 —
precompute уходит впустую); при n ≥ 9 v3 впереди, дальше ~19Т/линию (~26%
CPU-оверхеда; полный экран — экономия ~4000Т ≈ 190 мкс).
Б проигрывает v3 уже с n = 2 — от него и уходили.

## Размер кода (активные инструкции, на один leaf)

| вариант      | байт   | разбивка                                        |
|--------------|--------|-------------------------------------------------|
| v3 (текущий) | ~107   | пролог 18 + SMC 9 + precompute 26 + вход 4 + чанк-цикл 21 + хвост 22 + эпилог 7 |
| А (djnz)     | ~53–63 | пролог 18 + SMC 6 + счётчик 7 + линия 15 + эпилог 7 (+~10 двухпроходность vertical) |
| Б (IX)       | ~70    | пролог 18 + SMC 6 + цикл 39 + эпилог 7          |

Диспетчер `_gfx_rectfill256` ≈ 40 байт (проверка ориентации 160–245Т,
break-even |w−h| ≥ ~4 линии — при известной форме звать leaf напрямую).
Переход пары leaf'ов на А дал бы ~−100 байт на программу ценой −26%
скорости на n ≥ 9.

**Решение: оставлена v3** — в графическом коде такты дороже сотни байт;
для узких прямоугольников (≤8 линий) есть span-примитивы
_bgi_hspan_raw/_bgi_vspan_raw (одна линия одной скобкой без precompute).

## Идеи (не реализовано)

- **Отказаться от поддержки сторон > 256 точек.**  Сейчас vertical тащит
  ветку «старший байт w = +16 чанков» (precompute) ради 257..320; если
  ограничить контракт стороной ≤ 256 и оставить пользователю выводить
  широкие прямоугольники в два приёма самому — упрощается precompute
  (~−15Т подготовки, −5..8 байт), исчезает UB-зона w>511, контракты
  обеих leaf-функций становятся симметричными.  Цена: bar() на полную
  ширину экрана обязан сам разбить вызов на два (или это делает
  диспетчер — тогда экономии в диспетчерном пути нет, только в прямых
  вызовах leaf'ов).

- **Два варианта рисования + переключение опцией сборки.**  Реализовать
  оба: поблочный (v3, быстрый при n ≥ 9, DI-окно ≤0.7 мс) и построчный
  (А: per-line di/ei + djnz, −100 байт на пару leaf'ов, быстрый при
  n ≤ 8, DI-окно = 1 линия ≤37 мкс — идеально при активном CBL) — и
  выбирать опцией сборки в духе существующего GFX_NOCHECK / safe-fast
  вариантов libbgi.  Кандидат для size-critical сборок (small 32КБ,
  как mdview2) и для программ с жёсткими требованиями к латентности
  прерываний.

- **Span-примитивы для узких прямоугольников (узкая сторона ≤ 8).**
  Break-even n≈9 означает: прямоугольник в 2..8 линий по узкой стороне
  дешевле нарисовать циклом готовых span-примитивов
  (_bgi_hspan_raw/_bgi_vspan_raw — одна линия одной скобкой, без
  подготовки/precompute), чем через rectfill, где ~340Т подготовки
  уходят впустую.  Варианты: fast-path в диспетчере _gfx_rectfill256
  («узкая сторона ≤ 8 → цикл span'ов»; удорожает проверку диспетчера
  на ~20-30Т) или оставить на усмотрение пользователя, задокументировав
  рецепт.  Делать по результатам профиля — если такие прямоугольники
  встречаются в горячем коде (рамки, тонкие полосы, разделители).
  Продублировано в docs/TODO.md (GFX: оптимизации).
