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>
- irq_ctc_install(handler, div2, div3) / irq_ctc_remove: канал 2 CTC
делит видеотакт 875 кГц (1 тик = 1 знакоместо), канал 3 считает от
него и прерывает; f = 875000/(div2*div3), div 0 = 256. Пресет
IRQ_CTC_VSYNC_DIV2/3 = 112*160 — точное начало кадра ~48.8 Гц БЕЗ
примеси клавиатуры (вектор 0x06 отделён от общего 0xFF)
- CTC-трамплин: полный сейв -> handler -> EI/RETI; RETI обязателен
(daisy chain Z84C015 снимает IUS только по опкоду RETI); к DSS не
чейнится — личное прерывание
- общая IM2-таблица под счётчиком ссылок (_irq_table.c): кадровый и
CTC-хендлеры независимы, последний unref возвращает I/IM 1;
atexit-уборка глушит CTC обязательно (иначе кГц-прерывания душат
шелл после выхода)
- порты/слова по docs/samples: CH0=0x10/CH2=0x12/CH3=0x13,
0x57/0xD7/вектор в CH0, стоп 0x03
- irqtest: CTC-vsync параллельно с кадровым + произвольная частота;
MAME: frame 48 Гц, ctc(vsync) 49 Гц (parallel frame жив),
ctc(50x50) 350 Гц точно по формуле, remove/выход чистые
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Проще исходного плана (docs/im2_isr_design.md обновлён): отдельный
--memory im2 не понадобился.
- <irq.h>: irq_install(handler) — вызов ~50 Гц только на КАДРОВЫХ
прерываниях (клавиатура bit0:0x19 и CBL bit7:0xFE отфильтровываются);
штатный обработчик DSS чейнится ВСЕГДА (SMC-jp, адрес из старой
IM2-таблицы по регистру I) — клавиатура/SYSTIME/мышь живут
- вектор-таблица: 513 Б BSS + runtime-выравнивание; Sprinter шлёт
только вектор 0xFF, поэтому jp-заглушка лежит внутри самой таблицы
по смещению H — без linker-областей и правок crt0
- трамплин: полный сейв обоих наборов+IX/IY вокруг user-handler'а
(ex af,af' как .db 0x08 — апостроф ломает препроцессор SDCC)
- tiny/big: работает (код в W2); small/huge: EINVAL по проверке
адресов; irq_remove идемпотентен и висит на atexit (выход без
снятия = I в памяти умершего процесса = крах шелла); old_I==0 → IM1
- tests/irqtest: тики за 3 с против time() (~50 Гц), живая клавиатура
под handler'ом, остановка после remove, чистый выход
- docs: im2_isr_design (статус+дельты), libc-reference (<irq.h>), TODO
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- docs/libc-reference.md — сводный справочник по всем заголовкам:
сигнатуры + описание + особенности ABI и квирки
- CLAUDE.md — сборка/проверка (make, size-check, MAME-workflow),
правила libc (1 функция = 1 модуль, internal _-модули, русские
комментарии, без = 0, asm-связки), ABI-шпаргалка, структура репо
- docs/TODO.md переписан: открытые задачи наверху (MAME/железо,
auto-banking, v2: BGI/IM2/audio, gfx-расширения, Port_Y),
закрытые этапы 5-10 сжаты в «Историю»; снят протухший пункт
«FILE API rewrite для v2» (сделан в v1), fprintf/fscanf и др.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>