Compare commits
3 Commits
770b946a36
...
v0.9.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 602c3a20fa | |||
| 4ac3584bf9 | |||
| 3bf28da8ee |
@@ -89,3 +89,47 @@ raw-клавиатура (`<kbd_raw.h>`), которую мы и так заби
|
|||||||
после выхода — заметный. Делать после того, как закроем стражей и
|
после выхода — заметный. Делать после того, как закроем стражей и
|
||||||
займёмся бюджетом всерьёз (там же, где батчинг кроссбанковых вызовов и
|
займёмся бюджетом всерьёз (там же, где батчинг кроссбанковых вызовов и
|
||||||
возможный возврат `pop_bg` в резидент `--w3`).
|
возможный возврат `pop_bg` в резидент `--w3`).
|
||||||
|
|
||||||
|
## Готовить следующий уровень, пока играет мелодия конца текущего
|
||||||
|
|
||||||
|
**САМАЯ ДАЛЬНЯЯ ВЕРСИЯ.** Не полишинг и не порт: это улучшение ПРОТИВ
|
||||||
|
оригинала. Планируем, но не раньше, чем закроем уровни и полишинг.
|
||||||
|
|
||||||
|
**Идея (пользователь, 2026-08-28).** Пока звучит мелодия конца уровня (и
|
||||||
|
трек заставки между уровнями), экран не меняется — значит в это время можно
|
||||||
|
успеть прочитать с диска следующий уровень, чтобы после музыки он появлялся
|
||||||
|
сразу, а не через секунду-другую загрузки.
|
||||||
|
|
||||||
|
**Что УЖЕ сделано и мерено (2026-08-28).** Половина этого уже работает:
|
||||||
|
трек заставки играет ПОВЕРХ загрузки уровня — так же, как в оригинале
|
||||||
|
(`seg003:68-108`: `load_intro` возвращает управление, `load_level()` идёт под
|
||||||
|
музыку, и только потом `while (check_sound_playing())` на чёрном экране).
|
||||||
|
Насос это переживает: 500 подряд вызовов `pop_sfx_fill` через всю загрузку —
|
||||||
|
максимальный зазор 245 832 такта при дедлайне 251 000, ни одной пропущенной
|
||||||
|
порции. Осталась вторая половина: мелодия конца уровня досиживается на
|
||||||
|
ЖИВОМ экране пройденного уровня (как в оригинале, `play_level_2`), и только
|
||||||
|
после неё начинается загрузка.
|
||||||
|
|
||||||
|
**Оговорки, найденные при разборе.**
|
||||||
|
|
||||||
|
1. **Загрузку придётся разрезать надвое.** Сейчас `pop_level_switch` мешает
|
||||||
|
дисковую работу (файл уровня в EMM-страницу, атласы стража и тайлсета) с
|
||||||
|
экранно-палитровой (`pop_bg_load` переписывает записи палитры и атласы,
|
||||||
|
`pop_guard_load` физически применяет цветовые слоты, обе страницы
|
||||||
|
заливаются, `pop_pal_black`). Вторую половину НЕЛЬЗЯ выполнять, пока на
|
||||||
|
экране ещё живёт пройденный уровень — иначе палитра поедет прямо на
|
||||||
|
картинке. То есть в «музыкальное окно» можно вынести только диск, а всё,
|
||||||
|
что трогает палитру и VRAM, остаётся после.
|
||||||
|
2. **Бюджет кадра.** Ожидание мелодии идёт в игровом цикле (гейт
|
||||||
|
`pop_endmus_left` + `pop_music_busy()`), кадр там обычный, 80-100 мс.
|
||||||
|
Шаг подкачки должен быть такого же размера, как музыкальный — полстраницы
|
||||||
|
(~16 мс), а не страница целиком; иначе кадры на экране пройденного уровня
|
||||||
|
начнут дёргаться.
|
||||||
|
3. **Память.** Страницы следующего уровня придётся держать одновременно со
|
||||||
|
страницами текущего — проверить EMM-бюджет (memory `sprinter_emm_budget`),
|
||||||
|
тяжёлые тут не файл уровня, а атласы тайлсета и стража.
|
||||||
|
4. **Риск невелик, но он в самом хрупком месте.** Палитро-атласный обмен —
|
||||||
|
источник уже пойманных багов (`PAL-DUNGEON-STALE`, `PAL-L1-AFTER-INTRO`),
|
||||||
|
и трогать его ради экономии секунды стоит только на спокойную голову.
|
||||||
|
|
||||||
|
**Выигрыш.** Одна-две секунды один раз на уровень.
|
||||||
|
|||||||
@@ -1687,7 +1687,9 @@ uint8_t pop_intro_show(void) __banked
|
|||||||
/* Первая реплика PV читается ЗДЕСЬ, под чёрным экраном и в тишине:
|
/* Первая реплика PV читается ЗДЕСЬ, под чёрным экраном и в тишине:
|
||||||
* дальше внутри сцены остаётся только пуск, а следующие две реплики
|
* дальше внутри сцены остаётся только пуск, а следующие две реплики
|
||||||
* подкачиваются по странице за кадр. */
|
* подкачиваются по странице за кадр. */
|
||||||
pop_sfx_pause();
|
/* Глушение снято 2026-08-28: массовое чтение страниц насос не морит
|
||||||
|
* (замер на загрузке уровня — ни одной пропущенной порции), а трек
|
||||||
|
* титров может и должен доигрывать. */
|
||||||
(void)pop_music_load(POP_MUS_STORY_2);
|
(void)pop_music_load(POP_MUS_STORY_2);
|
||||||
pv_result = intro_pv_animated();
|
pv_result = intro_pv_animated();
|
||||||
if (pv_result > 0) {
|
if (pv_result > 0) {
|
||||||
@@ -1765,15 +1767,21 @@ uint8_t pop_intro_show(void) __banked
|
|||||||
* дальше, а не чтобы досидеть до конца трека. */
|
* дальше, а не чтобы досидеть до конца трека. */
|
||||||
static void pre_cut_finish(uint8_t aborted)
|
static void pre_cut_finish(uint8_t aborted)
|
||||||
{
|
{
|
||||||
if (!aborted) {
|
/* ТРЕК ПЕРЕЖИВАЕТ СЦЕНУ — как в оригинале. load_intro (seg001:690)
|
||||||
intro_skip_t tail;
|
* ничего не ждёт: гасит экран и возвращает управление, а звук играет
|
||||||
|
* дальше поверх загрузки уровня и первых секунд игры. Мы вместо этого
|
||||||
intro_skip_begin(&tail);
|
* ДОСИЖИВАЛИ трек на чёрном экране (27 — 10,7 с против 2,6 с картинки),
|
||||||
while (pop_music_busy() && !intro_skip_requested(&tail))
|
* то есть дарили игроку восемь секунд пустого экрана, и только потом
|
||||||
pop_wait_edge();
|
* глушили. ЗАМЕР 2026-08-28: снято вместе с глушением на загрузке
|
||||||
|
* уровня; если загрузка не укладывается в дедлайн насоса (251 000
|
||||||
|
* тактов), вернуть оба места разом.
|
||||||
|
*
|
||||||
|
* Пропуск сцены по-прежнему обрывает и музыку: игрок нажал клавишу,
|
||||||
|
* чтобы идти дальше, а не чтобы дослушать. */
|
||||||
|
if (aborted) {
|
||||||
|
pop_music_free();
|
||||||
|
pop_sfx_pause();
|
||||||
}
|
}
|
||||||
pop_music_free();
|
|
||||||
pop_sfx_pause();
|
|
||||||
intro_restore_game_palette();
|
intro_restore_game_palette();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1781,7 +1789,7 @@ void pop_pre_cutscene_show(pop_pre_cutscene_t scene) __banked
|
|||||||
{
|
{
|
||||||
const pop_cs_cmd_t *script = 0;
|
const pop_cs_cmd_t *script = 0;
|
||||||
|
|
||||||
pop_sfx_pause();
|
/* Глушение перед чтением трека снято 2026-08-28 (см. замер насоса). */
|
||||||
/* МУЗЫКА ПЕРЕЖИВАЕТ СЦЕНУ. Треки заставок между уровнями длиннее самих
|
/* МУЗЫКА ПЕРЕЖИВАЕТ СЦЕНУ. Треки заставок между уровнями длиннее самих
|
||||||
* сцен (27 — 10,7 с против 2,6 с картинки), и оригинал их не обрывает:
|
* сцен (27 — 10,7 с против 2,6 с картинки), и оригинал их не обрывает:
|
||||||
* load_intro просто гасит экран и возвращает управление, а звук
|
* load_intro просто гасит экран и возвращает управление, а звук
|
||||||
@@ -1849,7 +1857,7 @@ int pop_intro_hof_bg(uint8_t page) __banked
|
|||||||
|
|
||||||
void pop_time_expired_show(void) __banked
|
void pop_time_expired_show(void) __banked
|
||||||
{
|
{
|
||||||
pop_sfx_pause();
|
/* Глушение снято 2026-08-28 (см. замер насоса). */
|
||||||
/* time_expired (seg001:04D3) начинается с play_sound(sound_36). */
|
/* time_expired (seg001:04D3) начинается с play_sound(sound_36). */
|
||||||
if (pop_music_load(36) == 0) {
|
if (pop_music_load(36) == 0) {
|
||||||
(void)pop_sfx_start();
|
(void)pop_sfx_start();
|
||||||
@@ -1878,7 +1886,7 @@ void pop_time_expired_show(void) __banked
|
|||||||
|
|
||||||
void pop_ending_show(void) __banked
|
void pop_ending_show(void) __banked
|
||||||
{
|
{
|
||||||
pop_sfx_pause();
|
/* Глушение снято 2026-08-28 (см. замер насоса). */
|
||||||
/* Экран гасим ПЕРВЫМ действием — как load_intro оригинала, который
|
/* Экран гасим ПЕРВЫМ действием — как load_intro оригинала, который
|
||||||
* начинается с draw_rect(&screen_rect, color_0_black). Иначе загрузка
|
* начинается с draw_rect(&screen_rect, color_0_black). Иначе загрузка
|
||||||
* ресурсов сцены идёт поверх ЖИВОЙ комнаты уровня 14, и она «мелькает»
|
* ресурсов сцены идёт поверх ЖИВОЙ комнаты уровня 14, и она «мелькает»
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#include "pop_ctrl.h" /* pop_ctrl_init/close — ввод поднимает и закрывает boot */
|
#include "pop_ctrl.h" /* pop_ctrl_init/close — ввод поднимает и закрывает boot */
|
||||||
#include "pop_shadow.h" /* запечённый набор Тени */
|
#include "pop_shadow.h" /* запечённый набор Тени */
|
||||||
#include "pop_sfx.h" /* звуковые эффекты через CBL */
|
#include "pop_sfx.h" /* звуковые эффекты через CBL */
|
||||||
|
#include "pop_music.h" /* pop_music_busy/free — ждём трек заставки */
|
||||||
#include "pop_vflip.h" /* сброс зеркальных страниц на смене уровня */
|
#include "pop_vflip.h" /* сброс зеркальных страниц на смене уровня */
|
||||||
#include "pop_cheat.h" /* pop_cheats — режим разработки включает pop_boot */
|
#include "pop_cheat.h" /* pop_cheats — режим разработки включает pop_boot */
|
||||||
#include "pop_pace.h" /* pop_speed_mode — режим скорости в отладочной метке */
|
#include "pop_pace.h" /* pop_speed_mode — режим скорости в отладочной метке */
|
||||||
@@ -226,7 +227,12 @@ int8_t pop_level_change(uint8_t back) __banked
|
|||||||
* почти всегда — поймано пользователем 2026-08-25. Ветка с
|
* почти всегда — поймано пользователем 2026-08-25. Ветка с
|
||||||
* катсценой свободна от этого: pop_pre_cutscene_show глушит
|
* катсценой свободна от этого: pop_pre_cutscene_show глушит
|
||||||
* звук первым действием. */
|
* звук первым действием. */
|
||||||
pop_sfx_pause();
|
/* Глушение снято 2026-08-28 (замер: загрузка уровня насос не
|
||||||
|
* морит — 500 вызовов подряд, максимум 245 832 такта при
|
||||||
|
* дедлайне 251 000). Прежнее обоснование — скрежет на палитре
|
||||||
|
* через BIOS ($A4 с запрещёнными прерываниями), пойманный на
|
||||||
|
* переходе без катсцены 2026-08-25. Если он вернётся, это
|
||||||
|
* единственное место, куда его надо возвращать. */
|
||||||
/* Без fade: старый кадр гасим сразу и обязательно даём
|
/* Без fade: старый кадр гасим сразу и обязательно даём
|
||||||
* показать один чёрный растр ДО диска/перерисовки. */
|
* показать один чёрный растр ДО диска/перерисовки. */
|
||||||
pop_pal_apply(POP_PAL_DIM_BLACK);
|
pop_pal_apply(POP_PAL_DIM_BLACK);
|
||||||
@@ -1227,7 +1233,10 @@ int pop_level_switch(void) __banked
|
|||||||
* дальше, а урон — нет (do_startpos дольёт до потолка). */
|
* дальше, а урон — нет (do_startpos дольёт до потолка). */
|
||||||
hitp_beg_lev = hitp_max;
|
hitp_beg_lev = hitp_max;
|
||||||
pop_checkpoint = 0; /* уровень пройден (seg003:413) */
|
pop_checkpoint = 0; /* уровень пройден (seg003:413) */
|
||||||
pop_sfx_pause(); /* загрузка уровня — ESTEX надолго, звук глушим */
|
/* ЗАМЕР 2026-08-28: глушение снято ВРЕМЕННО, чтобы измерить
|
||||||
|
* зазоры насоса на загрузке уровня (дедлайн 251 000 тактов).
|
||||||
|
* Если зазоры укладываются — снятие остаётся, и трек заставки
|
||||||
|
* доигрывает поверх загрузки, как в оригинале (seg001:690). */
|
||||||
if (pop_level_load_num(pop_next_level) != 0) {
|
if (pop_level_load_num(pop_next_level) != 0) {
|
||||||
pop_next_level = pop_current_level; /* файла нет — остаёмся */
|
pop_next_level = pop_current_level; /* файла нет — остаёмся */
|
||||||
} else {
|
} else {
|
||||||
@@ -1294,6 +1303,34 @@ int pop_level_switch(void) __banked
|
|||||||
/* Атомарный commit на границе растра: впервые показать
|
/* Атомарный commit на границе растра: впервые показать
|
||||||
* готовую комнату уже с окончательной палитрой уровня.
|
* готовую комнату уже с окончательной палитрой уровня.
|
||||||
* Вторую страницу синхронизировать аппаратной копией. */
|
* Вторую страницу синхронизировать аппаратной копией. */
|
||||||
|
/* ЖДЁМ КОНЦА ТРЕКА ЗАСТАВКИ НА ЧЁРНОМ ЭКРАНЕ — порт
|
||||||
|
* seg003:107:
|
||||||
|
* while (check_sound_playing() && !do_paused()) idle();
|
||||||
|
* stop_sounds();
|
||||||
|
* draw_level_first();
|
||||||
|
* Порядок оригинала важен: загрузка уровня идёт ПОД
|
||||||
|
* звучащий трек, и только потом чернота до его конца. Тогда
|
||||||
|
* общее время черноты = max(трек, загрузка), а не их сумма,
|
||||||
|
* и трек не обрывается на полуслове. Раньше у нас было
|
||||||
|
* наоборот: pre_cut_finish досиживал трек, потом глушил
|
||||||
|
* насос и грузил уровень.
|
||||||
|
*
|
||||||
|
* ЗАМЕР 2026-08-28: 500 подряд вызовов насоса через всю
|
||||||
|
* загрузку — максимальный зазор 245 832 такта при дедлайне
|
||||||
|
* 251 000, ни одной пропущенной порции. Поэтому глушение на
|
||||||
|
* загрузке снято и музыка её переживает.
|
||||||
|
*
|
||||||
|
* Счётчик — страховка от вечного ожидания (потоковый трек,
|
||||||
|
* который никогда не кончится): ~20 с и идём дальше. */
|
||||||
|
{
|
||||||
|
uint16_t mus_guard = 1000;
|
||||||
|
|
||||||
|
while (pop_music_busy() && mus_guard) {
|
||||||
|
mus_guard--;
|
||||||
|
pop_wait_edge();
|
||||||
|
}
|
||||||
|
pop_music_free(); /* порт stop_sounds() */
|
||||||
|
}
|
||||||
pop_wait_edge();
|
pop_wait_edge();
|
||||||
gfx_set_visible_page(level_prepared_page);
|
gfx_set_visible_page(level_prepared_page);
|
||||||
gfx_set_draw_page(old_page);
|
gfx_set_draw_page(old_page);
|
||||||
|
|||||||
Reference in New Issue
Block a user