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

/* 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
