diff --git a/libpsx/include/stdio.h b/libpsx/include/stdio.h index 456a438..89d8817 100644 --- a/libpsx/include/stdio.h +++ b/libpsx/include/stdio.h @@ -70,6 +70,12 @@ typedef struct unsigned int eof; /** Error marker */ unsigned int error; + /** Sector buffer. */ + unsigned char *const buf; + /** Last used sector for reading. */ + size_t last_sect; + /** Sector buffer can be used for reading. */ + unsigned int cache_available; }FILE; extern FILE *const stdin, *const stdout, *const stderr; @@ -111,7 +117,7 @@ int vprintf(const char *fmt, va_list ap); FILE *fdopen(int fildes, const char *mode); FILE *fopen(const char *path, const char *mode); int fclose(FILE *stream); -int fread(void *ptr, int size, int nmemb, FILE *f); +size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); size_t fwrite(void *ptr, size_t size, size_t nmemb, FILE *stream); int fgetc(FILE *f); diff --git a/libpsx/src/libc.c b/libpsx/src/libc.c index 7480825..2cf4064 100644 --- a/libpsx/src/libc.c +++ b/libpsx/src/libc.c @@ -19,13 +19,14 @@ #include #include -char onesec_buf[2048]; int errno; int __stdio_direction = STDIO_DIRECTION_BIOS; static unsigned int __sio_cr_mapped = 0; #define NUM_OF_FILE_STRUCTS 6 +enum {SECTOR_SZ = 2048}; + static FILE file_structs[NUM_OF_FILE_STRUCTS] = { [0] = // stdin @@ -61,6 +62,11 @@ static FILE file_structs[NUM_OF_FILE_STRUCTS] = .eof = 0, .error = 0 }, + + [3 ... NUM_OF_FILE_STRUCTS - 1] = + { + .buf = (unsigned char[SECTOR_SZ]){} + } }; FILE *const stdin = &file_structs[0]; @@ -134,10 +140,20 @@ FILE *fdopen(int fildes, const char *mode) // Find a free file structure for(x = 0; x < NUM_OF_FILE_STRUCTS; x++) { - if(file_structs[x].used == 0) + FILE *const f = &file_structs[x]; + + if(f->used == 0) { - bzero(&file_structs[x], sizeof(FILE)); - file_structs[x].used = 1; + f->size = 0; + f->eof = 0; + f->error = 0; + f->used = 1; + f->last_sect = 0; + f->cache_available = 0; + + if (f->buf) + memset(f->buf, 0, SECTOR_SZ); + break; } } @@ -230,6 +246,110 @@ int fclose(FILE *stream) return 0; } +static size_t cdromread(void *restrict ptr, size_t sz, + FILE *restrict const f) +{ + const size_t start_sect = f->pos >> __builtin_ctz(SECTOR_SZ); + const size_t end_pos = f->pos + sz; + const size_t end_sector = end_pos >> __builtin_ctz(SECTOR_SZ); + size_t readb = 0; + int read_complete_sector = 0; + + if (feof(f) || ferror(f)) + return 0; + else if (f->cache_available) + { + const size_t first_sector_sz = f->pos % SECTOR_SZ; + + if (f->last_sect == start_sect && f->last_sect == end_sector) + { + /* Use buffered I/O. */ + size_t max; + + if (end_pos >= f->size) + { + max = f->size - f->pos; + f->eof = 1; + } + else + { + max = sz; + } + + memcpy(ptr, &f->buf[first_sector_sz], max); + f->pos += max; + readb = max; + } + else if (f->last_sect == start_sect) + { + /* Partial buffered I/O possible. */ + const size_t firstsz = SECTOR_SZ - first_sector_sz; + + memcpy(ptr, &f->buf[first_sector_sz], firstsz); + f->pos += firstsz; + lseek(f->fildes, f->pos, SEEK_SET); + f->cache_available = 0; + readb += firstsz; + read_complete_sector = 1; + } + } + + const size_t rem = sz - readb; + + if (rem) + { + const size_t whole_sectors = rem / SECTOR_SZ; + + if (whole_sectors) + { + if (!read_complete_sector) + lseek(f->fildes, f->pos & ~(SECTOR_SZ - 1), SEEK_SET); + + const size_t s = whole_sectors * SECTOR_SZ; + const int n = read(f->fildes, &((unsigned char *)ptr)[readb], s); + + if (n >= 0) + { + readb += s; + f->pos += s; + ptr = (unsigned char *)ptr + s; + + if (f->pos >= f->size) + f->eof = 1; + + f->cache_available = 1; + } + } + + if (f->pos < f->size) + { + int n; + + if (!read_complete_sector) + lseek(f->fildes, f->pos & ~(SECTOR_SZ - 1), SEEK_SET); + + n = read(f->fildes, f->buf, SECTOR_SZ); + + if (n >= 0) + { + readb = n > rem ? rem : n; + + memmove(ptr, f->buf, readb); + ptr = (unsigned char *)ptr + readb; + f->pos += readb; + + if (f->pos >= f->size) + f->eof = 1; + + f->cache_available = 1; + } + } + } + + f->last_sect = end_sector; + return f->pos; +} + /* * fread doesn't require reads to be carried in block unit * Notice that however seeks on the CD drive will be very slow - so avoid using non block units @@ -237,83 +357,29 @@ int fclose(FILE *stream) * This is done to make programming and porting easier */ -int fread(void *ptr, int size, int nmemb, FILE *f) +size_t fread(void *restrict const ptr, const size_t size, const size_t nmemb, + FILE *restrict const f) { - int rsize = size * nmemb; - int csize = rsize; - int max; - int nsect = (f->pos + rsize) >> 11; - nsect -= f->pos >> 11; - nsect++; - - //printf("f->dev = %d, f->pos = %d, rsize = %d\n", f->dev, f->pos, rsize); - - if(f->dev == FDEV_CDROM) + if (size && nmemb) { - // First sector - lseek(f->fildes, f->pos & (~0x7ff), SEEK_SET); - read(f->fildes, onesec_buf, 2048); + size_t rsize = size * nmemb; - max = 2048 - (f->pos & 2047); - - //printf("ptr(FIRST) = %d, %x\n", ptr, ptr); - printf("rsize = %d\n", rsize); - - memcpy(ptr, onesec_buf + (f->pos & 2047), (rsize > max) ? max : rsize); - - // Middle sector - ptr += max; - - //printf("ptr(MIDDLEsex) = %d, %x\n", ptr, ptr); - nsect--; - csize -= max; - - if(nsect > 1) + switch (f->dev) { - //lseek(f->fildes, (f->pos & (~0x7ff)) + 2048, SEEK_SET); + case FDEV_CDROM: + rsize = cdromread(ptr, rsize, f); + break; -//#warning "Check correctness of this calculation." - - /*if(rsize & 2047) - sect_num = (rsize|2047)+1; - else - sect_num = rsize; - - sect_num -= 4096;*/ - - //printf("read_middle=%d, sect_num = %d\n", read(f->fildes, ptr, sect_num), sect_num); - - read(f->fildes, ptr, (nsect - 1) * 2048); - - ptr += (nsect - 1) * 2048; - csize -= (nsect - 1) * 2048; - nsect = 1; + case FDEV_CONSOLE: + /* Fall through. */ + default: + return 0; } - //printf("ptr(LAST) = %d, %x\n", ptr, ptr); - - if(nsect == 1) - { - // Last sector - read(f->fildes, onesec_buf, 2048); - - memcpy(ptr, onesec_buf, csize); - } - } - else if(f->dev == FDEV_CONSOLE) - return 0; - - - if(f->dev != FDEV_CONSOLE) - { - f->pos+= rsize; - f->eof = (f->pos > f->size); - - if(f->eof) - f->pos = f->size; + return rsize / size; } - return rsize; + return 0; } int fgetc(FILE *f)