diff --git a/src/lib6502/BSDmakefile b/src/lib6502/BSDmakefile new file mode 100644 index 0000000..00961ec --- /dev/null +++ b/src/lib6502/BSDmakefile @@ -0,0 +1,83 @@ +# THIS FILE WAS GENERATED AUTOMATICALLY +# EDIT AT YOUR OWN RISK +# +# Makefile for lib6502, run6502 + +# Copyright (c) 2005 Ian Piumarta +# +# 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, sub-license, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS". USE ENTIRELY AT YOUR OWN RISK. + +# last edited: 2005-11-01 22:48:49 by piumarta on margaux.local + +CFLAGS = -g -O3 + +PREFIX = /usr/local +BINDIR = $(PREFIX)/bin +LIBDIR = $(PREFIX)/lib +INCDIR = $(PREFIX)/include +DOCDIR = $(PREFIX)/doc/lib6502 +EGSDIR = $(DOCDIR)/examples +MANDIR = $(PREFIX)/man +MAN1DIR = $(MANDIR)/man1 +MAN3DIR = $(MANDIR)/man3 + +all : run6502 + +run6502 : run6502.o lib6502.a + +lib6502.a : lib6502.o + $(AR) -rc $@.new lib6502.o + mv $@.new $@ + -ranlib $@ + +clean : .FORCE + rm -f run6502 lib1 *~ *.o *.a .gdb* *.img *.log + +.FORCE : + + install : .FORCE + install -d $(BINDIR) + install -d $(LIBDIR) + install -d $(INCDIR) + install -d $(MANDIR) + install -d $(MAN1DIR) + install -d $(MAN3DIR) + install -d $(DOCDIR) + install -d $(EGSDIR) + install -c run6502 $(BINDIR)/run6502 + install -c lib6502.a $(LIBDIR)/lib6502.a + install -c lib6502.h $(INCDIR)/lib6502.h + install -c man/run6502.1 $(MAN1DIR)/run6502.1 + install -c man/lib6502.3 $(MAN3DIR)/lib6502.3 + install -c man/M6502_delete.3 $(MAN3DIR)/M6502_delete.3 + install -c man/M6502_disassemble.3 $(MAN3DIR)/M6502_disassemble.3 + install -c man/M6502_dump.3 $(MAN3DIR)/M6502_dump.3 + install -c man/M6502_getCallback.3 $(MAN3DIR)/M6502_getCallback.3 + install -c man/M6502_getVector.3 $(MAN3DIR)/M6502_getVector.3 + install -c man/M6502_irq.3 $(MAN3DIR)/M6502_irq.3 + install -c man/M6502_new.3 $(MAN3DIR)/M6502_new.3 + install -c man/M6502_nmi.3 $(MAN3DIR)/M6502_nmi.3 + install -c man/M6502_reset.3 $(MAN3DIR)/M6502_reset.3 + install -c man/M6502_run.3 $(MAN3DIR)/M6502_run.3 + install -c man/M6502_setCallback.3 $(MAN3DIR)/M6502_setCallback.3 + install -c man/M6502_setVector.3 $(MAN3DIR)/M6502_setVector.3 + install -c ChangeLog $(DOCDIR)/ChangeLog + install -c COPYING $(DOCDIR)/COPYING + install -c README $(DOCDIR)/README + install -c examples/README $(EGSDIR)/README + install -c examples/lib1.c $(EGSDIR)/lib1.c + install -c examples/hex2bin $(EGSDIR)/hex2bin + + uninstall : .FORCE + rm -f $(BINDIR)/run6502 $(LIBDIR)/lib6502.a $(INCDIR)/lib6502.h $(MAN1DIR)/run6502.1 $(MAN3DIR)/lib6502.3 $(MAN3DIR)/M6502_delete.3 $(MAN3DIR)/M6502_disassemble.3 $(MAN3DIR)/M6502_dump.3 $(MAN3DIR)/M6502_getCallback.3 $(MAN3DIR)/M6502_getVector.3 $(MAN3DIR)/M6502_irq.3 $(MAN3DIR)/M6502_new.3 $(MAN3DIR)/M6502_nmi.3 $(MAN3DIR)/M6502_reset.3 $(MAN3DIR)/M6502_run.3 $(MAN3DIR)/M6502_setCallback.3 $(MAN3DIR)/M6502_setVector.3 $(DOCDIR)/ChangeLog $(DOCDIR)/COPYING $(DOCDIR)/README $(EGSDIR)/README $(EGSDIR)/lib1.c $(EGSDIR)/hex2bin + rmdir $(EGSDIR) $(DOCDIR) diff --git a/src/lib6502/COPYING b/src/lib6502/COPYING new file mode 100644 index 0000000..0abf1e7 --- /dev/null +++ b/src/lib6502/COPYING @@ -0,0 +1,26 @@ +Distasteful though it is for me to have to induce from afar any perturbation +into your pursuit of happiness, this MIT (X11 flavour) license is at least +relatively benign. Investigation into copyright stupidity reveals that it is +effectively impossible to dedicate (formally) any software to the public +domain (the only sure path to this most enlightened status being to leave the +software to expire naturally from its 25-, 50-, 75- or whatever-year copyright +rot). I fear this is not going to change before the revolution comes. In the +meantime the only way I can *guarantee* you any rights at all to this software +would (unfortunately) appear to be... + + 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 or substantial portions of the Software. + + Inclusion of the above copyright notice(s) and this permission notice in + supporting documentation would be appreciated, but is not required. + + THE SOFTWARE IS PROVIDED 'AS IS'. USE ENTIRELY AT YOUR OWN RISK. diff --git a/src/lib6502/ChangeLog b/src/lib6502/ChangeLog new file mode 100644 index 0000000..106418c --- /dev/null +++ b/src/lib6502/ChangeLog @@ -0,0 +1,7 @@ +2005-11-01 Ian Piumarta + + * RELEASE 1.0 + +2005-10-30 Ian Piumarta + + * ChangeLog: created. diff --git a/src/lib6502/Makefile b/src/lib6502/Makefile new file mode 100644 index 0000000..d8285ba --- /dev/null +++ b/src/lib6502/Makefile @@ -0,0 +1,232 @@ +# Makefile for lib6502, run6502 + +# 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: 2013-06-08 01:08:02 by piumarta on vps2.piumarta.com + +CFLAGS = -g -O3 + +PREFIX = /usr/local +BINDIR = $(PREFIX)/bin +LIBDIR = $(PREFIX)/lib +INCDIR = $(PREFIX)/include +DOCDIR = $(PREFIX)/doc/lib6502 +EGSDIR = $(DOCDIR)/examples +MANDIR = $(PREFIX)/man +MAN1DIR = $(MANDIR)/man1 +MAN3DIR = $(MANDIR)/man3 + +all : run6502 + +run6502 : run6502.o lib6502.a + +lib6502.a : lib6502.o + $(AR) -rc $@.new lib6502.o + mv $@.new $@ + -ranlib $@ + +clean : .FORCE + rm -f run6502 lib1 *~ *.o *.a .gdb* *.img *.log + +.FORCE : + +# ---------------------------------------------------------------- + +INSTALLDIRS = $(BINDIR) $(LIBDIR) $(INCDIR) $(MANDIR) $(MAN1DIR) $(MAN3DIR) $(DOCDIR) $(EGSDIR) + +BINFILES = $(BINDIR)/run6502 + +LIBFILES = $(LIBDIR)/lib6502.a + +INCFILES = $(INCDIR)/lib6502.h + +MANFILES = $(MAN1DIR)/run6502.1 \ + $(MAN3DIR)/lib6502.3 \ + $(MAN3DIR)/M6502_delete.3 \ + $(MAN3DIR)/M6502_disassemble.3 \ + $(MAN3DIR)/M6502_dump.3 \ + $(MAN3DIR)/M6502_getCallback.3 \ + $(MAN3DIR)/M6502_getVector.3 \ + $(MAN3DIR)/M6502_irq.3 \ + $(MAN3DIR)/M6502_new.3 \ + $(MAN3DIR)/M6502_nmi.3 \ + $(MAN3DIR)/M6502_reset.3 \ + $(MAN3DIR)/M6502_run.3 \ + $(MAN3DIR)/M6502_setCallback.3 \ + $(MAN3DIR)/M6502_setVector.3 + +DOCFILES = $(DOCDIR)/ChangeLog \ + $(DOCDIR)/COPYING \ + $(DOCDIR)/README \ + $(EGSDIR)/README \ + $(EGSDIR)/lib1.c \ + $(EGSDIR)/hex2bin + +MKDIR = install -d +RMDIR = rmdir +INSTALL = install -c +RM = rm -f + +$(BINDIR)/% $(LIBDIR)/% $(INCDIR)/% $(DOCDIR)/% : % + $(INSTALL) $< $@ + +$(MAN1DIR)/% $(MAN3DIR)/% : man/% + $(INSTALL) $< $@ + +$(EGSDIR)/% : examples/% + $(INSTALL) $< $@ + +$(INSTALLDIRS) : + $(MKDIR) $@ + +install : $(INSTALLDIRS) $(BINFILES) $(LIBFILES) $(INCFILES) $(MANFILES) $(DOCFILES) + +uninstall : .FORCE + -$(RM) $(BINFILES) $(LIBFILES) $(INCFILES) $(MANFILES) $(DOCFILES) + -$(RMDIR) $(EGSDIR) $(DOCDIR) + +# ---------------------------------------------------------------- + +PACKAGE_VERSION = 1.3 +PACKAGE_TARNAME = lib6502 + +TARNAME= $(PACKAGE_TARNAME)-$(PACKAGE_VERSION) + +DISTFILES = \ + $(TARNAME)/ChangeLog \ + $(TARNAME)/COPYING \ + $(TARNAME)/README \ + $(TARNAME)/Makefile \ + $(TARNAME)/BSDmakefile \ + $(TARNAME)/config.h \ + $(TARNAME)/lib6502.h \ + $(TARNAME)/lib6502.c \ + $(TARNAME)/run6502.c \ + $(TARNAME)/test.out \ + $(TARNAME)/man/run6502.1 \ + $(TARNAME)/man/lib6502.3 \ + $(TARNAME)/man/M6502_delete.3 \ + $(TARNAME)/man/M6502_disassemble.3 \ + $(TARNAME)/man/M6502_dump.3 \ + $(TARNAME)/man/M6502_getCallback.3 \ + $(TARNAME)/man/M6502_getVector.3 \ + $(TARNAME)/man/M6502_irq.3 \ + $(TARNAME)/man/M6502_new.3 \ + $(TARNAME)/man/M6502_nmi.3 \ + $(TARNAME)/man/M6502_reset.3 \ + $(TARNAME)/man/M6502_run.3 \ + $(TARNAME)/man/M6502_setCallback.3 \ + $(TARNAME)/man/M6502_setVector.3 \ + $(TARNAME)/examples/hex2bin \ + $(TARNAME)/examples/lib1.c \ + $(TARNAME)/examples/README + +dist : .FORCE + rm -f $(TARNAME) + ln -s . $(TARNAME) + tar -cf $(TARNAME).tar $(DISTFILES) + gzip -v9 $(TARNAME).tar + rm -f $(TARNAME) + +dist-test : .FORCE + rm -rf $(TARNAME) + tar -xz -f $(TARNAME).tar.gz + ln -s ../images $(TARNAME)/images + $(MAKE) -C $(TARNAME) test + rm -rf $(TARNAME) + +# ---------------------------------------------------------------- + +image : + ./run6502 \ + -l C000 images/os1.2 \ + -l 8000 images/basic2 \ + -s 0000 +10000 image \ + -x + +newimage : .FORCE + rm -f image + $(MAKE) image + +test1 : run6502 .FORCE + echo a2418a20eeffe8e05bd0f7a90a20eeff0000 | perl -e '$$_=pack"H*",;print' > temp.img + ./run6502 \ + -l 1000 temp.img \ + -d 1000 +11 \ + -R 1000 \ + -P FFEE \ + -X 0 + +lib1 : lib6502.a + $(CC) -I. -o lib1 examples/lib1.c lib6502.a + +test2 : lib1 .FORCE + ./lib1 + +test3 : run6502 image .FORCE + echo 'PRINT:FORA%=1TO10:PRINTA%:NEXT:PRINT"HELLO WORLD"' | ./run6502 image + +test4 : run6502 image .FORCE + echo 'P%=&2800:O%=P%:[opt3:ldx#65:.l txa:jsr&FFEE:inx:cpx#91:bnel:lda#13:jsr&FFEE:lda#10:jmp&FFEE:]:CALL&2800' | ./run6502 image + +test : run6502 lib1 image .FORCE + @$(MAKE) test1 test2 test3 test4 | grep -v '^make.* directory' | tee test.log + cmp test.log test.out + @echo + @echo SUCCESS + @echo + +# ---------------------------------------------------------------- + +# I don't know what it is (probably me, who knows?) but every single +# time I try to write a Makefile that is compatible with both GNU and +# BSD make I spend three hours getting absolutely nowhere. It's +# telling when a program consisting of precisely TWO SOURCE FILES and +# a few man pages and examples is already TOO COMPLEX to be installed +# with BSD make, in the absence of an explicit rule for every single +# target, be it installed or intermediate, from a Makefile that won't +# also break GNU make. Good Grief Charlie Brown. + +# Yes I know I can compose the sed substitutions into a single script, +# but it looks even uglier that way. + +BSDmakefile : .FORCE + $(MAKE) + rm -rf /tmp/bsd + echo '# THIS FILE WAS GENERATED AUTOMATICALLY' > BSDmakefile + echo '# EDIT AT YOUR OWN RISK' >> BSDmakefile + echo '# ' >> BSDmakefile + sed '/# -/,$$d' < Makefile >> BSDmakefile + echo 'install : .FORCE' > BSDtemp + $(MAKE) install PREFIX=/tmp/bsd >> BSDtemp + echo >> BSDtemp + echo 'uninstall : .FORCE' >> BSDtemp + $(MAKE) uninstall PREFIX=/tmp/bsd >> BSDtemp + cat BSDtemp | \ + sed 's,/tmp/bsd/doc/lib6502/examples,$$(EGSDIR),g' | \ + sed 's,/tmp/bsd/doc/lib6502,$$(DOCDIR),g' | \ + sed 's,/tmp/bsd/man/man1,$$(MAN1DIR),g' | \ + sed 's,/tmp/bsd/man/man3,$$(MAN3DIR),g' | \ + sed 's,/tmp/bsd/man,$$(MANDIR),g' | \ + sed 's,/tmp/bsd/include,$$(INCDIR),g' | \ + sed 's,/tmp/bsd/lib,$$(LIBDIR),g' | \ + sed 's,/tmp/bsd/lib,$$(LIBDIR),g' | \ + sed 's,/tmp/bsd/bin,$$(BINDIR),g' | \ + sed 's,^, ,g' >> BSDmakefile + rm -f BSDtemp + rm -rf /tmp/bsd diff --git a/src/lib6502/README b/src/lib6502/README new file mode 100755 index 0000000..5d4e2da --- /dev/null +++ b/src/lib6502/README @@ -0,0 +1,131 @@ + lib6502 - 6502 Microprocessor Emulator + + Version: 1.0 + + +WHAT IF I'M TOO LAZY TO READ 'README'S? + + make + make install + more examples/README + + +WHAT IS LIB6502? + + lib6502 is a library that emulates the 6502 microprocessor. It + comes with a small 'shell', run6502, that can execute 6502 programs + from the command line. + + lib6502 is distributed under the MIT license: it is non-infectious + and will not make your projects contagious to others the instant you + choose to use lib6502 in them. See the file COPYING for details. + + +WHERE IS THE LATEST SOURCE CODE? + + Source code for lib6502 is available from the author's home page at + 'http://piumarta.com/software'. You can download the most recent + release or use Subversion to get the very latest sources. + + +WHERE IS THE DOCUMENTATION? + + Manual pages for run6502 and lib6502 (and all the functions it + exports) should be available once it is installed. Each includes a + short 'examples' section. Use the 'man' command to read them. + + Your best place to start looking for documentation on the 6502 + itself is 'http://6502.org'. A google search of the web will also + turn up vast quantities of information about (and programs for) the + 6502. + + +HOW DO I INSTALL IT? + + It's not really big enough to warrant the whole 'configure' thing. + Any system with an ANSI compiler and C library should be able to + compile it out of the box. After unpacking the archive, just type: + + make + + to build it. If the compiler blows up immediately, edit the + Makefile and play with the '-g' and '-O' flags and then try again. + If you really can't make the compiler happy you've found a bug (read + the next section but one). Otherwise, if you want it put it + somewhere more permanent then type: + + make install + + (as root) to install it. It goes into /usr/local by default; if you + want it elsewhere then set PREFIX in the make command. For example: + + make install PREFIX=/usr + + will put everything under '/usr'. + + When you get bored with it, go back to the source directory and + type: + + make uninstall + + (with the same PREFIX you specified during the install, if + necessary.) + + +WHAT CAN I DO WITH IT? + + See the file EXAMPLES for some suggestions (all of them polite). + + If that leaves you wanting more, read the source for run6502 -- it + exercises just about every feature in lib6502. + + +HOW DO I REPORT PROBLEMS? + + Send e-mail to the author at: firstName (at) lastName (dot) com + + (For suitable values of firstName and lastName, see the last section + of this file.) + + If you're still confused, contact him at: http://piumarta.com + + +HOW CAN I HELP? + + Use it. Find bugs. Fix bugs. Make it faster. Evangelism: spread + it to as many other projects as possible, especially those that + might be using a slower emulator! Read the manual pages to see + what's considered missing, then add it, then send it in. + + (One thing that would be be really handy, and isn't mentioned in the + manual pages, is a test suite. Figure out how to test every mode in + every instruction with every possible combination of operand values + and condition codes and verify the behaviour is correct. Then write + it down in the form of a program and send it in. If it's a + self-contained program that runs once to completion then we can + probably find some real hardware to test against the test suite.) + + If you know how to write software that emulates peripheral hardware + devices, google up some details on the popular 6502-based + microcomputers (Acorn, Commodore, etc.) and add some serious system + emulation to run6502. Make it all pluggable (think dynamic + libraries over an 'agnostic' core), so we can change machines at the + flip of a (command-line) switch. (The callback mechanism in lib6502 + was designed with this kind of 'pluggable hardware emulation' in + mind.) + + +WHO WROTE THIS STUFF, AND WHY? + + lib6502 was written by Ian Piumarta. + + While writing ccg (an entirely different project that creates + runtime assemblers for dynamic code generators) he decided to + include support for an 8-bit microprocessor, just for fun. He chose + the 6502 because it was used in the first computer he owned and + programmed (an Ohio Scientific Superboard II, when he was 14) as + well as the second (an Acorn 'BBC Model B', about four years later). + lib6502 started as a 'glorified switch statement' that ran some + small test programs spewed into memory by ccg, but rapidly got out + of control over the course of a weekend. You're looking at the + result. diff --git a/src/lib6502/a1cffa.c b/src/lib6502/a1cffa.c new file mode 100644 index 0000000..1f633ba --- /dev/null +++ b/src/lib6502/a1cffa.c @@ -0,0 +1,143 @@ +/* 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 + */ + +/* Apple 1 + CFFA1 support for PLASMA by resman + */ + +#include +#include +#include +#include +#include + +#include "config.h" +#include "lib6502.h" + +#define VERSION PACKAGE_NAME " " PACKAGE_VERSION " " PACKAGE_COPYRIGHT + +typedef uint8_t byte; +typedef uint16_t word; + +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 save(M6502 *mpu, word address, unsigned length, const char *path) +{ + FILE *file; + int count; + if (!(file = fopen(path, "wb"))) + return 0; + while ((count = fwrite(mpu->memory + address, 1, length, file))) + { + address += count; + length -= count; + } + fclose(file); + return 1; +} + +int load(M6502 *mpu, word address, const char *path) +{ + FILE *file; + int count; + 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 1; +} + +int cffa1(M6502 *mpu, word address, byte data) +{ + switch (mpu->registers->x) + { + case 0x7A: /* perform keyboard scan */ + mpu->registers->x= 0x00; + break; + + default: + { + char state[64]; + M6502_dump(mpu, state); + fflush(stdout); + fprintf(stderr, "\nCFFA1 %s\n", state); + fail("ABORT"); + } + break; + } + rts; +} + +int bye(M6502 *mpu, word addr, byte data) { exit(0); return 0; } +int cout(M6502 *mpu, word addr, byte data) { putchar(mpu->registers->a); fflush(stdout); rts; } + +int rd6820kbdctl(M6502 *mpu, word addr, byte data) { return 0x80; } +int rd6820vidctl(M6502 *mpu, word addr, byte data) { return 0x00; } +int rd6820kbd(M6502 *mpu, word addr, byte data) { return getchar(); } +int wr6820vid(M6502 *mpu, word addr, byte data) { putchar(data); fflush(stdout); return 0; } + +int setTraps(M6502 *mpu) +{ + /* Apple 1 memory-mapped IO */ + M6502_setCallback(mpu, read, 0xD010, rd6820kbd); + M6502_setCallback(mpu, read, 0xD011, rd6820kbdctl); + M6502_setCallback(mpu, write, 0xD012, wr6820vid); + M6502_setCallback(mpu, read, 0xD013, rd6820vidctl); + /* CFFA1 and ROM calls */ + M6502_setCallback(mpu, call, 0x9000, bye); + M6502_setCallback(mpu, call, 0x900C, cffa1); + M6502_setCallback(mpu, call, 0xFFEF, cout); + return 0; +} + +int main(int argc, char **argv) +{ + char *interpfile = "A1PLASMA"; + M6502 *mpu = M6502_new(0, 0, 0); + + if (argc == 2) + interpfile = argv[1]; + if (!load(mpu, 0x280, interpfile)) + pfail(interpfile); + setTraps(mpu); + M6502_reset(mpu); + M6502_run(mpu); + M6502_delete(mpu); + return 0; +} diff --git a/src/lib6502/config.h b/src/lib6502/config.h new file mode 100644 index 0000000..4de5ccb --- /dev/null +++ b/src/lib6502/config.h @@ -0,0 +1,9 @@ +#ifndef __config_h +#define __config_h + +#define PACKAGE_NAME "lib6502" +#define PACKAGE_VERSION "1.0" +#define PACKAGE_BUGREPORT "firstName (at) lastName (dot) com" +#define PACKAGE_COPYRIGHT "Copyright (c) 2005 Ian Piumarta" + +#endif /* __config_h */ diff --git a/src/lib6502/lib6502.c b/src/lib6502/lib6502.c new file mode 100644 index 0000000..84549fb --- /dev/null +++ b/src/lib6502/lib6502.c @@ -0,0 +1,893 @@ +/* lib6502.c -- MOS Technology 6502 emulator -*- 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: 2013-06-07 23:03:39 by piumarta on emilia.local + * + * BUGS: + * - RTS and RTI do not check the return address for a callback + * - the disassembler cannot be configured to read two bytes for BRK + * - architectural variations (unimplemented/extended instructions) not implemented + * - ANSI versions (from from gcc extensions) of the dispatch macros are missing + * - emulator+disassembler in same object file (library is kind of pointless) + */ + +#include +#include + +#include "lib6502.h" + +typedef uint8_t byte; +typedef uint16_t word; + +enum { + flagN= (1<<7), /* negative */ + flagV= (1<<6), /* overflow */ + flagX= (1<<5), /* unused */ + flagB= (1<<4), /* irq from brk */ + flagD= (1<<3), /* decimal mode */ + flagI= (1<<2), /* irq disable */ + flagZ= (1<<1), /* zero */ + flagC= (1<<0) /* carry */ +}; + +#define getN() (P & flagN) +#define getV() (P & flagV) +#define getB() (P & flagB) +#define getD() (P & flagD) +#define getI() (P & flagI) +#define getZ() (P & flagZ) +#define getC() (P & flagC) + +#define setNVZC(N,V,Z,C) (P= (P & ~(flagN | flagV | flagZ | flagC)) | (N) | ((V)<<6) | ((Z)<<1) | (C)) +#define setNZC(N,Z,C) (P= (P & ~(flagN | flagZ | flagC)) | (N) | ((Z)<<1) | (C)) +#define setNZ(N,Z) (P= (P & ~(flagN | flagZ )) | (N) | ((Z)<<1) ) +#define setZ(Z) (P= (P & ~( flagZ )) | ((Z)<<1) ) +#define setC(C) (P= (P & ~( flagC)) | (C)) + +#define NAND(P, Q) (!((P) & (Q))) + +#define tick(n) +#define tickIf(p) + +/* memory access (indirect if callback installed) -- ARGUMENTS ARE EVALUATED MORE THAN ONCE! */ + +#define putMemory(ADDR, BYTE) \ + ( writeCallback[ADDR] \ + ? writeCallback[ADDR](mpu, ADDR, BYTE) \ + : (memory[ADDR]= BYTE) ) + +#define getMemory(ADDR) \ + ( readCallback[ADDR] \ + ? readCallback[ADDR](mpu, ADDR, 0) \ + : memory[ADDR] ) + +/* stack access (always direct) */ + +#define push(BYTE) (memory[0x0100 + S--]= (BYTE)) +#define pop() (memory[++S + 0x0100]) + +/* adressing modes (memory access direct) */ + +#define implied(ticks) \ + tick(ticks); + +#define immediate(ticks) \ + tick(ticks); \ + ea= PC++; + +#define abs(ticks) \ + tick(ticks); \ + ea= memory[PC] + (memory[PC + 1] << 8); \ + PC += 2; + +#define relative(ticks) \ + tick(ticks); \ + ea= memory[PC++]; \ + if (ea & 0x80) ea -= 0x100; \ + tickIf((ea >> 8) != (PC >> 8)); + +#define indirect(ticks) \ + tick(ticks); \ + { \ + word tmp; \ + tmp= memory[PC] + (memory[PC + 1] << 8); \ + ea = memory[tmp] + (memory[tmp + 1] << 8); \ + PC += 2; \ + } + +#define absx(ticks) \ + tick(ticks); \ + ea= memory[PC] + (memory[PC + 1] << 8); \ + PC += 2; \ + tickIf((ticks == 4) && ((ea >> 8) != ((ea + X) >> 8))); \ + ea += X; + +#define absy(ticks) \ + tick(ticks); \ + ea= memory[PC] + (memory[PC + 1] << 8); \ + PC += 2; \ + tickIf((ticks == 4) && ((ea >> 8) != ((ea + Y) >> 8))); \ + ea += Y + +#define zp(ticks) \ + tick(ticks); \ + ea= memory[PC++]; + +#define zpx(ticks) \ + tick(ticks); \ + ea= memory[PC++] + X; \ + ea &= 0x00ff; + +#define zpy(ticks) \ + tick(ticks); \ + ea= memory[PC++] + Y; \ + ea &= 0x00ff; + +#define indx(ticks) \ + tick(ticks); \ + { \ + byte tmp= memory[PC++] + X; \ + ea= memory[tmp] + (memory[tmp + 1] << 8); \ + } + +#define indy(ticks) \ + tick(ticks); \ + { \ + byte tmp= memory[PC++]; \ + ea= memory[tmp] + (memory[tmp + 1] << 8); \ + tickIf((ticks == 5) && ((ea >> 8) != ((ea + Y) >> 8))); \ + ea += Y; \ + } + +#define indabsx(ticks) \ + tick(ticks); \ + { \ + word tmp; \ + tmp= memory[PC ] + (memory[PC + 1] << 8) + X; \ + ea = memory[tmp] + (memory[tmp + 1] << 8); \ + } + +#define indzp(ticks) \ + tick(ticks); \ + { \ + byte tmp; \ + tmp= memory[PC++]; \ + ea = memory[tmp] + (memory[tmp + 1] << 8); \ + } + +/* insns */ + +#define adc(ticks, adrmode) \ + adrmode(ticks); \ + { \ + byte B= getMemory(ea); \ + if (!getD()) \ + { \ + int c= A + B + getC(); \ + int v= (int8_t)A + (int8_t)B + getC(); \ + fetch(); \ + A= c; \ + setNVZC((A & 0x80), (((A & 0x80) > 0) ^ (v < 0)), (A == 0), ((c & 0x100) > 0)); \ + next(); \ + } \ + else \ + { \ + int l, h, s; \ + /* inelegant & slow, but consistent with the hw for illegal digits */ \ + l= (A & 0x0F) + (B & 0x0F) + getC(); \ + h= (A & 0xF0) + (B & 0xF0); \ + if (l >= 0x0A) { l -= 0x0A; h += 0x10; } \ + if (h >= 0xA0) { h -= 0xA0; } \ + fetch(); \ + s= h | (l & 0x0F); \ + /* only C is valid on NMOS 6502 */ \ + setNVZC(s & 0x80, !(((A ^ B) & 0x80) && ((A ^ s) & 0x80)), !s, !!(h & 0x80)); \ + A= s; \ + tick(1); \ + next(); \ + } \ + } + +#define sbc(ticks, adrmode) \ + adrmode(ticks); \ + { \ + byte B= getMemory(ea); \ + if (!getD()) \ + { \ + int b= 1 - (P &0x01); \ + int c= A - B - b; \ + int v= (int8_t)A - (int8_t) B - b; \ + fetch(); \ + A= c; \ + setNVZC(A & 0x80, ((A & 0x80) > 0) ^ ((v & 0x100) != 0), A == 0, c >= 0); \ + next(); \ + } \ + else \ + { \ + /* this is verbatim ADC, with a 10's complemented operand */ \ + int l, h, s; \ + B= 0x99 - B; \ + l= (A & 0x0F) + (B & 0x0F) + getC(); \ + h= (A & 0xF0) + (B & 0xF0); \ + if (l >= 0x0A) { l -= 0x0A; h += 0x10; } \ + if (h >= 0xA0) { h -= 0xA0; } \ + fetch(); \ + s= h | (l & 0x0F); \ + /* only C is valid on NMOS 6502 */ \ + setNVZC(s & 0x80, !(((A ^ B) & 0x80) && ((A ^ s) & 0x80)), !s, !!(h & 0x80)); \ + A= s; \ + tick(1); \ + next(); \ + } \ + } + +#define cmpR(ticks, adrmode, R) \ + adrmode(ticks); \ + fetch(); \ + { \ + byte B= getMemory(ea); \ + byte d= R - B; \ + setNZC(d & 0x80, !d, R >= B); \ + } \ + next(); + +#define cmp(ticks, adrmode) cmpR(ticks, adrmode, A) +#define cpx(ticks, adrmode) cmpR(ticks, adrmode, X) +#define cpy(ticks, adrmode) cmpR(ticks, adrmode, Y) + +#define dec(ticks, adrmode) \ + adrmode(ticks); \ + fetch(); \ + { \ + byte B= getMemory(ea); \ + --B; \ + putMemory(ea, B); \ + setNZ(B & 0x80, !B); \ + } \ + next(); + +#define decR(ticks, adrmode, R) \ + fetch(); \ + tick(ticks); \ + --R; \ + setNZ(R & 0x80, !R); \ + next(); + +#define dea(ticks, adrmode) decR(ticks, adrmode, A) +#define dex(ticks, adrmode) decR(ticks, adrmode, X) +#define dey(ticks, adrmode) decR(ticks, adrmode, Y) + +#define inc(ticks, adrmode) \ + adrmode(ticks); \ + fetch(); \ + { \ + byte B= getMemory(ea); \ + ++B; \ + putMemory(ea, B); \ + setNZ(B & 0x80, !B); \ + } \ + next(); + +#define incR(ticks, adrmode, R) \ + fetch(); \ + tick(ticks); \ + ++R; \ + setNZ(R & 0x80, !R); \ + next(); + +#define ina(ticks, adrmode) incR(ticks, adrmode, A) +#define inx(ticks, adrmode) incR(ticks, adrmode, X) +#define iny(ticks, adrmode) incR(ticks, adrmode, Y) + +#define bit(ticks, adrmode) \ + adrmode(ticks); \ + fetch(); \ + { \ + byte B= getMemory(ea); \ + P= (P & ~(flagN | flagV | flagZ)) \ + | (B & (0xC0)) | (((A & B) == 0) << 1); \ + } \ + next(); + +#define tsb(ticks, adrmode) \ + adrmode(ticks); \ + fetch(); \ + { \ + byte b= getMemory(ea); \ + b |= A; \ + putMemory(ea, b); \ + setZ(!b); \ + } \ + next(); + +#define trb(ticks, adrmode) \ + adrmode(ticks); \ + fetch(); \ + { \ + byte b= getMemory(ea); \ + b |= (A ^ 0xFF); \ + putMemory(ea, b); \ + setZ(!b); \ + } \ + next(); + +#define bitwise(ticks, adrmode, op) \ + adrmode(ticks); \ + fetch(); \ + A op##= getMemory(ea); \ + setNZ(A & 0x80, !A); \ + next(); + +#define and(ticks, adrmode) bitwise(ticks, adrmode, &) +#define eor(ticks, adrmode) bitwise(ticks, adrmode, ^) +#define ora(ticks, adrmode) bitwise(ticks, adrmode, |) + +#define asl(ticks, adrmode) \ + adrmode(ticks); \ + { \ + unsigned int i= getMemory(ea) << 1; \ + putMemory(ea, i); \ + fetch(); \ + setNZC(i & 0x80, !i, i >> 8); \ + } \ + next(); + +#define asla(ticks, adrmode) \ + tick(ticks); \ + fetch(); \ + { \ + int c= A >> 7; \ + A <<= 1; \ + setNZC(A & 0x80, !A, c); \ + } \ + next(); + +#define lsr(ticks, adrmode) \ + adrmode(ticks); \ + { \ + byte b= getMemory(ea); \ + int c= b & 1; \ + fetch(); \ + b >>= 1; \ + putMemory(ea, b); \ + setNZC(0, !b, c); \ + } \ + next(); + +#define lsra(ticks, adrmode) \ + tick(ticks); \ + fetch(); \ + { \ + int c= A & 1; \ + A >>= 1; \ + setNZC(0, !A, c); \ + } \ + next(); + +#define rol(ticks, adrmode) \ + adrmode(ticks); \ + { \ + word b= (getMemory(ea) << 1) | getC(); \ + fetch(); \ + putMemory(ea, b); \ + setNZC(b & 0x80, !(b & 0xFF), b >> 8); \ + } \ + next(); + +#define rola(ticks, adrmode) \ + tick(ticks); \ + fetch(); \ + { \ + word b= (A << 1) | getC(); \ + A= b; \ + setNZC(A & 0x80, !A, b >> 8); \ + } \ + next(); + +#define ror(ticks, adrmode) \ + adrmode(ticks); \ + { \ + int c= getC(); \ + byte m= getMemory(ea); \ + byte b= (c << 7) | (m >> 1); \ + fetch(); \ + putMemory(ea, b); \ + setNZC(b & 0x80, !b, m & 1); \ + } \ + next(); + +#define rora(ticks, adrmode) \ + adrmode(ticks); \ + { \ + int ci= getC(); \ + int co= A & 1; \ + fetch(); \ + A= (ci << 7) | (A >> 1); \ + setNZC(A & 0x80, !A, co); \ + } \ + next(); + +#define tRS(ticks, adrmode, R, S) \ + fetch(); \ + tick(ticks); \ + S= R; \ + setNZ(S & 0x80, !S); \ + next(); + +#define tax(ticks, adrmode) tRS(ticks, adrmode, A, X) +#define txa(ticks, adrmode) tRS(ticks, adrmode, X, A) +#define tay(ticks, adrmode) tRS(ticks, adrmode, A, Y) +#define tya(ticks, adrmode) tRS(ticks, adrmode, Y, A) +#define tsx(ticks, adrmode) tRS(ticks, adrmode, S, X) + +#define txs(ticks, adrmode) \ + fetch(); \ + tick(ticks); \ + S= X; \ + next(); + +#define ldR(ticks, adrmode, R) \ + adrmode(ticks); \ + fetch(); \ + R= getMemory(ea); \ + setNZ(R & 0x80, !R); \ + next(); + +#define lda(ticks, adrmode) ldR(ticks, adrmode, A) +#define ldx(ticks, adrmode) ldR(ticks, adrmode, X) +#define ldy(ticks, adrmode) ldR(ticks, adrmode, Y) + +#define stR(ticks, adrmode, R) \ + adrmode(ticks); \ + fetch(); \ + putMemory(ea, R); \ + next(); + +#define sta(ticks, adrmode) stR(ticks, adrmode, A) +#define stx(ticks, adrmode) stR(ticks, adrmode, X) +#define sty(ticks, adrmode) stR(ticks, adrmode, Y) +#define stz(ticks, adrmode) stR(ticks, adrmode, 0) + +#define branch(ticks, adrmode, cond) \ + if (cond) \ + { \ + adrmode(ticks); \ + PC += ea; \ + tick(1); \ + } \ + else \ + { \ + tick(ticks); \ + PC++; \ + } \ + fetch(); \ + next(); + +#define bcc(ticks, adrmode) branch(ticks, adrmode, !getC()) +#define bcs(ticks, adrmode) branch(ticks, adrmode, getC()) +#define bne(ticks, adrmode) branch(ticks, adrmode, !getZ()) +#define beq(ticks, adrmode) branch(ticks, adrmode, getZ()) +#define bpl(ticks, adrmode) branch(ticks, adrmode, !getN()) +#define bmi(ticks, adrmode) branch(ticks, adrmode, getN()) +#define bvc(ticks, adrmode) branch(ticks, adrmode, !getV()) +#define bvs(ticks, adrmode) branch(ticks, adrmode, getV()) + +#define bra(ticks, adrmode) \ + adrmode(ticks); \ + PC += ea; \ + fetch(); \ + tick(1); \ + next(); + +#define jmp(ticks, adrmode) \ + adrmode(ticks); \ + PC= ea; \ + if (mpu->callbacks->call[ea]) \ + { \ + word addr; \ + externalise(); \ + if ((addr= mpu->callbacks->call[ea](mpu, ea, 0))) \ + { \ + internalise(); \ + PC= addr; \ + } \ + } \ + fetch(); \ + next(); + +#define jsr(ticks, adrmode) \ + PC++; \ + push(PC >> 8); \ + push(PC & 0xff); \ + PC--; \ + adrmode(ticks); \ + if (mpu->callbacks->call[ea]) \ + { \ + word addr; \ + externalise(); \ + if ((addr= mpu->callbacks->call[ea](mpu, ea, 0))) \ + { \ + internalise(); \ + PC= addr; \ + fetch(); \ + next(); \ + } \ + } \ + PC=ea; \ + fetch(); \ + next(); + +#define rts(ticks, adrmode) \ + tick(ticks); \ + PC = pop(); \ + PC |= (pop() << 8); \ + PC++; \ + fetch(); \ + next(); + +#define brk(ticks, adrmode) \ + tick(ticks); \ + PC++; \ + push(PC >> 8); \ + push(PC & 0xff); \ + P |= flagB; \ + push(P | flagX); \ + P |= flagI; \ + { \ + word hdlr= getMemory(0xfffe) + (getMemory(0xffff) << 8); \ + if (mpu->callbacks->call[hdlr]) \ + { \ + word addr; \ + externalise(); \ + if ((addr= mpu->callbacks->call[hdlr](mpu, PC - 2, 0))) \ + { \ + internalise(); \ + hdlr= addr; \ + } \ + } \ + PC= hdlr; \ + } \ + fetch(); \ + next(); + +#define rti(ticks, adrmode) \ + tick(ticks); \ + P= pop(); \ + PC= pop(); \ + PC |= (pop() << 8); \ + fetch(); \ + next(); + +#define nop(ticks, adrmode) \ + fetch(); \ + tick(ticks); \ + next(); + +#define ill(ticks, adrmode) \ + fetch(); \ + tick(ticks); \ + fflush(stdout); \ + fprintf(stderr, "\nundefined instruction %02X\n", memory[PC-1]); \ + return; + +#define phR(ticks, adrmode, R) \ + fetch(); \ + tick(ticks); \ + push(R); \ + next(); + +#define pha(ticks, adrmode) phR(ticks, adrmode, A) +#define phx(ticks, adrmode) phR(ticks, adrmode, X) +#define phy(ticks, adrmode) phR(ticks, adrmode, Y) +#define php(ticks, adrmode) phR(ticks, adrmode, P | flagX | flagB) + +#define plR(ticks, adrmode, R) \ + fetch(); \ + tick(ticks); \ + R= pop(); \ + setNZ(R & 0x80, !R); \ + next(); + +#define pla(ticks, adrmode) plR(ticks, adrmode, A) +#define plx(ticks, adrmode) plR(ticks, adrmode, X) +#define ply(ticks, adrmode) plR(ticks, adrmode, Y) + +#define plp(ticks, adrmode) \ + fetch(); \ + tick(ticks); \ + P= pop(); \ + next(); + +#define clF(ticks, adrmode, F) \ + fetch(); \ + tick(ticks); \ + P &= ~F; \ + next(); + +#define clc(ticks, adrmode) clF(ticks, adrmode, flagC) +#define cld(ticks, adrmode) clF(ticks, adrmode, flagD) +#define cli(ticks, adrmode) clF(ticks, adrmode, flagI) +#define clv(ticks, adrmode) clF(ticks, adrmode, flagV) + +#define seF(ticks, adrmode, F) \ + fetch(); \ + tick(ticks); \ + P |= F; \ + next(); + +#define sec(ticks, adrmode) seF(ticks, adrmode, flagC) +#define sed(ticks, adrmode) seF(ticks, adrmode, flagD) +#define sei(ticks, adrmode) seF(ticks, adrmode, flagI) + +#define do_insns(_) \ + _(00, brk, implied, 7); _(01, ora, indx, 6); _(02, ill, implied, 2); _(03, ill, implied, 2); \ + _(04, tsb, zp, 3); _(05, ora, zp, 3); _(06, asl, zp, 5); _(07, ill, implied, 2); \ + _(08, php, implied, 3); _(09, ora, immediate, 3); _(0a, asla,implied, 2); _(0b, ill, implied, 2); \ + _(0c, tsb, abs, 4); _(0d, ora, abs, 4); _(0e, asl, abs, 6); _(0f, ill, implied, 2); \ + _(10, bpl, relative, 2); _(11, ora, indy, 5); _(12, ora, indzp, 3); _(13, ill, implied, 2); \ + _(14, trb, zp, 3); _(15, ora, zpx, 4); _(16, asl, zpx, 6); _(17, ill, implied, 2); \ + _(18, clc, implied, 2); _(19, ora, absy, 4); _(1a, ina, implied, 2); _(1b, ill, implied, 2); \ + _(1c, trb, abs, 4); _(1d, ora, absx, 4); _(1e, asl, absx, 7); _(1f, ill, implied, 2); \ + _(20, jsr, abs, 6); _(21, and, indx, 6); _(22, ill, implied, 2); _(23, ill, implied, 2); \ + _(24, bit, zp, 3); _(25, and, zp, 3); _(26, rol, zp, 5); _(27, ill, implied, 2); \ + _(28, plp, implied, 4); _(29, and, immediate, 3); _(2a, rola,implied, 2); _(2b, ill, implied, 2); \ + _(2c, bit, abs, 4); _(2d, and, abs, 4); _(2e, rol, abs, 6); _(2f, ill, implied, 2); \ + _(30, bmi, relative, 2); _(31, and, indy, 5); _(32, and, indzp, 3); _(33, ill, implied, 2); \ + _(34, bit, zpx, 4); _(35, and, zpx, 4); _(36, rol, zpx, 6); _(37, ill, implied, 2); \ + _(38, sec, implied, 2); _(39, and, absy, 4); _(3a, dea, implied, 2); _(3b, ill, implied, 2); \ + _(3c, bit, absx, 4); _(3d, and, absx, 4); _(3e, rol, absx, 7); _(3f, ill, implied, 2); \ + _(40, rti, implied, 6); _(41, eor, indx, 6); _(42, ill, implied, 2); _(43, ill, implied, 2); \ + _(44, ill, implied, 2); _(45, eor, zp, 3); _(46, lsr, zp, 5); _(47, ill, implied, 2); \ + _(48, pha, implied, 3); _(49, eor, immediate, 3); _(4a, lsra,implied, 2); _(4b, ill, implied, 2); \ + _(4c, jmp, abs, 3); _(4d, eor, abs, 4); _(4e, lsr, abs, 6); _(4f, ill, implied, 2); \ + _(50, bvc, relative, 2); _(51, eor, indy, 5); _(52, eor, indzp, 3); _(53, ill, implied, 2); \ + _(54, ill, implied, 2); _(55, eor, zpx, 4); _(56, lsr, zpx, 6); _(57, ill, implied, 2); \ + _(58, cli, implied, 2); _(59, eor, absy, 4); _(5a, phy, implied, 3); _(5b, ill, implied, 2); \ + _(5c, ill, implied, 2); _(5d, eor, absx, 4); _(5e, lsr, absx, 7); _(5f, ill, implied, 2); \ + _(60, rts, implied, 6); _(61, adc, indx, 6); _(62, ill, implied, 2); _(63, ill, implied, 2); \ + _(64, stz, zp, 3); _(65, adc, zp, 3); _(66, ror, zp, 5); _(67, ill, implied, 2); \ + _(68, pla, implied, 4); _(69, adc, immediate, 3); _(6a, rora,implied, 2); _(6b, ill, implied, 2); \ + _(6c, jmp, indirect, 5); _(6d, adc, abs, 4); _(6e, ror, abs, 6); _(6f, ill, implied, 2); \ + _(70, bvs, relative, 2); _(71, adc, indy, 5); _(72, adc, indzp, 3); _(73, ill, implied, 2); \ + _(74, stz, zpx, 4); _(75, adc, zpx, 4); _(76, ror, zpx, 6); _(77, ill, implied, 2); \ + _(78, sei, implied, 2); _(79, adc, absy, 4); _(7a, ply, implied, 4); _(7b, ill, implied, 2); \ + _(7c, jmp, indabsx, 6); _(7d, adc, absx, 4); _(7e, ror, absx, 7); _(7f, ill, implied, 2); \ + _(80, bra, relative, 2); _(81, sta, indx, 6); _(82, ill, implied, 2); _(83, ill, implied, 2); \ + _(84, sty, zp, 2); _(85, sta, zp, 2); _(86, stx, zp, 2); _(87, ill, implied, 2); \ + _(88, dey, implied, 2); _(89, bit, immediate, 2); _(8a, txa, implied, 2); _(8b, ill, implied, 2); \ + _(8c, sty, abs, 4); _(8d, sta, abs, 4); _(8e, stx, abs, 4); _(8f, ill, implied, 2); \ + _(90, bcc, relative, 2); _(91, sta, indy, 6); _(92, sta, indzp, 3); _(93, ill, implied, 2); \ + _(94, sty, zpx, 4); _(95, sta, zpx, 4); _(96, stx, zpy, 4); _(97, ill, implied, 2); \ + _(98, tya, implied, 2); _(99, sta, absy, 5); _(9a, txs, implied, 2); _(9b, ill, implied, 2); \ + _(9c, stz, abs, 4); _(9d, sta, absx, 5); _(9e, stz, absx, 5); _(9f, ill, implied, 2); \ + _(a0, ldy, immediate, 3); _(a1, lda, indx, 6); _(a2, ldx, immediate, 3); _(a3, ill, implied, 2); \ + _(a4, ldy, zp, 3); _(a5, lda, zp, 3); _(a6, ldx, zp, 3); _(a7, ill, implied, 2); \ + _(a8, tay, implied, 2); _(a9, lda, immediate, 3); _(aa, tax, implied, 2); _(ab, ill, implied, 2); \ + _(ac, ldy, abs, 4); _(ad, lda, abs, 4); _(ae, ldx, abs, 4); _(af, ill, implied, 2); \ + _(b0, bcs, relative, 2); _(b1, lda, indy, 5); _(b2, lda, indzp, 3); _(b3, ill, implied, 2); \ + _(b4, ldy, zpx, 4); _(b5, lda, zpx, 4); _(b6, ldx, zpy, 4); _(b7, ill, implied, 2); \ + _(b8, clv, implied, 2); _(b9, lda, absy, 4); _(ba, tsx, implied, 2); _(bb, ill, implied, 2); \ + _(bc, ldy, absx, 4); _(bd, lda, absx, 4); _(be, ldx, absy, 4); _(bf, ill, implied, 2); \ + _(c0, cpy, immediate, 3); _(c1, cmp, indx, 6); _(c2, ill, implied, 2); _(c3, ill, implied, 2); \ + _(c4, cpy, zp, 3); _(c5, cmp, zp, 3); _(c6, dec, zp, 5); _(c7, ill, implied, 2); \ + _(c8, iny, implied, 2); _(c9, cmp, immediate, 3); _(ca, dex, implied, 2); _(cb, ill, implied, 2); \ + _(cc, cpy, abs, 4); _(cd, cmp, abs, 4); _(ce, dec, abs, 6); _(cf, ill, implied, 2); \ + _(d0, bne, relative, 2); _(d1, cmp, indy, 5); _(d2, cmp, indzp, 3); _(d3, ill, implied, 2); \ + _(d4, ill, implied, 2); _(d5, cmp, zpx, 4); _(d6, dec, zpx, 6); _(d7, ill, implied, 2); \ + _(d8, cld, implied, 2); _(d9, cmp, absy, 4); _(da, phx, implied, 3); _(db, ill, implied, 2); \ + _(dc, ill, implied, 2); _(dd, cmp, absx, 4); _(de, dec, absx, 7); _(df, ill, implied, 2); \ + _(e0, cpx, immediate, 3); _(e1, sbc, indx, 6); _(e2, ill, implied, 2); _(e3, ill, implied, 2); \ + _(e4, cpx, zp, 3); _(e5, sbc, zp, 3); _(e6, inc, zp, 5); _(e7, ill, implied, 2); \ + _(e8, inx, implied, 2); _(e9, sbc, immediate, 3); _(ea, nop, implied, 2); _(eb, ill, implied, 2); \ + _(ec, cpx, abs, 4); _(ed, sbc, abs, 4); _(ee, inc, abs, 6); _(ef, ill, implied, 2); \ + _(f0, beq, relative, 2); _(f1, sbc, indy, 5); _(f2, sbc, indzp, 3); _(f3, ill, implied, 2); \ + _(f4, ill, implied, 2); _(f5, sbc, zpx, 4); _(f6, inc, zpx, 6); _(f7, ill, implied, 2); \ + _(f8, sed, implied, 2); _(f9, sbc, absy, 4); _(fa, plx, implied, 4); _(fb, ill, implied, 2); \ + _(fc, ill, implied, 2); _(fd, sbc, absx, 4); _(fe, inc, absx, 7); _(ff, ill, implied, 2); + + + +void M6502_irq(M6502 *mpu) +{ + if (!(mpu->registers->p & flagI)) + { + mpu->memory[0x0100 + mpu->registers->s--] = (byte)(mpu->registers->pc >> 8); + mpu->memory[0x0100 + mpu->registers->s--] = (byte)(mpu->registers->pc & 0xff); + mpu->memory[0x0100 + mpu->registers->s--] = mpu->registers->p; + mpu->registers->p &= ~flagB; + mpu->registers->p |= flagI; + mpu->registers->pc = M6502_getVector(mpu, IRQ); + } +} + + +void M6502_nmi(M6502 *mpu) +{ + mpu->memory[0x0100 + mpu->registers->s--] = (byte)(mpu->registers->pc >> 8); + mpu->memory[0x0100 + mpu->registers->s--] = (byte)(mpu->registers->pc & 0xff); + mpu->memory[0x0100 + mpu->registers->s--] = mpu->registers->p; + mpu->registers->p &= ~flagB; + mpu->registers->p |= flagI; + mpu->registers->pc = M6502_getVector(mpu, NMI); +} + + +void M6502_reset(M6502 *mpu) +{ + mpu->registers->p &= ~flagD; + mpu->registers->p |= flagI; + mpu->registers->pc = M6502_getVector(mpu, RST); +} + + +/* the compiler should elminate all call to this function */ + +static void oops(void) +{ + fprintf(stderr, "\noops -- instruction dispatch missing\n"); +} + + +void M6502_run(M6502 *mpu) +{ +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) + + static void *itab[256]= { &&_00, &&_01, &&_02, &&_03, &&_04, &&_05, &&_06, &&_07, &&_08, &&_09, &&_0a, &&_0b, &&_0c, &&_0d, &&_0e, &&_0f, + &&_10, &&_11, &&_12, &&_13, &&_14, &&_15, &&_16, &&_17, &&_18, &&_19, &&_1a, &&_1b, &&_1c, &&_1d, &&_1e, &&_1f, + &&_20, &&_21, &&_22, &&_23, &&_24, &&_25, &&_26, &&_27, &&_28, &&_29, &&_2a, &&_2b, &&_2c, &&_2d, &&_2e, &&_2f, + &&_30, &&_31, &&_32, &&_33, &&_34, &&_35, &&_36, &&_37, &&_38, &&_39, &&_3a, &&_3b, &&_3c, &&_3d, &&_3e, &&_3f, + &&_40, &&_41, &&_42, &&_43, &&_44, &&_45, &&_46, &&_47, &&_48, &&_49, &&_4a, &&_4b, &&_4c, &&_4d, &&_4e, &&_4f, + &&_50, &&_51, &&_52, &&_53, &&_54, &&_55, &&_56, &&_57, &&_58, &&_59, &&_5a, &&_5b, &&_5c, &&_5d, &&_5e, &&_5f, + &&_60, &&_61, &&_62, &&_63, &&_64, &&_65, &&_66, &&_67, &&_68, &&_69, &&_6a, &&_6b, &&_6c, &&_6d, &&_6e, &&_6f, + &&_70, &&_71, &&_72, &&_73, &&_74, &&_75, &&_76, &&_77, &&_78, &&_79, &&_7a, &&_7b, &&_7c, &&_7d, &&_7e, &&_7f, + &&_80, &&_81, &&_82, &&_83, &&_84, &&_85, &&_86, &&_87, &&_88, &&_89, &&_8a, &&_8b, &&_8c, &&_8d, &&_8e, &&_8f, + &&_90, &&_91, &&_92, &&_93, &&_94, &&_95, &&_96, &&_97, &&_98, &&_99, &&_9a, &&_9b, &&_9c, &&_9d, &&_9e, &&_9f, + &&_a0, &&_a1, &&_a2, &&_a3, &&_a4, &&_a5, &&_a6, &&_a7, &&_a8, &&_a9, &&_aa, &&_ab, &&_ac, &&_ad, &&_ae, &&_af, + &&_b0, &&_b1, &&_b2, &&_b3, &&_b4, &&_b5, &&_b6, &&_b7, &&_b8, &&_b9, &&_ba, &&_bb, &&_bc, &&_bd, &&_be, &&_bf, + &&_c0, &&_c1, &&_c2, &&_c3, &&_c4, &&_c5, &&_c6, &&_c7, &&_c8, &&_c9, &&_ca, &&_cb, &&_cc, &&_cd, &&_ce, &&_cf, + &&_d0, &&_d1, &&_d2, &&_d3, &&_d4, &&_d5, &&_d6, &&_d7, &&_d8, &&_d9, &&_da, &&_db, &&_dc, &&_dd, &&_de, &&_df, + &&_e0, &&_e1, &&_e2, &&_e3, &&_e4, &&_e5, &&_e6, &&_e7, &&_e8, &&_e9, &&_ea, &&_eb, &&_ec, &&_ed, &&_ee, &&_ef, + &&_f0, &&_f1, &&_f2, &&_f3, &&_f4, &&_f5, &&_f6, &&_f7, &&_f8, &&_f9, &&_fa, &&_fb, &&_fc, &&_fd, &&_fe, &&_ff }; + + register void **itabp= &itab[0]; + register void *tpc; + +# define begin() fetch(); next() +# define fetch() tpc= itabp[memory[PC++]] +# define next() goto *tpc +# define dispatch(num, name, mode, cycles) _##num: name(cycles, mode) oops(); next() +# define end() + +#else /* (!__GNUC__) || (__STRICT_ANSI__) */ + +# define begin() for (;;) switch (memory[PC++]) { +# define fetch() +# define next() break +# define dispatch(num, name, mode, cycles) case 0x##num: name(cycles, mode); next() +# define end() } + +#endif + + register byte *memory= mpu->memory; + register word PC; + word ea; + byte A, X, Y, P, S; + M6502_Callback *readCallback= mpu->callbacks->read; + M6502_Callback *writeCallback= mpu->callbacks->write; + +# define internalise() A= mpu->registers->a; X= mpu->registers->x; Y= mpu->registers->y; P= mpu->registers->p; S= mpu->registers->s; PC= mpu->registers->pc +# define externalise() mpu->registers->a= A; mpu->registers->x= X; mpu->registers->y= Y; mpu->registers->p= P; mpu->registers->s= S; mpu->registers->pc= PC + + internalise(); + + begin(); + do_insns(dispatch); + end(); + +# undef begin +# undef internalise +# undef externalise +# undef fetch +# undef next +# undef dispatch +# undef end + + (void)oops; +} + + +int M6502_disassemble(M6502 *mpu, word ip, char buffer[64]) +{ + char *s= buffer; + byte *b= mpu->memory + ip; + + switch (b[0]) + { +# define _implied return 1; +# define _immediate sprintf(s, "#%02X", b[1]); return 2; +# define _zp sprintf(s, "%02X", b[1]); return 2; +# define _zpx sprintf(s, "%02X,X", b[1]); return 2; +# define _zpy sprintf(s, "%02X,Y", b[1]); return 2; +# define _abs sprintf(s, "%02X%02X", b[2], b[1]); return 3; +# define _absx sprintf(s, "%02X%02X,X", b[2], b[1]); return 3; +# define _absy sprintf(s, "%02X%02X,Y", b[2], b[1]); return 3; +# define _relative sprintf(s, "%04X", ip + 2 + (int8_t)b[1]); return 2; +# define _indirect sprintf(s, "(%02X%02X)", b[2], b[1]); return 3; +# define _indzp sprintf(s, "(%02X)", b[1]); return 2; +# define _indx sprintf(s, "(%02X,X)", b[1]); return 2; +# define _indy sprintf(s, "(%02X),Y", b[1]); return 2; +# define _indabsx sprintf(s, "(%02X%02X,X)", b[2], b[1]); return 3; + +# define disassemble(num, name, mode, cycles) case 0x##num: s += sprintf(s, "%s ", #name); _##mode + do_insns(disassemble); +# undef _do + } + + return 0; +} + + +void M6502_dump(M6502 *mpu, char buffer[64]) +{ + M6502_Registers *r= mpu->registers; + uint8_t p= r->p; +# define P(N,C) (p & (1 << (N)) ? (C) : '-') + sprintf(buffer, "PC=%04X SP=%04X A=%02X X=%02X Y=%02X P=%02X %c%c%c%c%c%c%c%c", + r->pc, 0x0100 + r->s, + r->a, r->x, r->y, r->p, + P(7,'N'), P(6,'V'), P(5,'?'), P(4,'B'), P(3,'D'), P(2,'I'), P(1,'Z'), P(0,'C')); +# undef P +} + + +static void outOfMemory(void) +{ + fflush(stdout); + fprintf(stderr, "\nout of memory\n"); + abort(); +} + + +M6502 *M6502_new(M6502_Registers *registers, M6502_Memory memory, M6502_Callbacks *callbacks) +{ + M6502 *mpu= calloc(1, sizeof(M6502)); + if (!mpu) outOfMemory(); + + if (!registers) { registers = (M6502_Registers *)calloc(1, sizeof(M6502_Registers)); mpu->flags |= M6502_RegistersAllocated; } + if (!memory ) { memory = (uint8_t *)calloc(1, sizeof(M6502_Memory )); mpu->flags |= M6502_MemoryAllocated; } + if (!callbacks) { callbacks = (M6502_Callbacks *)calloc(1, sizeof(M6502_Callbacks)); mpu->flags |= M6502_CallbacksAllocated; } + + if (!registers || !memory || !callbacks) outOfMemory(); + + mpu->registers = registers; + mpu->memory = memory; + mpu->callbacks = callbacks; + + return mpu; +} + + +void M6502_delete(M6502 *mpu) +{ + if (mpu->flags & M6502_CallbacksAllocated) free(mpu->callbacks); + if (mpu->flags & M6502_MemoryAllocated ) free(mpu->memory); + if (mpu->flags & M6502_RegistersAllocated) free(mpu->registers); + + free(mpu); +} diff --git a/src/lib6502/lib6502.h b/src/lib6502/lib6502.h new file mode 100644 index 0000000..7039833 --- /dev/null +++ b/src/lib6502/lib6502.h @@ -0,0 +1,75 @@ +#ifndef __m6502_h +#define __m6502_h + + +#include +#include + +typedef struct _M6502 M6502; +typedef struct _M6502_Registers M6502_Registers; +typedef struct _M6502_Callbacks M6502_Callbacks; + +typedef int (*M6502_Callback)(M6502 *mpu, uint16_t address, uint8_t data); + +typedef M6502_Callback M6502_CallbackTable[0x10000]; +typedef uint8_t M6502_Memory[0x10000]; + +enum { + M6502_NMIVector= 0xfffa, M6502_NMIVectorLSB= 0xfffa, M6502_NMIVectorMSB= 0xfffb, + M6502_RSTVector= 0xfffc, M6502_RSTVectorLSB= 0xfffc, M6502_RSTVectorMSB= 0xfffd, + M6502_IRQVector= 0xfffe, M6502_IRQVectorLSB= 0xfffe, M6502_IRQVectorMSB= 0xffff +}; + +struct _M6502_Registers +{ + uint8_t a; /* accumulator */ + uint8_t x; /* X index register */ + uint8_t y; /* Y index register */ + uint8_t p; /* processor status register */ + uint8_t s; /* stack pointer */ + uint16_t pc; /* program counter */ +}; + +struct _M6502_Callbacks +{ + M6502_CallbackTable read; + M6502_CallbackTable write; + M6502_CallbackTable call; +}; + +struct _M6502 +{ + M6502_Registers *registers; + uint8_t *memory; + M6502_Callbacks *callbacks; + unsigned int flags; +}; + +enum { + M6502_RegistersAllocated = 1 << 0, + M6502_MemoryAllocated = 1 << 1, + M6502_CallbacksAllocated = 1 << 2 +}; + +extern M6502 *M6502_new(M6502_Registers *registers, M6502_Memory memory, M6502_Callbacks *callbacks); +extern void M6502_reset(M6502 *mpu); +extern void M6502_nmi(M6502 *mpu); +extern void M6502_irq(M6502 *mpu); +extern void M6502_run(M6502 *mpu); +extern int M6502_disassemble(M6502 *mpu, uint16_t addr, char buffer[64]); +extern void M6502_dump(M6502 *mpu, char buffer[64]); +extern void M6502_delete(M6502 *mpu); + +#define M6502_getVector(MPU, VEC) \ + ( ( ((MPU)->memory[M6502_##VEC##VectorLSB]) ) \ + | ((MPU)->memory[M6502_##VEC##VectorMSB] << 8) ) + +#define M6502_setVector(MPU, VEC, ADDR) \ + ( ( ((MPU)->memory[M6502_##VEC##VectorLSB]= ((uint8_t)(ADDR)) & 0xff) ) \ + , ((MPU)->memory[M6502_##VEC##VectorMSB]= (uint8_t)((ADDR) >> 8)) ) + +#define M6502_getCallback(MPU, TYPE, ADDR) ((MPU)->callbacks->TYPE[ADDR]) +#define M6502_setCallback(MPU, TYPE, ADDR, FN) ((MPU)->callbacks->TYPE[ADDR]= (FN)) + + +#endif __m6502_h diff --git a/src/lib6502/man/M6502_delete.3 b/src/lib6502/man/M6502_delete.3 new file mode 100644 index 0000000..4bd1ff4 --- /dev/null +++ b/src/lib6502/man/M6502_delete.3 @@ -0,0 +1 @@ +.so man3/lib6502.3 diff --git a/src/lib6502/man/M6502_disassemble.3 b/src/lib6502/man/M6502_disassemble.3 new file mode 100644 index 0000000..4bd1ff4 --- /dev/null +++ b/src/lib6502/man/M6502_disassemble.3 @@ -0,0 +1 @@ +.so man3/lib6502.3 diff --git a/src/lib6502/man/M6502_dump.3 b/src/lib6502/man/M6502_dump.3 new file mode 100644 index 0000000..4bd1ff4 --- /dev/null +++ b/src/lib6502/man/M6502_dump.3 @@ -0,0 +1 @@ +.so man3/lib6502.3 diff --git a/src/lib6502/man/M6502_getCallback.3 b/src/lib6502/man/M6502_getCallback.3 new file mode 100644 index 0000000..4bd1ff4 --- /dev/null +++ b/src/lib6502/man/M6502_getCallback.3 @@ -0,0 +1 @@ +.so man3/lib6502.3 diff --git a/src/lib6502/man/M6502_getVector.3 b/src/lib6502/man/M6502_getVector.3 new file mode 100644 index 0000000..4bd1ff4 --- /dev/null +++ b/src/lib6502/man/M6502_getVector.3 @@ -0,0 +1 @@ +.so man3/lib6502.3 diff --git a/src/lib6502/man/M6502_irq.3 b/src/lib6502/man/M6502_irq.3 new file mode 100644 index 0000000..4bd1ff4 --- /dev/null +++ b/src/lib6502/man/M6502_irq.3 @@ -0,0 +1 @@ +.so man3/lib6502.3 diff --git a/src/lib6502/man/M6502_new.3 b/src/lib6502/man/M6502_new.3 new file mode 100644 index 0000000..4bd1ff4 --- /dev/null +++ b/src/lib6502/man/M6502_new.3 @@ -0,0 +1 @@ +.so man3/lib6502.3 diff --git a/src/lib6502/man/M6502_nmi.3 b/src/lib6502/man/M6502_nmi.3 new file mode 100644 index 0000000..4bd1ff4 --- /dev/null +++ b/src/lib6502/man/M6502_nmi.3 @@ -0,0 +1 @@ +.so man3/lib6502.3 diff --git a/src/lib6502/man/M6502_reset.3 b/src/lib6502/man/M6502_reset.3 new file mode 100644 index 0000000..4bd1ff4 --- /dev/null +++ b/src/lib6502/man/M6502_reset.3 @@ -0,0 +1 @@ +.so man3/lib6502.3 diff --git a/src/lib6502/man/M6502_run.3 b/src/lib6502/man/M6502_run.3 new file mode 100644 index 0000000..4bd1ff4 --- /dev/null +++ b/src/lib6502/man/M6502_run.3 @@ -0,0 +1 @@ +.so man3/lib6502.3 diff --git a/src/lib6502/man/M6502_setCallback.3 b/src/lib6502/man/M6502_setCallback.3 new file mode 100644 index 0000000..4bd1ff4 --- /dev/null +++ b/src/lib6502/man/M6502_setCallback.3 @@ -0,0 +1 @@ +.so man3/lib6502.3 diff --git a/src/lib6502/man/M6502_setVector.3 b/src/lib6502/man/M6502_setVector.3 new file mode 100644 index 0000000..4bd1ff4 --- /dev/null +++ b/src/lib6502/man/M6502_setVector.3 @@ -0,0 +1 @@ +.so man3/lib6502.3 diff --git a/src/lib6502/man/lib6502.3 b/src/lib6502/man/lib6502.3 new file mode 100644 index 0000000..ba96e93 --- /dev/null +++ b/src/lib6502/man/lib6502.3 @@ -0,0 +1,508 @@ +.\" Copyright (c) 2005 Ian Piumarta +.\" +.\" 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:07 by piumarta on margaux.local +.\" +.Dd October 31, 2005 +.Dt LIB6502 3 LOCAL +.Os "" +.\" ---------------------------------------------------------------- +.Sh NAME +.\" +.Nm lib6502 +.Nd 6502 microprocessor emulator +.\" ---------------------------------------------------------------- +.Sh SYNOPSIS +.\" +.In stdint.h +.In lib6502.h +.Ft M6502 * +.Fn M6502_new "M6502_Registers *registers" "M6502_Memory memory" "M6502_Callbacks *callbacks" +.Ft void +.Fn M6502_reset "M6502 *mpu" +.Ft void +.Fn M6502_nmi "M6502 *mpu" +.Ft void +.Fn M6502_irq "M6502 *mpu" +.Ft uint16_t +.Fn M6502_getVector "M6502 *mpu" "vector" +.Ft uint16_t +.Fn M6502_setVector "M6502 *mpu" "vector" "uint16_t address" +.Ft M6502_Callback +.Fn M6502_getCallback "M6502 *mpu" "type" "uint16_t address" +.Ft M6502_Callback +.Fn M6502_setCallback "M6502 *mpu" "type" "uint16_t address" "M6502_Callback callback" +.Ft void +.Fn M6502_run "M6502 *mpu" +.Ft int +.Fn M6502_disassemble "M6502 *mpu" "uint16_t address" "char buffer[64]" +.Ft void +.Fn M6502_dump "M6502 *mpu" "char buffer[64]" +.Ft void +.Fn M6502_delete "M6502 *mpu" +.\" ---------------------------------------------------------------- +.Sh DESCRIPTION +.\" +.Fn M6502_new +creates an instance of a 6502 microprocessor. +.Fn M6502_reset , +.Fn M6502_nmi +and +.Fn M6502_irq +place it into the states associated with the hardware signals for +reset, non-maskable interrupt and interrupt request, respectively. +The macros +.Fn M6502_getVector +and +.Fn M6502_setVector +read and write the vectors through which the processor jumps in +response to the above signals. The macros +.Fn M6502_getCallback +and +.Fn M6502_setVector +read and write client-supplied functions that intercept accesses to +memory. +.Fn M6502_run +begins emulated execution. +.Fn M6502_dump +and +.Fn M6502_disassemble +create human-readable representations of processor or memory state. +.Fn M6502_delete +frees all resources associated with a processor instance. Each of +these functions and macros is described in more detail below. +.Pp +.Fn M6502_new +returns a pointer to a +.Fa M6502 +structure containing at least the following members: +.Bd -literal +struct _M6502 +{ + M6502_Registers *registers; /* processor state */ + uint8_t *memory; /* memory image */ + M6502_Callbacks *callbacks; /* r/w/x callbacks */ +}; +.Ed +.Pp +These members are initialised according to the supplied +.Fa registers , +.Fa memory +and +.Fa callbacks +arguments. If a given argument is NULL, the corresponding member is +initialised automatically with a suitable (non-NULL) value. +.Pp +The members of +.Fa M6502 +are as follows: +.Bl -tag -width ".Fa callbacks" +.It Fa registers +the processor state, containing all registers and condition codes. +.It Fa memory +a block of at least 64 kilobytes of storage containing the processor's +memory. (An array type +.Vt M6502_Memory, +suitable for defining values to pass as the +.Fa memory +argument, is defined in the +.In lib6502.h +include file.) +.It Fa callbacks +a structure mapping processor memory accesses to client callback +functions. +.El +.Pp +Access to the contents of the +.Fa registers +and +.Fa memory +members can be made directly. +The +.Fa registers +member is a +.Vt M6502_Registers +containing the following members: +.Bd -literal +struct _M6502_Registers +{ + uint8_t a; /* accumulator */ + uint8_t x; /* X index register */ + uint8_t y; /* Y index register */ + uint8_t p; /* processor status register */ + uint8_t s; /* stack pointer */ + uint16_t pc; /* program counter */ +}; +.Ed +.Pp +The +.Fa memory +member is an array of +.Vt unsigned char +and can be indexed directly. In addition, two convenience macros +.Fn M6502_getVector +and +.Fn M6502_setVector +provide access to the reset and interrupt vectors within +.Fa memory . +.Fn M6502_getVector +returns the address stored in the named +.Fa vector +which must be precisely one of the following: +.Bl -tag -width ".Dv RST" -offset indent +.It Dv RST +the reset vector. +.It Dv NMI +the non-maskable interrupt vector. +.It Dv IRQ +the interrupt request vector. +.El +.Pp +.Fn M6502_setVector +stores its +.Fa address +argument in the named +.Fa vector +and returns the new value. +.Pp +The +.Fa callbacks +member contains an opaque structure mapping processor memory accesses +to client callback functions. Whenever the processor performs an +access for which a corresponding entry exists in the the +.Fa callbacks +structure, the emulator suspends execution and invokes the callback to +complete the operation. Each callback function should have a +signature equivalent to: +.Bd -ragged -offset indent +int +.Va callback +(M6502 *mpu, uint16_t address, uint8_t data); +.Ed +.Pp +The macros +.Fn M6502_getCallback +and +.Fn M6502_setCallback +read and write entries in the +.Fa callbacks +structure. These macros identify a unique memory access operation +from the specified +.Fa address +on which it operates and +.Fa type +of access involved. The +.Fa type +argument must be one of the following: +.Bl -tag -width ".Dv write" +.It Dv read +the +.Fa callback +is invoked when the processor attempts to read from the +given address. The emulator passes the effective address of the +operation to the callback in its +.Fa address +argument. (The +.Fa data +argument is undefined.) The value returned by the callback will be +used by the emulator as the result of the read operation. +.It Dv write +the +.Fa callback +is invoked when the processor attempts to write to the +given address. The emulator passes the effective address of the +operation to the callback in its +.Fa address +argument and the byte being written in the +.Fa data +argument. The emulator will not perform the write operation before +invoking the callback; if the write should complete, the callback must +modify the processor's +.Fa memory +explicitly. The valued returned from the callback is ignored. +.It Dv call +the +.Fa callback +is invoked when the processor attempts to transfer control to the +given address by any instruction other than a relative branch. The +emulator passes the destination address to the callback in its +.Fa address +argument and the instruction that initiated the control transfer in +its +.Fa data +argument (one of JMP, JSR, BRK, RTS or RTI). If the callback returns +zero (the callback refuses to handle the operation) the emulator will +allow the operation to complete as normal. If the callback returns a +non-zero address (indicating that the callback has handled the +operation internally) the emulator will transfer control to that +address. +.El +.Pp +.Fn M6502_getCallback +returns zero if there is no callback associated with the given +.Fa type +and +.Fa address . +Passing zero as the +.Fa callback +argument of +.Fn M6502_setCallback +removes any callback that might have been associated with +.Fa type +and +.Fa address . +.Pp +.Fn M6502_run +emulates processor execution in the given +.Fa mpu +by repeatedly fetching the instruction addressed by +.Fa pc +and dispatching to it. This function normally never returns. +.Pp +.Fn M6502_dump +writes a (NUL-terminated) symbolic representation of the processor's +internal state into the supplied +.Fa buffer . +Typical output resembles: +.Bd -literal -offset indent +PC=1010 SP=01FE A=0A X=5B Y=00 P=D1 NV-B---C +.Ed +.Pp +.Fn M6502_disassemble +writes a (NUL-terminated) symbolic representation of the instruction +in the processor's memory at the given +.Fa address +into the supplied +.Fa buffer . +It returns the size (in bytes) of the instruction. (In other words, +the amount by which +.Fa address +should be incremented to arrive at the next instruction.) +Typical output resembles: +.Bd -literal -offset indent +1009 cpx #5B +.Ed +.Pp +(The +.Fa buffer +arguments are oversized to allow for future expansion.) +.Pp +.Fn M6502_delete +frees the resources associated with the given +.Fa mpu. +Any members that were allocated implicitly (passed as NULL to +.Fn M6502_new ) +are deallocated. Members that were initialised from non-NULL +arguments are not deallocated. +.\" ---------------------------------------------------------------- +.Sh IMPLEMENTATION NOTES +.\" +You can share the +.Fa memory +and +.Fa callbacks +members of +.Vt M6502 +between multiple instances to simulate multiprocessor hardware. +.\" ---------------------------------------------------------------- +.Sh RETURN VALUES +.\" +.Fn M6502_new +returns a pointer to a +.Vt M6502 +structure. +.Fn M6502_getVector +and +.Fn M6502_setVector +return the contents of the given +.Fa vector . +.Fn M6502_getCallback +and +.Fn M6502_setCallback +return the +.Vt M6502_Callback +function associated with the given +.Fa address +and access +.Fa type . +.Fn M6502_disassemble +returns the size (in bytes) of the instruction at the given +.Fa address . +.Fn M6502_reset , +.Fn M6502_nmi , +.Fn M6502_irq , +.Fn M6502_run , +.Fn M6502_dump +and +.Fn M6502_delete +don't return anything (unless you forgot to include +.In lib6502.h ) . +.\" ---------------------------------------------------------------- +.Sh EXAMPLES +.\" +The following program creates a 6502 processor, sets up callbacks for +printing characters and halting after a BRK instruction, stores a +program into memory that prints the alphabet, disassembles the program +on stdout, and then executes the program. +.Bd -literal -offset indent -compact + +#include +#include +#include + +#include "lib6502.h" + +#define WRCH 0xFFEE + +int wrch(M6502 *mpu, uint16_t address, uint8_t data) +{ + int pc; + putchar(mpu->registers->a); + pc = mpu->memory[++mpu->registers->s + 0x100]; + pc |= mpu->memory[++mpu->registers->s + 0x100] << 8; + return pc + 1; /* JSR pushes next insn addr - 1 */ +} + +int done(M6502 *mpu, uint16_t address, uint8_t data) +{ + char buffer[64]; + M6502_dump(mpu, buffer); + printf("\\nBRK instruction\\n%s\\n", buffer); + exit(0); +} + +int main(int argc, char **argv) +{ + M6502 *mpu = M6502_new(0, 0, 0); + unsigned pc = 0x1000; + + mpu->callbacks->call[WRCH] = wrch; /* write character */ + mpu->callbacks->call[0000] = done; /* reached after BRK */ + +# define gen1(X) (mpu->memory[pc++] = (uint8_t)(X)) +# define gen2(X,Y) gen1(X); gen1(Y) +# define gen3(X,Y,Z) gen1(X); gen2(Y,Z) + + gen2(0xA2, 'A' ); /* LDX #'A' */ + gen1(0x8A ); /* TXA */ + gen3(0x20,0xEE,0xFF); /* JSR FFEE */ + gen1(0xE8 ); /* INX */ + gen2(0xE0, 'Z'+1 ); /* CPX #'Z'+1 */ + gen2(0xD0, -9 ); /* BNE 1002 */ + gen2(0xA9, '\\n' ); /* LDA #'\\n' */ + gen3(0x20,0xEE,0xFF); /* JSR FFEE */ + gen2(0x00,0x00 ); /* BRK */ + + { + uint16_t ip = 0x1000; + while (ip < pc) + { + char insn[64]; + ip += M6502_disassemble(mpu, ip, insn); + printf("%04X %s\\n", ip, insn); + } + } + + M6502_setVector(mpu, RST, 0x1000); + + M6502_reset(mpu); + M6502_run(mpu); + M6502_delete(mpu); + + return 0; +} +.Ed +.\" ---------------------------------------------------------------- +.Sh DIAGNOSTICS +.\" +If +.Fn M6502_new +cannot allocate sufficient memory it prints "out of memory" to stderr +and exits with a non-zero status. +.Pp +If +.Fn M6502_run +encounters an illegal or undefined instruction, it prints "undefined +instruction" and the processor's state to stderr, then exits with a +non-zero status. +.\" ---------------------------------------------------------------- +.Sh COMPATIBILITY +.\" +M6502 is a generic name. The initial letter is mandated by C naming +conventions and chosen in deference to MOS Technology, the original +designers of the processor. To the best of my knowledge the 'M' +prefix was never stamped on a physical 6502. +.Pp +The emulator implements the CMOS version of the processor (NMOS bugs +in effective address calculations involving page boundaries are +corrected) but does not tolerate the execution of undefined +instructions (which were all no-ops in the first-generation CMOS +hardware). It would be nice to support the several alternative +instruction sets (model-specific undocumented instructions in NMOS +models, and various documented extensions in the later CMOS models) +but there are currently no plans to do so. +.Pp +The emulated 6502 will run much faster than real hardware on any +modern computer. The fastest 6502 hardware available at the time of +writing has a clock speed of 14 MHz. On a 2 GHz PowerPC, the emulated +6502 runs at almost 300 MHz. +.\" ---------------------------------------------------------------- +.Sh SEE ALSO +.\" +.Xr run6502 1 +.Pp +For development tools, documentation and source code: +.Pa http://6502.org +.\" ---------------------------------------------------------------- +.Sh AUTHORS +.\" +The software and manual pages were written by Ian Piumarta. +.Pp +The software is provided as-is, with absolutely no warranty, in the +hope that you will enjoy and benefit from it. You may use (entirely +at your own risk) and redistribute it under the terms of a very +liberal license that does not seek to restrict your rights in any way +(unlike certain so-called 'open source' licenses that significantly +limit your freedom in the name of 'free' software that is, ultimately, +anything but free). See the file COPYING for details. +.\" ---------------------------------------------------------------- +.Sh BUGS +.\" +.Fn M6502_getVector +and +.Fn M6502_setVector +evaluate their arguments more than once. +.Pp +The out-of-memory condition and attempted execution of +illegal/undefined instructions should not be fatal errors. +.Pp +There is no way to limit the duration of execution within +.Fn M6502_run +to a certain number of instructions or cycles. +.Pp +The emulator should support some means of implicit interrupt +generation, either by polling or in response to (Unix) signals. +.Pp +The +.Sx COMPATIBILITY +section in this manual page has been diverted from its legitimate +purpose. +.Pp +The plural of 'callback' really aught to be 'callsback'. +.Pp +Please send bug reports (and feature requests) to the author at: +firstName (at) lastName (dot) com. (See +.Sx AUTHORS +above for suitable values of firstName and lastName.) diff --git a/src/lib6502/man/run6502.1 b/src/lib6502/man/run6502.1 new file mode 100644 index 0000000..d11bf18 --- /dev/null +++ b/src/lib6502/man/run6502.1 @@ -0,0 +1,380 @@ +.\" Copyright (c) 2005 Ian Piumarta +.\" +.\" 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:22 by piumarta on margaux.local +.\" +.Dd October 31, 2005 +.Dt RUN6502 1 LOCAL +.Os "" +.\" ---------------------------------------------------------------- +.Sh NAME +.\" +.Nm run6502 +.Nd execute a 6502 microprocessor program +.\" ---------------------------------------------------------------- +.Sh SYNOPSIS +.\" +.Nm run6502 +.Op Ar option ... +.Nm run6502 +.Op Ar option ... +.Fl B +.Op Ar +.\" ---------------------------------------------------------------- +.Sh DESCRIPTION +The +.Nm run6502 +command emulates the execution of a 6502 microprocessor. It creates a +memory image from the contents of one or more files on the command +line and then simulates a power-on hardware reset to begin execution. +.Pp +In its first form, +.Nm run6502 +emulates an embedded 6502 processor with 64 kilobytes of RAM, no +memory-mapped hardware, and no input-output capabilities. Limited +interaction with the machine is possible only through the +.Fl G , M +and +.Fl P +options. +.Pp +In its second form (with the +.Fl B +option) +.Nm run6502 +provides minimal emulation of Acorn 'BBC Model B' hardware with 32 +kilobytes of RAM, 16 kilobytes of paged language ROMs, and 16 +kilobytes of operating system ROM. A few MOS calls are intercepted to +provide keyboard input and screen output via stdin and stdout. +Switching between the sixteen paged read-only memory banks is also +supported by the usual memory-mapped control register. Any +.Ar file +arguments after the +.Fl B +are loaded into successive paged ROM banks (starting at 15 and working +down towards 0) before execution begins. +.\" ---------------------------------------------------------------- +.Ss Options +.\" +.Bl -tag -width indent +.It Fl B +enable minimal Acorn 'BBC Model B' hardware emulation: +.Bl -bullet +.It +the contents of memory between addresses 0x8000 and 0xBFFF are copied +into paged ROM number 0; +.It +memory between 0x8000 and 0xBFFF becomes bank-switchable between +sixteen different ROM images; +.It +the memory-mapped pages ('FRED', 'JIM' and 'SHEILA') between 0xFC00 +and 0xFEFF are initialised to harmless values; +.It +the upper half of the address space is write-protected; and +.It +callbacks are installed on several OS entry points to provide +input-output via stdin and stdout. +.El +.Pp +Any remaining non-option arguments on the command line will name files +to be loaded successively into paged ROMs, starting at 15 and working +downwards towards 0. +.It Fl d Ar addr Ar end +dump memory from the address +.Ar addr +(given in hexadecimal) up to (but not including) +.Ar end . +The +.Ar end +argument is either an absolute address or a relative address specified +as a '+' character followed by the number (in hexadecimal) of bytes to +dump. In other words, the following two options dump the same region +of memory: +.Bd -ragged -offset indent +.Fl d +8000 C000 +.Ed +.Bd -ragged -offset indent -compact +.Fl d +8000 +4000 +.Ed +.Pp +The format of the dump cannot currently be modified and consists of +the current address followed by one, two or three hexadecimal bytes, +and a symbolic representation of the instruction at that address. +.It Fl G Ar addr +arrange that subroutine calls to +.Ar addr +will behave as if there were an implementation of +.Xr getchar 3 +at that address, reading a character from stdin and returning it in +the accumulator. +.It Fl h +print a summary of the available options and then exit. +.It Fl I Ar addr +set the IRQ (interrupt request) vector (the address to which the +processor will transfer control upon execution of a BRK instruction). +Setting this address to zero will cause execution to halt (and the +emulator to exit) when a BRK instruction is encountered. +.It Fl i Ar addr Ar file +Load +.Ar file +into the memory image at the address +.Ar addr +(in hexadecimal), skipping over any initial '#!' interpreter line. +.It Fl l Ar addr Ar file +Load +.Ar file +into the memory image at the address +.Ar addr +(in hexadecimal). +.It Fl M Ar addrio +arrange that memory reads from address +.Ar addrio +will return the next character on stdin (blocking if necessary), and +memory writes to +.Ar addrio +will send the value written to stdout. +.It Fl N Ar addr +set the NMI (non-maskable interrupt) vector to +.Ar addr . +.It Fl P Ar addr +arrange that subroutine calls to +.Ar addr +will behave as if there were an implementation of +.Xr putchar 3 +at that address, writing the contents of the accumulator to stdout. +.It Fl R Ar addr +set the RST (hardware reset) vector. The processor will transfer +control to this address when emulated execution begins. +.It Fl s Ar addr Ar end Ar file +save the contents of memory from the address +.Ar addr +up to +.Ar end +(exclusive) to the given +.Ar file . +As with the +.Fl d +option, +.Ar end +can be absolute or '+' followed by a byte count. +.It Fl v +print version information and then exit. +.It Fl X Ar addr +arrange that any transfer of control to the address +.Ar addr +will cause an immediate exit with zero exit status. +.It Fl x +exit immediately. (Useful after +.Fl d +or when +.Nm run6502 +is being used as a trivial 'image editor', with several +.Fl l +options followed by +.Fl s +and +.Fl x . ) +.It Ar +following a +.Fl B +option, load one or more ROM image +files +into successive paged ROM slots. Other than the paging aspect, this +is equivalent to: +.Bd -ragged -offset indent +.Fl l Ar 8000 Ar image +.Ed +.El +.\" ---------------------------------------------------------------- +.Sh EXAMPLES +.\" +.Ss A Very Simple Program +The +.Xr perl 1 +command can be used to create a binary file from hexadecimal input: +.Bd -literal + echo a2418a20eeffe8e05bd0f7a90a20eeff00 | + perl -e 'print pack "H*",' > temp.img +.Ed +.Pp +The file can be loaded and executed with: +.Bd -literal + run6502 -l 1000 temp.img -R 1000 -P FFEE -X 0 +.Ed +.Pp +The contents of the file can be inspected symbolically with: +.Bd -literal + run6502 -l 1000 temp.img -d 1000 +12 +.Ed +.Pp +The options passed to +.Nm run6502 +in the above examples have the following effects: +.Bl -tag -width offset +.It \-l 1000 temp.img +loads the file +.Pa temp.img +into memory at address 0x8000. +.It \-R 1000 +sets the reset vector (the address of first instruction to be executed +after 'power on') to 0x1000. +.It \-P FFEE +arranges for calls to address 0xFFEE to behave as if there were an +implementation of +.Xr putchar 3 +at that address. +.It \-X 0 +arranges for transfers of control to address 0 to exit from the +emulator. This works in the above example because the final 'BRK' +instruction causes an implicit subroutine call through an +uninitialised interrupt vector to location 0. To see this +instruction... +.It \-d 1000 +12 +disassembles 18 bytes of memory at address 0x8000. +.El +.Ss Standalone Images +The +.Fl i +option is designed for use in the 'interpreter command' appearing on +the first line of an executable script. Adding the line +.Bd -literal + #!run6502 -R 1000 -P FFEE -X 0 -i 1000 +.Ed +.Pp +(with no leading spaces and a single trailing newline character) +to the +.Pa temp.img +file from the first example turns it into a script. If the file is +made executable with +.Bd -literal + chmod +x temp.img +.Ed +.Pp +it can be run like a standalone program: +.Bd -literal + ./temp.img +.Ed +.Ss A Very Complex Program +Consider a pair of files named +.Pa os1.2 +and +.Pa basic2 +containing (legally-acquired, of course) ROM images of Acorn MOS 1.2 +and BBC Basic 2. The following command loads each of the images into +memory at the appropriate address, cleans up the regions of memory +containing memory-mapped i/o on the BBC computer, saves a snapshot of +the entire memory to the file +.Pa image +and then exits: +.Bd -literal + run6502 -l C000 os1.2 -l 8000 basic2 -B -s0 +10000 image -x +.Ed +.Pp +Running the generated image with +.Bd -literal + run6502 image +.Ed +.Pp +will cold-start the emulated hardware, run the OS for a while, and +then drop into the language ROM. Basic programs can then be entered, +edited and run from the terminal. +.Pp +More details are given in the +.Pa README +file available in the +.Pa examples +directory of the distribution. +.Ss Exercises +Create a standalone image (one that can be run as a program, with +a '#!' interpreter line at the beginning) that contains Basic2 and +OS1.2 (as described above). This image should be no larger than 32K +(memory below 0x8000, which would be full of zeroes, should not appear +in the image file). +.\" ---------------------------------------------------------------- +.Sh DIAGNOSTICS +.\" +If nothing goes wrong, none. Otherwise lots. They should be +self-explanatory. I'm too lazy to enumerate them. +.\" ---------------------------------------------------------------- +.Sh COMPATIBILITY +.\" +See +.Xr lib6502 3 +for a discussion of the emulated instruction set. +.\" ---------------------------------------------------------------- +.Sh SEE ALSO +.\" +.Xr lib6502 3 +.Pp +The file +.Pa examples/README +in the lib6502 distribution. (Depending on your system this may be +installed in +.Pa /usr/doc/lib6502 , +.Pa /usr/local/doc/lib6502 , +.Pa /usr/share/doc/lib6502 , +or similar.) +.Pp +.Pa http://piumarta.com/software/lib6502 +for updates and documentation. +.Pp +.Pa http://6502.org +for lots of 6502-related resources. +.\" ---------------------------------------------------------------- +.Sh AUTHORS +.\" +The software and manual pages were written by +.An "Ian Piumarta" . +.Pp +The software is provided as-is, with absolutely no warranty, in the +hope that you will enjoy and benefit from it. You may use (entirely +at your own risk) and redistribute it under the terms of a very +liberal license that does not seek to restrict your rights in any way +(unlike certain so-called 'open source' licenses that significantly +limit your freedom in the name of 'free' software that is, ultimately, +anything but free). See the file COPYING for details. +.\" ---------------------------------------------------------------- +.Sh BUGS +.\" +.Bl -bullet +.It +Options must appear one at a time. +.It +Any attempt (in a load or save operation) to transfer data beyond +0xFFFF is silently truncated at the end of memory. +.It +There is no way to specify the slot into which a ROM image should be +loaded, other than implicitly according to the order of arguments on +the command line. +.It +Execution can only be started via the emulated power-up reset. There +is no support for 'warm-starting' execution in an image at an +arbitrary address. +.It +Even though the emulator fully supports them, there is no way to +artificially generate a hardware interrupt request, non-maskable +interrupt, or reset condition. If you need these, read +.Xr lib6502 3 +and write your own shell. +.It +The Acorn 'BBC Model B' hardware emulation is totally lame. +.El +.Pp +Please send bug reports (and feature requests) to the author at: +firstName (at) lastName (dot) com. (See +.Sx AUTHORS +above for suitable values of firstName and lastName.) diff --git a/src/lib6502/run6502.c b/src/lib6502/run6502.c new file mode 100644 index 0000000..1aa373c --- /dev/null +++ b/src/lib6502/run6502.c @@ -0,0 +1,520 @@ +/* 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 +#include +#include +#include +#include + +#include "config.h" +#include "lib6502.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 -- dump memory between addr and last\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 -- 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, "w"))) + return 0; + while ((count= fwrite(mpu->memory + address, 1, length, file))) + { + address += count; + length -= count; + } + fclose(file); + return 1; +} + + +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"))) + return 0; + while ((count= fread(mpu->memory + address, 1, max, file)) > 0) + { + address += count; + max -= count; + } + fclose(file); + return 1; +} + + +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 gTrap(M6502 *mpu, word addr, byte data) { mpu->registers->a= getchar(); rts; } +static int pTrap(M6502 *mpu, word addr, byte data) { putchar(mpu->registers->a); 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; +} + +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; +} + + +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; +} + + +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; +} + + +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, "-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= 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); + + M6502_reset(mpu); + M6502_run(mpu); + M6502_delete(mpu); + + return 0; +}