libc: сплит «1 функция = 1 модуль» — вся библиотека, wildcard-сборка
- bios/conio/env/errno/gfx/io/mem/mouse/stdio/stdlib/string/sys/time/ video разложены по модулям: общие статики и helpers — в internal _-модулях (_conio.h/_mouse.h/_gfx.h/_palette.h/_atexit.h/_time.h) - lib/Makefile: LIBC_C = wildcard libc/*/*.c — гранулярность файлов = гранулярность DCE линкера - эффект _CODE: gfx_text 6986→2568 Б, gfx_mous −1745, gfx_demo/d16 −542; ранее timedir −3270, ls −3098, stattest −2995 - комментарии оставшихся модулей переведены на русский; puts: убран мёртвый pchars; videomode_raw разложен на get/set - docs/libc-split-asm-cases.md — правила asm-связок между модулями; docs/libc-roadmap.md — план этапа - восстановлен examples/mdview/SAMPLE.MD (нужен make floppy) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
# libc — план работ (на рассмотрение, 2026-07-06)
|
||||
|
||||
Анализ после сплита четвёрки лидеров (time/stat/file/conio, итог −19.6 КБ
|
||||
суммарно по _CODE приложений). Ниже — что ещё стоит сделать, по приоритетам.
|
||||
|
||||
---
|
||||
|
||||
## П1. Досплит остальной libc — СДЕЛАНО 2026-07-06
|
||||
|
||||
Всё из таблицы ниже посплитано (кроме dec_print — осознанно оставлен).
|
||||
gfx: 40 модулей (внутренний заголовок `libc/gfx/_gfx.h`, скретчи в
|
||||
data-модулях `_gfx_state/_gfx_w3_state/_gfx_acc256/_gfx_g16_state/
|
||||
_gfx_font_state`, helpers `_gfx_hfill256/_gfx_hfill16/_gfx_rmw16/
|
||||
_gfx_text16`); video/palette → 7 модулей + `_palette.h`;
|
||||
conio/text_palette → 5 модулей. Эффект (_CODE, Б):
|
||||
gfx_demo 3769→3227, gfx_d16 3869→3327, gfx_text 6986→2568,
|
||||
gfx_mous 7287→5542. Не-gfx тесты не изменились.
|
||||
|
||||
Правило было: 1 публичная функция = 1 модуль, state/helpers — в отдельные
|
||||
internal-модули, комментарии на русском, без `= 0`.
|
||||
|
||||
| Файл | Ф-ий | _CODE | Замечания |
|
||||
|---|---|---|---|
|
||||
| gfx/gfx_256.c + gfx_16.c | 13+13 | 1277+1159 Б | самый жирный кусок; резать по примитивам (putpixel/line/hline/vline/rect/fill/clear/text). Учесть SMC-паттерны акселератора и кэш Port_Y — state в data-модули |
|
||||
| gfx/gfx_raw_16 / raw_256 / raw_common / core / palette / font / text_* | ~40 | ~2.7 КБ | вместе с предыдущим — весь gfx |
|
||||
| mouse/mouse.c | 16 | 308 Б | state mb_*/mc_* → data-модули |
|
||||
| bios/text.c | 14 | 264 Б | все `__naked`, сплит чистый |
|
||||
| io/open.c | 3+3 | 216 Б | open/creat/close; asm-хелперы `_estex_*_raw` у единственных потребителей |
|
||||
| errno/errno.c | 2 | 680 Б | strerror + таблица строк неразделимы (один модуль); perror — отдельно, зовёт strerror |
|
||||
| video/palette.c | 6 | 348 Б | |
|
||||
| conio/text_palette.c | 5 | 140 Б | |
|
||||
| io/read.c | 2 | 60 Б | read + write — обязательно врозь (write-only приложения) |
|
||||
| io/fsdir.c | 4 | 80 Б | mkdir/rmdir/chdir/getcwd |
|
||||
| env/env.c | 3 | 78 Б | getenv/putenv/sysenv + общий env_buf → data-модуль |
|
||||
| mem/mem_bios, mem_estex, bank_io_w1/w3 | 3–4 каждый | ~430 Б | |
|
||||
| sys/atexit.c | 3 | 106 Б | atexit/exit/_exit; общий стек хендлеров → data-модуль (exit тянется всегда из crt0, выигрыш небольшой но правильный) |
|
||||
| io/dir.c | 2 | 53 Б | ffirst/fnext |
|
||||
| stdlib/minmax.c | 2 | 27 Б | min/max врозь |
|
||||
| stdio/hex_print.c | 3 | 38 Б | сплит чистый (call/jp по именам) — см. docs/libc-split-asm-cases.md |
|
||||
| stdio/dec_print.c | 3 | 176 Б | НЕ резать (общее тело); открытое решение: вариант «3 независимых цикла» — п. отложен |
|
||||
|
||||
Ожидаемый эффект: графические приложения −1–2 КБ, mouse/BIOS-text — сотни байт.
|
||||
|
||||
## П2. FILE* — отложенные баги и недостающее
|
||||
|
||||
Из шапки бывшего file.c («PROVISIONAL», stdio-review issues 3/4/5):
|
||||
|
||||
- [x] fwrite: короткая запись ставит _F_ERROR (issue 3) — сделано 2026-07-06
|
||||
- [x] fgets(n=1): возвращает пустую строку по стандарту (issue 4) — сделано 2026-07-06
|
||||
- [x] mode_to_flags: проверено — парсер сканирует весь хвост режима, «rb+» работает (issue 5, уже был исправлен)
|
||||
- [x] **fprintf/vfprintf** — сделаны 2026-07-06 (vsprintf в статический 256-байтовый буфер + fwrite)
|
||||
- [x] ungetc — 1-байтный putback через поле hold; работает и на stdin — сделано 2026-07-06
|
||||
- [x] Буферизация FILE v2 — **реализован вариант B+** (2026-07-06): единый ленивый буфер BUFSIZ=512 на чтение и запись с автопереключением направления, статическая таблица OPEN_MAX=8 слотов, _fclosall через atexit, fflush(NULL) = все потоки. Дизайн: docs/file-buffering-design.md. Цена: filetest (использует всё) 7411→9929 Б _CODE; не-FILE программы не платят ничего. **Ждёт MAME-прогона: filetest, fdmax (лимит DSS), fbench (замер скорости)**
|
||||
- [ ] fdopen/freopen/fclosall/fgetpos/fsetpos — по мере надобности (Solid-C категория C)
|
||||
|
||||
## П3. Solid-C совместимость — остатки (docs/solid_c_compatibility.md)
|
||||
|
||||
Phase 1 (алиасы) фактически закрыт — home/inp/outp/enable/disable, ms_*,
|
||||
getc/putc, min/max, dec*/hex*, strlwr/strupr, cgets есть. Осталось:
|
||||
|
||||
- [ ] getdisk()/setdisk() — ESTEX $02 CURDISK / $01 CHDISK
|
||||
- [ ] getdate/gettime/setdate/settime — обёртки над getdatetime/setdatetime
|
||||
- [ ] seek/tell/ltell, setmem/movmem, isascii, abort(), div(), remove→unlink, _ffirst
|
||||
- [ ] errno-константы Solid-C (EZERO/EINVFNC/…) как алиасы в errno.h
|
||||
- [ ] `<sprinter_solid.h>` — зонтичный compat-заголовок
|
||||
- [ ] обновить статусы/history в solid_c_compatibility.md (Phase 1 done)
|
||||
|
||||
Research (Phase 3): absread/abswrite (найти ESTEX READ_SECT/WRITE_SECT),
|
||||
scanf-семейство (**в SDCC 4.5 z80 его НЕТ** — писать самим или задокументировать
|
||||
отказ), brk/sbrk vs SDCC heap, isatty, ioctl (скорее скип).
|
||||
|
||||
## П4. Недостающие POSIX-мелочи вне Solid-C списка
|
||||
|
||||
- [ ] **rename()** — в gap-анализе отсутствует, ESTEX почти наверняка имеет
|
||||
RENAME (~$0F, рядом с DELETE $0E) — проверить по докам ESTEX и добавить
|
||||
- [ ] isatty(fd) — тривиально (fd<0/флаги)
|
||||
|
||||
## П5. Заголовки и гигиена сборки
|
||||
|
||||
- [ ] Аудит затенения SDCC-заголовков: stdlib.h уже на `#include_next`;
|
||||
stdio.h/time.h — полные замены (задокументировать контракт «что обязаны
|
||||
объявлять»); подумать про собственный string.h (include_next + strlwr/
|
||||
strupr из sprinter_compat.h)
|
||||
- [ ] lib/build: wildcard не удаляет stale .rel при удалении исходника —
|
||||
либо чистка в Makefile (сверка списка), либо привычка `make clean`
|
||||
после переименований (это уже укусило: старый file.rel лежал в build)
|
||||
- [ ] Включить extra-тесты (conio2, dec_test, gets, hello2, simple, stest2,
|
||||
winrest, bios_text, text_palette, gfx_dbuf, mdview2) в top-level
|
||||
TESTS/APPS — иначе `make all` их не собирает и их baseline протухает
|
||||
(уже дало ложные «+17 Б» при сравнении)
|
||||
- [ ] Размерный регресс-тест: скрипт сравнения _CODE по .map с эталоном
|
||||
(ловить случайные разжирения при правках libc)
|
||||
|
||||
## П6. Верификация после сплита
|
||||
|
||||
- [ ] `make floppy` + прогон в MAME ключевых тестов (conio, filetest, ptime,
|
||||
stattest, mouse, gfx_demo) — линковка прошла, но поведение надо
|
||||
подтвердить на эмуляторе
|
||||
- [ ] Потом на железе (mdview2 и так ждёт проверки на железе — совместить)
|
||||
|
||||
## П7. Документация
|
||||
|
||||
- [ ] docs/libc-reference.md — сводный справочник API (по заголовкам:
|
||||
сигнатура + 1 строка описания + особенности ABI); сейчас знание
|
||||
размазано по memory/ и комментариям
|
||||
- [ ] TODO.md: этапы 5/6/8 закрыты — перенести в history, добавить «этап 9 —
|
||||
libc-оптимизация» (этот план)
|
||||
- [ ] В CLAUDE.md/README зафиксировать правила libc: 1 ф-я = 1 модуль,
|
||||
internal `_`-модули, русские комментарии, без `= 0`, asm-правила
|
||||
(docs/libc-split-asm-cases.md)
|
||||
|
||||
## П8. Смежное (не libc, из TODO.md — чтобы не потерялось)
|
||||
|
||||
- auto-banking Phase 1 (toolchain/auto_bank.py) — когда проект перерастёт ~30 КБ
|
||||
- IM2 ISR v2 (docs/im2_isr_design.md) — отложено решением 2026-06-01
|
||||
- font-quad для 640×256 (per-cell палитра)
|
||||
- factoring parse_argv из crt0/crt0_banked в общий argv.s
|
||||
- check_banks.py: разбивка code/const/bss per bank (косметика)
|
||||
|
||||
---
|
||||
|
||||
## Предлагаемый порядок
|
||||
|
||||
1. **П1-лайт**: io/env/errno/atexit/minmax/mem/dir/fsdir (мелкие, час работы,
|
||||
выигрыш для всех CLI-приложений) + mouse + bios/text.
|
||||
2. **П6**: MAME-смоук — подтвердить, что сплит ничего не сломал в рантайме,
|
||||
до того как менять что-то ещё.
|
||||
3. **П1-gfx**: разбор графики (самый большой кусок, отдельный заход).
|
||||
4. **П2**: баги FILE* (3 шт.) + fprintf + ungetc.
|
||||
5. **П3/П4**: solid-c остатки + rename/isatty.
|
||||
6. **П5/П7**: гигиена сборки и документация — фоном, по кусочку.
|
||||
@@ -0,0 +1,52 @@
|
||||
# libc split: asm-связки между функциями
|
||||
|
||||
Журнал случаев, найденных при разбиении libc на «1 публичная функция = 1 модуль»
|
||||
(2026-07-05). Сюда записывается каждый обнаруженный переход `jr _func` или
|
||||
`jr/jp/call` на метку **внутри другой функции** — такие связки нельзя разрывать
|
||||
механически, разбираем каждую отдельно.
|
||||
|
||||
## Правила (справка)
|
||||
|
||||
| Паттерн | Через границу модулей |
|
||||
|---|---|
|
||||
| `call/jp _func` (публичная C-функция) | работает — метка глобальная (`::`) |
|
||||
| `call/jp _label` на метку в чужой функции | работает, только если метка объявлена `_label::` |
|
||||
| `jr` / `djnz` в другой модуль | **запрещено** — ±128 байт, разложение модулей не гарантировано |
|
||||
| fall-through (без перехода, в надежде на соседство) | **не работает никогда** |
|
||||
|
||||
## Случаи
|
||||
|
||||
### 1. stdio/dec_print.c — dec8/dec16/dec32: разделяемое тело (НЕ разрывать)
|
||||
|
||||
Статус: **оставлены в одном файле, решение отдельно.**
|
||||
|
||||
- `dec8` → `jp __dec_entry3` — прыжок в середину тела `dec32`;
|
||||
- `dec16` → `jp __dec_entry5` — то же;
|
||||
- метки уже глобальные (`__dec_entry3::`, `__dec_entry5::`) — линковаться будет,
|
||||
но выигрыша от сплита нет: dec8 всё равно притянет модуль с телом dec32;
|
||||
- внутри хвоста: `_dec_get_d16` **fall-through** в `_dec_emit_or_skip`,
|
||||
`_dec_get_d32` → `jr _dec_emit_or_skip`, общий флаг `_dec_flag`.
|
||||
|
||||
Это осознанный дизайн из solid-c: тройка делит per-digit код. Варианты на потом:
|
||||
(а) оставить как есть (176 Б тянутся целиком — терпимо);
|
||||
(б) развести на 3 независимых цикла — dec8 станет ~40 Б, но исходник длиннее
|
||||
и суммарно в exe, использующем dec8+dec32, станет хуже. Решение отложено.
|
||||
|
||||
### 2. stdio/hex_print.c — чист
|
||||
|
||||
`hex16` → `call _hex8` / `jp _hex8` (tail), `hex32` → `_hex16`: переходы по
|
||||
именам публичных функций. Разъезжается на hex8.c/hex16.c/hex32.c без правок.
|
||||
`_hex8_digit` — self-call внутри hex8, не мешает.
|
||||
|
||||
### 3. conio/conio.c — jp _clrscr_attr (чист)
|
||||
|
||||
`clrscr` → `jp _clrscr_attr` — tail-call публичной функции, работает через
|
||||
модули как есть.
|
||||
|
||||
### 4. mem/mem_bios.c, mem/mem_estex.c — jp __errno_set (чист)
|
||||
|
||||
Tail-call публичного internal-хелпера `_errno_set` — кросс-модульный уже сейчас.
|
||||
|
||||
---
|
||||
Все прочие `jr`-переходы в libc (проверены все `jr`, включая условные формы,
|
||||
2026-07-05) ведут на метки внутри своей же функции — сплиту не мешают.
|
||||
Reference in New Issue
Block a user