4a081501d8
- 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>
186 lines
6.6 KiB
C
186 lines
6.6 KiB
C
/*
|
||
* dec_print.c — компактная десятичная печать, портировано из solid-c
|
||
* (third_party/solid-c/SRC/CLIB/STDLIB.ASM, модули dec8/dec16/dec32).
|
||
*
|
||
* void dec8 (uint8_t v) — без ведущих нулей, "0" .. "255"
|
||
* void dec16(uint16_t v) — без ведущих нулей, "0" .. "65535"
|
||
* void dec32(uint32_t v) — без ведущих нулей, "0" .. "4294967295"
|
||
*
|
||
* Алгоритм: вычитание степени десяти со счётчиком, цифра печатается,
|
||
* если ведущие нули уже кончились. 32-битные значения живут в паре
|
||
* BC/DE/HL плюс теневой набор (exx) для старшей половины.
|
||
*
|
||
* Раскладка: dec32 — главная рутина; dec8/dec16 — крошечные обёртки,
|
||
* готовящие состояние и прыгающие на внутренние входы (__dec_entry3 —
|
||
* «последние 3 цифры», __dec_entry5 — «последние 5») — как в solid-c,
|
||
* per-digit код общий. Поэтому файл сознательно НЕ разрезан на три
|
||
* модуля: dec8 всё равно притянул бы тело dec32
|
||
* (см. docs/libc-split-asm-cases.md, случай 1).
|
||
*
|
||
* ESTEX PUTCHAR ($5B) сохраняет IX (проверено эмпирически), так что
|
||
* push/pop ix вокруг RST не нужен.
|
||
*/
|
||
|
||
#include <stdio.h>
|
||
#include <stdint.h>
|
||
|
||
/* Флаг подавления ведущих нулей: 0 — «всё ещё пропускаем нули»,
|
||
* не-0 — «первая значащая цифра встречена, дальше печатаем всё
|
||
* (включая последующие нули)». */
|
||
static uint8_t dec_flag;
|
||
|
||
void dec8(uint8_t v) __naked
|
||
{
|
||
(void)v;
|
||
__asm
|
||
;; A = v.
|
||
ld l, a
|
||
ld h, #0 ; HL = значение
|
||
xor a, a
|
||
ld (_dec_flag), a ; сбросить флаг подавления нулей
|
||
jp __dec_entry3
|
||
__endasm;
|
||
}
|
||
|
||
void dec16(uint16_t v) __naked
|
||
{
|
||
(void)v;
|
||
__asm
|
||
;; HL = v.
|
||
exx
|
||
ld hl, #0 ; HL-альт = 0 (старшие 16 составного)
|
||
exx
|
||
xor a, a
|
||
ld (_dec_flag), a
|
||
jp __dec_entry5
|
||
__endasm;
|
||
}
|
||
|
||
void dec32(uint32_t v) __naked
|
||
{
|
||
(void)v;
|
||
__asm
|
||
;; На входе HL = high16, DE = low16 (SDCC HLDE).
|
||
;; Старшие 16 — в HL-альт (теневой набор), младшие — в HL.
|
||
push hl
|
||
exx
|
||
pop hl ; HL-альт = high16
|
||
exx
|
||
ex de, hl ; HL = low16
|
||
xor a, a
|
||
ld (_dec_flag), a
|
||
|
||
;; ---- 5 старших декад (1e9..1e5) ----
|
||
ld de, #0xCA00
|
||
exx
|
||
ld de, #0x3B9A ; 0x3B9ACA00 = 1,000,000,000
|
||
exx
|
||
call _dec_get_d32
|
||
|
||
ld de, #0xE100
|
||
exx
|
||
ld de, #0x05F5 ; 0x05F5E100 = 100,000,000
|
||
exx
|
||
call _dec_get_d32
|
||
|
||
ld de, #0x9680
|
||
exx
|
||
ld de, #0x0098 ; 0x00989680 = 10,000,000
|
||
exx
|
||
call _dec_get_d32
|
||
|
||
ld de, #0x4240
|
||
exx
|
||
ld de, #0x000F ; 0x000F4240 = 1,000,000
|
||
exx
|
||
call _dec_get_d32
|
||
|
||
ld de, #0x86A0
|
||
exx
|
||
ld de, #0x0001 ; 0x000186A0 = 100,000
|
||
exx
|
||
call _dec_get_d32
|
||
|
||
__dec_entry5:: ; вход из dec16
|
||
ld de, #10000
|
||
exx
|
||
ld de, #0
|
||
exx
|
||
call _dec_get_d32
|
||
|
||
ld de, #1000
|
||
call _dec_get_d16
|
||
|
||
__dec_entry3:: ; вход из dec8
|
||
ld de, #100
|
||
call _dec_get_d16
|
||
ld de, #10
|
||
call _dec_get_d16
|
||
|
||
;; Цифра единиц — печатается всегда (dec*(0) выводит "0").
|
||
ld a, l
|
||
add a, #0x30
|
||
ld c, #0x5B
|
||
rst #0x10
|
||
ret
|
||
|
||
;; ---- 32 бита: сколько раз DE+DE-альт укладывается в HL+HL-альт ----
|
||
_dec_get_d32:
|
||
ld a, #0x2F ; 0x2F = символ перед 0 (пред-декремент)
|
||
and a, a ; CF = 0
|
||
_dec_get_d32_loop:
|
||
inc a
|
||
sbc hl, de ; младшая половина
|
||
exx
|
||
sbc hl, de ; старшая половина (с цепочкой заёма)
|
||
exx
|
||
jp nc, _dec_get_d32_loop
|
||
;; Перебрали — вернуть последнее корректное значение.
|
||
add hl, de
|
||
exx
|
||
adc hl, de
|
||
exx
|
||
jr _dec_emit_or_skip
|
||
|
||
;; ---- 16 бит: сколько раз DE укладывается в HL ----
|
||
_dec_get_d16:
|
||
ld a, #0x2F
|
||
and a, a
|
||
_dec_get_d16_loop:
|
||
inc a
|
||
sbc hl, de
|
||
jp nc, _dec_get_d16_loop
|
||
add hl, de
|
||
;; Fall-through в emit/skip.
|
||
|
||
_dec_emit_or_skip:
|
||
;; A = символ цифры 0x30..0x39. Не-ноль защёлкивает флаг.
|
||
;; Печатаем только при взведённом флаге.
|
||
ld b, a ; спасти цифру на время проверки флага
|
||
cp a, #0x30
|
||
jr z, _dec_check_flag
|
||
ld (_dec_flag), a ; встречена значащая цифра
|
||
_dec_check_flag:
|
||
ld a, (_dec_flag)
|
||
or a, a
|
||
ld a, b ; вернуть цифру (ld флаги не трогает)
|
||
ret z ; ведущий ноль — печать пропустить
|
||
|
||
;; ESTEX PUTCHAR ($5B) сохраняет основной набор (BC, DE, HL,
|
||
;; IX), но КЛОББЕРИТ теневой (BC/DE/HL-альт). Цикл вычитания
|
||
;; степени десяти в _dec_get_d32 держит старшие 16 бит остатка
|
||
;; в HL-альт, поэтому HL-альт сохраняем вокруг RST. Основной
|
||
;; HL (младшие 16 остатка) переживает вызов сам, спасать не
|
||
;; нужно. См. memory/estex_putchar_abi.md.
|
||
exx
|
||
push hl
|
||
exx
|
||
ld c, #0x5B
|
||
rst #0x10
|
||
exx
|
||
pop hl
|
||
exx
|
||
ret
|
||
__endasm;
|
||
}
|