/* * _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(); }