Files
Sprinter-SDCC/libc/cbl/_cbl_port.c
T
snark13 b34997073e 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
2026-09-02 10:55:15 +03:00

52 lines
2.9 KiB
C

/*
* _cbl_port — разделяемое владение bit7 порта управления 0x004E между
* реальной CBL-сессией (cbl_open/cbl_close) и другими держателями
* (сейчас — gfx_wait_vsync(), которому нужен только бит 5 порта 0xFE,
* доступный лишь пока включён cbl_mode(); см. libc/gfx/gfx_wait_vsync.c
* и MAME sprinter.cpp kbd_fe_r).
*
* Если реальный CBL уже открыт (_irq_cbl_hook != 0) — портом владеет
* он, ref/unref его не трогают вообще (это уже нужное состояние).
* Иначе включаем/выключаем bit7 "немым" кодом частоты _CBL_VSYNC_FREQ.
*
* ТИШИНУ ДАЁТ ЗАЛИТЫЙ БУФЕР, А НЕ ОСТАНОВЛЕННЫЙ ТАЙМЕР. Раньше здесь
* стоял reserved-код частоты 2 — ровно потому, что в MAME при нём не
* заводится таймер (divs[2]=0). На железе поведение reserved-кодов не
* определено, ЦАП тактируется, и включённый bit7 поверх старого буфера
* даёт бесконечную ноту из хвоста последнего сэмпла (пойман Ctrl+S во
* время музыки, 2026-09-02; разбор — в шапке _cbl.h). Поэтому после
* КАЖДОЙ записи в порт управления заливаем все 256 слотов тишиной:
* - bit7 включён — крутится тишина (беззвучно на любом железе);
* - bit7 выключен — те же записи уходят в ЦАП обычного COVOX и
* центруют его на 0x80, снимая щелчок/постоянное смещение.
* Оба состояния редкие (старт пейсинга, пауза звука, выход), 256 OUT'ов
* — около 0,3 мс.
*
* Цена для ГРАФИЧЕСКОЙ программы без звука: _cbl_prime (~10 Б кода)
* приезжает следом за gfx_wait_vsync. Дешевле, чем гудящее железо.
*/
#include "_cbl.h"
#include "../irq/_irq.h"
void _cbl_port_sync(void)
{
if (_irq_cbl_hook) return; /* реальный CBL сам владеет портом */
_cbl_ctrl(_cbl_port_refs ? (uint8_t)(0x80 | _CBL_VSYNC_FREQ) : 0);
/* Формат "немого" режима всегда 8-битный (bit5 не выставлен), поэтому
* тишина здесь — 0x80 независимо от того, каким был закрытый сеанс. */
_cbl_prime(0x80);
}
void _cbl_port_ref(void)
{
_cbl_port_refs++;
_cbl_port_sync();
}
void _cbl_port_unref(void)
{
if (_cbl_port_refs) _cbl_port_refs--;
_cbl_port_sync();
}