ba3aca07bf
ЗАМЕР, из которого всё выросло (MAME, 2026-08-25): `open` стоит 51,4 мс, а чтение 16 КБ из уже открытого файла — 32,6 мс. То есть 61 % времени загрузки уходит на ОТКРЫТИЕ, а не на данные, и чистая скорость чтения — 491 КБ/с, а не 260. На диске игры 343 файла; одна только стартовая пачка (kid+shadow+bg+sound+guard, ~142 файла) — это 7,3 секунды чистого open. * toolchain/pop_pack_arc.py + pop_arc.c/h (банк 8) — формат PBA1: заголовок 512 Б, магия, count, записи по 4 байта (offset в СЕКТОРАХ — архив бывает больше 64 КБ; size в байтах — ресурс всегда <= 16 КБ, он обязан лезть в EMM-страницу). Таблицу держит вызывающий на своём стеке: в W2 её класть нельзя, а стека в точке загрузки израсходовано 75 байт из 1279 (замер там же). * libc: bank_read_page — чтение из УЖЕ ОТКРЫТОГО файла в EMM-страницу. bank_load_file переписана через неё, поэтому дублирования работы с W3 не осталось и резидент почти не вырос. * Набор эффектов: 10 файлов -> SND\snd.arc, один open вместо десяти. * Музыка: трек лежит ОДНИМ файлом и читается порциями по странице — нарезка на куски больше не нужна. Этот же путь понадобится финалу `won` (1,2 МБ), который в память целиком не влезает. * Менеджер звука приведён к оригиналу: играющая музыка участвует в таблицах приоритета наравне с эффектами (seg000:1672 + data.h:433). Почти вся музыка НЕперебиваема, поэтому поверх неё эффект не стартует — ровно как в SDLPoP; раньше эффект её перебивал. * ALLOCS 3000 -> 6000 (решение пользователя): сборка +20 с, резидент -161 байт. Куча 87 -> 248 Б. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
107 lines
4.0 KiB
C
107 lines
4.0 KiB
C
/*
|
|
* pop_music.c — загрузка и запуск музыкального трека, БАНК 9.
|
|
*
|
|
* Здесь только холодная часть: выделить страницы EMM, вычитать в них куски
|
|
* m<id>_<nn>.bin и выставить курсор. Сам вывод делает насос CBL в
|
|
* резиденте (pop_sfx.c): когда эффект не звучит, он берёт байты отсюда, а
|
|
* не из блока тишины. Контракт и мотивация — в pop_music.h.
|
|
*
|
|
* Имя файла собираем вручную, без sprintf: printf-семейство в резидент не
|
|
* тянем, а тут и формат-то из двух чисел.
|
|
*/
|
|
#include <stdint.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <sprinter_mem.h>
|
|
#include "pop_music.h"
|
|
#include "pop_music_tbl.h"
|
|
#include "pop_sfx.h"
|
|
|
|
uint8_t pop_mus_page[POP_MUS_PAGES];
|
|
static uint8_t mus_blk; /* блок EMM под трек */
|
|
static uint8_t mus_pages; /* сколько страниц реально занято */
|
|
static uint16_t mus_blocks; /* длина трека в порциях по 128 Б */
|
|
static uint8_t mus_id; /* что загружено — для менеджера звука */
|
|
|
|
/* "MUS\mNN.bin" — ровно 8.3, как требует DSS. */
|
|
static void mus_name(char *buf, uint8_t id)
|
|
{
|
|
buf[0] = 'M'; buf[1] = 'U'; buf[2] = 'S'; buf[3] = '\\';
|
|
buf[4] = 'm';
|
|
buf[5] = (char)('0' + id / 10);
|
|
buf[6] = (char)('0' + id % 10);
|
|
buf[7] = '.'; buf[8] = 'b'; buf[9] = 'i'; buf[10] = 'n';
|
|
buf[11] = 0;
|
|
}
|
|
|
|
void pop_music_stop(void) __banked
|
|
{
|
|
pop_mus_left = 0;
|
|
}
|
|
|
|
void pop_music_free(void) __banked
|
|
{
|
|
pop_mus_left = 0;
|
|
if (mus_blk) {
|
|
mem_free_block(mus_blk);
|
|
mus_blk = 0;
|
|
}
|
|
mus_pages = 0;
|
|
mus_blocks = 0;
|
|
}
|
|
|
|
int8_t pop_music_load(uint8_t id) __banked
|
|
{
|
|
char name[12];
|
|
uint8_t i, pages;
|
|
int fd;
|
|
|
|
if (id >= POP_MUS_COUNT) return -1;
|
|
pages = pop_mus_tbl[id].pages;
|
|
if (!pages || pages > POP_MUS_PAGES) return -1;
|
|
|
|
pop_music_free();
|
|
mus_name(name, id);
|
|
/* Трек лежит ОДНИМ файлом и читается порциями по странице: `open` стоит
|
|
* дороже самого чтения (51,4 мс против 32,6 мс на 16 КБ), и платить его
|
|
* на каждую страницу — чистые потери. Тот же путь понадобится финалу
|
|
* `won`, который в память целиком не влезает. */
|
|
fd = open(name, O_RDONLY);
|
|
if (fd < 0) return -1;
|
|
mus_blk = mem_alloc_pages(pages);
|
|
if (!mus_blk) { close(fd); return -1; }
|
|
for (i = 0; i < pages; i++) {
|
|
pop_mus_page[i] = mem_get_page(mus_blk, i);
|
|
/* Последняя страница короче — читаем сколько есть; ошибку ловим по
|
|
* отрицательному коду, а не по «не 16384». */
|
|
if (!pop_mus_page[i] ||
|
|
bank_read_page(fd, pop_mus_page[i], 0, 16384) < 0) {
|
|
close(fd);
|
|
pop_music_free();
|
|
return -1;
|
|
}
|
|
}
|
|
close(fd);
|
|
mus_pages = pages;
|
|
mus_blocks = pop_mus_tbl[id].blocks;
|
|
mus_id = id;
|
|
return 0;
|
|
}
|
|
|
|
void pop_music_play(void) __banked
|
|
{
|
|
if (!mus_pages) return;
|
|
/* Порядок важен: сначала позиция, потом длина. Насос стартует ровно
|
|
* с той порции, где left стал ненулевым, и промежуточное состояние
|
|
* «играем, но курсор от прошлого трека» не возникает. */
|
|
pop_mus_id = mus_id; /* до left: менеджер спросит уже готовое */
|
|
pop_mus_pg = 0;
|
|
pop_mus_ptr = 0;
|
|
pop_mus_left = mus_blocks;
|
|
}
|
|
|
|
uint8_t pop_music_busy(void) __banked
|
|
{
|
|
return (uint8_t)(pop_mus_left != 0);
|
|
}
|