/* * 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 #include #include "_file.h" /* Медленный путь; зовётся из asm-обёртки (метка _fgetc_slow видна в * пределах этого модуля). */ static int fgetc_slow(FILE *fp) { if (!fp) { errno = EBADF; return EOF; } 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. ld a, h or a, l jr Z, _fg_slow ; NULL — C-путь выставит errno 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; }