summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libpsx/include/stdio.h8
-rw-r--r--libpsx/src/libc.c188
2 files changed, 134 insertions, 62 deletions
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 <errno.h>
#include <unistd.h>
-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,90 +246,140 @@ int fclose(FILE *stream)
return 0;
}
-/*
- * 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
- *
- * This is done to make programming and porting easier
- */
-
-int fread(void *ptr, int size, int nmemb, FILE *f)
+static size_t cdromread(void *restrict ptr, size_t sz,
+ FILE *restrict const f)
{
- int rsize = size * nmemb;
- int csize = rsize;
- int max;
- int nsect = (f->pos + rsize) >> 11;
- nsect -= f->pos >> 11;
- nsect++;
+ 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;
- //printf("f->dev = %d, f->pos = %d, rsize = %d\n", f->dev, f->pos, rsize);
-
- if(f->dev == FDEV_CDROM)
+ if (feof(f) || ferror(f))
+ return 0;
+ else if (f->cache_available)
{
- // First sector
- lseek(f->fildes, f->pos & (~0x7ff), SEEK_SET);
- read(f->fildes, onesec_buf, 2048);
+ const size_t first_sector_sz = f->pos % SECTOR_SZ;
- max = 2048 - (f->pos & 2047);
+ if (f->last_sect == start_sect && f->last_sect == end_sector)
+ {
+ /* Use buffered I/O. */
+ size_t max;
- //printf("ptr(FIRST) = %d, %x\n", ptr, ptr);
- printf("rsize = %d\n", rsize);
+ if (end_pos >= f->size)
+ {
+ max = f->size - f->pos;
+ f->eof = 1;
+ }
+ else
+ {
+ max = sz;
+ }
- memcpy(ptr, onesec_buf + (f->pos & 2047), (rsize > max) ? max : rsize);
+ 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;
+ }
+ }
- // Middle sector
- ptr += max;
+ const size_t rem = sz - readb;
- //printf("ptr(MIDDLEsex) = %d, %x\n", ptr, ptr);
- nsect--;
- csize -= max;
+ if (rem)
+ {
+ const size_t whole_sectors = rem / SECTOR_SZ;
- if(nsect > 1)
+ if (whole_sectors)
{
- //lseek(f->fildes, (f->pos & (~0x7ff)) + 2048, SEEK_SET);
+ if (!read_complete_sector)
+ lseek(f->fildes, f->pos & ~(SECTOR_SZ - 1), SEEK_SET);
-//#warning "Check correctness of this calculation."
+ const size_t s = whole_sectors * SECTOR_SZ;
+ const int n = read(f->fildes, &((unsigned char *)ptr)[readb], s);
- /*if(rsize & 2047)
- sect_num = (rsize|2047)+1;
- else
- sect_num = rsize;
+ if (n >= 0)
+ {
+ readb += s;
+ f->pos += s;
+ ptr = (unsigned char *)ptr + s;
- sect_num -= 4096;*/
+ if (f->pos >= f->size)
+ f->eof = 1;
- //printf("read_middle=%d, sect_num = %d\n", read(f->fildes, ptr, sect_num), sect_num);
+ f->cache_available = 1;
+ }
+ }
- read(f->fildes, ptr, (nsect - 1) * 2048);
+ if (f->pos < f->size)
+ {
+ int n;
- ptr += (nsect - 1) * 2048;
- csize -= (nsect - 1) * 2048;
- nsect = 1;
- }
+ if (!read_complete_sector)
+ lseek(f->fildes, f->pos & ~(SECTOR_SZ - 1), SEEK_SET);
- //printf("ptr(LAST) = %d, %x\n", ptr, ptr);
+ n = read(f->fildes, f->buf, SECTOR_SZ);
- if(nsect == 1)
- {
- // Last sector
- read(f->fildes, onesec_buf, 2048);
+ 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;
- memcpy(ptr, onesec_buf, csize);
+ f->cache_available = 1;
+ }
}
}
- else if(f->dev == FDEV_CONSOLE)
- return 0;
+ 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
+ *
+ * This is done to make programming and porting easier
+ */
- if(f->dev != FDEV_CONSOLE)
+size_t fread(void *restrict const ptr, const size_t size, const size_t nmemb,
+ FILE *restrict const f)
+{
+ if (size && nmemb)
{
- f->pos+= rsize;
- f->eof = (f->pos > f->size);
+ size_t rsize = size * nmemb;
+
+ switch (f->dev)
+ {
+ case FDEV_CDROM:
+ rsize = cdromread(ptr, rsize, f);
+ break;
+
+ case FDEV_CONSOLE:
+ /* Fall through. */
+ default:
+ return 0;
+ }
- if(f->eof)
- f->pos = f->size;
+ return rsize / size;
}
- return rsize;
+ return 0;
}
int fgetc(FILE *f)