580837d2ee
docs/accel-fill-budget.md: полный разбор v3 (подготовка ~340Т на прямоугольник, 46Т на чанк 16 линий, 53Т/51Т на линию, ~107 байт/leaf) против per-line di/ei вариантов (djnz 72Т/линию ~63 байта; 16-бит IX 206Т/линию); break-even n≈9 линий, тотализатор, решение остаться на v3. Краткие выжимки — в шапки _gfx_rect*fill256. Идеи на будущее (там же): ограничить контракт стороной <=256 (широкие прямоугольники пользователь выводит в два приёма); реализовать оба варианта (поблочный/построчный) с переключением опцией сборки в духе GFX_NOCHECK. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
109 lines
7.4 KiB
Markdown
109 lines
7.4 KiB
Markdown
# Акселератор: бюджет тактов и размеров 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) и для программ с жёсткими требованиями к латентности
|
||
прерываний.
|