From 50dca8b954c623b100e62f62cad0a2ab9b70e608 Mon Sep 17 00:00:00 2001 From: David Given Date: Sun, 16 Jun 2019 19:04:17 +0200 Subject: [PATCH] First at-least-slightly working version of the CP/M read/write stuff. Not as bad as I expected, but far too big. --- build.lua | 32 +++--- lang/cem/libcc.ansi/headers/ack/emufile.h | 2 + plat/cpm/descr | 1 + plat/cpm/include/ack/plat.h | 14 ++- plat/cpm/include/cpm.h | 18 ++-- plat/cpm/libsys/build.lua | 38 +++---- plat/cpm/libsys/close.c | 31 ++++-- plat/cpm/libsys/cpm_read_random_safe.c | 10 ++ plat/cpm/libsys/cpmsys.h | 22 ++++ plat/cpm/libsys/fcb.c | 70 +++++++++++++ plat/cpm/libsys/fclose.c | 20 ---- plat/cpm/libsys/fd.c | 19 ++++ plat/cpm/libsys/feof.c | 9 -- plat/cpm/libsys/ferror.c | 8 -- plat/cpm/libsys/fflush.c | 12 --- plat/cpm/libsys/getc.c | 36 ------- plat/cpm/libsys/lseek.c | 28 ++++-- plat/cpm/libsys/open.c | 59 +++++++++-- plat/cpm/libsys/putc.c | 17 ---- plat/cpm/libsys/read.c | 94 +++++++++++++++-- plat/cpm/libsys/write.c | 117 ++++++++++++++++++---- 21 files changed, 464 insertions(+), 193 deletions(-) create mode 100644 plat/cpm/libsys/cpm_read_random_safe.c create mode 100644 plat/cpm/libsys/cpmsys.h delete mode 100644 plat/cpm/libsys/fclose.c create mode 100644 plat/cpm/libsys/fd.c delete mode 100644 plat/cpm/libsys/feof.c delete mode 100644 plat/cpm/libsys/ferror.c delete mode 100644 plat/cpm/libsys/fflush.c delete mode 100644 plat/cpm/libsys/getc.c delete mode 100644 plat/cpm/libsys/putc.c diff --git a/build.lua b/build.lua index ccd113f1a..14110e4f5 100644 --- a/build.lua +++ b/build.lua @@ -7,24 +7,24 @@ vars.ackcflags = { vars.ackldflags = {} vars.plats = { "cpm", - "linux386", - "linux68k", - "linuxppc", - "linuxmips", - "osx386", - "osxppc", - "pc86", - "rpi", - "pdpv7", - "em22", +-- "linux386", +-- "linux68k", +-- "linuxppc", +-- "linuxmips", +-- "osx386", +-- "osxppc", +-- "pc86", +-- "rpi", +-- "pdpv7", +-- "em22", } vars.plats_with_tests = { - "cpm", - "linux68k", - "linux386", - "linuxppc", - "linuxmips", - "pc86", +-- "cpm", +-- "linux68k", +-- "linux386", +-- "linuxppc", +-- "linuxmips", +-- "pc86", } installable { diff --git a/lang/cem/libcc.ansi/headers/ack/emufile.h b/lang/cem/libcc.ansi/headers/ack/emufile.h index 186630e56..06a1ffcec 100644 --- a/lang/cem/libcc.ansi/headers/ack/emufile.h +++ b/lang/cem/libcc.ansi/headers/ack/emufile.h @@ -25,7 +25,9 @@ struct FILE { #define _IOWRITING 0x100 #define _IOAPPEND 0x200 +#if !defined BUFSIZ #define BUFSIZ 1024 +#endif #define FOPEN_MAX 20 extern FILE *__iotab[FOPEN_MAX]; diff --git a/plat/cpm/descr b/plat/cpm/descr index e01f2c389..c023fc45c 100644 --- a/plat/cpm/descr +++ b/plat/cpm/descr @@ -77,6 +77,7 @@ name led (.e:{TAIL}={PLATFORMDIR}/libem.a \ {PLATFORMDIR}/libsys.a \ {PLATFORMDIR}/libc.a \ + {PLATFORMDIR}/libsys.a \ {PLATFORMDIR}/libem.a \ {PLATFORMDIR}/libend.a) linker diff --git a/plat/cpm/include/ack/plat.h b/plat/cpm/include/ack/plat.h index 6e1bac6ae..d6270fe5c 100644 --- a/plat/cpm/include/ack/plat.h +++ b/plat/cpm/include/ack/plat.h @@ -6,17 +6,21 @@ #ifndef _ACK_PLAT_H #define _ACK_PLAT_H +/* The 8080 code generator doesn't do floating point. */ + +#define ACKCONF_WANT_STDIO_FLOAT 0 + /* We're providing a time() system call rather than wanting a wrapper around * gettimeofday() in the libc. */ #define ACKCONF_WANT_EMULATED_TIME 0 -/* CP/M's underlying file abstraction is weird and doesn't map well onto - * file descriptors. Disable the standard FILE* mechanism in favour of our - * own. - */ +/* Processes? CP/M? Hahahaha... */ -#define ACKCONF_WANT_EMULATED_FILE 0 #define ACKCONF_WANT_EMULATED_POPEN 0 +/* We have a very small address space, so override the default buffer size. */ + +#define BUFSIZ 256 + #endif diff --git a/plat/cpm/include/cpm.h b/plat/cpm/include/cpm.h index c7288856b..3ad97b183 100644 --- a/plat/cpm/include/cpm.h +++ b/plat/cpm/include/cpm.h @@ -67,10 +67,19 @@ extern uint8_t* cpm_ramtop; extern uint8_t cpm_cmdlinelen; extern char cpm_cmdline[0x7f]; +/* Special: parses a filename into an FCB. Returns the user code (if any). + * Warning: cannot fail (because CP/M filespecs are incredibly lax). */ + +extern uint8_t cpm_parse_filename(FCB* fcb, const char* filename); + /* Special: if the CCP hasn't been overwritten, returns to it; otherwise does * a warmboot. */ extern void cpm_exit(void); +/* Special: equivalent to cpm_read_random() except if you read unwritten data + * 0 is returned (and the buffer contains garbage). */ +extern uint8_t cpm_read_random_safe(FCB* fcb); + /* Extends cpm_ramtop over the CCP, for a little extra space. */ extern void cpm_overwrite_ccp(void); @@ -114,12 +123,7 @@ extern void cpm_overwrite_ccp(void); /* 37 */ extern uint8_t cpm_reset_drives(uint16_t drive_bitmap); /* 40 */ extern uint8_t cpm_write_random_filled(FCB* fcb); -/* File descriptor emulation */ - -struct FCBE -{ - FCB fcb; /* drive 0 means the console */ - uint8_t user; -}; +#define cpm_get_user() cpm_get_set_user(0xff) +#define cpm_set_user(u) cpm_get_set_user(u) #endif diff --git a/plat/cpm/libsys/build.lua b/plat/cpm/libsys/build.lua index 9091a6917..070290326 100644 --- a/plat/cpm/libsys/build.lua +++ b/plat/cpm/libsys/build.lua @@ -47,28 +47,28 @@ local bdos_calls = { local trap_calls = { "EARRAY", + "EBADGTO", + "EBADLAE", + "EBADLIN", + "EBADMON", + "EBADPC", + "EBADPTR", + "ECASE", + "ECONV", + "EFDIVZ", + "EFOVFL", + "EFUND", + "EFUNFL", + "EHEAP", + "EIDIVZ", + "EILLINS", + "EIOVFL", + "EIUND", + "EMEMFLT", + "EODDZ", "ERANGE", "ESET", - "EIOVFL", - "EFOVFL", - "EFUNFL", - "EIDIVZ", - "EFDIVZ", - "EIUND", - "EFUND", - "ECONV", "ESTACK", - "EHEAP", - "EILLINS", - "EODDZ", - "ECASE", - "EMEMFLT", - "EBADPTR", - "EBADPC", - "EBADLAE", - "EBADMON", - "EBADLIN", - "EBADGTO", "EUNIMPL", } diff --git a/plat/cpm/libsys/close.c b/plat/cpm/libsys/close.c index 1c570029b..be5ae0ec4 100644 --- a/plat/cpm/libsys/close.c +++ b/plat/cpm/libsys/close.c @@ -1,14 +1,31 @@ -/* $Source$ - * $State$ - * $Revision$ - */ - #include #include #include +#include +#include "cpmsys.h" int close(int fd) { - errno = EBADF; - return -1; + struct FCBE* fcbe = &__fd[fd]; + int result = 0; + uint8_t olduser; + + __init_file_descriptors(); + if (fcbe->fcb.f[0]) + { + /* There's an actual filename here, so assume it's an open file. */ + + olduser = cpm_get_user(); + cpm_set_user(fcbe->user); + if (cpm_close_file(&fcbe->fcb) == 0xff) + { + errno = EIO; + result = -1; + } + cpm_set_user(olduser); + } + + memset(fcbe, 0, sizeof(struct FCBE)); + return result; } + diff --git a/plat/cpm/libsys/cpm_read_random_safe.c b/plat/cpm/libsys/cpm_read_random_safe.c new file mode 100644 index 000000000..1c5838bd6 --- /dev/null +++ b/plat/cpm/libsys/cpm_read_random_safe.c @@ -0,0 +1,10 @@ +#include +#include + +uint8_t cpm_read_random_safe(FCB* fcb) +{ + uint8_t r = cpm_read_random(fcb); + if ((r == 1) || (r == 4)) + r = 0; + return r; +} diff --git a/plat/cpm/libsys/cpmsys.h b/plat/cpm/libsys/cpmsys.h new file mode 100644 index 000000000..d659f9e9f --- /dev/null +++ b/plat/cpm/libsys/cpmsys.h @@ -0,0 +1,22 @@ +#ifndef CPMSYS_H +#define CPMSYS_H + +/* File descriptor emulation */ + +struct FCBE +{ + uint16_t length; /* number of records */ + FCB fcb; /* drive 0 means the console, f[0] == 0 means unallocated */ + uint8_t user; + uint8_t offset; /* into current sector */ +}; + +#define NUM_FILE_DESCRIPTORS 8 +extern struct FCBE __fd[NUM_FILE_DESCRIPTORS]; +extern uint8_t __transfer_buffer[128]; + +extern void __init_file_descriptors(void); + +#define SECTOR_ALIGNED(s) (((s) & 0x7f) == 0) + +#endif diff --git a/plat/cpm/libsys/fcb.c b/plat/cpm/libsys/fcb.c index e8059de3e..0f98b5a4f 100644 --- a/plat/cpm/libsys/fcb.c +++ b/plat/cpm/libsys/fcb.c @@ -1,3 +1,73 @@ #include #include +#include +#include +#include "cpmsys.h" + +uint8_t cpm_parse_filename(FCB* fcb, const char* filename) +{ + uint8_t user = cpm_get_user(); + + memset(fcb, 0, sizeof(FCB)); + memset(fcb->f, ' ', sizeof(fcb->f)); + + if (strchr(filename, ':')) + { + char c = *filename++; + if (isdigit(c)) + { + user = c - '0'; + c = *filename++; + if (isdigit(c)) + { + user = (user*10) + (c - '0'); + c = *filename++; + } + } + c = toupper(c); + if (isalpha(c)) + { + fcb->dr = c - '@'; + c = *filename++; + } + + while (c != ':') + c = *filename++; + } + + /* Read filename part. */ + + { + uint8_t i = 8; + uint8_t* p = &fcb->f[0]; + while (i--) + { + char c = toupper(*filename++); + if (c == '.') + break; + if (!c) + goto exit; + *p++ = c; + } + } + + /* Read extension part. */ + + { + uint8_t i = 3; + uint8_t* p = &fcb->f[8]; + while (i--) + { + char c = toupper(*filename++); + if (!c) + goto exit; + *p++ = c; + } + } + +exit: + if (fcb->dr == 0) + fcb->dr = cpm_get_current_drive() + 1; + return user; +} diff --git a/plat/cpm/libsys/fclose.c b/plat/cpm/libsys/fclose.c deleted file mode 100644 index abae8a6c0..000000000 --- a/plat/cpm/libsys/fclose.c +++ /dev/null @@ -1,20 +0,0 @@ -#include -#include -#include - -int fclose(FILE* stream) -{ - if (stream == CPM_FAKE_FILE) - return 0; - - if (fflush(stream)) - return; - - cpm_get_set_user(stream->user); - if (cpm_close_file(&stream->fcb) == 0xff) - { - errno = EIO; - return -1; - } - return 0; -} diff --git a/plat/cpm/libsys/fd.c b/plat/cpm/libsys/fd.c new file mode 100644 index 000000000..e98c9d7b2 --- /dev/null +++ b/plat/cpm/libsys/fd.c @@ -0,0 +1,19 @@ +#include +#include +#include +#include +#include "cpmsys.h" + +struct FCBE __fd[NUM_FILE_DESCRIPTORS]; +uint8_t __transfer_buffer[128]; + +void __init_file_descriptors(void) +{ + static uint8_t initialised = 0; + if (!initialised) + { + /* Mark stdin, stdout, stderr as being open files. */ + __fd[0].fcb.f[0] = __fd[1].fcb.f[0] = __fd[2].fcb.f[0] = ' '; + initialised = 1; + } +} diff --git a/plat/cpm/libsys/feof.c b/plat/cpm/libsys/feof.c deleted file mode 100644 index a04a76a78..000000000 --- a/plat/cpm/libsys/feof.c +++ /dev/null @@ -1,9 +0,0 @@ -#include -#include -#include - -int cpm_feof(FILE* stream) -{ - return 0; -} - diff --git a/plat/cpm/libsys/ferror.c b/plat/cpm/libsys/ferror.c deleted file mode 100644 index b279c8cec..000000000 --- a/plat/cpm/libsys/ferror.c +++ /dev/null @@ -1,8 +0,0 @@ -#include -#include -#include - -int cpm_ferror(FILE* stream) -{ - return 0; -} diff --git a/plat/cpm/libsys/fflush.c b/plat/cpm/libsys/fflush.c deleted file mode 100644 index 664488467..000000000 --- a/plat/cpm/libsys/fflush.c +++ /dev/null @@ -1,12 +0,0 @@ -#include -#include -#include - -int fflush(FILE* stream) -{ - if (stream == CPM_FAKE_FILE) - return 0; - - errno = EBADF; - return -1; -} \ No newline at end of file diff --git a/plat/cpm/libsys/getc.c b/plat/cpm/libsys/getc.c deleted file mode 100644 index bb322fc04..000000000 --- a/plat/cpm/libsys/getc.c +++ /dev/null @@ -1,36 +0,0 @@ -#include -#include -#include - -struct buffer -{ - uint8_t mx; - uint8_t nc; - uint8_t c[128]; -}; - -static struct buffer buffer; -static uint8_t pos; - -int read_from_console(void) -{ - while (pos == buffer.nc) - { - /* Refill buffer. */ - buffer.mx = sizeof(buffer.c); - buffer.nc = 0; - cpm_readline((uint8_t*) &buffer); - pos = 0; - } - - return buffer.c[pos++]; -} - -int cpm_getc(FILE* stream) -{ - if (stream == CPM_FAKE_FILE) - return read_from_console(); - - errno = EBADF; - return -1; -} diff --git a/plat/cpm/libsys/lseek.c b/plat/cpm/libsys/lseek.c index ecbc4b520..ddc38df03 100644 --- a/plat/cpm/libsys/lseek.c +++ b/plat/cpm/libsys/lseek.c @@ -1,14 +1,28 @@ -/* $Source$ - * $State$ - * $Revision$ - */ - +#include #include #include +#include #include +#include +#include "cpmsys.h" off_t lseek(int fd, off_t offset, int whence) { - errno = EINVAL; - return -1; + struct FCBE* fcbe = &__fd[fd]; + + __init_file_descriptors(); + if (fcbe->fcb.dr == 0) + { + /* Can't seek the console. */ + return 0; + } + + if (whence == SEEK_END) + offset += fcbe->length<<7; + if (whence == SEEK_CUR) + offset += (U16(fcbe->fcb.r)<<7) | fcbe->offset; + + U16(fcbe->fcb.r) = offset>>7; + fcbe->offset = offset & 0x7f; + return 0; } diff --git a/plat/cpm/libsys/open.c b/plat/cpm/libsys/open.c index f3522eae5..82345fb55 100644 --- a/plat/cpm/libsys/open.c +++ b/plat/cpm/libsys/open.c @@ -1,14 +1,61 @@ -/* $Source$ - * $State$ - * $Revision$ - */ - +#include #include #include #include +#include +#include "cpmsys.h" int open(const char* path, int access, ...) { - errno = EACCES; + uint8_t fd = 0; + struct FCBE* fcbe = &__fd[0]; + uint8_t olduser; + + __init_file_descriptors(); + while (fd != NUM_FILE_DESCRIPTORS) + { + if (fcbe->fcb.f[0] == 0) + break; + fd++; + fcbe++; + } + if (fd == NUM_FILE_DESCRIPTORS) + { + errno = EMFILE; + return -1; + } + + fcbe->user = cpm_parse_filename(&fcbe->fcb, path); + + olduser = cpm_get_user(); + cpm_set_user(fcbe->user); + + if (access & O_TRUNC) + { + cpm_delete_file(&fcbe->fcb); + access |= O_CREAT; + } + if (access & O_CREAT) + { + if (cpm_make_file(&fcbe->fcb) == 0xff) + goto eio; + } + else + { + if (cpm_open_file(&fcbe->fcb) == 0xff) + goto eio; + } + + cpm_seek_to_end(&fcbe->fcb); + fcbe->length = U16(fcbe->fcb.r); + if (!(access & O_APPEND)) + U16(fcbe->fcb.r) = 0; + return fd; + +eio: + fcbe->fcb.f[0] = 0; + errno = EIO; return -1; } + + diff --git a/plat/cpm/libsys/putc.c b/plat/cpm/libsys/putc.c deleted file mode 100644 index f78027d97..000000000 --- a/plat/cpm/libsys/putc.c +++ /dev/null @@ -1,17 +0,0 @@ -#include -#include -#include - -int cpm_putc(int c, FILE* stream) -{ - if (stream == CPM_FAKE_FILE) - { - cpm_conout(c); - if (c == '\n') - cpm_conout(c); - return 0; - } - - errno = EBADF; - return -1; -} diff --git a/plat/cpm/libsys/read.c b/plat/cpm/libsys/read.c index 952995af8..1d64faaf6 100644 --- a/plat/cpm/libsys/read.c +++ b/plat/cpm/libsys/read.c @@ -7,18 +7,100 @@ #include #include #include +#include #include +#include "cpmsys.h" ssize_t read(int fd, void* buffer, size_t count) { - short save; - unsigned char before_n; + uint8_t* bbuffer = buffer; + struct FCBE* fcbe = &__fd[fd]; + uint8_t olduser; + ssize_t result; - if (fd != 0) + __init_file_descriptors(); + if (fcbe->fcb.dr == 0) { - errno = EBADF; - return -1; + /* Read from the console. */ + + if (count == 0) + return 0; + *(uint8_t*)buffer = cpm_conin(); + return 1; } - return fread(buffer, 1, count, stdin); + olduser = cpm_get_user(); + cpm_set_user(fcbe->user); + + if (U16(fcbe->fcb.r) >= fcbe->length) + goto done; + if (fcbe->offset || !SECTOR_ALIGNED(count)) + { + uint8_t delta; + + /* We need to read bytes until we're at a sector boundary. */ + + cpm_set_dma(__transfer_buffer); + if (cpm_read_random_safe(&fcbe->fcb) != 0) + goto eio; + + /* Copy enough bytes to reach the end of the sector. */ + + delta = 128 - fcbe->offset; + if (delta > count) + delta = count; + memcpy(bbuffer, __transfer_buffer+fcbe->offset, delta); + fcbe->offset += delta; + count -= delta; + bbuffer += delta; + + /* If we've read enough bytes, advance to the next sector. */ + + if (fcbe->offset == 128) + { + U16(fcbe->fcb.r)++; + fcbe->offset = 0; + } + } + + while (count >= 128) + { + if (U16(fcbe->fcb.r) >= fcbe->length) + goto done; + + /* Read entire sectors directly into the destination buffer. */ + + cpm_set_dma(bbuffer); + if (cpm_read_random_safe(&fcbe->fcb) != 0) + goto eio; + count -= 128; + bbuffer += 128; + U16(fcbe->fcb.r)++; + } + + if (count != 0) + { + if (U16(fcbe->fcb.r) >= fcbe->length) + goto done; + + /* There's some trailing data to read. */ + + cpm_set_dma(__transfer_buffer); + if (cpm_read_random_safe(&fcbe->fcb) != 0) + goto eio; + + memcpy(bbuffer, __transfer_buffer, count); + fcbe->offset = count; + } + +done: + result = bbuffer - (uint8_t*)buffer; +exit: + cpm_set_user(olduser); + return result; + +eio: + errno = EIO; + result = -1; + goto exit; } diff --git a/plat/cpm/libsys/write.c b/plat/cpm/libsys/write.c index 768e9b2eb..384425c08 100644 --- a/plat/cpm/libsys/write.c +++ b/plat/cpm/libsys/write.c @@ -6,7 +6,9 @@ #include #include #include +#include #include +#include "cpmsys.h" void _sys_write_tty(char c) { @@ -17,24 +19,103 @@ void _sys_write_tty(char c) ssize_t write(int fd, void* buffer, size_t count) { - int i; - char* p = buffer; - - /* We're only allowed to write to fd 0, 1 or 2. */ - - if ((fd < 0) || (fd > 2)) + uint8_t* bbuffer = buffer; + struct FCBE* fcbe = &__fd[fd]; + uint8_t olduser; + uint16_t result; + + __init_file_descriptors(); + if (fcbe->fcb.dr == 0) { - errno = EBADF; - return -1; + /* Write to the console. */ + + size_t i = count; + while (i--) + _sys_write_tty(*bbuffer++); + return count; } - - /* Write all data. */ - - i = count; - while (i--) - _sys_write_tty(*p++); - - /* No failures. */ - - return count; + + olduser = cpm_get_user(); + cpm_set_user(fcbe->user); + + if (fcbe->offset || !SECTOR_ALIGNED(count)) + { + uint8_t delta; + + /* We're not at a sector boundary, so we need to do a + * read/modify/write of the initial fragment. */ + + cpm_set_dma(__transfer_buffer); + if (cpm_read_random_safe(&fcbe->fcb) != 0) + goto eio; + + /* Copy enough bytes to reach the end of the sector. */ + + delta = 128 - fcbe->offset; + if (delta > count) + delta = count; + memcpy(__transfer_buffer+fcbe->offset, bbuffer, delta); + fcbe->offset += delta; + count -= delta; + bbuffer += delta; + + /* Write back. */ + + if (cpm_write_random(&fcbe->fcb) != 0) + goto eio; + + /* If we've written enough bytes, advance to the next sector. */ + + if (fcbe->offset == 128) + { + U16(fcbe->fcb.r)++; + fcbe->offset = 0; + } + } + + while (count >= 128) + { + /* Write entire sectors directly from the source buffer. */ + + cpm_set_dma(bbuffer); + if (cpm_write_random(&fcbe->fcb) != 0) + goto eio; + count -= 128; + bbuffer += 128; + U16(fcbe->fcb.r)++; + } + + if (count != 0) + { + /* There's some trailing data to write. We need another + * read/modify/write cycle. */ + + cpm_set_dma(__transfer_buffer); + if (cpm_read_random_safe(&fcbe->fcb) != 0) + goto eio; + + memcpy(__transfer_buffer, bbuffer, count); + + if (cpm_write_random(&fcbe->fcb) != 0) + goto eio; + + fcbe->offset = count; + } + + if (U16(fcbe->fcb.r) >= fcbe->length) + { + fcbe->length = U16(fcbe->fcb.r); + if (fcbe->offset != 0) + fcbe->length++; + } + + result = bbuffer - (uint8_t*)buffer; +exit: + cpm_set_user(olduser); + return result; + +eio: + errno = EIO; + result = -1; + goto exit; }