Files
Sprinter-SDCC/libc/cbl/_cbl.h
T
snark13 8458ec65f4 libc/cbl: две точки входа вместо underrun_mode — malloc больше не в резиденте
Линкер тянет .rel целиком, поэтому malloc/free, стоявшие в мёртвой ветке
CBL_UNDERRUN_SILENCE внутри cbl_open, приезжали в резидент КАЖДОМУ
приложению — включая те, что льют тишину сами и кучей не пользуются.

Разведено:
  cbl_open(freq, fmt, pump, fill)          — ничего не аллоцирует;
  cbl_open_silence(freq, fmt, pump, fill)  — аллоцирует буфер тишины;
  _cbl_open_raw(...)                       — общее тело.
Параметр underrun_mode из публичного API убран: режим задаёт выбор функции.

cbl_close больше не зовёт free — иначе malloc возвращался бы тем же путём.
Буфер тишины живёт до выхода из программы и переиспользуется; его размер
запоминается, иначе открытие 16-бит после 8-бит писало бы memset'ом мимо
выделенного куска.  _cbl_open_raw указатель на буфер не трогает вовсе —
иначе cbl_open после cbl_open_silence терял бы уже выделенную память.

В дереве режим SILENCE не использовал никто: все три вызова (cblwav,
cblstream, PoP) передавали CBL_UNDERRUN_APP.

Итог для PoP: _CODE 24329 -> 23716 (−613 Б), свободно в резиденте 129 ->
747 Б.  make size-check чистый (67 программ), звук в MAME проверен —
насос отработал 696 запросов, pop_snd_ok/want = 1/1.

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

83 lines
5.5 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);
/* Общее тело обеих публичных точек входа (cbl_open / cbl_open_silence).
* Буфер тишины не трогает — им владеет cbl_open_silence.c; так malloc не
* приезжает в резидент приложениям, которые льют тишину сами. */
int _cbl_open_raw(uint8_t freq_code, uint8_t fmt, uint8_t pump_mode,
uint8_t underrun_mode, cbl_fill_fn fill);
/* Залить весь аппаратный буфер (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