libc: IM2 Phase 2b — звук CBL/COVOX через callback fill(), без кольца libc
CBL уже имеет аппаратный буфер 256 Б (2×128, двойная буферизация на стороне железа) — держать поверх него ещё одно кольцо в libc было бы лишней копией. cbl_open(freq, fmt, pump_mode, underrun_mode, fill) регистрирует callback, вызываемый из ISR за очередным блоком; он сам пропихивает данные приложения (откуда угодно) через cbl_push_otir()/ cbl_push_accel() — без промежуточного буфера. - два насоса: OTIR (порт 0x4F) и ACCEL (акселератор, спец-страница EMM 0xFD@0xC000); OTIR+16-бит запрещён (EINVAL) — по исходнику MAME порт данных физически не может собрать 16-бит сэмпл из пары байт; - форматы CBL_FMT_MONO8/16/STEREO8/16, частоты 7.8..109к; - CBL_UNDERRUN_APP (по умолчанию, недолив не наша забота) / CBL_UNDERRUN_SILENCE (буфер тишины malloc'ится только в этом режиме); - tests/cbltest: матрица 64 комбинации (2 насоса × 8 форматов × 4 частоты); tests/cblwav: banked-стрим речи с дискеты (физстраницы кэшированы заранее — mem_get_page нельзя звать из fill()/ISR); tests/cblstream: единственный случай с собственным кольцом уровня приложения (диск нельзя читать из fill()). Verified в MAME 2026-07-07 — все три теста работают. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -168,6 +168,62 @@ font[row*256+char]).
|
||||
Handler'у нельзя: ESTEX/BIOS-вызовы, gfx_*/своп окон, акселератор,
|
||||
banked-функции; только volatile-глобалы и быстрая работа (<1 мс).
|
||||
|
||||
## <cbl.h> — потоковый звук CBL/COVOX (вектор 0xFF, свой ISR)
|
||||
|
||||
Без собственного кольца (2026-07-07): у CBL уже есть аппаратный буфер
|
||||
256 Б (2×128, двойная буферизация на стороне железа — см. официальную
|
||||
доку "5.3 COVOX-Blaster"); библиотека просто зовёт `fill()` приложения
|
||||
ИЗ ISR, а оно само пропихивает данные (откуда угодно) через
|
||||
`cbl_push_otir/accel` — без промежуточной копии.
|
||||
|
||||
| Сигнатура | Описание |
|
||||
|---|---|
|
||||
| `int cbl_open(freq_code, fmt, pump_mode, underrun_mode, fill)` | включить CBL (`0x90\|fmt\|freq`), зарегистрировать callback; 0 / -1+errno (EBUSY повтор, EINVAL — freq/fmt/pump_mode/underrun_mode плохие или OTIR+16-бит, ENOMEM — буфер тишины) |
|
||||
| `void cbl_close(void)` | выключить CBL, снять хук, освободить буфер тишины (если был); висит на atexit |
|
||||
| `void cbl_push_otir(const void *src, uint16_t n)` | пропихнуть n байт через `otir` в порт 0x4F; звать ИЗ fill() |
|
||||
| `void cbl_push_accel(const void *src, uint16_t n)` | то же акселератором (страница EMM 0xFD@0xC000); единственный путь для 16-бит |
|
||||
| `uint16_t cbl_requests(void)` | счётчик запросов блока ISR (~fs/(сэмплов в блоке) в секунду) |
|
||||
| `uint16_t cbl_underruns(void)` | счётчик недоливов (`fill` вернул 0 или не задан) |
|
||||
| `CBL_FREQ_7K8 .. CBL_FREQ_109K` | коды частоты (биты 3..0 control-порта) |
|
||||
| `CBL_FMT_MONO8/MONO16/STEREO8/STEREO16` | формат (биты 5/6 control-порта); блок 128 Б (8-бит) или 256 Б (16-бит), не зависит от моно/стерео; тишина 0x80 (8-бит) / 0x0000 (16-бит, знаковый) |
|
||||
| `CBL_PUMP_OTIR / CBL_PUMP_ACCEL` | способ выдачи — см. ниже |
|
||||
| `CBL_UNDERRUN_APP / CBL_UNDERRUN_SILENCE` | поведение при недоливе — см. ниже |
|
||||
|
||||
`typedef int (*cbl_fill_fn)(uint16_t n);` — callback, зовётся ИЗ ISR за
|
||||
очередным блоком (n = `_cbl_block`, 128/256). **Обязан быть быстрым**
|
||||
— никаких ESTEX/BIOS/gfx-вызовов (тот же констрейнт, что у
|
||||
`irq_install()`-хендлера); вернуть ненулевое, если реально пропихнул n
|
||||
байт. Диск (read()) читать из fill() НЕЛЬЗЯ — см. tests/cblstream,
|
||||
где под это заведено кольцо уровня приложения.
|
||||
|
||||
Два насоса (3-й параметр `cbl_open`):
|
||||
- **OTIR** (`_cbl_pump_otir`) — `cbl_push_otir()` в порт 0x4F; базовый.
|
||||
**НЕ умеет 16-бит** — `cbl_open(..., MONO16/STEREO16, CBL_PUMP_OTIR,
|
||||
...)` вернёт EINVAL: по исходнику MAME (sprinter.cpp) порт данных
|
||||
ВСЕГДА кладёт байт как есть в один слот, не собирая пару байт в
|
||||
16-бит значение и не сверяясь с 16-бит флагом вообще.
|
||||
- **ACCEL** (`_cbl_pump_accel`) — `cbl_push_accel()` через акселератор
|
||||
в спец-страницу EMM 0xFD, замапленную в окно W3 на 0xC000 (см.
|
||||
docs/converted/accel_r.txt, Forum.txt); размер блока патчится SMC
|
||||
(`LD D,D` + immediate `LD A,n` + `LD L,L` — тот же паттерн, что и в
|
||||
libc/gfx/_gfx_hfill256.c); единственный путь для 16-бит.
|
||||
**Verified в MAME 2026-07-07** (tests/cbltest, вся accel-половина
|
||||
матрицы прошла без ошибок/underrun).
|
||||
|
||||
Поведение при недоливе (4-й параметр `cbl_open`):
|
||||
- **CBL_UNDERRUN_APP** (по умолчанию, 0) — не забота библиотеки, буфер
|
||||
тишины НЕ аллоцируется, в CBL доигрывает то, что уже лежало в его
|
||||
аппаратном буфере.
|
||||
- **CBL_UNDERRUN_SILENCE** (1) — насос сам пропихивает тишину; буфер
|
||||
(128/256 Б по формату) аллоцируется malloc'ом ВНУТРИ `cbl_open()`
|
||||
только в этом режиме.
|
||||
`cbl_underruns()` считает недоливы в обоих режимах — диагностика,
|
||||
поведение не меняет.
|
||||
|
||||
Приватное прерывание, к DSS не чейнится; бит 7 порта 0xFE (запрос
|
||||
блока) читается только пока `cbl_open` не закрыт (при выключенном CBL
|
||||
бит всегда 1 — MAME-квирк).
|
||||
|
||||
## <palette.h> — низкий уровень (BIOS $A4/$A6)
|
||||
|
||||
`pal_load pal_get pal_set_color pal_get_color` (страница 0..7,
|
||||
|
||||
Reference in New Issue
Block a user