mirror of
https://github.com/RevCurtisP/C02.git
synced 2024-11-25 06:31:25 +00:00
582 lines
15 KiB
C
582 lines
15 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 "config.h"
|
|
#include "lib6502.h"
|
|
#include "file6502.h"
|
|
|
|
#define VERSION PACKAGE_NAME " " PACKAGE_VERSION " " PACKAGE_COPYRIGHT
|
|
|
|
typedef uint8_t byte;
|
|
typedef uint16_t word;
|
|
|
|
static char *program= 0;
|
|
|
|
static byte bank[0x10][0x4000];
|
|
|
|
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, " -D -- print debug messages\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 -- 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");
|
|
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 doDebug(int argc, char **argv, M6502 *mpu)
|
|
{
|
|
setdebug(-1);
|
|
return 0;
|
|
}
|
|
|
|
|
|
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;
|
|
while ((count= fread(mpu->memory + address, 1, max, file)) > 0)
|
|
{
|
|
address += count;
|
|
max -= count;
|
|
}
|
|
fclose(file);
|
|
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
|
|
|
|
static int fTrap(M6502 *mpu, word addr, byte data) {
|
|
filecmd(mpu, addr, data);
|
|
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;
|
|
}
|
|
|
|
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; }
|
|
|
|
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;
|
|
setdebug(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, "-D")) n= doDebug(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= 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);
|
|
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;
|
|
}
|