Files
Sprinter-SDCC/libc/cbl/_cbl.h
T
snark13 f159aa47e1 CBL: заливка буфера тишиной при открытии + щелчок на выходе из PoP
Две разные болячки, обе разобраны записью звука MAME в WAV.

libc: буфер CBL железо не чистит, а запись в порт управления сразу пускает
воспроизведение с нулевого слота — первые 256 сэмплов (23,4 мс) уходит то,
что лежало раньше.  Своими данными звук идёт лишь с третьей половины:
прерывание приходит на 128-м слоте и ставит указатель на противоположную
половину.  _cbl_prime заливает буфер тишиной сразу после включения (раньше
нельзя — запись проходит только при поднятом bit7).  В MAME это немо
(эмулируемый буфер стартует нулями при двухдополнительном ЦАП), на железе
это ровно тот мусор, что ловился на тестовых примерах CBL.

PoP: на выходе по ESC звучало ровно 11 мс шума на полной громкости — один
пропущенный блок (128 сэмплов).  Причина: pop_shutdown освобождал атласы и
графику через ESTEX при открытом звуке, насос не успевал долить.  Звук
гасим первым действием.  Проверено записью — всплеска больше нет.

Заодно записан разбор стартового всплеска (sound_plan.md §10): это не
мусор, а gate_closing_fast из левой комнаты, обрываемый soft_land.  Обрыв
одноголосьем — поведение оригинала (play_digi_sound начинается с
stop_digi, seg009.c:2402).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 16:23:06 +03:00

77 lines
5.0 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* _cbl.h — внутренности CBL-модуля (НЕ публичный заголовок).
*
* Механика (форум Sprinter Team + официальная документация "5.3
* COVOX-Blaster" + docs/converted/accel_r.txt):
* - порт управления 0x004E — 16-битный (только ld bc,#0x004E /
* out (c),a): bit7 = CBL on, bit6 = stereo, bit5 = 16-бит,
* bit4 = int enable, bits3..0 = частота; выключение — запись 0;
* - буфер 256 Б аппаратно разбит на две половины по 128 Б; бит 7
* порта 0xFE отражает, какая половина ИГРАЕТ сейчас — флип этого
* бита поднимает прерывание (вектор 0xFF), пора долить неактивную
* половину;
* - размер блока — 128 Б для 8-бит формата, 256 Б для 16-бит (не
* зависит от моно/стерео) — см. _cbl_block;
* - выдача блока — ДВА способа (cbl_push_otir.c/cbl_push_accel.c):
* OTIR в порт 0x4F, либо акселератором в спец-страницу EMM 0xFD
* (маппится в W3 на 0xC000); библиотека сама НИЧЕГО не пропихивает
* — это делает fill()-callback приложения;
* - насос вызывается из кадрового трамплина через хук _irq_cbl_hook
* (см. _irq.h) — прямой ссылки irq→cbl нет, DCE сохраняется.
*
* Никакого собственного кольца НЕТ — второй буфер поверх аппаратного
* был бы избыточен. _cbl_fill — callback приложения; _cbl_silence —
* буфер тишины, malloc'ится в cbl_open() ТОЛЬКО при
* CBL_UNDERRUN_SILENCE (иначе NULL).
*
* _cbl_port_ref/_cbl_port_unref (_cbl_port.c) — общий счётчик желающих
* держать bit7 порта 0x004E включённым НЕЗАВИСИМО от полноценной CBL-
* сессии. Нужно gfx_wait_vsync() (см. libc/gfx/gfx_wait_vsync.c): бит
* 5 порта 0xFE отражает позицию луча (см. MAME sprinter.cpp kbd_fe_r),
* но доступен ТОЛЬКО пока включён cbl_mode(). Если реальный CBL уже
* открыт — портом владеет он, ref/unref его не трогают; иначе включают
* bit7 "немым" кодом частоты _CBL_VSYNC_FREQ (таймер CBL не заводится
* — divs[2]=0 в MAME — ни звука, ни прерываний, только синхро-бит).
*/
#ifndef _CBL_INTERNAL_H
#define _CBL_INTERNAL_H
#include <stdint.h>
#include <cbl.h>
#define _CBL_CTRL_PORT 0x004E
#define _CBL_DATA_PORT 0x4F
#define _CBL_ACCEL_PAGE 0xFD /* спец-страница EMM: запись на W3@0xC000 идёт в CBL */
#define _CBL_VSYNC_FREQ 0x02 /* "немой" код частоты — divs[2]=0 в MAME, таймер не идёт */
extern uint16_t _cbl_block; /* размер блока: 128 или 256 (по формату) */
extern uint16_t _cbl_reqs; /* блоков-запросов обслужено */
extern uint16_t _cbl_undr; /* из них недолив (fill вернул 0/не задан) */
extern cbl_fill_fn _cbl_fill; /* callback приложения, может быть NULL */
extern uint8_t _cbl_underrun_mode; /* CBL_UNDERRUN_* */
extern uint8_t *_cbl_silence; /* буфер тишины или NULL */
extern uint8_t _cbl_port_refs; /* держатели bit7 помимо реальной CBL-сессии */
/* Запись в 16-битный порт управления 0x004E. */
void _cbl_ctrl(uint8_t v);
/* Залить весь аппаратный буфер (256 слотов) байтом тишины сразу после
* включения — иначе первые 23 мс играет то, что лежало в буфере раньше.
* Подробный разбор — в шапке _cbl_prime.c. */
void _cbl_prime(uint8_t silence);
/* ISR-насосы: вызвать fill(), при недоливе — тишина по режиму (один
* регистрируется в _irq_cbl_hook при cbl_open — по выбору CBL_PUMP_*). */
void _cbl_pump_otir(void);
void _cbl_pump_accel(void);
/* Разделяемое владение портом 0x004E (см. описание выше).
* _cbl_port_sync пересчитывает нужное значение порта из текущего
* состояния (реальная сессия? держатели?) и пишет его — вызывается
* из ref/unref, а также из cbl_close() после снятия _irq_cbl_hook. */
void _cbl_port_ref(void);
void _cbl_port_unref(void);
void _cbl_port_sync(void);
#endif