/* * pop_music.c — загрузка и запуск музыкального трека, БАНК 9. * * Здесь только холодная часть: выделить страницы EMM, вычитать в них куски * m_.bin и выставить курсор. Сам вывод делает насос CBL в * резиденте (pop_sfx.c): когда эффект не звучит, он берёт байты отсюда, а * не из блока тишины. Контракт и мотивация — в pop_music.h. * * Имя файла собираем вручную, без sprintf: printf-семейство в резидент не * тянем, а тут и формат-то из двух чисел. */ #include #include #include #include #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); }