Files
Sprinter-SDCC/docs/TODO.md
T
snark13 3d586af031 libc/kbd: raw-клавиатура — вычерпывание FIFO + селективный wipe модификаторов
- kbd_raw_sync: цикл вычерпывания SIO FIFO (не 1 байт/прерывание) —
  фикс залипания клавиш; overrun-wipe сбрасывает только пострадавшие
  клавиши, не модификаторы (typematic их не перечитывает).
- Гайд docs/kbd-games.md; заметка о Rx-overrun в docs/TODO.md; справочник
  скан-кодов в docs/libc-reference.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 19:38:12 +03:00

359 lines
27 KiB
Markdown
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.
# TODO / Roadmap
Открытые задачи в порядке убывания приоритета; закрытые этапы — в
«Истории» внизу. Текущий срез libc-работ: docs/libc-roadmap.md.
## Ближайшее
Порядок реализации связки: **сначала цепочка irq, потом FPS-делитель
поверх неё** (делитель — просто один слот цепи; так снимается EBUSY
на сосуществование со своим тиком приложения).
- [x] **(1) irq: цепочка кадровых обработчиков** — СДЕЛАНО 2026-07-15.
Массив `isr_t _irq_chain[4]` + `_irq_chain_n` в W2-data (заменили
`_irq_user`); трамплин `tr_frame` проходит слоты (один тяжёлый сейв
на всю цепь, пустой слот пропускается); API `irq_chain_add`
(0/-1+ENOMEM), `irq_chain_remove(h)`; `irq_install` → обёртка
chain_add (EBUSY исчез), `irq_remove()` рвёт всю цепь; refcount
таблицы на первом/последнем слоте; мутации под IRQ_DISABLE.
All-modes W1-remap: трамплин копируется в W2 (`_irq_tramp_w2buf`,
copy-safe — только jr/djnz + литерал jp 0x0038), вокруг вызова
восстанавливает базовую W1-страницу (`_irq_app_w1_page` = IN 0xA2
при install). ПРОВЕРЕНО в MAME (tests/irqtest, 2 хендлера):
tiny/big/huge — chain 97=97 → remove h2 → 48/0; **huge = код в W1,
remap работает** (цепочка тикает синхронно). Дизайн:
docs/im2_isr_design.md.
FOLLOW-UP: (а) CTC-трамплин пока требует код в W2 → в small/huge
`irq_ctc_install` = EINVAL (нужна такая же W2-копия _irq_ctc_tramp);
(б) small для мелких программ (BSS в W1) → irq_install EINVAL
(safe; снять резервом area _IM2 в верхней W2).
- [x] **(2) FPS-делитель (frame pacing)** — СДЕЛАНО 2026-07-15.
gfx_set_fps_div(n): логический кадр = ровно n кадровых интервалов
(1=50/2=25/3=~16.7 fps); при переполнении слота — выравнивание на
ближайший фронт (без дрейфа фазы). Счётчик кадров _gfx_frame_tick
через irq_chain_add(_gfx_frame_isr) — свой слот цепи (n<=1 снимает
его через irq_chain_remove, не трогая хендлер приложения).
gfx_wait_vsync: ветка n>=2 (счётчик+halt) перед лучевым поллингом.
Файлы: common/_gfx_fps_state.c, _gfx_frame_isr.c, gfx_set_fps_div.c,
правка gfx_wait_vsync.c. Работает tiny/big/huge (цепочка all-modes);
small для мелких программ = EINVAL. ПРОВЕРЕНО MAME (tests/fpsdiv):
n=1/2/3 → 20/40/60 кадров на 20 wait'ов (drift=0); n=2 с рендер-
заглушкой ~1 кадр → период держится 2 (поглощение перерасхода,
наивный путь дал бы ~60); huge идентично; small = EINVAL graceful.
ПЛАН: docs/sprite-api-design.md §9е.
- [ ] **П6/железо**: MAME-смоук всех тестов после libc-сплита (conio,
ptime, stattest, mouse, gfx_demo/gfx_d16/gfx_text/gfx_mous —
трогался asm акселератора); затем прогон на реальном Sprinter
(mdview2 + FILE* v2 + fdmax — подтвердить лимит 8 манипуляторов
и зависание DSS на 9-м OPEN)
- [ ] Мигрировать оставшиеся examples на sprinter-cc вместо ручных
Makefile (косметика)
- [ ] check_banks.py: разбивка size = code + const + bss per bank
## Auto-banking (memory/banking_roadmap.md)
Phase 1 — file-level bin-packing (`toolchain/auto_bank.py`) — когда
проект перерастёт ~30 KB кода: парсинг размеров из .rel/.map,
first-fit-decreasing, уважение `#pragma codeseg BANKn`, перелинковка,
печать плана. Phase 2-5 (rebalance, banks.toml, function-level) —
по потребности.
## ОБЯЗАТЕЛЬНОЕ ДЛЯ V2
### Turbo-C-style graphics API (BGI-like) — Фаза 1 ГОТОВА (256, 2026-07-08)
Архитектура: `graphics.h` — mode-agnostic слой (libc/bgi/*.c в
sprinter.lib), режим задаёт driver-архив; выбор линковкой через
`sprinter-cc --gfx 256` (16 — позже, тем же leaf-split'ом; см.
memory/bgi_two_lib_design).
**Фаза 1 (256, реализовано и проверено в MAME — tests/bgitest):**
initgraph/closegraph/graphresult/cleardevice, setcolor/getcolor/
setbkcolor/getbkcolor, getmaxx/getmaxy/getmaxcolor, putpixel/getpixel,
moveto/moverel/getx/gety, line/lineto/linerel, rectangle, bar, circle,
outtext/outtextxy. initgraph грузит EGA-палитру 0..15. Пакетные
примитивы (circle) — одна W3-скобка на весь примитив (raw-плот), иначе
на порядок медленнее.
**Фаза 2a/2b/2c ГОТОВЫ (2026-07-08, проверено в MAME tests/bgitest):**
- 2a: arc, ellipse, drawpoly (Q7-тригонометрия, БЕЗ 32-бит).
- 2b: setfillstyle/getfillsettings + 10 паттернов Borland, bar (с
паттерном), bar3d, fillpoly (scanline min/max), fillellipse (isqrt).
- 2c: floodfill (scanline span; медленный — self-bracket чтение, но
корректный), pieslice, sector.
**Фаза 2d-1/2/3 ГОТОВЫ (2026-07-08, MAME tests/bgitest):**
- 2d-1: getimage/putimage/imagesize (COPY/XOR/OR/AND/NOT_PUT), raw-блит
в одной W3-скобке (_gfx_getpixel256_raw).
- 2d-2: setlinestyle/getlinesettings (SOLID/DOTTED/CENTER/DASHED/USERBIT
+ NORM/THICK) — line/lineto/linerel/rectangle/drawpoly.
- 2d-3: settextstyle/gettextsettings/textwidth/textheight — масштаб
1..10, HORIZ/VERT, прозрачный фон (свой scaled-рендер поверх 8×8).
**Фаза 2d (осталось):** setviewport/clearviewport + клиппинг (инвазивно —
трогает все примитивы), settextjustify, setaspectratio (пиксели 320×256
неквадратные — круги визуально эллиптичны), setfillpattern (USER_FILL),
setactivepage/setvisualpage (2 страницы в gfx уже есть). Оптимизации:
floodfill на raw-чтении; ellipse/arc в одной W3-сессии. Потом drv16 +
sprinter_gfx16.lib (--gfx 16).
Acceptance: типичная BGI-программа переносится без существенных
правок. Референс: Turbo C 2.x BGIDEMO.
### IM2 Interrupt Handlers — Phase 1 ГОТОВ (2026-07-06, libc/irq, tests/irqtest; Phase 2: CBL/ISA/цепочки)
User-ISR через IM 2 — timer ticks, музыка (AY/COVOX), real-time
игры, async input. Phase 1 реализован БЕЗ отдельного memory mode:
libc/irq (<irq.h>: irq_install/irq_remove), таблица в BSS с runtime-
выравниванием, jp-заглушка внутри таблицы, чейн к DSS всегда;
работает в tiny/big, в small/huge — EINVAL. Детали и отличия от
исходного плана: docs/im2_isr_design.md.
### Прочее v2
- [ ] **Audio API** — AY-3-8910 + COVOX (требует IM2)
- [ ] **ISA-8 slot support** — ZX-Bus карты (требует IM2)
## GFX: расширения по accelerator_doc.txt
Quick wins:
- [ ] block-size через `LD A,(nn)` вместо SMC (док разрешает LD A,(HL/BC/DE))
- [ ] кэширование block-size между burst'ами (accel помнит размер)
Новые возможности:
- [x] ~~пакетное чтение/запись массива байт через акселератор~~
сделано 2026-07-11 (Фаза A спрайтового дизайна,
docs/sprite-api-design.md): leaf `_bgi_copy_rows_raw` (accel
block-copy LD L,L, до 256 байт/burst, размер блока армируется
один раз) + ядро `gfx_blit_part` (клиппинг, полосы ≤256);
putimage(COPY_PUT) и getimage переведены (регресс tests/bgi_img
1:1 с per-pixel эталоном, подрежимы банков на accel-пути —
tests/gfxbanks).
- [ ] **вертикальный copy-leaf `_bgi_copy_cols_raw`** (режим LD A,A —
вертикальная копия, Port_Y двигается сам как у vfill). Анализ
2026-07-12: для СПРАЙТОВ требует column-major хранения (читать
линейный буфер вертикально нельзя — Port_Y не действует вне
видеоокна; референс docs/samples/balls пишет колонками и потому
выводит спрайт ТРАНСПОНИРОВАННЫМ — незаметно только на
симметричном шаре) → формат несовместим с getimage, не делать.
А вот **вертикальный HEAL формат-независим** (экран→экран) и
выгоден для узких высоких областей: L-полоска 2×16 при
горизонтальном движении спрайта = 2 burst'а вместо 16 (8×
меньше оверхеда); выбор ориентации по форме — как в
_gfx_rectfill256. Нюанс: между read и write колонки Port_Y
надо вернуть на y0 (стоп → OUT → ре-арм, ~20Т/колонку).
Делать вместе с L-strip оптимизацией heal.
- [ ] **паттерны через акселератор + FF-прозрачность** (идея 2026-07-11):
всё, что сейчас рисуется по-пиксельно из-за «дырок», можно гнать
burst'ами через банк 0x58 — дырки паттерна кодируются 0xFF и
отбрасываются железом на записи:
- стилизованные линии (_bgi_styled_line: DOTTED/DASHED/CENTER/
USERBIT сейчас per-pixel): построить 16-байтовый шаблон строки
из 16-бит маски (бит=цвет, 0=0xFF) и повторять accel-copy;
- fill-паттерны (_bgi_fill_span, LTSLASH_FILL и пр.): 8-байтовые
строки-шаблоны 8×8 паттерна тем же способом;
- ВНИМАНИЕ: на MAME 0.283 запись FF через 0x58 портит теневое
ОЗУ (частичный скип, см. sprite-api-design «Результаты Фазы 0»)
— включать после подтверждения полного подавления на железе,
либо через 0x5C + пере-heal.
- [x] ~~gfx_blit / putsprite / gfx_heal / movesprite~~ — Фаза B
сделана 2026-07-11 (docs/sprite-api-design.md §3): GFX_BANK_*
константы + gfx_blit/gfx_blit_part/gfx_heal в gfx.h,
putsprite/movesprite в graphics.h; проверено tests/sprites
(прозрачность, клиппинг 4 краёв, heal src==dst, чистый след
movesprite, атлас). Осталась Фаза C — пример-курсор.
- [x] ~~**managed-движок спрайтов v2**~~ — СДЕЛАН 2026-07-12/13
(sprite.h: retained-модель + batch-пасс, см. пункт batch-пасса
ниже) и расширен: W0-атласы (.atl, atlas_load/atlas_sprite_init,
ISR-стаб — §9в), авто-анимация кадровая + tween (sprite_anim/
sprite_moveto/sprite_update тикер — §9г), gfx_pal_sync/fload/
fsave. Замерен бюджет: ~26К тактов на спрайт 16×16, лимит 14
спрайтов на стабильные 48 fps (§9д); демо examples/space,
examples/rpgwalk (+rpgprof — профайлер кадра).
- [ ] `gfx_xor_rect` / `gfx_or_rect` / `gfx_and_rect` / `gfx_invert_rect`
- [ ] шрифты ≠ 8×8: gfx_set_font_data(ptr,w,h,advance), proportional,
8×16/16×16, отдельный font_id API; font-quad для 640×256
(per-cell палитра через дескрипторы 0x0300..0x039F)
Оптимизации (не сейчас):
- [x] ~~stride-арифметика в _bgi_copy_rows_raw — вон из горячего
цикла~~ — сделано 2026-07-12 (профиль examples/balls vs
docs/samples/balls): универсальный leaf — SMC-патч страйдов в
8-битные add/adc-цепочки при входе (140Т → 73Т/строку, BC/push/
pop/ex ушли); heal — отдельный `_bgi_heal_rows_raw` без адресной
арифметики вообще (77Т/строку против 204Т; референсный уровень).
Шар 16×16: ~9.6 → ~6.6 кТ (heal 16×173 + блит 16×236). Регресс:
blitw/bgi_img/sprites/balls — 1:1. Остатки разрыва с asm-
референсом — C-обвязка вызовов (batch-пасс, см. ниже) и спец-
blit-leaf (dstride 0: 30Т/строку — делать по замеру).
- [x] ~~**batch-пасс для спрайтов** (движок v2, §9.1)~~ — СДЕЛАНО
2026-07-12/13: движок v2 (sprite.h: sprite_init/update/flip,
retained per-page dirty), два прохода heal→blit под одной
W3-скобкой/банком, спец-leaf'ы `_bgi_blit_rows_raw` (dstride 0,
SMC sstride) и `_bgi_heal_rows_raw`, лин-ядра ≤64×64, ОДИН DI на
спрайт (санкция 2026-07-12), noclip-ядра + funcptr-диспетч
`_gfx_blit_fn`/`_gfx_heal_fn` (см. sprite-api-design §9б).
Замер dev-MAME 16 шаров uncapped: clip 50 → noclip 60-61 fps
(+20-22%); с vsync-капом кадр < 20 мс. Исходный уровень был
24 fps. (Диагноз разрыва с asm-референсом — per-call C-обвязка,
не W3-скобка — и путь оптимизации описаны в sprite-api-design
§9-9б.) На железе перепроверить снятие src[0]-фикса (§9а).
- [ ] клип fast-path «прямоугольник целиком на экране» в gfx_blit_part
(4 сравнения вместо полного пути) — делается в _gfx_blit_full/
_gfx_heal_full вместе с batch-пассом.
- [ ] размер: общий clip-хелпер для gfx_blit_part (528 Б) и gfx_heal
(362 Б) — клиппинг сейчас продублирован; кандидат −300..400 Б.
- [ ] gfx_line через accel для пологих диагоналей (runs ≥ 4-5 px)
- [ ] композитные примитивы с одним W3-swap на операцию
- [ ] ~~**спрайт-анимация: heal только открывшейся L-полоски**~~
ОТВЕРГНУТО 2026-07-12 для ПРОЗРАЧНЫХ спрайтов (наш случай).
L-полоска (bbox старой позиции минус новой) корректна ТОЛЬКО для
непрозрачного full-box спрайта: тогда зону перекрытия целиком
перекрашивает новый блит. У прозрачного спрайта (0xFF через
0x5C) в перекрытии дырки нового кадра НЕ перекрывают старые
непрозрачные пиксели → на хвосте остаётся «полумесяц» старого
изображения ВНУТРИ bbox-перекрытия, куда L-полоска не достаёт.
Точный «новооткрытый» набор = old_opaque AND NOT new_opaque —
это heal-с-маской по форме, а не по bbox, на Z80 дороже самого
heal. Подтверждено чтением референса docs/samples/balls:
restore_bg лечит ПОЛНЫЙ 16×16 (ld b,16 + 16 байт/строку),
L-полоску не использует. Итог: heal остаётся full-box; экономия
только через batch-пасс (амортизация обвязки), не через L-полоску.
L-полоска годна лишь для непрозрачных тайлов фона — не спрайтов.
Дисциплина «heal ВСЕ → блит ВСЕ» по-прежнему обязательна (слияние
heal+блит per-sprite выкусывает соседа на перекрытии — проверено
examples/balls 2026-07-11).
- [ ] **span-примитивы для узких прямоугольников**: при узкой стороне
≤ 8 линий chunked-rectfill проигрывает простому циклу
_bgi_hspan_raw/_bgi_vspan_raw (подготовка+precompute ~340Т уходят
впустую; break-even n≈9 — docs/accel-fill-budget.md). Варианты:
fast-path в диспетчере _gfx_rectfill256 («узкая сторона ≤ 8 →
цикл span'ов») или просто задокументировать рецепт для
пользователя. Делать по результатам профиля — если узкие
прямоугольники реально встречаются в горячем коде.
## Прочий backlog
- [ ] factoring parse_argv из crt0/crt0_banked в общий argv.s
- [ ] `restore SP on EXIT` (паттерн z88dk +pps) — проверить нужность
- [x] ~~CI: MAME с -aviwrite для screenshot-сравнения без человека~~
`toolchain/mame_interactive.py`: авто-запуск .exe вводом с
эмуляцией клавиатуры + Lua-таймер для скриншотов/выхода; сравнение
визуальное (Claude читает скриншот), не автоматический diff.
Полный справочник: docs/mame-autotest.md.
- [ ] linker duplicate-symbol warnings: сейчас фильтруются в
sprinter-cc (наши overrides _puts/___sdcc_heap/_asctime/…);
радикально — --nostdlib с ручным списком модулей z80.lib
- [ ] ZX Spectrum-совместимый target; ZX-Bus драйверы; PGO-tools
## Проверить на реальном железе
- [ ] **Port_Y banking trick** (адреса 0xC000+0x400*N → строки
Y..Y+15): в MAME 0.283 НЕ работает (2026-06-01). На железе:
dual-write тест → если работает, кэшировать Port_Y в putpixel
(~8× меньше OUT для Брезенхэма); если нет — вычистить из доков.
- [ ] **Подрежимы видеостраниц #50..#5F** (tests/gfxbanks +
tests/sprites, дизайн docs/sprite-api-design.md): перепрогнать
на железе и сверить со скриншотами MAME. Три открытых вопроса: (1) 0x58 — полное
подавление FF-записи (док/master-MAME) или FF протекает в
теневое ОЗУ (MAME 0.283)? (2) FF в VRAM — цвет 255 (MAME) или
display-подстановка фона из ОЗУ (тогда дешёвое стирание
FF-заливкой, tests/fferase)? (3) accel-путь = CPU-пути (в ПЛМ
пути физически разные).
- [ ] fdmax: лимит манипуляторов и зависание 9-го OPEN — MAME vs железо.
- [ ] CBL: щелчок перед первым проигрыванием звука за сессию (tests/
cbltest, tests/cblwav) — воспроизводится ТОЛЬКО на первом запуске
программы за сессию MAME, не зависит от содержимого потока
(тон/тишина/речь одинаково). Похоже на разовый прогрев звуковой
подсистемы MAME при первой активации канала — на реальном железе
скорее всего отсутствует. Проверить и закрыть либо описать как
реальный аппаратный эффект.
- [ ] **CBL: помехи в звуке при движении мыши** (tests/cblstream,
2026-07-07) — при потоковом воспроизведении движение мыши даёт
слышимые артефакты. Гипотеза (НЕ подтверждена): мышь может
делить SIO-канал/детект-бит с клавиатурой (порт 0x19 бит 0,
см. _irq_tramp.c), и трамплин классифицирует байты мыши как
«клавиатура» → сразу chain на DSS (0x0038), пропуская проверку
CBL-хука для ЭТОГО прерывания — при движении мыши часть тиков,
которые должны были обслужить CBL-насос, уходят мимо, кольцо
недоливается. Нужно исследование (MAME-дамп/лог трамплина).
- [ ] **kbd_raw: частые Rx-overrun в MAME при тапах** (roomtest,
2026-07-22) — замеры watchpoint-счётчиками: при стабильном
удержании клавиши overrun'ов ноль, но каждый быстрый тап (5 байт:
E0 74 + E0 F0 74 при FIFO 3) даёт overrun. Подозрение: dev-MAME
не эмулирует прерывание SIO на каждый принятый байт (байты
вычерпываются только кадровым IRQ 50 Гц) и/или подаёт пачку без
реальных ~1 мс/байт — на железе per-byte INT должен делать
overrun'ы редкостью. Проверить mame/sources/MAME/src/mame/
sinclair/sprinter.cpp (путь байта клавиатуры → INT) и при
желании поправить dev-MAME. Recovery-политика уже терпима к
частым overrun'ам (селективный wipe, docs/kbd-games.md), но
аккорд «держу →, тапнул ↑» остаётся уязвим.
## Known quirks (зафиксированы, обходы в libc)
- ESTEX $46 ENV: A=0 это NOT FOUND (док врёт) — memory/sprinter_platform
- ESTEX WRITE $14: на успехе DE=0, не счётчик; успех = CF=0 & A=0 —
memory/estex_write_de_quirk
- DSS: 8 манипуляторов, 9-й OPEN вешает систему; _fd_guard в libc —
memory/dss_fd_limit
- SDCC z80 `if(n!=g)g=n;` пишет (n-g) — memory/sdcc_z80_cmp_store_a_bug
---
# История — закрытые этапы
## Этап 10 — libc: сплит + FILE v2 + Solid-C (2026-07-05/06) ✅
Полный план/итоги: docs/libc-roadmap.md. Кратко:
- вся libc разложена «1 функция = 1 модуль» (~250 модулей, wildcard-
сборка, DCE на уровне файлов): gfx_text 4.4 КБ, timedir/ls/stattest
−3 КБ и т.д.; правила asm-связок: docs/libc-split-asm-cases.md
- **FILE* v2 (B+)**: ленивый буфер 512 на чтение/запись с
автопереключением, таблица OPEN_MAX=8, flush-on-exit, ungetc,
fprintf/vfprintf, fdopen/freopen/fclosall/fgetpos/fsetpos; горячие
пути fgetc/fputc/fgets на asm (fgets 100 КБ: 144с unbuffered-оценка
→ ~1 с). Дизайн: docs/file-buffering-design.md
- **scanf/fscanf/sscanf** — своё C-ядро (в SDCC z80 нет)
- **Solid-C совместимость закрыта**: <dos.h> (даты/диски/absread),
errno-алиасы, <sprinter_solid.h> — docs/solid_c_compatibility.md
- гигиена: stale .rel чистка, все 43 теста в make all, размерный
регресс (make size-check), контракт заголовков docs/libc-headers.md
- справочник API: docs/libc-reference.md
## Этап 9 — memory modes (tiny/small/big/huge/manual) ✅ 2026-05-30
`--memory MODE` в sprinter-cc; crt0-семейство (crt0/minimal/small/
banked); small: ESTEX GETMEM+SETWIN2 до gsinit, auto-detect W2 по
порту 0xC2; big/huge: параметризация crt0_banked/bank.s через
BANK_W1; --debug, --stack-size. Детали: memory/memory_modes_
implemented, memory/sprinter_memory_modes.
Дизайн-решения: одна sprinter.lib на все режимы (DCE per-member);
gfx.lib отдельно не нужен; libc_banked + sprinter_home.lib — идея
на потом (триггер: HOME забит user-кодом).
## Этап 8 — графика ✅
320×256×256 + 640×256×16, акселератор (Fill h/v, SMC block-size),
Брезенхэм, bitmap font (WIN_GET_ZG, interleaved), gfx_text.
memory/sprinter_graphics*, sprinter_accelerator, sprinter_font_format.
## Bank-local data ✅
--codeseg/--constseg/--dataseg BANKn + mkexe -p 0; фикс трамплина
(pop bc/out (c),b — сохраняет A); malloc из банка прозрачен (heap в
W2). memory/bank_local_data_pattern.
## Этапы 5-7 и ранняя libc ✅
- malloc/free (SDCC + runtime/heap.s в W2), page allocator
(mem_alloc_pages, ESTEX $3C-$3E + BIOS $C4), bank_read/bank_write
- crt0 argv-парсинг (IX-prefix, CP/M-space quirk, APPINFO basename),
sprinter-cc wrapper со всеми опциями
- errno+strerror/perror, open state-machine, atexit, setjmp/longjmp,
sleep, ENV API ($46), ffirst/fnext, getdatetime/setdatetime,
chdir/getcwd/mkdir/rmdir, conio (полный), mouse (RST 30h, 14 ф-й),
POSIX time API, sys/stat, assert
- text I/O split (stdio fast / conio attr) — memory/text_output_api_split
- SDCC stdlib НЕ переписываем — memory/sdcc_stdlib_works