From 60ac041bb1001b2a4bce55e680dac700a8ec9874 Mon Sep 17 00:00:00 2001 From: Curtis F Kaylor Date: Thu, 8 Oct 2020 16:43:58 -0400 Subject: [PATCH] Multiple modifications to run6502.c and file6502.c --- lib6502/dirent.c | 7 + lib6502/dirent.h | 6 +- lib6502/error.h | 41 ++++ lib6502/file6502.c | 482 ++++++++++++++++++++++++++++++++------------- lib6502/file6502.h | 89 +++++++++ lib6502/lib6502.c | 2 +- lib6502/run6502.c | 14 ++ 7 files changed, 500 insertions(+), 141 deletions(-) create mode 100644 lib6502/error.h diff --git a/lib6502/dirent.c b/lib6502/dirent.c index 6168561..d54038c 100644 --- a/lib6502/dirent.c +++ b/lib6502/dirent.c @@ -13,12 +13,16 @@ #include /* _findfirst and _findnext set errno iff they return -1 */ #include #include +#include #ifdef __cplusplus extern "C" { #endif +#define EPOCH 116444736000000000 //January 1, 1970 as MS file time +#define RATIO 10000000 //Conversion Factor - Hundreds of Nanoseconds + typedef ptrdiff_t handle_type; /* C99's intptr_t not sufficiently portable */ struct DIR @@ -103,6 +107,9 @@ struct dirent *readdir(DIR *dir) if(!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) { result = &dir->result; + result->d_attr = dir->info.attrib; + result->d_size = dir->info.size; + result->d_time = localtime(&dir->info.time_write); result->d_name = dir->info.name; } } diff --git a/lib6502/dirent.h b/lib6502/dirent.h index bbbfce5..508309a 100644 --- a/lib6502/dirent.h +++ b/lib6502/dirent.h @@ -17,10 +17,14 @@ extern "C" #endif typedef struct DIR DIR; +typedef struct tm tm; struct dirent { - char *d_name; + int d_attr; //Attributes + unsigned d_size; //Size in Bytes + struct tm *d_time; //Timestamp + char *d_name; //Filename }; DIR *opendir(const char *); diff --git a/lib6502/error.h b/lib6502/error.h new file mode 100644 index 0000000..425e976 --- /dev/null +++ b/lib6502/error.h @@ -0,0 +1,41 @@ +/* Subset of Standard Unix/Posiz Error Codes */ + +#define ENOERROR 0 /* No Error */ +#define EPERM 1 /* Operation not permitted */ +#define ENOENT 2 /* No such file or directory */ +#define ESRCH 3 /* No such process */ +#define EINTR 4 /* Interrupted system call */ +#define EIO 5 /* Input/output error */ +#define ENXIO 6 /* No such device or address */ +#define E2BIG 7 /* Argument list too long */ +#define ENOEXEC 8 /* Exec format error */ +#define EBADF 9 /* Bad file descriptor */ +#define ECHILD 10 /* No child processes */ +#define EDEADLK 11 /* Resource deadlock avoided */ +#define ENOMEM 12 /* Cannot allocate memory */ +#define EACCES 13 /* Permission denied */ +#define EFAULT 14 /* Bad address */ +#define ENOTBLK 15 /* Block device required */ +#define EBUSY 16 /* Device busy */ +#define EEXIST 17 /* File exists */ +#define EXDEV 18 /* Cross-device link */ +#define ENOTDEV 19 /* Operation not supported by device */ +#define ENOTDIR 20 /* Not a directory */ +#define EISDIR 21 /* Is a directory */ +#define EINVAL 22 /* Invalid argument */ +#define ENFILE 23 /* Too many open files in system */ +#define EMFILE 24 /* Too many open files */ +#define ENOTTY 25 /* Inappropriate ioctl for device */ +#define ETXTBSY 26 /* Text file busy */ +#define EFBIG 27 /* File too large */ +#define ENOSPC 28 /* No space left on device */ +#define ESPIPE 29 /* Illegal seek */ +#define EROFS 30 /* Read-only file system */ +#define EMLINK 31 /* Too many links */ +#define EPIPE 32 /* Broken pipe */ +#define EDOM 33 /* Numerical argument out of domain */ +#define ERANGE 34 /* Result too large */ +#define EAGAIN 35 /* Resource temporarily unavailable */ +#define EALREADY 37 /* Operation already in progress */ +#define EBADFD 77 /* File descriptor in bad state */ +#define EEOF 255 /* End of File */ diff --git a/lib6502/file6502.c b/lib6502/file6502.c index f390544..f1c514c 100644 --- a/lib6502/file6502.c +++ b/lib6502/file6502.c @@ -5,10 +5,11 @@ #include #include #include +#include #include #include -#include "config.h" +#include "error.h" #include "lib6502.h" #include "file6502.h" @@ -18,23 +19,37 @@ typedef uint16_t word; int debug; +#define STRLEN 128 +#define STRSIZ STRLEN+1 + +/* DOS File Attributes */ +#define _A_ARCH 0x20 //Archive. Set whenever the file is changed, and cleared by the BACKUP command. +#define _A_HIDDEN 0x02 //Hidden file. Not normally seen with the DIR command, unless the /AH option is used. +#define _A_RDONLY 0x01 //Read-only. File cannot be opened for writing. +#define _A_SUBDIR 0x10 //Subdirectory. +#define _A_SYSTEM 0x04 //System file. Not normally seen with the DIR command, unless the /AS option is used. + /* I/O Blocks for File I/O */ #define MAXIOB 15 -#define STRLEN 128 +#define TDIR 'D' +#define TFILE 'F' + +typedef struct dirent dirent; + struct iocb { int opened; //Flag: file opened - char type; //Channel Type: 'F', 'D' + char type; //Channel Type: TFILE, TDIR char mode; //File Mode: 'R', 'W' int recsize; //Record Size (0=None) FILE *fp; //File Pointer DIR *dp; //Directory Pointer int errnum; //Last Error Number - char name[STRLEN]; //File/Directory Name + char name[STRSIZ]; //File/Directory Name }; static struct iocb iocbs[MAXIOB]; -static char filename[STRLEN]; //File name for open +static char filename[STRSIZ]; //File name for open static char filebuff[256]; //File I/O Buffer static word fileaddr; //File Read/Write Address static word fileindx; //File Record Size/Number @@ -91,6 +106,7 @@ int setiocb(int chan, int opened, char type, char mode, int recsize, FILE *fp, D void initiocb(int chan) { setiocb(chan, 0, ' ', ' ', 0, NULL, NULL, ""); + if (debug) fprintf(stderr, "cleared iocb for channel %d\n", chan); } void initiocbs(void) @@ -121,6 +137,54 @@ static int setname(M6502 *mpu, word addr, char *name) { return i; } +/* Normalize Drive ID */ +static char driveid(char drive) { + char id; + if (debug) fprintf(stderr, "normalizing drive $%02x\n", drive); + if (drive == 0 || strchr(" 0@", drive)) id = 0; //Default Drive + else id = toupper(drive); + if (debug) fprintf(stderr, "returning driveid $%02x\n", id); + return id; +} + +/* Convert Drive ID to Drive Number * + * Args: driveid - Drive ID (A-Z) * + * Returns: Drive Number (1-126, * + * 0 for Default, or -errno */ +static int driveno(char drvid) { + int id = driveid(drvid); + if (debug) fprintf(stderr, "converting drive id %02x\n", drvid); + if (id == 0 ) return 0; + if (id >='A' && id <='Z') return id - '@'; + return -seterror(0, ENXIO); //No such device or address +} + +/* Change Current Drive * + * Args: drivno - drive number (1-26) * + * Returns: Error (0=None) */ +static int chgdrive(char drvno) { + if (debug) fprintf(stderr, "changing default drive to %d: ", drvno); + int r = _chdrive(drvno); + if (r) r = seterror(0, errno); + else if (debug) fprintf(stderr, "default drvno changed\n"); + return r; +} + +/* Build File Spec */ +static int setspec(char *spec, char driveid, char* name, char* dflt) { + if (debug) fprintf(stderr, "building filespec for drive $%02x, name \"%s\"\n", driveid, name); + spec[0] = 0; //Set filespec to "" + int drive = driveno(driveid); if (drive < 0) return seterror(0, -drive); + for (int i = 0; imemory[fileaddr+i] = buffer[i]; + if (debug) fprintf(stderr, "wrote %d bytes\n", i); return i; } @@ -170,81 +236,139 @@ static int writestr(M6502 *mpu, char* buffer, int count) { return i; } +/* Convert tm struct to readable time */ +void strtm(char *s, tm *time) { + sprintf(s, "%d-%02d-%02d %02d:%02d:%02d", + time->tm_year+1900, time->tm_mon, time->tm_mday, + time->tm_hour, time->tm_min, time->tm_sec); +} + +int copystring(char *b, char *s, int ofs) { + if (debug) fprintf(stderr, "copying string \"$s\" to buffer at offset %d\n", s, ofs); + for (int i = 0; i < STRLEN; i++) { + if (s[i]) b[ofs++] = s[i]; else break; + } + b[ofs++] = 0; + return ofs; +} + +int copytime(char *b, tm *t, int ofs) { + char s[STRLEN]; strtm(s, t); + t->tm_mon = t->tm_mon + 1; //Adjust Zero-based Month + if (debug) fprintf(stderr, "copying timestamp %s to buffer at offset %d\n", s, ofs); + b[ofs++] = t->tm_year; b[ofs++] = t->tm_mon; b[ofs++] = t->tm_mday; + b[ofs++] = t->tm_hour; b[ofs++] = t->tm_min; b[ofs++] = t->tm_sec; + return ofs; +} + +int copyword(char *b, word w, int ofs) { + if (debug) fprintf(stderr, "copying word %04x to buffer at offset %d\n", w, ofs); + b[ofs++] = w & 0xFF; b[ofs++] = w >> 8; + return ofs; +} + +int copyattrs(char *b, int attr, int ofs) { + b[ofs++] = (attr & _A_ARCH) ? 0xFF : 00; + b[ofs++] = (attr & _A_HIDDEN) ? 0xFF : 00; + b[ofs++] = (attr & _A_RDONLY) ? 0xFF : 00; + b[ofs++] = (attr & _A_SUBDIR) ? 0xFF : 00; + b[ofs++] = (attr & _A_SYSTEM) ? 0xFF : 00; + return ofs; +} + +/* Copy dirent to character array matching run6502.h02 * + * struct dirent { * + * struct {char arch, hidden, system, rdonly, subdir, system} attr * + * struct {char year, month, day, hour, minute, second;} time; * + * int size, char name[128];} */ +int setentry(char *buffer, dirent *de) { + int offset = 0; + char ts[STRSIZ]; strtm(ts, de->d_time); + if (debug) fprintf(stderr, "copying entry \"%s\", attr=$%02x, size=%d, time=%s\n", + de->d_name, de->d_attr, de->d_size, ts); + offset = copyattrs(buffer, de->d_attr, offset); + offset = copytime(buffer, de->d_time, offset); + offset = copyword(buffer, de->d_size, offset); + offset = copystring(buffer, de->d_name, offset); + return offset; +} + /* Emulate fileio at addr */ extern int filecmd(M6502 *mpu, word addr, byte data) { - const char modes[8][4] = {"r", "rb", "w", "wb", "a", "ab", "r+", "rb+"}; + const char modes[8][4] = {"r", "rb", "w", "wb", "a", "ab", "a+", "ab+"}; + const char ddesc[2][9] = {"creating", "removing"}; + const char fdesc[2][10] = {"file", "directory"}; const char qdesc[2][7] = {"string", "line"}; int chan, e, i; - char c, m, n, mode[4]; - char *name; - struct dirent *de; + char c, d, m, n, t, mode[4]; + char *buffer; + char filespec[STRSIZ], newspec[STRSIZ] ; + struct tm time; + dirent *de; byte a = mpu->registers->a; byte x = mpu->registers->x; byte y = mpu->registers->y; byte p = mpu->registers->p; word yx = y << 8 | x; - char drive = y & 0x1f; - if (debug) fprintf(stderr, "executing '%c' with options %02x,%02x,%02x\n", a, y, x, p); + if (debug) fprintf(stderr, "executing file command '%c' with options %02x,%02x,%02x\n", a, y, x, p); switch (a) { //File I/O Command case 'A': //Set filebuffer address - Y.X = address if (p & 1) { fileindx = yx; - if (debug) fprintf(stderr, "file index set to %04x\n", fileaddr); + if (debug) fprintf(stderr, "file index set to %04x\n", fileindx); } else { fileaddr = yx; if (debug) fprintf(stderr, "file address set to %04x\n", fileaddr); } break; - case 'B': //Close Directory - Y = channel + //case 'B': //Unused + case 'C': //Close Channel - Y = channel, CC = File, CS = Directory chan = y; - if (debug) fprintf(stderr, "closing directory channel %d\n", chan); - y = valchan(chan, 'D'); if (y) break; - if (closedir(iocbs[chan].dp)) y = seterror(chan, errno); - else initiocb(chan); + m = p & 1; t = (m) ? TDIR : TFILE; + if (debug) fprintf(stderr, "closing %s channel %d\n", fdesc[m], chan); + y = valchan(chan, t); if (y) break; + if (m) e = closedir(iocbs[chan].dp); else e = fclose(iocbs[chan].fp); + if (e) y = seterror(chan, errno); else initiocb(chan); + if (debug) fprintf(stderr, "channel closed\n"); break; - case 'C': //Close file - Y = channel + case 'D': //Read Directory - Y = Channel, CC = Entry, CS = Header chan = y; - if (debug) fprintf(stderr, "closing file channel %d\n", chan); - y = valchan(chan, 'F'); if (y) break; - if (fclose(iocbs[chan].fp)) y = seterror(chan, errno); - else initiocb(chan); - break; - case 'D': //Open Directory - Y,X = Directory Name - x = 0; //File channel (none) - y = 0; //Error code (none) - chan = uniocb(); if (chan <0) {y = -chan; break;} - setname(mpu, yx, filename); - if (strlen(filename) == 0) strcpy(filename, "."); - if (debug) fprintf(stderr, "opening directory '%s'\n", filename, mode); - DIR *dp = opendir(filename); - if (dp == NULL) { y = seterror(0, errno); break;} - if (debug) fprintf(stderr, "directory opened on channel %d\n", chan); - y = setiocb(chan, -1, 'D', ' ', 0, NULL, dp, filename); //Setup IOCB - if (y == 0) x = chan; + x = 0; //Return Value (Read Failed) + y = valchan(chan, TDIR); if (y) break; + if (p & 1) { + if (debug) fprintf(stderr, "retrieving directory name\n"); + x = writestr(mpu, iocbs[chan].name, STRLEN); + } else { + if (debug) fprintf(stderr, "reading directory entry\n"); + de = readdir(iocbs[chan].dp); + if (de) {x = setentry(filebuff, de); writemem(mpu, filebuff, x);} + else if (errno != 2) y = seterror(chan, errno); + } break; case 'E': //EOF - Y = channel chan = y; - y = valchan(chan, 'F'); if (y) break; + y = valchan(chan, TFILE); if (y) break; y = feof(iocbs[chan].fp); break; case 'F': //Flush File - Y = Channel chan = y; - y = valchan(chan, 'F'); if (y) break; - if (fflush(iocbs[chan].fp)) y = seterror(chan, errno); + y = valchan(chan, TFILE); if (y) break; + x = fflush(iocbs[chan].fp); + if (x) y = seterror(chan, errno); break; - case 'G': //Get character - Y = channel + case 'G': //Get Character - Y = channel chan = y; x = 0; //Character read (none) - y = valchan(chan, 'F'); if (y) break; + y = valchan(chan, TFILE); if (y) break; c = fgetc(iocbs[chan].fp); - if (feof(iocbs[chan].fp)) {y = 255; break;} + if (feof(iocbs[chan].fp)) {y = EOF; break;} if (c == EOF) {y = seterror(chan, errno); break;} x = c & 0xFF; break; case 'H': //Get String - Y = channel chan = y; x = 0; //Number of Characters read - y = valchan(chan, 'F'); if (y) break; + y = valchan(chan, TFILE); if (y) break; char *s = fgets(filebuff, STRLEN, iocbs[chan].fp); if (s == NULL) {y = seterror(chan, errno); break;} if (debug) fprintf(stderr, "read string '%s'\n", filebuff); @@ -254,74 +378,100 @@ extern int filecmd(M6502 *mpu, word addr, byte data) { case 'I': //Init File System initiocbs(); //Initialize I/O Control Blocks break; - case 'J': //Read Directory Entry + case 'J': //Get/Put Word - Y = Channel, Carry Set = Put, Clear = Get chan = y; - x = 0; //Return Value (Read Failed) - y = valchan(chan, 'D'); if (y) break; + a = valchan(chan, TFILE); if (y) break; if (p & 1) { - if (debug) fprintf(stdout, "retrieving directory name\n"); - x = writestr(mpu, iocbs[chan].name, STRLEN); + y = fileaddr >> 8; x = fileaddr & 0xFF; + e = fputc(x, iocbs[chan].fp); + if (e == EOF) {a = seterror(chan, errno); break;} + e = fputc(y, iocbs[chan].fp); + if (e == EOF) {a = seterror(chan, errno); break;} } else { - if (debug) fprintf(stdout, "reading directory entry\n"); - de = readdir(iocbs[chan].dp); - if (de) { - if (debug) fprintf(stdout, "read entry '%s'\n", de->d_name); - x = writestr(mpu, de->d_name, STRLEN); - } - else if (errno != 2) y = seterror(chan, errno); + c = fgetc(iocbs[chan].fp); + if (feof(iocbs[chan].fp)) {a = EOF; break;} + if (c == EOF) {a = seterror(chan, errno); break;} + x = c & 0xFF; + c = fgetc(iocbs[chan].fp); + if (feof(iocbs[chan].fp)) {a = EOF; break;} + if (c == EOF) {a = seterror(chan, errno); break;} + y = c & 0xFF; } break; case 'K': //REMOVE - Delete File - Y,X = Filename - setname(mpu, yx, filename); - if (debug) fprintf(stderr, "removing file '%s'\n", filename); - x = remove(filename); - if (x) y=seterror(0, errno); else y=0; + d = driveid(y); + y = setspec(filespec, d, filename, "."); if (y) break; + if (debug) fprintf(stderr, "removing file '%s'\n", filespec); + e = remove(filespec); + if (e) y=seterror(0, errno); break; case 'L': //Load file - a = 0; //Error (none) + //ALLOW SPECIFYING DRIVE ID + a = ENOERROR; //Error (none) if (debug) fprintf(stderr, "loading file at %04h\n", fileaddr); e = load(mpu, fileaddr, filename); if (!e) {a = seterror(0, errno); break;} y = e >> 8; x = e & 0xff; break; - case 'M': //MOVE - Rename File - Y,X = Filename - setname(mpu, yx, filebuff); - if (debug) fprintf(stderr, "renaming file '%s' to '%s'\n", filename, filebuff); - x = rename(filename, filebuff); - if (x) y=seterror(0, errno); else y=0; + case 'M': //MOVE - Rename File - Y = Drive ID + d = driveid(y); //Not Implemented Yet + y = setspec(filespec, d, filename, "."); if (y) break; + y = setspec(newspec, d, filebuff, "."); if (y) break; + if (debug) fprintf(stderr, "renaming file \"%s\" to \"%s\"\n", filename, filebuff); + e = rename(filespec, newspec); + if (e) y=seterror(0, errno); else y=0; break; - case 'N': //Set filename - Y,X = string address - x = setname(mpu, yx, filename); //Set filename and Return Length - if (debug) fprintf(stderr, "filename set to '%s'\n", filename); + case 'N': //Set Name - Y,X = string address, CC = filename, CS = filebuff + m = p & 1; + if (m) { + x = setname(mpu, yx, filebuff); //Set filebuff and Return Length + if (debug) fprintf(stderr, "filebuff set to \"%s\"\n", filename); + } else { + x = setname(mpu, yx, filename); //Set filename and Return Length + if (debug) fprintf(stderr, "filename set to \"%s\"\n", filename); + } break; - case 'O': //Open file - Y = Drive#, X = Mode (Bits 7,6 = RWA, Bit 5 = Binary) - m = x >> 5 & 7; //Get Mode Index - x = 0; //File channel (none) - y = 0; //Error code (none) - strcpy(mode, modes[m]); //Set Mode from Mode Index - chan = uniocb(); if (chan <0) {y = seterror(0, -chan); break;} - if (debug) fprintf(stderr, "opening file '%s' with mode '%s'\n", filename, mode); - FILE *fp = fopen(filename, mode); - if (fp == NULL) { y = seterror(0, errno); break;} - if (debug) fprintf(stderr, "file opened on channel %d\n", chan); - i = (m == 3) ? fileindx : 0; //Set Records Size if Mode RECORD - y = setiocb(chan, -1, 'F', mode[0], i, fp, NULL, filename); //Setup IOCB - if (y == 0) x = chan; + case 'O': //Open file/directory - Y = Drive#, X = Mode, CC = File, CS = Directory + if (p & 1) { + x = 0; //File channel (none) + d = driveid(y); //Normalize Drive ID + y = setspec(filespec, d, filename, "."); if (y) {seterror(0, y); break;} + chan = uniocb(); if (chan <0) {y = -chan; break;} + if (debug) fprintf(stderr, "opening directory '%s'\n", filespec, mode); + DIR *dp = opendir(filespec); + if (dp == NULL) { y = seterror(0, errno); break;} + if (debug) fprintf(stderr, "directory opened on channel %d\n", chan); + y = setiocb(chan, -1, TDIR, ' ', 0, NULL, dp, filespec); //Setup IOCB + if (y == 0) x = chan; + } else { + m = x >> 5 & 7; //Get Mode Index + x = 0; //File channel (none) + y = ENOERROR; //Error code (none) + strcpy(mode, modes[m]); //Set Mode from Mode Index + chan = uniocb(); if (chan <0) {y = seterror(0, -chan); break;} + if (debug) fprintf(stderr, "opening file '%s' with mode '%s'\n", filename, mode); + FILE *fp = fopen(filename, mode); + if (fp == NULL) { y = seterror(0, errno); break;} + if (debug) fprintf(stderr, "file opened on channel %d\n", chan); + i = (m > 5) ? fileindx : 0; //Set Records Size if Mode RECORD + y = setiocb(chan, -1, TFILE, mode[0], i, fp, NULL, filename); //Setup IOCB + if (y == 0) x = chan; + } break; case 'P': //Put character - Y = channel; X = character chan = y; c = x; if (debug) fprintf(stderr, "writing '%c' to channel %d\n", c, chan); - a = 0; //Character written (none) - y = valchan(chan, 'F'); if (y) break; + x = 0xFF; //Character written (Error) + y = valchan(chan, TFILE); if (y) break; e = fputc(c, iocbs[chan].fp); if (e == EOF) {y = seterror(chan, errno); break;} - a = e & 0xFF; + x = e & 0xFF; break; - case 'Q': //Put String - Y = channel + case 'Q': //Put String - Y = channel, Carry Set = putline chan = y; x = 0; //Number of characters written - y = valchan(chan, 'F'); if (y) break; + y = valchan(chan, TFILE); if (y) break; for (i = 0; i<128; i++) { c = mpu->memory[fileaddr+i]; if (c) filebuff[i] = c; @@ -338,7 +488,7 @@ extern int filecmd(M6502 *mpu, word addr, byte data) { m = p & 1; //Set Mode: 0 = fread, 1 = fgetr chan = y; if (m) { - a = valchan(chan, 'F'); if (a) break; + a = valchan(chan, TFILE); if (a) break; if (debug) fprintf(stderr, "selecting record #%d\n", fileindx); n = iocbs[chan].recsize; i = fileindx * n; @@ -346,7 +496,7 @@ extern int filecmd(M6502 *mpu, word addr, byte data) { e = fseek(iocbs[chan].fp, i, SEEK_SET); if (e) {a = seterror(chan, errno); break;} } else { - y = valchan(chan, 'F'); if (y) break; + y = valchan(chan, TFILE); if (y) break; n = x; } if (debug) fprintf(stderr, "reading %d bytes\n", n); @@ -361,69 +511,83 @@ extern int filecmd(M6502 *mpu, word addr, byte data) { } break; case 'S': //Save file Y,X = end address - a = 0; //Error (none) + //CHANGE INPUT PARAMETER TO DRIVE ID + a = ENOERROR; //Error (none) if (debug) fprintf(stderr, "saving file from %04x to %04x\n", fileaddr, yx); e = save(mpu, fileaddr, yx-fileaddr-1, filename); if (!e) {a = seterror(0, errno); break;} y = e >> 8; x = e & 0xff; break; - case 'T': //Get Current Directory - YX = String Address - x = 0; //Directory Name Length - y = 0; //Error (None) - if (debug) fprintf(stderr, "getting current working directory\n"); - if (_getcwd(filename, STRLEN)) { - if (debug) fprintf(stderr, "cwd: %s\n", filename); - for (i = 0; imemory[yx+i] = c; - } - mpu->memory[yx+i] = 0; - x = i; - } - else y = seterror(0, errno); - break; - case 'U': //Change Directory - YX = Directory Name - x = 0; //Result (Success) - y = 0; //Error (None) - setname(mpu, yx, filename); //Set filename to Directory Name - if (debug) fprintf(stderr, "changing directory to '%s'\n", filename); - if (_chdir(filename)) {x = 0xFF; y = seterror(0, errno);} + //case 'T': //Unused + case 'U': //Get/Set Current Directory - Y = Drive/Disk ID, CC=Get. CS=Set + if (p & 1) { //Change Directory + d = driveid(y); + y = setspec(filespec, d, filename, "."); if (y) break; + if (debug) fprintf(stderr, "changing directory to \"%s\"\n", filespec); + if (_chdir(filespec)) {y = seterror(0, errno);} + } else { //Get Directory + x = 0; filename[0] = 0; //Clear filename, Set Length to 0 + d = driveno(y); if (d < 0) {y=seterror(0, -d); break;} + e = _getdrive(); if (e == 0) {y=seterror(0, errno); break;} + if (d && e != d ) {y = chgdrive(d); if (y) break;} + if (debug) fprintf(stderr, "getting current working directory for drive %d\n", d); + if (_getcwd(filename, STRLEN) == NULL) y = seterror(0, errno); + else { + if (debug) fprintf(stderr, "current working directory ie \"%s\"\n", filename); + x = writestr(mpu, filename, STRLEN); + } + if (d && e != d ) {e = chgdrive(e); if (e) seterror(0, errno);} + } break; case 'V': //Get or Set Current Drive, Y=Drive, Carry=Get/Set if (p & 1) { if (debug) fprintf(stderr, "changing drive to %c\n", y+'@'); - x = _chdrive(y); - if (x) y=seterror(0, errno); else y=0; + x = _chdrive(y); if (x) y=seterror(0, errno); else y=0; } else { - x = _getdrive(); - if (x) y=0; else y=seterror(0, errno); + x = _getdrive(); if (x) y=0; else y=seterror(0, errno); if (debug) fprintf(stderr, "current drive is %c\n", x+'@'); } break; - case 'W': //Write bytes - Y = channel, X=Number of Bytes + case 'W': //Write bytes - Y = channel, X = Number of Bytes + m = p & 1; //Set Mode: 0 = fread, 1 = fgetr chan = y; - y = valchan(chan, 'F'); if (y) break; - for (i = 0; imemory[fileaddr+i]; - if (debug) fprintf(stderr, "writing %d bytes\n", x); - e = fwrite(filebuff, x, 1, iocbs[chan].fp); - if (e != 1) {y = seterror(chan, errno); break;} - break; - case 'X': //Make/Remove Directory - YX = Directory Name - setname(mpu, yx, filename); - if (p & 1) { - if (debug) fprintf(stderr, "removing directory '%s'\n", filename); - x = _rmdir(filename); + if (m) { + a = valchan(chan, TFILE); if (a) break; + if (debug) fprintf(stderr, "selecting record #%d\n", fileindx); + n = iocbs[chan].recsize; + i = fileindx * n; + if (debug) fprintf(stderr, "setting position to %d\n", i); + e = fseek(iocbs[chan].fp, i, SEEK_SET); + if (e) {a = seterror(chan, errno); break;} + i = ftell(iocbs[chan].fp); + if (debug) fprintf(stderr, "position set to %d\n", i); } else { - if (debug) fprintf(stderr, "creating directory '%s'\n", filename); - x = _mkdir(filename); + y = valchan(chan, TFILE); if (y) break; + n = x; } - if (x) y=seterror(0, errno); else y=0; + for (i = 0; imemory[fileaddr+i]; + if (debug) fprintf(stderr, "writing %d bytes\n", n); + e = fwrite(filebuff, n, 1, iocbs[chan].fp); + if (debug) fprintf(stderr, "fwrite() returned %d\n", e); + if (e == 0) {e = seterror(chan, errno); if (m) a = e; else y = e; break; } + if (m) { + fileindx++; y = fileindx >> 8; x = fileindx & 0xff; + if (debug) fprintf(stderr, "returning record #%d\n", fileindx); + } + break; + case 'X': //Make/Remove Directory - Y = Drive ID, CC = RMDIR, CS = MKDIR + d = driveid(y); + m = p & 1; + y = setspec(filespec, d, filename, "."); if (y) break; + if (debug) fprintf(stderr, "%s directory '%s'\n", ddesc[m], filespec); + if (m) e = _rmdir(filespec); + else e = _mkdir(filespec); + if (e) y=seterror(0, errno); break; case 'Y': //Get Last Error: Y=chan chan = y; - y = 0; //Set Error to None + y = ENOERROR; //Set Error to None x = 0xFF; //Set Result to Invalid if (chan > MAXIOB) {y = 44; break;} if (debug) fprintf(stderr, "getting last error for channel %d\n", chan); @@ -434,26 +598,66 @@ extern int filecmd(M6502 *mpu, word addr, byte data) { break; case 'Z': //File Position: Y=chan, fileaddr = position, Carry = seek/tell chan = y; - y = valchan(chan, 'F'); if (y) break; + y = valchan(chan, TFILE); if (y) break; if (p & 1) { e = ftell(iocbs[chan].fp); if (e < 0) {a = seterror(chan, errno);} else { if (debug) fprintf(stderr, "returning position %d\n", e); - a = 0; y = e >> 8; x = e & 0xff; + a = ENOERROR; } + y = e >> 8; x = e & 0xff; } else { if (fileindx == 0xFFFF) {i = 0; m = SEEK_END;} else {i = fileindx, m = SEEK_SET;} if (debug) fprintf(stderr, "seeking position %d\n", i); - x = fseek(iocbs[chan].fp, i, m); - if (x) y = seterror(chan, errno); + e = fseek(iocbs[chan].fp, i, m); + if (e) y = seterror(chan, errno); } break; default: y = 22; //Error - invalid argument } - if (debug) fprintf(stderr, "returning values %02x, %02x, %02x\n", a, y, x); + if (debug) fprintf(stderr, "returning values %02x, %02x, %02x, %02x\n", a, y, x, p); + mpu->registers->a = a; + mpu->registers->x = x; + mpu->registers->y = y; +} + +/* Emulate system command dispatch at addr */ +extern int syscmd(M6502 *mpu, word addr, byte data) { + struct tm time; + dirent *de; + byte a = mpu->registers->a; + byte x = mpu->registers->x; + byte y = mpu->registers->y; + byte p = mpu->registers->p; + word yx = y << 8 | x; + if (debug) fprintf(stderr, "executing system command '%c' with options %02x,%02x,%02x\n", a, y, x, p); + switch(a) { + case 'C': //Get System Clock + if (debug) fprintf(stderr, "reading system clock\n"); + clock_t clk = clock(); //Get System Clock + if (debug) fprintf(stderr, "system clock = %d ticks\n", clk); + int i = (clk / 50) & 0xFFFFFF; //Convert to 24-bit integer, 50 ticks per second + a = i >> 16; y = i >> 8; x = i; //with resolution of 50 ticks per second + if (debug) fprintf(stderr, "returning $%02x%02x%02x (%d)\n", a, y, x, clk); + break; + case 'T': //Get/Set Date and Time - YX = tm address; CC = Get, CS = Set + if (p & 1) { + y = 0xFF; //Return Error - Not Implemented + } else { + fileaddr = yx; //Set Struct Address + _getsystime(&time); + x = copytime(filebuff, &time, 0); //Convert time struct + writemem(mpu, filebuff, x); //Write to Memory + y = 0; //Return Error = None + } + break; + default: + y = 22; //Error - invalid argument + } + if (debug) fprintf(stderr, "returning values %02x, %02x, %02x, %02x\n", a, y, x, p); mpu->registers->a = a; mpu->registers->x = x; mpu->registers->y = y; diff --git a/lib6502/file6502.h b/lib6502/file6502.h index 5d95bdf..cc1d372 100644 --- a/lib6502/file6502.h +++ b/lib6502/file6502.h @@ -6,4 +6,93 @@ typedef uint8_t byte; typedef uint16_t word; extern void setdebug(int dbg); + + +/********************************************************************************************** + * Process File Command * + * Command passed in A and parameters in X, Y, and Carry * + * 16-Bit values are passed with the MSB in Y and the LSB in X * + * Results returned in A, X, and Y * + * Unless otherwise specified, A contains the Command Code * + * Status of 0 indicates success, any other value indicates failure * + * For FOPEN only bits 5-7 of the File Mode are significant; bits 0-4 are ignored * + * Bits 6-7 are the access mode: %00=Read, %01=Write, %10=Append, %11=Records * + * Bit 5 is the access type: 0=text, 1=binary * + * * + * A=Command Description Parameters Returns * + * A SETADDR Set File Buffer Address Y,X=Address Y,X=Address * + * C FCLOSR Close File (Carry Clear) Y=Channel Y=Error * + * CLOSEDIR Close Directory (Carry Set) Y=Channel Y=Error * + * D READDIR Read Dir Entry (Carry Clear) Y=Channel X=Name Length, Y=Error * + * SETADDR Struct Address * + * READHDR Read Dir Header (Carry Set) Y=Channel X=Name Length, Y=Error * + * SETADDR Struct Address * + * E FEOF Check for End of File Y=Channel Y=EOF Indicator * + * F FFLUSH Flush File to Disk Y=Channel X=Status, Y=Error * + * G FGETC Read Character from File Y=Channel X=Character, Y=Error * + * H FGETS Read String from File Y=Channel X=String Legth, Y=Error * + * SETADDR Array Address * + * I FSINIT Initialize File System * + * G FGETW Read Integer (Carry Clear) Y=Channel Y.X=Integer, A=Error * + * FPUTW Write Integer (Carry Set) Y=Channel Y=Error * + * SETADDR Struct Address * + * K REMOVE Remove File from Disk Y=Drive Y=Error * + * SETNAME Filename * + * L FLOAD Load File into Memory Y,X=End Address, A=Error * + * SETNAME File Name * + * SETADDR Start Address * + * M RENAME Change Name of File on Disk Y=Drive Y=Error * + * SETNAME Old Name * + * SETBUFF New Name * + * N SETNAME Set Filename (Carry Clear) YX=Name YX=Name * + * SETBUFF Set File Buffer (Carry Set) YX=String YX=String * + * O FOPEN Open File (Carry Clear) Y=Drive, X=Mode X=Channel, Y=Error * + * SETNAME File Name * + * SETINDEX Record Length (MRECRD) * + * OPENDIR Open Directory (Carry Set) Y=Drive X=Channel, Y=Error * + * SETNAME Directory Name * + * P FPUTC Write Character to File Y=Channel, X=Char E=Status, Y=Error * + * Q FPUTS Write String (Carry Clear) Y=Channel X=String Length, Y=Error * + * SETADDR String Address * + * FPUTLN Write Line (Carry Set) Y=Channel X=String Length, Y=Error * + * SETADDR String Address * + * R FREAD Read Bytes (Carry Clear) Y=Channel, X=Count X=Count, Y=Error * + * SETADDR Array Address * + * FGETR Read Record (Carry Set) Y=Channel A=Error, YX=Next Index * + * SETADDR Struct * + * SETINDEX Record Address * + * S FSAVE Save File to Disk Y,X=End Address Y=Error * + * SETNAME File Name * + * SETADDR Start Address * + * U GETCWD Get Current Dir (Carry Clear) Y=Drive ID X=Name Length, Y=Error * + * SETADDR String Buffer * + * U CHDIR Change Directory (Carry Set) Y=Drive ID Y=Error * + * SETNAME Directory Name * + * V GETDRIVE Get Current Drive (Carry Clear) X=Drive ID, Y=Error * + * CHDRIVE Set Current Drive (Carry Set) Y=Drive ID Y=Error * + * W FWRITE Write Bytes (Carry Clear) Y=Channel, X=Count X=Count, Y=Error * + * X MKDIR Create Directory (Carry Clear) Y=Drive ID Y=Error * + * SETNAME Directory Name * + * RMDIR Remove Directory (Carry Set) Y=Drive ID Y=Error * + * SETNAME Directory Name * + * Y FERROR Get Last Error on Channel Y=Channel X=Channel Error, Y = Error * + * SETADDR Array Address * + * Z FSEEK Move to Position (Carry Clear) Y=Channel X=Status, Y=Error * + * SETINDEX Position * + * FTELL Get File Position (Carry Clear) Y=Channel A=Error, YX=Position * + **********************************************************************************************/ extern int filecmd(M6502 *mpu, word addr, byte data); + +/********************************************************************************************** + * Process System Command * + * Command passed in A and parameters in X, Y, and Carry * + * 16-Bit values are passed with the MSB in Y and the LSB in X * + * Results returned in A, X, and Y * + * Unless otherwise specified, A contains the Command Code * + * * + * A=Command Description Parameters Returns * + * C CLOCK Get System Clock A,Y,X=Clock (Seconds/50) * + * T GETTM Get Date/Time (Carry Clear) Y,X = tm Address * + * SETTM Set Date/Time Time (Carry Set Y,X = tm Address Y=Error * * + **********************************************************************************************/ +extern int syscmd(M6502 *mpu, word addr, byte data); diff --git a/lib6502/lib6502.c b/lib6502/lib6502.c index 8eb614b..615f728 100644 --- a/lib6502/lib6502.c +++ b/lib6502/lib6502.c @@ -583,7 +583,7 @@ enum { fetch(); \ tick(ticks); \ fflush(stdout); \ - fprintf(stderr, "\nundefined instruction %02X\n", memory[PC-1]); \ + fprintf(stderr, "\nundefined instruction %02X at address %04x\n", memory[PC-1], PC-1); \ return; #define phR(ticks, adrmode, R) \ diff --git a/lib6502/run6502.c b/lib6502/run6502.c index 98a606b..c546c06 100644 --- a/lib6502/run6502.c +++ b/lib6502/run6502.c @@ -259,6 +259,7 @@ static void usage(int status) fprintf(stream, " -N addr -- set NMI vector\n"); fprintf(stream, " -P addr -- emulate putchar(3) at addr\n"); fprintf(stream, " -R addr -- set RST vector\n"); + fprintf(stream, " -S addr -- emulate system commands at addr\n"); fprintf(stream, " -s addr last file -- save memory from addr to last in file\n"); fprintf(stream, " -v -- print version number then exit\n"); fprintf(stream, " -X addr -- terminate emulation if PC reaches addr\n"); @@ -407,6 +408,18 @@ static int doFtrap(int argc, char **argv, M6502 *mpu) return 1; } +static int STrap(M6502 *mpu, word addr, byte data) { + syscmd(mpu, addr, data); + rts; +} +static int doStrap(int argc, char **argv, M6502 *mpu) +{ + unsigned addr; + if (argc < 2) usage(1); + addr= htol(argv[1]); + M6502_setCallback(mpu, call, addr, STrap); + return 1; +} /* Emulate getchar(3) at addr */ static int gTrap(M6502 *mpu, word addr, byte data) { mpu->registers->a= getchar(); rts; } @@ -532,6 +545,7 @@ int main(int argc, char **argv) else if (!strcmp(*argv, "-N")) n= doNMI(argc, argv, mpu); else if (!strcmp(*argv, "-P")) n= doPtrap(argc, argv, mpu); else if (!strcmp(*argv, "-R")) n= doRST(argc, argv, mpu); + else if (!strcmp(*argv, "-S")) n= doStrap(argc, argv, mpu); else if (!strcmp(*argv, "-s")) n= doSave(argc, argv, mpu); else if (!strcmp(*argv, "-v")) n= doVersion(argc, argv, mpu); else if (!strcmp(*argv, "-X")) n= doXtrap(argc, argv, mpu);