├── .gitignore ├── BSDmakefile ├── COPYING ├── ChangeLog ├── Makefile ├── README ├── config.h ├── examples ├── README ├── hex2bin └── lib1.c ├── lib6502.c ├── lib6502.h ├── lib6502.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── man ├── M6502_delete.3 ├── M6502_disassemble.3 ├── M6502_dump.3 ├── M6502_getCallback.3 ├── M6502_getVector.3 ├── M6502_irq.3 ├── M6502_new.3 ├── M6502_nmi.3 ├── M6502_reset.3 ├── M6502_run.3 ├── M6502_setCallback.3 ├── M6502_setVector.3 ├── lib6502.3 └── run6502.1 ├── run6502.c └── test.out /.gitignore: -------------------------------------------------------------------------------- 1 | run6502 2 | 3 | # Object files 4 | *.o 5 | *.ko 6 | *.obj 7 | *.elf 8 | 9 | # Precompiled Headers 10 | *.gch 11 | *.pch 12 | 13 | # Libraries 14 | *.lib 15 | *.a 16 | *.la 17 | *.lo 18 | 19 | # Shared objects (inc. Windows DLLs) 20 | *.dll 21 | *.so 22 | *.so.* 23 | *.dylib 24 | 25 | # Executables 26 | *.exe 27 | *.out 28 | *.app 29 | *.i*86 30 | *.x86_64 31 | *.hex 32 | 33 | # Debug files 34 | *.dSYM/ 35 | # Xcode 36 | # 37 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 38 | 39 | ## Build generated 40 | build/ 41 | DerivedData/ 42 | 43 | ## Various settings 44 | *.pbxuser 45 | !default.pbxuser 46 | *.mode1v3 47 | !default.mode1v3 48 | *.mode2v3 49 | !default.mode2v3 50 | *.perspectivev3 51 | !default.perspectivev3 52 | xcuserdata/ 53 | 54 | ## Other 55 | *.moved-aside 56 | *.xcuserstate 57 | 58 | ## Obj-C/Swift specific 59 | *.hmap 60 | *.ipa 61 | *.dSYM.zip 62 | *.dSYM 63 | 64 | # CocoaPods 65 | # 66 | # We recommend against adding the Pods directory to your .gitignore. However 67 | # you should judge for yourself, the pros and cons are mentioned at: 68 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 69 | # 70 | # Pods/ 71 | 72 | # Carthage 73 | # 74 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 75 | # Carthage/Checkouts 76 | 77 | Carthage/Build 78 | 79 | # fastlane 80 | # 81 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 82 | # screenshots whenever they are needed. 83 | # For more information about the recommended setup visit: 84 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 85 | 86 | fastlane/report.xml 87 | fastlane/screenshots 88 | 89 | #Code Injection 90 | # 91 | # After new code Injection tools there's a generated folder /iOSInjectionProject 92 | # https://github.com/johnno1962/injectionforxcode 93 | 94 | iOSInjectionProject/ 95 | -------------------------------------------------------------------------------- /BSDmakefile: -------------------------------------------------------------------------------- 1 | # THIS FILE WAS GENERATED AUTOMATICALLY 2 | # EDIT AT YOUR OWN RISK 3 | # 4 | # Makefile for lib6502, run6502 5 | 6 | # Copyright (c) 2005 Ian Piumarta 7 | # 8 | # Permission is hereby granted, free of charge, to any person obtaining a copy 9 | # of this software and associated documentation files (the "Software"), to 10 | # deal in the Software without restriction, including without limitation the 11 | # rights to use, copy, modify, merge, publish, distribute, sub-license, and/or 12 | # sell copies of the Software, and to permit persons to whom the Software is 13 | # furnished to do so, subject to the following conditions: 14 | # 15 | # The above copyright notice and this permission notice shall be included in 16 | # all copies or substantial portions of the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED "AS IS". USE ENTIRELY AT YOUR OWN RISK. 19 | 20 | # last edited: 2005-11-01 22:48:49 by piumarta on margaux.local 21 | 22 | CFLAGS = -g -O3 23 | 24 | PREFIX = /usr/local 25 | BINDIR = $(PREFIX)/bin 26 | LIBDIR = $(PREFIX)/lib 27 | INCDIR = $(PREFIX)/include 28 | DOCDIR = $(PREFIX)/doc/lib6502 29 | EGSDIR = $(DOCDIR)/examples 30 | MANDIR = $(PREFIX)/man 31 | MAN1DIR = $(MANDIR)/man1 32 | MAN3DIR = $(MANDIR)/man3 33 | 34 | all : run6502 35 | 36 | run6502 : run6502.o lib6502.a 37 | 38 | lib6502.a : lib6502.o 39 | $(AR) -rc $@.new lib6502.o 40 | mv $@.new $@ 41 | -ranlib $@ 42 | 43 | clean : .FORCE 44 | rm -f run6502 lib1 *~ *.o *.a .gdb* *.img *.log 45 | 46 | .FORCE : 47 | 48 | install : .FORCE 49 | install -d $(BINDIR) 50 | install -d $(LIBDIR) 51 | install -d $(INCDIR) 52 | install -d $(MANDIR) 53 | install -d $(MAN1DIR) 54 | install -d $(MAN3DIR) 55 | install -d $(DOCDIR) 56 | install -d $(EGSDIR) 57 | install -c run6502 $(BINDIR)/run6502 58 | install -c lib6502.a $(LIBDIR)/lib6502.a 59 | install -c lib6502.h $(INCDIR)/lib6502.h 60 | install -c man/run6502.1 $(MAN1DIR)/run6502.1 61 | install -c man/lib6502.3 $(MAN3DIR)/lib6502.3 62 | install -c man/M6502_delete.3 $(MAN3DIR)/M6502_delete.3 63 | install -c man/M6502_disassemble.3 $(MAN3DIR)/M6502_disassemble.3 64 | install -c man/M6502_dump.3 $(MAN3DIR)/M6502_dump.3 65 | install -c man/M6502_getCallback.3 $(MAN3DIR)/M6502_getCallback.3 66 | install -c man/M6502_getVector.3 $(MAN3DIR)/M6502_getVector.3 67 | install -c man/M6502_irq.3 $(MAN3DIR)/M6502_irq.3 68 | install -c man/M6502_new.3 $(MAN3DIR)/M6502_new.3 69 | install -c man/M6502_nmi.3 $(MAN3DIR)/M6502_nmi.3 70 | install -c man/M6502_reset.3 $(MAN3DIR)/M6502_reset.3 71 | install -c man/M6502_run.3 $(MAN3DIR)/M6502_run.3 72 | install -c man/M6502_setCallback.3 $(MAN3DIR)/M6502_setCallback.3 73 | install -c man/M6502_setVector.3 $(MAN3DIR)/M6502_setVector.3 74 | install -c ChangeLog $(DOCDIR)/ChangeLog 75 | install -c COPYING $(DOCDIR)/COPYING 76 | install -c README $(DOCDIR)/README 77 | install -c examples/README $(EGSDIR)/README 78 | install -c examples/lib1.c $(EGSDIR)/lib1.c 79 | install -c examples/hex2bin $(EGSDIR)/hex2bin 80 | 81 | uninstall : .FORCE 82 | 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 83 | rmdir $(EGSDIR) $(DOCDIR) 84 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Distasteful though it is for me to have to induce from afar any perturbation 2 | into your pursuit of happiness, this MIT (X11 flavour) license is at least 3 | relatively benign. Investigation into copyright stupidity reveals that it is 4 | effectively impossible to dedicate (formally) any software to the public 5 | domain (the only sure path to this most enlightened status being to leave the 6 | software to expire naturally from its 25-, 50-, 75- or whatever-year copyright 7 | rot). I fear this is not going to change before the revolution comes. In the 8 | meantime the only way I can *guarantee* you any rights at all to this software 9 | would (unfortunately) appear to be... 10 | 11 | Copyright (c) 2005 Ian Piumarta 12 | 13 | All rights reserved. 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining a copy 16 | of this software and associated documentation files (the 'Software'), to 17 | deal in the Software without restriction, including without limitation the 18 | rights to use, copy, modify, merge, publish, distribute, and/or sell copies 19 | of the Software, and to permit persons to whom the Software is furnished to 20 | do so, provided that the above copyright notice(s) and this permission 21 | notice appear in all copies or substantial portions of the Software. 22 | 23 | Inclusion of the above copyright notice(s) and this permission notice in 24 | supporting documentation would be appreciated, but is not required. 25 | 26 | THE SOFTWARE IS PROVIDED 'AS IS'. USE ENTIRELY AT YOUR OWN RISK. 27 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | 2005-11-01 Ian Piumarta 2 | 3 | * RELEASE 1.0 4 | 5 | 2005-10-30 Ian Piumarta 6 | 7 | * ChangeLog: created. 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for lib6502, run6502 2 | 3 | # Copyright (c) 2005 Ian Piumarta 4 | # 5 | # All rights reserved. 6 | # 7 | # Permission is hereby granted, free of charge, to any person 8 | # obtaining a copy of this software and associated documentation files 9 | # (the 'Software'), to deal in the Software without restriction, 10 | # including without limitation the rights to use, copy, modify, merge, 11 | # publish, distribute, and/or sell copies of the Software, and to 12 | # permit persons to whom the Software is furnished to do so, provided 13 | # that the above copyright notice(s) and this permission notice appear 14 | # in all copies of the Software and that both the above copyright 15 | # notice(s) and this permission notice appear in supporting 16 | # documentation. 17 | # 18 | # THE SOFTWARE IS PROVIDED 'AS IS'. USE ENTIRELY AT YOUR OWN RISK. 19 | 20 | # last edited: 2013-06-08 01:08:02 by piumarta on vps2.piumarta.com 21 | 22 | CFLAGS = -g -O3 23 | 24 | PREFIX = /usr/local 25 | BINDIR = $(PREFIX)/bin 26 | LIBDIR = $(PREFIX)/lib 27 | INCDIR = $(PREFIX)/include 28 | DOCDIR = $(PREFIX)/doc/lib6502 29 | EGSDIR = $(DOCDIR)/examples 30 | MANDIR = $(PREFIX)/man 31 | MAN1DIR = $(MANDIR)/man1 32 | MAN3DIR = $(MANDIR)/man3 33 | 34 | all : run6502 35 | 36 | run6502 : run6502.o lib6502.a 37 | 38 | lib6502.a : lib6502.o 39 | $(AR) -rc $@.new lib6502.o 40 | mv $@.new $@ 41 | -ranlib $@ 42 | 43 | clean : .FORCE 44 | rm -f run6502 lib1 *~ *.o *.a .gdb* *.img *.log 45 | 46 | .FORCE : 47 | 48 | # ---------------------------------------------------------------- 49 | 50 | INSTALLDIRS = $(BINDIR) $(LIBDIR) $(INCDIR) $(MANDIR) $(MAN1DIR) $(MAN3DIR) $(DOCDIR) $(EGSDIR) 51 | 52 | BINFILES = $(BINDIR)/run6502 53 | 54 | LIBFILES = $(LIBDIR)/lib6502.a 55 | 56 | INCFILES = $(INCDIR)/lib6502.h 57 | 58 | MANFILES = $(MAN1DIR)/run6502.1 \ 59 | $(MAN3DIR)/lib6502.3 \ 60 | $(MAN3DIR)/M6502_delete.3 \ 61 | $(MAN3DIR)/M6502_disassemble.3 \ 62 | $(MAN3DIR)/M6502_dump.3 \ 63 | $(MAN3DIR)/M6502_getCallback.3 \ 64 | $(MAN3DIR)/M6502_getVector.3 \ 65 | $(MAN3DIR)/M6502_irq.3 \ 66 | $(MAN3DIR)/M6502_new.3 \ 67 | $(MAN3DIR)/M6502_nmi.3 \ 68 | $(MAN3DIR)/M6502_reset.3 \ 69 | $(MAN3DIR)/M6502_run.3 \ 70 | $(MAN3DIR)/M6502_setCallback.3 \ 71 | $(MAN3DIR)/M6502_setVector.3 72 | 73 | DOCFILES = $(DOCDIR)/ChangeLog \ 74 | $(DOCDIR)/COPYING \ 75 | $(DOCDIR)/README \ 76 | $(EGSDIR)/README \ 77 | $(EGSDIR)/lib1.c \ 78 | $(EGSDIR)/hex2bin 79 | 80 | MKDIR = install -d 81 | RMDIR = rmdir 82 | INSTALL = install -c 83 | RM = rm -f 84 | 85 | $(BINDIR)/% $(LIBDIR)/% $(INCDIR)/% $(DOCDIR)/% : % 86 | $(INSTALL) $< $@ 87 | 88 | $(MAN1DIR)/% $(MAN3DIR)/% : man/% 89 | $(INSTALL) $< $@ 90 | 91 | $(EGSDIR)/% : examples/% 92 | $(INSTALL) $< $@ 93 | 94 | $(INSTALLDIRS) : 95 | $(MKDIR) $@ 96 | 97 | install : $(INSTALLDIRS) $(BINFILES) $(LIBFILES) $(INCFILES) $(MANFILES) $(DOCFILES) 98 | 99 | uninstall : .FORCE 100 | -$(RM) $(BINFILES) $(LIBFILES) $(INCFILES) $(MANFILES) $(DOCFILES) 101 | -$(RMDIR) $(EGSDIR) $(DOCDIR) 102 | 103 | # ---------------------------------------------------------------- 104 | 105 | PACKAGE_VERSION = 1.3 106 | PACKAGE_TARNAME = lib6502 107 | 108 | TARNAME= $(PACKAGE_TARNAME)-$(PACKAGE_VERSION) 109 | 110 | DISTFILES = \ 111 | $(TARNAME)/ChangeLog \ 112 | $(TARNAME)/COPYING \ 113 | $(TARNAME)/README \ 114 | $(TARNAME)/Makefile \ 115 | $(TARNAME)/BSDmakefile \ 116 | $(TARNAME)/config.h \ 117 | $(TARNAME)/lib6502.h \ 118 | $(TARNAME)/lib6502.c \ 119 | $(TARNAME)/run6502.c \ 120 | $(TARNAME)/test.out \ 121 | $(TARNAME)/man/run6502.1 \ 122 | $(TARNAME)/man/lib6502.3 \ 123 | $(TARNAME)/man/M6502_delete.3 \ 124 | $(TARNAME)/man/M6502_disassemble.3 \ 125 | $(TARNAME)/man/M6502_dump.3 \ 126 | $(TARNAME)/man/M6502_getCallback.3 \ 127 | $(TARNAME)/man/M6502_getVector.3 \ 128 | $(TARNAME)/man/M6502_irq.3 \ 129 | $(TARNAME)/man/M6502_new.3 \ 130 | $(TARNAME)/man/M6502_nmi.3 \ 131 | $(TARNAME)/man/M6502_reset.3 \ 132 | $(TARNAME)/man/M6502_run.3 \ 133 | $(TARNAME)/man/M6502_setCallback.3 \ 134 | $(TARNAME)/man/M6502_setVector.3 \ 135 | $(TARNAME)/examples/hex2bin \ 136 | $(TARNAME)/examples/lib1.c \ 137 | $(TARNAME)/examples/README 138 | 139 | dist : .FORCE 140 | rm -f $(TARNAME) 141 | ln -s . $(TARNAME) 142 | tar -cf $(TARNAME).tar $(DISTFILES) 143 | gzip -v9 $(TARNAME).tar 144 | rm -f $(TARNAME) 145 | 146 | dist-test : .FORCE 147 | rm -rf $(TARNAME) 148 | tar -xz -f $(TARNAME).tar.gz 149 | ln -s ../images $(TARNAME)/images 150 | $(MAKE) -C $(TARNAME) test 151 | rm -rf $(TARNAME) 152 | 153 | # ---------------------------------------------------------------- 154 | 155 | image : 156 | ./run6502 \ 157 | -l C000 images/os1.2 \ 158 | -l 8000 images/basic2 \ 159 | -s 0000 +10000 image \ 160 | -x 161 | 162 | newimage : .FORCE 163 | rm -f image 164 | $(MAKE) image 165 | 166 | test1 : run6502 .FORCE 167 | echo a2418a20eeffe8e05bd0f7a90a20eeff0000 | perl -e '$$_=pack"H*",;print' > temp.img 168 | ./run6502 \ 169 | -l 1000 temp.img \ 170 | -d 1000 +11 \ 171 | -R 1000 \ 172 | -P FFEE \ 173 | -X 0 174 | 175 | lib1 : lib6502.a 176 | $(CC) -I. -o lib1 examples/lib1.c lib6502.a 177 | 178 | test2 : lib1 .FORCE 179 | ./lib1 180 | 181 | test3 : run6502 image .FORCE 182 | echo 'PRINT:FORA%=1TO10:PRINTA%:NEXT:PRINT"HELLO WORLD"' | ./run6502 image 183 | 184 | test4 : run6502 image .FORCE 185 | 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 186 | 187 | test : run6502 lib1 image .FORCE 188 | @$(MAKE) test1 test2 test3 test4 | grep -v '^make.* directory' | tee test.log 189 | cmp test.log test.out 190 | @echo 191 | @echo SUCCESS 192 | @echo 193 | 194 | # ---------------------------------------------------------------- 195 | 196 | # I don't know what it is (probably me, who knows?) but every single 197 | # time I try to write a Makefile that is compatible with both GNU and 198 | # BSD make I spend three hours getting absolutely nowhere. It's 199 | # telling when a program consisting of precisely TWO SOURCE FILES and 200 | # a few man pages and examples is already TOO COMPLEX to be installed 201 | # with BSD make, in the absence of an explicit rule for every single 202 | # target, be it installed or intermediate, from a Makefile that won't 203 | # also break GNU make. Good Grief Charlie Brown. 204 | 205 | # Yes I know I can compose the sed substitutions into a single script, 206 | # but it looks even uglier that way. 207 | 208 | BSDmakefile : .FORCE 209 | $(MAKE) 210 | rm -rf /tmp/bsd 211 | echo '# THIS FILE WAS GENERATED AUTOMATICALLY' > BSDmakefile 212 | echo '# EDIT AT YOUR OWN RISK' >> BSDmakefile 213 | echo '# ' >> BSDmakefile 214 | sed '/# -/,$$d' < Makefile >> BSDmakefile 215 | echo 'install : .FORCE' > BSDtemp 216 | $(MAKE) install PREFIX=/tmp/bsd >> BSDtemp 217 | echo >> BSDtemp 218 | echo 'uninstall : .FORCE' >> BSDtemp 219 | $(MAKE) uninstall PREFIX=/tmp/bsd >> BSDtemp 220 | cat BSDtemp | \ 221 | sed 's,/tmp/bsd/doc/lib6502/examples,$$(EGSDIR),g' | \ 222 | sed 's,/tmp/bsd/doc/lib6502,$$(DOCDIR),g' | \ 223 | sed 's,/tmp/bsd/man/man1,$$(MAN1DIR),g' | \ 224 | sed 's,/tmp/bsd/man/man3,$$(MAN3DIR),g' | \ 225 | sed 's,/tmp/bsd/man,$$(MANDIR),g' | \ 226 | sed 's,/tmp/bsd/include,$$(INCDIR),g' | \ 227 | sed 's,/tmp/bsd/lib,$$(LIBDIR),g' | \ 228 | sed 's,/tmp/bsd/lib,$$(LIBDIR),g' | \ 229 | sed 's,/tmp/bsd/bin,$$(BINDIR),g' | \ 230 | sed 's,^, ,g' >> BSDmakefile 231 | rm -f BSDtemp 232 | rm -rf /tmp/bsd 233 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | lib6502 - 6502 Microprocessor Emulator 2 | 3 | Version: 1.0 4 | 5 | 6 | WHAT IF I'M TOO LAZY TO READ 'README'S? 7 | 8 | make 9 | make install 10 | more examples/README 11 | 12 | 13 | WHAT IS LIB6502? 14 | 15 | lib6502 is a library that emulates the 6502 microprocessor. It 16 | comes with a small 'shell', run6502, that can execute 6502 programs 17 | from the command line. 18 | 19 | lib6502 is distributed under the MIT license: it is non-infectious 20 | and will not make your projects contagious to others the instant you 21 | choose to use lib6502 in them. See the file COPYING for details. 22 | 23 | 24 | WHERE IS THE LATEST SOURCE CODE? 25 | 26 | Source code for lib6502 is available from the author's home page at 27 | 'http://piumarta.com/software'. You can download the most recent 28 | release or use Subversion to get the very latest sources. 29 | 30 | 31 | WHERE IS THE DOCUMENTATION? 32 | 33 | Manual pages for run6502 and lib6502 (and all the functions it 34 | exports) should be available once it is installed. Each includes a 35 | short 'examples' section. Use the 'man' command to read them. 36 | 37 | Your best place to start looking for documentation on the 6502 38 | itself is 'http://6502.org'. A google search of the web will also 39 | turn up vast quantities of information about (and programs for) the 40 | 6502. 41 | 42 | 43 | HOW DO I INSTALL IT? 44 | 45 | It's not really big enough to warrant the whole 'configure' thing. 46 | Any system with an ANSI compiler and C library should be able to 47 | compile it out of the box. After unpacking the archive, just type: 48 | 49 | make 50 | 51 | to build it. If the compiler blows up immediately, edit the 52 | Makefile and play with the '-g' and '-O' flags and then try again. 53 | If you really can't make the compiler happy you've found a bug (read 54 | the next section but one). Otherwise, if you want it put it 55 | somewhere more permanent then type: 56 | 57 | make install 58 | 59 | (as root) to install it. It goes into /usr/local by default; if you 60 | want it elsewhere then set PREFIX in the make command. For example: 61 | 62 | make install PREFIX=/usr 63 | 64 | will put everything under '/usr'. 65 | 66 | When you get bored with it, go back to the source directory and 67 | type: 68 | 69 | make uninstall 70 | 71 | (with the same PREFIX you specified during the install, if 72 | necessary.) 73 | 74 | 75 | WHAT CAN I DO WITH IT? 76 | 77 | See the file EXAMPLES for some suggestions (all of them polite). 78 | 79 | If that leaves you wanting more, read the source for run6502 -- it 80 | exercises just about every feature in lib6502. 81 | 82 | 83 | HOW DO I REPORT PROBLEMS? 84 | 85 | Send e-mail to the author at: firstName (at) lastName (dot) com 86 | 87 | (For suitable values of firstName and lastName, see the last section 88 | of this file.) 89 | 90 | If you're still confused, contact him at: http://piumarta.com 91 | 92 | 93 | HOW CAN I HELP? 94 | 95 | Use it. Find bugs. Fix bugs. Make it faster. Evangelism: spread 96 | it to as many other projects as possible, especially those that 97 | might be using a slower emulator! Read the manual pages to see 98 | what's considered missing, then add it, then send it in. 99 | 100 | (One thing that would be be really handy, and isn't mentioned in the 101 | manual pages, is a test suite. Figure out how to test every mode in 102 | every instruction with every possible combination of operand values 103 | and condition codes and verify the behaviour is correct. Then write 104 | it down in the form of a program and send it in. If it's a 105 | self-contained program that runs once to completion then we can 106 | probably find some real hardware to test against the test suite.) 107 | 108 | If you know how to write software that emulates peripheral hardware 109 | devices, google up some details on the popular 6502-based 110 | microcomputers (Acorn, Commodore, etc.) and add some serious system 111 | emulation to run6502. Make it all pluggable (think dynamic 112 | libraries over an 'agnostic' core), so we can change machines at the 113 | flip of a (command-line) switch. (The callback mechanism in lib6502 114 | was designed with this kind of 'pluggable hardware emulation' in 115 | mind.) 116 | 117 | 118 | WHO WROTE THIS STUFF, AND WHY? 119 | 120 | lib6502 was written by Ian Piumarta. 121 | 122 | While writing ccg (an entirely different project that creates 123 | runtime assemblers for dynamic code generators) he decided to 124 | include support for an 8-bit microprocessor, just for fun. He chose 125 | the 6502 because it was used in the first computer he owned and 126 | programmed (an Ohio Scientific Superboard II, when he was 14) as 127 | well as the second (an Acorn 'BBC Model B', about four years later). 128 | lib6502 started as a 'glorified switch statement' that ran some 129 | small test programs spewed into memory by ccg, but rapidly got out 130 | of control over the course of a weekend. You're looking at the 131 | result. 132 | -------------------------------------------------------------------------------- /config.h: -------------------------------------------------------------------------------- 1 | #ifndef __config_h 2 | #define __config_h 3 | 4 | #define PACKAGE_NAME "lib6502" 5 | #define PACKAGE_VERSION "1.0" 6 | #define PACKAGE_BUGREPORT "firstName (at) lastName (dot) com" 7 | #define PACKAGE_COPYRIGHT "Copyright (c) 2005 Ian Piumarta" 8 | 9 | #endif /* __config_h */ 10 | -------------------------------------------------------------------------------- /examples/README: -------------------------------------------------------------------------------- 1 | lib6502 - 6502 Microprocessor Emulator 2 | 3 | EXAMPLES 4 | 5 | This file has three sections: 6 | 7 | 1. PROGRAMS that you can compile and run 8 | 2. COMMANDS that you can copy and paste into a terminal 9 | 3. ADVANCED stuff that requires some additional setup 10 | 11 | A few numbered footnotes appear at the end and are referenced in the 12 | text in square brackets [6]. 13 | 14 | ---------------------------------------------------------------- 15 | 16 | 1. PROGRAMS 17 | 18 | (We're going to start in 'serious mode'. Bear with me.) 19 | 20 | The file 'lib1.c' contains the example from the run6502 manual page. 21 | Just compile and run it: 22 | 23 | cc -o lib1 lib1.c 24 | ./lib1 25 | 26 | The file has been commented extensively to explain exactly what is 27 | going on. 28 | 29 | ---------------------------------------------------------------- 30 | 31 | 2. COMMANDS 32 | 33 | (Much more fun: this is the section that appeals to the geek in me.) 34 | 35 | 6502 machine code is pretty straightforward. (Many 6502 programmers 36 | remember a time from their misguided childhood when they could 37 | compose and edit programs directly in hexadecimal using their 'front 38 | panel' monitor program -- the next best thing to programming with a 39 | row of switches and lamps, but I digress and will leave that story 40 | until the pdp11 emulator is ready. ;-) We can use this fact to 41 | generate an entire program without needing an assembler. The 'perl' 42 | program is available on most Unixy (and several other) systems and 43 | makes it easy to create binary files from a string of hex digits. 44 | (There is a program called 'xxd' that's very good at this kind of 45 | thing, but you might not have it.) 46 | 47 | First the program (stolen from lib1.c): 48 | 49 | 1000 ldx #41 A241 50 | 1002 txa 8A 51 | 1003 jsr FFEE 20EEFF 52 | 1006 inx E8 53 | 1007 cpx #5B E05B 54 | 1009 bne 1002 D0F7 55 | 100B lda #0A A90A 56 | 100D jsr FFEE 20EEFF 57 | 1010 brk 00 58 | 59 | In C-like syntax it is equivalent to: 60 | 61 | regX = 'A'; 62 | do { 63 | regA = regX; 64 | putchar(regA); 65 | } while (regX != 'Z' + 1); 66 | putchar('\n'); 67 | 68 | (which by today's standards is a *huge* amount of stuff packed into 69 | just 17 bytes of 'compiled' code -- on a 386 the same program is 70 | around 65 bytes [1], and more like 88 bytes on a 32-bit RISC [2]). 71 | 72 | The column on the right is the machine code in hexadecimal. When 73 | strung out in a line it looks like this: 74 | 75 | A2418A20EEFFE8E05BD0F7A90A20EEFF00 76 | 77 | We can tell perl to 'pack' this hexadecimal string into binary and 78 | save the output in a file: 79 | 80 | echo A2418A20EEFFE8E05BD0F7A90A20EEFF00 | 81 | perl -e 'print pack "H*",' > temp.img 82 | 83 | To check the contents of the file, we can load it into run6502 and 84 | then disassemble it: 85 | 86 | run6502 -l 1000 temp.img -d 1000 +11 -x 87 | 88 | The '-l 1000 temp.img' loads the file into the 6502's memory at 89 | address 0x1000, and the '-d 1000 +11' disassembles 17 bytes (11 in 90 | hex) of code starting at 0x1000. The final '-x' tells run6502 not 91 | to try to execute the code. The output should look just like the 92 | program listing above. 93 | 94 | This is almost all we need to run it; just a few details remain. 95 | 96 | - The emulator doesn't know where to start execution. We need to 97 | set the 'reset' vector to 0x1000 -- the address of the first 98 | instruction in the program. The '-R 1000' option does this. 99 | 100 | - The program calls the 'putchar' function at address 0xFFEE to 101 | send a character to the terminal. run6502 can emulate this for 102 | us, with the '-P FFEE' option. 103 | 104 | - We have to have some way to make the processor stop execution 105 | (there is no 'halt' instruction on the 6502, at least not the 106 | early versions). The trick is in the last instruction 'BRK', 107 | that generates a 'software interrupt' -- eventually jumping to 108 | the addres in the 'interrupt vector'. If we don't set the 109 | interrupt vector explicitly it remains empty (zero) and BRK will 110 | try to transfer control to address 0. The '-X 0' option tells 111 | run6502 to stop executing if/when the program attempts to 112 | transfer control to address 0 -- which it will, when it executes 113 | the 'BRK' instruction with an empty interrupt vector. QED :-) 114 | 115 | Here, then, is the complete command to run our program: 116 | 117 | run6502 -l 1000 temp.img -R 1000 -P FFEE -X 0 118 | 119 | This program is relocatable. You can load it at address 4321 120 | (change both the -l and -R options) and it will work just fine. 121 | 122 | Google for "6502 Reference Card" (with the quotes), grab a pencil 123 | and paper, and you can start writing 6502 programs immediately! (If 124 | you really want to experience what it was like in the late 1970s, 125 | but without the added fun of entering each hex digit one at a time 126 | into a monitor program, simply avoid the temptation ever to look at 127 | your hand-assembled code with the '-d' option. ;-) 128 | 129 | If you really start liking this and want to write longer programs in 130 | text files with the hex split over many lines, you'll need a perl 131 | script that can deal with newlines in the input. Something like 132 | this should do the trick... 133 | 134 | #!/usr/bin/perl 135 | 136 | while () { 137 | chomp; 138 | print pack "H*", $_ 139 | } 140 | 141 | (This script is included in the 'examples' directory, in a file 142 | called 'hex2bin', to save you 15 seconds of copy and paste.) 143 | 144 | Need a fun project? Write a 6502 assembler... in 6502 machine code, 145 | of course! Read in the assembly language text via 'getchar' (see 146 | the '-G' option) and write out the assembled binary via 'putchar' 147 | (the '-P' option, that we've already seen). Soon you'll be able to: 148 | 149 | cat prog.s | 150 | run6502 -l 1000 asm.img -R 1000 -G FFE0 -P FFEE -X 0 > prog.img 151 | 152 | run6502 -l 1000 prog.img -R 1000 -G FFE0 -P FFEE -X 0 153 | 154 | (The first prog.s you write should probably be the assembler itself, 155 | transcribed from the paper copy used to hand-assemble the assembler 156 | binary. This significant milestone can be reached with a 157 | surprisingly simple assembler. After this pivotal moment the 158 | assembler, assembling itself, can very quickly become very 159 | powerful.) 160 | 161 | ---------------------------------------------------------------- 162 | 163 | 3. ADVANCED 164 | 165 | (Official justification: let's run something big and non-trivial. 166 | More likely: a flimsy excuse for a trip down memory lane.) 167 | 168 | The remaining examples assume that you have access to two ROM images 169 | from the Acorn 'BBC Model B' microcomputer: the operating system and 170 | the BASIC language . (Just crawl into the attic, fire up the old 171 | Beeb, '*SAVE' the images into files, and then transfer them to your 172 | Unix box over RS423. Under no circumstances should you google for 173 | 'Acorn BBC B OS ROMs zip', without the quotes. That would be 174 | naughty, and probably illegal -- at least until the glorious day 175 | when the revolution finally comes.) 176 | 177 | After brushing yourself down (the attic is kind of dusty, no?) save 178 | the two ROM images as 'OS12.ROM' and 'BASIC2.ROM'. 179 | 180 | The first thing we can do is use run6502 as an editor to merge the 181 | two ROMs into a single image file: 182 | 183 | run6502 \ 184 | -l C000 OS12.ROM \ 185 | -l 8000 BASIC2.ROM \ 186 | -s 0000 +10000 bbc.img \ 187 | -x 188 | 189 | (This is a single command, with '\' continuation characters joining 190 | the lines into one. Your shell should figure it out if you just 191 | copy and paste.) It leaves a file 'bbc.img' containing both the OS 192 | and BASIC. 193 | 194 | To run this image we need the '-B' option. It enables some minimal, 195 | totally lame, hardware emulation of the BBC computer -- just enough 196 | to boot the 'virtual beeb' into BASIC [3]: 197 | 198 | run6502 -l 0 bbc.img -B 199 | 200 | If all goes well, you should be greeted with a 'beep' and a message 201 | telling you what computer you have (BBC Computer), how much RAM is 202 | available (32K), the language you've been dropped into (BASIC), and 203 | a '>' prompt. Turn on 'CAPS LOCK' (many of us remember those days, 204 | and some of us even used to speak in ALL CAPS) and play: 205 | 206 | PRINT 3+4 207 | 208 | or maybe: 209 | 210 | 10 FOR A%=1 TO 10 211 | 20 PRINT A% 212 | 30 NEXT 213 | LIST 214 | RUN 215 | 216 | or even: 217 | 218 | 10 P%=&2800 219 | 20 O%=P% 220 | 30 [ 221 | 40 opt3 222 | 50 lda #10 223 | 60 jsr &FFEE 224 | 70 ldx #65 225 | 80 .l txa 226 | 90 jsr &FFEE 227 | 100 inx 228 | 110 cpx #91 229 | 120 bne l 230 | 130 lda #10 231 | 140 jmp &FFEE 232 | 150 ] 233 | 160 CALL &2800 234 | LIST 235 | RUN 236 | 237 | (How cool is that? ;-) 238 | 239 | One final thing: there is an option '-i' that works just like '-l' 240 | except that it looks to see if the image file begins with '#!'. If 241 | so, it skips over the first line of the file, up to and including 242 | the first newline. Why? The system call that executes programs on 243 | Unixy systems makes the same check. If the user executes a text 244 | file 'foo' staring with '#!prog ...' then the OS loads and runs 245 | 'prog' instead, passing all the '...'s and the name of the text file 246 | 'foo' as arguments [4]. If you have 'temp.img' left over from from 247 | the second example, open it in a text editor and add a single line 248 | at the beginning that reads: 249 | 250 | #!run6502 -i 1000 251 | 252 | (If 'run6502' is not in your current working directory then you will 253 | have to use the full path to the file: '#!/usr/bin/run6502' or 254 | '#!/usr/local/bin/6502' or whatever. No spaces before the '#'!) 255 | 256 | Now make the image executable: 257 | 258 | chmod +x temp.img 259 | 260 | and then (as if you hadn't already guessed) execute it: 261 | 262 | ./temp.img 263 | 264 | Saves an awful lot of tedious typing. [5] 265 | 266 | Have fun! 267 | 268 | ---------------------------------------------------------------- 269 | 270 | FOOTNOTES 271 | 272 | 273 | [1] Here is the 'alphabet' program, verbatim, compiled (with 274 | optimisation) on a 386. It's 66 bytes long, almost four times 275 | longer than the 6502 version. (If I were more generous I might 276 | consider that fair: 32 bits divided by 8 bits is four.) 277 | 278 | 0: 55 push %ebp 279 | 1: 89 e5 mov %esp,%ebp 280 | 3: 53 push %ebx 281 | 4: 83 ec 14 sub $0x14,%esp 282 | 7: bb 41 00 00 00 mov $0x41,%ebx 283 | c: a1 00 00 00 00 mov 0x0,%eax 284 | 11: 89 44 24 04 mov %eax,0x4(%esp) 285 | 15: 89 1c 24 mov %ebx,(%esp) 286 | 18: e8 fc ff ff ff call 19 287 | 1d: 43 inc %ebx 288 | 1e: 83 fb 5b cmp $0x5b,%ebx 289 | 21: 75 e9 jne c 290 | 23: a1 00 00 00 00 mov 0x0,%eax 291 | 28: 89 44 24 04 mov %eax,0x4(%esp) 292 | 2c: c7 04 24 0a 00 00 00 movl $0xa,(%esp) 293 | 33: e8 fc ff ff ff call 34 294 | 38: b8 00 00 00 00 mov $0x0,%eax 295 | 3d: 83 c4 14 add $0x14,%esp 296 | 40: 5b pop %ebx 297 | 41: 5d pop %ebp 298 | 42: c3 ret 299 | 300 | 301 | [2] Here is the 'alphabet' program, verbatim, compiled (with 302 | optimisation) on a PowerPC. It's 88 bytes long, more than five 303 | times longer than the 6502 version. (I don't care what you say: 304 | Apple Macs rule and mine has oodles of RAM to spare.) 305 | 306 | 00000000 mfspr r0,lr 307 | 00000004 stmw r29,0xfff4(r1) 308 | 00000008 stw r0,0x8(r1) 309 | 0000000c stwu r1,0xffb0(r1) 310 | 00000010 bcl 20,31,0x14 311 | 00000014 mfspr r31,lr 312 | 00000018 li r30,0x41 313 | 0000001c addis r2,r31,ha16(0xa4-0x14) 314 | 00000020 lwz r29,lo16(0xa4-0x14)(r2) 315 | 00000024 or r3,r30,r30 316 | 00000028 addi r4,r29,0x58 317 | 0000002c bl 0x7c ; symbol stub for: _fputc 318 | 00000030 cmpwi cr7,r30,0x5a 319 | 00000034 addi r30,r30,0x1 320 | 00000038 bne cr7,0x24 321 | 0000003c li r3,0xa 322 | 00000040 bl 0x5c ; symbol stub for: _fputc 323 | 00000044 li r3,0x0 324 | 00000048 lwz r0,0x58(r1) 325 | 0000004c addi r1,r1,0x50 326 | 00000050 mtspr lr,r0 327 | 00000054 lmw r29,0xfff4(r1) 328 | 00000058 blr 329 | 330 | 331 | [3] Time to 'fess up with an undocumented 'feature'. We ran our 332 | 'bbc.img' file like this: 333 | 334 | run6502 -l 0 bbc.img -B 335 | 336 | I grew tired of typing all those '-'s and made run6502 check to 337 | see if it was invoked with a single, non-option argument. 338 | Running: 339 | 340 | run6502 bbc.img 341 | 342 | is precisely equivalent to the '-l -B' form above. I don't feel 343 | too guilty about this since the manual page suggests that 344 | providing a single, non-option argument is illegal usage. 345 | 346 | 347 | [4] Okay, that might be a little confusing. Here it is written out in 348 | full. If you have a text file called 'foo' containing 349 | 350 | #!/usr/bin/prog -gobble 351 | blah blah blah 352 | blah blah blah 353 | 354 | that is executable, and then you execute it like a compiled 355 | program 356 | 357 | ./foo 358 | 359 | then the OS will notice the '#!' and run the following command 360 | instead: 361 | 362 | /usr/bin/prog -gobble ./foo 363 | 364 | The '-gobble' tells 'prog' to eat the first line, leaving just the 365 | blah that follows. (The reason for choosing '#!' is that '#' is 366 | the comment character in the standard Unix shell, with the obvious 367 | happy consequences for shell scripts.) 368 | 369 | 370 | [5] We can play the same '#!' game with our 'bbc.img' file. Open it 371 | up and add the line 372 | 373 | #!/usr/local/bin/run6502 -B -l 0 374 | 375 | (or whatever, according to the location of the 'run6502' program), 376 | make it executable 377 | 378 | chmod +x bbc.img 379 | 380 | and execute it: 381 | 382 | ./bbc.img 383 | 384 | To save a whopping 32K of zeros at the beginning of the file, 385 | create the image again with 386 | 387 | run6502 \ 388 | -l C000 OS12.ROM \ 389 | -l 8000 BASIC2.ROM \ 390 | -s 8000 +8000 bbc.img \ 391 | -x 392 | 393 | and run it with 394 | 395 | run6502 -l 0 bbc.img -B 396 | 397 | and, if you like, insert the single line 398 | 399 | #!/usr/local/bin/run6502 -B -l 8000 400 | 401 | at the start of the image file and make it executable: 402 | 403 | ./bbc.img 404 | 405 | 406 | [6] There is no footnote 6. 407 | -------------------------------------------------------------------------------- /examples/hex2bin: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | while () { 4 | chomp; 5 | print pack "H*", $_ 6 | } 7 | -------------------------------------------------------------------------------- /examples/lib1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "lib6502.h" 5 | 6 | /* Emulated OS functions. */ 7 | 8 | #define WRCH 0xFFEE /* Write accumulator to stdout. */ 9 | 10 | /* Write the accumulator to stdout. This function will be invoked 11 | * when the emulated program calls 0xFFEE. 12 | */ 13 | int wrch(M6502 *mpu, uint16_t address, uint8_t data) 14 | { 15 | int pc; 16 | 17 | /* Write the character. 18 | */ 19 | putchar(mpu->registers->a); 20 | 21 | /* We arrived here from a JSR instruction. The stack contains the 22 | * saved PC. Pop it off the stack. 23 | */ 24 | pc = mpu->memory[++mpu->registers->s + 0x100]; 25 | pc |= mpu->memory[++mpu->registers->s + 0x100] << 8; 26 | 27 | /* The JSR instruction pushes the value of PC before it has been 28 | * incremented to point to the instruction after the JSR. Return PC 29 | * + 1 as the address for the next insn. Returning non-zero 30 | * indicates that we handled the 'subroutine' ourselves, and the 31 | * emulator should pretend the original 'JSR' neveer happened at 32 | * all. 33 | */ 34 | return pc + 1; /* JSR pushes next insn addr - 1 */ 35 | } 36 | 37 | 38 | /* Exit gracefully. We arrange for this function to be called when 39 | * the emulator tries to transfer control to address 0. 40 | */ 41 | int done(M6502 *mpu, uint16_t address, uint8_t data) 42 | { 43 | char buffer[64]; 44 | 45 | /* Dump the internal state of the processor. 46 | */ 47 | M6502_dump(mpu, buffer); 48 | 49 | /* Print a cute message and quit. 50 | */ 51 | printf("\nBRK instruction\n%s\n", buffer); 52 | exit(0); 53 | } 54 | 55 | int main() 56 | { 57 | M6502 *mpu = M6502_new(0, 0, 0); /* Make a 6502 */ 58 | unsigned pc = 0x1000; /* PC for 'assembly' */ 59 | 60 | /* Install the two callback functions defined above. 61 | */ 62 | M6502_setCallback(mpu, call, WRCH, wrch); /* Calling FFEE -> wrch() */ 63 | M6502_setCallback(mpu, call, 0, done); /* Calling 0 -> done() */ 64 | 65 | /* A few macros that dump bytes into the 6502's memory. 66 | */ 67 | # define gen1(X) (mpu->memory[pc++]= (uint8_t)(X)) 68 | # define gen2(X,Y) gen1(X); gen1(Y) 69 | # define gen3(X,Y,Z) gen1(X); gen2(Y,Z) 70 | 71 | /* Hand-assemble the program. 72 | */ 73 | gen2(0xA2, 'A' ); // LDX #'A' 74 | gen1(0x8A ); // TXA 75 | gen3(0x20,0xEE,0xFF); // JSR FFEE 76 | gen1(0xE8 ); // INX 77 | gen2(0xE0, 'Z'+1 ); // CPX #'Z'+1 78 | gen2(0xD0, -9 ); // BNE 0x1002 79 | gen2(0xA9, '\n' ); // LDA #'\n' 80 | gen3(0x20,0xEE,0xFF); // JSR FFEE 81 | gen2(0x00,0x00 ); // BRK 82 | 83 | /* Just for fun: disssemble the program. 84 | */ 85 | { 86 | char insn[64]; 87 | uint16_t ip= 0x1000; 88 | while (ip < pc) 89 | { 90 | ip += M6502_disassemble(mpu, ip, insn); 91 | printf("%04X %s\n", ip, insn); 92 | } 93 | } 94 | 95 | /* Point the RESET vector at the first instruction in the assembled 96 | * program. 97 | */ 98 | M6502_setVector(mpu, RST, 0x1000); 99 | 100 | /* Reset the 6502 and run the program. 101 | */ 102 | M6502_reset(mpu); 103 | M6502_run(mpu); 104 | M6502_delete(mpu); /* We never reach here, but what the hey. */ 105 | 106 | return 0; 107 | } 108 | -------------------------------------------------------------------------------- /lib6502.c: -------------------------------------------------------------------------------- 1 | /* lib6502.c -- MOS Technology 6502 emulator -*- C -*- */ 2 | 3 | /* Copyright (c) 2005 Ian Piumarta 4 | * 5 | * All rights reserved. 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a 8 | * copy of this software and associated documentation files (the 'Software'), 9 | * to deal in the Software without restriction, including without limitation 10 | * the rights to use, copy, modify, merge, publish, distribute, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, provided that the above copyright notice(s) and this 13 | * permission notice appear in all copies of the Software and that both the 14 | * above copyright notice(s) and this permission notice appear in supporting 15 | * documentation. 16 | * 17 | * THE SOFTWARE IS PROVIDED 'AS IS'. USE ENTIRELY AT YOUR OWN RISK. 18 | */ 19 | 20 | /* Last edited: 2013-06-07 23:03:39 by piumarta on emilia.local 21 | * 22 | * BUGS: 23 | * - RTS and RTI do not check the return address for a callback 24 | * - the disassembler cannot be configured to read two bytes for BRK 25 | * - architectural variations (unimplemented/extended instructions) not implemented 26 | * - ANSI versions (from from gcc extensions) of the dispatch macros are missing 27 | * - emulator+disassembler in same object file (library is kind of pointless) 28 | */ 29 | 30 | #include 31 | #include 32 | 33 | #include "lib6502.h" 34 | 35 | typedef uint8_t byte; 36 | typedef uint16_t word; 37 | 38 | enum { 39 | flagN= (1<<7), /* negative */ 40 | flagV= (1<<6), /* overflow */ 41 | flagX= (1<<5), /* unused */ 42 | flagB= (1<<4), /* irq from brk */ 43 | flagD= (1<<3), /* decimal mode */ 44 | flagI= (1<<2), /* irq disable */ 45 | flagZ= (1<<1), /* zero */ 46 | flagC= (1<<0) /* carry */ 47 | }; 48 | 49 | #define getN() (P & flagN) 50 | #define getV() (P & flagV) 51 | #define getB() (P & flagB) 52 | #define getD() (P & flagD) 53 | #define getI() (P & flagI) 54 | #define getZ() (P & flagZ) 55 | #define getC() (P & flagC) 56 | 57 | #define setNVZC(N,V,Z,C) (P= (P & ~(flagN | flagV | flagZ | flagC)) | (N) | ((V)<<6) | ((Z)<<1) | (C)) 58 | #define setNZC(N,Z,C) (P= (P & ~(flagN | flagZ | flagC)) | (N) | ((Z)<<1) | (C)) 59 | #define setNZ(N,Z) (P= (P & ~(flagN | flagZ )) | (N) | ((Z)<<1) ) 60 | #define setZ(Z) (P= (P & ~( flagZ )) | ((Z)<<1) ) 61 | #define setC(C) (P= (P & ~( flagC)) | (C)) 62 | 63 | #define NAND(P, Q) (!((P) & (Q))) 64 | 65 | #define tick(n, cpu) cpu->ticks += n 66 | #define tickIf(p) 67 | 68 | /* memory access (indirect if callback installed) -- ARGUMENTS ARE EVALUATED MORE THAN ONCE! */ 69 | 70 | #define putMemory(ADDR, BYTE) \ 71 | ( writeCallback[ADDR] \ 72 | ? writeCallback[ADDR](mpu, ADDR, BYTE) \ 73 | : (memory[ADDR]= BYTE) ) 74 | 75 | #define getMemory(ADDR) \ 76 | ( readCallback[ADDR] \ 77 | ? readCallback[ADDR](mpu, ADDR, 0) \ 78 | : memory[ADDR] ) 79 | 80 | /* stack access (always direct) */ 81 | 82 | #define push(BYTE) (memory[0x0100 + S--]= (BYTE)) 83 | #define pop() (memory[++S + 0x0100]) 84 | 85 | /* adressing modes (memory access direct) */ 86 | 87 | #define implied(ticks, cpu) \ 88 | tick(ticks, cpu); 89 | 90 | #define immediate(ticks, cpu) \ 91 | tick(ticks, cpu); \ 92 | ea= PC++; 93 | 94 | #define abs(ticks, cpu) \ 95 | tick(ticks, cpu); \ 96 | ea= memory[PC] + (memory[PC + 1] << 8); \ 97 | PC += 2; 98 | 99 | #define relative(ticks, cpu) \ 100 | tick(ticks, cpu); \ 101 | ea= memory[PC++]; \ 102 | if (ea & 0x80) ea -= 0x100; \ 103 | tickIf((ea >> 8) != (PC >> 8)); 104 | 105 | #define indirect(ticks, cpu) \ 106 | tick(ticks, cpu); \ 107 | { \ 108 | word tmp; \ 109 | tmp= memory[PC] + (memory[PC + 1] << 8); \ 110 | ea = memory[tmp] + (memory[tmp + 1] << 8); \ 111 | PC += 2; \ 112 | } 113 | 114 | #define absx(ticks, cpu) \ 115 | tick(ticks, cpu); \ 116 | ea= memory[PC] + (memory[PC + 1] << 8); \ 117 | PC += 2; \ 118 | tickIf((ticks == 4) && ((ea >> 8) != ((ea + X) >> 8))); \ 119 | ea += X; 120 | 121 | #define absy(ticks, cpu) \ 122 | tick(ticks, cpu); \ 123 | ea= memory[PC] + (memory[PC + 1] << 8); \ 124 | PC += 2; \ 125 | tickIf((ticks == 4) && ((ea >> 8) != ((ea + Y) >> 8))); \ 126 | ea += Y 127 | 128 | #define zp(ticks, cpu) \ 129 | tick(ticks, cpu); \ 130 | ea= memory[PC++]; 131 | 132 | #define zpx(ticks, cpu) \ 133 | tick(ticks, cpu); \ 134 | ea= memory[PC++] + X; \ 135 | ea &= 0x00ff; 136 | 137 | #define zpy(ticks, cpu) \ 138 | tick(ticks, cpu); \ 139 | ea= memory[PC++] + Y; \ 140 | ea &= 0x00ff; 141 | 142 | #define indx(ticks, cpu) \ 143 | tick(ticks, cpu); \ 144 | { \ 145 | byte tmp= memory[PC++] + X; \ 146 | ea= memory[tmp] + (memory[tmp + 1] << 8); \ 147 | } 148 | 149 | #define indy(ticks, cpu) \ 150 | tick(ticks, cpu); \ 151 | { \ 152 | byte tmp= memory[PC++]; \ 153 | ea= memory[tmp] + (memory[tmp + 1] << 8); \ 154 | tickIf((ticks == 5) && ((ea >> 8) != ((ea + Y) >> 8))); \ 155 | ea += Y; \ 156 | } 157 | 158 | #define indabsx(ticks, cpu) \ 159 | tick(ticks, cpu); \ 160 | { \ 161 | word tmp; \ 162 | tmp= memory[PC ] + (memory[PC + 1] << 8) + X; \ 163 | ea = memory[tmp] + (memory[tmp + 1] << 8); \ 164 | } 165 | 166 | #define indzp(ticks, cpu) \ 167 | tick(ticks, cpu); \ 168 | { \ 169 | byte tmp; \ 170 | tmp= memory[PC++]; \ 171 | ea = memory[tmp] + (memory[tmp + 1] << 8); \ 172 | } 173 | 174 | /* insns */ 175 | 176 | #define adc(ticks, cpu, adrmode) \ 177 | adrmode(ticks, cpu); \ 178 | { \ 179 | byte B= getMemory(ea); \ 180 | if (!getD()) \ 181 | { \ 182 | int c= A + B + getC(); \ 183 | int v= (int8_t)A + (int8_t)B + getC(); \ 184 | fetch(); \ 185 | A= c; \ 186 | setNVZC((A & 0x80), (((A & 0x80) > 0) ^ (v < 0)), (A == 0), ((c & 0x100) > 0)); \ 187 | next(); \ 188 | } \ 189 | else \ 190 | { \ 191 | int l, h, s; \ 192 | /* inelegant & slow, but consistent with the hw for illegal digits */ \ 193 | l= (A & 0x0F) + (B & 0x0F) + getC(); \ 194 | h= (A & 0xF0) + (B & 0xF0); \ 195 | if (l >= 0x0A) { l -= 0x0A; h += 0x10; } \ 196 | if (h >= 0xA0) { h -= 0xA0; } \ 197 | fetch(); \ 198 | s= h | (l & 0x0F); \ 199 | /* only C is valid on NMOS 6502 */ \ 200 | setNVZC(s & 0x80, !(((A ^ B) & 0x80) && ((A ^ s) & 0x80)), !s, !!(h & 0x80)); \ 201 | A= s; \ 202 | tick(1, cpu); \ 203 | next(); \ 204 | } \ 205 | } 206 | 207 | #define sbc(ticks, cpu, adrmode) \ 208 | adrmode(ticks, cpu); \ 209 | { \ 210 | byte B= getMemory(ea); \ 211 | if (!getD()) \ 212 | { \ 213 | int b= 1 - (P &0x01); \ 214 | int c= A - B - b; \ 215 | int v= (int8_t)A - (int8_t) B - b; \ 216 | fetch(); \ 217 | A= c; \ 218 | setNVZC(A & 0x80, ((A & 0x80) > 0) ^ ((v & 0x100) != 0), A == 0, c >= 0); \ 219 | next(); \ 220 | } \ 221 | else \ 222 | { \ 223 | /* this is verbatim ADC, with a 10's complemented operand */ \ 224 | int l, h, s; \ 225 | B= 0x99 - B; \ 226 | l= (A & 0x0F) + (B & 0x0F) + getC(); \ 227 | h= (A & 0xF0) + (B & 0xF0); \ 228 | if (l >= 0x0A) { l -= 0x0A; h += 0x10; } \ 229 | if (h >= 0xA0) { h -= 0xA0; } \ 230 | fetch(); \ 231 | s= h | (l & 0x0F); \ 232 | /* only C is valid on NMOS 6502 */ \ 233 | setNVZC(s & 0x80, !(((A ^ B) & 0x80) && ((A ^ s) & 0x80)), !s, !!(h & 0x80)); \ 234 | A= s; \ 235 | tick(1, cpu); \ 236 | next(); \ 237 | } \ 238 | } 239 | 240 | #define cmpR(ticks, cpu, adrmode, R) \ 241 | adrmode(ticks, cpu); \ 242 | fetch(); \ 243 | { \ 244 | byte B= getMemory(ea); \ 245 | byte d= R - B; \ 246 | setNZC(d & 0x80, !d, R >= B); \ 247 | } \ 248 | next(); 249 | 250 | #define cmp(ticks, cpu, adrmode) cmpR(ticks, cpu, adrmode, A) 251 | #define cpx(ticks, cpu, adrmode) cmpR(ticks, cpu, adrmode, X) 252 | #define cpy(ticks, cpu, adrmode) cmpR(ticks, cpu, adrmode, Y) 253 | 254 | #define dec(ticks, cpu, adrmode) \ 255 | adrmode(ticks, cpu); \ 256 | fetch(); \ 257 | { \ 258 | byte B= getMemory(ea); \ 259 | --B; \ 260 | putMemory(ea, B); \ 261 | setNZ(B & 0x80, !B); \ 262 | } \ 263 | next(); 264 | 265 | #define decR(ticks, cpu, adrmode, R) \ 266 | fetch(); \ 267 | tick(ticks, cpu); \ 268 | --R; \ 269 | setNZ(R & 0x80, !R); \ 270 | next(); 271 | 272 | #define dea(ticks, cpu, adrmode) decR(ticks, cpu, adrmode, A) 273 | #define dex(ticks, cpu, adrmode) decR(ticks, cpu, adrmode, X) 274 | #define dey(ticks, cpu, adrmode) decR(ticks, cpu, adrmode, Y) 275 | 276 | #define inc(ticks, cpu, adrmode) \ 277 | adrmode(ticks, cpu); \ 278 | fetch(); \ 279 | { \ 280 | byte B= getMemory(ea); \ 281 | ++B; \ 282 | putMemory(ea, B); \ 283 | setNZ(B & 0x80, !B); \ 284 | } \ 285 | next(); 286 | 287 | #define incR(ticks, cpu, adrmode, R) \ 288 | fetch(); \ 289 | tick(ticks, cpu); \ 290 | ++R; \ 291 | setNZ(R & 0x80, !R); \ 292 | next(); 293 | 294 | #define ina(ticks, cpu, adrmode) incR(ticks, cpu, adrmode, A) 295 | #define inx(ticks, cpu, adrmode) incR(ticks, cpu, adrmode, X) 296 | #define iny(ticks, cpu, adrmode) incR(ticks, cpu, adrmode, Y) 297 | 298 | #define bit(ticks, cpu, adrmode) \ 299 | adrmode(ticks, cpu); \ 300 | fetch(); \ 301 | { \ 302 | byte B= getMemory(ea); \ 303 | P= (P & ~(flagN | flagV | flagZ)) \ 304 | | (B & (0xC0)) | (((A & B) == 0) << 1); \ 305 | } \ 306 | next(); 307 | 308 | #define tsb(ticks, cpu, adrmode) \ 309 | adrmode(ticks, cpu); \ 310 | fetch(); \ 311 | { \ 312 | byte b= getMemory(ea); \ 313 | b |= A; \ 314 | putMemory(ea, b); \ 315 | setZ(!b); \ 316 | } \ 317 | next(); 318 | 319 | #define trb(ticks, cpu, adrmode) \ 320 | adrmode(ticks, cpu); \ 321 | fetch(); \ 322 | { \ 323 | byte b= getMemory(ea); \ 324 | b |= (A ^ 0xFF); \ 325 | putMemory(ea, b); \ 326 | setZ(!b); \ 327 | } \ 328 | next(); 329 | 330 | #define bitwise(ticks, cpu, adrmode, op) \ 331 | adrmode(ticks, cpu); \ 332 | fetch(); \ 333 | A op##= getMemory(ea); \ 334 | setNZ(A & 0x80, !A); \ 335 | next(); 336 | 337 | #define and(ticks, cpu, adrmode) bitwise(ticks, cpu, adrmode, &) 338 | #define eor(ticks, cpu, adrmode) bitwise(ticks, cpu, adrmode, ^) 339 | #define ora(ticks, cpu, adrmode) bitwise(ticks, cpu, adrmode, |) 340 | 341 | #define asl(ticks, cpu, adrmode) \ 342 | adrmode(ticks, cpu); \ 343 | { \ 344 | unsigned int i= getMemory(ea) << 1; \ 345 | putMemory(ea, i); \ 346 | fetch(); \ 347 | setNZC(i & 0x80, !i, i >> 8); \ 348 | } \ 349 | next(); 350 | 351 | #define asla(ticks, cpu, adrmode) \ 352 | tick(ticks, cpu); \ 353 | fetch(); \ 354 | { \ 355 | int c= A >> 7; \ 356 | A <<= 1; \ 357 | setNZC(A & 0x80, !A, c); \ 358 | } \ 359 | next(); 360 | 361 | #define lsr(ticks, cpu, adrmode) \ 362 | adrmode(ticks, cpu); \ 363 | { \ 364 | byte b= getMemory(ea); \ 365 | int c= b & 1; \ 366 | fetch(); \ 367 | b >>= 1; \ 368 | putMemory(ea, b); \ 369 | setNZC(0, !b, c); \ 370 | } \ 371 | next(); 372 | 373 | #define lsra(ticks, cpu, adrmode) \ 374 | tick(ticks, cpu); \ 375 | fetch(); \ 376 | { \ 377 | int c= A & 1; \ 378 | A >>= 1; \ 379 | setNZC(0, !A, c); \ 380 | } \ 381 | next(); 382 | 383 | #define rol(ticks, cpu, adrmode) \ 384 | adrmode(ticks, cpu); \ 385 | { \ 386 | word b= (getMemory(ea) << 1) | getC(); \ 387 | fetch(); \ 388 | putMemory(ea, b); \ 389 | setNZC(b & 0x80, !(b & 0xFF), b >> 8); \ 390 | } \ 391 | next(); 392 | 393 | #define rola(ticks, cpu, adrmode) \ 394 | tick(ticks, cpu); \ 395 | fetch(); \ 396 | { \ 397 | word b= (A << 1) | getC(); \ 398 | A= b; \ 399 | setNZC(A & 0x80, !A, b >> 8); \ 400 | } \ 401 | next(); 402 | 403 | #define ror(ticks, cpu, adrmode) \ 404 | adrmode(ticks, cpu); \ 405 | { \ 406 | int c= getC(); \ 407 | byte m= getMemory(ea); \ 408 | byte b= (c << 7) | (m >> 1); \ 409 | fetch(); \ 410 | putMemory(ea, b); \ 411 | setNZC(b & 0x80, !b, m & 1); \ 412 | } \ 413 | next(); 414 | 415 | #define rora(ticks, cpu, adrmode) \ 416 | adrmode(ticks, cpu); \ 417 | { \ 418 | int ci= getC(); \ 419 | int co= A & 1; \ 420 | fetch(); \ 421 | A= (ci << 7) | (A >> 1); \ 422 | setNZC(A & 0x80, !A, co); \ 423 | } \ 424 | next(); 425 | 426 | #define tRS(ticks, cpu, adrmode, R, S) \ 427 | fetch(); \ 428 | tick(ticks, cpu); \ 429 | S= R; \ 430 | setNZ(S & 0x80, !S); \ 431 | next(); 432 | 433 | #define tax(ticks, cpu, adrmode) tRS(ticks, cpu, adrmode, A, X) 434 | #define txa(ticks, cpu, adrmode) tRS(ticks, cpu, adrmode, X, A) 435 | #define tay(ticks, cpu, adrmode) tRS(ticks, cpu, adrmode, A, Y) 436 | #define tya(ticks, cpu, adrmode) tRS(ticks, cpu, adrmode, Y, A) 437 | #define tsx(ticks, cpu, adrmode) tRS(ticks, cpu, adrmode, S, X) 438 | 439 | #define txs(ticks, cpu, adrmode) \ 440 | fetch(); \ 441 | tick(ticks, cpu); \ 442 | S= X; \ 443 | next(); 444 | 445 | #define ldR(ticks, cpu, adrmode, R) \ 446 | adrmode(ticks, cpu); \ 447 | fetch(); \ 448 | R= getMemory(ea); \ 449 | setNZ(R & 0x80, !R); \ 450 | next(); 451 | 452 | #define lda(ticks, cpu, adrmode) ldR(ticks, cpu, adrmode, A) 453 | #define ldx(ticks, cpu, adrmode) ldR(ticks, cpu, adrmode, X) 454 | #define ldy(ticks, cpu, adrmode) ldR(ticks, cpu, adrmode, Y) 455 | 456 | #define stR(ticks, cpu, adrmode, R) \ 457 | adrmode(ticks, cpu); \ 458 | fetch(); \ 459 | putMemory(ea, R); \ 460 | next(); 461 | 462 | #define sta(ticks, cpu, adrmode) stR(ticks, cpu, adrmode, A) 463 | #define stx(ticks, cpu, adrmode) stR(ticks, cpu, adrmode, X) 464 | #define sty(ticks, cpu, adrmode) stR(ticks, cpu, adrmode, Y) 465 | #define stz(ticks, cpu, adrmode) stR(ticks, cpu, adrmode, 0) 466 | 467 | #define branch(ticks, cpu, adrmode, cond) \ 468 | if (cond) \ 469 | { \ 470 | adrmode(ticks, cpu); \ 471 | PC += ea; \ 472 | tick(1, cpu); \ 473 | } \ 474 | else \ 475 | { \ 476 | tick(ticks, cpu); \ 477 | PC++; \ 478 | } \ 479 | fetch(); \ 480 | next(); 481 | 482 | #define bcc(ticks, cpu, adrmode) branch(ticks, cpu, adrmode, !getC()) 483 | #define bcs(ticks, cpu, adrmode) branch(ticks, cpu, adrmode, getC()) 484 | #define bne(ticks, cpu, adrmode) branch(ticks, cpu, adrmode, !getZ()) 485 | #define beq(ticks, cpu, adrmode) branch(ticks, cpu, adrmode, getZ()) 486 | #define bpl(ticks, cpu, adrmode) branch(ticks, cpu, adrmode, !getN()) 487 | #define bmi(ticks, cpu, adrmode) branch(ticks, cpu, adrmode, getN()) 488 | #define bvc(ticks, cpu, adrmode) branch(ticks, cpu, adrmode, !getV()) 489 | #define bvs(ticks, cpu, adrmode) branch(ticks, cpu, adrmode, getV()) 490 | 491 | #define bra(ticks, cpu, adrmode) \ 492 | adrmode(ticks, cpu); \ 493 | PC += ea; \ 494 | fetch(); \ 495 | tick(1, cpu); \ 496 | next(); 497 | 498 | #define jmp(ticks, cpu, adrmode) \ 499 | adrmode(ticks, cpu); \ 500 | PC= ea; \ 501 | if (mpu->callbacks->call[ea]) \ 502 | { \ 503 | word addr; \ 504 | externalise(); \ 505 | if ((addr= mpu->callbacks->call[ea](mpu, ea, 0))) \ 506 | { \ 507 | internalise(); \ 508 | PC= addr; \ 509 | } \ 510 | } \ 511 | fetch(); \ 512 | next(); 513 | 514 | #define jsr(ticks, cpu, adrmode) \ 515 | PC++; \ 516 | push(PC >> 8); \ 517 | push(PC & 0xff); \ 518 | PC--; \ 519 | adrmode(ticks, cpu); \ 520 | if (mpu->callbacks->call[ea]) \ 521 | { \ 522 | word addr; \ 523 | externalise(); \ 524 | if ((addr= mpu->callbacks->call[ea](mpu, ea, 0))) \ 525 | { \ 526 | internalise(); \ 527 | PC= addr; \ 528 | fetch(); \ 529 | next(); \ 530 | } \ 531 | } \ 532 | PC=ea; \ 533 | fetch(); \ 534 | next(); 535 | 536 | #define rts(ticks, cpu, adrmode) \ 537 | tick(ticks, cpu); \ 538 | PC = pop(); \ 539 | PC |= (pop() << 8); \ 540 | PC++; \ 541 | fetch(); \ 542 | next(); 543 | 544 | #define brk(ticks, cpu, adrmode) \ 545 | tick(ticks, cpu); \ 546 | PC++; \ 547 | push(PC >> 8); \ 548 | push(PC & 0xff); \ 549 | P |= flagB; \ 550 | push(P | flagX); \ 551 | P |= flagI; \ 552 | { \ 553 | word hdlr= getMemory(0xfffe) + (getMemory(0xffff) << 8); \ 554 | if (mpu->callbacks->call[hdlr]) \ 555 | { \ 556 | word addr; \ 557 | externalise(); \ 558 | if ((addr= mpu->callbacks->call[hdlr](mpu, PC - 2, 0))) \ 559 | { \ 560 | internalise(); \ 561 | hdlr= addr; \ 562 | } \ 563 | } \ 564 | PC= hdlr; \ 565 | } \ 566 | fetch(); \ 567 | next(); 568 | 569 | #define rti(ticks, cpu, adrmode) \ 570 | tick(ticks, cpu); \ 571 | P= pop(); \ 572 | PC= pop(); \ 573 | PC |= (pop() << 8); \ 574 | fetch(); \ 575 | next(); 576 | 577 | #define nop(ticks, cpu, adrmode) \ 578 | fetch(); \ 579 | tick(ticks, cpu); \ 580 | next(); 581 | 582 | #define ill(ticks, cpu, adrmode) \ 583 | fetch(); \ 584 | tick(ticks, cpu); \ 585 | fflush(stdout); \ 586 | fprintf(stderr, "\nundefined instruction %02X\n", memory[PC-1]); \ 587 | return; 588 | 589 | #define phR(ticks, cpu, adrmode, R) \ 590 | fetch(); \ 591 | tick(ticks, cpu); \ 592 | push(R); \ 593 | next(); 594 | 595 | #define pha(ticks, cpu, adrmode) phR(ticks, cpu, adrmode, A) 596 | #define phx(ticks, cpu, adrmode) phR(ticks, cpu, adrmode, X) 597 | #define phy(ticks, cpu, adrmode) phR(ticks, cpu, adrmode, Y) 598 | #define php(ticks, cpu, adrmode) phR(ticks, cpu, adrmode, P | flagX | flagB) 599 | 600 | #define plR(ticks, cpu, adrmode, R) \ 601 | fetch(); \ 602 | tick(ticks, cpu); \ 603 | R= pop(); \ 604 | setNZ(R & 0x80, !R); \ 605 | next(); 606 | 607 | #define pla(ticks, cpu, adrmode) plR(ticks, cpu, adrmode, A) 608 | #define plx(ticks, cpu, adrmode) plR(ticks, cpu, adrmode, X) 609 | #define ply(ticks, cpu, adrmode) plR(ticks, cpu, adrmode, Y) 610 | 611 | #define plp(ticks, cpu, adrmode) \ 612 | fetch(); \ 613 | tick(ticks, cpu); \ 614 | P= pop(); \ 615 | next(); 616 | 617 | #define clF(ticks, cpu, adrmode, F) \ 618 | fetch(); \ 619 | tick(ticks, cpu); \ 620 | P &= ~F; \ 621 | next(); 622 | 623 | #define clc(ticks, cpu, adrmode) clF(ticks, cpu, adrmode, flagC) 624 | #define cld(ticks, cpu, adrmode) clF(ticks, cpu, adrmode, flagD) 625 | #define cli(ticks, cpu, adrmode) clF(ticks, cpu, adrmode, flagI) 626 | #define clv(ticks, cpu, adrmode) clF(ticks, cpu, adrmode, flagV) 627 | 628 | #define seF(ticks, cpu, adrmode, F) \ 629 | fetch(); \ 630 | tick(ticks, cpu); \ 631 | P |= F; \ 632 | next(); 633 | 634 | #define sec(ticks, cpu, adrmode) seF(ticks, cpu, adrmode, flagC) 635 | #define sed(ticks, cpu, adrmode) seF(ticks, cpu, adrmode, flagD) 636 | #define sei(ticks, cpu, adrmode) seF(ticks, cpu, adrmode, flagI) 637 | 638 | #define do_insns(_) \ 639 | _(00, brk, implied, 7); _(01, ora, indx, 6); _(02, ill, implied, 2); _(03, ill, implied, 2); \ 640 | _(04, tsb, zp, 3); _(05, ora, zp, 3); _(06, asl, zp, 5); _(07, ill, implied, 2); \ 641 | _(08, php, implied, 3); _(09, ora, immediate, 3); _(0a, asla,implied, 2); _(0b, ill, implied, 2); \ 642 | _(0c, tsb, abs, 4); _(0d, ora, abs, 4); _(0e, asl, abs, 6); _(0f, ill, implied, 2); \ 643 | _(10, bpl, relative, 2); _(11, ora, indy, 5); _(12, ora, indzp, 3); _(13, ill, implied, 2); \ 644 | _(14, trb, zp, 3); _(15, ora, zpx, 4); _(16, asl, zpx, 6); _(17, ill, implied, 2); \ 645 | _(18, clc, implied, 2); _(19, ora, absy, 4); _(1a, ina, implied, 2); _(1b, ill, implied, 2); \ 646 | _(1c, trb, abs, 4); _(1d, ora, absx, 4); _(1e, asl, absx, 7); _(1f, ill, implied, 2); \ 647 | _(20, jsr, abs, 6); _(21, and, indx, 6); _(22, ill, implied, 2); _(23, ill, implied, 2); \ 648 | _(24, bit, zp, 3); _(25, and, zp, 3); _(26, rol, zp, 5); _(27, ill, implied, 2); \ 649 | _(28, plp, implied, 4); _(29, and, immediate, 3); _(2a, rola,implied, 2); _(2b, ill, implied, 2); \ 650 | _(2c, bit, abs, 4); _(2d, and, abs, 4); _(2e, rol, abs, 6); _(2f, ill, implied, 2); \ 651 | _(30, bmi, relative, 2); _(31, and, indy, 5); _(32, and, indzp, 3); _(33, ill, implied, 2); \ 652 | _(34, bit, zpx, 4); _(35, and, zpx, 4); _(36, rol, zpx, 6); _(37, ill, implied, 2); \ 653 | _(38, sec, implied, 2); _(39, and, absy, 4); _(3a, dea, implied, 2); _(3b, ill, implied, 2); \ 654 | _(3c, bit, absx, 4); _(3d, and, absx, 4); _(3e, rol, absx, 7); _(3f, ill, implied, 2); \ 655 | _(40, rti, implied, 6); _(41, eor, indx, 6); _(42, ill, implied, 2); _(43, ill, implied, 2); \ 656 | _(44, ill, implied, 2); _(45, eor, zp, 3); _(46, lsr, zp, 5); _(47, ill, implied, 2); \ 657 | _(48, pha, implied, 3); _(49, eor, immediate, 3); _(4a, lsra,implied, 2); _(4b, ill, implied, 2); \ 658 | _(4c, jmp, abs, 3); _(4d, eor, abs, 4); _(4e, lsr, abs, 6); _(4f, ill, implied, 2); \ 659 | _(50, bvc, relative, 2); _(51, eor, indy, 5); _(52, eor, indzp, 3); _(53, ill, implied, 2); \ 660 | _(54, ill, implied, 2); _(55, eor, zpx, 4); _(56, lsr, zpx, 6); _(57, ill, implied, 2); \ 661 | _(58, cli, implied, 2); _(59, eor, absy, 4); _(5a, phy, implied, 3); _(5b, ill, implied, 2); \ 662 | _(5c, ill, implied, 2); _(5d, eor, absx, 4); _(5e, lsr, absx, 7); _(5f, ill, implied, 2); \ 663 | _(60, rts, implied, 6); _(61, adc, indx, 6); _(62, ill, implied, 2); _(63, ill, implied, 2); \ 664 | _(64, stz, zp, 3); _(65, adc, zp, 3); _(66, ror, zp, 5); _(67, ill, implied, 2); \ 665 | _(68, pla, implied, 4); _(69, adc, immediate, 3); _(6a, rora,implied, 2); _(6b, ill, implied, 2); \ 666 | _(6c, jmp, indirect, 5); _(6d, adc, abs, 4); _(6e, ror, abs, 6); _(6f, ill, implied, 2); \ 667 | _(70, bvs, relative, 2); _(71, adc, indy, 5); _(72, adc, indzp, 3); _(73, ill, implied, 2); \ 668 | _(74, stz, zpx, 4); _(75, adc, zpx, 4); _(76, ror, zpx, 6); _(77, ill, implied, 2); \ 669 | _(78, sei, implied, 2); _(79, adc, absy, 4); _(7a, ply, implied, 4); _(7b, ill, implied, 2); \ 670 | _(7c, jmp, indabsx, 6); _(7d, adc, absx, 4); _(7e, ror, absx, 7); _(7f, ill, implied, 2); \ 671 | _(80, bra, relative, 2); _(81, sta, indx, 6); _(82, ill, implied, 2); _(83, ill, implied, 2); \ 672 | _(84, sty, zp, 2); _(85, sta, zp, 2); _(86, stx, zp, 2); _(87, ill, implied, 2); \ 673 | _(88, dey, implied, 2); _(89, bit, immediate, 2); _(8a, txa, implied, 2); _(8b, ill, implied, 2); \ 674 | _(8c, sty, abs, 4); _(8d, sta, abs, 4); _(8e, stx, abs, 4); _(8f, ill, implied, 2); \ 675 | _(90, bcc, relative, 2); _(91, sta, indy, 6); _(92, sta, indzp, 3); _(93, ill, implied, 2); \ 676 | _(94, sty, zpx, 4); _(95, sta, zpx, 4); _(96, stx, zpy, 4); _(97, ill, implied, 2); \ 677 | _(98, tya, implied, 2); _(99, sta, absy, 5); _(9a, txs, implied, 2); _(9b, ill, implied, 2); \ 678 | _(9c, stz, abs, 4); _(9d, sta, absx, 5); _(9e, stz, absx, 5); _(9f, ill, implied, 2); \ 679 | _(a0, ldy, immediate, 3); _(a1, lda, indx, 6); _(a2, ldx, immediate, 3); _(a3, ill, implied, 2); \ 680 | _(a4, ldy, zp, 3); _(a5, lda, zp, 3); _(a6, ldx, zp, 3); _(a7, ill, implied, 2); \ 681 | _(a8, tay, implied, 2); _(a9, lda, immediate, 3); _(aa, tax, implied, 2); _(ab, ill, implied, 2); \ 682 | _(ac, ldy, abs, 4); _(ad, lda, abs, 4); _(ae, ldx, abs, 4); _(af, ill, implied, 2); \ 683 | _(b0, bcs, relative, 2); _(b1, lda, indy, 5); _(b2, lda, indzp, 3); _(b3, ill, implied, 2); \ 684 | _(b4, ldy, zpx, 4); _(b5, lda, zpx, 4); _(b6, ldx, zpy, 4); _(b7, ill, implied, 2); \ 685 | _(b8, clv, implied, 2); _(b9, lda, absy, 4); _(ba, tsx, implied, 2); _(bb, ill, implied, 2); \ 686 | _(bc, ldy, absx, 4); _(bd, lda, absx, 4); _(be, ldx, absy, 4); _(bf, ill, implied, 2); \ 687 | _(c0, cpy, immediate, 3); _(c1, cmp, indx, 6); _(c2, ill, implied, 2); _(c3, ill, implied, 2); \ 688 | _(c4, cpy, zp, 3); _(c5, cmp, zp, 3); _(c6, dec, zp, 5); _(c7, ill, implied, 2); \ 689 | _(c8, iny, implied, 2); _(c9, cmp, immediate, 3); _(ca, dex, implied, 2); _(cb, ill, implied, 2); \ 690 | _(cc, cpy, abs, 4); _(cd, cmp, abs, 4); _(ce, dec, abs, 6); _(cf, ill, implied, 2); \ 691 | _(d0, bne, relative, 2); _(d1, cmp, indy, 5); _(d2, cmp, indzp, 3); _(d3, ill, implied, 2); \ 692 | _(d4, ill, implied, 2); _(d5, cmp, zpx, 4); _(d6, dec, zpx, 6); _(d7, ill, implied, 2); \ 693 | _(d8, cld, implied, 2); _(d9, cmp, absy, 4); _(da, phx, implied, 3); _(db, ill, implied, 2); \ 694 | _(dc, ill, implied, 2); _(dd, cmp, absx, 4); _(de, dec, absx, 7); _(df, ill, implied, 2); \ 695 | _(e0, cpx, immediate, 3); _(e1, sbc, indx, 6); _(e2, ill, implied, 2); _(e3, ill, implied, 2); \ 696 | _(e4, cpx, zp, 3); _(e5, sbc, zp, 3); _(e6, inc, zp, 5); _(e7, ill, implied, 2); \ 697 | _(e8, inx, implied, 2); _(e9, sbc, immediate, 3); _(ea, nop, implied, 2); _(eb, ill, implied, 2); \ 698 | _(ec, cpx, abs, 4); _(ed, sbc, abs, 4); _(ee, inc, abs, 6); _(ef, ill, implied, 2); \ 699 | _(f0, beq, relative, 2); _(f1, sbc, indy, 5); _(f2, sbc, indzp, 3); _(f3, ill, implied, 2); \ 700 | _(f4, ill, implied, 2); _(f5, sbc, zpx, 4); _(f6, inc, zpx, 6); _(f7, ill, implied, 2); \ 701 | _(f8, sed, implied, 2); _(f9, sbc, absy, 4); _(fa, plx, implied, 4); _(fb, ill, implied, 2); \ 702 | _(fc, ill, implied, 2); _(fd, sbc, absx, 4); _(fe, inc, absx, 7); _(ff, ill, implied, 2); 703 | 704 | 705 | 706 | void M6502_irq(M6502 *mpu) 707 | { 708 | if (!(mpu->registers->p & flagI)) 709 | { 710 | mpu->memory[0x0100 + mpu->registers->s--] = (byte)(mpu->registers->pc >> 8); 711 | mpu->memory[0x0100 + mpu->registers->s--] = (byte)(mpu->registers->pc & 0xff); 712 | mpu->memory[0x0100 + mpu->registers->s--] = mpu->registers->p; 713 | mpu->registers->p &= ~flagB; 714 | mpu->registers->p |= flagI; 715 | mpu->registers->pc = M6502_getVector(mpu, IRQ); 716 | } 717 | } 718 | 719 | 720 | void M6502_nmi(M6502 *mpu) 721 | { 722 | mpu->memory[0x0100 + mpu->registers->s--] = (byte)(mpu->registers->pc >> 8); 723 | mpu->memory[0x0100 + mpu->registers->s--] = (byte)(mpu->registers->pc & 0xff); 724 | mpu->memory[0x0100 + mpu->registers->s--] = mpu->registers->p; 725 | mpu->registers->p &= ~flagB; 726 | mpu->registers->p |= flagI; 727 | mpu->registers->pc = M6502_getVector(mpu, NMI); 728 | } 729 | 730 | 731 | void M6502_reset(M6502 *mpu) 732 | { 733 | mpu->registers->p &= ~flagD; 734 | mpu->registers->p |= flagI; 735 | mpu->registers->pc = M6502_getVector(mpu, RST); 736 | } 737 | 738 | 739 | /* the compiler should elminate all call to this function */ 740 | 741 | static void oops(void) 742 | { 743 | fprintf(stderr, "\noops -- instruction dispatch missing\n"); 744 | } 745 | 746 | void M6502_run(M6502 *mpu) 747 | { 748 | #if defined(__GNUC__) && !defined(__STRICT_ANSI__) 749 | 750 | static void *itab[256]= { &&_00, &&_01, &&_02, &&_03, &&_04, &&_05, &&_06, &&_07, &&_08, &&_09, &&_0a, &&_0b, &&_0c, &&_0d, &&_0e, &&_0f, 751 | &&_10, &&_11, &&_12, &&_13, &&_14, &&_15, &&_16, &&_17, &&_18, &&_19, &&_1a, &&_1b, &&_1c, &&_1d, &&_1e, &&_1f, 752 | &&_20, &&_21, &&_22, &&_23, &&_24, &&_25, &&_26, &&_27, &&_28, &&_29, &&_2a, &&_2b, &&_2c, &&_2d, &&_2e, &&_2f, 753 | &&_30, &&_31, &&_32, &&_33, &&_34, &&_35, &&_36, &&_37, &&_38, &&_39, &&_3a, &&_3b, &&_3c, &&_3d, &&_3e, &&_3f, 754 | &&_40, &&_41, &&_42, &&_43, &&_44, &&_45, &&_46, &&_47, &&_48, &&_49, &&_4a, &&_4b, &&_4c, &&_4d, &&_4e, &&_4f, 755 | &&_50, &&_51, &&_52, &&_53, &&_54, &&_55, &&_56, &&_57, &&_58, &&_59, &&_5a, &&_5b, &&_5c, &&_5d, &&_5e, &&_5f, 756 | &&_60, &&_61, &&_62, &&_63, &&_64, &&_65, &&_66, &&_67, &&_68, &&_69, &&_6a, &&_6b, &&_6c, &&_6d, &&_6e, &&_6f, 757 | &&_70, &&_71, &&_72, &&_73, &&_74, &&_75, &&_76, &&_77, &&_78, &&_79, &&_7a, &&_7b, &&_7c, &&_7d, &&_7e, &&_7f, 758 | &&_80, &&_81, &&_82, &&_83, &&_84, &&_85, &&_86, &&_87, &&_88, &&_89, &&_8a, &&_8b, &&_8c, &&_8d, &&_8e, &&_8f, 759 | &&_90, &&_91, &&_92, &&_93, &&_94, &&_95, &&_96, &&_97, &&_98, &&_99, &&_9a, &&_9b, &&_9c, &&_9d, &&_9e, &&_9f, 760 | &&_a0, &&_a1, &&_a2, &&_a3, &&_a4, &&_a5, &&_a6, &&_a7, &&_a8, &&_a9, &&_aa, &&_ab, &&_ac, &&_ad, &&_ae, &&_af, 761 | &&_b0, &&_b1, &&_b2, &&_b3, &&_b4, &&_b5, &&_b6, &&_b7, &&_b8, &&_b9, &&_ba, &&_bb, &&_bc, &&_bd, &&_be, &&_bf, 762 | &&_c0, &&_c1, &&_c2, &&_c3, &&_c4, &&_c5, &&_c6, &&_c7, &&_c8, &&_c9, &&_ca, &&_cb, &&_cc, &&_cd, &&_ce, &&_cf, 763 | &&_d0, &&_d1, &&_d2, &&_d3, &&_d4, &&_d5, &&_d6, &&_d7, &&_d8, &&_d9, &&_da, &&_db, &&_dc, &&_dd, &&_de, &&_df, 764 | &&_e0, &&_e1, &&_e2, &&_e3, &&_e4, &&_e5, &&_e6, &&_e7, &&_e8, &&_e9, &&_ea, &&_eb, &&_ec, &&_ed, &&_ee, &&_ef, 765 | &&_f0, &&_f1, &&_f2, &&_f3, &&_f4, &&_f5, &&_f6, &&_f7, &&_f8, &&_f9, &&_fa, &&_fb, &&_fc, &&_fd, &&_fe, &&_ff }; 766 | 767 | register void **itabp= &itab[0]; 768 | register void *tpc; 769 | 770 | # define begin() fetch(); next() 771 | # define fetch() tpc= itabp[memory[PC++]] 772 | # define next() goto *tpc 773 | # define dispatch(num, name, mode, cycles) _##num: name(cycles, mpu, mode) oops(); next() 774 | # define end() 775 | 776 | #else /* (!__GNUC__) || (__STRICT_ANSI__) */ 777 | 778 | # define begin() for (;;) switch (memory[PC++]) { 779 | # define fetch() 780 | # define next() break 781 | # define dispatch(num, name, mode, cycles) case 0x##num: name(cycles, mpu, mode); next() 782 | # define end() } 783 | 784 | #endif 785 | 786 | register byte *memory= mpu->memory; 787 | register word PC; 788 | word ea; 789 | byte A, X, Y, P, S; 790 | M6502_Callback *readCallback= mpu->callbacks->read; 791 | M6502_Callback *writeCallback= mpu->callbacks->write; 792 | 793 | # 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 794 | # 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 795 | 796 | internalise(); 797 | 798 | begin(); 799 | do_insns(dispatch); 800 | end(); 801 | 802 | # undef begin 803 | # undef internalise 804 | # undef externalise 805 | # undef fetch 806 | # undef next 807 | # undef dispatch 808 | # undef end 809 | 810 | (void)oops; 811 | } 812 | 813 | void M6502_tick(M6502 *mpu) 814 | { 815 | if ( ! mpu->ticks-- ) 816 | { 817 | mpu->ticks = 0; 818 | M6502_step(mpu); 819 | } 820 | } 821 | 822 | void M6502_step(M6502 *mpu) 823 | { 824 | 825 | # define begin() switch (memory[PC++]) { 826 | # define fetch() 827 | # define next() break 828 | # define dispatch(num, name, mode, cycles) case 0x##num: name(cycles, mpu, mode); next() 829 | # define end() } 830 | 831 | 832 | register byte *memory= mpu->memory; 833 | register word PC; 834 | word ea; 835 | byte A, X, Y, P, S; 836 | M6502_Callback *readCallback= mpu->callbacks->read; 837 | M6502_Callback *writeCallback= mpu->callbacks->write; 838 | 839 | # 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 840 | # 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 841 | 842 | internalise(); 843 | 844 | begin(); 845 | do_insns(dispatch); 846 | end(); 847 | 848 | externalise(); 849 | 850 | # undef begin 851 | # undef internalise 852 | # undef externalise 853 | # undef fetch 854 | # undef next 855 | # undef dispatch 856 | # undef end 857 | 858 | } 859 | 860 | int M6502_disassemble(M6502 *mpu, word ip, char buffer[64]) 861 | { 862 | char *s= buffer; 863 | byte *b= mpu->memory + ip; 864 | 865 | switch (b[0]) 866 | { 867 | # define _implied return 1; 868 | # define _immediate sprintf(s, "#%02X", b[1]); return 2; 869 | # define _zp sprintf(s, "%02X", b[1]); return 2; 870 | # define _zpx sprintf(s, "%02X,X", b[1]); return 2; 871 | # define _zpy sprintf(s, "%02X,Y", b[1]); return 2; 872 | # define _abs sprintf(s, "%02X%02X", b[2], b[1]); return 3; 873 | # define _absx sprintf(s, "%02X%02X,X", b[2], b[1]); return 3; 874 | # define _absy sprintf(s, "%02X%02X,Y", b[2], b[1]); return 3; 875 | # define _relative sprintf(s, "%04X", ip + 2 + (int8_t)b[1]); return 2; 876 | # define _indirect sprintf(s, "(%02X%02X)", b[2], b[1]); return 3; 877 | # define _indzp sprintf(s, "(%02X)", b[1]); return 2; 878 | # define _indx sprintf(s, "(%02X,X)", b[1]); return 2; 879 | # define _indy sprintf(s, "(%02X),Y", b[1]); return 2; 880 | # define _indabsx sprintf(s, "(%02X%02X,X)", b[2], b[1]); return 3; 881 | 882 | # define disassemble(num, name, mode, cycles) case 0x##num: s += sprintf(s, "%s ", #name); _##mode 883 | do_insns(disassemble); 884 | # undef _do 885 | } 886 | 887 | return 0; 888 | } 889 | 890 | 891 | void M6502_dump(M6502 *mpu, char buffer[64]) 892 | { 893 | M6502_Registers *r= mpu->registers; 894 | uint8_t p= r->p; 895 | # define P(N,C) (p & (1 << (N)) ? (C) : '-') 896 | sprintf(buffer, "PC=%04X SP=%04X A=%02X X=%02X Y=%02X P=%02X %c%c%c%c%c%c%c%c", 897 | r->pc, 0x0100 + r->s, 898 | r->a, r->x, r->y, r->p, 899 | P(7,'N'), P(6,'V'), P(5,'?'), P(4,'B'), P(3,'D'), P(2,'I'), P(1,'Z'), P(0,'C')); 900 | # undef P 901 | } 902 | 903 | 904 | static void outOfMemory(void) 905 | { 906 | fflush(stdout); 907 | fprintf(stderr, "\nout of memory\n"); 908 | abort(); 909 | } 910 | 911 | 912 | M6502 *M6502_new(M6502_Registers *registers, M6502_Memory memory, M6502_Callbacks *callbacks) 913 | { 914 | M6502 *mpu= calloc(1, sizeof(M6502)); 915 | if (!mpu) outOfMemory(); 916 | 917 | if (!registers) { registers = (M6502_Registers *)calloc(1, sizeof(M6502_Registers)); mpu->flags |= M6502_RegistersAllocated; } 918 | if (!memory ) { memory = (uint8_t *)calloc(1, sizeof(M6502_Memory )); mpu->flags |= M6502_MemoryAllocated; } 919 | if (!callbacks) { callbacks = (M6502_Callbacks *)calloc(1, sizeof(M6502_Callbacks)); mpu->flags |= M6502_CallbacksAllocated; } 920 | 921 | if (!registers || !memory || !callbacks) outOfMemory(); 922 | 923 | mpu->registers = registers; 924 | mpu->memory = memory; 925 | mpu->callbacks = callbacks; 926 | mpu->ticks = 0; 927 | 928 | return mpu; 929 | } 930 | 931 | 932 | void M6502_delete(M6502 *mpu) 933 | { 934 | if (mpu->flags & M6502_CallbacksAllocated) free(mpu->callbacks); 935 | if (mpu->flags & M6502_MemoryAllocated ) free(mpu->memory); 936 | if (mpu->flags & M6502_RegistersAllocated) free(mpu->registers); 937 | 938 | free(mpu); 939 | } 940 | -------------------------------------------------------------------------------- /lib6502.h: -------------------------------------------------------------------------------- 1 | #ifndef __m6502_h 2 | #define __m6502_h 3 | 4 | 5 | #include 6 | #include 7 | 8 | typedef struct _M6502 M6502; 9 | typedef struct _M6502_Registers M6502_Registers; 10 | typedef struct _M6502_Callbacks M6502_Callbacks; 11 | 12 | typedef int (*M6502_Callback)(M6502 *mpu, uint16_t address, uint8_t data); 13 | 14 | typedef M6502_Callback M6502_CallbackTable[0x10000]; 15 | typedef uint8_t M6502_Memory[0x10000]; 16 | 17 | enum { 18 | M6502_NMIVector= 0xfffa, M6502_NMIVectorLSB= 0xfffa, M6502_NMIVectorMSB= 0xfffb, 19 | M6502_RSTVector= 0xfffc, M6502_RSTVectorLSB= 0xfffc, M6502_RSTVectorMSB= 0xfffd, 20 | M6502_IRQVector= 0xfffe, M6502_IRQVectorLSB= 0xfffe, M6502_IRQVectorMSB= 0xffff 21 | }; 22 | 23 | struct _M6502_Registers 24 | { 25 | uint8_t a; /* accumulator */ 26 | uint8_t x; /* X index register */ 27 | uint8_t y; /* Y index register */ 28 | uint8_t p; /* processor status register */ 29 | uint8_t s; /* stack pointer */ 30 | uint16_t pc; /* program counter */ 31 | }; 32 | 33 | struct _M6502_Callbacks 34 | { 35 | M6502_CallbackTable read; 36 | M6502_CallbackTable write; 37 | M6502_CallbackTable call; 38 | }; 39 | 40 | struct _M6502 41 | { 42 | M6502_Registers *registers; 43 | uint8_t *memory; 44 | M6502_Callbacks *callbacks; 45 | unsigned int flags; 46 | unsigned char ticks; 47 | }; 48 | 49 | enum { 50 | M6502_RegistersAllocated = 1 << 0, 51 | M6502_MemoryAllocated = 1 << 1, 52 | M6502_CallbacksAllocated = 1 << 2 53 | }; 54 | 55 | extern M6502 *M6502_new(M6502_Registers *registers, M6502_Memory memory, M6502_Callbacks *callbacks); 56 | extern void M6502_reset(M6502 *mpu); 57 | extern void M6502_nmi(M6502 *mpu); 58 | extern void M6502_irq(M6502 *mpu); 59 | extern void M6502_run(M6502 *mpu); 60 | extern void M6502_tick(M6502 *mpu); 61 | extern void M6502_step(M6502 *mpu); 62 | extern int M6502_disassemble(M6502 *mpu, uint16_t addr, char buffer[64]); 63 | extern void M6502_dump(M6502 *mpu, char buffer[64]); 64 | extern void M6502_delete(M6502 *mpu); 65 | 66 | #define M6502_getVector(MPU, VEC) \ 67 | ( ( ((MPU)->memory[M6502_##VEC##VectorLSB]) ) \ 68 | | ((MPU)->memory[M6502_##VEC##VectorMSB] << 8) ) 69 | 70 | #define M6502_setVector(MPU, VEC, ADDR) \ 71 | ( ( ((MPU)->memory[M6502_##VEC##VectorLSB]= ((uint8_t)(ADDR)) & 0xff) ) \ 72 | , ((MPU)->memory[M6502_##VEC##VectorMSB]= (uint8_t)((ADDR) >> 8)) ) 73 | 74 | #define M6502_getCallback(MPU, TYPE, ADDR) ((MPU)->callbacks->TYPE[ADDR]) 75 | #define M6502_setCallback(MPU, TYPE, ADDR, FN) ((MPU)->callbacks->TYPE[ADDR]= (FN)) 76 | 77 | 78 | #endif //__m6502_h 79 | -------------------------------------------------------------------------------- /lib6502.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4C01B7161D0E28CC00E2B509 /* config.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C01B7131D0E28CC00E2B509 /* config.h */; }; 11 | 4C01B7171D0E28CC00E2B509 /* lib6502.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C01B7141D0E28CC00E2B509 /* lib6502.c */; }; 12 | 4C01B7181D0E28CC00E2B509 /* lib6502.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C01B7151D0E28CC00E2B509 /* lib6502.h */; }; 13 | 4C01B7261D0E299400E2B509 /* run6502.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C01B7251D0E299400E2B509 /* run6502.c */; }; 14 | 4C01B7271D0E29BA00E2B509 /* liblib6502.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C01B70B1D0E288300E2B509 /* liblib6502.a */; }; 15 | 4C01B7381D0E300600E2B509 /* lib1.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C01B72A1D0E2A3B00E2B509 /* lib1.c */; }; 16 | 4C01B7391D0E301000E2B509 /* liblib6502.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C01B70B1D0E288300E2B509 /* liblib6502.a */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXCopyFilesBuildPhase section */ 20 | 4C01B71C1D0E28EE00E2B509 /* CopyFiles */ = { 21 | isa = PBXCopyFilesBuildPhase; 22 | buildActionMask = 2147483647; 23 | dstPath = /usr/share/man/man1/; 24 | dstSubfolderSpec = 0; 25 | files = ( 26 | ); 27 | runOnlyForDeploymentPostprocessing = 1; 28 | }; 29 | 4C01B72F1D0E2FFE00E2B509 /* CopyFiles */ = { 30 | isa = PBXCopyFilesBuildPhase; 31 | buildActionMask = 2147483647; 32 | dstPath = /usr/share/man/man1/; 33 | dstSubfolderSpec = 0; 34 | files = ( 35 | ); 36 | runOnlyForDeploymentPostprocessing = 1; 37 | }; 38 | /* End PBXCopyFilesBuildPhase section */ 39 | 40 | /* Begin PBXFileReference section */ 41 | 4C01B70B1D0E288300E2B509 /* liblib6502.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = liblib6502.a; sourceTree = BUILT_PRODUCTS_DIR; }; 42 | 4C01B7131D0E28CC00E2B509 /* config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = config.h; sourceTree = ""; }; 43 | 4C01B7141D0E28CC00E2B509 /* lib6502.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lib6502.c; sourceTree = ""; }; 44 | 4C01B7151D0E28CC00E2B509 /* lib6502.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lib6502.h; sourceTree = ""; }; 45 | 4C01B71E1D0E28EE00E2B509 /* emulator6502 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = emulator6502; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | 4C01B7251D0E299400E2B509 /* run6502.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = run6502.c; sourceTree = SOURCE_ROOT; }; 47 | 4C01B7281D0E29F200E2B509 /* README */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README; sourceTree = ""; }; 48 | 4C01B72A1D0E2A3B00E2B509 /* lib1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lib1.c; sourceTree = ""; }; 49 | 4C01B72B1D0E2A3B00E2B509 /* README */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README; sourceTree = ""; }; 50 | 4C01B7311D0E2FFE00E2B509 /* lib1 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = lib1; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | /* End PBXFileReference section */ 52 | 53 | /* Begin PBXFrameworksBuildPhase section */ 54 | 4C01B7081D0E288300E2B509 /* Frameworks */ = { 55 | isa = PBXFrameworksBuildPhase; 56 | buildActionMask = 2147483647; 57 | files = ( 58 | ); 59 | runOnlyForDeploymentPostprocessing = 0; 60 | }; 61 | 4C01B71B1D0E28EE00E2B509 /* Frameworks */ = { 62 | isa = PBXFrameworksBuildPhase; 63 | buildActionMask = 2147483647; 64 | files = ( 65 | 4C01B7271D0E29BA00E2B509 /* liblib6502.a in Frameworks */, 66 | ); 67 | runOnlyForDeploymentPostprocessing = 0; 68 | }; 69 | 4C01B72E1D0E2FFE00E2B509 /* Frameworks */ = { 70 | isa = PBXFrameworksBuildPhase; 71 | buildActionMask = 2147483647; 72 | files = ( 73 | 4C01B7391D0E301000E2B509 /* liblib6502.a in Frameworks */, 74 | ); 75 | runOnlyForDeploymentPostprocessing = 0; 76 | }; 77 | /* End PBXFrameworksBuildPhase section */ 78 | 79 | /* Begin PBXGroup section */ 80 | 4C01B7021D0E288300E2B509 = { 81 | isa = PBXGroup; 82 | children = ( 83 | 4C01B7281D0E29F200E2B509 /* README */, 84 | 4C01B7121D0E28A900E2B509 /* Source */, 85 | 4C01B71F1D0E28EE00E2B509 /* emulator6502 */, 86 | 4C01B7291D0E29FC00E2B509 /* examples */, 87 | 4C01B70C1D0E288300E2B509 /* Products */, 88 | ); 89 | sourceTree = ""; 90 | }; 91 | 4C01B70C1D0E288300E2B509 /* Products */ = { 92 | isa = PBXGroup; 93 | children = ( 94 | 4C01B70B1D0E288300E2B509 /* liblib6502.a */, 95 | 4C01B71E1D0E28EE00E2B509 /* emulator6502 */, 96 | 4C01B7311D0E2FFE00E2B509 /* lib1 */, 97 | ); 98 | name = Products; 99 | sourceTree = ""; 100 | }; 101 | 4C01B7121D0E28A900E2B509 /* Source */ = { 102 | isa = PBXGroup; 103 | children = ( 104 | 4C01B7131D0E28CC00E2B509 /* config.h */, 105 | 4C01B7141D0E28CC00E2B509 /* lib6502.c */, 106 | 4C01B7151D0E28CC00E2B509 /* lib6502.h */, 107 | ); 108 | name = Source; 109 | sourceTree = ""; 110 | }; 111 | 4C01B71F1D0E28EE00E2B509 /* emulator6502 */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | 4C01B7251D0E299400E2B509 /* run6502.c */, 115 | ); 116 | path = emulator6502; 117 | sourceTree = ""; 118 | }; 119 | 4C01B7291D0E29FC00E2B509 /* examples */ = { 120 | isa = PBXGroup; 121 | children = ( 122 | 4C01B72B1D0E2A3B00E2B509 /* README */, 123 | 4C01B72A1D0E2A3B00E2B509 /* lib1.c */, 124 | ); 125 | path = examples; 126 | sourceTree = ""; 127 | }; 128 | /* End PBXGroup section */ 129 | 130 | /* Begin PBXHeadersBuildPhase section */ 131 | 4C01B7091D0E288300E2B509 /* Headers */ = { 132 | isa = PBXHeadersBuildPhase; 133 | buildActionMask = 2147483647; 134 | files = ( 135 | 4C01B7161D0E28CC00E2B509 /* config.h in Headers */, 136 | 4C01B7181D0E28CC00E2B509 /* lib6502.h in Headers */, 137 | ); 138 | runOnlyForDeploymentPostprocessing = 0; 139 | }; 140 | /* End PBXHeadersBuildPhase section */ 141 | 142 | /* Begin PBXNativeTarget section */ 143 | 4C01B70A1D0E288300E2B509 /* lib6502 */ = { 144 | isa = PBXNativeTarget; 145 | buildConfigurationList = 4C01B70F1D0E288300E2B509 /* Build configuration list for PBXNativeTarget "lib6502" */; 146 | buildPhases = ( 147 | 4C01B7071D0E288300E2B509 /* Sources */, 148 | 4C01B7081D0E288300E2B509 /* Frameworks */, 149 | 4C01B7091D0E288300E2B509 /* Headers */, 150 | ); 151 | buildRules = ( 152 | ); 153 | dependencies = ( 154 | ); 155 | name = lib6502; 156 | productName = lib6502; 157 | productReference = 4C01B70B1D0E288300E2B509 /* liblib6502.a */; 158 | productType = "com.apple.product-type.library.static"; 159 | }; 160 | 4C01B71D1D0E28EE00E2B509 /* emulator6502 */ = { 161 | isa = PBXNativeTarget; 162 | buildConfigurationList = 4C01B7221D0E28EE00E2B509 /* Build configuration list for PBXNativeTarget "emulator6502" */; 163 | buildPhases = ( 164 | 4C01B71A1D0E28EE00E2B509 /* Sources */, 165 | 4C01B71B1D0E28EE00E2B509 /* Frameworks */, 166 | 4C01B71C1D0E28EE00E2B509 /* CopyFiles */, 167 | ); 168 | buildRules = ( 169 | ); 170 | dependencies = ( 171 | ); 172 | name = emulator6502; 173 | productName = emulator6502; 174 | productReference = 4C01B71E1D0E28EE00E2B509 /* emulator6502 */; 175 | productType = "com.apple.product-type.tool"; 176 | }; 177 | 4C01B7301D0E2FFE00E2B509 /* lib1 */ = { 178 | isa = PBXNativeTarget; 179 | buildConfigurationList = 4C01B7351D0E2FFE00E2B509 /* Build configuration list for PBXNativeTarget "lib1" */; 180 | buildPhases = ( 181 | 4C01B72D1D0E2FFE00E2B509 /* Sources */, 182 | 4C01B72E1D0E2FFE00E2B509 /* Frameworks */, 183 | 4C01B72F1D0E2FFE00E2B509 /* CopyFiles */, 184 | ); 185 | buildRules = ( 186 | ); 187 | dependencies = ( 188 | ); 189 | name = lib1; 190 | productName = lib1; 191 | productReference = 4C01B7311D0E2FFE00E2B509 /* lib1 */; 192 | productType = "com.apple.product-type.tool"; 193 | }; 194 | /* End PBXNativeTarget section */ 195 | 196 | /* Begin PBXProject section */ 197 | 4C01B7031D0E288300E2B509 /* Project object */ = { 198 | isa = PBXProject; 199 | attributes = { 200 | LastUpgradeCheck = 0730; 201 | ORGANIZATIONNAME = "Fyrestead, LLC"; 202 | TargetAttributes = { 203 | 4C01B70A1D0E288300E2B509 = { 204 | CreatedOnToolsVersion = 7.3.1; 205 | }; 206 | 4C01B71D1D0E28EE00E2B509 = { 207 | CreatedOnToolsVersion = 7.3.1; 208 | }; 209 | 4C01B7301D0E2FFE00E2B509 = { 210 | CreatedOnToolsVersion = 7.3.1; 211 | }; 212 | }; 213 | }; 214 | buildConfigurationList = 4C01B7061D0E288300E2B509 /* Build configuration list for PBXProject "lib6502" */; 215 | compatibilityVersion = "Xcode 3.2"; 216 | developmentRegion = English; 217 | hasScannedForEncodings = 0; 218 | knownRegions = ( 219 | en, 220 | ); 221 | mainGroup = 4C01B7021D0E288300E2B509; 222 | productRefGroup = 4C01B70C1D0E288300E2B509 /* Products */; 223 | projectDirPath = ""; 224 | projectRoot = ""; 225 | targets = ( 226 | 4C01B70A1D0E288300E2B509 /* lib6502 */, 227 | 4C01B71D1D0E28EE00E2B509 /* emulator6502 */, 228 | 4C01B7301D0E2FFE00E2B509 /* lib1 */, 229 | ); 230 | }; 231 | /* End PBXProject section */ 232 | 233 | /* Begin PBXSourcesBuildPhase section */ 234 | 4C01B7071D0E288300E2B509 /* Sources */ = { 235 | isa = PBXSourcesBuildPhase; 236 | buildActionMask = 2147483647; 237 | files = ( 238 | 4C01B7171D0E28CC00E2B509 /* lib6502.c in Sources */, 239 | ); 240 | runOnlyForDeploymentPostprocessing = 0; 241 | }; 242 | 4C01B71A1D0E28EE00E2B509 /* Sources */ = { 243 | isa = PBXSourcesBuildPhase; 244 | buildActionMask = 2147483647; 245 | files = ( 246 | 4C01B7261D0E299400E2B509 /* run6502.c in Sources */, 247 | ); 248 | runOnlyForDeploymentPostprocessing = 0; 249 | }; 250 | 4C01B72D1D0E2FFE00E2B509 /* Sources */ = { 251 | isa = PBXSourcesBuildPhase; 252 | buildActionMask = 2147483647; 253 | files = ( 254 | 4C01B7381D0E300600E2B509 /* lib1.c in Sources */, 255 | ); 256 | runOnlyForDeploymentPostprocessing = 0; 257 | }; 258 | /* End PBXSourcesBuildPhase section */ 259 | 260 | /* Begin XCBuildConfiguration section */ 261 | 4C01B70D1D0E288300E2B509 /* Debug */ = { 262 | isa = XCBuildConfiguration; 263 | buildSettings = { 264 | ALWAYS_SEARCH_USER_PATHS = NO; 265 | CLANG_ANALYZER_NONNULL = YES; 266 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 267 | CLANG_CXX_LIBRARY = "libc++"; 268 | CLANG_ENABLE_MODULES = YES; 269 | CLANG_ENABLE_OBJC_ARC = YES; 270 | CLANG_WARN_BOOL_CONVERSION = YES; 271 | CLANG_WARN_CONSTANT_CONVERSION = YES; 272 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 273 | CLANG_WARN_EMPTY_BODY = YES; 274 | CLANG_WARN_ENUM_CONVERSION = YES; 275 | CLANG_WARN_INT_CONVERSION = YES; 276 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 277 | CLANG_WARN_UNREACHABLE_CODE = YES; 278 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 279 | CODE_SIGN_IDENTITY = "-"; 280 | COPY_PHASE_STRIP = NO; 281 | DEBUG_INFORMATION_FORMAT = dwarf; 282 | ENABLE_STRICT_OBJC_MSGSEND = YES; 283 | ENABLE_TESTABILITY = YES; 284 | GCC_C_LANGUAGE_STANDARD = gnu99; 285 | GCC_DYNAMIC_NO_PIC = NO; 286 | GCC_NO_COMMON_BLOCKS = YES; 287 | GCC_OPTIMIZATION_LEVEL = 0; 288 | GCC_PREPROCESSOR_DEFINITIONS = ( 289 | "DEBUG=1", 290 | "$(inherited)", 291 | ); 292 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 293 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 294 | GCC_WARN_UNDECLARED_SELECTOR = YES; 295 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 296 | GCC_WARN_UNUSED_FUNCTION = YES; 297 | GCC_WARN_UNUSED_VARIABLE = YES; 298 | MACOSX_DEPLOYMENT_TARGET = 10.11; 299 | MTL_ENABLE_DEBUG_INFO = YES; 300 | ONLY_ACTIVE_ARCH = YES; 301 | SDKROOT = macosx; 302 | }; 303 | name = Debug; 304 | }; 305 | 4C01B70E1D0E288300E2B509 /* Release */ = { 306 | isa = XCBuildConfiguration; 307 | buildSettings = { 308 | ALWAYS_SEARCH_USER_PATHS = NO; 309 | CLANG_ANALYZER_NONNULL = YES; 310 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 311 | CLANG_CXX_LIBRARY = "libc++"; 312 | CLANG_ENABLE_MODULES = YES; 313 | CLANG_ENABLE_OBJC_ARC = YES; 314 | CLANG_WARN_BOOL_CONVERSION = YES; 315 | CLANG_WARN_CONSTANT_CONVERSION = YES; 316 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 317 | CLANG_WARN_EMPTY_BODY = YES; 318 | CLANG_WARN_ENUM_CONVERSION = YES; 319 | CLANG_WARN_INT_CONVERSION = YES; 320 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 321 | CLANG_WARN_UNREACHABLE_CODE = YES; 322 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 323 | CODE_SIGN_IDENTITY = "-"; 324 | COPY_PHASE_STRIP = NO; 325 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 326 | ENABLE_NS_ASSERTIONS = NO; 327 | ENABLE_STRICT_OBJC_MSGSEND = YES; 328 | GCC_C_LANGUAGE_STANDARD = gnu99; 329 | GCC_NO_COMMON_BLOCKS = YES; 330 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 331 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 332 | GCC_WARN_UNDECLARED_SELECTOR = YES; 333 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 334 | GCC_WARN_UNUSED_FUNCTION = YES; 335 | GCC_WARN_UNUSED_VARIABLE = YES; 336 | MACOSX_DEPLOYMENT_TARGET = 10.11; 337 | MTL_ENABLE_DEBUG_INFO = NO; 338 | SDKROOT = macosx; 339 | }; 340 | name = Release; 341 | }; 342 | 4C01B7101D0E288300E2B509 /* Debug */ = { 343 | isa = XCBuildConfiguration; 344 | buildSettings = { 345 | EXECUTABLE_PREFIX = lib; 346 | PRODUCT_NAME = "$(TARGET_NAME)"; 347 | }; 348 | name = Debug; 349 | }; 350 | 4C01B7111D0E288300E2B509 /* Release */ = { 351 | isa = XCBuildConfiguration; 352 | buildSettings = { 353 | EXECUTABLE_PREFIX = lib; 354 | PRODUCT_NAME = "$(TARGET_NAME)"; 355 | }; 356 | name = Release; 357 | }; 358 | 4C01B7231D0E28EE00E2B509 /* Debug */ = { 359 | isa = XCBuildConfiguration; 360 | buildSettings = { 361 | PRODUCT_NAME = "$(TARGET_NAME)"; 362 | }; 363 | name = Debug; 364 | }; 365 | 4C01B7241D0E28EE00E2B509 /* Release */ = { 366 | isa = XCBuildConfiguration; 367 | buildSettings = { 368 | PRODUCT_NAME = "$(TARGET_NAME)"; 369 | }; 370 | name = Release; 371 | }; 372 | 4C01B7361D0E2FFE00E2B509 /* Debug */ = { 373 | isa = XCBuildConfiguration; 374 | buildSettings = { 375 | PRODUCT_NAME = "$(TARGET_NAME)"; 376 | }; 377 | name = Debug; 378 | }; 379 | 4C01B7371D0E2FFE00E2B509 /* Release */ = { 380 | isa = XCBuildConfiguration; 381 | buildSettings = { 382 | PRODUCT_NAME = "$(TARGET_NAME)"; 383 | }; 384 | name = Release; 385 | }; 386 | /* End XCBuildConfiguration section */ 387 | 388 | /* Begin XCConfigurationList section */ 389 | 4C01B7061D0E288300E2B509 /* Build configuration list for PBXProject "lib6502" */ = { 390 | isa = XCConfigurationList; 391 | buildConfigurations = ( 392 | 4C01B70D1D0E288300E2B509 /* Debug */, 393 | 4C01B70E1D0E288300E2B509 /* Release */, 394 | ); 395 | defaultConfigurationIsVisible = 0; 396 | defaultConfigurationName = Release; 397 | }; 398 | 4C01B70F1D0E288300E2B509 /* Build configuration list for PBXNativeTarget "lib6502" */ = { 399 | isa = XCConfigurationList; 400 | buildConfigurations = ( 401 | 4C01B7101D0E288300E2B509 /* Debug */, 402 | 4C01B7111D0E288300E2B509 /* Release */, 403 | ); 404 | defaultConfigurationIsVisible = 0; 405 | defaultConfigurationName = Release; 406 | }; 407 | 4C01B7221D0E28EE00E2B509 /* Build configuration list for PBXNativeTarget "emulator6502" */ = { 408 | isa = XCConfigurationList; 409 | buildConfigurations = ( 410 | 4C01B7231D0E28EE00E2B509 /* Debug */, 411 | 4C01B7241D0E28EE00E2B509 /* Release */, 412 | ); 413 | defaultConfigurationIsVisible = 0; 414 | }; 415 | 4C01B7351D0E2FFE00E2B509 /* Build configuration list for PBXNativeTarget "lib1" */ = { 416 | isa = XCConfigurationList; 417 | buildConfigurations = ( 418 | 4C01B7361D0E2FFE00E2B509 /* Debug */, 419 | 4C01B7371D0E2FFE00E2B509 /* Release */, 420 | ); 421 | defaultConfigurationIsVisible = 0; 422 | }; 423 | /* End XCConfigurationList section */ 424 | }; 425 | rootObject = 4C01B7031D0E288300E2B509 /* Project object */; 426 | } 427 | -------------------------------------------------------------------------------- /lib6502.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /man/M6502_delete.3: -------------------------------------------------------------------------------- 1 | .so man3/lib6502.3 2 | -------------------------------------------------------------------------------- /man/M6502_disassemble.3: -------------------------------------------------------------------------------- 1 | .so man3/lib6502.3 2 | -------------------------------------------------------------------------------- /man/M6502_dump.3: -------------------------------------------------------------------------------- 1 | .so man3/lib6502.3 2 | -------------------------------------------------------------------------------- /man/M6502_getCallback.3: -------------------------------------------------------------------------------- 1 | .so man3/lib6502.3 2 | -------------------------------------------------------------------------------- /man/M6502_getVector.3: -------------------------------------------------------------------------------- 1 | .so man3/lib6502.3 2 | -------------------------------------------------------------------------------- /man/M6502_irq.3: -------------------------------------------------------------------------------- 1 | .so man3/lib6502.3 2 | -------------------------------------------------------------------------------- /man/M6502_new.3: -------------------------------------------------------------------------------- 1 | .so man3/lib6502.3 2 | -------------------------------------------------------------------------------- /man/M6502_nmi.3: -------------------------------------------------------------------------------- 1 | .so man3/lib6502.3 2 | -------------------------------------------------------------------------------- /man/M6502_reset.3: -------------------------------------------------------------------------------- 1 | .so man3/lib6502.3 2 | -------------------------------------------------------------------------------- /man/M6502_run.3: -------------------------------------------------------------------------------- 1 | .so man3/lib6502.3 2 | -------------------------------------------------------------------------------- /man/M6502_setCallback.3: -------------------------------------------------------------------------------- 1 | .so man3/lib6502.3 2 | -------------------------------------------------------------------------------- /man/M6502_setVector.3: -------------------------------------------------------------------------------- 1 | .so man3/lib6502.3 2 | -------------------------------------------------------------------------------- /man/lib6502.3: -------------------------------------------------------------------------------- 1 | .\" Copyright (c) 2005 Ian Piumarta 2 | .\" 3 | .\" Permission is hereby granted, free of charge, to any person 4 | .\" obtaining a copy of this software and associated documentation 5 | .\" files (the 'Software'), to deal in the Software without 6 | .\" restriction, including without limitation the rights to use, copy, 7 | .\" modify, merge, publish, distribute, and/or sell copies of the 8 | .\" Software, and to permit persons to whom the Software is furnished 9 | .\" to do so, provided that the above copyright notice(s) and this 10 | .\" permission notice appear in all copies of the Software and that 11 | .\" both the above copyright notice(s) and this permission notice 12 | .\" appear in supporting documentation. 13 | .\" 14 | .\" THE SOFTWARE IS PROVIDED 'AS IS'. USE ENTIRELY AT YOUR OWN RISK. 15 | .\" 16 | .\" last edited: 2005-11-02 01:18:07 by piumarta on margaux.local 17 | .\" 18 | .Dd October 31, 2005 19 | .Dt LIB6502 3 LOCAL 20 | .Os "" 21 | .\" ---------------------------------------------------------------- 22 | .Sh NAME 23 | .\" 24 | .Nm lib6502 25 | .Nd 6502 microprocessor emulator 26 | .\" ---------------------------------------------------------------- 27 | .Sh SYNOPSIS 28 | .\" 29 | .In stdint.h 30 | .In lib6502.h 31 | .Ft M6502 * 32 | .Fn M6502_new "M6502_Registers *registers" "M6502_Memory memory" "M6502_Callbacks *callbacks" 33 | .Ft void 34 | .Fn M6502_reset "M6502 *mpu" 35 | .Ft void 36 | .Fn M6502_nmi "M6502 *mpu" 37 | .Ft void 38 | .Fn M6502_irq "M6502 *mpu" 39 | .Ft uint16_t 40 | .Fn M6502_getVector "M6502 *mpu" "vector" 41 | .Ft uint16_t 42 | .Fn M6502_setVector "M6502 *mpu" "vector" "uint16_t address" 43 | .Ft M6502_Callback 44 | .Fn M6502_getCallback "M6502 *mpu" "type" "uint16_t address" 45 | .Ft M6502_Callback 46 | .Fn M6502_setCallback "M6502 *mpu" "type" "uint16_t address" "M6502_Callback callback" 47 | .Ft void 48 | .Fn M6502_run "M6502 *mpu" 49 | .Ft int 50 | .Fn M6502_disassemble "M6502 *mpu" "uint16_t address" "char buffer[64]" 51 | .Ft void 52 | .Fn M6502_dump "M6502 *mpu" "char buffer[64]" 53 | .Ft void 54 | .Fn M6502_delete "M6502 *mpu" 55 | .\" ---------------------------------------------------------------- 56 | .Sh DESCRIPTION 57 | .\" 58 | .Fn M6502_new 59 | creates an instance of a 6502 microprocessor. 60 | .Fn M6502_reset , 61 | .Fn M6502_nmi 62 | and 63 | .Fn M6502_irq 64 | place it into the states associated with the hardware signals for 65 | reset, non-maskable interrupt and interrupt request, respectively. 66 | The macros 67 | .Fn M6502_getVector 68 | and 69 | .Fn M6502_setVector 70 | read and write the vectors through which the processor jumps in 71 | response to the above signals. The macros 72 | .Fn M6502_getCallback 73 | and 74 | .Fn M6502_setVector 75 | read and write client-supplied functions that intercept accesses to 76 | memory. 77 | .Fn M6502_run 78 | begins emulated execution. 79 | .Fn M6502_dump 80 | and 81 | .Fn M6502_disassemble 82 | create human-readable representations of processor or memory state. 83 | .Fn M6502_delete 84 | frees all resources associated with a processor instance. Each of 85 | these functions and macros is described in more detail below. 86 | .Pp 87 | .Fn M6502_new 88 | returns a pointer to a 89 | .Fa M6502 90 | structure containing at least the following members: 91 | .Bd -literal 92 | struct _M6502 93 | { 94 | M6502_Registers *registers; /* processor state */ 95 | uint8_t *memory; /* memory image */ 96 | M6502_Callbacks *callbacks; /* r/w/x callbacks */ 97 | }; 98 | .Ed 99 | .Pp 100 | These members are initialised according to the supplied 101 | .Fa registers , 102 | .Fa memory 103 | and 104 | .Fa callbacks 105 | arguments. If a given argument is NULL, the corresponding member is 106 | initialised automatically with a suitable (non-NULL) value. 107 | .Pp 108 | The members of 109 | .Fa M6502 110 | are as follows: 111 | .Bl -tag -width ".Fa callbacks" 112 | .It Fa registers 113 | the processor state, containing all registers and condition codes. 114 | .It Fa memory 115 | a block of at least 64 kilobytes of storage containing the processor's 116 | memory. (An array type 117 | .Vt M6502_Memory, 118 | suitable for defining values to pass as the 119 | .Fa memory 120 | argument, is defined in the 121 | .In lib6502.h 122 | include file.) 123 | .It Fa callbacks 124 | a structure mapping processor memory accesses to client callback 125 | functions. 126 | .El 127 | .Pp 128 | Access to the contents of the 129 | .Fa registers 130 | and 131 | .Fa memory 132 | members can be made directly. 133 | The 134 | .Fa registers 135 | member is a 136 | .Vt M6502_Registers 137 | containing the following members: 138 | .Bd -literal 139 | struct _M6502_Registers 140 | { 141 | uint8_t a; /* accumulator */ 142 | uint8_t x; /* X index register */ 143 | uint8_t y; /* Y index register */ 144 | uint8_t p; /* processor status register */ 145 | uint8_t s; /* stack pointer */ 146 | uint16_t pc; /* program counter */ 147 | }; 148 | .Ed 149 | .Pp 150 | The 151 | .Fa memory 152 | member is an array of 153 | .Vt unsigned char 154 | and can be indexed directly. In addition, two convenience macros 155 | .Fn M6502_getVector 156 | and 157 | .Fn M6502_setVector 158 | provide access to the reset and interrupt vectors within 159 | .Fa memory . 160 | .Fn M6502_getVector 161 | returns the address stored in the named 162 | .Fa vector 163 | which must be precisely one of the following: 164 | .Bl -tag -width ".Dv RST" -offset indent 165 | .It Dv RST 166 | the reset vector. 167 | .It Dv NMI 168 | the non-maskable interrupt vector. 169 | .It Dv IRQ 170 | the interrupt request vector. 171 | .El 172 | .Pp 173 | .Fn M6502_setVector 174 | stores its 175 | .Fa address 176 | argument in the named 177 | .Fa vector 178 | and returns the new value. 179 | .Pp 180 | The 181 | .Fa callbacks 182 | member contains an opaque structure mapping processor memory accesses 183 | to client callback functions. Whenever the processor performs an 184 | access for which a corresponding entry exists in the the 185 | .Fa callbacks 186 | structure, the emulator suspends execution and invokes the callback to 187 | complete the operation. Each callback function should have a 188 | signature equivalent to: 189 | .Bd -ragged -offset indent 190 | int 191 | .Va callback 192 | (M6502 *mpu, uint16_t address, uint8_t data); 193 | .Ed 194 | .Pp 195 | The macros 196 | .Fn M6502_getCallback 197 | and 198 | .Fn M6502_setCallback 199 | read and write entries in the 200 | .Fa callbacks 201 | structure. These macros identify a unique memory access operation 202 | from the specified 203 | .Fa address 204 | on which it operates and 205 | .Fa type 206 | of access involved. The 207 | .Fa type 208 | argument must be one of the following: 209 | .Bl -tag -width ".Dv write" 210 | .It Dv read 211 | the 212 | .Fa callback 213 | is invoked when the processor attempts to read from the 214 | given address. The emulator passes the effective address of the 215 | operation to the callback in its 216 | .Fa address 217 | argument. (The 218 | .Fa data 219 | argument is undefined.) The value returned by the callback will be 220 | used by the emulator as the result of the read operation. 221 | .It Dv write 222 | the 223 | .Fa callback 224 | is invoked when the processor attempts to write to the 225 | given address. The emulator passes the effective address of the 226 | operation to the callback in its 227 | .Fa address 228 | argument and the byte being written in the 229 | .Fa data 230 | argument. The emulator will not perform the write operation before 231 | invoking the callback; if the write should complete, the callback must 232 | modify the processor's 233 | .Fa memory 234 | explicitly. The valued returned from the callback is ignored. 235 | .It Dv call 236 | the 237 | .Fa callback 238 | is invoked when the processor attempts to transfer control to the 239 | given address by any instruction other than a relative branch. The 240 | emulator passes the destination address to the callback in its 241 | .Fa address 242 | argument and the instruction that initiated the control transfer in 243 | its 244 | .Fa data 245 | argument (one of JMP, JSR, BRK, RTS or RTI). If the callback returns 246 | zero (the callback refuses to handle the operation) the emulator will 247 | allow the operation to complete as normal. If the callback returns a 248 | non-zero address (indicating that the callback has handled the 249 | operation internally) the emulator will transfer control to that 250 | address. 251 | .El 252 | .Pp 253 | .Fn M6502_getCallback 254 | returns zero if there is no callback associated with the given 255 | .Fa type 256 | and 257 | .Fa address . 258 | Passing zero as the 259 | .Fa callback 260 | argument of 261 | .Fn M6502_setCallback 262 | removes any callback that might have been associated with 263 | .Fa type 264 | and 265 | .Fa address . 266 | .Pp 267 | .Fn M6502_run 268 | emulates processor execution in the given 269 | .Fa mpu 270 | by repeatedly fetching the instruction addressed by 271 | .Fa pc 272 | and dispatching to it. This function normally never returns. 273 | .Pp 274 | .Fn M6502_dump 275 | writes a (NUL-terminated) symbolic representation of the processor's 276 | internal state into the supplied 277 | .Fa buffer . 278 | Typical output resembles: 279 | .Bd -literal -offset indent 280 | PC=1010 SP=01FE A=0A X=5B Y=00 P=D1 NV-B---C 281 | .Ed 282 | .Pp 283 | .Fn M6502_disassemble 284 | writes a (NUL-terminated) symbolic representation of the instruction 285 | in the processor's memory at the given 286 | .Fa address 287 | into the supplied 288 | .Fa buffer . 289 | It returns the size (in bytes) of the instruction. (In other words, 290 | the amount by which 291 | .Fa address 292 | should be incremented to arrive at the next instruction.) 293 | Typical output resembles: 294 | .Bd -literal -offset indent 295 | 1009 cpx #5B 296 | .Ed 297 | .Pp 298 | (The 299 | .Fa buffer 300 | arguments are oversized to allow for future expansion.) 301 | .Pp 302 | .Fn M6502_delete 303 | frees the resources associated with the given 304 | .Fa mpu. 305 | Any members that were allocated implicitly (passed as NULL to 306 | .Fn M6502_new ) 307 | are deallocated. Members that were initialised from non-NULL 308 | arguments are not deallocated. 309 | .\" ---------------------------------------------------------------- 310 | .Sh IMPLEMENTATION NOTES 311 | .\" 312 | You can share the 313 | .Fa memory 314 | and 315 | .Fa callbacks 316 | members of 317 | .Vt M6502 318 | between multiple instances to simulate multiprocessor hardware. 319 | .\" ---------------------------------------------------------------- 320 | .Sh RETURN VALUES 321 | .\" 322 | .Fn M6502_new 323 | returns a pointer to a 324 | .Vt M6502 325 | structure. 326 | .Fn M6502_getVector 327 | and 328 | .Fn M6502_setVector 329 | return the contents of the given 330 | .Fa vector . 331 | .Fn M6502_getCallback 332 | and 333 | .Fn M6502_setCallback 334 | return the 335 | .Vt M6502_Callback 336 | function associated with the given 337 | .Fa address 338 | and access 339 | .Fa type . 340 | .Fn M6502_disassemble 341 | returns the size (in bytes) of the instruction at the given 342 | .Fa address . 343 | .Fn M6502_reset , 344 | .Fn M6502_nmi , 345 | .Fn M6502_irq , 346 | .Fn M6502_run , 347 | .Fn M6502_dump 348 | and 349 | .Fn M6502_delete 350 | don't return anything (unless you forgot to include 351 | .In lib6502.h ) . 352 | .\" ---------------------------------------------------------------- 353 | .Sh EXAMPLES 354 | .\" 355 | The following program creates a 6502 processor, sets up callbacks for 356 | printing characters and halting after a BRK instruction, stores a 357 | program into memory that prints the alphabet, disassembles the program 358 | on stdout, and then executes the program. 359 | .Bd -literal -offset indent -compact 360 | 361 | #include 362 | #include 363 | #include 364 | 365 | #include "lib6502.h" 366 | 367 | #define WRCH 0xFFEE 368 | 369 | int wrch(M6502 *mpu, uint16_t address, uint8_t data) 370 | { 371 | int pc; 372 | putchar(mpu->registers->a); 373 | pc = mpu->memory[++mpu->registers->s + 0x100]; 374 | pc |= mpu->memory[++mpu->registers->s + 0x100] << 8; 375 | return pc + 1; /* JSR pushes next insn addr - 1 */ 376 | } 377 | 378 | int done(M6502 *mpu, uint16_t address, uint8_t data) 379 | { 380 | char buffer[64]; 381 | M6502_dump(mpu, buffer); 382 | printf("\\nBRK instruction\\n%s\\n", buffer); 383 | exit(0); 384 | } 385 | 386 | int main(int argc, char **argv) 387 | { 388 | M6502 *mpu = M6502_new(0, 0, 0); 389 | unsigned pc = 0x1000; 390 | 391 | mpu->callbacks->call[WRCH] = wrch; /* write character */ 392 | mpu->callbacks->call[0000] = done; /* reached after BRK */ 393 | 394 | # define gen1(X) (mpu->memory[pc++] = (uint8_t)(X)) 395 | # define gen2(X,Y) gen1(X); gen1(Y) 396 | # define gen3(X,Y,Z) gen1(X); gen2(Y,Z) 397 | 398 | gen2(0xA2, 'A' ); /* LDX #'A' */ 399 | gen1(0x8A ); /* TXA */ 400 | gen3(0x20,0xEE,0xFF); /* JSR FFEE */ 401 | gen1(0xE8 ); /* INX */ 402 | gen2(0xE0, 'Z'+1 ); /* CPX #'Z'+1 */ 403 | gen2(0xD0, -9 ); /* BNE 1002 */ 404 | gen2(0xA9, '\\n' ); /* LDA #'\\n' */ 405 | gen3(0x20,0xEE,0xFF); /* JSR FFEE */ 406 | gen2(0x00,0x00 ); /* BRK */ 407 | 408 | { 409 | uint16_t ip = 0x1000; 410 | while (ip < pc) 411 | { 412 | char insn[64]; 413 | ip += M6502_disassemble(mpu, ip, insn); 414 | printf("%04X %s\\n", ip, insn); 415 | } 416 | } 417 | 418 | M6502_setVector(mpu, RST, 0x1000); 419 | 420 | M6502_reset(mpu); 421 | M6502_run(mpu); 422 | M6502_delete(mpu); 423 | 424 | return 0; 425 | } 426 | .Ed 427 | .\" ---------------------------------------------------------------- 428 | .Sh DIAGNOSTICS 429 | .\" 430 | If 431 | .Fn M6502_new 432 | cannot allocate sufficient memory it prints "out of memory" to stderr 433 | and exits with a non-zero status. 434 | .Pp 435 | If 436 | .Fn M6502_run 437 | encounters an illegal or undefined instruction, it prints "undefined 438 | instruction" and the processor's state to stderr, then exits with a 439 | non-zero status. 440 | .\" ---------------------------------------------------------------- 441 | .Sh COMPATIBILITY 442 | .\" 443 | M6502 is a generic name. The initial letter is mandated by C naming 444 | conventions and chosen in deference to MOS Technology, the original 445 | designers of the processor. To the best of my knowledge the 'M' 446 | prefix was never stamped on a physical 6502. 447 | .Pp 448 | The emulator implements the CMOS version of the processor (NMOS bugs 449 | in effective address calculations involving page boundaries are 450 | corrected) but does not tolerate the execution of undefined 451 | instructions (which were all no-ops in the first-generation CMOS 452 | hardware). It would be nice to support the several alternative 453 | instruction sets (model-specific undocumented instructions in NMOS 454 | models, and various documented extensions in the later CMOS models) 455 | but there are currently no plans to do so. 456 | .Pp 457 | The emulated 6502 will run much faster than real hardware on any 458 | modern computer. The fastest 6502 hardware available at the time of 459 | writing has a clock speed of 14 MHz. On a 2 GHz PowerPC, the emulated 460 | 6502 runs at almost 300 MHz. 461 | .\" ---------------------------------------------------------------- 462 | .Sh SEE ALSO 463 | .\" 464 | .Xr run6502 1 465 | .Pp 466 | For development tools, documentation and source code: 467 | .Pa http://6502.org 468 | .\" ---------------------------------------------------------------- 469 | .Sh AUTHORS 470 | .\" 471 | The software and manual pages were written by Ian Piumarta. 472 | .Pp 473 | The software is provided as-is, with absolutely no warranty, in the 474 | hope that you will enjoy and benefit from it. You may use (entirely 475 | at your own risk) and redistribute it under the terms of a very 476 | liberal license that does not seek to restrict your rights in any way 477 | (unlike certain so-called 'open source' licenses that significantly 478 | limit your freedom in the name of 'free' software that is, ultimately, 479 | anything but free). See the file COPYING for details. 480 | .\" ---------------------------------------------------------------- 481 | .Sh BUGS 482 | .\" 483 | .Fn M6502_getVector 484 | and 485 | .Fn M6502_setVector 486 | evaluate their arguments more than once. 487 | .Pp 488 | The out-of-memory condition and attempted execution of 489 | illegal/undefined instructions should not be fatal errors. 490 | .Pp 491 | There is no way to limit the duration of execution within 492 | .Fn M6502_run 493 | to a certain number of instructions or cycles. 494 | .Pp 495 | The emulator should support some means of implicit interrupt 496 | generation, either by polling or in response to (Unix) signals. 497 | .Pp 498 | The 499 | .Sx COMPATIBILITY 500 | section in this manual page has been diverted from its legitimate 501 | purpose. 502 | .Pp 503 | The plural of 'callback' really aught to be 'callsback'. 504 | .Pp 505 | Please send bug reports (and feature requests) to the author at: 506 | firstName (at) lastName (dot) com. (See 507 | .Sx AUTHORS 508 | above for suitable values of firstName and lastName.) 509 | -------------------------------------------------------------------------------- /man/run6502.1: -------------------------------------------------------------------------------- 1 | .\" Copyright (c) 2005 Ian Piumarta 2 | .\" 3 | .\" Permission is hereby granted, free of charge, to any person 4 | .\" obtaining a copy of this software and associated documentation 5 | .\" files (the 'Software'), to deal in the Software without 6 | .\" restriction, including without limitation the rights to use, copy, 7 | .\" modify, merge, publish, distribute, and/or sell copies of the 8 | .\" Software, and to permit persons to whom the Software is furnished 9 | .\" to do so, provided that the above copyright notice(s) and this 10 | .\" permission notice appear in all copies of the Software and that 11 | .\" both the above copyright notice(s) and this permission notice 12 | .\" appear in supporting documentation. 13 | .\" 14 | .\" THE SOFTWARE IS PROVIDED 'AS IS'. USE ENTIRELY AT YOUR OWN RISK. 15 | .\" 16 | .\" last edited: 2023-01-01 22:22:22 by brucehoult on m1-mini.local 17 | .\" 18 | .Dd October 31, 2005 19 | .Dt RUN6502 1 LOCAL 20 | .Os "" 21 | .\" ---------------------------------------------------------------- 22 | .Sh NAME 23 | .\" 24 | .Nm run6502 25 | .Nd execute a 6502 microprocessor program 26 | .\" ---------------------------------------------------------------- 27 | .Sh SYNOPSIS 28 | .\" 29 | .Nm run6502 30 | .Op Ar option ... 31 | .Nm run6502 32 | .Op Ar option ... 33 | .Fl B 34 | .Op Ar 35 | .\" ---------------------------------------------------------------- 36 | .Sh DESCRIPTION 37 | The 38 | .Nm run6502 39 | command emulates the execution of a 6502 microprocessor. It creates a 40 | memory image from the contents of one or more files on the command 41 | line and then simulates a power-on hardware reset to begin execution. 42 | .Pp 43 | In its first form, 44 | .Nm run6502 45 | emulates an embedded 6502 processor with 64 kilobytes of RAM, no 46 | memory-mapped hardware, and no input-output capabilities. Limited 47 | interaction with the machine is possible only through the 48 | .Fl G , M 49 | and 50 | .Fl P 51 | options. 52 | .Pp 53 | In its second form (with the 54 | .Fl B 55 | option) 56 | .Nm run6502 57 | provides minimal emulation of Acorn 'BBC Model B' hardware with 32 58 | kilobytes of RAM, 16 kilobytes of paged language ROMs, and 16 59 | kilobytes of operating system ROM. A few MOS calls are intercepted to 60 | provide keyboard input and screen output via stdin and stdout. 61 | Switching between the sixteen paged read-only memory banks is also 62 | supported by the usual memory-mapped control register. Any 63 | .Ar file 64 | arguments after the 65 | .Fl B 66 | are loaded into successive paged ROM banks (starting at 15 and working 67 | down towards 0) before execution begins. 68 | .\" ---------------------------------------------------------------- 69 | .Ss Options 70 | .\" 71 | .Bl -tag -width indent 72 | .It Fl B 73 | enable minimal Acorn 'BBC Model B' hardware emulation: 74 | .Bl -bullet 75 | .It 76 | the contents of memory between addresses 0x8000 and 0xBFFF are copied 77 | into paged ROM number 0; 78 | .It 79 | memory between 0x8000 and 0xBFFF becomes bank-switchable between 80 | sixteen different ROM images; 81 | .It 82 | the memory-mapped pages ('FRED', 'JIM' and 'SHEILA') between 0xFC00 83 | and 0xFEFF are initialised to harmless values; 84 | .It 85 | the upper half of the address space is write-protected; and 86 | .It 87 | callbacks are installed on several OS entry points to provide 88 | input-output via stdin and stdout. 89 | .El 90 | .Pp 91 | Any remaining non-option arguments on the command line will name files 92 | to be loaded successively into paged ROMs, starting at 15 and working 93 | downwards towards 0. 94 | .It Fl c 95 | Print total number of instructions executed and cycles to stderr at program exit. 96 | .It Fl d Ar addr Ar end 97 | dump memory from the address 98 | .Ar addr 99 | (given in hexadecimal) up to (but not including) 100 | .Ar end . 101 | The 102 | .Ar end 103 | argument is either an absolute address or a relative address specified 104 | as a '+' character followed by the number (in hexadecimal) of bytes to 105 | dump. In other words, the following two options dump the same region 106 | of memory: 107 | .Bd -ragged -offset indent 108 | .Fl d 109 | 8000 C000 110 | .Ed 111 | .Bd -ragged -offset indent -compact 112 | .Fl d 113 | 8000 +4000 114 | .Ed 115 | .Pp 116 | The format of the dump cannot currently be modified and consists of 117 | the current address followed by one, two or three hexadecimal bytes, 118 | and a symbolic representation of the instruction at that address. 119 | .It Fl G Ar addr 120 | arrange that subroutine calls to 121 | .Ar addr 122 | will behave as if there were an implementation of 123 | .Xr getchar 3 124 | at that address, reading a character from stdin and returning it in 125 | the accumulator. 126 | .It Fl h 127 | print a summary of the available options and then exit. 128 | .It Fl I Ar addr 129 | set the IRQ (interrupt request) vector (the address to which the 130 | processor will transfer control upon execution of a BRK instruction). 131 | Setting this address to zero will cause execution to halt (and the 132 | emulator to exit) when a BRK instruction is encountered. 133 | .It Fl i Ar addr Ar file 134 | Load 135 | .Ar file 136 | into the memory image at the address 137 | .Ar addr 138 | (in hexadecimal), skipping over any initial '#!' interpreter line. 139 | .It Fl l Ar addr Ar file 140 | Load 141 | .Ar file 142 | into the memory image at the address 143 | .Ar addr 144 | (in hexadecimal). 145 | .It Fl M Ar addrio 146 | arrange that memory reads from address 147 | .Ar addrio 148 | will return the next character on stdin (blocking if necessary), and 149 | memory writes to 150 | .Ar addrio 151 | will send the value written to stdout. 152 | .It Fl N Ar addr 153 | set the NMI (non-maskable interrupt) vector to 154 | .Ar addr . 155 | .It Fl P Ar addr 156 | arrange that subroutine calls to 157 | .Ar addr 158 | will behave as if there were an implementation of 159 | .Xr putchar 3 160 | at that address, writing the contents of the accumulator to stdout. 161 | .It Fl R Ar addr 162 | set the RST (hardware reset) vector. The processor will transfer 163 | control to this address when emulated execution begins. 164 | .It Fl s Ar addr Ar end Ar file 165 | save the contents of memory from the address 166 | .Ar addr 167 | up to 168 | .Ar end 169 | (exclusive) to the given 170 | .Ar file . 171 | As with the 172 | .Fl d 173 | option, 174 | .Ar end 175 | can be absolute or '+' followed by a byte count. 176 | .It Fl v 177 | print version information and then exit. 178 | .It Fl X Ar addr 179 | arrange that any transfer of control to the address 180 | .Ar addr 181 | will cause an immediate exit with zero exit status. 182 | .It Fl x 183 | exit immediately. (Useful after 184 | .Fl d 185 | or when 186 | .Nm run6502 187 | is being used as a trivial 'image editor', with several 188 | .Fl l 189 | options followed by 190 | .Fl s 191 | and 192 | .Fl x . ) 193 | .It Ar 194 | following a 195 | .Fl B 196 | option, load one or more ROM image 197 | files 198 | into successive paged ROM slots. Other than the paging aspect, this 199 | is equivalent to: 200 | .Bd -ragged -offset indent 201 | .Fl l Ar 8000 Ar image 202 | .Ed 203 | .El 204 | .\" ---------------------------------------------------------------- 205 | .Sh EXAMPLES 206 | .\" 207 | .Ss A Very Simple Program 208 | The 209 | .Xr perl 1 210 | command can be used to create a binary file from hexadecimal input: 211 | .Bd -literal 212 | echo a2418a20eeffe8e05bd0f7a90a20eeff00 | 213 | perl -e 'print pack "H*",' > temp.img 214 | .Ed 215 | .Pp 216 | The file can be loaded and executed with: 217 | .Bd -literal 218 | run6502 -l 1000 temp.img -R 1000 -P FFEE -X 0 219 | .Ed 220 | .Pp 221 | The contents of the file can be inspected symbolically with: 222 | .Bd -literal 223 | run6502 -l 1000 temp.img -d 1000 +12 224 | .Ed 225 | .Pp 226 | The options passed to 227 | .Nm run6502 228 | in the above examples have the following effects: 229 | .Bl -tag -width offset 230 | .It \-l 1000 temp.img 231 | loads the file 232 | .Pa temp.img 233 | into memory at address 0x8000. 234 | .It \-R 1000 235 | sets the reset vector (the address of first instruction to be executed 236 | after 'power on') to 0x1000. 237 | .It \-P FFEE 238 | arranges for calls to address 0xFFEE to behave as if there were an 239 | implementation of 240 | .Xr putchar 3 241 | at that address. 242 | .It \-X 0 243 | arranges for transfers of control to address 0 to exit from the 244 | emulator. This works in the above example because the final 'BRK' 245 | instruction causes an implicit subroutine call through an 246 | uninitialised interrupt vector to location 0. To see this 247 | instruction... 248 | .It \-d 1000 +12 249 | disassembles 18 bytes of memory at address 0x8000. 250 | .El 251 | .Ss Standalone Images 252 | The 253 | .Fl i 254 | option is designed for use in the 'interpreter command' appearing on 255 | the first line of an executable script. Adding the line 256 | .Bd -literal 257 | #!run6502 -R 1000 -P FFEE -X 0 -i 1000 258 | .Ed 259 | .Pp 260 | (with no leading spaces and a single trailing newline character) 261 | to the 262 | .Pa temp.img 263 | file from the first example turns it into a script. If the file is 264 | made executable with 265 | .Bd -literal 266 | chmod +x temp.img 267 | .Ed 268 | .Pp 269 | it can be run like a standalone program: 270 | .Bd -literal 271 | ./temp.img 272 | .Ed 273 | .Ss A Very Complex Program 274 | Consider a pair of files named 275 | .Pa os1.2 276 | and 277 | .Pa basic2 278 | containing (legally-acquired, of course) ROM images of Acorn MOS 1.2 279 | and BBC Basic 2. The following command loads each of the images into 280 | memory at the appropriate address, cleans up the regions of memory 281 | containing memory-mapped i/o on the BBC computer, saves a snapshot of 282 | the entire memory to the file 283 | .Pa image 284 | and then exits: 285 | .Bd -literal 286 | run6502 -l C000 os1.2 -l 8000 basic2 -B -s0 +10000 image -x 287 | .Ed 288 | .Pp 289 | Running the generated image with 290 | .Bd -literal 291 | run6502 image 292 | .Ed 293 | .Pp 294 | will cold-start the emulated hardware, run the OS for a while, and 295 | then drop into the language ROM. Basic programs can then be entered, 296 | edited and run from the terminal. 297 | .Pp 298 | More details are given in the 299 | .Pa README 300 | file available in the 301 | .Pa examples 302 | directory of the distribution. 303 | .Ss Exercises 304 | Create a standalone image (one that can be run as a program, with 305 | a '#!' interpreter line at the beginning) that contains Basic2 and 306 | OS1.2 (as described above). This image should be no larger than 32K 307 | (memory below 0x8000, which would be full of zeroes, should not appear 308 | in the image file). 309 | .\" ---------------------------------------------------------------- 310 | .Sh DIAGNOSTICS 311 | .\" 312 | If nothing goes wrong, none. Otherwise lots. They should be 313 | self-explanatory. I'm too lazy to enumerate them. 314 | .\" ---------------------------------------------------------------- 315 | .Sh COMPATIBILITY 316 | .\" 317 | See 318 | .Xr lib6502 3 319 | for a discussion of the emulated instruction set. 320 | .\" ---------------------------------------------------------------- 321 | .Sh SEE ALSO 322 | .\" 323 | .Xr lib6502 3 324 | .Pp 325 | The file 326 | .Pa examples/README 327 | in the lib6502 distribution. (Depending on your system this may be 328 | installed in 329 | .Pa /usr/doc/lib6502 , 330 | .Pa /usr/local/doc/lib6502 , 331 | .Pa /usr/share/doc/lib6502 , 332 | or similar.) 333 | .Pp 334 | .Pa http://piumarta.com/software/lib6502 335 | for updates and documentation. 336 | .Pp 337 | .Pa http://6502.org 338 | for lots of 6502-related resources. 339 | .\" ---------------------------------------------------------------- 340 | .Sh AUTHORS 341 | .\" 342 | The software and manual pages were written by 343 | .An "Ian Piumarta" . 344 | .Pp 345 | The software is provided as-is, with absolutely no warranty, in the 346 | hope that you will enjoy and benefit from it. You may use (entirely 347 | at your own risk) and redistribute it under the terms of a very 348 | liberal license that does not seek to restrict your rights in any way 349 | (unlike certain so-called 'open source' licenses that significantly 350 | limit your freedom in the name of 'free' software that is, ultimately, 351 | anything but free). See the file COPYING for details. 352 | .\" ---------------------------------------------------------------- 353 | .Sh BUGS 354 | .\" 355 | .Bl -bullet 356 | .It 357 | Options must appear one at a time. 358 | .It 359 | Any attempt (in a load or save operation) to transfer data beyond 360 | 0xFFFF is silently truncated at the end of memory. 361 | .It 362 | There is no way to specify the slot into which a ROM image should be 363 | loaded, other than implicitly according to the order of arguments on 364 | the command line. 365 | .It 366 | Execution can only be started via the emulated power-up reset. There 367 | is no support for 'warm-starting' execution in an image at an 368 | arbitrary address. 369 | .It 370 | Even though the emulator fully supports them, there is no way to 371 | artificially generate a hardware interrupt request, non-maskable 372 | interrupt, or reset condition. If you need these, read 373 | .Xr lib6502 3 374 | and write your own shell. 375 | .It 376 | The Acorn 'BBC Model B' hardware emulation is totally lame. 377 | .El 378 | .Pp 379 | Please send bug reports (and feature requests) to the author at: 380 | firstName (at) lastName (dot) com. (See 381 | .Sx AUTHORS 382 | above for suitable values of firstName and lastName.) 383 | -------------------------------------------------------------------------------- /run6502.c: -------------------------------------------------------------------------------- 1 | /* run6502.c -- 6502 emulator shell -*- C -*- */ 2 | 3 | /* Copyright (c) 2005 Ian Piumarta 4 | * 5 | * All rights reserved. 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a 8 | * copy of this software and associated documentation files (the 'Software'), 9 | * to deal in the Software without restriction, including without limitation 10 | * the rights to use, copy, modify, merge, publish, distribute, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, provided that the above copyright notice(s) and this 13 | * permission notice appear in all copies of the Software and that both the 14 | * above copyright notice(s) and this permission notice appear in supporting 15 | * documentation. 16 | * 17 | * THE SOFTWARE IS PROVIDED 'AS IS'. USE ENTIRELY AT YOUR OWN RISK. 18 | */ 19 | 20 | /* Last edited: 2005-11-02 01:18:58 by piumarta on margaux.local 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "config.h" 31 | #include "lib6502.h" 32 | 33 | #define VERSION PACKAGE_NAME " " PACKAGE_VERSION " " PACKAGE_COPYRIGHT 34 | 35 | typedef uint8_t byte; 36 | typedef uint16_t word; 37 | 38 | static char *program= 0; 39 | 40 | static byte bank[0x10][0x4000]; 41 | 42 | static jmp_buf exit_6502_env; 43 | 44 | 45 | void fail(const char *fmt, ...) 46 | { 47 | va_list ap; 48 | fflush(stdout); 49 | va_start(ap, fmt); 50 | vfprintf(stderr, fmt, ap); 51 | va_end(ap); 52 | fprintf(stderr, "\n"); 53 | exit(1); 54 | } 55 | 56 | 57 | void pfail(const char *msg) 58 | { 59 | fflush(stdout); 60 | perror(msg); 61 | exit(1); 62 | } 63 | 64 | 65 | #define rts \ 66 | { \ 67 | word pc; \ 68 | pc = mpu->memory[++mpu->registers->s + 0x100]; \ 69 | pc |= mpu->memory[++mpu->registers->s + 0x100] << 8; \ 70 | return pc + 1; \ 71 | } 72 | 73 | 74 | int osword(M6502 *mpu, word address, byte data) 75 | { 76 | byte *params= mpu->memory + mpu->registers->x + (mpu->registers->y << 8); 77 | 78 | switch (mpu->registers->a) 79 | { 80 | case 0x00: /* input line */ 81 | /* On entry: XY+0,1=>string area, 82 | * XY+2=maximum line length, 83 | * XY+3=minimum acceptable ASCII value, 84 | * XY+4=maximum acceptable ASCII value. 85 | * On exit: Y is the line length (excluding CR), 86 | * C is set if Escape terminated input. 87 | */ 88 | { 89 | word offset= params[0] + (params[1] << 8); 90 | byte *buffer= mpu->memory + offset; 91 | byte length= params[2], minVal= params[3], maxVal= params[4], b= 0; 92 | if (!fgets(buffer, length, stdin)) 93 | { 94 | putchar('\n'); 95 | longjmp(exit_6502_env, 1); 96 | } 97 | for (b= 0; b < length; ++b) 98 | if ((buffer[b] < minVal) || (buffer[b] > maxVal) || ('\n' == buffer[b])) 99 | break; 100 | buffer[b]= 13; 101 | mpu->registers->y= b; 102 | mpu->registers->p &= 0xFE; 103 | break; 104 | } 105 | 106 | default: 107 | { 108 | char state[64]; 109 | M6502_dump(mpu, state); 110 | fflush(stdout); 111 | fprintf(stderr, "\nOSWORD %s\n", state); 112 | fail("ABORT"); 113 | } 114 | break; 115 | } 116 | 117 | rts; 118 | } 119 | 120 | 121 | int osbyte(M6502 *mpu, word address, byte data) 122 | { 123 | switch (mpu->registers->a) 124 | { 125 | case 0x7A: /* perform keyboard scan */ 126 | mpu->registers->x= 0x00; 127 | break; 128 | 129 | case 0x7E: /* acknowledge detection of escape condition */ 130 | return 1; 131 | break; 132 | 133 | case 0x82: /* read machine higher order address */ 134 | mpu->registers->y= 0x00; 135 | mpu->registers->x= 0x00; 136 | break; 137 | 138 | case 0x83: /* read top of OS ram address (OSHWM) */ 139 | mpu->registers->y= 0x0E; 140 | mpu->registers->x= 0x00; 141 | break; 142 | 143 | case 0x84: /* read bottom of display ram address */ 144 | mpu->registers->y= 0x80; 145 | mpu->registers->x= 0x00; 146 | break; 147 | 148 | case 0x89: /* motor control */ 149 | break; 150 | 151 | case 0xDA: /* read/write number of items in vdu queue (stored at 0x026A) */ 152 | return 0; 153 | break; 154 | 155 | default: 156 | { 157 | char state[64]; 158 | M6502_dump(mpu, state); 159 | fflush(stdout); 160 | fprintf(stderr, "\nOSBYTE %s\n", state); 161 | fail("ABORT"); 162 | } 163 | break; 164 | } 165 | 166 | rts; 167 | } 168 | 169 | 170 | int oscli(M6502 *mpu, word address, byte data) 171 | { 172 | byte *params= mpu->memory + mpu->registers->x + (mpu->registers->y << 8); 173 | char command[1024], *ptr= command; 174 | while (('*' == *params) || (' ' == *params)) 175 | ++params; 176 | while (13 != *params) 177 | *ptr++= *params++; 178 | *ptr= '\0'; 179 | system(command); 180 | rts; 181 | } 182 | 183 | 184 | int oswrch(M6502 *mpu, word address, byte data) 185 | { 186 | switch (mpu->registers->a) 187 | { 188 | case 0x0C: 189 | fputs("\033[2J\033[H", stdout); 190 | break; 191 | 192 | default: 193 | putchar(mpu->registers->a); 194 | break; 195 | } 196 | fflush(stdout); 197 | rts; 198 | } 199 | 200 | 201 | static int writeROM(M6502 *mpu, word address, byte value) 202 | { 203 | return 0; 204 | } 205 | 206 | 207 | static int bankSelect(M6502 *mpu, word address, byte value) 208 | { 209 | memcpy(mpu->memory + 0x8000, bank[value & 0x0F], 0x4000); 210 | return 0; 211 | } 212 | 213 | 214 | static int doBtraps(int argc, char **argv, M6502 *mpu) 215 | { 216 | unsigned addr; 217 | 218 | /* Acorn Model B ROM and memory-mapped IO */ 219 | 220 | for (addr= 0x8000; addr <= 0xFBFF; ++addr) mpu->callbacks->write[addr]= writeROM; 221 | for (addr= 0xFC00; addr <= 0xFEFF; ++addr) mpu->memory[addr]= 0xFF; 222 | for (addr= 0xFE30; addr <= 0xFE33; ++addr) mpu->callbacks->write[addr]= bankSelect; 223 | for (addr= 0xFE40; addr <= 0xFE4F; ++addr) mpu->memory[addr]= 0x00; 224 | for (addr= 0xFF00; addr <= 0xFFFF; ++addr) mpu->callbacks->write[addr]= writeROM; 225 | 226 | /* anything already loaded at 0x8000 appears in bank 0 */ 227 | 228 | memcpy(bank[0x00], mpu->memory + 0x8000, 0x4000); 229 | 230 | /* fake a few interesting OS calls */ 231 | 232 | # define trap(vec, addr, func) mpu->callbacks->call[addr]= (func) 233 | trap(0x020C, 0xFFF1, osword); 234 | trap(0x020A, 0xFFF4, osbyte); 235 | //trap(0x0208, 0xFFF7, oscli ); /* enable this to send '*COMMAND's to system(3) :-) */ 236 | trap(0x020E, 0xFFEE, oswrch); 237 | trap(0x020E, 0xE0A4, oswrch); /* NVWRCH */ 238 | #undef trap 239 | 240 | return 0; 241 | } 242 | 243 | 244 | static void usage(int status) 245 | { 246 | FILE *stream= status ? stderr : stdout; 247 | fprintf(stream, VERSION"\n"); 248 | fprintf(stream, "please send bug reports to: %s\n", PACKAGE_BUGREPORT); 249 | fprintf(stream, "\n"); 250 | fprintf(stream, "usage: %s [option ...]\n", program); 251 | fprintf(stream, " %s [option ...] -B [image ...]\n", program); 252 | fprintf(stream, " -B -- minimal Acorn 'BBC Model B' compatibility\n"); 253 | fprintf(stream, " -c -- print total number of instructions and cycles to stderr\n"); 254 | fprintf(stream, " -d addr last -- dump memory between addr and last\n"); 255 | fprintf(stream, " -G addr -- emulate getchar(3) at addr\n"); 256 | fprintf(stream, " -h -- help (print this message)\n"); 257 | fprintf(stream, " -I addr -- set IRQ vector\n"); 258 | fprintf(stream, " -l addr file -- load file at addr\n"); 259 | fprintf(stream, " -M addr -- emulate memory-mapped stdio at addr\n"); 260 | fprintf(stream, " -N addr -- set NMI vector\n"); 261 | fprintf(stream, " -P addr -- emulate putchar(3) at addr\n"); 262 | fprintf(stream, " -R addr -- set RST vector\n"); 263 | fprintf(stream, " -s addr last file -- save memory from addr to last in file\n"); 264 | fprintf(stream, " -v -- print version number then exit\n"); 265 | fprintf(stream, " -X addr -- terminate emulation if PC reaches addr\n"); 266 | fprintf(stream, " -x -- exit wihout further ado\n"); 267 | fprintf(stream, " image -- '-l 8000 image' in available ROM slot\n"); 268 | fprintf(stream, "\n"); 269 | fprintf(stream, "'last' can be an address (non-inclusive) or '+size' (in bytes)\n"); 270 | exit(status); 271 | } 272 | 273 | 274 | static int doHelp(int argc, char **argv, M6502 *mpu) 275 | { 276 | usage(0); 277 | return 0; 278 | } 279 | 280 | 281 | static int doVersion(int argc, char **argv, M6502 *mpu) 282 | { 283 | puts(VERSION); 284 | exit(0); 285 | return 0; 286 | } 287 | 288 | 289 | static unsigned long htol(char *hex) 290 | { 291 | char *end; 292 | unsigned long l= strtol(hex, &end, 16); 293 | if (*end) fail("bad hex number: %s", hex); 294 | return l; 295 | } 296 | 297 | 298 | static int loadInterpreter(M6502 *mpu, word start, const char *path) 299 | { 300 | FILE *file= 0; 301 | int count= 0; 302 | byte *memory= mpu->memory + start; 303 | size_t max= 0x10000 - start; 304 | int c= 0; 305 | 306 | if ((!(file= fopen(path, "r"))) || ('#' != fgetc(file)) || ('!' != fgetc(file))) 307 | return 0; 308 | while ((c= fgetc(file)) >= ' ') 309 | ; 310 | while ((count= fread(memory, 1, max, file)) > 0) 311 | { 312 | memory += count; 313 | max -= count; 314 | } 315 | fclose(file); 316 | return 1; 317 | } 318 | 319 | 320 | static int save(M6502 *mpu, word address, unsigned length, const char *path) 321 | { 322 | FILE *file= 0; 323 | int count= 0; 324 | if (!(file= fopen(path, "w"))) 325 | return 0; 326 | while ((count= fwrite(mpu->memory + address, 1, length, file))) 327 | { 328 | address += count; 329 | length -= count; 330 | } 331 | fclose(file); 332 | return 1; 333 | } 334 | 335 | 336 | static int load(M6502 *mpu, word address, const char *path) 337 | { 338 | FILE *file= 0; 339 | int count= 0; 340 | size_t max= 0x10000 - address; 341 | if (!(file= fopen(path, "r"))) 342 | return 0; 343 | while ((count= fread(mpu->memory + address, 1, max, file)) > 0) 344 | { 345 | address += count; 346 | max -= count; 347 | } 348 | fclose(file); 349 | return 1; 350 | } 351 | 352 | 353 | static int doLoadInterpreter(int argc, char **argv, M6502 *mpu) 354 | { 355 | if (argc < 3) usage(1); 356 | if (!loadInterpreter(mpu, htol(argv[1]), argv[2])) pfail(argv[2]); 357 | return 2; 358 | } 359 | 360 | 361 | static int doLoad(int argc, char **argv, M6502 *mpu) /* -l addr file */ 362 | { 363 | if (argc < 3) usage(1); 364 | if (!load(mpu, htol(argv[1]), argv[2])) pfail(argv[2]); 365 | return 2; 366 | } 367 | 368 | 369 | static int doSave(int argc, char **argv, M6502 *mpu) /* -l addr size file */ 370 | { 371 | if (argc < 4) usage(1); 372 | if (!save(mpu, htol(argv[1]), htol(argv[2]), argv[3])) pfail(argv[3]); 373 | return 3; 374 | } 375 | 376 | 377 | #define doVEC(VEC) \ 378 | static int do##VEC(int argc, char **argv, M6502 *mpu) \ 379 | { \ 380 | unsigned addr= 0; \ 381 | if (argc < 2) usage(1); \ 382 | addr= htol(argv[1]); \ 383 | M6502_setVector(mpu, VEC, addr); \ 384 | return 1; \ 385 | } 386 | 387 | doVEC(IRQ); 388 | doVEC(NMI); 389 | doVEC(RST); 390 | 391 | #undef doVEC 392 | 393 | 394 | static int gTrap(M6502 *mpu, word addr, byte data) { mpu->registers->a= getchar(); rts; } 395 | static int pTrap(M6502 *mpu, word addr, byte data) { putchar(mpu->registers->a); rts; } 396 | 397 | static int doGtrap(int argc, char **argv, M6502 *mpu) 398 | { 399 | unsigned addr; 400 | if (argc < 2) usage(1); 401 | addr= htol(argv[1]); 402 | M6502_setCallback(mpu, call, addr, gTrap); 403 | return 1; 404 | } 405 | 406 | static int doPtrap(int argc, char **argv, M6502 *mpu) 407 | { 408 | unsigned addr; 409 | if (argc < 2) usage(1); 410 | addr= htol(argv[1]); 411 | M6502_setCallback(mpu, call, addr, pTrap); 412 | return 1; 413 | } 414 | 415 | 416 | static int mTrapRead(M6502 *mpu, word addr, byte data) { return getchar(); } 417 | static int mTrapWrite(M6502 *mpu, word addr, byte data) { return putchar(data); } 418 | 419 | static int doMtrap(int argc, char **argv, M6502 *mpu) 420 | { 421 | unsigned addr= 0; 422 | if (argc < 2) usage(1); 423 | addr= htol(argv[1]); 424 | M6502_setCallback(mpu, read, addr, mTrapRead); 425 | M6502_setCallback(mpu, write, addr, mTrapWrite); 426 | return 1; 427 | } 428 | 429 | static int xTrap(M6502 *mpu, word addr, byte data) { longjmp(exit_6502_env, 1); return 0; } 430 | 431 | static int doXtrap(int argc, char **argv, M6502 *mpu) 432 | { 433 | unsigned addr= 0; 434 | if (argc < 2) usage(1); 435 | addr= htol(argv[1]); 436 | M6502_setCallback(mpu, call, addr, xTrap); 437 | return 1; 438 | } 439 | 440 | 441 | static int doDisassemble(int argc, char **argv, M6502 *mpu) 442 | { 443 | unsigned addr= 0, last= 0; 444 | if (argc < 3) usage(1); 445 | addr= htol(argv[1]); 446 | last= ('+' == *argv[2]) ? addr + htol(1 + argv[2]) : htol(argv[2]); 447 | while (addr < last) 448 | { 449 | char insn[64]; 450 | int i= 0, size= M6502_disassemble(mpu, addr, insn); 451 | printf("%04X ", addr); 452 | while (i++ < size) printf("%02X", mpu->memory[addr + i - 1]); 453 | while (i++ < 4) printf(" "); 454 | putchar(' '); 455 | i= 0; 456 | while (i++ < size) putchar(isgraph(mpu->memory[addr + i - 1]) ? mpu->memory[addr + i - 1] : ' '); 457 | while (i++ < 4) putchar(' '); 458 | printf(" %s\n", insn); 459 | addr += size; 460 | } 461 | return 2; 462 | } 463 | 464 | 465 | int main(int argc, char **argv) 466 | { 467 | M6502 *mpu= M6502_new(0, 0, 0); 468 | int bTraps= 0; 469 | int countCycles = 0; 470 | 471 | program= argv[0]; 472 | 473 | if ((2 == argc) && ('-' != *argv[1])) 474 | { 475 | if ((!loadInterpreter(mpu, 0, argv[1])) && (!load(mpu, 0, argv[1]))) 476 | pfail(argv[1]); 477 | doBtraps(0, 0, mpu); 478 | } 479 | else 480 | while (++argv, --argc > 0) 481 | { 482 | int n= 0; 483 | if (!strcmp(*argv, "-B")) bTraps= 1; 484 | else if (!strcmp(*argv, "-c")) countCycles = 1; 485 | else if (!strcmp(*argv, "-d")) n= doDisassemble(argc, argv, mpu); 486 | else if (!strcmp(*argv, "-G")) n= doGtrap(argc, argv, mpu); 487 | else if (!strcmp(*argv, "-h")) n= doHelp(argc, argv, mpu); 488 | else if (!strcmp(*argv, "-i")) n= doLoadInterpreter(argc, argv, mpu); 489 | else if (!strcmp(*argv, "-I")) n= doIRQ(argc, argv, mpu); 490 | else if (!strcmp(*argv, "-l")) n= doLoad(argc, argv, mpu); 491 | else if (!strcmp(*argv, "-M")) n= doMtrap(argc, argv, mpu); 492 | else if (!strcmp(*argv, "-N")) n= doNMI(argc, argv, mpu); 493 | else if (!strcmp(*argv, "-P")) n= doPtrap(argc, argv, mpu); 494 | else if (!strcmp(*argv, "-R")) n= doRST(argc, argv, mpu); 495 | else if (!strcmp(*argv, "-s")) n= doSave(argc, argv, mpu); 496 | else if (!strcmp(*argv, "-v")) n= doVersion(argc, argv, mpu); 497 | else if (!strcmp(*argv, "-X")) n= doXtrap(argc, argv, mpu); 498 | else if (!strcmp(*argv, "-x")) exit(0); 499 | else if ('-' == **argv) usage(1); 500 | else 501 | { 502 | /* doBtraps() left 0x8000+0x4000 in bank 0, so load */ 503 | /* additional images starting at 15 and work down */ 504 | static int bankSel= 0x0F; 505 | if (!bTraps) usage(1); 506 | if (bankSel < 0) fail("too many images"); 507 | if (!load(mpu, 0x8000, argv[0])) pfail(argv[0]); 508 | memcpy(bank[bankSel--], 509 | 0x8000 + mpu->memory, 510 | 0x4000); 511 | n= 1; 512 | } 513 | argc -= n; 514 | argv += n; 515 | } 516 | 517 | if (bTraps) 518 | doBtraps(0, 0, mpu); 519 | 520 | volatile unsigned long isns = 0, cycles = 0; 521 | 522 | if (!setjmp(exit_6502_env)){ 523 | M6502_reset(mpu); 524 | if (countCycles){ 525 | while (1){ 526 | M6502_step(mpu); 527 | isns++; 528 | cycles += mpu->ticks; 529 | mpu->ticks = 0; 530 | } 531 | } else { 532 | M6502_run(mpu); 533 | } 534 | } 535 | if (countCycles){ 536 | fprintf(stderr, "Instructions: %lu Cycles: %lu\n", isns, cycles); 537 | } 538 | M6502_delete(mpu); 539 | 540 | return 0; 541 | } 542 | -------------------------------------------------------------------------------- /test.out: -------------------------------------------------------------------------------- 1 | echo a2418a20eeffe8e05bd0f7a90a20eeff0000 | perl -e '$_=pack"H*",;print' > temp.img 2 | ./run6502 \ 3 | -l 1000 temp.img \ 4 | -d 1000 +11 \ 5 | -R 1000 \ 6 | -P FFEE \ 7 | -X 0 8 | 1000 A241 A ldx #41 9 | 1002 8A txa 10 | 1003 20EEFF jsr FFEE 11 | 1006 E8 inx 12 | 1007 E05B [ cpx #5B 13 | 1009 D0F7 bne 1002 14 | 100B A90A lda #0A 15 | 100D 20EEFF jsr FFEE 16 | 1010 00 brk 17 | ABCDEFGHIJKLMNOPQRSTUVWXYZ 18 | ./lib1 19 | 1002 ldx #41 20 | 1003 txa 21 | 1006 jsr FFEE 22 | 1007 inx 23 | 1009 cpx #5B 24 | 100B bne 1002 25 | 100D lda #0A 26 | 1010 jsr FFEE 27 | 1011 brk 28 | 1012 brk 29 | ABCDEFGHIJKLMNOPQRSTUVWXYZ 30 | 31 | BRK instruction 32 | PC=1012 SP=01FD A=0A X=5B Y=00 P=15 ---B-I-C 33 | echo 'PRINT:FORA%=1TO10:PRINTA%:NEXT:PRINT"HELLO WORLD"' | ./run6502 image 34 | 35 | BBC Computer 32K 36 | 37 | BASIC 38 | 39 | > 40 | 1 41 | 2 42 | 3 43 | 4 44 | 5 45 | 6 46 | 7 47 | 8 48 | 9 49 | 10 50 | HELLO WORLD 51 | > 52 | 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 53 | 54 | BBC Computer 32K 55 | 56 | BASIC 57 | 58 | >2800 opt3 59 | 2800 A2 41 ldx#65 60 | 2802 8A .l txa 61 | 2803 20 EE FF jsr&FFEE 62 | 2806 E8 inx 63 | 2807 E0 5B cpx#91 64 | 2809 D0 F7 bnel 65 | 280B A9 0D lda#13 66 | 280D 20 EE FF jsr&FFEE 67 | 2810 A9 0A lda#10 68 | 2812 4C EE FF jmp&FFEE 69 | ABCDEFGHIJKLMNOPQRSTUVWXYZ 70 | > 71 | --------------------------------------------------------------------------------