libc/cbl: выключение CBL больше не оставляет железо петь одну ноту
cbl_close() не пишет в порт управления ноль: пока gfx_wait_vsync держит ссылку ради бита луча (в PoP её берёт pop_pace_arm на всю программу), bit7 остаётся включённым. А буфер CBL (256 слотов) при включённом bit7 крутится по кругу и не чистится ничем — железо бесконечно поёт хвост последнего сэмпла, тоном и громкостью по последней мелодии. Пойман пользователем на железе: Ctrl+S во время музыки и пропуск заставки давали ноту до следующего cbl_open. В MAME не воспроизводится: "немым" кодом частоты был reserved-код 2, у которого divs[2]==0 и таймер не заводится вовсе. На железе reserved-коды не определены, ЦАП тактируется — тишина держалась на свойстве эмулятора, а не железа. Теперь тишину даёт СОДЕРЖИМОЕ БУФЕРА: - _cbl_port_sync() после каждой записи в порт управления зовёт _cbl_prime(0x80). Это закрывает и паузу звука, и включение bit7 ради луча на холодном старте (в буфере лежал мусор от прошлой программы), и полное выключение — при bit7=0 те же 256 записей уходят прямо в ЦАП обычного COVOX и центруют его, снимая щелчок; - _CBL_VSYNC_FREQ переведён с reserved-кода 2 на документированный 8 (7,8125 кГц): поведение определено и на железе, и в MAME, прерывания по-прежнему выключены (bit4=0), а бит 7 порта 0xFE трамплин смотрит только при живом хуке насоса; - cbl_close() зовёт sync внутри той же DI-скобки, где снимает хук, иначе насос долил бы буфер уже после заливки. Цена: +2 Б программам со звуком, +11 Б графическим (тянется _cbl_prime следом за gfx_wait_vsync), 256 OUT'ов (~0,3 мс) на редкое событие — эталоны cblstream/cbltest/cblwav/gfx_dbuf обновлены. Рост atlas в size-check к этой правке отношения не имеет (замерен тот же и без неё). Проверено: кодоген _cbl_port.asm; SprPoP пересобран и прогнан в MAME (титры → пропуск заставки → уровень 1, пейсинг по лучу жив). Сам баг проверяется только на железе. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DSYUpuaQpKr48kBav2iiV4
This commit is contained in:
+15
-1
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* _cbl_prime — залить аппаратный буфер CBL тишиной СРАЗУ после включения.
|
||||
* _cbl_prime — залить аппаратный буфер CBL тишиной СРАЗУ после записи в
|
||||
* порт управления: и при включении, и при выключении.
|
||||
*
|
||||
* ЗАЧЕМ. Буфер CBL (256 слотов, две половины по 128) железо НЕ чистит ни
|
||||
* при сбросе, ни при записи в порт управления — там остаётся то, что лежало
|
||||
@@ -31,6 +32,19 @@
|
||||
* Слотов ровно 256 НЕЗАВИСИМО от формата (в 16-бит слот держит целый
|
||||
* сэмпл), поэтому и записей всегда 256 — байт тишины разный: 0x80 для
|
||||
* 8-бит беззнакового, 0x00 для 16-бит.
|
||||
*
|
||||
* ВТОРАЯ РОЛЬ — ГАШЕНИЕ (добавлена 2026-09-02, _cbl_port_sync). Запись
|
||||
* в порт управления НЕ чистит буфер и в обратную сторону: пока bit7
|
||||
* включён (а его держит gfx_wait_vsync ради бита луча), железо крутит
|
||||
* оставшиеся 256 байт по кругу — это ровно "одна нота" из фирменного
|
||||
* примера Forum.txt, только незваная. Поэтому после каждой записи в
|
||||
* порт управления зовём эту же функцию:
|
||||
* - bit7 остался включён — по кругу крутится тишина;
|
||||
* - bit7 снят — те же 256 записей идут прямо в ЦАП обычного COVOX
|
||||
* (MAME sprinter.cpp, `case 0x88` без cbl_mode()) и оставляют его в
|
||||
* нуле 0x80, а не на последнем сэмпле.
|
||||
* Гонки с насосом нет и здесь: при гашении хук уже снят, при включении
|
||||
* первое прерывание будет только на 128-м слоте.
|
||||
*/
|
||||
|
||||
#include "_cbl.h"
|
||||
|
||||
Reference in New Issue
Block a user