libbgi: W0-атласы спрайтов — загрузчик, движок, упаковщик (все проверки PASS)

Реализация §3.1/§9в: атлас = один .atl-файл = одна EMM-страница,
подключаемая в W0 на время блита.

- atlas_load: read() файла целиком в страницу через W3 + патч ISR-стаба
  (0x38: JP _gfx_w0_isr; 0x66: RETN); atlas_free/atlas_image/
  atlas_sprite_init; gfx_w0_map/unmap для ручных вызовов.
- _gfx_w0_isr (стаб из tests/w0page): свап на ядро DSS → честный 0x38 →
  restore спрайт-страницы; покрывает IM1 и IM2-чейн.
- sprite_t.page (0 = обычная память); sprite_update в блит-проходе
  мапит страницу по смене (один OUT на атлас), эпилог возвращает DSS
  (+52 Б на движок — цена фичи).
- toolchain/mkatlas.py: PNG (indexed) / raw → .atl; заголовок 0x100,
  каталог 0x68 (19 лент), файл-офсет == офсет страницы == W0-адрес.
- Сплит _gfx_sprite_fns → _gfx_blit_fn.c + _gfx_heal_fn.c (1 указатель
  = 1 модуль): putsprite-only программа не тянет heal-ядро (spriteclip
  ловил +292 Б; теперь −68 Б от эталона).

tests/atlas (MAME dev, PASS): загрузка (count/страница), каталог и
данные по W0-адресам (заголовок ленты через gfx_w0_map), 3 спрайта из
двух лент через движок — пиксели проверены read_vram побайтно
(0x10/0x12/0x21, прозрачный угол = фон).

docs: §9г — предложение авто-анимации (кадровая ±step без умножений,
tween-Брезенхэм порциями, тикер через funcptr) — ОБСУЖДАЕТСЯ.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-13 15:52:00 +03:00
parent c4a512200f
commit 0e935343d9
22 changed files with 648 additions and 29 deletions
+4 -3
View File
@@ -2,13 +2,14 @@
accfill 3772
argv 3431
assrtest 3847
atlas 9154
attrprob 4087
balls 6156
balls 6208
banked 1056
bankedbg 1067
banklocl 4683
banktest 3767
bgi_img 7060
bgi_img 7057
bgitest 3737
bios_text 4461
blitperf 5860
@@ -43,7 +44,7 @@ rt_test 4892
seek 4188
simple 955
solidt 11531
spriteclip 4187
spriteclip 4119
sprites 6791
stattest 7549
stdlib 6643
1 # Эталон размеров _CODE (байт); обновление: python3 toolchain/size_check.py --update
2 accfill
3 argv
4 assrtest
5 atlas
6 attrprob
7 balls
8 banked
9 bankedbg
10 banklocl
11 banktest
12 bgi_img
13 bgitest
14 bios_text
15 blitperf
44 seek
45 simple
46 solidt
47 spriteclip
48 sprites
49 stattest
50 stdlib
+63
View File
@@ -700,6 +700,69 @@ Probe-тест tests/w0page (MAME dev, 2026-07-13, ВСЕ PASS):
W1, но только tiny (в small/big/huge в W1 код) — отвергнута в пользу
универсального W0.
## 9г. Авто-анимация спрайтов (ПРЕДЛОЖЕНИЕ 2026-07-13, обсуждается)
Идея пользователя: sprite_update (вызывается раз на кадр) сам тикает
анимации — приложение только описывает их декларативно.
### Кадровая анимация (смена изображений)
«Крути кадры от M до P с интервалом N кадров»; два режима: линейный
(0/1/2/3/0/1/2/3) и маятник (0/1/2/3/2/1/0/...).
Ключ к дешёвому тику на Z80: индекс кадра НЕ хранить — переход между
соседними кадрами ленты это ±step к sx или sy (step = fw или fh),
БЕЗ умножения/деления. Состояние: axis-смещение текущего кадра уже
живёт в s->sx/s->sy; добавляются границы и шаг:
uint8_t an_speed; /* кадров между сменами; 0 = анимации нет */
uint8_t an_timer; /* счётчик до следующей смены */
uint8_t an_step; /* fw или fh (ось ленты) */
int an_lo,an_hi; /* smin..smax по оси (M и P умножены заранее*
* в sprite_anim() — один раз, не в тике) */
флаги: ANIM_PINGPONG, ANIM_DIR (текущее направление), ANIM_ONESHOT?
API: sprite_anim(s, first, last, speed, mode) / sprite_anim_stop(s).
Тик (в sprite_update, до проходов): --an_timer; при 0: sy ± an_step,
на границе — wrap (линейный: sy=an_lo) или разворот (маятник:
инверсия DIR), s->flags |= _SPR_DIRTY. Стоимость: декремент + редкая
ветка — копейки.
### Анимированное перемещение (tween)
«Плыви к (tx,ty), максимум max_step пикселей за срабатывание, раз в
interval кадров». Пример: (0,0)→(100,50), шаг 5, интервал 4 → 20
срабатываний × 4 кадра = 80 кадров (1.6 с); Y идёт нелинейно 2/3/2/3.
Реализация — инкрементальный Брезенхэм (как _bgi_lineseg), но
«порциями» по ≤max_step вдоль БОЛЬШЕЙ оси за срабатывание: ошибка-
аккумулятор тянет меньшую ось, деления нет. Состояние ~10 байт:
tx,ty, err, adx,ady (абс. дельты), знаки, mv_speed/mv_timer.
Завершение: x==tx && y==ty → снять SPR_MOVING (опрос sprite_moving(s);
callback НЕ делаем — опрос проще и дешевле).
### Общее / критика / расширения
- Тик — отдельный проход в НАЧАЛЕ sprite_update (до heal: dirty должен
взводиться раньше проходов). ВАЖНО: тик должен идти раз на КАДР —
sprite_update и так зовётся на кадр (дабл-буфер: попеременно на
страницу); при просадке fps анимации замедляются вместе с кадром
(стандартное поведение, принимаем).
- DCE: тикер — в отдельном модуле, sprite_update зовёт его через
указатель _spr_tick_fn (дефолт NULL → один if на кадр); указатель
ставит первый вызов sprite_anim()/sprite_moveto(). Программа без
анимации не тянет код тикера (паттерн funcptr как у clip/noclip).
- Цена памяти: sprite_t вырастает на ~16-18 байт (кадровая ~10 +
tween ~10 с перекрытием полей?). Вариант «отдельная anim-структура
по указателю (NULL = нет)» экономит память статиков, но добавляет
indirection в тик и второй массив приложению. ПРЕДЛОЖЕНИЕ: поля в
sprite_t (спрайтов десятки — до 64×~34 Б ≈ 2 КБ, W2 переживёт),
но решение за пользователем.
- Расширения (потом): one-shot кадровая анимация (проиграть раз и
замереть/скрыться); дробная скорость 8.8 fixed-point (равномерное
медленное движение «1 пиксель в 1.5 кадра»); цепочки целей
(waypoints) — приложение ставит новую цель по sprite_moving()==0.
## 10. Риски / что проверить артефактом (Фаза 0)
Статус 2026-07-11: пп. 13 закрыты для MAME 0.283 (tests/gfxbanks,