Проще исходного плана (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>
Регрессия от 961cfb7 (gsinit зануляет _DATA, 2026-07-04): crt0_banked
заполняет таблицу физических страниц _bank_pages ДО gsinit, а тот её
стирал — трамплины banked-вызовов читали нули и прыгали в незамапленную
страницу. Висли ВСЕ banked-программы (banked/bankedbg/banklocl/
banktest); найдено MAME-смоуком. Тот коммит перенёс crt0-приватные
_estex_* в _CODE, но _bank_pages в runtime/bank.s пропустил.
- runtime/bank.s: _bank_pages → .area _CODE (RAM, всегда замаплен —
это же условие нужно и трамплину); +16 Б _CODE у программ с bank.s
- app.mk: exe теперь зависит от runtime/*.s — правка crt0/bank.s
перелинковывает тесты без make clean (фикс иначе не подхватывался)
- эталон размеров обновлён (+16 Б у banked/bankedbg/banklocl/
banktest/openenv — size-check поймал ровно их)
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>
- 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>
- единый ленивый буфер BUFSIZ=512 на чтение и запись с
автопереключением направления (_F_DIROUT, _file_sync: запись
сбрасывается write()-ом, readahead откатывается lseek-ом)
- статическая таблица OPEN_MAX=8 слотов вместо malloc для FILE;
_fclosall через atexit — exit() сбрасывает несброшенную запись
- fread/fwrite: мелкое через буфер (memcpy), блоки >= BUFSIZ — мимо
буфера одним syscall; горячие пути fgetc/fputc и сканер строк
fgets (LDI до '\n') — на asm, SDCC на эти цепочки генерит ~90
инструкций с IX-фреймом
- новое: ungetc (1 байт через hold, работает и на stdin),
fprintf/vfprintf (vsprintf+fwrite), fflush(NULL) = все потоки
- фиксы stdio-review: fwrite ставит _F_ERROR при короткой записи
(issue 3), fgets(n=1) возвращает пустую строку (issue 4)
- замер (MAME, HDD, 100 КБ): небуферизованная оценка ~144 с →
fgetc 5 с (×29), fgets ~1 с; дизайн и отвергнутые варианты —
docs/file-buffering-design.md
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Горизонтальный скролл (Фаза 5, финал):
- Скроллим по ТИПУ, а не по длине: новый флаг IF_HSCROLL ставится только
на код и таблицы; HR и границы fence (IF_NOWRAP без IF_HSCROLL) не
двигаются. Блок едет целиком, включая строки короче 80.
- Обход бага кодогенерации SDCC z80: `if (n!=g) g=n;` пишет (n-g) вместо n
(SUB сравнения затирает A, store переиспользует испорченный A). Лечится
записью viewport_x ДО сравнения. Минимальный репродьюсер и оба описания
для трекера — в docs/bugs/sdcc-z80-cmp-store-a/ (воспроизводится на чистом
sdcc 4.5, в т.ч. с --no-peep → это кодогенератор, не peephole).
Рендеринг:
- Отступленный fence (```c внутри списка) теперь распознаётся: is_fence_raw
пропускает ведущие пробелы/табы; то же в рендере прячет строку-границу.
- Строки-разделители (HR, ровно 80) больше не участвуют в скролле.
Чистка: удалён мёртвый код (is_fence_delim, get_init_style[_raw], is_cont,
seg_flags). Makefile (mdview/mdview2): iconv UTF-8→CP866 завершается ненулевым
кодом при отбрасывании символов — игнорируем (|| true).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
libc/mem/:
• Split bank_io.c into bank_io_w3.c (existing W3 helpers, base 0xC000,
port 0xE2) and bank_io_w1.c (new mirror through W1, base 0x4000,
port 0xA2). Two .rel files so DCE picks only the needed group:
a W3-only user pulls ~70 bytes instead of all 134. W1 variants are
`--memory tiny`-only (any other mode runs code from W1 or uses W1
for the banked-code segment, swapping it crashes).
• mem_alloc.c: add CF=err checks for mem_free_block and mem_get_page
(were silently ignored), per-function docstrings on alloc/free/
get_page/info, drop the confused "wait wrong order" comment in
mem_info. Header sprinter_mem.h gets matching per-function doc.
libc/stdio/:
• Add hex_print.c (hex8/hex16/hex32, ~26 bytes) and dec_print.c
(dec8/dec16/dec32, ~170 bytes) ported from solid-c STDLIB.ASM.
Replaces the printf("%u"/"%X") wrappers in solid_helpers.c that
dragged in the 3-5 KB printf machinery.
- hex* use the classic cp 10 / sbc 0x69 / daa nibble→ASCII trick;
hex8 self-calls for the high nibble, hex16/hex32 tail-call hex8.
- dec32 is the master routine; dec8/dec16 jump into shared entry
points (__dec_entry3 / __dec_entry5). 32-bit subtract-power-of-10
keeps the high 16 bits in HL alt (shadow set).
- DISCOVERY: ESTEX PUTCHAR ($5B) on our Sprinter build preserves
the main register set + IX but CLOBBERS the shadow set
(BC'/DE'/HL'). solid-c's original code assumed otherwise and
garbled output for values ≥ 6 digits. Fix: save/restore HL alt
around the RST 10 in _dec_emit_or_skip. Documented in
memory/estex_putchar_abi.md.
• file.c: drop stdaux/stdprn (no Sprinter printer API), change
stdin/stdout/stderr fd markers to 0/-1/-2 (positive fds clash with
ESTEX OPEN return values), add TODO header pointing at v2 buffered
FILE rewrite (see docs/TODO.md for the Solid-C reference struct).
bin/sprinter-cc:
• --memory big and --memory huge now always use crt0_banked.s (was:
only with --bank flags), matching docs/memory_modes_implemented.md.
When the user has no --bank flags, generate a tiny stub with
`const uint8_t n_banks = 0;` and assemble bank.s for _bank_pages.
Without this fix, openenv with --memory big could not see the
estex_file_handle symbol exported by crt0_banked.
examples/openenv:
• Add usage of estex_file_handle to confirm the crt0_banked startup-
info is reachable. Local extern decl — keeps the symbol out of
sprinter.h since it only exists in big/huge builds.
examples/dec_test:
• New regression test covering hex8/16/32 and dec8/16/32 across the
interesting boundary values.
.gitignore: add .kilo/ (editor session cache).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>