Оптимизации A+B (профиль rpgwalk-15: активная часть кадра 410К → 307К
тактов из 430080; лимит спрайтов 16×16 на стабильные 48.8 fps: 14 → ~21):
- (B) sprite_t.src/stride — готовый адрес кадра: считают только
sprite_frame (теперь функция, одно умножение на СМЕНУ кадра) и тикер
(±an_step БАЙТ инкрементально); блит-ядра принимают src+stride,
img/sx/sy из сигнатуры ушли. Блит 177К → 146К на кадр.
- (A) тик 100К → 37.7К: tween переформулирован Брезенхэм → беззнаковый
DDA (mv_rem/mv_acc, «приехали» = rem==0 — без знаковых 16-бит
сравнений), tick_move и tick_anim — ручной asm (SDCC спиллит такие
функции в IX-фрейм ~100 обращений; C-реструктуризации не помогали —
проверено кодогеном). Биты an_flags переименованы по категориям
(_SPR_STRIP_HORZ, _SPR_PP_BACK).
Y-сортировка (gfx_sprite_ysort, идеи пользователя — 8-бит ключ,
персистентность):
- painter's algorithm по ключу {layer:8, clamp_y:8}; поле
sprite_t.layer (в КОНЦЕ структуры — asm-офсеты не сдвигает): слои
сцены в одном массиве/одном sprite_update;
- ПЕРСИСТЕНТНАЯ asm-таблица {key16, ptr16}: resort порядка прошлого
кадра (почти линейно), rebuild при смене arr/count; массив
приложения не трогается; ~28К/15 спрайтов (с нуля было 44К);
- компоненты YSORT_Y/YSORT_LAYER отключаемы независимо масками ключа
(без ветвлений в сортировщике); ВНИМАНИЕ: mode=1 значит Y-only,
полный порядок = YSORT_Y|YSORT_LAYER;
- funcptr-DCE: выключено = код и таблица не линкуются (rpgwalk −190 Б);
- ПРАВИЛО в sprite.h: два sprite_update на страницу запрещены (heal
второй группы стирает спрайты первой — ОЗУ-копия чистая).
Попутные фиксы:
- libbgi/Makefile: .rel зависят от заголовков (HDRS) — stale .rel со
старой раскладкой sprite_t молча ломал рантайм;
- rpgprof: --memory small (перерос tiny: BSS вылезал за W2 → мгновенный
«Unexpected application termination»; mkexe это пока не ловит);
- tests/spranim: проверки переведены на кэш src, добавлены T6 (reframe
после тикера) и T7 (Y-сортировка: порядок, слои, персистентный
resort, LAYER-only) — 7/7 PASS в MAME.
Доки: §9д — новый бюджет (19.5К/спрайт), §9е — ПЛАН FPS-делителя
(frame pacing, gfx_set_fps_div); TODO — дизайн цепочки irq-обработчиков.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Реализация §9г по требованиям пользователя:
- sprite_anim(first,last,speed,mode): ANIM_LOOP / ANIM_PINGPONG /
ANIM_ONCE (one-shot замирает на last), | ANIM_HORIZ для
горизонтальных лент. Смена кадра в тике = ±an_step к оси ленты —
без умножений (осевое смещение first считается в setup циклом).
- sprite_moveto(tx,ty,max_step,interval): Брезенхэм порциями
≤max_step вдоль большей оси (меньшая — err-аккумулятором,
нелинейные шаги Y сами собой), деления нет.
- Одновременность кадровой и tween — независимые поля/биты.
- Статус: sprite_anim_status() — битовое поле SPR_ANIM_ON/DONE +
SPR_MOVE_ON/DONE; sprite_anim_frame() — текущий индекс кадра;
sprite_moving().
- Стопы: sprite_anim_stop(frame | -1 = текущий);
sprite_move_stop(0 = замереть / 1 = прыжок в цель + DONE).
- Тикер _sprite_tick — проход 0 sprite_update через funcptr
_spr_tick_fn (DCE: без sprite_anim/moveto код не линкуется,
цена — один if на кадр; +40 Б программе с движком, sprite_t +18 Б).
tests/spranim (MAME dev, все PASS): LOOP/PINGPONG (разворот на границе
без удвоения краёв)/ONCE+DONE; пример (0,0)→(100,50), шаг 5,
интервал 4 → ровно 80 кадров, Y идёт 3/2/3/2; стопы обоих видов.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Спрайтовая графика поверх accel block-copy (docs/sprite-api-design.md):
- Ядро блиттинга: leaf'ы _bgi_blit_rows_raw (dst фикс, только src-страйд) /
_bgi_copy_rows_raw (getimage) / _bgi_heal_rows_raw (src==dst). DI один на
спрайт (санкция: малый спрайт под одним DI аудио не рвёт); src[0]-фикс
снят (точная MAME подавляет CPU-байт триггера записи — на железе
перепроверить; для heal был избыточен и снят безусловно).
- Общие bracket-free ядра _gfx_blit_full/_gfx_heal_full (полная ширина:
клип по экрану + split >256 для putimage) + лин _gfx_blit_sprite/
_gfx_heal_sprite (кадр ≤64, без split, 8-бит w/h) + noclip-варианты
(клип-кода нет → полный codegen-win). Имя *_full (не *_clip) — «clip»
двусмысленно (sprite-ядра тоже клипуют; различитель — ширина/split).
- Движок retained-модели <sprite.h>: sprite_init/update/flip + inline
move/frame/show/hide/touch; drawn[2] per-page внутри структуры; кадр —
двухпроходно heal ВСЕ -> блит ВСЕ под одной W3-скобкой/банком на проход.
- Флаг gfx_sprite_clip(on/off): приложение, само следящее за границами,
отключает клип (~+19% на анимации; диспетч пока через if — funcptr далее).
- putsprite/movesprite/gfx_blit/putimage(COPY)/getimage переведены на ядро.
Тесты: examples/balls (движок, дабл-буфер, boundary-тест клипа),
tests/sprites (RAM PASS, клип 4 края, атлас), tests/blitw (trig-leak),
tests/spriteclip (hardware-probe: железо НЕ режет за краем -> клип нужен),
tests/blitperf, tests/gfxbanks. size-baseline обновлён (53 программы).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>