/* * 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) { if (!buf || n < 1 || !fp) return NULL; 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; }