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:
@@ -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: пп. 1–3 закрыты для MAME 0.283 (tests/gfxbanks,
|
||||
|
||||
Reference in New Issue
Block a user