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:
2026-07-06 16:08:58 +03:00
parent 46553f4e07
commit 4a081501d8
237 changed files with 4830 additions and 3933 deletions
+98
View File
@@ -0,0 +1,98 @@
/*
* _putch_wrchar — воркер вывода mode-0: пишет символ в (pc_place)
* через ESTEX WRCHAR ($58) атрибутом attr, интерпретируя управляющие:
*
* 0x08 BS → pc_col-- (если не 0)
* 0x09 TAB → pc_col к следующему кратному 8 (потолок 80)
* 0x0A LF → pc_row++ (потолок 32; без глифа)
* 0x0D CR → pc_col = 0
* прочее → WRCHAR + pc_col++
*
* WRCHAR подавляется при pc_col ≥ 80 или pc_row ≥ 32 (вне экрана) —
* координаты только [0..79] × [0..31].
*
* Курсор BIOS НЕ трогает — вызывающий читает его один раз до серии
* вызовов и записывает один раз после: плата BIOS за ОПЕРАЦИЮ,
* а не за символ.
*/
#include "_conio.h"
void _putch_wrchar(char ch, uint8_t attr) __naked
{
(void)ch; (void)attr;
__asm
;; __sdcccall(1): ch в A, attr в L.
;; Диспетчеризация по управляющим, пока A держит ch (cp не
;; трогает A). B/C грузятся только на пути вывода, поэтому
;; ветки управляющих символов дешевле.
cp #0x08
jr z, _rp0_bs
cp #0x09
jr z, _rp0_tab
cp #0x0A
jr z, _rp0_lf
cp #0x0D
jr z, _rp0_cr
;; Всё остальное (печатное или незнакомый ctrl) глиф.
_rp0_pri:
ld c, a ; C = ch (сохранить до порчи A)
ld a, (_pc_place + 1)
cp #32
ret nc ; за нижним краем молча пропустить
ld d, a ; D = row (конвенция ESTEX WRCHAR)
ld a, (_pc_place)
cp #80
ret nc ; за правым краем молча пропустить
ld e, a ; E = col
inc a
ld (_pc_place), a ; pc_col++
_rp0_wr:
ld b, l ; B = attr
ld a, c ; A = ch
push ix
ld c, #0x58 ; ESTEX WRCHAR
rst #0x10
pop ix
ret
_rp0_bs:
ld a, (_pc_place)
or a, a
ret z ; уже в колонке 0 без изменений
dec a
ld (_pc_place), a
ld e, a ; E = col
ld a, (_pc_place + 1)
ld d, a ; D = row (конвенция ESTEX WRCHAR)
ld c, #0x20
jr _rp0_wr
_rp0_tab:
ld a, (_pc_place)
or #0x07 ; округлить вниз к кратному 8
inc a ; следующее кратное 8
cp #81 ; сравнить A с 81 (0x51)
jr c, _rp0_tab_skip ; если A < 81 (т.е. A 80), пропустить загрузку
ld a, #80 ; иначе A > 80 установить A = 80
_rp0_tab_skip:
ld (_pc_place), a
ret
_rp0_lf:
ld a, (_pc_place + 1)
cp #32
ret nc ; уже у нижнего края
inc a
ld (_pc_place + 1), a
ret
_rp0_cr:
xor a, a
ld (_pc_place), a
ret
__endasm;
}