Files
Sprinter-SDCC/applications/PoP/roomtest/pop_vflip.c
T
snark13 65797af44c Ресурсы — в архивы PBA1; тайминги сцены PV — по часам насоса CBL
ТАЙМИНГИ.  Пользователь заметил, что заклинание Джафара не совпадает с
музыкой. Замер в MAME (breakpoint на вспышке + курсор трека): реплика m53
успевала ДОИГРАТЬ до молнии, хотя по шкале оригинала между входом Джафара
и заклинанием ровно 822 тика = 13,7 с.

Причина: пейсинг считал НАШИ ожидания vsync, а не прошедшее время.
Полноэкранная копия страницы стоит больше кадра луча, и разница копилась.
Кадровые прерывания для счёта не годятся (теряются в di-окнах
акселератора), поэтому часами стал насос CBL: он идёт от расхода буфера
железом, 85,4 Гц, и ему безразлично, чем занят главный цикл. Один тик
оригинала = 57/40 тика насоса (0,07 % ошибки, без деления в кадре).
Когда звук выключен, работает прежний путь по кадрам луча.

После фикса замер даёт 229 блоков остатка m53 против расчётных 233 —
расхождение 47 мс.

АРХИВЫ.  Группы ресурсов сложены в PBA1: kid (58 атласов), pv (78),
shadow (32), bg (25), title (20), guard (10), vizier (5), skel (4). Было
232 файла на образе, стало 8 архивов плюс шесть палитр и таблицы уровней.
При цене `open` 51,4 мс против 32,6 мс за чтение 16 КБ это возвращает
секунды на каждой загрузке.

- pop_pack_arc.py печатает индексы элементов константами (ARC_<GRP>_<FILE>),
  порядок задаёт Makefile, а серии код проверяет статически;
- atlas_load разделён: atlas_attach принимает уже прочитанную страницу,
  поэтому чтение из архива не дублирует проверку магии и подготовку W0;
- имена архивов живут в pop_arc.c и адресуются номером. Строковый литерал
  лежит в rodata своего банка, и указатель на него из другого банка после
  переключения W3 показывает на чужие данные — ровно так падала загрузка
  фона (поймано брейкпоинтом на puts).

Проверено в MAME: заставка, интро и уровень 1 собираются из архивов,
Тень грузит все 32 страницы. Host-тесты: 15 наборов.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 21:29:14 +03:00

68 lines
3.2 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* pop_vflip.c — таблица «страница атласа -> её ВЕРТИКАЛЬНО ЗЕРКАЛЬНАЯ пара»
* (зелье инверсии, уровень 9). См. pop_vflip.h.
*
* Сами зеркальные ленты делает упаковщик (`vflip_cols` в pop_pack_kid.py) и
* кладёт рядом с обычными: kid0_v.atl, sword_v.atl, g0_v.atl… Здесь остаётся
* только связать пары: зеркальный файл грузится обычным atlas_load, а его
* раскладка байт-в-байт совпадает с оригиналом — значит движку достаточно
* подменить НОМЕР СТРАНИЦЫ, которую он мапит в W0.
*
* Почему не считаем в рантайме (замеры MAME 2026-08-12, такты Z80):
* загрузка одной 16-КБ страницы с HDD — 970 003 такта (0.045 с);
* разворот той же страницы на месте — 12 645 582 такта (0.59 с).
* Даже идеальная конвертация (accel-копия + asm-реверс) даёт ~1.05 млн —
* то есть в лучшем случае сравнивается с чтением готового файла, а стоит
* asm-модуля. На все 34 страницы: 1.5 с загрузки против 20 с разворота.
*
* РЕЗИДЕНТ: pop_vflip_page зовёт отрисовка персонажей (банк 4) на каждый
* спрайт — трамплин тут ни к чему, а кода всего ничего.
*/
#include <stdint.h>
#include <sprite.h>
#include "pop_vflip.h"
/* Персонажных страниц: 28 (Кид) + 5 (страж) + 1 (меч) = 34; с запасом. */
#define VF_MAX 40
static uint8_t vf_src[VF_MAX]; /* страница оригинала */
static atlas_t vf_atl[VF_MAX]; /* зеркальный атлас (его page) */
static uint8_t vf_n;
/* Взять УЖЕ ЗАГРУЖЕННЫЙ зеркальный атлас под своё управление (дальше его
* освобождает pop_vflip_reset). Нужен тем, кто читает зеркала не файлом, а
* куском архива ресурсов: загрузку делает вызывающий, связывание — мы. */
int pop_vflip_take(const atlas_t *orig, const atlas_t *mirror)
{
if (!orig || !mirror || vf_n >= VF_MAX) return -1;
vf_atl[vf_n] = *mirror;
vf_src[vf_n] = orig->page;
vf_n++;
return 0;
}
int pop_vflip_add(const atlas_t *orig, const char *path)
{
atlas_t m;
if (!orig || vf_n >= VF_MAX) return -1;
if (atlas_load(&m, path) != 0) return -1;
return pop_vflip_take(orig, &m);
}
uint8_t pop_vflip_page(uint8_t src)
{
uint8_t i;
for (i = 0; i < vf_n; i++)
if (vf_src[i] == src) return vf_atl[i].page;
return 0; /* пары нет — рисуем обычным атласом */
}
uint8_t pop_vflip_ready(void) { return vf_n != 0; }
void pop_vflip_reset(void)
{
uint8_t i;
for (i = 0; i < vf_n; i++) atlas_free(&vf_atl[i]);
vf_n = 0;
}