Files
Sprinter-SDCC/applications/PoP/roomtest/pop_shadow.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

106 lines
5.4 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_shadow.c — загрузка запечённого набора спрайтов Тени. Зачем набор
* отдельный и почему половин две — в шапке pop_shadow.h.
*
* Код ХОЛОДНЫЙ (раз на уровень), поэтому живёт в банке 8 вместе с прочей
* загрузкой. Сами atlas_t — в W2: их читает отрисовка из банка 4, а
* данные видны из любого банка.
*/
#include <stdint.h>
#include <gfx.h>
#include <sprite.h>
#include "pop_arc.h"
#include "shadow_arc.h"
#include "pop_shadow.h"
#include "pop_shadow_atlas.h"
atlas_t pop_shadow_kid[POP_SHADOW_KID_PAGES];
atlas_t pop_shadow_fgt[POP_SHADOW_FGT_PAGES];
uint8_t pop_shadow_kid_n, pop_shadow_fgt_n;
/* ГРУЗИМ ОДИН РАЗ НА ЗАПУСК, а не по уровням.
*
* Тень бывает на ЧЕТЫРЁХ уровнях: 4 — рождается прыжком сквозь зеркало
* (jump_through_mirror, seg003:0811, ставит Char.charid = charid_1_shadow
* прямо из Кида), 5 — крадёт зелье, 6 — прыгает через пропасть, 12 —
* дерётся. Читается как три, потому что таблиц init_shad_N всего три
* (5/6/12): четвёртой нет — на 4-м Тень копируется с Кида, а не поднимается
* из таблицы. Так вот, напрашивается
* грузить набор только там, как это сделано с перевёрнутыми наборами
* (pop_vflip_load_all зовётся лишь на POP_UPSIDE_LEVEL). Решение
* пользователя — грузить сразу, и оно правильное по двум причинам:
* страниц EMM с запасом (32 из ~215 свободных), а вот забыть перезагрузку
* на границе уровня легко — ровно так и появился BUG-SHADOW-SET. Разовая
* загрузка убирает целый класс ошибок и заодно не тормозит переход между
* уровнями на 32 файла. */
int pop_shadow_load(void) __banked
{
uint8_t i;
if (pop_shadow_kid_n || pop_shadow_fgt_n) return 0; /* уже загружен */
{
uint8_t tbl[ARC_SHADOW_COUNT * POP_ARC_REC];
pop_arc_t arc;
/* Тридцать два атласа — из одного архива: раньше это были тридцать
* два `open` по 51,4 мс, то есть полторы секунды на ровном месте. */
if (pop_arc_open_id(&arc, POP_ARC_SHADOW, tbl,
ARC_SHADOW_COUNT) < 0)
return -1;
for (i = 0; i < POP_SHADOW_KID_PAGES; i++) {
if (pop_arc_atlas(&arc, tbl, (uint8_t)(ARC_SHADOW_SK0 + i),
&pop_shadow_kid[i]) != 0) {
pop_arc_close(&arc);
pop_shadow_free();
return -1;
}
pop_shadow_kid_n = (uint8_t)(i + 1);
}
for (i = 0; i < POP_SHADOW_FGT_PAGES; i++) {
if (pop_arc_atlas(&arc, tbl, (uint8_t)(ARC_SHADOW_SF0 + i),
&pop_shadow_fgt[i]) != 0) {
pop_arc_close(&arc);
pop_shadow_free();
return -1;
}
pop_shadow_fgt_n = (uint8_t)(i + 1);
}
pop_arc_close(&arc);
}
pop_shadow_pal_apply();
return 0;
}
/* ПАЛИТРА ОТДЕЛЬНО ОТ ЗАГРУЗКИ, и это не косметика.
*
* `gfx_pal_fload("KID\\kid.pal")` заливает ВСЕ 256 записей — это единая
* игровая палитра, — и затирает любые слоты, выставленные до него. Ровно
* та же беда уже была с тайлсетом: сразу за ним стоит pop_bg_pal_apply,
* который возвращает цвета env/wall. Наши 0xA0..0xAF из той же оперы, и
* в первом заходе я на это наступил: набор грузился, силуэт рисовался
* правильной формы, но ЦЕЛИКОМ ЧЁРНЫЙ (поймано пользователем на 6-м
* уровне). Поэтому вызов живёт рядом с pop_bg_pal_apply, а не внутри
* загрузки атласов.
*
* Обе страницы дабл-буфера: у них свои палитры. Через буфер в W2 —
* gfx_pal_load отдаёт указатель BIOS ($A4), а тот читает только
* #4000-#BFFF и банковую rodata не увидит. */
void pop_shadow_pal_apply(void) __banked
{
uint8_t buf[64], k;
if (!pop_shadow_kid_n) return; /* набора нет — нечего красить */
for (k = 0; k < 64; k++) buf[k] = pop_shadow_pal[k];
gfx_pal_load(0, POP_SHADOW_PAL_BASE, 16, buf);
gfx_pal_load(1, POP_SHADOW_PAL_BASE, 16, buf);
}
void pop_shadow_free(void) __banked
{
uint8_t i;
for (i = 0; i < pop_shadow_kid_n; i++) atlas_free(&pop_shadow_kid[i]);
for (i = 0; i < pop_shadow_fgt_n; i++) atlas_free(&pop_shadow_fgt[i]);
pop_shadow_kid_n = pop_shadow_fgt_n = 0;
}