1
0
mirror of https://github.com/RevCurtisP/C02.git synced 2024-06-26 05:29:32 +00:00
C02/lib6502/run6502.c
2020-09-21 20:01:43 -04:00

915 lines
26 KiB
C

/* run6502.c -- 6502 emulator shell -*- C -*- */
/* Copyright (c) 2005 Ian Piumarta
*
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the 'Software'),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, provided that the above copyright notice(s) and this
* permission notice appear in all copies of the Software and that both the
* above copyright notice(s) and this permission notice appear in supporting
* documentation.
*
* THE SOFTWARE IS PROVIDED 'AS IS'. USE ENTIRELY AT YOUR OWN RISK.
*/
/* Last edited: 2005-11-02 01:18:58 by piumarta on margaux.local
*/
#include <stdio.h>
#include <stdlib.h>
#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;
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, ...)
{
va_list ap;
fflush(stdout);
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
fprintf(stderr, "\n");
exit(1);
}
void pfail(const char *msg)
{
fflush(stdout);
perror(msg);
exit(1);
}
#define rts \
{ \
word pc; \
pc = mpu->memory[++mpu->registers->s + 0x100]; \
pc |= mpu->memory[++mpu->registers->s + 0x100] << 8; \
return pc + 1; \
}
int osword(M6502 *mpu, word address, byte data)
{
byte *params= mpu->memory + mpu->registers->x + (mpu->registers->y << 8);
switch (mpu->registers->a)
{
case 0x00: /* input line */
/* On entry: XY+0,1=>string area,
* XY+2=maximum line length,
* XY+3=minimum acceptable ASCII value,
* XY+4=maximum acceptable ASCII value.
* On exit: Y is the line length (excluding CR),
* C is set if Escape terminated input.
*/
{
word offset= params[0] + (params[1] << 8);
byte *buffer= mpu->memory + offset;
byte length= params[2], minVal= params[3], maxVal= params[4], b= 0;
if (!fgets(buffer, length, stdin))
{
putchar('\n');
exit(0);
}
for (b= 0; b < length; ++b)
if ((buffer[b] < minVal) || (buffer[b] > maxVal) || ('\n' == buffer[b]))
break;
buffer[b]= 13;
mpu->registers->y= b;
mpu->registers->p &= 0xFE;
break;
}
default:
{
char state[64];
M6502_dump(mpu, state);
fflush(stdout);
fprintf(stderr, "\nOSWORD %s\n", state);
fail("ABORT");
}
break;
}
rts;
}
int osbyte(M6502 *mpu, word address, byte data)
{
switch (mpu->registers->a)
{
case 0x7A: /* perform keyboard scan */
mpu->registers->x= 0x00;
break;
case 0x7E: /* acknowledge detection of escape condition */
return 1;
break;
case 0x82: /* read machine higher order address */
mpu->registers->y= 0x00;
mpu->registers->x= 0x00;
break;
case 0x83: /* read top of OS ram address (OSHWM) */
mpu->registers->y= 0x0E;
mpu->registers->x= 0x00;
break;
case 0x84: /* read bottom of display ram address */
mpu->registers->y= 0x80;
mpu->registers->x= 0x00;
break;
case 0x89: /* motor control */
break;
case 0xDA: /* read/write number of items in vdu queue (stored at 0x026A) */
return 0;
break;
default:
{
char state[64];
M6502_dump(mpu, state);
fflush(stdout);
fprintf(stderr, "\nOSBYTE %s\n", state);
fail("ABORT");
}
break;
}
rts;
}
int oscli(M6502 *mpu, word address, byte data)
{
byte *params= mpu->memory + mpu->registers->x + (mpu->registers->y << 8);
char command[1024], *ptr= command;
while (('*' == *params) || (' ' == *params))
++params;
while (13 != *params)
*ptr++= *params++;
*ptr= '\0';
system(command);
rts;
}
int oswrch(M6502 *mpu, word address, byte data)
{
switch (mpu->registers->a)
{
case 0x0C:
fputs("\033[2J\033[H", stdout);
break;
default:
putchar(mpu->registers->a);
break;
}
fflush(stdout);
rts;
}
static int writeROM(M6502 *mpu, word address, byte value)
{
return 0;
}
static int bankSelect(M6502 *mpu, word address, byte value)
{
memcpy(mpu->memory + 0x8000, bank[value & 0x0F], 0x4000);
return 0;
}
static int doBtraps(int argc, char **argv, M6502 *mpu)
{
unsigned addr;
/* Acorn Model B ROM and memory-mapped IO */
for (addr= 0x8000; addr <= 0xFBFF; ++addr) mpu->callbacks->write[addr]= writeROM;
for (addr= 0xFC00; addr <= 0xFEFF; ++addr) mpu->memory[addr]= 0xFF;
for (addr= 0xFE30; addr <= 0xFE33; ++addr) mpu->callbacks->write[addr]= bankSelect;
for (addr= 0xFE40; addr <= 0xFE4F; ++addr) mpu->memory[addr]= 0x00;
for (addr= 0xFF00; addr <= 0xFFFF; ++addr) mpu->callbacks->write[addr]= writeROM;
/* anything already loaded at 0x8000 appears in bank 0 */
memcpy(bank[0x00], mpu->memory + 0x8000, 0x4000);
/* fake a few interesting OS calls */
# define trap(vec, addr, func) mpu->callbacks->call[addr]= (func)
trap(0x020C, 0xFFF1, osword);
trap(0x020A, 0xFFF4, osbyte);
//trap(0x0208, 0xFFF7, oscli ); /* enable this to send '*COMMAND's to system(3) :-) */
trap(0x020E, 0xFFEE, oswrch);
trap(0x020E, 0xE0A4, oswrch); /* NVWRCH */
#undef trap
return 0;
}
static void usage(int status)
{
FILE *stream= status ? stderr : stdout;
fprintf(stream, VERSION"\n");
fprintf(stream, "please send bug reports to: %s\n", PACKAGE_BUGREPORT);
fprintf(stream, "\n");
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 -- 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");
fprintf(stream, " -R addr -- set RST vector\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");
fprintf(stream, " -x -- exit wihout further ado\n");
fprintf(stream, " image -- '-l 8000 image' in available ROM slot\n");
fprintf(stream, "\n");
fprintf(stream, "'last' can be an address (non-inclusive) or '+size' (in bytes)\n");
exit(status);
}
static int doHelp(int argc, char **argv, M6502 *mpu)
{
usage(0);
return 0;
}
static int doVersion(int argc, char **argv, M6502 *mpu)
{
puts(VERSION);
exit(0);
return 0;
}
static unsigned long htol(char *hex)
{
char *end;
unsigned long l= strtol(hex, &end, 16);
if (*end) fail("bad hex number: %s", hex);
return l;
}
static int loadInterpreter(M6502 *mpu, word start, const char *path)
{
FILE *file= 0;
int count= 0;
byte *memory= mpu->memory + start;
size_t max= 0x10000 - start;
int c= 0;
if ((!(file= fopen(path, "r"))) || ('#' != fgetc(file)) || ('!' != fgetc(file)))
return 0;
while ((c= fgetc(file)) >= ' ')
;
while ((count= fread(memory, 1, max, file)) > 0)
{
memory += count;
max -= count;
}
fclose(file);
return 1;
}
static int save(M6502 *mpu, word address, unsigned length, const char *path)
{
FILE *file= 0;
int count= 0;
if (!(file= fopen(path, "wb")))
return 0;
while ((count= fwrite(mpu->memory + address, 1, length, file)))
{
address += count;
length -= count;
}
fclose(file);
return address;
}
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, "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;
}
fclose(file);
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);
if (!loadInterpreter(mpu, htol(argv[1]), argv[2])) pfail(argv[2]);
return 2;
}
static int doLoad(int argc, char **argv, M6502 *mpu) /* -l addr file */
{
if (argc < 3) usage(1);
if (!load(mpu, htol(argv[1]), argv[2])) pfail(argv[2]);
return 2;
}
static int doSave(int argc, char **argv, M6502 *mpu) /* -l addr size file */
{
if (argc < 4) usage(1);
if (!save(mpu, htol(argv[1]), htol(argv[2]), argv[3])) pfail(argv[3]);
return 3;
}
#define doVEC(VEC) \
static int do##VEC(int argc, char **argv, M6502 *mpu) \
{ \
unsigned addr= 0; \
if (argc < 2) usage(1); \
addr= htol(argv[1]); \
M6502_setVector(mpu, VEC, addr); \
return 1; \
}
doVEC(IRQ);
doVEC(NMI);
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;
if (DEBUG) fprintf(stderr, "copying name from address $%04x\n", addr);
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
if (DEBUG) fprintf(stderr, "copied %d characters\n", i);
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 = Drive#, X = Mode (0x80 = Write)
strcpy(mode, modes[x >> 7]); //Set Mode based on Negative Flag
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; }
static int doGtrap(int argc, char **argv, M6502 *mpu)
{
unsigned addr;
if (argc < 2) usage(1);
addr= htol(argv[1]);
M6502_setCallback(mpu, call, addr, gTrap);
return 1;
}
/* Emulate putchar(3) at addr */
static int pTrap(M6502 *mpu, word addr, byte data) { putchar(mpu->registers->a); rts; }
static int doPtrap(int argc, char **argv, M6502 *mpu)
{
unsigned addr;
if (argc < 2) usage(1);
addr= htol(argv[1]);
M6502_setCallback(mpu, call, addr, pTrap);
return 1;
}
/* Emulate memory-mapped stdio at addr */
static int mTrapRead(M6502 *mpu, word addr, byte data) { return getchar(); }
static int mTrapWrite(M6502 *mpu, word addr, byte data) { return putchar(data); }
static int doMtrap(int argc, char **argv, M6502 *mpu)
{
unsigned addr= 0;
if (argc < 2) usage(1);
addr= htol(argv[1]);
M6502_setCallback(mpu, read, addr, mTrapRead);
M6502_setCallback(mpu, write, addr, mTrapWrite);
return 1;
}
/* Terminate emulation if PC reaches addr */
static int xTrap(M6502 *mpu, word addr, byte data) { exit(0); return 0; }
static int doXtrap(int argc, char **argv, M6502 *mpu)
{
unsigned addr= 0;
if (argc < 2) usage(1);
addr= htol(argv[1]);
M6502_setCallback(mpu, call, addr, xTrap);
return 1;
}
static int doDisassemble(int argc, char **argv, M6502 *mpu)
{
unsigned addr= 0, last= 0;
if (argc < 3) usage(1);
addr= htol(argv[1]);
last= ('+' == *argv[2]) ? addr + htol(1 + argv[2]) : htol(argv[2]);
while (addr < last)
{
char insn[64];
int i= 0, size= M6502_disassemble(mpu, addr, insn);
printf("%04X ", addr);
while (i++ < size) printf("%02X", mpu->memory[addr + i - 1]);
while (i++ < 4) printf(" ");
putchar(' ');
i= 0;
while (i++ < size) putchar(isgraph(mpu->memory[addr + i - 1]) ? mpu->memory[addr + i - 1] : ' ');
while (i++ < 4) putchar(' ');
printf(" %s\n", insn);
addr += size;
}
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)
{
M6502 *mpu= M6502_new(0, 0, 0);
int bTraps= 0;
program= argv[0];
if ((2 == argc) && ('-' != *argv[1]))
{
if ((!loadInterpreter(mpu, 0, argv[1])) && (!load(mpu, 0, argv[1])))
pfail(argv[1]);
doBtraps(0, 0, mpu);
}
else
while (++argv, --argc > 0)
{
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);
else if (!strcmp(*argv, "-R")) n= doRST(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);
else if (!strcmp(*argv, "-x")) exit(0);
else if ('-' == **argv) usage(1);
else
{
/* doBtraps() left 0x8000+0x4000 in bank 0, so load */
/* additional images starting at 15 and work down */
static int bankSel= 0x0F;
if (!bTraps) usage(1);
if (bankSel < 0) fail("too many images");
if (!load(mpu, 0x8000, argv[0])) pfail(argv[0]);
memcpy(bank[bankSel--],
0x8000 + mpu->memory,
0x4000);
n= 1;
}
argc -= n;
argv += n;
}
if (bTraps)
doBtraps(0, 0, mpu);
initiocbs(); //Initialize fileio blocks
M6502_reset(mpu);
M6502_run(mpu);
M6502_delete(mpu);
return 0;
}