Files
Sprinter-SDCC/libc/file/fputc.c
T
snark13 9f8aa6fc28 libc: две версии библиотеки — sprinter.lib (fast) / sprinter_safe.lib
Симметрично libbgi (bgi256/bgi256_safe): fast = -DLIBC_NOCHECK,
дефолт sprinter-cc; safe линкуется по --safe (флаг уже существовал).

Под LIBC_NOCHECK вырезаны ТОЛЬКО параметр-валидации:
- fgetc/fputc: NULL-check в горячей asm-обёртке (~11Т на каждый байт);
- fgets/fputs/fread/fwrite: NULL ptr/fp и EBADF на неверное направление
  потока; ftell/fseek/ungetc/fclose: NULL fp; cputs: NULL s.
НЕ тронуты: критичный _fd_guard (9-й OPEN вешает DSS — в обеих
версиях), функциональная маршрутизация (консоль/направление/hold),
cold-path валидации (fopen/cbl_open/irq/settextmode — экономии ноль).

libc/Makefile — dual-build по образцу libbgi (build/fast + build/safe,
общий стейл-контроль).  Корневой Makefile: в TESTS добавлены bgitest,
bgi_img, accfill, cblstream — раньше не собирались корневым make и
выпадали из size-check при чистой пересборке.

Дельты fast vs старая (safe-семантика): filetest -481, fbench -384,
solidt -180, errno -71, остальные -3..-9; роста нет.  Проверено в MAME:
filetest fast и safe (--safe, sprinter_safe.lib) — прогоны идентичны.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 22:11:01 +03:00

105 lines
3.6 KiB
C
Raw Permalink 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.
/*
* fputc — записать один байт в поток (буферизованно, вариант B+).
*
* Горячий путь («поток уже копит запись и в буфере есть место»)
* написан на asm — см. пояснение в fgetc.c. Остальное — медленный
* C-путь fputc_slow: консольный putchar(), переключение направления
* (_file_sync откатывает readahead), сброс полного буфера, ленивое
* выделение, fallback без буфера (прямой write() 1 байта).
* ВНИМАНИЕ: успех fputc = «байт принят в буфер»; ошибка реальной
* записи может вылезти позже (fflush/fclose).
*
* Раскладка FILE: +0 fd, +2 flags, +3 buf, +5 curp, +7 level, +9 hold.
* asm рассчитывает на BUFSIZ == 512 (проверка уровня по старшему
* байту level < 2).
*/
#include <unistd.h>
#include <errno.h>
#include "_file.h"
#if BUFSIZ != 512
#error "asm-горячий путь fputc рассчитан на BUFSIZ == 512"
#endif
/* Медленный путь; зовётся из asm-обёртки. */
static int fputc_slow(int c, FILE *fp)
{
#ifndef LIBC_NOCHECK
if (!fp) { errno = EBADF; return EOF; }
#endif
if (fp->flags & _F_CONOUT) {
putchar(c);
return (int)(uint8_t)c;
}
if (!(fp->flags & _F_WRITE)) { errno = EBADF; return EOF; }
if (!(fp->flags & _F_DIROUT)) {
if (_file_sync(fp)) return EOF; /* откат readahead/hold */
} else if (fp->level >= BUFSIZ) {
if (_file_sync(fp)) return EOF; /* буфер полон — сброс */
}
uint8_t *b = _file_buf(fp);
if (!b) {
/* Небуферизованный fallback. */
uint8_t ch = (uint8_t)c;
if (write(fp->fd, &ch, 1) != 1) {
fp->flags |= _F_ERROR;
return EOF;
}
return (int)ch;
}
if (!(fp->flags & _F_DIROUT)) {
fp->flags |= _F_DIROUT;
fp->curp = b;
}
*fp->curp++ = (uint8_t)c;
fp->level++;
return (int)(uint8_t)c;
}
int fputc(int c, FILE *fp) __naked
{
(void)c; (void)fp;
(void)fputc_slow; /* используется из asm */
__asm
;; HL = c, DE = fp.
#ifndef LIBC_NOCHECK
ld a, d
or a, e
jr Z, _fp_slow ; NULL C-путь выставит errno
#endif
push de
pop iy ; IY = fp
ld a, 2 (iy) ; flags
and a, #0x08 ; _F_DIROUT уже копим запись?
jr Z, _fp_slow ; нет (или консоль/чтение) C-путь
ld a, 8 (iy) ; level_hi < 2 <=> level < 512
cp a, #2
jr NC, _fp_slow ; буфер полон сброс в C-пути
;; --- горячий путь: *curp++ = c, level++ ---
ld e, 5 (iy) ; DE = curp (fp больше не нужен в DE)
ld d, 6 (iy)
ld a, l
ld (de), a
inc de
ld 5 (iy), e
ld 6 (iy), d
ld a, 7 (iy) ; level++
inc a
ld 7 (iy), a
jr NZ, _fp_ret
inc 8 (iy)
_fp_ret:
ld e, l ; вернуть (uint8_t)c
ld d, #0
ret
_fp_slow:
jp _fputc_slow ; HL = c, DE = fp уже на месте
__endasm;
}