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>
Первая версия висла на первом прерывании. Две причины (verified по
docs/samples/sprinterIntLib.asm, SIO_CTC_KEY.asm и исходникам MAME):
- DSS работает в IM 1 (обработчик 0x0038); I=0x3F — наследие Spectrum
ROM, НЕ таблица: чтение [I<<8|0xFF] давало мусор (0x00BF) и прыжок в
никуда. Чейн из трамплина теперь ВСЕГДА jp 0x0038 (interrupted-PC на
стеке = имитация RST 38); irq_remove безусловно восстанавливает IM 1
- CBL-фильтр по биту 7 порта 0xFE убран: при выключенном CBL бит
подтянут к 1 (MAME kbd_fe_r: data |= 0xE0) — каждый кадр ложно
уходил в чейн, user-handler не вызывался бы. Вернуть в Phase 2
вместе с поддержкой CBL
Попутно подтверждено: порт 0x19 = SIO-A RR0 (Z84C015), бит 0 = Rx
Available; вектора встроенной периферии SIO 0x10..0x1E / CTC 0x06
(заливка 257×H ловит любые); внешний вектор 0xFF.
irqtest: диагностика I до установки + фаза без ESTEX; прогон в MAME:
~49 Гц, клавиатура жива, 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>
- lib/Makefile: stale .rel удаляются сверкой списка модулей перед
упаковкой; штамп build/.modules триггерит перелинковку при любом
изменении состава исходников (при смене списка архив сносится —
на exFAT гранулярность mtime грубая, сравнение времён ненадёжно)
- top-level TESTS: все каталоги tests/ теперь собираются make all
(43 программы; banktest переименован из banked.exe — конфликт имён
с tests/banked); mdview2 добавлен в APPS
- размерный регресс: toolchain/size_check.py сверяет _CODE всех
программ с docs/size_baseline.tsv; make size-check / size-baseline
- заголовки: контракт затенения SDCC задокументирован в
docs/libc-headers.md; новый string.h (include_next + strlwr/strupr);
из sprinter_compat.h убраны макросы min/max — конфликтовали с
функциями из stdlib.h, и в Solid-C min/max тоже функции
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New examples/mdview2 — render-cache version of mdview: each logical
line is rendered into an EMM (char,attr) buffer once during file load
(interleaved with index_lines()), then scrolling draws straight from
the cache via ESTEX WINREST, with no re-parsing or fb() access in the
steady state. Horizontal scroll still uses the old live-render path
(Phase 5, not yet migrated).
Format and budget were derisked empirically first (tests/winrest):
confirmed ESTEX WINCOPY/WINREST buffer layout and measured EMM free
space, both folded into the implementation.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
hello2 — stdio vs conio output API comparison (fast/no-attr vs
slower/attributed), also exercises textcolor/textbackground.
simple — packed 2-bit style array bit-twiddling smoke test.
banktest — __banked function calls across two explicit banks in huge
memory mode.
hello2.c updated to call the renamed gettextmode/settextmode (see the
conio rename in 5e5e70d).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
palette.h/video/palette.c wrap BIOS $A4 PIC_SET_PAL/PIC_GET_PAL and $A6
SET_PAL_INIT for the 8 palette pages (0-3 graphics, 4-7 text planes).
gfx_palette.c and conio/text_palette.c are thin per-domain wrappers;
text_palette.c maps the text "plane" 0..3 (paper/ink/blink-paper/
blink-ink) onto BIOS pages 4..7. Covered by tests/text_palette.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
_get_cursor/_set_cursor now use BIOS GetCursor/SetCursor (RST 8, 08Eh/084h)
instead of ESTEX CURSOR/LOCATE; cursor position packed into a two_bytes
union. get_videotextmode/set_videotextmode renamed to gettextmode/settextmode.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Direct __naked z80 wrappers over BIOS LP_PRINT_*/LP_SET_PLACE/LP_GET_PLACE
(RST 8, opcodes 081h-08Eh) for fast text rendering — a single BIOS call
replaces a wrchar() loop for constant-attribute/constant-character runs.
Covered by tests/bios_text.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Split tests/ (libc feature tests) and examples/ (real apps); shared
app.mk in repo root, was examples/example.mk
- libc/io/* split into libc/{conio,env,errno,file,mouse,string,sys,
time,video}/ — clearer module boundaries
- New examples/mdview/: markdown viewer (Phases 1-5 + light nested
lists). Headers (H1-H4), HR, ulist/olist/quote with nesting via
leading spaces, fenced code blocks, inline emphasis (bold/italic/
underscore/code), wrap/unwrap mode with soft wrap (F2), horizontal
pan (← →) with '>' truncation indicator
- libc additions: scroll() in conio (ESTEX SCROLL), strlwr/strupr,
gets() test
- Makefile updates across tests/ for the new shared app.mk path
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>