Files
Sprinter-SDCC/tests/cbltest/cbltest.c
T
snark13 3ea4546656 cbltest: пропущенный вызывающий cbl_open + обновление размерного эталона
tests/cbltest не попал в правку API (мой греп обрезался на артефактах
сборки, полный make его и поймал).

Эталон размеров: cblstream −614, cbltest −617, cblwav −592 — это ушедший
malloc.  bgi_img +229 к моей правке отношения не имеет (CBL он не линкует
вовсе): рост пришёл с b3e754a, реверта «каталог атласа читается из W0».

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

160 lines
6.9 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.
/*
* cbltest — полный перебор CBL-звука (Phase 2b, docs/im2_isr_design.md).
*
* Матрица: 2 насоса (OTIR/ACCEL) × 8 форматов (mono/stereo-L/stereo-R/
* stereo-LR, каждый в 8- и 16-бит) × 4 частоты (7.8/10.9/15.6/21.9 кГц)
* = 64 комбинации, каждая ~1 с пилообразного тона (период 64 сэмпла/
* кадра). Для каждой печатается requests/underruns — 0 недоливов
* ожидается всегда. В MAME на слух должно быть отчётливо слышно:
* смену тембра насоса (не должно быть разницы OTIR/ACCEL), канал
* (левый/правый/оба) и высоту тона (растёт с частотой).
*
* OTIR+16-бит (16 из 64 комбинаций) ПРОПУСКАЮТСЯ — cbl_open() отдаёт
* EINVAL: OTIR-порт данных не умеет собирать 16-бит сэмпл из пары
* байт (только акселератор это делает; см. cbl_open.c, сверено с
* исходником MAME sprinter.cpp).
*
* Новая модель без кольца libc (2026-07-07): fill_tone() зовётся ИЗ
* ISR, сама пропихивает n байт из уже готового периодического чанка
* через cbl_push_otir/accel (по current_pump) — данные никогда не
* кончаются, так что CBL_UNDERRUN_APP (без буфера тишины) достаточно.
* Main-цикл ничего не поллит — просто ждёт ~1 с через halt().
*/
#include <stdio.h>
#include <time.h>
#include <conio.h>
#include <errno.h>
#include <cbl.h>
static uint8_t chunk[256];
static uint8_t current_pump; /* CBL_PUMP_OTIR/ACCEL для активной комбинации */
/* Заполнить chunk одним периодом (64 сэмпла/кадра) пилы — раскладка
* зависит от bits (8/16) и stereo (моно / чередование L,R). */
static void gen_chunk(uint8_t bits, uint8_t stereo, uint8_t want_l, uint8_t want_r)
{
if (!stereo) {
if (bits == 8) {
for (uint16_t i = 0; i < sizeof(chunk); i++) {
chunk[i] = (uint8_t)(i * 4); /* период 64 (обёртка uint8_t) */
}
} else {
int16_t *p = (int16_t *)chunk;
for (uint16_t i = 0; i < sizeof(chunk) / 2; i++) {
uint16_t idx = i & 63;
p[i] = (int16_t)(uint16_t)(idx * 1024u);
}
}
} else if (bits == 8) {
for (uint16_t k = 0; k < sizeof(chunk) / 2; k++) {
uint8_t frame = (uint8_t)(k & 63);
uint8_t tone = (uint8_t)(frame * 4);
chunk[2 * k] = want_l ? tone : 0x80;
chunk[2 * k + 1] = want_r ? tone : 0x80;
}
} else {
int16_t *p = (int16_t *)chunk;
for (uint16_t k = 0; k < sizeof(chunk) / 4; k++) {
uint16_t frame = k & 63;
int16_t tone = (int16_t)(uint16_t)(frame * 1024u);
p[2 * k] = want_l ? tone : 0;
p[2 * k + 1] = want_r ? tone : 0;
}
}
}
/* Зовётся ИЗ ISR (см. cbl.h: обязано быть быстрым). chunk — готовый
* period-64 тон, никогда не "кончается", поэтому всегда успех. */
static int fill_tone(uint16_t n)
{
if (current_pump == CBL_PUMP_ACCEL) cbl_push_accel(chunk, n);
else cbl_push_otir(chunk, n);
return 1;
}
static void play_combo(uint16_t idx, uint16_t total,
const char *pump_name, uint8_t pump_mode,
const char *fmt_name, uint8_t fmt, uint8_t bits,
uint8_t stereo, uint8_t want_l, uint8_t want_r,
const char *freq_name, uint8_t freq_code)
{
gen_chunk(bits, stereo, want_l, want_r);
current_pump = pump_mode;
if (cbl_open(freq_code, fmt, pump_mode, fill_tone) != 0) {
printf("[%2u/%u] %s %s %s: cbl_open failed errno=%d\n",
idx, total, pump_name, fmt_name, freq_name, errno);
return;
}
printf("[%2u/%u] %s %s %s ...\n", idx, total, pump_name, fmt_name, freq_name);
datetime_t t, t2;
getdatetime(&t);
getdatetime(&t2);
while (t.second == t2.second) {
__asm__("halt"); /* ISR сам кормит CBL — просто ждём */
getdatetime(&t2);
}
uint16_t reqs = cbl_requests();
uint16_t undr = cbl_underruns();
cbl_close();
if (undr) printf(" !! underruns=%u (reqs=%u)\n", undr, reqs);
}
int main(void)
{
static const uint8_t pumps[2] = { CBL_PUMP_OTIR, CBL_PUMP_ACCEL };
static const char *const pump_names[2] = { "otir ", "accel" };
static const uint8_t freq_codes[4] = {
CBL_FREQ_7K8, CBL_FREQ_10K9, CBL_FREQ_15K6, CBL_FREQ_21K9
};
static const char *const freq_names[4] = { "7.8k ", "10.9k", "15.6k", "21.9k" };
/* формат: биты, fmt-код, stereo?, L?, R?, имя */
static const uint8_t fmt_bits[8] = { 8, 8, 8, 8, 16, 16, 16, 16 };
static const uint8_t fmt_code[8] = {
CBL_FMT_MONO8, CBL_FMT_STEREO8, CBL_FMT_STEREO8, CBL_FMT_STEREO8,
CBL_FMT_MONO16, CBL_FMT_STEREO16, CBL_FMT_STEREO16, CBL_FMT_STEREO16
};
static const uint8_t fmt_stereo[8] = { 0, 1, 1, 1, 0, 1, 1, 1 };
static const uint8_t fmt_l[8] = { 1, 1, 0, 1, 1, 1, 0, 1 };
static const uint8_t fmt_r[8] = { 1, 0, 1, 1, 1, 0, 1, 1 };
static const char *const fmt_names[8] = {
"mono8 ", "st8-L ", "st8-R ", "st8-LR ",
"mono16 ", "st16-L ", "st16-R ", "st16-LR "
};
uint16_t total = 2u * 8u * 4u;
uint16_t idx = 0;
for (uint8_t p = 0; p < 2; p++) {
for (uint8_t f = 0; f < 8; f++) {
for (uint8_t q = 0; q < 4; q++) {
idx++;
/* OTIR принципиально не может собрать 16-бит сэмпл
* (см. _cbl_open_raw.c) — cbl_open вернёт EINVAL; не тратим
* время на заведомо запрещённую комбинацию. */
if (pumps[p] == CBL_PUMP_OTIR && fmt_bits[f] == 16) {
printf("[%2u/%u] %s %s %s : skip (OTIR can't do 16-bit)\n",
idx, total, pump_names[p], fmt_names[f], freq_names[q]);
continue;
}
play_combo(idx, total,
pump_names[p], pumps[p],
fmt_names[f], fmt_code[f], fmt_bits[f],
fmt_stereo[f], fmt_l[f], fmt_r[f],
freq_names[q], freq_codes[q]);
}
}
}
puts("press any key (keyboard must work)...");
getch();
puts("cbltest done.");
return 0;
}