Files
Sprinter-SDCC/libc/stdio/dec_print.c
T
snark13 4a081501d8 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>
2026-07-06 16:08:58 +03:00

186 lines
6.6 KiB
C
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.
/*
* 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;
}