SprPoP: звуковые эффекты без перелинковки — раскладка уехала на диск
Часть II плана music_runtime_index_plan.md (SI0..SI4). gen/pop_sound_tbl.h БОЛЬШЕ НЕ ГЕНЕРИРУЕТСЯ: раскладка набора читается из SND/snd.idx (формат PSI1, писатель и разборщик — tools/pop_idx.py, 22 теста в make test-tools). Один и тот же sprpop.exe работает с набором SDLPoP (9 страниц) и MSDOS (10) — sha256 бинарника при смене набора не меняется. Заодно умолчание источника эффектов переведено на SDLPoP (SND_SRC=sdlpop): сборка обязана работать без оригинального дистрибутива DOS. У кого он есть, включает лучший набор явно — make SND_SRC=msdos (там полнее оцифровка: в SDLPoP звук 48 spiked пустой). Устройство: pop_snd_tbl/pop_snd_page/pop_snd_pages — резидентные данные (pop_snd_data.c), тип и инварианты — рукописный pop_snd_tbl.h. Записи читаются ОДНИМ read прямо в таблицу, поэтому sizeof(pop_snd_ent_t) == 5 стало частью дискового контракта: проверяется статически и полем размера записи в заголовке. POP_SND_PAGES как compile-time размер набора исчез — вместо него POP_SND_MAX_PAGES (вместимость, 16) и runtime pop_snd_pages. Цена: таблица переехала из _CODE в _DATA, суммарный резидент почти не изменился (куча 239 -> 229 Б); банк 8 +601 Б на чтение и валидацию. Валидация не доверяет файлу: заголовок целиком плюс каждая запись (страница, смещение, кратность блоку, непересечение с блоком тишины, выход за последнюю страницу). Последнее считается В БЛОКАХ — байтовый адрес конца не влезает в uint16, а 32-битная арифметика на Z80 дорога. НЕТ ИНДЕКСА — ЭФФЕКТОВ НЕТ, НО МУЗЫКА ИГРАЕТ. Первая версия просто возвращала ошибку, и игра становилась непроходимой: тишину льёт первый блок набора, без набора CBL не открывался, а с ним вставала музыка (её блоки считает тот же насос) — заставка ждала конца трека вечно. Теперь поднимается пустой набор с блоком тишины. Заливается ровно 128 байт и под DI: gfx_w0_page_prepare ставит в страницу IRQ-стабы, и заливка всей страницы затирала их — первое же прерывание давало чёрный экран. Грабли сборки: смена SND_SRC тихо давала неверный результат (sdlpop -> msdos -> sdlpop оставлял чужой набор в assets/packed). Причина не в логике, а в секундной гранулярности mtime. Лечение убирает время из решения: смена варианта сносит stamp'ы своего семейства, а упаковка, сборка архива и копия индекса делаются одним рецептом. То же получила и музыка (MUSIC_FMT). Проверено в MAME: таблица в памяти совпадает с файлом из образа побайтово; один EXE поднимает оба набора; отладочный --order reverse (30 из 31 записей отличаются от штатных) звучит правильно; битый индекс выключает эффекты, не роняя игру; без индекса PV-сцена проходит с музыкой; Ctrl+S работает в обоих режимах. Разбор — docs/sound_plan.md §10. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011MsUsEFAQfsjjQpJ7RtKVY
This commit is contained in:
@@ -133,3 +133,51 @@ raw-клавиатура (`<kbd_raw.h>`), которую мы и так заби
|
||||
и трогать его ради экономии секунды стоит только на спокойную голову.
|
||||
|
||||
**Выигрыш.** Одна-две секунды один раз на уровень.
|
||||
|
||||
## Музыка одним постоянно открытым архивом (замер 2026-08-31, НЕ сейчас)
|
||||
|
||||
**Идея.** Сейчас каждый трек — отдельный файл `MUS/mNN.bin`, и
|
||||
`pop_music_load_begin` открывает свой на каждый запуск. Свести треки в
|
||||
один файл и держать его `fd` открытым: вместо `chdir`+`open`+`chdir`
|
||||
останется `lseek` к смещению трека.
|
||||
|
||||
**Почему это стоит внимания — разложение окна старта музыки** (полный
|
||||
замер и метод — `sound_plan.md` §9):
|
||||
|
||||
| шаг | цена | уйдёт? |
|
||||
|---|---:|---|
|
||||
| ожидание `pop_music_service` в кадре | 22,6 мс | нет |
|
||||
| `chdir` #1 | 46,1 мс | **да** |
|
||||
| `open` файла трека | 34,5 мс | **да** |
|
||||
| `chdir` #2 | 59,0 мс | **да** |
|
||||
| чтение первых 8 КБ | 34,0 мс | нет |
|
||||
| **итого** | **196,7 мс** | |
|
||||
|
||||
Уходит **139,6 мс** — но это ВЕРХНЯЯ граница: появляется `lseek` к
|
||||
смещению трека, которого сейчас нет вовсе (файл читается последовательно
|
||||
с нуля), и его цена НЕ ИЗМЕРЕНА. Реальный выигрыш = 139,6 минус `lseek`;
|
||||
померить можно тем же способом, `lseek` уже используется в
|
||||
`pop_arc.c:89`. Остаётся ≈ 57 мс, то есть старт трека ускоряется
|
||||
примерно вчетверо.
|
||||
|
||||
**Где заметно.** Реплики PV-сцены идут встык (три трека подряд), плюс
|
||||
каждый игровой джингл — смерть, зелье, подобранный меч.
|
||||
|
||||
**Чем осложнено.**
|
||||
|
||||
1. **`PBA1` под это не годится.** Размер элемента там `uint16` (≤ 64 КБ),
|
||||
а трек — до 1,2 МБ (m56, 78 страниц); резать по страницам нельзя,
|
||||
элементов вышло бы ~240 при `POP_ARC_MAX = 126`. Нужен свой индекс со
|
||||
смещениями — то есть эта задача СМЫКАЕТСЯ с `MUS/MUSIC.IDX` из
|
||||
`music_runtime_index_plan.md`: одно изменение раскладки, а не два.
|
||||
2. **Постоянно занятый файловый манипулятор** — один из восьми (memory
|
||||
`dss_fd_limit`, девятый `open` вешает DSS). Рядом свои открывают
|
||||
`POP.CFG`, quicksave и загрузка уровня.
|
||||
3. **`chdir` убрать нельзя** — он и есть половина выигрыша, но нужен
|
||||
старым DSS (`POP_PATH_CALL`); экономия берётся не его удалением, а тем,
|
||||
что открытие вообще перестаёт выполняться на каждый трек.
|
||||
|
||||
**Оговорка.** Задержка старта музыки сама по себе НЕ является дефектом:
|
||||
трек начинается на границе события, на слух это не сбой. У эффектов
|
||||
такой задержки нет вовсе — они целиком в EMM (см. `sound_plan.md` §9.1).
|
||||
Так что это оптимизация, а не исправление.
|
||||
|
||||
Reference in New Issue
Block a user