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

87 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.
/*
* fgets — прочитать строку (до n-1 символов, '\n' сохраняется) из
* потока в buf с завершающим NUL. Возвращает buf или NULL, если EOF
* наступил до первого сохранённого символа. n=1 по стандарту —
* валидный вызов: ничего не читает, возвращает пустую строку.
*
* Быстрый путь: пока строка лежит в readahead-буфере, она копируется
* блоком asm-сканером (LDI + сравнение с '\n', ~50 тактов/байт) —
* fgetc-на-символ был в ~10 раз медленнее (замер fbench). Медленные
* случаи (консоль, hold от ungetc, режим записи, дозаполнение буфера,
* EOF) уходят посимвольно через fgetc.
*/
#include "_file.h"
/* Копировать из src в dst до max байт, остановившись ПОСЛЕ '\n'.
* Возврат: число скопированных байт | 0x8000, если '\n' найден.
* max: 1..512 (бит 15 счётчика свободен). */
static uint16_t fgets_scan(uint8_t *dst, const uint8_t *src,
uint16_t max) __naked
{
(void)dst; (void)src; (void)max;
__asm
;; HL = dst, DE = src, max на стеке (callee чистит).
pop iy ; адрес возврата
pop bc ; BC = max (> 0)
ex de, hl ; HL = src, DE = dst
push bc ; max понадобится для подсчёта
_fgs_loop:
ld a, (hl)
cp a, #0x0A ; сравнить ДО ldi: LDI не трогает Z,
ldi ; но выставляет P/V по BC ((DE)<-(HL))
jr Z, _fgs_found ; Z от cp
jp PE, _fgs_loop ; P/V от LDI: BC != 0
;; max исчерпан, '\n' нет: count = max
pop de
jp (iy)
_fgs_found:
;; count = max - BC, бит 15 = флаг '\n' (count <= 512 безопасно)
pop hl ; HL = max
ld a, l
sub a, c
ld e, a
ld a, h
sbc a, b
or a, #0x80
ld d, a
jp (iy)
__endasm;
}
char *fgets(char *buf, int n, FILE *fp)
{
#ifndef LIBC_NOCHECK
if (!buf || n < 1 || !fp) return NULL;
#endif
uint8_t *dst = (uint8_t *)buf;
int space = n - 1;
while (space > 0) {
if (fp->level && !(fp->flags & _F_DIROUT) && fp->hold == EOF) {
/* Быстрый блок: сканировать readahead до '\n'. */
uint16_t take = fp->level;
if (take > (uint16_t)space) take = (uint16_t)space;
uint16_t got = fgets_scan(dst, fp->curp, take);
uint16_t cnt = got & 0x7FFF;
fp->curp += cnt;
fp->level -= cnt;
dst += cnt;
space -= (int)cnt;
if (got & 0x8000) break; /* строка закончена */
continue; /* буфер иссяк — дозаполнить */
}
/* Медленные случаи + refill/EOF — посимвольно. */
int c = fgetc(fp);
if (c == EOF) {
if (dst == (uint8_t *)buf) return NULL;
break;
}
*dst++ = (uint8_t)c;
space--;
if (c == '\n') break;
}
*dst = '\0';
return buf;
}