Files
Sprinter-SDCC/libc/file/fgetc.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

106 lines
3.7 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.
/*
* fgetc — прочитать один байт из потока (буферизованно, вариант B+).
*
* Горячий путь («в readahead-буфере есть байт») написан на asm —
* SDCC на эту цепочку проверок генерит ~90 инструкций с IX-фреймом,
* asm укладывается в ~30 без фрейма (замер fbench: ~176 мкс/байт
* у C-версии). Всё остальное — медленный C-путь fgetc_slow:
* hold-байт от ungetc, консольный getchar(), сброс накопленной
* записи, дозаполнение буфера read(fd, buf, BUFSIZ), fallback без
* буфера. Возвращает байт, EOF на конце файла (_F_EOF) или при
* ошибке (_F_ERROR).
*
* Раскладка FILE (см. stdio.h, байтовые смещения):
* +0 fd, +2 flags, +3 buf, +5 curp, +7 level, +9 hold
*/
#include <unistd.h>
#include <errno.h>
#include "_file.h"
/* Медленный путь; зовётся из asm-обёртки (метка _fgetc_slow видна в
* пределах этого модуля). */
static int fgetc_slow(FILE *fp)
{
#ifndef LIBC_NOCHECK
if (!fp) { errno = EBADF; return EOF; }
#endif
if (fp->hold != EOF) {
int c = fp->hold;
fp->hold = EOF;
return c;
}
if (fp->flags & _F_CONIN) {
return getchar();
}
if (!(fp->flags & _F_READ)) { errno = EBADF; return EOF; }
if (fp->flags & _F_DIROUT) {
if (_file_sync(fp)) return EOF; /* сброс накопленной записи */
}
if (fp->level == 0) {
uint8_t *b = _file_buf(fp);
if (!b) {
/* Небуферизованный fallback. */
uint8_t ch;
int r = read(fp->fd, &ch, 1);
if (r == 0) { fp->flags |= _F_EOF; return EOF; }
if (r < 0) { fp->flags |= _F_ERROR; return EOF; }
return (int)ch;
}
int r = read(fp->fd, b, BUFSIZ);
if (r == 0) { fp->flags |= _F_EOF; return EOF; }
if (r < 0) { fp->flags |= _F_ERROR; return EOF; }
fp->level = (uint16_t)r;
fp->curp = b;
}
fp->level--;
return (int)*fp->curp++;
}
int fgetc(FILE *fp) __naked
{
(void)fp;
(void)fgetc_slow; /* используется из asm */
__asm
;; HL = fp.
#ifndef LIBC_NOCHECK
ld a, h
or a, l
jr Z, _fg_slow ; NULL C-путь выставит errno
#endif
push hl
pop iy ; IY = fp (IY scratch в __sdcccall(1))
ld a, 7 (iy) ; level != 0 ?
or a, 8 (iy)
jr Z, _fg_slow ; буфер пуст/не наш режим
ld a, 2 (iy) ; flags
and a, #0x08 ; _F_DIROUT буфер копит запись?
jr NZ, _fg_slow
ld a, 9 (iy) ; hold == EOF (0xFFFF)?
and a, 10 (iy)
inc a
jr NZ, _fg_slow ; есть отложенный ungetc-байт
;; --- горячий путь: level--, вернуть *curp++ ---
ld a, 7 (iy)
sub a, #1
ld 7 (iy), a
jr NC, _fg_nb
dec 8 (iy)
_fg_nb:
ld l, 5 (iy) ; HL = curp
ld h, 6 (iy)
ld e, (hl) ; E = байт
ld d, #0 ; zero-extend в int
inc hl
ld 5 (iy), l ; curp++
ld 6 (iy), h
ret
_fg_slow:
jp _fgetc_slow ; HL = fp уже на месте
__endasm;
}