1
0
mirror of https://github.com/RevCurtisP/C02.git synced 2024-11-22 01:31:33 +00:00

Added File Command Processor to run6502 emulator

This commit is contained in:
Curtis F Kaylor 2020-09-21 12:31:56 -04:00
parent e9d5c04f87
commit 17e487d4a5
4 changed files with 597 additions and 10 deletions

148
lib6502/dirent.c Normal file
View File

@ -0,0 +1,148 @@
/*
Implementation of POSIX directory browsing functions and types for Win32.
Author: Kevlin Henney (kevlin@acm.org, kevlin@curbralan.com)
History: Created March 1997. Updated June 2003 and July 2012.
Rights: See end of file.
*/
#include <dirent.h>
#include <errno.h>
#include <io.h> /* _findfirst and _findnext set errno iff they return -1 */
#include <stdlib.h>
#include <string.h>
#ifdef __cplusplus
extern "C"
{
#endif
typedef ptrdiff_t handle_type; /* C99's intptr_t not sufficiently portable */
struct DIR
{
handle_type handle; /* -1 for failed rewind */
struct _finddata_t info;
struct dirent result; /* d_name null iff first time */
char *name; /* null-terminated char string */
};
DIR *opendir(const char *name)
{
DIR *dir = 0;
if(name && name[0])
{
size_t base_length = strlen(name);
const char *all = /* search pattern must end with suitable wildcard */
strchr("/\\", name[base_length - 1]) ? "*" : "/*";
if((dir = (DIR *) malloc(sizeof *dir)) != 0 &&
(dir->name = (char *) malloc(base_length + strlen(all) + 1)) != 0)
{
strcat(strcpy(dir->name, name), all);
if((dir->handle =
(handle_type) _findfirst(dir->name, &dir->info)) != -1)
{
dir->result.d_name = 0;
}
else /* rollback */
{
free(dir->name);
free(dir);
dir = 0;
}
}
else /* rollback */
{
free(dir);
dir = 0;
errno = ENOMEM;
}
}
else
{
errno = EINVAL;
}
return dir;
}
int closedir(DIR *dir)
{
int result = -1;
if(dir)
{
if(dir->handle != -1)
{
result = _findclose(dir->handle);
}
free(dir->name);
free(dir);
}
if(result == -1) /* map all errors to EBADF */
{
errno = EBADF;
}
return result;
}
struct dirent *readdir(DIR *dir)
{
struct dirent *result = 0;
if(dir && dir->handle != -1)
{
if(!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1)
{
result = &dir->result;
result->d_name = dir->info.name;
}
}
else
{
errno = EBADF;
}
return result;
}
void rewinddir(DIR *dir)
{
if(dir && dir->handle != -1)
{
_findclose(dir->handle);
dir->handle = (handle_type) _findfirst(dir->name, &dir->info);
dir->result.d_name = 0;
}
else
{
errno = EBADF;
}
}
#ifdef __cplusplus
}
#endif
/*
Copyright Kevlin Henney, 1997, 2003, 2012. All rights reserved.
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose is hereby granted without fee, provided
that this copyright and permissions notice appear in all copies and
derivatives.
This software is supplied "as is" without express or implied warranty.
But that said, if there are any problems please get in touch.
*/

50
lib6502/dirent.h Normal file
View File

@ -0,0 +1,50 @@
#ifndef DIRENT_INCLUDED
#define DIRENT_INCLUDED
/*
Declaration of POSIX directory browsing functions and types for Win32.
Author: Kevlin Henney (kevlin@acm.org, kevlin@curbralan.com)
History: Created March 1997. Updated June 2003.
Rights: See end of file.
*/
#ifdef __cplusplus
extern "C"
{
#endif
typedef struct DIR DIR;
struct dirent
{
char *d_name;
};
DIR *opendir(const char *);
int closedir(DIR *);
struct dirent *readdir(DIR *);
void rewinddir(DIR *);
/*
Copyright Kevlin Henney, 1997, 2003. All rights reserved.
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose is hereby granted without fee, provided
that this copyright and permissions notice appear in all copies and
derivatives.
This software is supplied "as is" without express or implied warranty.
But that said, if there are any problems please get in touch.
*/
#ifdef __cplusplus
}
#endif
#endif

View File

@ -25,12 +25,16 @@
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <direct.h>
#include <dirent.h>
#include "config.h"
#include "lib6502.h"
#define VERSION PACKAGE_NAME " " PACKAGE_VERSION " " PACKAGE_COPYRIGHT
#define DEBUG 0
typedef uint8_t byte;
typedef uint16_t word;
@ -38,6 +42,24 @@ static char *program= 0;
static byte bank[0x10][0x4000];
/* I/O Blocks for File I/O */
#define MAXIOB 15
#define STRLEN 128
struct iocb
{
int opened; //Flag: file opened
char type; //Channel Type: 'F', 'D'
char mode; //File Mode: 'R', 'W'
FILE *fp; //File Pointer
DIR *dp; //Directory Pointer
int errnum; //Last Error Number
char name[STRLEN]; //File/Directory Name
};
static struct iocb iocbs[MAXIOB];
static char filename[STRLEN]; //File name for open
static char filebuff[256]; //File I/O Buffer
static word fileaddr; //File Read/Write Address
void fail(const char *fmt, ...)
{
@ -247,11 +269,13 @@ static void usage(int status)
fprintf(stream, "usage: %s [option ...]\n", program);
fprintf(stream, " %s [option ...] -B [image ...]\n", program);
fprintf(stream, " -B -- minimal Acorn 'BBC Model B' compatibility\n");
fprintf(stream, " -d addr last -- dump memory between addr and last\n");
fprintf(stream, " -d addr last -- disassemble memory between addr and last\n");
fprintf(stream, " -F addr -- emulate fileio at addr\n");
fprintf(stream, " -G addr -- emulate getchar(3) at addr\n");
fprintf(stream, " -h -- help (print this message)\n");
fprintf(stream, " -I addr -- set IRQ vector\n");
fprintf(stream, " -l addr file -- load file at addr\n");
fprintf(stream, " -m addr last -- dump memory between addr and last\n");
fprintf(stream, " -M addr -- emulate memory-mapped stdio at addr\n");
fprintf(stream, " -N addr -- set NMI vector\n");
fprintf(stream, " -P addr -- emulate putchar(3) at addr\n");
@ -317,7 +341,7 @@ static int save(M6502 *mpu, word address, unsigned length, const char *path)
{
FILE *file= 0;
int count= 0;
if (!(file= fopen(path, "w")))
if (!(file= fopen(path, "wb")))
return 0;
while ((count= fwrite(mpu->memory + address, 1, length, file)))
{
@ -325,7 +349,7 @@ static int save(M6502 *mpu, word address, unsigned length, const char *path)
length -= count;
}
fclose(file);
return 1;
return address;
}
@ -334,18 +358,19 @@ static int load(M6502 *mpu, word address, const char *path)
FILE *file= 0;
int count= 0;
size_t max= 0x10000 - address;
if (!(file= fopen(path, "r")))
if (!(file= fopen(path, "rb")))
return 0;
if (DEBUG) fprintf(stderr, "loading %s\nstart address %04x\n", path, address);
while ((count= fread(mpu->memory + address, 1, max, file)) > 0)
{
address += count;
max -= count;
}
{
address += count;
max -= count;
}
fclose(file);
return 1;
if (DEBUG) fprintf(stderr, "end address %04x\n", address);
return address;
}
static int doLoadInterpreter(int argc, char **argv, M6502 *mpu)
{
if (argc < 3) usage(1);
@ -386,6 +411,349 @@ doVEC(RST);
#undef doVEC
int setiocb(int chan, int opened, char type, char mode, FILE *fp, DIR *dp, char *name) {
iocbs[chan].opened = opened;
iocbs[chan].type = type;
iocbs[chan].mode = mode;
iocbs[chan].fp = fp;
iocbs[chan].dp = dp;
iocbs[chan].errnum = 0;
strcpy(iocbs[chan].name, name);
return 0;
}
void initiocb(int chan) {
setiocb(chan, 0, ' ', ' ', NULL, NULL, "");
}
void initiocbs(void)
{
filename[0] = 0; //Set filename to ""
for (int chan=0; chan<=MAXIOB; chan++)
initiocb(chan);
}
/* Set Error Number and Error Message */
static int seterror(int chan) {
int errnum = errno;
if (DEBUG) fprintf(stderr, "setting channel %d error to %d\n", chan, errnum);
iocbs[chan].errnum = errnum;
return errnum;
}
/* Set File Name */
static int setname(M6502 *mpu, word addr, char *name) {
int i;
for (i=0; i<STRLEN; i++) {
char c = mpu->memory[addr + i & 0xFFFF];
if (c) name[i] = c;
else break;
}
name[i] = 0; //Terminate String
return i;
}
/* Find Unused IOCB */
static int uniocb(void) {
int chan;
for (chan=1; chan<=MAXIOB; chan++) {
if (iocbs[chan].opened == 0) break;
if (chan > MAXIOB) {chan = -24; break;}
}
return chan;
}
/* Validate Channel */
static int valchan(int chan, char valtype) {
int errnum = 0; //Error (none)
if (DEBUG) fprintf(stderr, "validating channel %d\n", chan);
if (chan > MAXIOB) errnum = 44; //Channel number out of range
else if (iocbs[chan].opened == 0) errnum = 9; //Bad file descriptor
else if (iocbs[chan].type != valtype) {
if (DEBUG) fprintf(stderr, "invalid channel type '%c'\n", iocbs[chan].type);
switch (iocbs[chan].type) {
case 'D': errnum = 21; //Is a directory
case 'F': errnum = 20; //Not a directory
default: errnum = 77; //File descriptor in bad state
}
}
return errnum;
}
/* Write Buffer to Memory */
static int writemem(M6502 *mpu, char* buffer, int count) {
int i;
for (i = 0; i<count; i++)
mpu->memory[fileaddr+i] = buffer[i];
return i;
}
/* Write String to Memory */
static int writestr(M6502 *mpu, char* buffer, int count) {
int i;
if (DEBUG) fprintf(stderr, "writing '%s' to address %04x\n", buffer, fileaddr);
for (i = 0; i<count; i++) {
char c = buffer[i];
if (c == 0) break;
mpu->memory[fileaddr+i] = c;
}
mpu->memory[fileaddr+i] = 0; //Terminate String
if (DEBUG) fprintf(stderr, "wrote %d characters\n", i);
return i;
}
/* Emulate fileio at addr */
static int fTrap(M6502 *mpu, word addr, byte data) {
const char modes[2][3] = {"r", "w"};
int chan, e, i;
char c, mode[3];
char *name;
struct 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);
switch (a) { //File I/O Command
case 'A': //Set filebuffer address - Y.X = address
fileaddr = yx;
if (DEBUG) fprintf(stdout, "file address set to %04x\n", fileaddr);
break;
case 'B': //Close Directory - Y = channel
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);
else initiocb(chan);
break;
case 'C': //Close file - Y = channel
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);
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); break;}
if (DEBUG) fprintf(stderr, "directory opened on channel %d\n", chan);
y = setiocb(chan, -1, 'D', ' ', NULL, dp, filename); //Setup IOCB
if (y == 0) x = chan;
break;
case 'E': //EOF - Y = channel
chan = y;
y = valchan(chan, 'F'); 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);
break;
case 'G': //Get character - Y = channel
chan = y;
x = 0; //Character read (none)
y = valchan(chan, 'F'); if (y) break;
c = fgetc(iocbs[chan].fp);
if (feof(iocbs[chan].fp)) {y = 255; break;}
if (c == EOF) {y = seterror(chan); 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;
char *s = fgets(filebuff, STRLEN, iocbs[chan].fp);
if (s == NULL) {y = seterror(chan); break;}
if (DEBUG) fprintf(stderr, "read string '%s'\n", filebuff);
writestr(mpu, filebuff, STRLEN);
break;
case 'I': //Init File System
initiocbs(); //Initialize I/O Control Blocks
break;
case 'J': //Read Directory Entry
chan = y;
x = 0; //Return Value (Read Failed)
y = valchan(chan, 'D'); if (y) break;
if (p & 1) {
if (DEBUG) fprintf(stdout, "retrieving directory name\n");
x = writestr(mpu, iocbs[chan].name, STRLEN);
} 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);
}
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); else y=0;
break;
case 'L': //Load file
a = 0; //Error (none)
if (DEBUG) fprintf(stderr, "loading file at %04h\n", fileaddr);
e = load(mpu, fileaddr, filename);
if (!e) {a = seterror(0); 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); 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);
break;
case 'O': //Open file - Y = mode
strcpy(mode, modes[y>>7]);
x = 0; //File channel (none)
y = 0; //Error code (none)
chan = uniocb(); if (chan <0) {y = -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); break;}
if (DEBUG) fprintf(stderr, "file opened on channel %d\n", chan);
y = setiocb(chan, -1, 'F', mode[0], 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;
e = fputc(c, iocbs[chan].fp);
if (e == EOF) {y = seterror(chan); break;}
a = e & 0xFF;
break;
case 'Q': //Put String - Y = channel
chan = y;
x = 0; //Number of characters written
y = valchan(chan, 'F'); if (y) break;
for (i = 0; i<128; i++) {
c = mpu->memory[fileaddr+i];
if (c) filebuff[i] = c;
else break;
}
filebuff[i] = 0;
if (DEBUG) fprintf(stderr, "writing string '%s'\n", filebuff);
if (p & 1) strcat(filebuff, "\n");
e = fputs(filebuff, iocbs[chan].fp);
if (e == EOF) {y = seterror(chan); break;}
break;
case 'R': //Read bytes - Y = channel, X=Number of Bytes
chan = y;
y = valchan(chan, 'F'); if (y) break;
if (DEBUG) fprintf(stderr, "reading %d bytes\n", x);
e = fread(filebuff, x, 1, iocbs[chan].fp);
if (e != 1) {y = seterror(chan); break;}
writemem(mpu, filebuff, x);
break;
case 'S': //Save file Y,X = end address
a = 0; //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); 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; i<STRLEN; i++) {
c = filename[i];
if (c == 0) break;
mpu->memory[yx+i] = c;
}
mpu->memory[yx+i] = 0;
x = i;
}
else y = seterror(0);
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);}
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); else y=0;
} else {
x = _getdrive();
if (x) y=0; else y=seterror(0);
if (DEBUG) fprintf(stderr, "current drive is %c\n", x+'@');
}
break;
case 'W': //Write bytes - Y = channel, X=Number of Bytes
chan = y;
y = valchan(chan, 'F'); if (y) break;
for (i = 0; i<x; i++)
filebuff[i] = mpu->memory[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); 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);
} else {
if (DEBUG) fprintf(stderr, "creating directory '%s'\n", filename);
x = _mkdir(filename);
}
if (x) y=seterror(0); else y=0;
break;
case 'Y': //Get Last Error, Y=chan
chan = y;
y = 0; //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);
x = iocbs[chan].errnum;
char *msg = strerror(x);
if (DEBUG) fprintf(stderr, "retrieved error %d, '%s'\n", x, msg);
writestr(mpu, msg, STRLEN);
break;
default:
y = 22; //Error - invalid argument
}
mpu->registers->a = a;
mpu->registers->x = x;
mpu->registers->y = y;
rts;
}
static int doFtrap(int argc, char **argv, M6502 *mpu)
{
unsigned addr;
if (argc < 2) usage(1);
addr= htol(argv[1]);
M6502_setCallback(mpu, call, addr, fTrap);
return 1;
}
/* Emulate getchar(3) at addr */
static int gTrap(M6502 *mpu, word addr, byte data) { mpu->registers->a= getchar(); rts; }
@ -462,6 +830,22 @@ static int doDisassemble(int argc, char **argv, M6502 *mpu)
return 2;
}
static int doDump(int argc, char **argv, M6502 *mpu)
{
unsigned addr= 0, last= 0, i= 0;
if (argc < 3) usage(1);
addr= htol(argv[1]);
last= ('+' == *argv[2]) ? addr + htol(1 + argv[2]) : htol(argv[2]);
while (addr < last)
{
printf("%04x ", addr);
for (i=0; i<16; i++)
if (addr + i < 0x10000) printf("%02x ", mpu->memory[addr + i]);
printf("\n");
addr += 16;
}
return 2;
}
int main(int argc, char **argv)
{
@ -482,11 +866,13 @@ int main(int argc, char **argv)
int n= 0;
if (!strcmp(*argv, "-B")) bTraps= 1;
else if (!strcmp(*argv, "-d")) n= doDisassemble(argc, argv, mpu);
else if (!strcmp(*argv, "-F")) n= doFtrap(argc, argv, mpu);
else if (!strcmp(*argv, "-G")) n= doGtrap(argc, argv, mpu);
else if (!strcmp(*argv, "-h")) n= doHelp(argc, argv, mpu);
else if (!strcmp(*argv, "-i")) n= doLoadInterpreter(argc, argv, mpu);
else if (!strcmp(*argv, "-I")) n= doIRQ(argc, argv, mpu);
else if (!strcmp(*argv, "-l")) n= doLoad(argc, argv, mpu);
else if (!strcmp(*argv, "-m")) n= doDump(argc, argv, mpu);
else if (!strcmp(*argv, "-M")) n= doMtrap(argc, argv, mpu);
else if (!strcmp(*argv, "-N")) n= doNMI(argc, argv, mpu);
else if (!strcmp(*argv, "-P")) n= doPtrap(argc, argv, mpu);
@ -516,6 +902,8 @@ int main(int argc, char **argv)
if (bTraps)
doBtraps(0, 0, mpu);
initiocbs(); //Initialize fileio blocks
M6502_reset(mpu);
M6502_run(mpu);
M6502_delete(mpu);

1
lib6502/tcc6502.bat Normal file
View File

@ -0,0 +1 @@
tcc -o run6502.exe -I . dirent.c lib6502.c run6502.c