├── LICENSE ├── Makefile ├── README.md ├── fake6502.h ├── fake65c02.h ├── test2cmos.c ├── tests.c ├── update_fake6502.sh └── update_fake65c02.sh /LICENSE: -------------------------------------------------------------------------------- 1 | Statement of Purpose 2 | 3 | The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). 4 | 5 | Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. 6 | 7 | For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. 8 | 9 | 1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: 10 | 11 | the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; 12 | moral rights retained by the original author(s) and/or performer(s); 13 | publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; 14 | rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; 15 | rights protecting the extraction, dissemination, use and reuse of data in a Work; 16 | database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and 17 | other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. 18 | 2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. 19 | 20 | 3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. 21 | 22 | 4. Limitations and Disclaimers. 23 | 24 | No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. 25 | Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. 26 | Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. 27 | Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. 28 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | 3 | INCLUDE_DIR = /usr/local/include 4 | 5 | all: 6 | cc -std=c99 -Os tests.c -o main.out 7 | cc -std=c99 -Os test2cmos.c -o maincmos.out 8 | 9 | install: 10 | install --mode=444 fake6502.h $(INCLUDE_DIR)/ 11 | install --mode=444 fake65c02.h $(INCLUDE_DIR)/ 12 | 13 | clean: 14 | rm -f main.out maincmos.out *.exe 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fake6502, revamped Plus fake65c02 2 | 3 | Mike Chambers' Fake6502 with revamped bug fixes for decimal mode, along with a few other fixes. 4 | 5 | Now with an all-new Fake65c02 for the CMOS chip. 6 | 7 | The header files in this repository are in the public domain. 8 | 9 | The tests files are not! They are GPL'd instruction set exercisers. 10 | 11 | fake65c02.h incorporates CMOS support code from the codebase for the [Commander X16 Emulator](https://github.com/commanderx16/x16-emulator/tree/master/src/cpu) which is not a public domain repository, however the changes are relatively minor and fake6502.c is still marked as fully public domain. 12 | 13 | Since their Fake6502 code is still marked as public domain, I figured that it would be ok to incorporate those changes here. 14 | 15 | I have a pending github issue on the commander x16 repository asking if they intended this or if I have to relicense under the BSD 2 clause. 16 | 17 | # CHANGELOG 18 | 19 | * Wrote decimal mode according to http://www.6502.org/tutorials/decimal_mode.html#A 20 | to be exactly correct. 21 | * Fixed interrupt masking. 22 | * Fixed decimal mode adc and sbc 23 | * Fixed exec6502 possibly executing many billions more instructions than desired. 24 | * Fixed documentation 25 | * Fixed overflow calculation (I believe) for decimal mode. The V flag is undocumented, and its value is pretty much useless, 26 | but I believe it is correct. I have yet to run the test code from http://www.6502.org/tutorials/decimal_mode.html#A 27 | but it is a TODO. 28 | 29 | 30 | 31 | The emulator uses global state and there is no "instancing" it. 32 | 33 | To use the emulator, the expected usage is that you include it in *ONE* c file. 34 | 35 | these are the functions you must (and typically would only) implement: 36 | 37 | ```c 38 | uint8 read6502(ushort addr) { 39 | /*Return something that would make sense given address "addr"*/ 40 | } 41 | 42 | void write6502(ushort addr, uint8 val) { 43 | /*Do something here using the 8 bit value "val"*/ 44 | } 45 | ``` 46 | 47 | you can additionally define a "hook" to be executed after every instruction. 48 | 49 | 50 | 51 | I used [this](https://github.com/omarandlorraine/fake6502) instruction exerciser along with 52 | [this](https://github.com/mist64/kernalemu) C64 kernal emulator to verify that my fixes were correct and 53 | did not break anything. 54 | 55 | I used these references for opcodes and what they do: 56 | 57 | [6502.org's opcodes list](http://6502.org/tutorials/6502opcodes.html) 58 | 59 | [obelisk.me.uk's 6502 reference](http://www.obelisk.me.uk/6502/reference.html) 60 | 61 | [This one I found on google sites](https://sites.google.com/site/6502asembly/6502-instruction-set) 62 | 63 | Commodore basic v2 boots and BASIC works just as expected. For loops, prints, math, all work as expected. 64 | 65 | All other 6502 based systems I tested also work, although kernalemu only implements a few of them completely. 66 | 67 | For instance, many commands in the C128 kernalemu implementation are completely dead due to being stubbed out with NYI() 68 | 69 | (Not Yet Implemented) calls. 70 | 71 | I have yet to verify that this emulator works with MOARNES, mike chamber's NES emulator. 72 | However, given that the NES's 6502 variant does not use decimal mode, I would guess 73 | that it would still work just fine. 74 | 75 | If you find any errors feel free to post an issue or make a PR. 76 | 77 | Moarnes can be found on github [here](https://github.com/darlanalves/moarnes) 78 | 79 | or on sourceforge, [here](https://sourceforge.net/projects/moarnes/) 80 | 81 | The latter is supposedly "more official" 82 | 83 | 84 | Further documentation on how to use the emulator is in the header file. 85 | 86 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 87 | FAQ 88 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 89 | Q: How do I use this in multiple C files? 90 | 91 | A: Define FAKE6502_NOT_STATIC. Then, create externs for all the registers and functions you want to access. 92 | 93 | These are the registers as they are declared in the header file, with FAKE6502_NOT_STATIC. the only difference 94 | when you don't define FAKE6502_NOT_STATIC is that these are all declared "static" so that hopefully the compiler 95 | will optimize the code a bit better. 96 | 97 | ``` 98 | /*6502 CPU registers*/ 99 | ushort pc; 100 | uint8 sp, a, x, y, status; 101 | /*helper variables*/ 102 | uint32 instructions = 0; 103 | uint32 clockticks6502 = 0; 104 | signed long clockgoal6502 = 0; /*Made a signed number.*/ 105 | ushort oldpc, ea, reladdr, value, result; 106 | uint8 opcode, oldstatus; 107 | ``` 108 | 109 | And for fake65c02, there is an additional variable "waiting" which indicates if the WAI instruction has been executed, 110 | meaning that the processor will not execute anything until an interrupt occurs. 111 | 112 | ``` 113 | /*6502 CPU registers*/ 114 | ushort pc; 115 | uint8 sp, a, x, y, status; 116 | /*helper variables*/ 117 | uint32 instructions = 0; 118 | uint32 clockticks6502 = 0; 119 | uint32 clockgoal6502 = 0; 120 | ushort oldpc, ea, reladdr, value, result; 121 | uint8 opcode, oldstatus, waiting6502 = 0; 122 | ``` 123 | 124 | 125 | These are the functions as they are declared in fake6502.h: 126 | 127 | ```c 128 | void reset6502() 129 | /* Call this once before you begin execution*/ 130 | 131 | uint32 exec6502(uint32 tickcount) 132 | /* Execute 6502 code up to, and possibly one instruction over, the next specified 133 | count of clock ticks. Returns the number of clock ticks actually executed. */ 134 | 135 | uint32 step6502() 136 | /*Execute a single instrution. */ 137 | 138 | void irq6502() 139 | /* Trigger a hardware IRQ in the 6502 core. */ 140 | 141 | void nmi6502() 142 | /* Trigger an NMI in the 6502 core. */ 143 | 144 | void hookexternal(void *funcptr) 145 | /* Pass a pointer to a void function taking no 146 | parameters. This will cause Fake6502 to call 147 | that function once after each emulated 148 | instruction. */ 149 | ``` 150 | 151 | Q: why did you define these weird types like "ushort" and "uint8"!!! Why not just use stdint 152 | 153 | A: C89 compliance. 154 | 155 | Q: Why are the registers global variables? Don't you know that's super slow and bad? Make a struct! 156 | 157 | A: Well since you will typically be stepping this CPU alongside emulating other devices in a computer, 158 | it's probably not possible for the opimizer to turn the emulated cpu's registers into real registers, 159 | since you'll be jumping between functions often. 160 | 161 | Also, function pointers are used for the dispatch. Obviously that rules out the possibility of using hardware 162 | registers entirely. 163 | 164 | Yes, I know, this makes it more difficult to have multiple virtual 6502's running at the same time. You'll figure out 165 | how to work around that. 166 | 167 | Blame Mike Chambers. He made that design decision. If you really want it as a class or struct, then make a pull request. 168 | 169 | Q: I'm writing an NES emulator... 170 | 171 | A: Another one? 172 | 173 | Q: Can this be used to emulate the commander X16? 174 | 175 | A: Yes. Some changes were adapted from the Commander X16's emulator, although that emulator has bugs in some of its decimal mode ops. 176 | 177 | Those bugs have been fixed in this repository. 178 | 179 | Q: I have found a bug in your implementation! 180 | 181 | A: Make an issue about it! 182 | 183 | Q: Did you implement decimal mode correctly? 184 | 185 | A: I believe so. The overflow flag works based on the results of a binary (not BCD) addition or subtraction, 186 | 187 | which is what I have implemented. It's what 6502.org says. 188 | 189 | The carry flag is *not* set back to one (the non-carry state, it is an inverse borrow) by SBC if a carry does not occur. 190 | 191 | This is, I believe, the intended behavior. However, it means that having the carry flag cleared will cause more than just 192 | 193 | the subsequent SBC to be treated as if a carry occured. Specifically, if the accumulator starts out at 0 and the 194 | 195 | carry flag starts out at 5 (inverse borrow) then... 196 | 197 | ```asm 198 | SED ; enable decimal mode 199 | SBC 4 ; the carry flag is set so an additional 1 is subtracted. 200 | SBC 1 ; This causes A to be 99. The carry flag is now 201 | 202 | ``` 203 | -------------------------------------------------------------------------------- /fake6502.h: -------------------------------------------------------------------------------- 1 | /* Fake6502 CPU emulator core v1.3 ******************* 2 | *Original Author:Mike Chambers (miker00lz@gmail.com)* 3 | * * 4 | *New Author:David MHS Webster (github.com/gek169) * 5 | * Leave a star on github to show thanks for this * 6 | * FULLY PUBLIC DOMAIN, CC0 CODE * 7 | * Which I give to you and the world with absolutely * 8 | * no attribution, monetary compensation, or * 9 | * copyleft requirement. Just write code! * 10 | ***************************************************** 11 | * Let all that you do be done with love * 12 | ***************************************************** 13 | *This version has been overhauled with major bug * 14 | *fixes relating to decimal mode and adc/sbc. I've * 15 | *put the emulator through its paces in kernalemu * 16 | *as well as run it through an instruction exerciser * 17 | *to make sure it works properly. I also discovered * 18 | *bugs in the instruction exerciser while I was at it* 19 | *I might contribute some fixes back to them. * 20 | ***************************************************** 21 | * v1.3 - refactoring and more bug fixes * 22 | * v1.2 - Major bug fixes in handling adc and sbc * 23 | * v1.1 - Small bugfix in BIT opcode, but it was the * 24 | * difference between a few games in my NES * 25 | * emulator working and being broken! * 26 | * I went through the rest carefully again * 27 | * after fixing it just to make sure I didn't * 28 | * have any other typos! (Dec. 17, 2011) * 29 | * * 30 | * v1.0 - First release (Nov. 24, 2011) * 31 | ***************************************************** 32 | * LICENSE: This source code is released into the * 33 | * public domain, but if you use it please do give * 34 | * credit. I put a lot of effort into writing this! * 35 | * Note by GEK: this is not a requirement. * 36 | ***************************************************** 37 | * Fake6502 is a MOS Technology 6502 CPU emulation * 38 | * engine in C. It was written as part of a Nintendo * 39 | * Entertainment System emulator I've been writing. * 40 | * * 41 | * A couple important things to know about are two * 42 | * defines in the code. One is "UNDOCUMENTED" which, * 43 | * when defined, allows Fake6502 to compile with * 44 | * full support for the more predictable * 45 | * undocumented instructions of the 6502. If it is * 46 | * undefined, undocumented opcodes just act as NOPs. * 47 | * * 48 | * The other define is "NES_CPU", which causes the * 49 | * code to compile without support for binary-coded * 50 | * decimal (BCD) support for the ADC and SBC * 51 | * opcodes. The Ricoh 2A03 CPU in the NES does not * 52 | * support BCD, but is otherwise identical to the * 53 | * standard MOS 6502. (Note that this define is * 54 | * enabled in this file if you haven't changed it * 55 | * yourself. If you're not emulating a NES, you * 56 | * should comment it out.) * 57 | * * 58 | * If you do discover an error in timing accuracy, * 59 | * or operation in general please e-mail me at the * 60 | * address above so that I can fix it. Thank you! * 61 | * * 62 | ***************************************************** 63 | * Usage: * 64 | * * 65 | * Fake6502 requires you to provide two external * 66 | * functions: * 67 | * * 68 | * uint8 read6502(ushort address) * 69 | * void write6502(ushort address, uint8 value) * 70 | * * 71 | * You may optionally pass Fake6502 the pointer to a * 72 | * function which you want to be called after every * 73 | * emulated instruction. This function should be a * 74 | * void with no parameters expected to be passed to * 75 | * it. * 76 | * * 77 | * This can be very useful. For example, in a NES * 78 | * emulator, you check the number of clock ticks * 79 | * that have passed so you can know when to handle * 80 | * APU events. * 81 | * * 82 | * To pass Fake6502 this pointer, use the * 83 | * hookexternal(void *funcptr) function provided. * 84 | * * 85 | * To disable the hook later, pass NULL to it. * 86 | ***************************************************** 87 | * Useful functions in this emulator: * 88 | * * 89 | * void reset6502() * 90 | * - Call this once before you begin execution. * 91 | * * 92 | * uint32 exec6502(uint32 tickcount) * 93 | * - Execute 6502 code up to the next specified * 94 | * count of clock ticks. * 95 | * * 96 | * uint32 step6502() * 97 | * - Execute a single instrution. * 98 | * * 99 | * void irq6502() * 100 | * - Trigger a hardware IRQ in the 6502 core. * 101 | * * 102 | * void nmi6502() * 103 | * - Trigger an NMI in the 6502 core. * 104 | * * 105 | * void hookexternal(void *funcptr) * 106 | * - Pass a pointer to a void function taking no * 107 | * parameters. This will cause Fake6502 to call * 108 | * that function once after each emulated * 109 | * instruction. * 110 | * * 111 | ***************************************************** 112 | * Useful variables in this emulator: * 113 | * * 114 | * uint32 clockticks6502 * 115 | * - A running total of the emulated cycle count * 116 | * during a call to exec6502. * 117 | * uint32 instructions * 118 | * - A running total of the total emulated * 119 | * instruction count. This is not related to * 120 | * clock cycle timing. * 121 | * * 122 | *****************************************************/ 123 | 124 | 125 | /* 126 | 6510 EMULATION NOTES: 127 | 1) On the 6510 processor, the only difference is that the addresses 0 and 1 are used 128 | for data direction and data, respectively. 129 | 130 | 2) The initial value of address 0 should always be 0. 131 | 132 | 3) Read this page 133 | https://ist.uwaterloo.ca/~schepers/MJK/6510.html 134 | */ 135 | 136 | #include 137 | #ifdef FAKE6502_USE_STDINT 138 | #include 139 | typedef uint16_t ushort; 140 | typedef unsigned char uint8; 141 | typedef uint32_t uint32 142 | #else 143 | typedef unsigned short ushort ; 144 | typedef unsigned char uint8; 145 | 146 | #ifdef FAKE6502_USE_LONG 147 | typedef unsigned long uint32; 148 | #else 149 | typedef unsigned int uint32; 150 | #endif 151 | 152 | #endif 153 | /* 154 | when this is defined, undocumented opcodes are handled. 155 | otherwise, they're simply treated as NOPs. 156 | */ 157 | #define UNDOCUMENTED 158 | 159 | /* 160 | * #define NES_CPU 161 | * when this is defined, the binary-coded decimal (BCD) 162 | * status flag is not honored by ADC and SBC. the 2A03 163 | * CPU in the Nintendo Entertainment System does not 164 | * support BCD operation. 165 | */ 166 | 167 | 168 | #define FLAG_CARRY 0x01 169 | #define FLAG_ZERO 0x02 170 | #define FLAG_INTERRUPT 0x04 171 | #define FLAG_DECIMAL 0x08 172 | /*bits 4 and 5.*/ 173 | #define FLAG_BREAK 0x10 174 | #define FLAG_CONSTANT 0x20 175 | #define FLAG_OVERFLOW 0x40 176 | #define FLAG_SIGN 0x80 177 | 178 | #define BASE_STACK 0x100 179 | 180 | #define saveaccum(n) a = (uint8)((n) & 0x00FF) 181 | 182 | 183 | /*flag modifier macros*/ 184 | #define setcarry() status |= FLAG_CARRY 185 | #define clearcarry() status &= (~FLAG_CARRY) 186 | #define setzero() status |= FLAG_ZERO 187 | #define clearzero() status &= (~FLAG_ZERO) 188 | #define setinterrupt() status |= FLAG_INTERRUPT 189 | #define clearinterrupt() status &= (~FLAG_INTERRUPT) 190 | #define setdecimal() status |= FLAG_DECIMAL 191 | #define cleardecimal() status &= (~FLAG_DECIMAL) 192 | #define setoverflow() status |= FLAG_OVERFLOW 193 | #define clearoverflow() status &= (~FLAG_OVERFLOW) 194 | #define setsign() status |= FLAG_SIGN 195 | #define clearsign() status &= (~FLAG_SIGN) 196 | 197 | 198 | /*flag calculation macros*/ 199 | #define zerocalc(n) {\ 200 | if ((n) & 0x00FF) clearzero();\ 201 | else setzero();\ 202 | } 203 | 204 | #define signcalc(n) {\ 205 | if ((n) & 0x0080) setsign();\ 206 | else clearsign();\ 207 | } 208 | 209 | #define carrycalc(n) {\ 210 | if ((n) & 0xFF00) setcarry();\ 211 | else clearcarry();\ 212 | } 213 | 214 | #define overflowcalc(n, m, o) { /* n = result, m = accumulator, o = memory */ \ 215 | if (((n) ^ (ushort)(m)) & ((n) ^ (o)) & 0x0080) setoverflow();\ 216 | else clearoverflow();\ 217 | } 218 | 219 | 220 | #ifdef FAKE6502_NOT_STATIC 221 | /*6502 CPU registers*/ 222 | ushort pc; 223 | uint8 sp, a, x, y, status; 224 | /*helper variables*/ 225 | uint32 instructions = 0; 226 | uint32 clockticks6502 = 0; 227 | uint32 clockgoal6502 = 0; 228 | ushort oldpc, ea, reladdr, value, result; 229 | uint8 opcode, oldstatus; 230 | void reset6502(); 231 | void nmi6502(); 232 | void irq6502(); 233 | void irq6502(); 234 | uint32 exec6502(uint32 tickcount); 235 | uint32 step6502(); 236 | void hookexternal(void *funcptr); 237 | #else 238 | static ushort pc; 239 | static uint8 sp, a, x, y, status; 240 | static uint32 instructions = 0; 241 | static uint32 clockticks6502 = 0; 242 | static uint32 clockgoal6502 = 0; 243 | static ushort oldpc, ea, reladdr, value, result; 244 | static uint8 opcode, oldstatus; 245 | #endif 246 | /*externally supplied functions*/ 247 | extern uint8 read6502(ushort address); 248 | extern void write6502(ushort address, uint8 value); 249 | 250 | 251 | #ifndef FAKE6502_INCLUDE 252 | /*a few general functions used by various other functions*/ 253 | static void push_6502_16(ushort pushval) { 254 | write6502(BASE_STACK + sp, (pushval >> 8) & 0xFF); 255 | write6502(BASE_STACK + ((sp - 1) & 0xFF), pushval & 0xFF); 256 | sp -= 2; 257 | } 258 | 259 | static void push_6502_8(uint8 pushval) { 260 | write6502(BASE_STACK + sp--, pushval); 261 | } 262 | 263 | static ushort pull_6502_16() { 264 | ushort temp16; 265 | temp16 = read6502(BASE_STACK + ((sp + 1) & 0xFF)) | ((ushort)read6502(BASE_STACK + ((sp + 2) & 0xFF)) << 8); 266 | sp += 2; 267 | return(temp16); 268 | } 269 | 270 | static uint8 pull_6502_8() { 271 | return (read6502(BASE_STACK + ++sp)); 272 | } 273 | 274 | static ushort mem_6502_read16(ushort addr) { 275 | return ((ushort)read6502(addr) | 276 | ((ushort)read6502(addr + 1) << 8)); 277 | } 278 | 279 | void reset6502() { 280 | /* 281 | pc = (ushort)read6502(0xFFFC) | ((ushort)read6502(0xFFFD) << 8); 282 | a = 0; 283 | x = 0; 284 | y = 0; 285 | sp = 0xFD; 286 | status |= FLAG_CONSTANT; 287 | */ 288 | read6502(0x00ff); 289 | read6502(0x00ff); 290 | read6502(0x00ff); 291 | read6502(0x0100); 292 | read6502(0x01ff); 293 | read6502(0x01fe); 294 | pc = mem_6502_read16(0xfffc); 295 | sp = 0xfd; 296 | status |= FLAG_CONSTANT | FLAG_INTERRUPT; 297 | } 298 | 299 | 300 | static void (*addrtable[256])(); 301 | static void (*optable[256])(); 302 | static uint8 penaltyop, penaltyaddr; 303 | 304 | /*addressing mode functions, calculates effective addresses*/ 305 | static void imp() { 306 | } 307 | 308 | /*addressing mode functions, calculates effective addresses*/ 309 | static void acc() { 310 | } 311 | 312 | /*addressing mode functions, calculates effective addresses*/ 313 | static void imm() { 314 | ea = pc++; 315 | } 316 | 317 | static void zp() { /*zero-page*/ 318 | ea = (ushort)read6502((ushort)pc++); 319 | } 320 | 321 | static void zpx() { /*zero-page,X*/ 322 | ea = ((ushort)read6502((ushort)pc++) + (ushort)x) & 0xFF; /*zero-page wraparound*/ 323 | } 324 | 325 | static void zpy() { /*zero-page,Y*/ 326 | ea = ((ushort)read6502((ushort)pc++) + (ushort)y) & 0xFF; /*zero-page wraparound*/ 327 | } 328 | 329 | static void rel() { /*relative for branch ops (8-bit immediate value, sign-extended)*/ 330 | reladdr = (ushort)read6502(pc++); 331 | if (reladdr & 0x80) reladdr |= 0xFF00; 332 | } 333 | 334 | static void abso() { /*absolute*/ 335 | ea = (ushort)read6502(pc) | ((ushort)read6502(pc+1) << 8); 336 | pc += 2; 337 | } 338 | 339 | static void absx() { /*absolute,X*/ 340 | ushort startpage; 341 | ea = ((ushort)read6502(pc) | ((ushort)read6502(pc+1) << 8)); 342 | startpage = ea & 0xFF00; 343 | ea += (ushort)x; 344 | 345 | if (startpage != (ea & 0xFF00)) { /*one cycle penlty for page-crossing on some opcodes*/ 346 | penaltyaddr = 1; 347 | } 348 | 349 | pc += 2; 350 | } 351 | 352 | static void absy() { /*absolute,Y*/ 353 | ushort startpage; 354 | ea = ((ushort)read6502(pc) | ((ushort)read6502(pc+1) << 8)); 355 | startpage = ea & 0xFF00; 356 | ea += (ushort)y; 357 | 358 | if (startpage != (ea & 0xFF00)) { /*one cycle penlty for page-crossing on some opcodes*/ 359 | penaltyaddr = 1; 360 | } 361 | 362 | pc += 2; 363 | } 364 | 365 | static void ind() { /*indirect*/ 366 | ushort eahelp, eahelp2; 367 | eahelp = (ushort)read6502(pc) | (ushort)((ushort)read6502(pc+1) << 8); 368 | eahelp2 = (eahelp & 0xFF00) | ((eahelp + 1) & 0x00FF); /*replicate 6502 page-boundary wraparound bug*/ 369 | ea = (ushort)read6502(eahelp) | ((ushort)read6502(eahelp2) << 8); 370 | pc += 2; 371 | } 372 | 373 | static void indx() { /* (indirect,X)*/ 374 | ushort eahelp; 375 | eahelp = (ushort)(((ushort)read6502(pc++) + (ushort)x) & 0xFF); /*zero-page wraparound for table pointer*/ 376 | ea = (ushort)read6502(eahelp & 0x00FF) | ((ushort)read6502((eahelp+1) & 0x00FF) << 8); 377 | } 378 | 379 | static void indy() { /* (indirect),Y*/ 380 | ushort eahelp, eahelp2, startpage; 381 | eahelp = (ushort)read6502(pc++); 382 | eahelp2 = (eahelp & 0xFF00) | ((eahelp + 1) & 0x00FF); /*zero-page wraparound*/ 383 | ea = (ushort)read6502(eahelp) | ((ushort)read6502(eahelp2) << 8); 384 | startpage = ea & 0xFF00; 385 | ea += (ushort)y; 386 | 387 | if (startpage != (ea & 0xFF00)) { /*one cycle penlty for page-crossing on some opcodes*/ 388 | penaltyaddr = 1; 389 | } 390 | } 391 | 392 | static ushort getvalue() { 393 | if (addrtable[opcode] == acc) return((ushort)a); 394 | else return((ushort)read6502(ea)); 395 | } 396 | 397 | static ushort getvalue16() { 398 | return((ushort)read6502(ea) | ((ushort)read6502(ea+1) << 8)); 399 | } 400 | 401 | static void putvalue(ushort saveval) { 402 | if (addrtable[opcode] == acc) a = (uint8)(saveval & 0x00FF); 403 | else write6502(ea, (saveval & 0x00FF)); 404 | } 405 | 406 | 407 | /*instruction handler functions*/ 408 | static void adc() { 409 | penaltyop = 1; 410 | #ifndef NES_CPU 411 | if (status & FLAG_DECIMAL) { 412 | ushort AL, A, result_dec; 413 | A = a; 414 | value = getvalue(); 415 | result_dec = (ushort)A + value + (ushort)(status & FLAG_CARRY); /*dec*/ 416 | 417 | AL = (A & 0x0F) + (value & 0x0F) + (ushort)(status & FLAG_CARRY); /*SEQ 1A OR 2A*/ 418 | if(AL >= 0xA) AL = ((AL + 0x06) & 0x0F) + 0x10; /*SEQ 1B OR SEQ 2B*/ 419 | A = (A & 0xF0) + (value & 0xF0) + AL; /*SEQ2C OR SEQ 1C*/ 420 | if(A & 0x80) setsign(); else clearsign(); /*SEQ 2E it says "bit 7"*/ 421 | if(A >= 0xA0) A += 0x60; /*SEQ 1E*/ 422 | result = A; /*1F*/ 423 | if(A & 0xff80) setoverflow();else clearoverflow(); 424 | if(A >= 0x100) setcarry(); else clearcarry(); /*SEQ 1G*/ 425 | 426 | zerocalc(result_dec); /*Original nmos does zerocalc on the binary result.*/ 427 | } else 428 | #endif 429 | { 430 | value = getvalue(); 431 | result = (ushort)a + value + (ushort)(status & FLAG_CARRY); 432 | carrycalc(result); 433 | zerocalc(result); 434 | overflowcalc(result, a, value); 435 | signcalc(result); 436 | } 437 | saveaccum(result); 438 | } 439 | 440 | static void and() { 441 | penaltyop = 1; 442 | value = getvalue(); 443 | result = (ushort)a & value; 444 | 445 | zerocalc(result); 446 | signcalc(result); 447 | 448 | saveaccum(result); 449 | } 450 | 451 | static void asl() { 452 | value = getvalue(); 453 | result = value << 1; 454 | 455 | carrycalc(result); 456 | zerocalc(result); 457 | signcalc(result); 458 | 459 | putvalue(result); 460 | } 461 | 462 | static void bcc() { 463 | if ((status & FLAG_CARRY) == 0) { 464 | oldpc = pc; 465 | pc += reladdr; 466 | if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; /*check if jump crossed a page boundary*/ 467 | else clockticks6502++; 468 | } 469 | } 470 | 471 | static void bcs() { 472 | if ((status & FLAG_CARRY) == FLAG_CARRY) { 473 | oldpc = pc; 474 | pc += reladdr; 475 | if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; /*check if jump crossed a page boundary*/ 476 | else clockticks6502++; 477 | } 478 | } 479 | 480 | static void beq() { 481 | if ((status & FLAG_ZERO) == FLAG_ZERO) { 482 | oldpc = pc; 483 | pc += reladdr; 484 | if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; /*check if jump crossed a page boundary*/ 485 | else clockticks6502++; 486 | } 487 | } 488 | 489 | static void bit() { 490 | value = getvalue(); 491 | result = (ushort)a & value; 492 | 493 | zerocalc(result); 494 | status = (status & 0x3F) | (uint8)(value & 0xC0); 495 | } 496 | 497 | static void bmi() { 498 | if ((status & FLAG_SIGN) == FLAG_SIGN) { 499 | oldpc = pc; 500 | pc += reladdr; 501 | if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; /*check if jump crossed a page boundary*/ 502 | else clockticks6502++; 503 | } 504 | } 505 | 506 | static void bne() { 507 | if ((status & FLAG_ZERO) == 0) { 508 | oldpc = pc; 509 | pc += reladdr; 510 | if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; /*check if jump crossed a page boundary*/ 511 | else clockticks6502++; 512 | } 513 | } 514 | 515 | static void bpl() { 516 | if ((status & FLAG_SIGN) == 0) { 517 | oldpc = pc; 518 | pc += reladdr; 519 | if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; /*check if jump crossed a page boundary*/ 520 | else clockticks6502++; 521 | } 522 | } 523 | 524 | static void brk_6502() { 525 | pc++; 526 | push_6502_16(pc); 527 | push_6502_8(status | FLAG_BREAK); 528 | setinterrupt(); 529 | pc = (ushort)read6502(0xFFFE) | ((ushort)read6502(0xFFFF) << 8); 530 | } 531 | 532 | static void bvc() { 533 | if ((status & FLAG_OVERFLOW) == 0) { 534 | oldpc = pc; 535 | pc += reladdr; 536 | if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; /*check if jump crossed a page boundary*/ 537 | else clockticks6502++; 538 | } 539 | } 540 | 541 | static void bvs() { 542 | if ((status & FLAG_OVERFLOW) == FLAG_OVERFLOW) { 543 | oldpc = pc; 544 | pc += reladdr; 545 | if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; /*check if jump crossed a page boundary*/ 546 | else clockticks6502++; 547 | } 548 | } 549 | 550 | static void clc() { 551 | clearcarry(); 552 | } 553 | 554 | static void cld() { 555 | cleardecimal(); 556 | } 557 | 558 | static void cli() { 559 | clearinterrupt(); 560 | } 561 | 562 | static void clv() { 563 | clearoverflow(); 564 | } 565 | 566 | static void cmp() { 567 | penaltyop = 1; 568 | value = getvalue(); 569 | result = (ushort)a - value; 570 | 571 | if (a >= (uint8)(value & 0x00FF)) setcarry(); 572 | else clearcarry(); 573 | if (a == (uint8)(value & 0x00FF)) setzero(); 574 | else clearzero(); 575 | signcalc(result); 576 | } 577 | 578 | static void cpx() { 579 | value = getvalue(); 580 | result = (ushort)x - value; 581 | 582 | if (x >= (uint8)(value & 0x00FF)) setcarry(); 583 | else clearcarry(); 584 | if (x == (uint8)(value & 0x00FF)) setzero(); 585 | else clearzero(); 586 | signcalc(result); 587 | } 588 | 589 | static void cpy() { 590 | value = getvalue(); 591 | result = (ushort)y - value; 592 | 593 | if (y >= (uint8)(value & 0x00FF)) setcarry(); 594 | else clearcarry(); 595 | if (y == (uint8)(value & 0x00FF)) setzero(); 596 | else clearzero(); 597 | signcalc(result); 598 | } 599 | 600 | static void dec() { 601 | value = getvalue(); 602 | result = value - 1; 603 | 604 | zerocalc(result); 605 | signcalc(result); 606 | 607 | putvalue(result); 608 | } 609 | 610 | static void dex() { 611 | x--; 612 | 613 | zerocalc(x); 614 | signcalc(x); 615 | } 616 | 617 | static void dey() { 618 | y--; 619 | 620 | zerocalc(y); 621 | signcalc(y); 622 | } 623 | 624 | static void eor() { 625 | penaltyop = 1; 626 | value = getvalue(); 627 | result = (ushort)a ^ value; 628 | 629 | zerocalc(result); 630 | signcalc(result); 631 | 632 | saveaccum(result); 633 | } 634 | 635 | static void inc() { 636 | value = getvalue(); 637 | result = value + 1; 638 | 639 | zerocalc(result); 640 | signcalc(result); 641 | 642 | putvalue(result); 643 | } 644 | 645 | static void inx() { 646 | x++; 647 | 648 | zerocalc(x); 649 | signcalc(x); 650 | } 651 | 652 | static void iny() { 653 | y++; 654 | 655 | zerocalc(y); 656 | signcalc(y); 657 | } 658 | 659 | static void jmp() { 660 | pc = ea; 661 | } 662 | 663 | static void jsr() { 664 | push_6502_16(pc - 1); 665 | pc = ea; 666 | } 667 | 668 | static void lda() { 669 | penaltyop = 1; 670 | value = getvalue(); 671 | a = (uint8)(value & 0x00FF); 672 | 673 | zerocalc(a); 674 | signcalc(a); 675 | } 676 | 677 | static void ldx() { 678 | penaltyop = 1; 679 | value = getvalue(); 680 | x = (uint8)(value & 0x00FF); 681 | 682 | zerocalc(x); 683 | signcalc(x); 684 | } 685 | 686 | static void ldy() { 687 | penaltyop = 1; 688 | value = getvalue(); 689 | y = (uint8)(value & 0x00FF); 690 | 691 | zerocalc(y); 692 | signcalc(y); 693 | } 694 | 695 | static void lsr() { 696 | value = getvalue(); 697 | result = value >> 1; 698 | 699 | if (value & 1) setcarry(); 700 | else clearcarry(); 701 | zerocalc(result); 702 | signcalc(result); 703 | 704 | putvalue(result); 705 | } 706 | 707 | static void nop() { 708 | switch (opcode) { 709 | case 0x1C: 710 | case 0x3C: 711 | case 0x5C: 712 | case 0x7C: 713 | case 0xDC: 714 | case 0xFC: 715 | penaltyop = 1; 716 | break; 717 | } 718 | } 719 | 720 | static void ora() { 721 | penaltyop = 1; 722 | value = getvalue(); 723 | result = (ushort)a | value; 724 | 725 | zerocalc(result); 726 | signcalc(result); 727 | 728 | saveaccum(result); 729 | } 730 | 731 | static void pha() { 732 | push_6502_8(a); 733 | } 734 | 735 | static void php() { 736 | push_6502_8(status | FLAG_BREAK); 737 | } 738 | 739 | static void pla() { 740 | a = pull_6502_8(); 741 | 742 | zerocalc(a); 743 | signcalc(a); 744 | } 745 | 746 | static void plp() { 747 | status = pull_6502_8() | FLAG_CONSTANT; 748 | } 749 | 750 | static void rol() { 751 | value = getvalue(); 752 | result = (value << 1) | (status & FLAG_CARRY); 753 | 754 | carrycalc(result); 755 | zerocalc(result); 756 | signcalc(result); 757 | 758 | putvalue(result); 759 | } 760 | 761 | static void ror() { 762 | value = getvalue(); 763 | result = (value >> 1) | ((status & FLAG_CARRY) << 7); 764 | 765 | if (value & 1) setcarry(); 766 | else clearcarry(); 767 | zerocalc(result); 768 | signcalc(result); 769 | 770 | putvalue(result); 771 | } 772 | 773 | static void rti() { 774 | status = pull_6502_8(); 775 | value = pull_6502_16(); 776 | pc = value; 777 | } 778 | 779 | static void rts() { 780 | value = pull_6502_16(); 781 | pc = value + 1; 782 | } 783 | 784 | static void sbc() { 785 | penaltyop = 1; 786 | #ifndef NES_CPU 787 | if (status & FLAG_DECIMAL) { 788 | ushort result_dec, A, AL, B, C; 789 | A = a; 790 | C = (ushort)(status & FLAG_CARRY); 791 | value = getvalue();B = value;value = value ^ 0x00FF; 792 | result_dec = (ushort)a + value + (ushort)(status & FLAG_CARRY); /*dec*/ 793 | /*Both Cmos and Nmos*/ 794 | carrycalc(result_dec); 795 | overflowcalc(result_dec, a, value); 796 | /*NMOS ONLY*/ 797 | signcalc(result_dec); 798 | zerocalc(result_dec); 799 | /*Sequence 3 is NMOS ONLY*/ 800 | AL = (A & 0x0F) - (B & 0x0F) + C -1; /* 3a*/ 801 | if(AL & 0x8000) AL = ((AL - 0x06) & 0x0F) - 0x10; /*3b*/ 802 | A = (A & 0xF0) - (B & 0xF0) + AL; /*3c*/ 803 | if(A & 0x8000) A = A - 0x60; /*3d*/ 804 | result = A; /*3e*/ 805 | } else 806 | #endif 807 | { 808 | value = getvalue() ^ 0x00FF; 809 | result = (ushort)a + value + (ushort)(status & FLAG_CARRY); 810 | 811 | carrycalc(result); 812 | zerocalc(result); 813 | overflowcalc(result, a, value); 814 | signcalc(result); 815 | } 816 | saveaccum(result); 817 | } 818 | 819 | static void sec() { 820 | setcarry(); 821 | } 822 | 823 | static void sed() { 824 | setdecimal(); 825 | } 826 | 827 | static void sei() { 828 | setinterrupt(); 829 | } 830 | 831 | static void sta() { 832 | putvalue(a); 833 | } 834 | 835 | static void stx() { 836 | putvalue(x); 837 | } 838 | 839 | static void sty() { 840 | putvalue(y); 841 | } 842 | 843 | static void tax() { 844 | x = a; 845 | 846 | zerocalc(x); 847 | signcalc(x); 848 | } 849 | 850 | static void tay() { 851 | y = a; 852 | 853 | zerocalc(y); 854 | signcalc(y); 855 | } 856 | 857 | static void tsx() { 858 | x = sp; 859 | 860 | zerocalc(x); 861 | signcalc(x); 862 | } 863 | 864 | static void txa() { 865 | a = x; 866 | 867 | zerocalc(a); 868 | signcalc(a); 869 | } 870 | 871 | static void txs() { 872 | sp = x; 873 | } 874 | 875 | static void tya() { 876 | a = y; 877 | 878 | zerocalc(a); 879 | signcalc(a); 880 | } 881 | 882 | /*undocumented instructions~~~~~~~~~~~~~~~~~~~~~~~~~*/ 883 | #ifdef UNDOCUMENTED 884 | static void lax() { 885 | lda(); 886 | ldx(); 887 | } 888 | 889 | static void sax() { 890 | sta(); 891 | stx(); 892 | putvalue(a & x); 893 | if (penaltyop && penaltyaddr) clockticks6502--; 894 | } 895 | 896 | static void dcp() { 897 | dec(); 898 | cmp(); 899 | if (penaltyop && penaltyaddr) clockticks6502--; 900 | } 901 | 902 | static void isb() { 903 | inc(); 904 | sbc(); 905 | if (penaltyop && penaltyaddr) clockticks6502--; 906 | } 907 | 908 | static void slo() { 909 | asl(); 910 | ora(); 911 | if (penaltyop && penaltyaddr) clockticks6502--; 912 | } 913 | 914 | static void rla() { 915 | rol(); 916 | and(); 917 | if (penaltyop && penaltyaddr) clockticks6502--; 918 | } 919 | 920 | static void sre() { 921 | lsr(); 922 | eor(); 923 | if (penaltyop && penaltyaddr) clockticks6502--; 924 | } 925 | 926 | static void rra() { 927 | ror(); 928 | adc(); 929 | if (penaltyop && penaltyaddr) clockticks6502--; 930 | } 931 | #else 932 | #define lax nop 933 | #define sax nop 934 | #define dcp nop 935 | #define isb nop 936 | #define slo nop 937 | #define rla nop 938 | #define sre nop 939 | #define rra nop 940 | #endif 941 | 942 | 943 | static void (*addrtable[256])() = { 944 | /* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | */ 945 | /* 0 */ imp, indx, imp, indx, zp, zp, zp, zp, imp, imm, acc, imm, abso, abso, abso, abso, /* 0 */ 946 | /* 1 */ rel, indy, imp, indy, zpx, zpx, zpx, zpx, imp, absy, imp, absy, absx, absx, absx, absx, /* 1 */ 947 | /* 2 */ abso, indx, imp, indx, zp, zp, zp, zp, imp, imm, acc, imm, abso, abso, abso, abso, /* 2 */ 948 | /* 3 */ rel, indy, imp, indy, zpx, zpx, zpx, zpx, imp, absy, imp, absy, absx, absx, absx, absx, /* 3 */ 949 | /* 4 */ imp, indx, imp, indx, zp, zp, zp, zp, imp, imm, acc, imm, abso, abso, abso, abso, /* 4 */ 950 | /* 5 */ rel, indy, imp, indy, zpx, zpx, zpx, zpx, imp, absy, imp, absy, absx, absx, absx, absx, /* 5 */ 951 | /* 6 */ imp, indx, imp, indx, zp, zp, zp, zp, imp, imm, acc, imm, ind, abso, abso, abso, /* 6 */ 952 | /* 7 */ rel, indy, imp, indy, zpx, zpx, zpx, zpx, imp, absy, imp, absy, absx, absx, absx, absx, /* 7 */ 953 | /* 8 */ imm, indx, imm, indx, zp, zp, zp, zp, imp, imm, imp, imm, abso, abso, abso, abso, /* 8 */ 954 | /* 9 */ rel, indy, imp, indy, zpx, zpx, zpy, zpy, imp, absy, imp, absy, absx, absx, absy, absy, /* 9 */ 955 | /* A */ imm, indx, imm, indx, zp, zp, zp, zp, imp, imm, imp, imm, abso, abso, abso, abso, /* A */ 956 | /* B */ rel, indy, imp, indy, zpx, zpx, zpy, zpy, imp, absy, imp, absy, absx, absx, absy, absy, /* B */ 957 | /* C */ imm, indx, imm, indx, zp, zp, zp, zp, imp, imm, imp, imm, abso, abso, abso, abso, /* C */ 958 | /* D */ rel, indy, imp, indy, zpx, zpx, zpx, zpx, imp, absy, imp, absy, absx, absx, absx, absx, /* D */ 959 | /* E */ imm, indx, imm, indx, zp, zp, zp, zp, imp, imm, imp, imm, abso, abso, abso, abso, /* E */ 960 | /* F */ rel, indy, imp, indy, zpx, zpx, zpx, zpx, imp, absy, imp, absy, absx, absx, absx, absx /* F */ 961 | }; 962 | 963 | static void (*optable[256])() = { 964 | /* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | */ 965 | /* 0 */ brk_6502, ora, nop, slo, nop, ora, asl, slo, php, ora, asl, nop, nop, ora, asl, slo, /* 0 */ 966 | /* 1 */ bpl, ora, nop, slo, nop, ora, asl, slo, clc, ora, nop, slo, nop, ora, asl, slo, /* 1 */ 967 | /* 2 */ jsr, and, nop, rla, bit, and, rol, rla, plp, and, rol, nop, bit, and, rol, rla, /* 2 */ 968 | /* 3 */ bmi, and, nop, rla, nop, and, rol, rla, sec, and, nop, rla, nop, and, rol, rla, /* 3 */ 969 | /* 4 */ rti, eor, nop, sre, nop, eor, lsr, sre, pha, eor, lsr, nop, jmp, eor, lsr, sre, /* 4 */ 970 | /* 5 */ bvc, eor, nop, sre, nop, eor, lsr, sre, cli, eor, nop, sre, nop, eor, lsr, sre, /* 5 */ 971 | /* 6 */ rts, adc, nop, rra, nop, adc, ror, rra, pla, adc, ror, nop, jmp, adc, ror, rra, /* 6 */ 972 | /* 7 */ bvs, adc, nop, rra, nop, adc, ror, rra, sei, adc, nop, rra, nop, adc, ror, rra, /* 7 */ 973 | /* 8 */ nop, sta, nop, sax, sty, sta, stx, sax, dey, nop, txa, nop, sty, sta, stx, sax, /* 8 */ 974 | /* 9 */ bcc, sta, nop, nop, sty, sta, stx, sax, tya, sta, txs, nop, nop, sta, nop, nop, /* 9 */ 975 | /* A */ ldy, lda, ldx, lax, ldy, lda, ldx, lax, tay, lda, tax, nop, ldy, lda, ldx, lax, /* A */ 976 | /* B */ bcs, lda, nop, lax, ldy, lda, ldx, lax, clv, lda, tsx, lax, ldy, lda, ldx, lax, /* B */ 977 | /* C */ cpy, cmp, nop, dcp, cpy, cmp, dec, dcp, iny, cmp, dex, nop, cpy, cmp, dec, dcp, /* C */ 978 | /* D */ bne, cmp, nop, dcp, nop, cmp, dec, dcp, cld, cmp, nop, dcp, nop, cmp, dec, dcp, /* D */ 979 | /* E */ cpx, sbc, nop, isb, cpx, sbc, inc, isb, inx, sbc, nop, sbc, cpx, sbc, inc, isb, /* E */ 980 | /* F */ beq, sbc, nop, isb, nop, sbc, inc, isb, sed, sbc, nop, isb, nop, sbc, inc, isb /* F */ 981 | }; 982 | 983 | static const uint32 ticktable[256] = { 984 | /* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | */ 985 | /* 0 */ 7, 6, 2, 8, 3, 3, 5, 5, 3, 2, 2, 2, 4, 4, 6, 6, /* 0 */ 986 | /* 1 */ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, /* 1 */ 987 | /* 2 */ 6, 6, 2, 8, 3, 3, 5, 5, 4, 2, 2, 2, 4, 4, 6, 6, /* 2 */ 988 | /* 3 */ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, /* 3 */ 989 | /* 4 */ 6, 6, 2, 8, 3, 3, 5, 5, 3, 2, 2, 2, 3, 4, 6, 6, /* 4 */ 990 | /* 5 */ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, /* 5 */ 991 | /* 6 */ 6, 6, 2, 8, 3, 3, 5, 5, 4, 2, 2, 2, 5, 4, 6, 6, /* 6 */ 992 | /* 7 */ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, /* 7 */ 993 | /* 8 */ 2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4, /* 8 */ 994 | /* 9 */ 2, 6, 2, 6, 4, 4, 4, 4, 2, 5, 2, 5, 5, 5, 5, 5, /* 9 */ 995 | /* A */ 2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4, /* A */ 996 | /* B */ 2, 5, 2, 5, 4, 4, 4, 4, 2, 4, 2, 4, 4, 4, 4, 4, /* B */ 997 | /* C */ 2, 6, 2, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6, /* C */ 998 | /* D */ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, /* D */ 999 | /* E */ 2, 6, 2, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6, /* E */ 1000 | /* F */ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7 /* F */ 1001 | }; 1002 | 1003 | 1004 | void nmi6502() { 1005 | push_6502_16(pc); 1006 | push_6502_8(status & ~FLAG_BREAK); 1007 | status |= FLAG_INTERRUPT; 1008 | pc = (ushort)read6502(0xFFFA) | ((ushort)read6502(0xFFFB) << 8); 1009 | } 1010 | 1011 | void irq6502() { 1012 | /* 1013 | push_6502_16(pc); 1014 | push_6502_8(status); 1015 | status |= FLAG_INTERRUPT; 1016 | pc = (ushort)read6502(0xFFFE) | ((ushort)read6502(0xFFFF) << 8); 1017 | */ 1018 | if ((status & FLAG_INTERRUPT) == 0) { 1019 | push_6502_16(pc); 1020 | push_6502_8(status & ~FLAG_BREAK); 1021 | status |= FLAG_INTERRUPT; 1022 | /*pc = mem_6502_read16(0xfffe);*/ 1023 | pc = (ushort)read6502(0xFFFE) | ((ushort)read6502(0xFFFF) << 8); 1024 | } 1025 | } 1026 | 1027 | uint8 callexternal = 0; 1028 | void (*loopexternal)(); 1029 | 1030 | uint32 exec6502(uint32 tickcount) { 1031 | /* 1032 | BUG FIX: 1033 | overflow of unsigned 32 bit integer causes emulation to hang. 1034 | An instruction might cause the tick count to wrap around into the billions. 1035 | 1036 | The system is changed so that now clockticks 6502 is reset every single time that exec is called. 1037 | */ 1038 | clockgoal6502 = tickcount; 1039 | clockticks6502 = 0; 1040 | while (clockticks6502 < clockgoal6502) { 1041 | opcode = read6502(pc++); 1042 | status |= FLAG_CONSTANT; 1043 | penaltyop = 0; 1044 | penaltyaddr = 0; 1045 | (*addrtable[opcode])(); 1046 | (*optable[opcode])(); 1047 | clockticks6502 += ticktable[opcode]; 1048 | if (penaltyop && penaltyaddr) {clockticks6502++;} 1049 | instructions++; 1050 | if (callexternal) (*loopexternal)(); 1051 | } 1052 | return clockticks6502; 1053 | } 1054 | 1055 | uint32 step6502() { 1056 | opcode = read6502(pc++); 1057 | status |= FLAG_CONSTANT; 1058 | 1059 | penaltyop = 0; 1060 | penaltyaddr = 0; 1061 | clockticks6502 = 0; 1062 | (*addrtable[opcode])(); 1063 | (*optable[opcode])(); 1064 | clockticks6502 += ticktable[opcode]; 1065 | /*The following line goes commented out in Mike Chamber's usage of the 6502 emulator for MOARNES*/ 1066 | if (penaltyop && penaltyaddr) clockticks6502++; 1067 | /*clockgoal6502 = clockticks6502; irrelevant.*/ 1068 | 1069 | instructions++; 1070 | 1071 | if (callexternal) (*loopexternal)(); 1072 | return clockticks6502; 1073 | } 1074 | 1075 | void hookexternal(void *funcptr) { 1076 | if (funcptr != (void *)NULL) { 1077 | loopexternal = funcptr; 1078 | callexternal = 1; 1079 | } else callexternal = 0; 1080 | } 1081 | /*FAKE6502 INCLUDE*/ 1082 | #endif 1083 | -------------------------------------------------------------------------------- /fake65c02.h: -------------------------------------------------------------------------------- 1 | /* LICENSING NOTICE: 2 | 3 | This file contains changes incorporated from the non public domain 4 | Commander X16 emulator. 5 | 6 | However, the fake6502 code in their repository is still marked public domain! 7 | 8 | If Michael Steil or Paul Robson (or others who worked on the fake6502 implementation 9 | in the X16 repo) have any issues whatsoever with the public domain license in use here, 10 | 11 | please get them to leave an issue. 12 | */ 13 | 14 | /* Fake65c02 CPU emulator core v1.4 ****************** 15 | *Original Author:Mike Chambers (miker00lz@gmail.com)* 16 | * Author 2: Paul Robson * 17 | *New Author:David MHS Webster (github.com/gek169) * 18 | * Leave a star on github to show thanks for this * 19 | * FULLY PUBLIC DOMAIN, CC0 CODE * 20 | * Which I give to you and the world with absolutely * 21 | * no attribution, monetary compensation, or * 22 | * copyleft requirement. Just write code! * 23 | ***************************************************** 24 | * Let all that you do be done with love * 25 | ***************************************************** 26 | *This version has been overhauled with major bug * 27 | *fixes relating to decimal mode and adc/sbc. I've * 28 | *put the emulator through its paces in kernalemu * 29 | *as well as run it through an instruction exerciser * 30 | *to make sure it works properly. I also discovered * 31 | *bugs in the instruction exerciser while I was at it* 32 | *I might contribute some fixes back to them. * 33 | ***************************************************** 34 | * v1.4 - Update for 65c02 compatibility. * 35 | * v1.3 - refactoring and more bug fixes * 36 | * v1.2 - Major bug fixes in handling adc and sbc * 37 | * v1.1 - Small bugfix in BIT opcode, but it was the * 38 | * difference between a few games in my NES * 39 | * emulator working and being broken! * 40 | * I went through the rest carefully again * 41 | * after fixing it just to make sure I didn't * 42 | * have any other typos! (Dec. 17, 2011) * 43 | * * 44 | * v1.0 - First release (Nov. 24, 2011) * 45 | ***************************************************** 46 | * LICENSE: This source code is released into the * 47 | * public domain, but if you use it please do give * 48 | * credit. I put a lot of effort into writing this! * 49 | * Note by GEK: this is not a requirement. * 50 | ***************************************************** 51 | * Fake6502 is a MOS Technology 6502 CPU emulation * 52 | * engine in C. It was written as part of a Nintendo * 53 | * Entertainment System emulator I've been writing. * 54 | * * 55 | * A couple important things to know about are two * 56 | * defines in the code. One is "UNDOCUMENTED" which, * 57 | * when defined, allows Fake6502 to compile with * 58 | * full support for the more predictable * 59 | * undocumented instructions of the 6502. If it is * 60 | * undefined, undocumented opcodes just act as NOPs. * 61 | * * 62 | * The other define is "NES_CPU", which causes the * 63 | * code to compile without support for binary-coded * 64 | * decimal (BCD) support for the ADC and SBC * 65 | * opcodes. The Ricoh 2A03 CPU in the NES does not * 66 | * support BCD, but is otherwise identical to the * 67 | * standard MOS 6502. (Note that this define is * 68 | * enabled in this file if you haven't changed it * 69 | * yourself. If you're not emulating a NES, you * 70 | * should comment it out.) * 71 | * * 72 | * If you do discover an error in timing accuracy, * 73 | * or operation in general please e-mail me at the * 74 | * address above so that I can fix it. Thank you! * 75 | * * 76 | ***************************************************** 77 | * Usage: * 78 | * * 79 | * Fake6502 requires you to provide two external * 80 | * functions: * 81 | * * 82 | * uint8 read6502(ushort address) * 83 | * void write6502(ushort address, uint8 value) * 84 | * * 85 | * You may optionally pass Fake6502 the pointer to a * 86 | * function which you want to be called after every * 87 | * emulated instruction. This function should be a * 88 | * void with no parameters expected to be passed to * 89 | * it. * 90 | * * 91 | * This can be very useful. For example, in a NES * 92 | * emulator, you check the number of clock ticks * 93 | * that have passed so you can know when to handle * 94 | * APU events. * 95 | * * 96 | * To pass Fake6502 this pointer, use the * 97 | * hookexternal(void *funcptr) function provided. * 98 | * * 99 | * To disable the hook later, pass NULL to it. * 100 | ***************************************************** 101 | * Useful functions in this emulator: * 102 | * * 103 | * void reset6502() * 104 | * - Call this once before you begin execution. * 105 | * * 106 | * uint32 exec6502(uint32 tickcount) * 107 | * - Execute 6502 code up to the next specified * 108 | * count of clock ticks. * 109 | * * 110 | * uint32 step6502() * 111 | * - Execute a single instrution. * 112 | * * 113 | * void irq6502() * 114 | * - Trigger a hardware IRQ in the 6502 core. * 115 | * * 116 | * void nmi6502() * 117 | * - Trigger an NMI in the 6502 core. * 118 | * * 119 | * void hookexternal(void *funcptr) * 120 | * - Pass a pointer to a void function taking no * 121 | * parameters. This will cause Fake6502 to call * 122 | * that function once after each emulated * 123 | * instruction. * 124 | * * 125 | ***************************************************** 126 | * Useful variables in this emulator: * 127 | * * 128 | * uint32 clockticks6502 * 129 | * - A running total of the emulated cycle count * 130 | * during a call to exec6502. * 131 | * uint32 instructions * 132 | * - A running total of the total emulated * 133 | * instruction count. This is not related to * 134 | * clock cycle timing. * 135 | * * 136 | *****************************************************/ 137 | 138 | 139 | /* 140 | 6510 EMULATION NOTES: 141 | 1) On the 6510 processor, the only difference is that the addresses 0 and 1 are used 142 | for data direction and data, respectively. 143 | 144 | 2) The initial value of address 0 should always be 0. 145 | 146 | 3) Read this page 147 | https://ist.uwaterloo.ca/~schepers/MJK/6510.html 148 | 149 | */ 150 | 151 | #include 152 | #ifdef FAKE6502_USE_STDINT 153 | #include 154 | typedef uint16_t ushort; 155 | typedef unsigned char uint8; 156 | typedef uint32_t uint32 157 | #else 158 | typedef unsigned short ushort ; 159 | typedef unsigned char uint8; 160 | 161 | #ifdef FAKE6502_USE_LONG 162 | typedef unsigned long uint32; 163 | #else 164 | typedef unsigned int uint32; 165 | #endif 166 | 167 | #endif 168 | /* 169 | when this is defined, undocumented opcodes are handled. 170 | otherwise, they're simply treated as NOPs. 171 | */ 172 | #define UNDOCUMENTED 173 | 174 | /* 175 | * #define NES_CPU 176 | * when this is defined, the binary-coded decimal (BCD) 177 | * status flag is not honored by ADC and SBC. the 2A03 178 | * CPU in the Nintendo Entertainment System does not 179 | * support BCD operation. 180 | */ 181 | 182 | 183 | #define FLAG_CARRY 0x01 184 | #define FLAG_ZERO 0x02 185 | #define FLAG_INTERRUPT 0x04 186 | #define FLAG_DECIMAL 0x08 187 | /*bits 4 and 5.*/ 188 | #define FLAG_BREAK 0x10 189 | #define FLAG_CONSTANT 0x20 190 | #define FLAG_OVERFLOW 0x40 191 | #define FLAG_SIGN 0x80 192 | 193 | #define BASE_STACK 0x100 194 | 195 | #define saveaccum(n) a = (uint8)((n) & 0x00FF) 196 | 197 | 198 | /*flag modifier macros*/ 199 | #define setcarry() status |= FLAG_CARRY 200 | #define clearcarry() status &= (~FLAG_CARRY) 201 | #define setzero() status |= FLAG_ZERO 202 | #define clearzero() status &= (~FLAG_ZERO) 203 | #define setinterrupt() status |= FLAG_INTERRUPT 204 | #define clearinterrupt() status &= (~FLAG_INTERRUPT) 205 | #define setdecimal() status |= FLAG_DECIMAL 206 | #define cleardecimal() status &= (~FLAG_DECIMAL) 207 | #define setoverflow() status |= FLAG_OVERFLOW 208 | #define clearoverflow() status &= (~FLAG_OVERFLOW) 209 | #define setsign() status |= FLAG_SIGN 210 | #define clearsign() status &= (~FLAG_SIGN) 211 | 212 | 213 | /*flag calculation macros*/ 214 | #define zerocalc(n) {\ 215 | if ((n) & 0x00FF) clearzero();\ 216 | else setzero();\ 217 | } 218 | 219 | #define signcalc(n) {\ 220 | if ((n) & 0x0080) setsign();\ 221 | else clearsign();\ 222 | } 223 | 224 | #define carrycalc(n) {\ 225 | if ((n) & 0xFF00) setcarry();\ 226 | else clearcarry();\ 227 | } 228 | 229 | 230 | #define overflowcalc(n, m, o) { /* n = result, m = accumulator, o = memory */ \ 231 | if (((n) ^ (ushort)(m)) & ((n) ^ (o)) & 0x0080) setoverflow();\ 232 | else clearoverflow();\ 233 | } 234 | 235 | 236 | #ifdef FAKE6502_NOT_STATIC 237 | /*6502 CPU registers*/ 238 | ushort pc; 239 | uint8 sp, a, x, y, status; 240 | /*helper variables*/ 241 | uint32 instructions = 0; 242 | uint32 clockticks6502 = 0; 243 | uint32 clockgoal6502 = 0; 244 | ushort oldpc, ea, reladdr, value, result; 245 | uint8 opcode, oldstatus, waiting6502 = 0; 246 | void reset6502(); 247 | void nmi6502(); 248 | void irq6502(); 249 | void irq6502(); 250 | uint32 exec6502(uint32 tickcount); 251 | uint32 step6502(); 252 | void hookexternal(void *funcptr); 253 | #else 254 | static ushort pc; 255 | static uint8 sp, a, x, y, status; 256 | static uint32 instructions = 0; 257 | static uint32 clockticks6502 = 0; 258 | static uint32 clockgoal6502 = 0; 259 | static ushort oldpc, ea, reladdr, value, result; 260 | static uint8 opcode, oldstatus, waiting6502 = 0; 261 | #endif 262 | /*externally supplied functions*/ 263 | extern uint8 read6502(ushort address); 264 | extern void write6502(ushort address, uint8 value); 265 | 266 | #ifndef FAKE6502_INCLUDE 267 | 268 | /*a few general functions used by various other functions*/ 269 | static void push_6502_16(ushort pushval) { 270 | write6502(BASE_STACK + sp, (pushval >> 8) & 0xFF); 271 | write6502(BASE_STACK + ((sp - 1) & 0xFF), pushval & 0xFF); 272 | sp -= 2; 273 | } 274 | 275 | static void push_6502_8(uint8 pushval) { 276 | write6502(BASE_STACK + sp--, pushval); 277 | } 278 | 279 | static ushort pull_6502_16() { 280 | ushort temp16; 281 | temp16 = read6502(BASE_STACK + ((sp + 1) & 0xFF)) | ((ushort)read6502(BASE_STACK + ((sp + 2) & 0xFF)) << 8); 282 | sp += 2; 283 | return(temp16); 284 | } 285 | 286 | static uint8 pull_6502_8() { 287 | return (read6502(BASE_STACK + ++sp)); 288 | } 289 | 290 | static ushort mem_6502_read16(ushort addr) { 291 | return ((ushort)read6502(addr) | 292 | ((ushort)read6502(addr + 1) << 8)); 293 | } 294 | 295 | void reset6502() { 296 | /* 297 | pc = (ushort)read6502(0xFFFC) | ((ushort)read6502(0xFFFD) << 8); 298 | a = 0; 299 | x = 0; 300 | y = 0; 301 | sp = 0xFD; 302 | status |= FLAG_CONSTANT; 303 | */ 304 | pc = mem_6502_read16(0xfffc); 305 | a = 0; 306 | x = 0; 307 | y = 0; 308 | sp = 0xFD; 309 | cleardecimal(); 310 | status |= FLAG_CONSTANT; 311 | setinterrupt(); 312 | } 313 | 314 | 315 | static void (*addrtable[256])(); 316 | static void (*optable[256])(); 317 | static uint8 penaltyop, penaltyaddr; 318 | 319 | /*addressing mode functions, calculates effective addresses*/ 320 | static void imp() { 321 | } 322 | 323 | /*addressing mode functions, calculates effective addresses*/ 324 | static void acc() { 325 | } 326 | 327 | /*addressing mode functions, calculates effective addresses*/ 328 | static void imm() { 329 | ea = pc++; 330 | } 331 | 332 | static void zp() { /*zero-page*/ 333 | ea = (ushort)read6502((ushort)pc++); 334 | } 335 | 336 | static void zpx() { /*zero-page,X*/ 337 | ea = ((ushort)read6502((ushort)pc++) + (ushort)x) & 0xFF; /*zero-page wraparound*/ 338 | } 339 | 340 | static void zpy() { /*zero-page,Y*/ 341 | ea = ((ushort)read6502((ushort)pc++) + (ushort)y) & 0xFF; /*zero-page wraparound*/ 342 | } 343 | 344 | static void rel() { /*relative for branch ops (8-bit immediate value, sign-extended)*/ 345 | reladdr = (ushort)read6502(pc++); 346 | if (reladdr & 0x80) reladdr |= 0xFF00; 347 | } 348 | 349 | static void abso() { /*absolute*/ 350 | ea = (ushort)read6502(pc) | ((ushort)read6502(pc+1) << 8); 351 | pc += 2; 352 | } 353 | 354 | static void absx() { /*absolute,X*/ 355 | ushort startpage; 356 | ea = ((ushort)read6502(pc) | ((ushort)read6502(pc+1) << 8)); 357 | startpage = ea & 0xFF00; 358 | ea += (ushort)x; 359 | 360 | if (startpage != (ea & 0xFF00)) { /*one cycle penlty for page-crossing on some opcodes*/ 361 | penaltyaddr = 1; 362 | } 363 | 364 | pc += 2; 365 | } 366 | 367 | static void absy() { /*absolute,Y*/ 368 | ushort startpage; 369 | ea = ((ushort)read6502(pc) | ((ushort)read6502(pc+1) << 8)); 370 | startpage = ea & 0xFF00; 371 | ea += (ushort)y; 372 | 373 | if (startpage != (ea & 0xFF00)) { /*one cycle penlty for page-crossing on some opcodes*/ 374 | penaltyaddr = 1; 375 | } 376 | 377 | pc += 2; 378 | } 379 | 380 | static void ind() { /*indirect*/ 381 | ushort eahelp, eahelp2; 382 | eahelp = (ushort)read6502(pc) | (ushort)((ushort)read6502(pc+1) << 8); 383 | /*Page boundary bug is absent on CMOS models.*/ 384 | eahelp2 = (eahelp+1) & 0xffFF; 385 | ea = (ushort)read6502(eahelp) | ((ushort)read6502(eahelp2) << 8); 386 | pc += 2; 387 | } 388 | 389 | static void indx() { /* (indirect,X)*/ 390 | ushort eahelp; 391 | eahelp = (ushort)(((ushort)read6502(pc++) + (ushort)x) & 0xFF); /*zero-page wraparound for table pointer*/ 392 | ea = (ushort)read6502(eahelp & 0x00FF) | ((ushort)read6502((eahelp+1) & 0x00FF) << 8); 393 | } 394 | 395 | static void indy() { /* (indirect),Y*/ 396 | ushort eahelp, eahelp2, startpage; 397 | eahelp = (ushort)read6502(pc++); 398 | eahelp2 = (eahelp & 0xFF00) | ((eahelp + 1) & 0x00FF); /*zero-page wraparound*/ 399 | ea = (ushort)read6502(eahelp) | ((ushort)read6502(eahelp2) << 8); 400 | startpage = ea & 0xFF00; 401 | ea += (ushort)y; 402 | 403 | if (startpage != (ea & 0xFF00)) { /*one cycle penlty for page-crossing on some opcodes*/ 404 | penaltyaddr = 1; 405 | } 406 | } 407 | 408 | static void zprel() { /* zero-page, relative for branch ops (8-bit immediate value, sign-extended)*/ 409 | ea = (ushort)read6502(pc); 410 | reladdr = (ushort)read6502(pc+1); 411 | if (reladdr & 0x80) reladdr |= 0xFF00; 412 | 413 | pc += 2; 414 | } 415 | 416 | static ushort getvalue() { 417 | if (addrtable[opcode] == acc) return((ushort)a); 418 | else return((ushort)read6502(ea)); 419 | } 420 | 421 | static ushort getvalue16() { 422 | return((ushort)read6502(ea) | ((ushort)read6502(ea+1) << 8)); 423 | } 424 | 425 | static void putvalue(ushort saveval) { 426 | if (addrtable[opcode] == acc) a = (uint8)(saveval & 0x00FF); 427 | else write6502(ea, (saveval & 0x00FF)); 428 | } 429 | 430 | 431 | /*instruction handler functions*/ 432 | static void adc() { 433 | penaltyop = 1; 434 | if (status & FLAG_DECIMAL) { 435 | ushort AL, A /*, result_dec */; 436 | A = a; 437 | value = getvalue(); 438 | /*result_dec = (ushort)A + value + (ushort)(status & FLAG_CARRY); dec*/ 439 | AL = (A & 0x0F) + (value & 0x0F) + (ushort)(status & FLAG_CARRY); /*SEQ 1A or 2A*/ 440 | if(AL >= 0xA) AL = ((AL + 0x06) & 0x0F) + 0x10; /*1B or 2B*/ 441 | A = (A & 0xF0) + (value & 0xF0) + AL; /*1C or 2C*/ 442 | if(A >= 0xA0) A += 0x60; /*1E*/ 443 | result = A; /*1F*/ 444 | if(A & 0xff80) setoverflow(); else clearoverflow(); 445 | if(A >= 0x100) setcarry(); else clearcarry(); /*SEQ 1G*/ 446 | zerocalc(result); /* 65C02 change, Decimal Arithmetic sets NZV */ 447 | signcalc(result); 448 | clockticks6502++; 449 | } else { 450 | value = getvalue(); 451 | result = (ushort)a + value + (ushort)(status & FLAG_CARRY); 452 | 453 | carrycalc(result); 454 | zerocalc(result); 455 | overflowcalc(result, a, value); 456 | signcalc(result); 457 | } 458 | saveaccum(result); 459 | } 460 | 461 | static void and() { 462 | penaltyop = 1; 463 | value = getvalue(); 464 | result = (ushort)a & value; 465 | 466 | zerocalc(result); 467 | signcalc(result); 468 | 469 | saveaccum(result); 470 | } 471 | 472 | static void asl() { 473 | value = getvalue(); 474 | result = value << 1; 475 | 476 | carrycalc(result); 477 | zerocalc(result); 478 | signcalc(result); 479 | 480 | putvalue(result); 481 | } 482 | 483 | static void bcc() { 484 | if ((status & FLAG_CARRY) == 0) { 485 | oldpc = pc; 486 | pc += reladdr; 487 | if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; /*check if jump crossed a page boundary*/ 488 | else clockticks6502++; 489 | } 490 | } 491 | 492 | static void bcs() { 493 | if ((status & FLAG_CARRY) == FLAG_CARRY) { 494 | oldpc = pc; 495 | pc += reladdr; 496 | if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; /*check if jump crossed a page boundary*/ 497 | else clockticks6502++; 498 | } 499 | } 500 | 501 | static void beq() { 502 | if ((status & FLAG_ZERO) == FLAG_ZERO) { 503 | oldpc = pc; 504 | pc += reladdr; 505 | if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; /*check if jump crossed a page boundary*/ 506 | else clockticks6502++; 507 | } 508 | } 509 | 510 | static void bit() { 511 | value = getvalue(); 512 | result = (ushort)a & value; 513 | zerocalc(result); 514 | status = (status & 0x3F) | (uint8)(value & 0xC0); 515 | } 516 | 517 | static void bit_imm() { 518 | value = getvalue(); 519 | result = (ushort)a & value; 520 | zerocalc(result); 521 | } 522 | 523 | static void bmi() { 524 | if ((status & FLAG_SIGN) == FLAG_SIGN) { 525 | oldpc = pc; 526 | pc += reladdr; 527 | if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; /*check if jump crossed a page boundary*/ 528 | else clockticks6502++; 529 | } 530 | } 531 | 532 | static void bne() { 533 | if ((status & FLAG_ZERO) == 0) { 534 | oldpc = pc; 535 | pc += reladdr; 536 | if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; /*check if jump crossed a page boundary*/ 537 | else clockticks6502++; 538 | } 539 | } 540 | 541 | static void bpl() { 542 | if ((status & FLAG_SIGN) == 0) { 543 | oldpc = pc; 544 | pc += reladdr; 545 | if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; /*check if jump crossed a page boundary*/ 546 | else clockticks6502++; 547 | } 548 | } 549 | 550 | static void brk_6502() { 551 | pc++; 552 | push_6502_16(pc); 553 | push_6502_8(status | FLAG_BREAK); 554 | setinterrupt(); 555 | cleardecimal(); /*CMOS change*/ 556 | pc = (ushort)read6502(0xFFFE) | ((ushort)read6502(0xFFFF) << 8); 557 | } 558 | 559 | static void bvc() { 560 | if ((status & FLAG_OVERFLOW) == 0) { 561 | oldpc = pc; 562 | pc += reladdr; 563 | if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; /*check if jump crossed a page boundary*/ 564 | else clockticks6502++; 565 | } 566 | } 567 | 568 | static void bvs() { 569 | if ((status & FLAG_OVERFLOW) == FLAG_OVERFLOW) { 570 | oldpc = pc; 571 | pc += reladdr; 572 | if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; /*check if jump crossed a page boundary*/ 573 | else clockticks6502++; 574 | } 575 | } 576 | 577 | static void clc() { 578 | clearcarry(); 579 | } 580 | 581 | static void cld() { 582 | cleardecimal(); 583 | } 584 | 585 | static void cli() { 586 | clearinterrupt(); 587 | } 588 | 589 | static void clv() { 590 | clearoverflow(); 591 | } 592 | 593 | static void cmp() { 594 | penaltyop = 1; 595 | value = getvalue(); 596 | result = (ushort)a - value; 597 | 598 | if (a >= (uint8)(value & 0x00FF)) setcarry(); 599 | else clearcarry(); 600 | if (a == (uint8)(value & 0x00FF)) setzero(); 601 | else clearzero(); 602 | signcalc(result); 603 | } 604 | 605 | static void cpx() { 606 | value = getvalue(); 607 | result = (ushort)x - value; 608 | 609 | if (x >= (uint8)(value & 0x00FF)) setcarry(); 610 | else clearcarry(); 611 | if (x == (uint8)(value & 0x00FF)) setzero(); 612 | else clearzero(); 613 | signcalc(result); 614 | } 615 | 616 | static void cpy() { 617 | value = getvalue(); 618 | result = (ushort)y - value; 619 | 620 | if (y >= (uint8)(value & 0x00FF)) setcarry(); 621 | else clearcarry(); 622 | if (y == (uint8)(value & 0x00FF)) setzero(); 623 | else clearzero(); 624 | signcalc(result); 625 | } 626 | 627 | static void dec() { 628 | value = getvalue(); 629 | result = value - 1; 630 | 631 | zerocalc(result); 632 | signcalc(result); 633 | 634 | putvalue(result); 635 | } 636 | 637 | static void dex() { 638 | x--; 639 | 640 | zerocalc(x); 641 | signcalc(x); 642 | } 643 | 644 | static void dey() { 645 | y--; 646 | 647 | zerocalc(y); 648 | signcalc(y); 649 | } 650 | 651 | static void eor() { 652 | penaltyop = 1; 653 | value = getvalue(); 654 | result = (ushort)a ^ value; 655 | 656 | zerocalc(result); 657 | signcalc(result); 658 | 659 | saveaccum(result); 660 | } 661 | 662 | static void inc() { 663 | value = getvalue(); 664 | result = value + 1; 665 | 666 | zerocalc(result); 667 | signcalc(result); 668 | 669 | putvalue(result); 670 | } 671 | 672 | static void inx() { 673 | x++; 674 | 675 | zerocalc(x); 676 | signcalc(x); 677 | } 678 | 679 | static void iny() { 680 | y++; 681 | 682 | zerocalc(y); 683 | signcalc(y); 684 | } 685 | 686 | static void jmp() { 687 | pc = ea; 688 | /*if(opcode == 0x6c) clockticks6502++;*/ 689 | } 690 | 691 | static void jsr() { 692 | push_6502_16(pc - 1); 693 | pc = ea; 694 | } 695 | 696 | static void lda() { 697 | penaltyop = 1; 698 | value = getvalue(); 699 | a = (uint8)(value & 0x00FF); 700 | 701 | zerocalc(a); 702 | signcalc(a); 703 | } 704 | 705 | static void ldx() { 706 | penaltyop = 1; 707 | value = getvalue(); 708 | x = (uint8)(value & 0x00FF); 709 | 710 | zerocalc(x); 711 | signcalc(x); 712 | } 713 | 714 | static void ldy() { 715 | penaltyop = 1; 716 | value = getvalue(); 717 | y = (uint8)(value & 0x00FF); 718 | 719 | zerocalc(y); 720 | signcalc(y); 721 | } 722 | 723 | static void lsr() { 724 | value = getvalue(); 725 | result = value >> 1; 726 | 727 | if (value & 1) setcarry(); 728 | else clearcarry(); 729 | zerocalc(result); 730 | signcalc(result); 731 | 732 | putvalue(result); 733 | } 734 | 735 | static void nop() { 736 | switch (opcode) { 737 | case 0x1C: 738 | case 0x3C: 739 | case 0x5C: 740 | case 0x7C: 741 | case 0xDC: 742 | case 0xFC: 743 | penaltyop = 1; 744 | break; 745 | } 746 | } 747 | 748 | static void ora() { 749 | penaltyop = 1; 750 | value = getvalue(); 751 | result = (ushort)a | value; 752 | 753 | zerocalc(result); 754 | signcalc(result); 755 | 756 | saveaccum(result); 757 | } 758 | 759 | static void pha() { 760 | push_6502_8(a); 761 | } 762 | 763 | static void php() { 764 | push_6502_8(status | FLAG_BREAK); 765 | } 766 | 767 | static void pla() { 768 | a = pull_6502_8(); 769 | 770 | zerocalc(a); 771 | signcalc(a); 772 | } 773 | 774 | static void plp() { 775 | status = pull_6502_8() | FLAG_CONSTANT; 776 | } 777 | 778 | static void rol() { 779 | value = getvalue(); 780 | result = (value << 1) | (status & FLAG_CARRY); 781 | 782 | carrycalc(result); 783 | zerocalc(result); 784 | signcalc(result); 785 | 786 | putvalue(result); 787 | } 788 | 789 | static void ror() { 790 | value = getvalue(); 791 | result = (value >> 1) | ((status & FLAG_CARRY) << 7); 792 | 793 | if (value & 1) setcarry(); 794 | else clearcarry(); 795 | zerocalc(result); 796 | signcalc(result); 797 | 798 | putvalue(result); 799 | } 800 | 801 | static void rti() { 802 | status = pull_6502_8(); 803 | value = pull_6502_16(); 804 | pc = value; 805 | } 806 | 807 | static void rts() { 808 | value = pull_6502_16(); 809 | pc = value + 1; 810 | } 811 | 812 | static void sbc() { 813 | penaltyop = 1; 814 | if (status & FLAG_DECIMAL) { 815 | ushort result_dec, A, AL, B, C; 816 | A = a; 817 | C = (ushort)(status & FLAG_CARRY); 818 | value = getvalue(); B = value; value = value ^ 0x00FF; 819 | result_dec = (ushort)a + value + C; 820 | /*Both Cmos and Nmos*/ 821 | carrycalc(result_dec); 822 | overflowcalc(result_dec, a, value); 823 | /*SEQUENCE 4 IS CMOS ONLY*/ 824 | AL = (A & 0x0F) - (B & 0x0F) + C - 1; /*4a*/ 825 | A = A - B + C - 1; /*4b*/ 826 | if(A & 0x8000) A = A - 0x60; /*4C*/ 827 | if(AL & 0x8000) A = A - 0x06; /*4D*/ 828 | result = A & 0xff; /*4E*/ 829 | signcalc(result); 830 | zerocalc(result); 831 | clockticks6502++; 832 | } else { 833 | value = getvalue() ^ 0x00FF; 834 | result = (ushort)a + value + (ushort)(status & FLAG_CARRY); 835 | carrycalc(result); 836 | zerocalc(result); 837 | overflowcalc(result, a, value); 838 | signcalc(result); 839 | } 840 | saveaccum(result); 841 | } 842 | 843 | static void sec() { 844 | setcarry(); 845 | } 846 | 847 | static void sed() { 848 | setdecimal(); 849 | } 850 | 851 | static void sei() { 852 | setinterrupt(); 853 | } 854 | 855 | static void sta() { 856 | putvalue(a); 857 | } 858 | 859 | static void stx() { 860 | putvalue(x); 861 | } 862 | 863 | static void sty() { 864 | putvalue(y); 865 | } 866 | 867 | static void tax() { 868 | x = a; 869 | 870 | zerocalc(x); 871 | signcalc(x); 872 | } 873 | 874 | static void tay() { 875 | y = a; 876 | 877 | zerocalc(y); 878 | signcalc(y); 879 | } 880 | 881 | static void tsx() { 882 | x = sp; 883 | 884 | zerocalc(x); 885 | signcalc(x); 886 | } 887 | 888 | static void txa() { 889 | a = x; 890 | 891 | zerocalc(a); 892 | signcalc(a); 893 | } 894 | 895 | static void txs() { 896 | sp = x; 897 | } 898 | 899 | static void tya() { 900 | a = y; 901 | 902 | zerocalc(a); 903 | signcalc(a); 904 | } 905 | 906 | 907 | /* 908 | CMOS ADDITIONS 909 | */ 910 | static void ind0() { 911 | ushort eahelp, eahelp2; 912 | eahelp = (ushort)read6502(pc++); 913 | eahelp2 = (eahelp & 0xFF00) | ((eahelp + 1) & 0x00FF); /*zero page wrap*/ 914 | ea = (ushort)read6502(eahelp) | ((ushort)read6502(eahelp2) << 8); 915 | } 916 | 917 | static void ainx() { /* abs indexed bra*/ 918 | ushort eahelp, eahelp2; 919 | eahelp = (ushort)read6502(pc) | (ushort)((ushort)read6502(pc+1) << 8); 920 | eahelp = (eahelp + (ushort)x) & 0xFFFF; 921 | eahelp2 = eahelp + 1; /*No bug on CMOS*/ 922 | ea = (ushort)read6502(eahelp) | ((ushort)read6502(eahelp2) << 8); 923 | pc += 2; 924 | } 925 | 926 | static void stz(){ 927 | putvalue(0); 928 | } 929 | 930 | static void bra() { 931 | oldpc = pc; 932 | pc += reladdr; 933 | if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; /*page boundary*/ 934 | else clockticks6502++; 935 | } 936 | 937 | 938 | static void phx() { 939 | push_6502_8(x); 940 | } 941 | 942 | static void plx() { 943 | x = pull_6502_8(); 944 | 945 | zerocalc(x); 946 | signcalc(x); 947 | } 948 | 949 | static void phy() { 950 | push_6502_8(y); 951 | } 952 | 953 | static void ply() { 954 | y = pull_6502_8(); 955 | 956 | zerocalc(y); 957 | signcalc(y); 958 | } 959 | 960 | 961 | static void tsb() { 962 | value = getvalue(); 963 | result = (ushort)a & value; 964 | zerocalc(result); 965 | result = value | a; 966 | putvalue(result); 967 | } 968 | 969 | static void trb() { 970 | value = getvalue(); 971 | result = (ushort)a & value; 972 | zerocalc(result); 973 | result = value & (a ^ 0xFF); 974 | putvalue(result); 975 | } 976 | 977 | static void db6502(){ 978 | pc--; /*This is how we wait until RESET.*/ 979 | return; 980 | } 981 | 982 | static void wai() { 983 | if (~status & FLAG_INTERRUPT) waiting6502 = 1; 984 | } 985 | static void bbr(ushort bitmask) 986 | { 987 | if ((getvalue() & bitmask) == 0) { 988 | oldpc = pc; 989 | pc += reladdr; 990 | if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; /*check if jump crossed a page boundary*/ 991 | else clockticks6502++; 992 | } 993 | } 994 | static void bbr0() {bbr(0x01);} 995 | static void bbr1() {bbr(0x02);} 996 | static void bbr2() {bbr(0x04);} 997 | static void bbr3() {bbr(0x08);} 998 | static void bbr4() {bbr(0x10);} 999 | static void bbr5() {bbr(0x20);} 1000 | static void bbr6() {bbr(0x40);} 1001 | static void bbr7() {bbr(0x80);} 1002 | 1003 | static void bbs(ushort bitmask) 1004 | { 1005 | if ((getvalue() & bitmask) != 0) { 1006 | oldpc = pc; 1007 | pc += reladdr; 1008 | if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; /*check if jump crossed a page boundary*/ 1009 | else clockticks6502++; 1010 | } 1011 | } 1012 | static void bbs0() {bbs(0x01);} 1013 | static void bbs1() {bbs(0x02);} 1014 | static void bbs2() {bbs(0x04);} 1015 | static void bbs3() {bbs(0x08);} 1016 | static void bbs4() {bbs(0x10);} 1017 | static void bbs5() {bbs(0x20);} 1018 | static void bbs6() {bbs(0x40);} 1019 | static void bbs7() {bbs(0x80);} 1020 | 1021 | 1022 | static void smb0() { putvalue(getvalue() | 0x01); } 1023 | static void smb1() { putvalue(getvalue() | 0x02); } 1024 | static void smb2() { putvalue(getvalue() | 0x04); } 1025 | static void smb3() { putvalue(getvalue() | 0x08); } 1026 | static void smb4() { putvalue(getvalue() | 0x10); } 1027 | static void smb5() { putvalue(getvalue() | 0x20); } 1028 | static void smb6() { putvalue(getvalue() | 0x40); } 1029 | static void smb7() { putvalue(getvalue() | 0x80); } 1030 | 1031 | static void rmb0() { putvalue(getvalue() & ~0x01); } 1032 | static void rmb1() { putvalue(getvalue() & ~0x02); } 1033 | static void rmb2() { putvalue(getvalue() & ~0x04); } 1034 | static void rmb3() { putvalue(getvalue() & ~0x08); } 1035 | static void rmb4() { putvalue(getvalue() & ~0x10); } 1036 | static void rmb5() { putvalue(getvalue() & ~0x20); } 1037 | static void rmb6() { putvalue(getvalue() & ~0x40); } 1038 | static void rmb7() { putvalue(getvalue() & ~0x80); } 1039 | 1040 | /*undocumented instructions~~~~~~~~~~~~~~~~~~~~~~~~~*/ 1041 | /* 1042 | #define lax nop 1043 | #define sax nop 1044 | #define dcp nop 1045 | #define isb nop 1046 | #define slo nop 1047 | #define rla nop 1048 | #define sre nop 1049 | #define rra nop 1050 | */ 1051 | 1052 | static void (*addrtable[256])() = { 1053 | /* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | */ 1054 | /* 0 */ imp, indx, imp, imp, zp, zp, zp, zp, imp, imm, acc, imp, abso, abso, abso,zprel, /* 0 */ 1055 | /* 1 */ rel, indy, ind0, imp, zp, zpx, zpx, zp, imp, absy, acc, imp, abso, absx, absx,zprel, /* 1 */ 1056 | /* 2 */ abso, indx, imp, imp, zp, zp, zp, zp, imp, imm, acc, imp, abso, abso, abso,zprel, /* 2 */ 1057 | /* 3 */ rel, indy, ind0, imp, zpx, zpx, zpx, zp, imp, absy, acc, imp, absx, absx, absx,zprel, /* 3 */ 1058 | /* 4 */ imp, indx, imp, imp, imp, zp, zp, zp, imp, imm, acc, imp, abso, abso, abso,zprel, /* 4 */ 1059 | /* 5 */ rel, indy, ind0, imp, imp, zpx, zpx, zp, imp, absy, imp, imp, imp, absx, absx,zprel, /* 5 */ 1060 | /* 6 */ imp, indx, imp, imp, zp, zp, zp, zp, imp, imm, acc, imp, ind, abso, abso,zprel, /* 6 */ 1061 | /* 7 */ rel, indy, ind0, imp, zpx, zpx, zpx, zp, imp, absy, imp, imp, ainx, absx, absx,zprel, /* 7 */ 1062 | /* 8 */ rel, indx, imp, imp, zp, zp, zp, zp, imp, imm, imp, imp, abso, abso, abso,zprel, /* 8 */ 1063 | /* 9 */ rel, indy, ind0, imp, zpx, zpx, zpy, zp, imp, absy, imp, imp, abso, absx, absx,zprel, /* 9 */ 1064 | /* A */ imm, indx, imm, imp, zp, zp, zp, zp, imp, imm, imp, imp, abso, abso, abso,zprel, /* A */ 1065 | /* B */ rel, indy, ind0, imp, zpx, zpx, zpy, zp, imp, absy, imp, imp, absx, absx, absy,zprel, /* B */ 1066 | /* C */ imm, indx, imp, imp, zp, zp, zp, zp, imp, imm, imp, imp, abso, abso, abso,zprel, /* C */ 1067 | /* D */ rel, indy, ind0, imp, imp, zpx, zpx, zp, imp, absy, imp, imp, imp, absx, absx,zprel, /* D */ 1068 | /* E */ imm, indx, imp, imp, zp, zp, zp, zp, imp, imm, imp, imp, abso, abso, abso,zprel, /* E */ 1069 | /* F */ rel, indy, ind0, imp, imp, zpx, zpx, zp, imp, absy, imp, imp, imp, absx, absx,zprel /* F */ 1070 | }; 1071 | 1072 | /* 1073 | NOTE: the "db6502" instruction is *supposed* to be "wait until hardware reset" 1074 | */ 1075 | 1076 | static void (*optable[256])() = { 1077 | /* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | */ 1078 | /* 0 */ brk_6502, ora, nop, nop, tsb, ora, asl, rmb0, php, ora, asl, nop, tsb, ora, asl, bbr0, /* 0 */ 1079 | /* 1 */ bpl, ora, ora, nop, trb, ora, asl, rmb1, clc, ora, inc, nop, trb, ora, asl, bbr1, /* 1 */ 1080 | /* 2 */ jsr, and, nop, nop, bit, and, rol, rmb2, plp, and, rol, nop, bit, and, rol, bbr2, /* 2 */ 1081 | /* 3 */ bmi, and, and, nop, bit, and, rol, rmb3, sec, and, dec, nop, bit, and, rol, bbr3, /* 3 */ 1082 | /* 4 */ rti, eor, nop, nop, nop, eor, lsr, rmb4, pha, eor, lsr, nop, jmp, eor, lsr, bbr4, /* 4 */ 1083 | /* 5 */ bvc, eor, eor, nop, nop, eor, lsr, rmb5, cli, eor, phy, nop, nop, eor, lsr, bbr5, /* 5 */ 1084 | /* 6 */ rts, adc, nop, nop, stz, adc, ror, rmb6, pla, adc, ror, nop, jmp, adc, ror, bbr6, /* 6 */ 1085 | /* 7 */ bvs, adc, adc, nop, stz, adc, ror, rmb7, sei, adc, ply, nop, jmp, adc, ror, bbr7, /* 7 */ 1086 | /* 8 */ bra, sta, nop, nop, sty, sta, stx, smb0, dey, bit_imm, txa, nop, sty, sta, stx, bbs0, /* 8 */ 1087 | /* 9 */ bcc, sta, sta, nop, sty, sta, stx, smb1, tya, sta, txs, nop, stz, sta, stz, bbs1, /* 9 */ 1088 | /* A */ ldy, lda, ldx, nop, ldy, lda, ldx, smb2, tay, lda, tax, nop, ldy, lda, ldx, bbs2, /* A */ 1089 | /* B */ bcs, lda, lda, nop, ldy, lda, ldx, smb3, clv, lda, tsx, nop, ldy, lda, ldx, bbs3, /* B */ 1090 | /* C */ cpy, cmp, nop, nop, cpy, cmp, dec, smb4, iny, cmp, dex, wai, cpy, cmp, dec, bbs4, /* C */ 1091 | /* D */ bne, cmp, cmp, nop, nop, cmp, dec, smb5, cld, cmp, phx, db6502, nop, cmp, dec, bbs5, /* D */ 1092 | /* E */ cpx, sbc, nop, nop, cpx, sbc, inc, smb6, inx, sbc, nop, nop, cpx, sbc, inc, bbs6, /* E */ 1093 | /* F */ beq, sbc, sbc, nop, nop, sbc, inc, smb7, sed, sbc, plx, nop, nop, sbc, inc, bbs7 /* F */ 1094 | }; 1095 | 1096 | static const uint32 ticktable[256] = { 1097 | /* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | */ 1098 | /* 0 */ 7, 6, 2, 2, 5, 3, 5, 5, 3, 2, 2, 2, 6, 4, 6, 2, /* 0 */ 1099 | /* 1 */ 2, 5, 5, 2, 5, 4, 6, 5, 2, 4, 2, 2, 6, 4, 7, 2, /* 1 */ 1100 | /* 2 */ 6, 6, 2, 2, 3, 3, 5, 5, 4, 2, 2, 2, 4, 4, 6, 2, /* 2 */ 1101 | /* 3 */ 2, 5, 5, 2, 4, 4, 6, 5, 2, 4, 2, 2, 4, 4, 7, 2, /* 3 */ 1102 | /* 4 */ 6, 6, 2, 2, 2, 3, 5, 5, 3, 2, 2, 2, 3, 4, 6, 2, /* 4 */ 1103 | /* 5 */ 2, 5, 5, 2, 2, 4, 6, 5, 2, 4, 3, 2, 2, 4, 7, 2, /* 5 */ 1104 | /* 6 */ 6, 6, 2, 2, 3, 3, 5, 5, 4, 2, 2, 2, 6, 4, 6, 2, /* 6 */ 1105 | /* 7 */ 2, 5, 5, 2, 4, 4, 6, 5, 2, 4, 4, 2, 6, 4, 7, 2, /* 7 */ 1106 | /* 8 */ 3, 6, 2, 2, 3, 3, 3, 5, 2, 2, 2, 2, 4, 4, 4, 2, /* 8 */ 1107 | /* 9 */ 2, 6, 5, 2, 4, 4, 4, 5, 2, 5, 2, 2, 4, 5, 5, 2, /* 9 */ 1108 | /* A */ 2, 6, 2, 2, 3, 3, 3, 5, 2, 2, 2, 2, 4, 4, 4, 2, /* A */ 1109 | /* B */ 2, 5, 5, 2, 4, 4, 4, 5, 2, 4, 2, 2, 4, 4, 4, 2, /* B */ 1110 | /* C */ 2, 6, 2, 2, 3, 3, 5, 5, 2, 2, 2, 3, 4, 4, 6, 2, /* C */ 1111 | /* D */ 2, 5, 5, 2, 2, 4, 6, 5, 2, 4, 3, 1, 2, 4, 7, 2, /* D */ 1112 | /* E */ 2, 6, 2, 2, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 2, /* E */ 1113 | /* F */ 2, 5, 5, 2, 2, 4, 6, 5, 2, 4, 4, 2, 2, 4, 7, 2 /* F */ 1114 | }; 1115 | 1116 | 1117 | void nmi6502() { 1118 | push_6502_16(pc); 1119 | push_6502_8(status & ~FLAG_BREAK); 1120 | setinterrupt(); 1121 | cleardecimal(); 1122 | pc = (ushort)read6502(0xFFFA) | ((ushort)read6502(0xFFFB) << 8); 1123 | waiting6502 = 0; 1124 | } 1125 | 1126 | void irq6502() { 1127 | /* 1128 | push_6502_16(pc); 1129 | push_6502_8(status); 1130 | status |= FLAG_INTERRUPT; 1131 | pc = (ushort)read6502(0xFFFE) | ((ushort)read6502(0xFFFF) << 8); 1132 | */ 1133 | if ((status & FLAG_INTERRUPT) == 0) { 1134 | push_6502_16(pc); 1135 | push_6502_8(status & ~FLAG_BREAK); 1136 | setinterrupt(); 1137 | cleardecimal(); 1138 | /*pc = mem_6502_read16(0xfffe);*/ 1139 | pc = (ushort)read6502(0xFFFE) | ((ushort)read6502(0xFFFF) << 8); 1140 | waiting6502 = 0; 1141 | } 1142 | 1143 | } 1144 | 1145 | uint8 callexternal = 0; 1146 | void (*loopexternal)(); 1147 | 1148 | uint32 exec6502(uint32 tickcount) { 1149 | /* 1150 | BUG FIX: 1151 | overflow of unsigned 32 bit integer causes emulation to hang. 1152 | An instruction might cause the tick count to wrap around into the billions. 1153 | 1154 | The system is changed so that now clockticks 6502 is reset every single time that exec is called. 1155 | */ 1156 | if(waiting6502) return tickcount; 1157 | clockgoal6502 = tickcount; 1158 | clockticks6502 = 0; 1159 | while (clockticks6502 < clockgoal6502) { 1160 | opcode = read6502(pc++); 1161 | status |= FLAG_CONSTANT; 1162 | penaltyop = 0; 1163 | penaltyaddr = 0; 1164 | (*addrtable[opcode])(); 1165 | (*optable[opcode])(); 1166 | clockticks6502 += ticktable[opcode]; 1167 | if (penaltyop && penaltyaddr) {clockticks6502++;} 1168 | instructions++; 1169 | if (callexternal) (*loopexternal)(); 1170 | } 1171 | return clockticks6502; 1172 | } 1173 | 1174 | uint32 step6502() { 1175 | if(waiting6502) return 1; 1176 | opcode = read6502(pc++); 1177 | status |= FLAG_CONSTANT; 1178 | 1179 | penaltyop = 0; 1180 | penaltyaddr = 0; 1181 | clockticks6502 = 0; 1182 | (*addrtable[opcode])(); 1183 | (*optable[opcode])(); 1184 | clockticks6502 += ticktable[opcode]; 1185 | /*The following line goes commented out in Mike Chamber's usage of the 6502 emulator for MOARNES*/ 1186 | if (penaltyop && penaltyaddr) clockticks6502++; 1187 | /*clockgoal6502 = clockticks6502; irrelevant.*/ 1188 | 1189 | instructions++; 1190 | 1191 | if (callexternal) (*loopexternal)(); 1192 | return clockticks6502; 1193 | } 1194 | 1195 | void hookexternal(void *funcptr) { 1196 | if (funcptr != (void *)NULL) { 1197 | loopexternal = funcptr; 1198 | callexternal = 1; 1199 | } else callexternal = 0; 1200 | } 1201 | 1202 | /* 1203 | Check all changes against 1204 | http://6502.org/tutorials/65c02opcodes.html 1205 | and 1206 | https://github.com/commanderx16/x16-emulator 1207 | 1208 | The commander X16 emulator has bugs, but it seems to be reasonably solid. 1209 | */ 1210 | /*FAKE6502 INCLUDE*/ 1211 | #endif 1212 | -------------------------------------------------------------------------------- /test2cmos.c: -------------------------------------------------------------------------------- 1 | /* 2 | THIS FILE IS NOT IN THE PUBLIC DOMAIN 3 | 4 | The .h file, fake6502.h, however, is. 5 | 6 | SO, don't put this file in your project, or richard stallman's going to be bustin' your ass for source code. 7 | 8 | The author (https://github.com/omarandlorraine/fake6502) 9 | wants this file to have copyleft stuff on it. 10 | 11 | So, if you use it, yours does too. 12 | 13 | Sort of like shit. Get a little on your hands? Now it's on everything you touch. 14 | Here's the shit: 15 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 16 | 17 | GNU GENERAL PUBLIC LICENSE 18 | Version 2, June 1991 19 | 20 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 21 | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA 22 | Everyone is permitted to copy and distribute verbatim copies 23 | of this license document, but changing it is not allowed. 24 | 25 | Preamble 26 | 27 | The licenses for most software are designed to take away your 28 | freedom to share and change it. By contrast, the GNU General Public 29 | License is intended to guarantee your freedom to share and change free 30 | software--to make sure the software is free for all its users. This 31 | General Public License applies to most of the Free Software 32 | Foundation's software and to any other program whose authors commit to 33 | using it. (Some other Free Software Foundation software is covered by 34 | the GNU Library General Public License instead.) You can apply it to 35 | your programs, too. 36 | 37 | When we speak of free software, we are referring to freedom, not 38 | price. Our General Public Licenses are designed to make sure that you 39 | have the freedom to distribute copies of free software (and charge for 40 | this service if you wish), that you receive source code or can get it 41 | if you want it, that you can change the software or use pieces of it 42 | in new free programs; and that you know you can do these things. 43 | 44 | To protect your rights, we need to make restrictions that forbid 45 | anyone to deny you these rights or to ask you to surrender the rights. 46 | These restrictions translate to certain responsibilities for you if you 47 | distribute copies of the software, or if you modify it. 48 | 49 | For example, if you distribute copies of such a program, whether 50 | gratis or for a fee, you must give the recipients all the rights that 51 | you have. You must make sure that they, too, receive or can get the 52 | source code. And you must show them these terms so they know their 53 | rights. 54 | 55 | We protect your rights with two steps: (1) copyright the software, and 56 | (2) offer you this license which gives you legal permission to copy, 57 | distribute and/or modify the software. 58 | 59 | Also, for each author's protection and ours, we want to make certain 60 | that everyone understands that there is no warranty for this free 61 | software. If the software is modified by someone else and passed on, we 62 | want its recipients to know that what they have is not the original, so 63 | that any problems introduced by others will not reflect on the original 64 | authors' reputations. 65 | 66 | Finally, any free program is threatened constantly by software 67 | patents. We wish to avoid the danger that redistributors of a free 68 | program will individually obtain patent licenses, in effect making the 69 | program proprietary. To prevent this, we have made it clear that any 70 | patent must be licensed for everyone's free use or not licensed at all. 71 | 72 | The precise terms and conditions for copying, distribution and 73 | modification follow. 74 | 75 | GNU GENERAL PUBLIC LICENSE 76 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 77 | 78 | 0. This License applies to any program or other work which contains 79 | a notice placed by the copyright holder saying it may be distributed 80 | under the terms of this General Public License. The "Program", below, 81 | refers to any such program or work, and a "work based on the Program" 82 | means either the Program or any derivative work under copyright law: 83 | that is to say, a work containing the Program or a portion of it, 84 | either verbatim or with modifications and/or translated into another 85 | language. (Hereinafter, translation is included without limitation in 86 | the term "modification".) Each licensee is addressed as "you". 87 | 88 | Activities other than copying, distribution and modification are not 89 | covered by this License; they are outside its scope. The act of 90 | running the Program is not restricted, and the output from the Program 91 | is covered only if its contents constitute a work based on the 92 | Program (independent of having been made by running the Program). 93 | Whether that is true depends on what the Program does. 94 | 95 | 1. You may copy and distribute verbatim copies of the Program's 96 | source code as you receive it, in any medium, provided that you 97 | conspicuously and appropriately publish on each copy an appropriate 98 | copyright notice and disclaimer of warranty; keep intact all the 99 | notices that refer to this License and to the absence of any warranty; 100 | and give any other recipients of the Program a copy of this License 101 | along with the Program. 102 | 103 | You may charge a fee for the physical act of transferring a copy, and 104 | you may at your option offer warranty protection in exchange for a fee. 105 | 106 | 2. You may modify your copy or copies of the Program or any portion 107 | of it, thus forming a work based on the Program, and copy and 108 | distribute such modifications or work under the terms of Section 1 109 | above, provided that you also meet all of these conditions: 110 | 111 | a) You must cause the modified files to carry prominent notices 112 | stating that you changed the files and the date of any change. 113 | 114 | b) You must cause any work that you distribute or publish, that in 115 | whole or in part contains or is derived from the Program or any 116 | part thereof, to be licensed as a whole at no charge to all third 117 | parties under the terms of this License. 118 | 119 | c) If the modified program normally reads commands interactively 120 | when run, you must cause it, when started running for such 121 | interactive use in the most ordinary way, to print or display an 122 | announcement including an appropriate copyright notice and a 123 | notice that there is no warranty (or else, saying that you provide 124 | a warranty) and that users may redistribute the program under 125 | these conditions, and telling the user how to view a copy of this 126 | License. (Exception: if the Program itself is interactive but 127 | does not normally print such an announcement, your work based on 128 | the Program is not required to print an announcement.) 129 | 130 | These requirements apply to the modified work as a whole. If 131 | identifiable sections of that work are not derived from the Program, 132 | and can be reasonably considered independent and separate works in 133 | themselves, then this License, and its terms, do not apply to those 134 | sections when you distribute them as separate works. But when you 135 | distribute the same sections as part of a whole which is a work based 136 | on the Program, the distribution of the whole must be on the terms of 137 | this License, whose permissions for other licensees extend to the 138 | entire whole, and thus to each and every part regardless of who wrote it. 139 | 140 | Thus, it is not the intent of this section to claim rights or contest 141 | your rights to work written entirely by you; rather, the intent is to 142 | exercise the right to control the distribution of derivative or 143 | collective works based on the Program. 144 | 145 | In addition, mere aggregation of another work not based on the Program 146 | with the Program (or with a work based on the Program) on a volume of 147 | a storage or distribution medium does not bring the other work under 148 | the scope of this License. 149 | 150 | 3. You may copy and distribute the Program (or a work based on it, 151 | under Section 2) in object code or executable form under the terms of 152 | Sections 1 and 2 above provided that you also do one of the following: 153 | 154 | a) Accompany it with the complete corresponding machine-readable 155 | source code, which must be distributed under the terms of Sections 156 | 1 and 2 above on a medium customarily used for software interchange; or, 157 | 158 | b) Accompany it with a written offer, valid for at least three 159 | years, to give any third party, for a charge no more than your 160 | cost of physically performing source distribution, a complete 161 | machine-readable copy of the corresponding source code, to be 162 | distributed under the terms of Sections 1 and 2 above on a medium 163 | customarily used for software interchange; or, 164 | 165 | c) Accompany it with the information you received as to the offer 166 | to distribute corresponding source code. (This alternative is 167 | allowed only for noncommercial distribution and only if you 168 | received the program in object code or executable form with such 169 | an offer, in accord with Subsection b above.) 170 | 171 | The source code for a work means the preferred form of the work for 172 | making modifications to it. For an executable work, complete source 173 | code means all the source code for all modules it contains, plus any 174 | associated interface definition files, plus the scripts used to 175 | control compilation and installation of the executable. However, as a 176 | special exception, the source code distributed need not include 177 | anything that is normally distributed (in either source or binary 178 | form) with the major components (compiler, kernel, and so on) of the 179 | operating system on which the executable runs, unless that component 180 | itself accompanies the executable. 181 | 182 | If distribution of executable or object code is made by offering 183 | access to copy from a designated place, then offering equivalent 184 | access to copy the source code from the same place counts as 185 | distribution of the source code, even though third parties are not 186 | compelled to copy the source along with the object code. 187 | 188 | 4. You may not copy, modify, sublicense, or distribute the Program 189 | except as expressly provided under this License. Any attempt 190 | otherwise to copy, modify, sublicense or distribute the Program is 191 | void, and will automatically terminate your rights under this License. 192 | However, parties who have received copies, or rights, from you under 193 | this License will not have their licenses terminated so long as such 194 | parties remain in full compliance. 195 | 196 | 5. You are not required to accept this License, since you have not 197 | signed it. However, nothing else grants you permission to modify or 198 | distribute the Program or its derivative works. These actions are 199 | prohibited by law if you do not accept this License. Therefore, by 200 | modifying or distributing the Program (or any work based on the 201 | Program), you indicate your acceptance of this License to do so, and 202 | all its terms and conditions for copying, distributing or modifying 203 | the Program or works based on it. 204 | 205 | 6. Each time you redistribute the Program (or any work based on the 206 | Program), the recipient automatically receives a license from the 207 | original licensor to copy, distribute or modify the Program subject to 208 | these terms and conditions. You may not impose any further 209 | restrictions on the recipients' exercise of the rights granted herein. 210 | You are not responsible for enforcing compliance by third parties to 211 | this License. 212 | 213 | 7. If, as a consequence of a court judgment or allegation of patent 214 | infringement or for any other reason (not limited to patent issues), 215 | conditions are imposed on you (whether by court order, agreement or 216 | otherwise) that contradict the conditions of this License, they do not 217 | excuse you from the conditions of this License. If you cannot 218 | distribute so as to satisfy simultaneously your obligations under this 219 | License and any other pertinent obligations, then as a consequence you 220 | may not distribute the Program at all. For example, if a patent 221 | license would not permit royalty-free redistribution of the Program by 222 | all those who receive copies directly or indirectly through you, then 223 | the only way you could satisfy both it and this License would be to 224 | refrain entirely from distribution of the Program. 225 | 226 | If any portion of this section is held invalid or unenforceable under 227 | any particular circumstance, the balance of the section is intended to 228 | apply and the section as a whole is intended to apply in other 229 | circumstances. 230 | 231 | It is not the purpose of this section to induce you to infringe any 232 | patents or other property right claims or to contest validity of any 233 | such claims; this section has the sole purpose of protecting the 234 | integrity of the free software distribution system, which is 235 | implemented by public license practices. Many people have made 236 | generous contributions to the wide range of software distributed 237 | through that system in reliance on consistent application of that 238 | system; it is up to the author/donor to decide if he or she is willing 239 | to distribute software through any other system and a licensee cannot 240 | impose that choice. 241 | 242 | This section is intended to make thoroughly clear what is believed to 243 | be a consequence of the rest of this License. 244 | 245 | 8. If the distribution and/or use of the Program is restricted in 246 | certain countries either by patents or by copyrighted interfaces, the 247 | original copyright holder who places the Program under this License 248 | may add an explicit geographical distribution limitation excluding 249 | those countries, so that distribution is permitted only in or among 250 | countries not thus excluded. In such case, this License incorporates 251 | the limitation as if written in the body of this License. 252 | 253 | 9. The Free Software Foundation may publish revised and/or new versions 254 | of the General Public License from time to time. Such new versions will 255 | be similar in spirit to the present version, but may differ in detail to 256 | address new problems or concerns. 257 | 258 | Each version is given a distinguishing version number. If the Program 259 | specifies a version number of this License which applies to it and "any 260 | later version", you have the option of following the terms and conditions 261 | either of that version or of any later version published by the Free 262 | Software Foundation. If the Program does not specify a version number of 263 | this License, you may choose any version ever published by the Free Software 264 | Foundation. 265 | 266 | 10. If you wish to incorporate parts of the Program into other free 267 | programs whose distribution conditions are different, write to the author 268 | to ask for permission. For software which is copyrighted by the Free 269 | Software Foundation, write to the Free Software Foundation; we sometimes 270 | make exceptions for this. Our decision will be guided by the two goals 271 | of preserving the free status of all derivatives of our free software and 272 | of promoting the sharing and reuse of software generally. 273 | 274 | NO WARRANTY 275 | 276 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 277 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 278 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 279 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 280 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 281 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 282 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 283 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 284 | REPAIR OR CORRECTION. 285 | 286 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 287 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 288 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 289 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 290 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 291 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 292 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 293 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 294 | POSSIBILITY OF SUCH DAMAGES. 295 | 296 | END OF TERMS AND CONDITIONS 297 | 298 | Appendix: How to Apply These Terms to Your New Programs 299 | 300 | If you develop a new program, and you want it to be of the greatest 301 | possible use to the public, the best way to achieve this is to make it 302 | free software which everyone can redistribute and change under these terms. 303 | 304 | To do so, attach the following notices to the program. It is safest 305 | to attach them to the start of each source file to most effectively 306 | convey the exclusion of warranty; and each file should have at least 307 | the "copyright" line and a pointer to where the full notice is found. 308 | 309 | 310 | Copyright (C) 19yy 311 | 312 | This program is free software; you can redistribute it and/or modify 313 | it under the terms of the GNU General Public License as published by 314 | the Free Software Foundation; either version 2 of the License, or 315 | (at your option) any later version. 316 | 317 | This program is distributed in the hope that it will be useful, 318 | but WITHOUT ANY WARRANTY; without even the implied warranty of 319 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 320 | GNU General Public License for more details. 321 | 322 | You should have received a copy of the GNU General Public License 323 | along with this program; if not, write to the Free Software 324 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA 325 | 326 | Also add information on how to contact you by electronic and paper mail. 327 | 328 | If the program is interactive, make it output a short notice like this 329 | when it starts in an interactive mode: 330 | 331 | Gnomovision version 69, Copyright (C) 19yy name of author 332 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 333 | This is free software, and you are welcome to redistribute it 334 | under certain conditions; type `show c' for details. 335 | 336 | The hypothetical commands `show w' and `show c' should show the appropriate 337 | parts of the General Public License. Of course, the commands you use may 338 | be called something other than `show w' and `show c'; they could even be 339 | mouse-clicks or menu items--whatever suits your program. 340 | 341 | You should also get your employer (if you work as a programmer) or your 342 | school, if any, to sign a "copyright disclaimer" for the program, if 343 | necessary. Here is a sample; alter the names: 344 | 345 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 346 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 347 | 348 | , 1 April 1989 349 | Ty Coon, President of Vice 350 | 351 | This General Public License does not permit incorporating your program into 352 | proprietary programs. If your program is a subroutine library, you may 353 | consider it more useful to permit linking proprietary applications with the 354 | library. If this is what you want to do, use the GNU Library General 355 | Public License instead of this License. 356 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 357 | Yuck! Back to code. 358 | 359 | */ 360 | 361 | 362 | 363 | #include 364 | #include 365 | #include 366 | #include "fake65c02.h" 367 | 368 | #define FLAG_CARRY 0x01 369 | #define FLAG_ZERO 0x02 370 | #define FLAG_INTERRUPT 0x04 371 | #define FLAG_DECIMAL 0x08 372 | #define FLAG_BREAK 0x10 373 | #define FLAG_CONSTANT 0x20 374 | #define FLAG_OVERFLOW 0x40 375 | #define FLAG_SIGN 0x80 376 | 377 | #define CHECK(var, shouldbe) \ 378 | if (var != shouldbe) \ 379 | return printf("line %d: " #var " should've been %04x but was %04x\n", \ 380 | __LINE__, shouldbe, var); 381 | #define CHECKMEM(var, shouldbe) \ 382 | if (read6502(var) != (shouldbe)) \ 383 | return printf("line %d: memory location " #var \ 384 | " should've been %02x but was %02x\n", \ 385 | __LINE__, shouldbe, read6502(var)); 386 | 387 | #define CHECKFLAG(flag, shouldbe) \ 388 | if (!!(status & flag) != !!shouldbe) \ 389 | return printf( \ 390 | "line %d: " #flag " should be %sset but isn't, [ %02x, %02x] \n", \ 391 | __LINE__, shouldbe ? "" : "re", (status & flag), shouldbe); 392 | 393 | uint8 mem[65536]; 394 | 395 | int reads, writes; 396 | 397 | uint8 read6502(ushort addr) { 398 | reads++; 399 | return mem[addr]; 400 | } 401 | 402 | void write6502(ushort addr, uint8 val) { 403 | writes++; 404 | mem[addr] = val; 405 | } 406 | 407 | void exec_instruction(uint8 opcode, uint8 op1, 408 | uint8 op2) { 409 | write6502(pc, opcode); 410 | write6502(pc + 1, op1); 411 | write6502(pc + 2, op2); 412 | 413 | clockticks6502 = reads = writes = 0; 414 | step6502(); 415 | } 416 | 417 | int interrupt() { 418 | status = 0xff; 419 | 420 | write6502(0xfffa, 0x00); 421 | write6502(0xfffb, 0x40); 422 | write6502(0xfffc, 0x00); 423 | write6502(0xfffd, 0x50); 424 | write6502(0xfffe, 0x00); 425 | write6502(0xffff, 0x60); 426 | 427 | // On reset, a 6502 initialises the stack pointer to 0xFD, and jumps to the 428 | // address at 0xfffc. Also, the interrupt flag is cleared. 429 | status &= ~0x04; 430 | reset6502(); 431 | CHECK(sp, 0x00fd); 432 | CHECK(pc, 0x5000); 433 | 434 | CHECKFLAG(FLAG_INTERRUPT, 1); 435 | 436 | // This IRQ shouldn't fire because the interrupts are disabled 437 | irq6502(); 438 | CHECK(sp, 0x00fd); 439 | CHECK(pc, 0x5000); 440 | 441 | // Enable interrupts and try again 442 | status &= ~0x04; 443 | irq6502(); 444 | 445 | // On IRQ, a 6502 pushes the pc and flags onto the stack and then fetches pc 446 | // from the vector at 0xFFFE 447 | CHECK(sp, 0x00fa); 448 | CHECK(pc, 0x6000); 449 | 450 | CHECKMEM(0x01fd, 0x50); 451 | CHECKMEM(0x01fc, 0x00); 452 | CHECKMEM(0x01fb, status & 0xeb); 453 | 454 | CHECKFLAG(FLAG_INTERRUPT, 1); 455 | 456 | // The NMI may fire even when the Interrupt flag is set 457 | nmi6502(); 458 | CHECK(sp, 0x00f7); 459 | CHECK(pc, 0x4000); 460 | 461 | CHECKFLAG(FLAG_INTERRUPT, 1); 462 | 463 | return 0; 464 | } 465 | 466 | int test_zp() { 467 | 468 | pc = 0x200; 469 | exec_instruction(0xa5, 0x03, 0x00); 470 | 471 | CHECK(pc, 0x0202); 472 | CHECK(ea, 0x0003); 473 | return 0; 474 | } 475 | 476 | int test_zpx() { 477 | x = 1; 478 | y = 1; 479 | pc = 0x200; 480 | 481 | exec_instruction(0xb5, 0x03, 0x00); // lda $03,x 482 | CHECK(pc, 0x0202); 483 | CHECK(ea, 0x0004); 484 | 485 | exec_instruction(0xb5, 0xff, 0x00); // lda $ff,x 486 | CHECK(pc, 0x0204); 487 | CHECK(ea, 0x0000); 488 | 489 | exec_instruction(0xb6, 0x03, 0x00); // lda $03,x 490 | CHECK(pc, 0x0206); 491 | CHECK(ea, 0x0004); 492 | 493 | exec_instruction(0xb6, 0xff, 0x00); // ldx $ff,y 494 | CHECK(pc, 0x0208); 495 | CHECK(ea, 0x0000); 496 | 497 | return 0; 498 | } 499 | 500 | int decimal_mode() { 501 | a = 0x89; 502 | pc = 0x200; 503 | status = FLAG_DECIMAL; // Turn on decimal mode, clear carry flag 504 | 505 | exec_instruction(0x69, 0x01, 0x00); // ADC #$01 506 | CHECK(pc, 0x202); 507 | CHECK(a, 0x90); 508 | 509 | 510 | exec_instruction(0x69, 0x10, 0x00); // ADC #$10 511 | CHECK(pc, 0x204); 512 | CHECK(a, 0x00); 513 | 514 | exec_instruction(0x18, 0x00, 0x00); // CLC 515 | //exec_instruction(0x38, 0x00, 0x00); // SEC 516 | CHECK(pc, 0x205); 517 | CHECKFLAG(FLAG_CARRY, 0); 518 | /*ODDITY- carry flag doesn't matter here! Huh!*/ 519 | 520 | exec_instruction(0xe9, 0x01, 0x00); // SBC #$01 521 | CHECK(pc, 0x207); 522 | CHECK(a, 0x98); 523 | CHECKFLAG(FLAG_CARRY, 0); /*Inverse borrow- carry should be 0.*/ 524 | /*The carry bit is not set, so a subtraction should take us down to 98! However, the carry bit *will* be set after this.*/ 525 | 526 | /*This instruction starts with the carry bit not set, so we expect it to subtract an extra. */ 527 | exec_instruction(0xe9, 0x10, 0x00); // SBC #$10 528 | CHECK(pc, 0x209); 529 | CHECKFLAG(FLAG_CARRY, 1); 530 | CHECK(a, 0x87); /*this is the result.*/ 531 | 532 | 533 | exec_instruction(0x38, 0x00, 0x00); /* SEC*/ 534 | CHECK(pc, 0x20A); 535 | CHECKFLAG(FLAG_CARRY, 1); 536 | 537 | exec_instruction(0xe9, 0x03, 0x00); /* SBC #$3*/ 538 | CHECK(pc, 0x20C); 539 | CHECK(a, 0x84); 540 | CHECKFLAG(FLAG_CARRY, 1); 541 | /*the carry bit is set so it will subtract 4*/ 542 | exec_instruction(0xe9, 0x04, 0x00); // SBC #$4 543 | CHECK(pc, 0x20E); 544 | CHECK(a, 0x80); 545 | CHECKFLAG(FLAG_CARRY, 1); 546 | /*the carry bit is set so it will subtract 2 instead of just two.*/ 547 | exec_instruction(0xe9, 0x02, 0x00); // SBC #$2 548 | CHECK(pc, 0x210); 549 | CHECK(a, 0x78); 550 | 551 | return 0; 552 | } 553 | int binary_mode() { 554 | a = 0x89; 555 | pc = 0x200; 556 | status = 0x00; // Turn off decimal mode, clear carry flag 557 | 558 | exec_instruction(0x69, 0x01, 0x00); // ADC #$01 559 | CHECK(pc, 0x202); 560 | CHECK(a, 0x8a); 561 | 562 | exec_instruction(0x69, 0x14, 0x00); // ADC #$10 563 | CHECK(pc, 0x204); 564 | CHECK(a, 0x9e); 565 | 566 | exec_instruction(0x18, 0x00, 0x00); // CLC 567 | CHECK(pc, 0x205); 568 | 569 | exec_instruction(0xe9, 0x01, 0x00); // SBC #$01 570 | CHECK(pc, 0x207); 571 | CHECK(a, 0x9c); /*BUG! the carry flag was not set. it should be 9c, not 9d.*/ 572 | CHECKFLAG(FLAG_CARRY, 1); 573 | 574 | exec_instruction(0xe9, 0x10, 0x00); // SBC #$10 575 | CHECK(pc, 0x209); 576 | CHECK(a, 0x8c); /*The carry flag was set, */ 577 | CHECKFLAG(FLAG_CARRY, 1); /*Continued subtractions should permit continuing the result.*/ 578 | 579 | return 0; 580 | } 581 | 582 | int pushpull() { 583 | 584 | a = 0x89; 585 | sp = 0xff; 586 | pc = 0x0200; 587 | exec_instruction(0xa9, 0x40, 0x00); // LDA #$40 588 | exec_instruction(0x48, 0x00, 0x00); // PHA 589 | CHECK(sp, 0xfe); 590 | CHECKMEM(0x01ff, 0x40); 591 | exec_instruction(0xa9, 0x00, 0x00); // LDA #$00 592 | exec_instruction(0x48, 0x00, 0x00); // PHA 593 | CHECK(sp, 0xfd); 594 | CHECKMEM(0x01fe, 0x00); 595 | exec_instruction(0x60, 0x00, 0x00); // RTS 596 | CHECK(sp, 0xff); 597 | CHECK(pc, 0x4001); 598 | return 0; 599 | } 600 | 601 | int rotations() { 602 | 603 | 604 | a = 0x01; 605 | status = 0x00; 606 | pc = 0x0200; 607 | exec_instruction(0x6a, 0x00, 0x00); // ROR A 608 | CHECK(a, 0x00); 609 | CHECK(pc, 0x0201); 610 | exec_instruction(0x6a, 0x00, 0x00); // ROR A 611 | CHECK(a, 0x80); 612 | CHECK(pc, 0x0202); 613 | 614 | a = 0x01; 615 | status = 0x00; 616 | pc = 0x0200; 617 | exec_instruction(0x4a, 0x00, 0x00); // LSR A 618 | CHECK(a, 0x00); 619 | CHECK(pc, 0x0201); 620 | exec_instruction(0x4a, 0x00, 0x00); // LSR A 621 | CHECK(a, 0x00); 622 | CHECK(pc, 0x0202); 623 | return 0; 624 | } 625 | 626 | int incdec() { 627 | 628 | x = 0x80; 629 | y = 0x80; 630 | 631 | write6502(0x00, 0x00); 632 | status = 0x00; 633 | pc = 0x0200; 634 | 635 | exec_instruction(0xc6, 0x00, 0x00); // DEC $0 636 | CHECK(pc, 0x0202); 637 | CHECKFLAG(FLAG_ZERO, 0); 638 | CHECKFLAG(FLAG_SIGN, 1); 639 | 640 | exec_instruction(0xe6, 0x00, 0x00); // INC $0 641 | CHECK(pc, 0x0204); 642 | CHECKFLAG(FLAG_ZERO, 1); 643 | CHECKFLAG(FLAG_SIGN, 0); 644 | 645 | exec_instruction(0xe8, 0x00, 0x00); // INX 646 | CHECK(pc, 0x0205); 647 | CHECK(x, 0x81); 648 | CHECKFLAG(FLAG_ZERO, 0); 649 | CHECKFLAG(FLAG_SIGN, 1); 650 | 651 | exec_instruction(0x88, 0x00, 0x00); // DEY 652 | CHECK(pc, 0x0206); 653 | CHECK(y, 0x7f); 654 | CHECKFLAG(FLAG_ZERO, 0); 655 | CHECKFLAG(FLAG_SIGN, 0); 656 | 657 | x = 0x00; 658 | exec_instruction(0xca, 0x00, 0x00); // DEX 659 | CHECK(pc, 0x0207); 660 | CHECK(y, 0x7f); 661 | CHECK(x, 0xff); 662 | CHECKFLAG(FLAG_ZERO, 0); 663 | CHECKFLAG(FLAG_SIGN, 1); 664 | 665 | y = 0x00; 666 | exec_instruction(0xc8, 0x00, 0x00); // INY 667 | CHECK(pc, 0x0208); 668 | CHECK(y, 0x01); 669 | CHECK(x, 0xff); 670 | CHECKFLAG(FLAG_ZERO, 0); 671 | CHECKFLAG(FLAG_SIGN, 0); 672 | 673 | return 0; 674 | } 675 | 676 | int branches() { 677 | 678 | status = 0x00; 679 | pc = 0x0200; 680 | exec_instruction(0x10, 0x60, 0x00); // BPL *+$60 681 | CHECK(pc, 0x0262); 682 | exec_instruction(0x30, 0x10, 0x00); // BMI *+$10 683 | CHECK(pc, 0x0264); 684 | exec_instruction(0x50, 0x70, 0x00); // BVC *+$70 685 | CHECK(pc, 0x02d6); 686 | exec_instruction(0x90, 0x70, 0x00); // BCC *+$70 687 | CHECK(pc, 0x0348); 688 | exec_instruction(0xb0, 0x70, 0x00); // BCS *+$70 689 | CHECK(pc, 0x034a); 690 | exec_instruction(0x70, 0xfa, 0x00); // BVS *-$06 691 | CHECK(pc, 0x034c); 692 | exec_instruction(0xd0, 0xfa, 0x00); // BNE *-$06 693 | CHECK(pc, 0x0348); 694 | exec_instruction(0xf0, 0xfa, 0x00); // BEQ *-$06 695 | CHECK(pc, 0x034a); 696 | 697 | status = FLAG_CARRY | FLAG_ZERO | FLAG_SIGN | FLAG_OVERFLOW; 698 | exec_instruction(0xb0, 0x70, 0x00); // BCS *+$70 699 | CHECK(pc, 0x03bc); 700 | exec_instruction(0xf0, 0xfa, 0x00); // BEQ *-$06 701 | CHECK(pc, 0x03b8); 702 | exec_instruction(0x30, 0x10, 0x00); // BMI *+$10 703 | CHECK(pc, 0x03ca); 704 | exec_instruction(0x70, 0xfa, 0x00); // BVS *-$06 705 | CHECK(pc, 0x03c6); 706 | return 0; 707 | } 708 | 709 | int comparisons() { 710 | 711 | status = 0x00; 712 | pc = 0x0200; 713 | a = 0x50; 714 | x = 0x00; 715 | y = 0xc0; 716 | 717 | exec_instruction(0xc9, 0x00, 0x00); 718 | CHECKFLAG(FLAG_CARRY, 1); 719 | CHECKFLAG(FLAG_ZERO, 0); 720 | CHECKFLAG(FLAG_SIGN, 0); 721 | 722 | exec_instruction(0xc9, 0x51, 0x00); 723 | CHECKFLAG(FLAG_CARRY, 0); 724 | CHECKFLAG(FLAG_ZERO, 0); 725 | CHECKFLAG(FLAG_SIGN, 1); 726 | 727 | exec_instruction(0xe0, 0x00, 0x00); 728 | CHECKFLAG(FLAG_CARRY, 1); 729 | CHECKFLAG(FLAG_ZERO, 1); 730 | CHECKFLAG(FLAG_SIGN, 0); 731 | 732 | exec_instruction(0xc0, 0x01, 0x00); 733 | CHECKFLAG(FLAG_CARRY, 1); 734 | CHECKFLAG(FLAG_ZERO, 0); 735 | CHECKFLAG(FLAG_SIGN, 1); 736 | 737 | return 0; 738 | } 739 | 740 | int absolute() { 741 | 742 | clockticks6502 = 0; 743 | pc = 0x200; 744 | exec_instruction(0xad, 0x60, 0x00); 745 | CHECK(ea, 0x0060); 746 | CHECK(pc, 0x0203); 747 | CHECK(clockticks6502, 4); 748 | return 0; 749 | } 750 | 751 | int absolute_x() { 752 | 753 | pc = 0x200; 754 | x = 0x80; 755 | clockticks6502 = 0; 756 | 757 | exec_instruction(0xbd, 0x60, 0x00); 758 | CHECK(ea, 0x00e0); 759 | CHECK(pc, 0x0203); 760 | CHECK(clockticks6502, 4); 761 | 762 | // Takes another cycle because of page-crossing 763 | clockticks6502 = 0; 764 | exec_instruction(0xbd, 0xa0, 0x00); 765 | CHECK(ea, 0x0120); 766 | CHECK(pc, 0x0206); 767 | CHECK(clockticks6502, 5); 768 | 769 | // Should NOT take another cycle because of page-crossing 770 | clockticks6502 = 0; 771 | exec_instruction(0x9d, 0x60, 0x00); 772 | CHECK(ea, 0x00e0); 773 | CHECK(pc, 0x0209); 774 | CHECK(clockticks6502, 5); 775 | 776 | clockticks6502 = 0; 777 | exec_instruction(0x9d, 0xa0, 0x00); 778 | CHECK(ea, 0x0120); 779 | CHECK(pc, 0x020c); 780 | CHECK(clockticks6502, 5); 781 | return 0; 782 | } 783 | 784 | int absolute_y() { 785 | 786 | pc = 0x200; 787 | y = 0x80; 788 | clockticks6502 = 0; 789 | 790 | exec_instruction(0xb9, 0x60, 0x00); 791 | CHECK(ea, 0x00e0); 792 | CHECK(pc, 0x0203); 793 | CHECK(clockticks6502, 4); 794 | 795 | // Takes another cycle because of page-crossing 796 | clockticks6502 = 0; 797 | exec_instruction(0xb9, 0xa0, 0x00); 798 | CHECK(ea, 0x0120); 799 | CHECK(pc, 0x0206); 800 | CHECK(clockticks6502, 5); 801 | 802 | // Should NOT take another cycle because of page-crossing 803 | clockticks6502 = 0; 804 | exec_instruction(0x99, 0x60, 0x00); 805 | CHECK(ea, 0x00e0); 806 | CHECK(pc, 0x0209); 807 | CHECK(clockticks6502, 5); 808 | 809 | clockticks6502 = 0; 810 | exec_instruction(0x99, 0xa0, 0x00); 811 | CHECK(ea, 0x0120); 812 | CHECK(pc, 0x020c); 813 | CHECK(clockticks6502, 5); 814 | return 0; 815 | } 816 | 817 | int indirect() { 818 | 819 | pc = 0x200; 820 | write6502(0x8000, 0x01); 821 | write6502(0x80fe, 0x02); 822 | write6502(0x80ff, 0x03); 823 | write6502(0x8100, 0x04); 824 | 825 | clockticks6502 = 0; 826 | exec_instruction(0x6c, 0xff, 0x80); 827 | CHECK(pc, 0x0103); 828 | CHECK(clockticks6502, 5); 829 | 830 | return 0; 831 | } 832 | 833 | int indirect_y() { 834 | 835 | pc = 0x200; 836 | y = 0x80; 837 | write6502(0x20, 0x81); 838 | write6502(0x21, 0x20); 839 | 840 | // Takes another cycle because of page-crossing 841 | clockticks6502 = 0; 842 | exec_instruction(0xb1, 0x20, 0x00); 843 | CHECK(ea, 0x2101); 844 | CHECK(pc, 0x0202); 845 | CHECK(clockticks6502, 6); 846 | 847 | // Should NOT take another cycle because of page-crossing 848 | clockticks6502 = 0; 849 | y = 0x10; 850 | exec_instruction(0xb1, 0x20, 0x00); 851 | CHECK(ea, 0x2091); 852 | CHECK(pc, 0x0204); 853 | CHECK(clockticks6502, 5); 854 | 855 | // Takes 6 cycles regardless of page-crossing or not 856 | clockticks6502 = 0; 857 | y = 0x80; 858 | exec_instruction(0x91, 0x20, 0x00); 859 | CHECK(ea, 0x2101); 860 | CHECK(pc, 0x0206); 861 | CHECK(clockticks6502, 6); 862 | 863 | // Takes 6 cycles regardless of page-crossing or not 864 | clockticks6502 = 0; 865 | y = 0x10; 866 | exec_instruction(0x91, 0x20, 0x00); 867 | CHECK(ea, 0x2091); 868 | CHECK(pc, 0x0208); 869 | CHECK(clockticks6502, 6); 870 | 871 | return 0; 872 | } 873 | 874 | int indirect_x() { 875 | 876 | pc = 0x200; 877 | x = 0x00; 878 | write6502(0x20, 0x81); 879 | write6502(0x21, 0x20); 880 | write6502(0xff, 0x81); 881 | write6502(0x00, 0x20); 882 | 883 | exec_instruction(0xa1, 0x20, 0x00); 884 | CHECK(ea, 0x2081); 885 | CHECK(pc, 0x0202); 886 | CHECK(clockticks6502, 6); 887 | 888 | x = 0x40; 889 | exec_instruction(0xa1, 0xe0, 0x00); 890 | CHECK(ea, 0x2081); 891 | CHECK(clockticks6502, 6); 892 | 893 | x = 0; 894 | exec_instruction(0xa1, 0x20, 0x00); 895 | CHECK(ea, 0x2081); 896 | CHECK(clockticks6502, 6); 897 | 898 | exec_instruction(0x81, 0xff, 0x00); 899 | CHECK(ea, 0x2081); 900 | CHECK(clockticks6502, 6); 901 | 902 | exec_instruction(0x81, 0x20, 0x00); 903 | CHECK(ea, 0x2081); 904 | CHECK(clockticks6502, 6); 905 | 906 | return 0; 907 | } 908 | 909 | int test_zpi() { 910 | 911 | pc = 0x200; 912 | x = 0x59; 913 | write6502(0x20, 0x81); 914 | write6502(0x21, 0x20); 915 | write6502(0xff, 0x81); 916 | write6502(0x00, 0x20); 917 | 918 | exec_instruction(0xb2, 0x20, 0x00); 919 | CHECK(ea, 0x2081); 920 | CHECK(pc, 0x0202); 921 | CHECK(clockticks6502, 5); 922 | 923 | x = 0; 924 | exec_instruction(0xb2, 0x20, 0x00); 925 | CHECK(ea, 0x2081); 926 | CHECK(clockticks6502, 5); 927 | 928 | exec_instruction(0x92, 0xff, 0x00); 929 | CHECK(ea, 0x2081); 930 | CHECK(clockticks6502, 5); 931 | 932 | exec_instruction(0x92, 0x20, 0x00); 933 | CHECK(ea, 0x2081); 934 | CHECK(clockticks6502, 5); 935 | 936 | return 0; 937 | } 938 | 939 | int flags() { 940 | 941 | pc = 0x200; 942 | status = 0xff; 943 | 944 | exec_instruction(0x18, 0x00, 0x00); 945 | CHECKFLAG(FLAG_CARRY, 0); 946 | exec_instruction(0x38, 0x00, 0x00); 947 | CHECKFLAG(FLAG_CARRY, 1); 948 | 949 | exec_instruction(0x58, 0x00, 0x00); 950 | CHECKFLAG(FLAG_INTERRUPT, 0); 951 | exec_instruction(0x78, 0x00, 0x00); 952 | CHECKFLAG(FLAG_INTERRUPT, 1); 953 | 954 | exec_instruction(0xd8, 0x00, 0x00); 955 | CHECKFLAG(FLAG_DECIMAL, 0); 956 | exec_instruction(0xf8, 0x00, 0x00); 957 | CHECKFLAG(FLAG_DECIMAL, 1); 958 | 959 | exec_instruction(0xb8, 0x00, 0x00); 960 | CHECKFLAG(FLAG_OVERFLOW, 0); 961 | 962 | return 0; 963 | } 964 | 965 | int loads() { 966 | 967 | pc = 0x200; 968 | 969 | exec_instruction(0xa0, 0x00, 0x00); // ldy #$00 970 | CHECKFLAG(FLAG_ZERO, 1); 971 | CHECKFLAG(FLAG_SIGN, 0); 972 | 973 | exec_instruction(0xa0, 0x80, 0x00); // ldy #$80 974 | CHECKFLAG(FLAG_ZERO, 0); 975 | CHECKFLAG(FLAG_SIGN, 1); 976 | 977 | exec_instruction(0xa0, 0x7f, 0x00); // ldy #$7f 978 | CHECKFLAG(FLAG_ZERO, 0); 979 | CHECKFLAG(FLAG_SIGN, 0); 980 | 981 | exec_instruction(0xa2, 0x00, 0x00); // ldx #$ff 982 | CHECKFLAG(FLAG_ZERO, 1); 983 | CHECKFLAG(FLAG_SIGN, 0); 984 | 985 | return 0; 986 | } 987 | 988 | int transfers() { 989 | 990 | pc = 0x200; 991 | status = 0x00; 992 | 993 | x = 0x80; 994 | 995 | exec_instruction(0x9a, 0x00, 0x00); // txs 996 | CHECKFLAG(FLAG_ZERO, 0); 997 | CHECKFLAG(FLAG_SIGN, 0); 998 | CHECK(sp, 0x80); 999 | 1000 | exec_instruction(0x8a, 0x00, 0x00); // txa 1001 | CHECKFLAG(FLAG_ZERO, 0); 1002 | CHECKFLAG(FLAG_SIGN, 1); 1003 | CHECK(a, 0x80); 1004 | 1005 | a = 0x1; 1006 | exec_instruction(0xaa, 0x00, 0x00); // tax 1007 | CHECKFLAG(FLAG_ZERO, 0); 1008 | CHECKFLAG(FLAG_SIGN, 0); 1009 | CHECK(x, 0x01); 1010 | 1011 | exec_instruction(0xba, 0x80, 0x00); // tsx 1012 | CHECKFLAG(FLAG_ZERO, 0); 1013 | CHECKFLAG(FLAG_SIGN, 1); 1014 | CHECK(x, 0x80); 1015 | 1016 | exec_instruction(0xa8, 0x00, 0x00); // tay 1017 | CHECKFLAG(FLAG_ZERO, 0); 1018 | CHECKFLAG(FLAG_SIGN, 0); 1019 | CHECK(y, 0x01); 1020 | 1021 | a = 0x80; 1022 | exec_instruction(0x98, 0x00, 0x00); // tya 1023 | CHECKFLAG(FLAG_ZERO, 0); 1024 | CHECKFLAG(FLAG_SIGN, 0); 1025 | CHECK(a, 0x01); 1026 | 1027 | return 0; 1028 | } 1029 | 1030 | int and_opcode() { 1031 | 1032 | 1033 | a = 0xff; 1034 | status = 0x00; 1035 | pc = 0x200; 1036 | 1037 | exec_instruction(0x29, 0xe1, 0x00); 1038 | CHECK(a, 0xe1); 1039 | CHECKFLAG(FLAG_ZERO, 0); 1040 | CHECKFLAG(FLAG_SIGN, 1); 1041 | CHECK(pc, 0x0202); 1042 | 1043 | exec_instruction(0x29, 0x71, 0x00); 1044 | CHECK(a, 0x61); 1045 | CHECKFLAG(FLAG_ZERO, 0); 1046 | CHECKFLAG(FLAG_SIGN, 0); 1047 | 1048 | exec_instruction(0x29, 0x82, 0x00); 1049 | CHECK(a, 0x00); 1050 | CHECKFLAG(FLAG_ZERO, 1); 1051 | CHECKFLAG(FLAG_SIGN, 0); 1052 | 1053 | return 0; 1054 | } 1055 | 1056 | int asl_opcode() { 1057 | 1058 | 1059 | a = 0x50; 1060 | status = 0x00; 1061 | pc = 0x200; 1062 | 1063 | exec_instruction(0x0a, 0x00, 0x00); 1064 | CHECK(a, 0xa0); 1065 | CHECKFLAG(FLAG_ZERO, 0); 1066 | CHECKFLAG(FLAG_SIGN, 1); 1067 | CHECKFLAG(FLAG_CARRY, 0); 1068 | CHECK(pc, 0x0201); 1069 | 1070 | exec_instruction(0x0a, 0x00, 0x00); 1071 | CHECK(a, 0x40); 1072 | CHECKFLAG(FLAG_ZERO, 0); 1073 | CHECKFLAG(FLAG_SIGN, 0); 1074 | CHECKFLAG(FLAG_CARRY, 1); 1075 | 1076 | exec_instruction(0x0a, 0x00, 0x00); 1077 | CHECK(a, 0x80); 1078 | CHECKFLAG(FLAG_ZERO, 0); 1079 | CHECKFLAG(FLAG_SIGN, 1); 1080 | CHECKFLAG(FLAG_CARRY, 0); 1081 | 1082 | exec_instruction(0x0a, 0x00, 0x00); 1083 | CHECK(a, 0x00); 1084 | CHECKFLAG(FLAG_ZERO, 1); 1085 | CHECKFLAG(FLAG_SIGN, 0); 1086 | CHECKFLAG(FLAG_CARRY, 1); 1087 | 1088 | return 0; 1089 | } 1090 | 1091 | int bit_opcode() { 1092 | 1093 | 1094 | a = 0x50; 1095 | status = 0x00; 1096 | pc = 0x200; 1097 | write6502(0xff, 0x80); 1098 | 1099 | exec_instruction(0x24, 0xff, 0x00); 1100 | CHECK(a, 0x50); 1101 | CHECKFLAG(FLAG_ZERO, 1); 1102 | CHECKFLAG(FLAG_SIGN, 1); 1103 | CHECKFLAG(FLAG_CARRY, 0); 1104 | CHECK(pc, 0x0202); 1105 | 1106 | a = 0x40; 1107 | write6502(0xff, 0x40); 1108 | exec_instruction(0x2c, 0xff, 0x00); 1109 | CHECK(a, 0x40); 1110 | CHECKFLAG(FLAG_ZERO, 0); 1111 | CHECKFLAG(FLAG_SIGN, 0); 1112 | CHECKFLAG(FLAG_CARRY, 0); 1113 | CHECK(pc, 0x0205); 1114 | 1115 | return 0; 1116 | } 1117 | 1118 | int bit_imm_opcode() { 1119 | 1120 | 1121 | a = 0x50; 1122 | status = 0x00; 1123 | pc = 0x200; 1124 | 1125 | exec_instruction(0x89, 0xff, 0x00); 1126 | CHECK(a, 0x50); 1127 | CHECKFLAG(FLAG_ZERO, 0); 1128 | CHECKFLAG(FLAG_SIGN, 0); /*originally tested for 0*/ 1129 | CHECKFLAG(FLAG_CARRY, 0); 1130 | CHECK(pc, 0x0202); 1131 | 1132 | exec_instruction(0x89, 0x80, 0x00); 1133 | CHECK(a, 0x50); 1134 | CHECKFLAG(FLAG_ZERO, 1); 1135 | CHECKFLAG(FLAG_SIGN, 0); /*originally tested for 0*/ 1136 | CHECKFLAG(FLAG_CARRY, 0); 1137 | CHECK(pc, 0x0204); 1138 | 1139 | return 0; 1140 | } 1141 | 1142 | int brk_opcode() { 1143 | 1144 | reset6502(); 1145 | 1146 | status = FLAG_ZERO | FLAG_SIGN | FLAG_CARRY | FLAG_OVERFLOW; 1147 | pc = 0x200; 1148 | write6502(0xfffe, 0x00); 1149 | write6502(0xffff, 0x60); 1150 | 1151 | exec_instruction(0x00, 0x00, 0x00); 1152 | CHECKFLAG(FLAG_ZERO, 1); 1153 | CHECKFLAG(FLAG_SIGN, 1); 1154 | CHECKFLAG(FLAG_CARRY, 1); 1155 | CHECKFLAG(FLAG_INTERRUPT, 1); 1156 | CHECKFLAG(FLAG_OVERFLOW, 1); 1157 | 1158 | CHECK(sp, 0x00fa); 1159 | CHECK(pc, 0x6000); 1160 | 1161 | CHECKMEM(0x01fd, 0x02); 1162 | CHECKMEM(0x01fc, 0x02); 1163 | CHECKMEM(0x01fb, FLAG_ZERO | FLAG_SIGN | FLAG_CARRY | FLAG_OVERFLOW | 1164 | FLAG_BREAK | FLAG_CONSTANT); 1165 | 1166 | return 0; 1167 | } 1168 | 1169 | int eor_opcode() { 1170 | 1171 | reset6502(); 1172 | 1173 | status = 0; 1174 | pc = 0x200; 1175 | a = 0xf0; 1176 | 1177 | exec_instruction(0x49, 0x43, 0x00); 1178 | CHECKFLAG(FLAG_SIGN, 1); 1179 | CHECKFLAG(FLAG_ZERO, 0); 1180 | CHECK(a, 0xb3); 1181 | 1182 | exec_instruction(0x49, 0xb3, 0x00); 1183 | CHECKFLAG(FLAG_SIGN, 0); 1184 | CHECKFLAG(FLAG_ZERO, 1); 1185 | CHECK(a, 0x00); 1186 | 1187 | return 0; 1188 | } 1189 | 1190 | int jsr_opcode() { 1191 | 1192 | reset6502(); 1193 | 1194 | status = 0; 1195 | pc = 0x300; 1196 | 1197 | exec_instruction(0x20, 0x43, 0x00); // JSR $0043 1198 | CHECK(pc, 0x0043); 1199 | 1200 | // check the return address on the stack 1201 | // (Because RTS increments the PC after fetching it, JSR actually pushes 1202 | // PC-1, so we need to check that the return address is $0302) 1203 | CHECK(sp, 0xfb); 1204 | CHECKMEM(0x01fd, 0x03); 1205 | CHECKMEM(0x01fc, 0x02); 1206 | 1207 | exec_instruction(0x60, 0x00, 0x00); // RTS 1208 | CHECK(sp, 0xfd); 1209 | // (RTS increments the return address, so it's $303, the location 1210 | // immediately after the JSR instruction) 1211 | CHECK(pc, 0x303); 1212 | 1213 | return 0; 1214 | } 1215 | 1216 | int rla_opcode() { 1217 | 1218 | 1219 | a = 0x23; 1220 | status = 0xf6; // Turn off the carry flag and decimal mode 1221 | write6502(0x01, 0x12); 1222 | 1223 | pc = 0x200; 1224 | reads = 0; 1225 | writes = 0; 1226 | exec_instruction(0x27, 0x01, 0x00); 1227 | /* 1228 | if (reads != 3) 1229 | return printf("rla zero-page did %d reads instead of 3\n", reads); 1230 | if (writes != 2) 1231 | return printf("rla zero-page did %d writes instead of 2\n", writes); 1232 | */ 1233 | CHECKMEM(0x01, 0x24); 1234 | 1235 | CHECK(pc, 0x0202); 1236 | CHECK(ea, 0x0001); 1237 | CHECK(a, 0x20); 1238 | return 0; 1239 | } 1240 | 1241 | int rra_opcode() { 1242 | a = 0x3; 1243 | status = 0xf6; // Turn off the carry flag and decimal mode 1244 | write6502(0x01, 0x02); 1245 | 1246 | pc = 0x200; 1247 | reads = 0; 1248 | writes = 0; 1249 | exec_instruction(0x67, 0x01, 0x00); 1250 | /* 1251 | if (reads != 3) 1252 | return printf("rra zero-page did %d reads instead of 3\n", reads); 1253 | if (writes != 2) 1254 | return printf("rra zero-page did %d writes instead of 2\n", writes); 1255 | */ 1256 | CHECKMEM(0x01, 0x01); 1257 | 1258 | CHECK(pc, 0x0202); 1259 | CHECK(ea, 0x0001); 1260 | CHECK(a, 0x04); 1261 | return 0; 1262 | } 1263 | 1264 | int nop_opcode() { 1265 | 1266 | pc = 0x200; 1267 | exec_instruction(0xea, 0x00, 0x00); // nop 1268 | return 0; 1269 | } 1270 | 1271 | int ora_opcode() { 1272 | 1273 | pc = 0x200; 1274 | a = 0x00; 1275 | exec_instruction(0x09, 0x00, 0x00); // ora #$00 1276 | CHECKFLAG(FLAG_SIGN, 0); 1277 | CHECKFLAG(FLAG_ZERO, 1); 1278 | CHECK(a, 0x00); 1279 | exec_instruction(0x09, 0x01, 0x00); // ora #$01 1280 | CHECKFLAG(FLAG_SIGN, 0); 1281 | CHECKFLAG(FLAG_ZERO, 0); 1282 | CHECK(a, 0x01); 1283 | exec_instruction(0x09, 0x02, 0x00); // ora #$02 1284 | CHECKFLAG(FLAG_SIGN, 0); 1285 | CHECKFLAG(FLAG_ZERO, 0); 1286 | CHECK(a, 0x03); 1287 | exec_instruction(0x09, 0x82, 0x00); // ora #$82 1288 | CHECKFLAG(FLAG_SIGN, 1); 1289 | CHECKFLAG(FLAG_ZERO, 0); 1290 | CHECK(a, 0x83); 1291 | return 0; 1292 | } 1293 | 1294 | int rol_opcode() { 1295 | 1296 | pc = 0x200; 1297 | a = 0x20; 1298 | status = 0x00; // Make sure Carry's clear 1299 | 1300 | exec_instruction(0x2a, 0x00, 0x00); // rol 1301 | CHECKFLAG(FLAG_CARRY, 0); 1302 | CHECKFLAG(FLAG_SIGN, 0); 1303 | CHECK(a, 0x40); 1304 | 1305 | exec_instruction(0x2a, 0x00, 0x00); // rol 1306 | CHECKFLAG(FLAG_CARRY, 0); 1307 | CHECKFLAG(FLAG_SIGN, 1); 1308 | CHECK(a, 0x80); 1309 | 1310 | exec_instruction(0x2a, 0x00, 0x00); // rol 1311 | CHECKFLAG(FLAG_CARRY, 1); 1312 | CHECKFLAG(FLAG_SIGN, 0); 1313 | CHECK(a, 0x00); 1314 | 1315 | exec_instruction(0x2a, 0x00, 0x00); // rol 1316 | CHECKFLAG(FLAG_CARRY, 0); 1317 | CHECKFLAG(FLAG_SIGN, 0); 1318 | CHECK(a, 0x01); 1319 | return 0; 1320 | } 1321 | 1322 | int rti_opcode() { 1323 | 1324 | pc = 0x200; 1325 | a = 0x20; 1326 | status = 0x00; 1327 | reset6502(); 1328 | 1329 | CHECK(sp, 0xfd); 1330 | write6502(0x1fe, 0x01); 1331 | write6502(0x1ff, 0x02); 1332 | write6502(0x100, 0x03); 1333 | 1334 | exec_instruction(0x40, 0x00, 0x00); // rti 1335 | CHECK(sp, 0x00); 1336 | CHECK(pc, 0x0302); 1337 | CHECKFLAG(FLAG_CARRY, 1); 1338 | CHECKFLAG(FLAG_SIGN, 0); 1339 | CHECKFLAG(FLAG_ZERO, 0); 1340 | CHECKFLAG(FLAG_INTERRUPT, 0); 1341 | CHECKFLAG(FLAG_DECIMAL, 0); 1342 | CHECKFLAG(FLAG_OVERFLOW, 0); 1343 | 1344 | return 0; 1345 | } 1346 | 1347 | int sax_opcode() { 1348 | 1349 | pc = 0x200; 1350 | a = 0x03; 1351 | x = 0x06; 1352 | status = 0x00; 1353 | uint8 address = 0xff; 1354 | 1355 | write6502(address, 0x03); 1356 | 1357 | exec_instruction(0x87, address, 0x00); // sax address 1358 | CHECK(pc, 0x0202); 1359 | CHECKMEM(address, 0x02); 1360 | 1361 | return 0; 1362 | } 1363 | 1364 | int sta_opcode() { 1365 | 1366 | pc = 0x200; 1367 | a = 0x20; 1368 | status = 0x00; 1369 | uint8 address = 0xff; 1370 | 1371 | write6502(address, 0x03); 1372 | 1373 | exec_instruction(0x85, address, 0x00); // sta address 1374 | CHECK(pc, 0x0202); 1375 | CHECKMEM(address, 0x20); 1376 | 1377 | return 0; 1378 | } 1379 | 1380 | int stx_opcode() { 1381 | 1382 | pc = 0x200; 1383 | x = 0x80; 1384 | status = 0x00; 1385 | uint8 address = 0xff; 1386 | 1387 | write6502(address, 0x03); 1388 | 1389 | exec_instruction(0x86, address, 0x00); // sta address 1390 | CHECK(pc, 0x0202); 1391 | CHECKMEM(address, 0x80); 1392 | 1393 | return 0; 1394 | } 1395 | 1396 | int sty_opcode() { 1397 | 1398 | pc = 0x200; 1399 | y = 0x01; 1400 | status = 0x00; 1401 | uint8 address = 0xff; 1402 | 1403 | write6502(0xff, 0x03); 1404 | 1405 | exec_instruction(0x84, address, 0x00); // sta address 1406 | CHECK(pc, 0x0202); 1407 | CHECKMEM(address, 0x01); 1408 | 1409 | return 0; 1410 | } 1411 | 1412 | int stz_opcode() { 1413 | 1414 | pc = 0x200; 1415 | status = 0x00; 1416 | uint8 address = 0xff; 1417 | 1418 | write6502(0xff, 0x03); 1419 | 1420 | exec_instruction(0x64, address, 0x00); // stz address 1421 | CHECK(pc, 0x0202); 1422 | CHECKMEM(address, 0); 1423 | 1424 | return 0; 1425 | } 1426 | 1427 | int sre_opcode() { 1428 | 1429 | 1430 | a = 0x3; 1431 | pc = 0x200; 1432 | write6502(0x01, 0x02); 1433 | 1434 | exec_instruction(0x47, 0x01, 0x00); // LSE $01 1435 | CHECKMEM(0x01, 0x01); 1436 | 1437 | CHECK(pc, 0x0202); 1438 | CHECK(ea, 0x0001); 1439 | CHECK(a, 0x02); 1440 | return 0; 1441 | } 1442 | 1443 | int lax_opcode() { 1444 | 1445 | pc = 0x200; 1446 | 1447 | write6502(0xab, 0x00); 1448 | exec_instruction(0xa7, 0xab, 0x00); // lax $ab 1449 | CHECKFLAG(FLAG_ZERO, 1); 1450 | CHECKFLAG(FLAG_SIGN, 0); 1451 | 1452 | write6502(0xab, 0x7b); 1453 | exec_instruction(0xa7, 0xab, 0x00); // lax $ab 1454 | CHECKFLAG(FLAG_ZERO, 0); 1455 | CHECKFLAG(FLAG_SIGN, 0); 1456 | 1457 | write6502(0xab, 0x8a); 1458 | exec_instruction(0xa7, 0xab, 0x00); // lax $ab 1459 | CHECKFLAG(FLAG_ZERO, 0); 1460 | CHECKFLAG(FLAG_SIGN, 1); 1461 | 1462 | return 0; 1463 | } 1464 | 1465 | /* 1466 | See this document: 1467 | http://www.zimmers.net/anonftp/pub/cbm/documents/chipdata/6502-NMOS.extra.opcodes 1468 | for information about how illegal opcodes work 1469 | */ 1470 | 1471 | int cmos_jmp_indirect() { 1472 | 1473 | pc = 0x200; 1474 | write6502(0x8000, 0x01); 1475 | write6502(0x80fe, 0x02); 1476 | write6502(0x80ff, 0x03); 1477 | write6502(0x8100, 0x04); 1478 | 1479 | clockticks6502 = 0; 1480 | exec_instruction(0x6c, 0xff, 0x80); 1481 | CHECK(pc, 0x0403); 1482 | CHECK(clockticks6502, 6); 1483 | 1484 | return 0; 1485 | } 1486 | 1487 | int cmos_jmp_absxi() { 1488 | 1489 | pc = 0x0200; 1490 | x = 0xff; 1491 | write6502(0x1456, 0xcd); 1492 | write6502(0x1457, 0xab); 1493 | exec_instruction(0x7c, 0x57, 0x13); 1494 | CHECK(pc, 0xabcd); 1495 | CHECK(clockticks6502, 6); 1496 | return 0; 1497 | } 1498 | 1499 | int pushme_pullyou() { 1500 | 1501 | reset6502(); 1502 | pc = 0x0200; 1503 | x = 0xff; 1504 | y = 0x01; 1505 | a = 0x00; 1506 | status = 0x00; 1507 | exec_instruction(0xda, 0x0, 0x0); // phx 1508 | CHECK(sp, 0xfc); 1509 | CHECKMEM(0x1fd, 0xff); 1510 | exec_instruction(0x5a, 0x0, 0x0); // phy 1511 | CHECK(sp, 0xfb); 1512 | CHECKMEM(0x1fc, 0x01); 1513 | exec_instruction(0x48, 0x0, 0x0); // pha 1514 | CHECK(sp, 0xfa); 1515 | CHECKMEM(0x1fb, 0x00); 1516 | exec_instruction(0x7a, 0x0, 0x0); // ply 1517 | CHECK(sp, 0xfb); 1518 | CHECK(y, 0x00); 1519 | exec_instruction(0x28, 0x0, 0x0); // plp 1520 | CHECK(sp, 0xfc); 1521 | CHECKFLAG(FLAG_CARRY, 1); 1522 | CHECKFLAG(FLAG_ZERO, 0); 1523 | CHECKFLAG(FLAG_INTERRUPT, 0); 1524 | CHECKFLAG(FLAG_DECIMAL, 0); 1525 | CHECKFLAG(FLAG_SIGN, 0); 1526 | CHECKFLAG(FLAG_OVERFLOW, 0); 1527 | exec_instruction(0x68, 0x0, 0x0); // pla 1528 | CHECK(sp, 0xfd); 1529 | CHECK(a, 0xff); 1530 | exec_instruction(0x08, 0x0, 0x0); // php 1531 | CHECK(sp, 0xfc); 1532 | exec_instruction(0xfa, 0x0, 0x0); // plx 1533 | CHECK(sp, 0xfd); 1534 | CHECK(x, (FLAG_SIGN | FLAG_CARRY | FLAG_CONSTANT | FLAG_BREAK)); 1535 | 1536 | return 0; 1537 | } 1538 | 1539 | typedef struct { 1540 | char *testname; 1541 | int (*fp)(); 1542 | } test_t; 1543 | 1544 | test_t normal_tests[] = {{"interrupts", &interrupt}, 1545 | {"zero page addressing", &test_zp}, 1546 | {"indexed zero page addressing", &test_zpx}, 1547 | {"absolute addressing", &absolute}, 1548 | {"absolute,x addressing", &absolute_x}, 1549 | {"absolute,y addressing", &absolute_y}, 1550 | {"indirect,y addressing", &indirect_y}, 1551 | {"indirect,x addressing", &indirect_x}, 1552 | {"decimal mode", decimal_mode}, 1553 | {"flags set & reset", flags}, 1554 | {"binary mode", binary_mode}, 1555 | {"push & pull", &pushpull}, 1556 | {"rotations", &rotations}, 1557 | {"branches", &branches}, 1558 | {"comparisons", &comparisons}, 1559 | {"increments and decrements", &incdec}, 1560 | {"loads", &loads}, 1561 | {"transfers", &transfers}, 1562 | {"and", &and_opcode}, 1563 | {"asl", &asl_opcode}, 1564 | {"bit", &bit_opcode}, 1565 | {"brk", &brk_opcode}, 1566 | {"eor", &eor_opcode}, 1567 | {"jsr & rts", &jsr_opcode}, 1568 | {"nop", &nop_opcode}, 1569 | {"ora", &ora_opcode}, 1570 | {"rol", &rol_opcode}, 1571 | {"rti", &rti_opcode}, 1572 | {"sta", &sta_opcode}, 1573 | {"stx", &stx_opcode}, 1574 | {"sty", &sty_opcode}, 1575 | {NULL, NULL}, 1576 | }; 1577 | 1578 | test_t nmos_tests[] = {{"indirect addressing", &indirect}, 1579 | {"rra", &rra_opcode}, 1580 | {"rla", &rla_opcode}, 1581 | {"sre", &sre_opcode}, 1582 | {"sax", &sax_opcode}, 1583 | {"lax", &lax_opcode}, 1584 | {NULL, NULL}}; 1585 | 1586 | test_t cmos_tests[] = {{"CMOS jmp indirect", &cmos_jmp_indirect}, 1587 | {"Immediate BIT", &bit_imm_opcode}, 1588 | {"(absolute,x)", &cmos_jmp_absxi}, 1589 | {"(zp) addressing", &test_zpi}, 1590 | {"CMOS pushes and pulls", &pushme_pullyou}, 1591 | {"stz", &stz_opcode}, 1592 | {NULL, NULL}}; 1593 | 1594 | int run_tests(test_t tests[]) { 1595 | int i; 1596 | for (i = 0; tests[i].fp != NULL 1597 | && tests[i].testname != NULL; i++ 1598 | ) 1599 | { 1600 | 1601 | if(tests[i].testname == NULL)break; 1602 | if(tests[i].fp){ 1603 | if (tests[i].fp()) { 1604 | printf("\033[0;31m%s failed\033[0m\n", tests[i].testname); 1605 | exit(1); 1606 | } 1607 | printf("\033[0;33m%s okay\033[0m\n", tests[i].testname); 1608 | } 1609 | } 1610 | printf("\r\n\r\n"); 1611 | return 0; 1612 | } 1613 | 1614 | int main(int argc, char **argv) { 1615 | (void)argc; 1616 | (void)argv; 1617 | run_tests(normal_tests); 1618 | run_tests(cmos_tests); 1619 | /* 1620 | if(argc > 1) 1621 | if (!strcmp(argv[1], "cmos")) 1622 | run_tests(cmos_tests); 1623 | */ 1624 | 1625 | return EXIT_SUCCESS; 1626 | } 1627 | -------------------------------------------------------------------------------- /tests.c: -------------------------------------------------------------------------------- 1 | /* 2 | THIS FILE IS NOT IN THE PUBLIC DOMAIN 3 | 4 | The .h file, fake6502.h, however, is. 5 | 6 | SO, don't put this file in your project, or richard stallman's going to be bustin' your ass for source code. 7 | 8 | The author (https://github.com/omarandlorraine/fake6502) 9 | wants this file to have copyleft stuff on it. 10 | 11 | So, if you use it, yours does too. 12 | 13 | Sort of like shit. Get a little on your hands? Now it's on everything you touch. 14 | Here's the shit: 15 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 16 | 17 | GNU GENERAL PUBLIC LICENSE 18 | Version 2, June 1991 19 | 20 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 21 | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA 22 | Everyone is permitted to copy and distribute verbatim copies 23 | of this license document, but changing it is not allowed. 24 | 25 | Preamble 26 | 27 | The licenses for most software are designed to take away your 28 | freedom to share and change it. By contrast, the GNU General Public 29 | License is intended to guarantee your freedom to share and change free 30 | software--to make sure the software is free for all its users. This 31 | General Public License applies to most of the Free Software 32 | Foundation's software and to any other program whose authors commit to 33 | using it. (Some other Free Software Foundation software is covered by 34 | the GNU Library General Public License instead.) You can apply it to 35 | your programs, too. 36 | 37 | When we speak of free software, we are referring to freedom, not 38 | price. Our General Public Licenses are designed to make sure that you 39 | have the freedom to distribute copies of free software (and charge for 40 | this service if you wish), that you receive source code or can get it 41 | if you want it, that you can change the software or use pieces of it 42 | in new free programs; and that you know you can do these things. 43 | 44 | To protect your rights, we need to make restrictions that forbid 45 | anyone to deny you these rights or to ask you to surrender the rights. 46 | These restrictions translate to certain responsibilities for you if you 47 | distribute copies of the software, or if you modify it. 48 | 49 | For example, if you distribute copies of such a program, whether 50 | gratis or for a fee, you must give the recipients all the rights that 51 | you have. You must make sure that they, too, receive or can get the 52 | source code. And you must show them these terms so they know their 53 | rights. 54 | 55 | We protect your rights with two steps: (1) copyright the software, and 56 | (2) offer you this license which gives you legal permission to copy, 57 | distribute and/or modify the software. 58 | 59 | Also, for each author's protection and ours, we want to make certain 60 | that everyone understands that there is no warranty for this free 61 | software. If the software is modified by someone else and passed on, we 62 | want its recipients to know that what they have is not the original, so 63 | that any problems introduced by others will not reflect on the original 64 | authors' reputations. 65 | 66 | Finally, any free program is threatened constantly by software 67 | patents. We wish to avoid the danger that redistributors of a free 68 | program will individually obtain patent licenses, in effect making the 69 | program proprietary. To prevent this, we have made it clear that any 70 | patent must be licensed for everyone's free use or not licensed at all. 71 | 72 | The precise terms and conditions for copying, distribution and 73 | modification follow. 74 | 75 | GNU GENERAL PUBLIC LICENSE 76 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 77 | 78 | 0. This License applies to any program or other work which contains 79 | a notice placed by the copyright holder saying it may be distributed 80 | under the terms of this General Public License. The "Program", below, 81 | refers to any such program or work, and a "work based on the Program" 82 | means either the Program or any derivative work under copyright law: 83 | that is to say, a work containing the Program or a portion of it, 84 | either verbatim or with modifications and/or translated into another 85 | language. (Hereinafter, translation is included without limitation in 86 | the term "modification".) Each licensee is addressed as "you". 87 | 88 | Activities other than copying, distribution and modification are not 89 | covered by this License; they are outside its scope. The act of 90 | running the Program is not restricted, and the output from the Program 91 | is covered only if its contents constitute a work based on the 92 | Program (independent of having been made by running the Program). 93 | Whether that is true depends on what the Program does. 94 | 95 | 1. You may copy and distribute verbatim copies of the Program's 96 | source code as you receive it, in any medium, provided that you 97 | conspicuously and appropriately publish on each copy an appropriate 98 | copyright notice and disclaimer of warranty; keep intact all the 99 | notices that refer to this License and to the absence of any warranty; 100 | and give any other recipients of the Program a copy of this License 101 | along with the Program. 102 | 103 | You may charge a fee for the physical act of transferring a copy, and 104 | you may at your option offer warranty protection in exchange for a fee. 105 | 106 | 2. You may modify your copy or copies of the Program or any portion 107 | of it, thus forming a work based on the Program, and copy and 108 | distribute such modifications or work under the terms of Section 1 109 | above, provided that you also meet all of these conditions: 110 | 111 | a) You must cause the modified files to carry prominent notices 112 | stating that you changed the files and the date of any change. 113 | 114 | b) You must cause any work that you distribute or publish, that in 115 | whole or in part contains or is derived from the Program or any 116 | part thereof, to be licensed as a whole at no charge to all third 117 | parties under the terms of this License. 118 | 119 | c) If the modified program normally reads commands interactively 120 | when run, you must cause it, when started running for such 121 | interactive use in the most ordinary way, to print or display an 122 | announcement including an appropriate copyright notice and a 123 | notice that there is no warranty (or else, saying that you provide 124 | a warranty) and that users may redistribute the program under 125 | these conditions, and telling the user how to view a copy of this 126 | License. (Exception: if the Program itself is interactive but 127 | does not normally print such an announcement, your work based on 128 | the Program is not required to print an announcement.) 129 | 130 | These requirements apply to the modified work as a whole. If 131 | identifiable sections of that work are not derived from the Program, 132 | and can be reasonably considered independent and separate works in 133 | themselves, then this License, and its terms, do not apply to those 134 | sections when you distribute them as separate works. But when you 135 | distribute the same sections as part of a whole which is a work based 136 | on the Program, the distribution of the whole must be on the terms of 137 | this License, whose permissions for other licensees extend to the 138 | entire whole, and thus to each and every part regardless of who wrote it. 139 | 140 | Thus, it is not the intent of this section to claim rights or contest 141 | your rights to work written entirely by you; rather, the intent is to 142 | exercise the right to control the distribution of derivative or 143 | collective works based on the Program. 144 | 145 | In addition, mere aggregation of another work not based on the Program 146 | with the Program (or with a work based on the Program) on a volume of 147 | a storage or distribution medium does not bring the other work under 148 | the scope of this License. 149 | 150 | 3. You may copy and distribute the Program (or a work based on it, 151 | under Section 2) in object code or executable form under the terms of 152 | Sections 1 and 2 above provided that you also do one of the following: 153 | 154 | a) Accompany it with the complete corresponding machine-readable 155 | source code, which must be distributed under the terms of Sections 156 | 1 and 2 above on a medium customarily used for software interchange; or, 157 | 158 | b) Accompany it with a written offer, valid for at least three 159 | years, to give any third party, for a charge no more than your 160 | cost of physically performing source distribution, a complete 161 | machine-readable copy of the corresponding source code, to be 162 | distributed under the terms of Sections 1 and 2 above on a medium 163 | customarily used for software interchange; or, 164 | 165 | c) Accompany it with the information you received as to the offer 166 | to distribute corresponding source code. (This alternative is 167 | allowed only for noncommercial distribution and only if you 168 | received the program in object code or executable form with such 169 | an offer, in accord with Subsection b above.) 170 | 171 | The source code for a work means the preferred form of the work for 172 | making modifications to it. For an executable work, complete source 173 | code means all the source code for all modules it contains, plus any 174 | associated interface definition files, plus the scripts used to 175 | control compilation and installation of the executable. However, as a 176 | special exception, the source code distributed need not include 177 | anything that is normally distributed (in either source or binary 178 | form) with the major components (compiler, kernel, and so on) of the 179 | operating system on which the executable runs, unless that component 180 | itself accompanies the executable. 181 | 182 | If distribution of executable or object code is made by offering 183 | access to copy from a designated place, then offering equivalent 184 | access to copy the source code from the same place counts as 185 | distribution of the source code, even though third parties are not 186 | compelled to copy the source along with the object code. 187 | 188 | 4. You may not copy, modify, sublicense, or distribute the Program 189 | except as expressly provided under this License. Any attempt 190 | otherwise to copy, modify, sublicense or distribute the Program is 191 | void, and will automatically terminate your rights under this License. 192 | However, parties who have received copies, or rights, from you under 193 | this License will not have their licenses terminated so long as such 194 | parties remain in full compliance. 195 | 196 | 5. You are not required to accept this License, since you have not 197 | signed it. However, nothing else grants you permission to modify or 198 | distribute the Program or its derivative works. These actions are 199 | prohibited by law if you do not accept this License. Therefore, by 200 | modifying or distributing the Program (or any work based on the 201 | Program), you indicate your acceptance of this License to do so, and 202 | all its terms and conditions for copying, distributing or modifying 203 | the Program or works based on it. 204 | 205 | 6. Each time you redistribute the Program (or any work based on the 206 | Program), the recipient automatically receives a license from the 207 | original licensor to copy, distribute or modify the Program subject to 208 | these terms and conditions. You may not impose any further 209 | restrictions on the recipients' exercise of the rights granted herein. 210 | You are not responsible for enforcing compliance by third parties to 211 | this License. 212 | 213 | 7. If, as a consequence of a court judgment or allegation of patent 214 | infringement or for any other reason (not limited to patent issues), 215 | conditions are imposed on you (whether by court order, agreement or 216 | otherwise) that contradict the conditions of this License, they do not 217 | excuse you from the conditions of this License. If you cannot 218 | distribute so as to satisfy simultaneously your obligations under this 219 | License and any other pertinent obligations, then as a consequence you 220 | may not distribute the Program at all. For example, if a patent 221 | license would not permit royalty-free redistribution of the Program by 222 | all those who receive copies directly or indirectly through you, then 223 | the only way you could satisfy both it and this License would be to 224 | refrain entirely from distribution of the Program. 225 | 226 | If any portion of this section is held invalid or unenforceable under 227 | any particular circumstance, the balance of the section is intended to 228 | apply and the section as a whole is intended to apply in other 229 | circumstances. 230 | 231 | It is not the purpose of this section to induce you to infringe any 232 | patents or other property right claims or to contest validity of any 233 | such claims; this section has the sole purpose of protecting the 234 | integrity of the free software distribution system, which is 235 | implemented by public license practices. Many people have made 236 | generous contributions to the wide range of software distributed 237 | through that system in reliance on consistent application of that 238 | system; it is up to the author/donor to decide if he or she is willing 239 | to distribute software through any other system and a licensee cannot 240 | impose that choice. 241 | 242 | This section is intended to make thoroughly clear what is believed to 243 | be a consequence of the rest of this License. 244 | 245 | 8. If the distribution and/or use of the Program is restricted in 246 | certain countries either by patents or by copyrighted interfaces, the 247 | original copyright holder who places the Program under this License 248 | may add an explicit geographical distribution limitation excluding 249 | those countries, so that distribution is permitted only in or among 250 | countries not thus excluded. In such case, this License incorporates 251 | the limitation as if written in the body of this License. 252 | 253 | 9. The Free Software Foundation may publish revised and/or new versions 254 | of the General Public License from time to time. Such new versions will 255 | be similar in spirit to the present version, but may differ in detail to 256 | address new problems or concerns. 257 | 258 | Each version is given a distinguishing version number. If the Program 259 | specifies a version number of this License which applies to it and "any 260 | later version", you have the option of following the terms and conditions 261 | either of that version or of any later version published by the Free 262 | Software Foundation. If the Program does not specify a version number of 263 | this License, you may choose any version ever published by the Free Software 264 | Foundation. 265 | 266 | 10. If you wish to incorporate parts of the Program into other free 267 | programs whose distribution conditions are different, write to the author 268 | to ask for permission. For software which is copyrighted by the Free 269 | Software Foundation, write to the Free Software Foundation; we sometimes 270 | make exceptions for this. Our decision will be guided by the two goals 271 | of preserving the free status of all derivatives of our free software and 272 | of promoting the sharing and reuse of software generally. 273 | 274 | NO WARRANTY 275 | 276 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 277 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 278 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 279 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 280 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 281 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 282 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 283 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 284 | REPAIR OR CORRECTION. 285 | 286 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 287 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 288 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 289 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 290 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 291 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 292 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 293 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 294 | POSSIBILITY OF SUCH DAMAGES. 295 | 296 | END OF TERMS AND CONDITIONS 297 | 298 | Appendix: How to Apply These Terms to Your New Programs 299 | 300 | If you develop a new program, and you want it to be of the greatest 301 | possible use to the public, the best way to achieve this is to make it 302 | free software which everyone can redistribute and change under these terms. 303 | 304 | To do so, attach the following notices to the program. It is safest 305 | to attach them to the start of each source file to most effectively 306 | convey the exclusion of warranty; and each file should have at least 307 | the "copyright" line and a pointer to where the full notice is found. 308 | 309 | 310 | Copyright (C) 19yy 311 | 312 | This program is free software; you can redistribute it and/or modify 313 | it under the terms of the GNU General Public License as published by 314 | the Free Software Foundation; either version 2 of the License, or 315 | (at your option) any later version. 316 | 317 | This program is distributed in the hope that it will be useful, 318 | but WITHOUT ANY WARRANTY; without even the implied warranty of 319 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 320 | GNU General Public License for more details. 321 | 322 | You should have received a copy of the GNU General Public License 323 | along with this program; if not, write to the Free Software 324 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA 325 | 326 | Also add information on how to contact you by electronic and paper mail. 327 | 328 | If the program is interactive, make it output a short notice like this 329 | when it starts in an interactive mode: 330 | 331 | Gnomovision version 69, Copyright (C) 19yy name of author 332 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 333 | This is free software, and you are welcome to redistribute it 334 | under certain conditions; type `show c' for details. 335 | 336 | The hypothetical commands `show w' and `show c' should show the appropriate 337 | parts of the General Public License. Of course, the commands you use may 338 | be called something other than `show w' and `show c'; they could even be 339 | mouse-clicks or menu items--whatever suits your program. 340 | 341 | You should also get your employer (if you work as a programmer) or your 342 | school, if any, to sign a "copyright disclaimer" for the program, if 343 | necessary. Here is a sample; alter the names: 344 | 345 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 346 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 347 | 348 | , 1 April 1989 349 | Ty Coon, President of Vice 350 | 351 | This General Public License does not permit incorporating your program into 352 | proprietary programs. If your program is a subroutine library, you may 353 | consider it more useful to permit linking proprietary applications with the 354 | library. If this is what you want to do, use the GNU Library General 355 | Public License instead of this License. 356 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 357 | Yuck! Back to code. 358 | 359 | */ 360 | 361 | 362 | 363 | #include 364 | #include 365 | #include 366 | #include "fake6502.h" 367 | 368 | #define FLAG_CARRY 0x01 369 | #define FLAG_ZERO 0x02 370 | #define FLAG_INTERRUPT 0x04 371 | #define FLAG_DECIMAL 0x08 372 | #define FLAG_BREAK 0x10 373 | #define FLAG_CONSTANT 0x20 374 | #define FLAG_OVERFLOW 0x40 375 | #define FLAG_SIGN 0x80 376 | 377 | #define CHECK(var, shouldbe) \ 378 | if (var != shouldbe) \ 379 | return printf("line %d: " #var " should've been %04x but was %04x\n", \ 380 | __LINE__, shouldbe, var); 381 | #define CHECKMEM(var, shouldbe) \ 382 | if (read6502(var) != (shouldbe)) \ 383 | return printf("line %d: memory location " #var \ 384 | " should've been %02x but was %02x\n", \ 385 | __LINE__, shouldbe, read6502(var)); 386 | 387 | #define CHECKFLAG(flag, shouldbe) \ 388 | if (!!(status & flag) != !!shouldbe) \ 389 | return printf( \ 390 | "line %d: " #flag " should be %sset but isn't, [ %02x, %02x] \n", \ 391 | __LINE__, shouldbe ? "" : "re", (status & flag), shouldbe); 392 | 393 | uint8 mem[65536]; 394 | 395 | int reads, writes; 396 | 397 | uint8 read6502(ushort addr) { 398 | reads++; 399 | return mem[addr]; 400 | } 401 | 402 | void write6502(ushort addr, uint8 val) { 403 | writes++; 404 | mem[addr] = val; 405 | } 406 | 407 | void exec_instruction(uint8 opcode, uint8 op1, 408 | uint8 op2) { 409 | write6502(pc, opcode); 410 | write6502(pc + 1, op1); 411 | write6502(pc + 2, op2); 412 | 413 | clockticks6502 = reads = writes = 0; 414 | step6502(); 415 | } 416 | 417 | int interrupt() { 418 | status = 0xff; 419 | 420 | write6502(0xfffa, 0x00); 421 | write6502(0xfffb, 0x40); 422 | write6502(0xfffc, 0x00); 423 | write6502(0xfffd, 0x50); 424 | write6502(0xfffe, 0x00); 425 | write6502(0xffff, 0x60); 426 | 427 | // On reset, a 6502 initialises the stack pointer to 0xFD, and jumps to the 428 | // address at 0xfffc. Also, the interrupt flag is cleared. 429 | status &= ~0x04; 430 | reset6502(); 431 | CHECK(sp, 0x00fd); 432 | CHECK(pc, 0x5000); 433 | 434 | CHECKFLAG(FLAG_INTERRUPT, 1); 435 | 436 | // This IRQ shouldn't fire because the interrupts are disabled 437 | irq6502(); 438 | CHECK(sp, 0x00fd); 439 | CHECK(pc, 0x5000); 440 | 441 | // Enable interrupts and try again 442 | status &= ~0x04; 443 | irq6502(); 444 | 445 | // On IRQ, a 6502 pushes the pc and flags onto the stack and then fetches pc 446 | // from the vector at 0xFFFE 447 | CHECK(sp, 0x00fa); 448 | CHECK(pc, 0x6000); 449 | 450 | CHECKMEM(0x01fd, 0x50); 451 | CHECKMEM(0x01fc, 0x00); 452 | CHECKMEM(0x01fb, status & 0xeb); 453 | 454 | CHECKFLAG(FLAG_INTERRUPT, 1); 455 | 456 | // The NMI may fire even when the Interrupt flag is set 457 | nmi6502(); 458 | CHECK(sp, 0x00f7); 459 | CHECK(pc, 0x4000); 460 | 461 | CHECKFLAG(FLAG_INTERRUPT, 1); 462 | 463 | return 0; 464 | } 465 | 466 | int test_zp() { 467 | 468 | pc = 0x200; 469 | exec_instruction(0xa5, 0x03, 0x00); 470 | 471 | CHECK(pc, 0x0202); 472 | CHECK(ea, 0x0003); 473 | return 0; 474 | } 475 | 476 | int test_zpx() { 477 | x = 1; 478 | y = 1; 479 | pc = 0x200; 480 | 481 | exec_instruction(0xb5, 0x03, 0x00); // lda $03,x 482 | CHECK(pc, 0x0202); 483 | CHECK(ea, 0x0004); 484 | 485 | exec_instruction(0xb5, 0xff, 0x00); // lda $ff,x 486 | CHECK(pc, 0x0204); 487 | CHECK(ea, 0x0000); 488 | 489 | exec_instruction(0xb6, 0x03, 0x00); // lda $03,x 490 | CHECK(pc, 0x0206); 491 | CHECK(ea, 0x0004); 492 | 493 | exec_instruction(0xb6, 0xff, 0x00); // ldx $ff,y 494 | CHECK(pc, 0x0208); 495 | CHECK(ea, 0x0000); 496 | 497 | return 0; 498 | } 499 | 500 | int decimal_mode() { 501 | a = 0x89; 502 | pc = 0x200; 503 | status = FLAG_DECIMAL; // Turn on decimal mode, clear carry flag 504 | 505 | exec_instruction(0x69, 0x01, 0x00); // ADC #$01 506 | CHECK(pc, 0x202); 507 | CHECK(a, 0x90); 508 | 509 | 510 | exec_instruction(0x69, 0x10, 0x00); // ADC #$10 511 | CHECK(pc, 0x204); 512 | CHECK(a, 0x00); 513 | 514 | exec_instruction(0x18, 0x00, 0x00); // CLC 515 | //exec_instruction(0x38, 0x00, 0x00); // SEC 516 | CHECK(pc, 0x205); 517 | CHECKFLAG(FLAG_CARRY, 0); 518 | /*ODDITY- carry flag doesn't matter here! Huh!*/ 519 | 520 | exec_instruction(0xe9, 0x01, 0x00); // SBC #$01 521 | CHECK(pc, 0x207); 522 | CHECK(a, 0x98); 523 | CHECKFLAG(FLAG_CARRY, 0); /*Inverse borrow- carry should be 0.*/ 524 | /*The carry bit is not set, so a subtraction should take us down to 98! However, the carry bit *will* be set after this.*/ 525 | 526 | /*This instruction starts with the carry bit not set, so we expect it to subtract an extra. */ 527 | exec_instruction(0xe9, 0x10, 0x00); // SBC #$10 528 | CHECK(pc, 0x209); 529 | CHECKFLAG(FLAG_CARRY, 1); 530 | CHECK(a, 0x87); /*this is the result.*/ 531 | 532 | 533 | exec_instruction(0x38, 0x00, 0x00); /* SEC*/ 534 | CHECK(pc, 0x20A); 535 | CHECKFLAG(FLAG_CARRY, 1); 536 | 537 | exec_instruction(0xe9, 0x03, 0x00); /* SBC #$3*/ 538 | CHECK(pc, 0x20C); 539 | CHECK(a, 0x84); 540 | CHECKFLAG(FLAG_CARRY, 1); 541 | /*the carry bit is set so it will subtract 4*/ 542 | exec_instruction(0xe9, 0x04, 0x00); // SBC #$4 543 | CHECK(pc, 0x20E); 544 | CHECK(a, 0x80); 545 | CHECKFLAG(FLAG_CARRY, 1); 546 | /*the carry bit is set so it will subtract 2 instead of just two.*/ 547 | exec_instruction(0xe9, 0x02, 0x00); // SBC #$2 548 | CHECK(pc, 0x210); 549 | CHECK(a, 0x78); 550 | 551 | return 0; 552 | } 553 | 554 | int binary_mode() { 555 | a = 0x89; 556 | pc = 0x200; 557 | status = 0x00; // Turn off decimal mode, clear carry flag 558 | 559 | exec_instruction(0x69, 0x01, 0x00); // ADC #$01 560 | CHECK(pc, 0x202); 561 | CHECK(a, 0x8a); 562 | 563 | exec_instruction(0x69, 0x14, 0x00); // ADC #$10 564 | CHECK(pc, 0x204); 565 | CHECK(a, 0x9e); 566 | 567 | exec_instruction(0x18, 0x00, 0x00); // CLC 568 | CHECK(pc, 0x205); 569 | 570 | exec_instruction(0xe9, 0x01, 0x00); // SBC #$01 571 | CHECK(pc, 0x207); 572 | CHECK(a, 0x9c); /*BUG! the carry flag was not set. it should be 9c, not 9d.*/ 573 | CHECKFLAG(FLAG_CARRY, 1); 574 | 575 | exec_instruction(0xe9, 0x10, 0x00); // SBC #$10 576 | CHECK(pc, 0x209); 577 | CHECK(a, 0x8c); /*The carry flag was set, */ 578 | CHECKFLAG(FLAG_CARRY, 1); /*Continued subtractions should permit continuing the result.*/ 579 | 580 | return 0; 581 | } 582 | 583 | int pushpull() { 584 | 585 | a = 0x89; 586 | sp = 0xff; 587 | pc = 0x0200; 588 | exec_instruction(0xa9, 0x40, 0x00); // LDA #$40 589 | exec_instruction(0x48, 0x00, 0x00); // PHA 590 | CHECK(sp, 0xfe); 591 | CHECKMEM(0x01ff, 0x40); 592 | exec_instruction(0xa9, 0x00, 0x00); // LDA #$00 593 | exec_instruction(0x48, 0x00, 0x00); // PHA 594 | CHECK(sp, 0xfd); 595 | CHECKMEM(0x01fe, 0x00); 596 | exec_instruction(0x60, 0x00, 0x00); // RTS 597 | CHECK(sp, 0xff); 598 | CHECK(pc, 0x4001); 599 | return 0; 600 | } 601 | 602 | int rotations() { 603 | 604 | 605 | a = 0x01; 606 | status = 0x00; 607 | pc = 0x0200; 608 | exec_instruction(0x6a, 0x00, 0x00); // ROR A 609 | CHECK(a, 0x00); 610 | CHECK(pc, 0x0201); 611 | exec_instruction(0x6a, 0x00, 0x00); // ROR A 612 | CHECK(a, 0x80); 613 | CHECK(pc, 0x0202); 614 | 615 | a = 0x01; 616 | status = 0x00; 617 | pc = 0x0200; 618 | exec_instruction(0x4a, 0x00, 0x00); // LSR A 619 | CHECK(a, 0x00); 620 | CHECK(pc, 0x0201); 621 | exec_instruction(0x4a, 0x00, 0x00); // LSR A 622 | CHECK(a, 0x00); 623 | CHECK(pc, 0x0202); 624 | return 0; 625 | } 626 | 627 | int incdec() { 628 | 629 | x = 0x80; 630 | y = 0x80; 631 | 632 | write6502(0x00, 0x00); 633 | status = 0x00; 634 | pc = 0x0200; 635 | 636 | exec_instruction(0xc6, 0x00, 0x00); // DEC $0 637 | CHECK(pc, 0x0202); 638 | CHECKFLAG(FLAG_ZERO, 0); 639 | CHECKFLAG(FLAG_SIGN, 1); 640 | 641 | exec_instruction(0xe6, 0x00, 0x00); // INC $0 642 | CHECK(pc, 0x0204); 643 | CHECKFLAG(FLAG_ZERO, 1); 644 | CHECKFLAG(FLAG_SIGN, 0); 645 | 646 | exec_instruction(0xe8, 0x00, 0x00); // INX 647 | CHECK(pc, 0x0205); 648 | CHECK(x, 0x81); 649 | CHECKFLAG(FLAG_ZERO, 0); 650 | CHECKFLAG(FLAG_SIGN, 1); 651 | 652 | exec_instruction(0x88, 0x00, 0x00); // DEY 653 | CHECK(pc, 0x0206); 654 | CHECK(y, 0x7f); 655 | CHECKFLAG(FLAG_ZERO, 0); 656 | CHECKFLAG(FLAG_SIGN, 0); 657 | 658 | x = 0x00; 659 | exec_instruction(0xca, 0x00, 0x00); // DEX 660 | CHECK(pc, 0x0207); 661 | CHECK(y, 0x7f); 662 | CHECK(x, 0xff); 663 | CHECKFLAG(FLAG_ZERO, 0); 664 | CHECKFLAG(FLAG_SIGN, 1); 665 | 666 | y = 0x00; 667 | exec_instruction(0xc8, 0x00, 0x00); // INY 668 | CHECK(pc, 0x0208); 669 | CHECK(y, 0x01); 670 | CHECK(x, 0xff); 671 | CHECKFLAG(FLAG_ZERO, 0); 672 | CHECKFLAG(FLAG_SIGN, 0); 673 | 674 | return 0; 675 | } 676 | 677 | int branches() { 678 | 679 | status = 0x00; 680 | pc = 0x0200; 681 | exec_instruction(0x10, 0x60, 0x00); // BPL *+$60 682 | CHECK(pc, 0x0262); 683 | exec_instruction(0x30, 0x10, 0x00); // BMI *+$10 684 | CHECK(pc, 0x0264); 685 | exec_instruction(0x50, 0x70, 0x00); // BVC *+$70 686 | CHECK(pc, 0x02d6); 687 | exec_instruction(0x90, 0x70, 0x00); // BCC *+$70 688 | CHECK(pc, 0x0348); 689 | exec_instruction(0xb0, 0x70, 0x00); // BCS *+$70 690 | CHECK(pc, 0x034a); 691 | exec_instruction(0x70, 0xfa, 0x00); // BVS *-$06 692 | CHECK(pc, 0x034c); 693 | exec_instruction(0xd0, 0xfa, 0x00); // BNE *-$06 694 | CHECK(pc, 0x0348); 695 | exec_instruction(0xf0, 0xfa, 0x00); // BEQ *-$06 696 | CHECK(pc, 0x034a); 697 | 698 | status = FLAG_CARRY | FLAG_ZERO | FLAG_SIGN | FLAG_OVERFLOW; 699 | exec_instruction(0xb0, 0x70, 0x00); // BCS *+$70 700 | CHECK(pc, 0x03bc); 701 | exec_instruction(0xf0, 0xfa, 0x00); // BEQ *-$06 702 | CHECK(pc, 0x03b8); 703 | exec_instruction(0x30, 0x10, 0x00); // BMI *+$10 704 | CHECK(pc, 0x03ca); 705 | exec_instruction(0x70, 0xfa, 0x00); // BVS *-$06 706 | CHECK(pc, 0x03c6); 707 | return 0; 708 | } 709 | 710 | int comparisons() { 711 | 712 | status = 0x00; 713 | pc = 0x0200; 714 | a = 0x50; 715 | x = 0x00; 716 | y = 0xc0; 717 | 718 | exec_instruction(0xc9, 0x00, 0x00); 719 | CHECKFLAG(FLAG_CARRY, 1); 720 | CHECKFLAG(FLAG_ZERO, 0); 721 | CHECKFLAG(FLAG_SIGN, 0); 722 | 723 | exec_instruction(0xc9, 0x51, 0x00); 724 | CHECKFLAG(FLAG_CARRY, 0); 725 | CHECKFLAG(FLAG_ZERO, 0); 726 | CHECKFLAG(FLAG_SIGN, 1); 727 | 728 | exec_instruction(0xe0, 0x00, 0x00); 729 | CHECKFLAG(FLAG_CARRY, 1); 730 | CHECKFLAG(FLAG_ZERO, 1); 731 | CHECKFLAG(FLAG_SIGN, 0); 732 | 733 | exec_instruction(0xc0, 0x01, 0x00); 734 | CHECKFLAG(FLAG_CARRY, 1); 735 | CHECKFLAG(FLAG_ZERO, 0); 736 | CHECKFLAG(FLAG_SIGN, 1); 737 | 738 | return 0; 739 | } 740 | 741 | int absolute() { 742 | 743 | clockticks6502 = 0; 744 | pc = 0x200; 745 | exec_instruction(0xad, 0x60, 0x00); 746 | CHECK(ea, 0x0060); 747 | CHECK(pc, 0x0203); 748 | CHECK(clockticks6502, 4); 749 | return 0; 750 | } 751 | 752 | int absolute_x() { 753 | 754 | pc = 0x200; 755 | x = 0x80; 756 | clockticks6502 = 0; 757 | 758 | exec_instruction(0xbd, 0x60, 0x00); 759 | CHECK(ea, 0x00e0); 760 | CHECK(pc, 0x0203); 761 | CHECK(clockticks6502, 4); 762 | 763 | // Takes another cycle because of page-crossing 764 | clockticks6502 = 0; 765 | exec_instruction(0xbd, 0xa0, 0x00); 766 | CHECK(ea, 0x0120); 767 | CHECK(pc, 0x0206); 768 | CHECK(clockticks6502, 5); 769 | 770 | // Should NOT take another cycle because of page-crossing 771 | clockticks6502 = 0; 772 | exec_instruction(0x9d, 0x60, 0x00); 773 | CHECK(ea, 0x00e0); 774 | CHECK(pc, 0x0209); 775 | CHECK(clockticks6502, 5); 776 | 777 | clockticks6502 = 0; 778 | exec_instruction(0x9d, 0xa0, 0x00); 779 | CHECK(ea, 0x0120); 780 | CHECK(pc, 0x020c); 781 | CHECK(clockticks6502, 5); 782 | return 0; 783 | } 784 | 785 | int absolute_y() { 786 | 787 | pc = 0x200; 788 | y = 0x80; 789 | clockticks6502 = 0; 790 | 791 | exec_instruction(0xb9, 0x60, 0x00); 792 | CHECK(ea, 0x00e0); 793 | CHECK(pc, 0x0203); 794 | CHECK(clockticks6502, 4); 795 | 796 | // Takes another cycle because of page-crossing 797 | clockticks6502 = 0; 798 | exec_instruction(0xb9, 0xa0, 0x00); 799 | CHECK(ea, 0x0120); 800 | CHECK(pc, 0x0206); 801 | CHECK(clockticks6502, 5); 802 | 803 | // Should NOT take another cycle because of page-crossing 804 | clockticks6502 = 0; 805 | exec_instruction(0x99, 0x60, 0x00); 806 | CHECK(ea, 0x00e0); 807 | CHECK(pc, 0x0209); 808 | CHECK(clockticks6502, 5); 809 | 810 | clockticks6502 = 0; 811 | exec_instruction(0x99, 0xa0, 0x00); 812 | CHECK(ea, 0x0120); 813 | CHECK(pc, 0x020c); 814 | CHECK(clockticks6502, 5); 815 | return 0; 816 | } 817 | 818 | int indirect() { 819 | 820 | pc = 0x200; 821 | write6502(0x8000, 0x01); 822 | write6502(0x80fe, 0x02); 823 | write6502(0x80ff, 0x03); 824 | write6502(0x8100, 0x04); 825 | 826 | clockticks6502 = 0; 827 | exec_instruction(0x6c, 0xff, 0x80); 828 | CHECK(pc, 0x0103); 829 | CHECK(clockticks6502, 5); 830 | 831 | return 0; 832 | } 833 | 834 | int indirect_y() { 835 | 836 | pc = 0x200; 837 | y = 0x80; 838 | write6502(0x20, 0x81); 839 | write6502(0x21, 0x20); 840 | 841 | // Takes another cycle because of page-crossing 842 | clockticks6502 = 0; 843 | exec_instruction(0xb1, 0x20, 0x00); 844 | CHECK(ea, 0x2101); 845 | CHECK(pc, 0x0202); 846 | CHECK(clockticks6502, 6); 847 | 848 | // Should NOT take another cycle because of page-crossing 849 | clockticks6502 = 0; 850 | y = 0x10; 851 | exec_instruction(0xb1, 0x20, 0x00); 852 | CHECK(ea, 0x2091); 853 | CHECK(pc, 0x0204); 854 | CHECK(clockticks6502, 5); 855 | 856 | // Takes 6 cycles regardless of page-crossing or not 857 | clockticks6502 = 0; 858 | y = 0x80; 859 | exec_instruction(0x91, 0x20, 0x00); 860 | CHECK(ea, 0x2101); 861 | CHECK(pc, 0x0206); 862 | CHECK(clockticks6502, 6); 863 | 864 | // Takes 6 cycles regardless of page-crossing or not 865 | clockticks6502 = 0; 866 | y = 0x10; 867 | exec_instruction(0x91, 0x20, 0x00); 868 | CHECK(ea, 0x2091); 869 | CHECK(pc, 0x0208); 870 | CHECK(clockticks6502, 6); 871 | 872 | return 0; 873 | } 874 | 875 | int indirect_x() { 876 | 877 | pc = 0x200; 878 | x = 0x00; 879 | write6502(0x20, 0x81); 880 | write6502(0x21, 0x20); 881 | write6502(0xff, 0x81); 882 | write6502(0x00, 0x20); 883 | 884 | exec_instruction(0xa1, 0x20, 0x00); 885 | CHECK(ea, 0x2081); 886 | CHECK(pc, 0x0202); 887 | CHECK(clockticks6502, 6); 888 | 889 | x = 0x40; 890 | exec_instruction(0xa1, 0xe0, 0x00); 891 | CHECK(ea, 0x2081); 892 | CHECK(clockticks6502, 6); 893 | 894 | x = 0; 895 | exec_instruction(0xa1, 0x20, 0x00); 896 | CHECK(ea, 0x2081); 897 | CHECK(clockticks6502, 6); 898 | 899 | exec_instruction(0x81, 0xff, 0x00); 900 | CHECK(ea, 0x2081); 901 | CHECK(clockticks6502, 6); 902 | 903 | exec_instruction(0x81, 0x20, 0x00); 904 | CHECK(ea, 0x2081); 905 | CHECK(clockticks6502, 6); 906 | 907 | return 0; 908 | } 909 | 910 | int test_zpi() { 911 | 912 | pc = 0x200; 913 | x = 0x59; 914 | write6502(0x20, 0x81); 915 | write6502(0x21, 0x20); 916 | write6502(0xff, 0x81); 917 | write6502(0x00, 0x20); 918 | 919 | exec_instruction(0xb2, 0x20, 0x00); 920 | CHECK(ea, 0x2081); 921 | CHECK(pc, 0x0202); 922 | CHECK(clockticks6502, 5); 923 | 924 | x = 0; 925 | exec_instruction(0xb2, 0x20, 0x00); 926 | CHECK(ea, 0x2081); 927 | CHECK(clockticks6502, 5); 928 | 929 | exec_instruction(0x92, 0xff, 0x00); 930 | CHECK(ea, 0x2081); 931 | CHECK(clockticks6502, 5); 932 | 933 | exec_instruction(0x92, 0x20, 0x00); 934 | CHECK(ea, 0x2081); 935 | CHECK(clockticks6502, 5); 936 | 937 | return 0; 938 | } 939 | 940 | int flags() { 941 | 942 | pc = 0x200; 943 | status = 0xff; 944 | 945 | exec_instruction(0x18, 0x00, 0x00); 946 | CHECKFLAG(FLAG_CARRY, 0); 947 | exec_instruction(0x38, 0x00, 0x00); 948 | CHECKFLAG(FLAG_CARRY, 1); 949 | 950 | exec_instruction(0x58, 0x00, 0x00); 951 | CHECKFLAG(FLAG_INTERRUPT, 0); 952 | exec_instruction(0x78, 0x00, 0x00); 953 | CHECKFLAG(FLAG_INTERRUPT, 1); 954 | 955 | exec_instruction(0xd8, 0x00, 0x00); 956 | CHECKFLAG(FLAG_DECIMAL, 0); 957 | exec_instruction(0xf8, 0x00, 0x00); 958 | CHECKFLAG(FLAG_DECIMAL, 1); 959 | 960 | exec_instruction(0xb8, 0x00, 0x00); 961 | CHECKFLAG(FLAG_OVERFLOW, 0); 962 | 963 | return 0; 964 | } 965 | 966 | int loads() { 967 | 968 | pc = 0x200; 969 | 970 | exec_instruction(0xa0, 0x00, 0x00); // ldy #$00 971 | CHECKFLAG(FLAG_ZERO, 1); 972 | CHECKFLAG(FLAG_SIGN, 0); 973 | 974 | exec_instruction(0xa0, 0x80, 0x00); // ldy #$80 975 | CHECKFLAG(FLAG_ZERO, 0); 976 | CHECKFLAG(FLAG_SIGN, 1); 977 | 978 | exec_instruction(0xa0, 0x7f, 0x00); // ldy #$7f 979 | CHECKFLAG(FLAG_ZERO, 0); 980 | CHECKFLAG(FLAG_SIGN, 0); 981 | 982 | exec_instruction(0xa2, 0x00, 0x00); // ldx #$ff 983 | CHECKFLAG(FLAG_ZERO, 1); 984 | CHECKFLAG(FLAG_SIGN, 0); 985 | 986 | return 0; 987 | } 988 | 989 | int transfers() { 990 | 991 | pc = 0x200; 992 | status = 0x00; 993 | 994 | x = 0x80; 995 | 996 | exec_instruction(0x9a, 0x00, 0x00); // txs 997 | CHECKFLAG(FLAG_ZERO, 0); 998 | CHECKFLAG(FLAG_SIGN, 0); 999 | CHECK(sp, 0x80); 1000 | 1001 | exec_instruction(0x8a, 0x00, 0x00); // txa 1002 | CHECKFLAG(FLAG_ZERO, 0); 1003 | CHECKFLAG(FLAG_SIGN, 1); 1004 | CHECK(a, 0x80); 1005 | 1006 | a = 0x1; 1007 | exec_instruction(0xaa, 0x00, 0x00); // tax 1008 | CHECKFLAG(FLAG_ZERO, 0); 1009 | CHECKFLAG(FLAG_SIGN, 0); 1010 | CHECK(x, 0x01); 1011 | 1012 | exec_instruction(0xba, 0x80, 0x00); // tsx 1013 | CHECKFLAG(FLAG_ZERO, 0); 1014 | CHECKFLAG(FLAG_SIGN, 1); 1015 | CHECK(x, 0x80); 1016 | 1017 | exec_instruction(0xa8, 0x00, 0x00); // tay 1018 | CHECKFLAG(FLAG_ZERO, 0); 1019 | CHECKFLAG(FLAG_SIGN, 0); 1020 | CHECK(y, 0x01); 1021 | 1022 | a = 0x80; 1023 | exec_instruction(0x98, 0x00, 0x00); // tya 1024 | CHECKFLAG(FLAG_ZERO, 0); 1025 | CHECKFLAG(FLAG_SIGN, 0); 1026 | CHECK(a, 0x01); 1027 | 1028 | return 0; 1029 | } 1030 | 1031 | int and_opcode() { 1032 | 1033 | 1034 | a = 0xff; 1035 | status = 0x00; 1036 | pc = 0x200; 1037 | 1038 | exec_instruction(0x29, 0xe1, 0x00); 1039 | CHECK(a, 0xe1); 1040 | CHECKFLAG(FLAG_ZERO, 0); 1041 | CHECKFLAG(FLAG_SIGN, 1); 1042 | CHECK(pc, 0x0202); 1043 | 1044 | exec_instruction(0x29, 0x71, 0x00); 1045 | CHECK(a, 0x61); 1046 | CHECKFLAG(FLAG_ZERO, 0); 1047 | CHECKFLAG(FLAG_SIGN, 0); 1048 | 1049 | exec_instruction(0x29, 0x82, 0x00); 1050 | CHECK(a, 0x00); 1051 | CHECKFLAG(FLAG_ZERO, 1); 1052 | CHECKFLAG(FLAG_SIGN, 0); 1053 | 1054 | return 0; 1055 | } 1056 | 1057 | int asl_opcode() { 1058 | 1059 | 1060 | a = 0x50; 1061 | status = 0x00; 1062 | pc = 0x200; 1063 | 1064 | exec_instruction(0x0a, 0x00, 0x00); 1065 | CHECK(a, 0xa0); 1066 | CHECKFLAG(FLAG_ZERO, 0); 1067 | CHECKFLAG(FLAG_SIGN, 1); 1068 | CHECKFLAG(FLAG_CARRY, 0); 1069 | CHECK(pc, 0x0201); 1070 | 1071 | exec_instruction(0x0a, 0x00, 0x00); 1072 | CHECK(a, 0x40); 1073 | CHECKFLAG(FLAG_ZERO, 0); 1074 | CHECKFLAG(FLAG_SIGN, 0); 1075 | CHECKFLAG(FLAG_CARRY, 1); 1076 | 1077 | exec_instruction(0x0a, 0x00, 0x00); 1078 | CHECK(a, 0x80); 1079 | CHECKFLAG(FLAG_ZERO, 0); 1080 | CHECKFLAG(FLAG_SIGN, 1); 1081 | CHECKFLAG(FLAG_CARRY, 0); 1082 | 1083 | exec_instruction(0x0a, 0x00, 0x00); 1084 | CHECK(a, 0x00); 1085 | CHECKFLAG(FLAG_ZERO, 1); 1086 | CHECKFLAG(FLAG_SIGN, 0); 1087 | CHECKFLAG(FLAG_CARRY, 1); 1088 | 1089 | return 0; 1090 | } 1091 | 1092 | int bit_opcode() { 1093 | 1094 | 1095 | a = 0x50; 1096 | status = 0x00; 1097 | pc = 0x200; 1098 | write6502(0xff, 0x80); 1099 | 1100 | exec_instruction(0x24, 0xff, 0x00); 1101 | CHECK(a, 0x50); 1102 | CHECKFLAG(FLAG_ZERO, 1); 1103 | CHECKFLAG(FLAG_SIGN, 1); 1104 | CHECKFLAG(FLAG_CARRY, 0); 1105 | CHECK(pc, 0x0202); 1106 | 1107 | a = 0x40; 1108 | write6502(0xff, 0x40); 1109 | exec_instruction(0x2c, 0xff, 0x00); 1110 | CHECK(a, 0x40); 1111 | CHECKFLAG(FLAG_ZERO, 0); 1112 | CHECKFLAG(FLAG_SIGN, 0); 1113 | CHECKFLAG(FLAG_CARRY, 0); 1114 | CHECK(pc, 0x0205); 1115 | 1116 | return 0; 1117 | } 1118 | 1119 | int bit_imm_opcode() { 1120 | 1121 | 1122 | a = 0x50; 1123 | status = 0x00; 1124 | pc = 0x200; 1125 | 1126 | exec_instruction(0x89, 0xff, 0x00); 1127 | CHECK(a, 0x50); 1128 | CHECKFLAG(FLAG_ZERO, 0); 1129 | CHECKFLAG(FLAG_SIGN, 0); 1130 | CHECKFLAG(FLAG_CARRY, 0); 1131 | CHECK(pc, 0x0202); 1132 | 1133 | exec_instruction(0x89, 0x80, 0x00); 1134 | CHECK(a, 0x50); 1135 | CHECKFLAG(FLAG_ZERO, 1); 1136 | CHECKFLAG(FLAG_SIGN, 0); 1137 | CHECKFLAG(FLAG_CARRY, 0); 1138 | CHECK(pc, 0x0204); 1139 | 1140 | return 0; 1141 | } 1142 | 1143 | int brk_opcode() { 1144 | 1145 | reset6502(); 1146 | 1147 | status = FLAG_ZERO | FLAG_SIGN | FLAG_CARRY | FLAG_OVERFLOW; 1148 | pc = 0x200; 1149 | write6502(0xfffe, 0x00); 1150 | write6502(0xffff, 0x60); 1151 | 1152 | exec_instruction(0x00, 0x00, 0x00); 1153 | CHECKFLAG(FLAG_ZERO, 1); 1154 | CHECKFLAG(FLAG_SIGN, 1); 1155 | CHECKFLAG(FLAG_CARRY, 1); 1156 | CHECKFLAG(FLAG_INTERRUPT, 1); 1157 | CHECKFLAG(FLAG_OVERFLOW, 1); 1158 | 1159 | CHECK(sp, 0x00fa); 1160 | CHECK(pc, 0x6000); 1161 | 1162 | CHECKMEM(0x01fd, 0x02); 1163 | CHECKMEM(0x01fc, 0x02); 1164 | CHECKMEM(0x01fb, FLAG_ZERO | FLAG_SIGN | FLAG_CARRY | FLAG_OVERFLOW | 1165 | FLAG_BREAK | FLAG_CONSTANT); 1166 | 1167 | return 0; 1168 | } 1169 | 1170 | int eor_opcode() { 1171 | 1172 | reset6502(); 1173 | 1174 | status = 0; 1175 | pc = 0x200; 1176 | a = 0xf0; 1177 | 1178 | exec_instruction(0x49, 0x43, 0x00); 1179 | CHECKFLAG(FLAG_SIGN, 1); 1180 | CHECKFLAG(FLAG_ZERO, 0); 1181 | CHECK(a, 0xb3); 1182 | 1183 | exec_instruction(0x49, 0xb3, 0x00); 1184 | CHECKFLAG(FLAG_SIGN, 0); 1185 | CHECKFLAG(FLAG_ZERO, 1); 1186 | CHECK(a, 0x00); 1187 | 1188 | return 0; 1189 | } 1190 | 1191 | int jsr_opcode() { 1192 | 1193 | reset6502(); 1194 | 1195 | status = 0; 1196 | pc = 0x300; 1197 | 1198 | exec_instruction(0x20, 0x43, 0x00); // JSR $0043 1199 | CHECK(pc, 0x0043); 1200 | 1201 | // check the return address on the stack 1202 | // (Because RTS increments the PC after fetching it, JSR actually pushes 1203 | // PC-1, so we need to check that the return address is $0302) 1204 | CHECK(sp, 0xfb); 1205 | CHECKMEM(0x01fd, 0x03); 1206 | CHECKMEM(0x01fc, 0x02); 1207 | 1208 | exec_instruction(0x60, 0x00, 0x00); // RTS 1209 | CHECK(sp, 0xfd); 1210 | // (RTS increments the return address, so it's $303, the location 1211 | // immediately after the JSR instruction) 1212 | CHECK(pc, 0x303); 1213 | 1214 | return 0; 1215 | } 1216 | 1217 | int rla_opcode() { 1218 | 1219 | 1220 | a = 0x23; 1221 | status = 0xf6; // Turn off the carry flag and decimal mode 1222 | write6502(0x01, 0x12); 1223 | 1224 | pc = 0x200; 1225 | reads = 0; 1226 | writes = 0; 1227 | exec_instruction(0x27, 0x01, 0x00); 1228 | /* 1229 | if (reads != 3) 1230 | return printf("rla zero-page did %d reads instead of 3\n", reads); 1231 | if (writes != 2) 1232 | return printf("rla zero-page did %d writes instead of 2\n", writes); 1233 | */ 1234 | CHECKMEM(0x01, 0x24); 1235 | 1236 | CHECK(pc, 0x0202); 1237 | CHECK(ea, 0x0001); 1238 | CHECK(a, 0x20); 1239 | return 0; 1240 | } 1241 | 1242 | int rra_opcode() { 1243 | a = 0x3; 1244 | status = 0xf6; // Turn off the carry flag and decimal mode 1245 | write6502(0x01, 0x02); 1246 | 1247 | pc = 0x200; 1248 | reads = 0; 1249 | writes = 0; 1250 | exec_instruction(0x67, 0x01, 0x00); 1251 | /* 1252 | if (reads != 3) 1253 | return printf("rra zero-page did %d reads instead of 3\n", reads); 1254 | if (writes != 2) 1255 | return printf("rra zero-page did %d writes instead of 2\n", writes); 1256 | */ 1257 | CHECKMEM(0x01, 0x01); 1258 | 1259 | CHECK(pc, 0x0202); 1260 | CHECK(ea, 0x0001); 1261 | CHECK(a, 0x04); 1262 | return 0; 1263 | } 1264 | 1265 | int nop_opcode() { 1266 | 1267 | pc = 0x200; 1268 | exec_instruction(0xea, 0x00, 0x00); // nop 1269 | return 0; 1270 | } 1271 | 1272 | int ora_opcode() { 1273 | 1274 | pc = 0x200; 1275 | a = 0x00; 1276 | exec_instruction(0x09, 0x00, 0x00); // ora #$00 1277 | CHECKFLAG(FLAG_SIGN, 0); 1278 | CHECKFLAG(FLAG_ZERO, 1); 1279 | CHECK(a, 0x00); 1280 | exec_instruction(0x09, 0x01, 0x00); // ora #$01 1281 | CHECKFLAG(FLAG_SIGN, 0); 1282 | CHECKFLAG(FLAG_ZERO, 0); 1283 | CHECK(a, 0x01); 1284 | exec_instruction(0x09, 0x02, 0x00); // ora #$02 1285 | CHECKFLAG(FLAG_SIGN, 0); 1286 | CHECKFLAG(FLAG_ZERO, 0); 1287 | CHECK(a, 0x03); 1288 | exec_instruction(0x09, 0x82, 0x00); // ora #$82 1289 | CHECKFLAG(FLAG_SIGN, 1); 1290 | CHECKFLAG(FLAG_ZERO, 0); 1291 | CHECK(a, 0x83); 1292 | return 0; 1293 | } 1294 | 1295 | int rol_opcode() { 1296 | 1297 | pc = 0x200; 1298 | a = 0x20; 1299 | status = 0x00; // Make sure Carry's clear 1300 | 1301 | exec_instruction(0x2a, 0x00, 0x00); // rol 1302 | CHECKFLAG(FLAG_CARRY, 0); 1303 | CHECKFLAG(FLAG_SIGN, 0); 1304 | CHECK(a, 0x40); 1305 | 1306 | exec_instruction(0x2a, 0x00, 0x00); // rol 1307 | CHECKFLAG(FLAG_CARRY, 0); 1308 | CHECKFLAG(FLAG_SIGN, 1); 1309 | CHECK(a, 0x80); 1310 | 1311 | exec_instruction(0x2a, 0x00, 0x00); // rol 1312 | CHECKFLAG(FLAG_CARRY, 1); 1313 | CHECKFLAG(FLAG_SIGN, 0); 1314 | CHECK(a, 0x00); 1315 | 1316 | exec_instruction(0x2a, 0x00, 0x00); // rol 1317 | CHECKFLAG(FLAG_CARRY, 0); 1318 | CHECKFLAG(FLAG_SIGN, 0); 1319 | CHECK(a, 0x01); 1320 | return 0; 1321 | } 1322 | 1323 | int rti_opcode() { 1324 | 1325 | pc = 0x200; 1326 | a = 0x20; 1327 | status = 0x00; 1328 | reset6502(); 1329 | 1330 | CHECK(sp, 0xfd); 1331 | write6502(0x1fe, 0x01); 1332 | write6502(0x1ff, 0x02); 1333 | write6502(0x100, 0x03); 1334 | 1335 | exec_instruction(0x40, 0x00, 0x00); // rti 1336 | CHECK(sp, 0x00); 1337 | CHECK(pc, 0x0302); 1338 | CHECKFLAG(FLAG_CARRY, 1); 1339 | CHECKFLAG(FLAG_SIGN, 0); 1340 | CHECKFLAG(FLAG_ZERO, 0); 1341 | CHECKFLAG(FLAG_INTERRUPT, 0); 1342 | CHECKFLAG(FLAG_DECIMAL, 0); 1343 | CHECKFLAG(FLAG_OVERFLOW, 0); 1344 | 1345 | return 0; 1346 | } 1347 | 1348 | int sax_opcode() { 1349 | 1350 | pc = 0x200; 1351 | a = 0x03; 1352 | x = 0x06; 1353 | status = 0x00; 1354 | uint8 address = 0xff; 1355 | 1356 | write6502(address, 0x03); 1357 | 1358 | exec_instruction(0x87, address, 0x00); // sax address 1359 | CHECK(pc, 0x0202); 1360 | CHECKMEM(address, 0x02); 1361 | 1362 | return 0; 1363 | } 1364 | 1365 | int sta_opcode() { 1366 | 1367 | pc = 0x200; 1368 | a = 0x20; 1369 | status = 0x00; 1370 | uint8 address = 0xff; 1371 | 1372 | write6502(address, 0x03); 1373 | 1374 | exec_instruction(0x85, address, 0x00); // sta address 1375 | CHECK(pc, 0x0202); 1376 | CHECKMEM(address, 0x20); 1377 | 1378 | return 0; 1379 | } 1380 | 1381 | int stx_opcode() { 1382 | 1383 | pc = 0x200; 1384 | x = 0x80; 1385 | status = 0x00; 1386 | uint8 address = 0xff; 1387 | 1388 | write6502(address, 0x03); 1389 | 1390 | exec_instruction(0x86, address, 0x00); // sta address 1391 | CHECK(pc, 0x0202); 1392 | CHECKMEM(address, 0x80); 1393 | 1394 | return 0; 1395 | } 1396 | 1397 | int sty_opcode() { 1398 | 1399 | pc = 0x200; 1400 | y = 0x01; 1401 | status = 0x00; 1402 | uint8 address = 0xff; 1403 | 1404 | write6502(0xff, 0x03); 1405 | 1406 | exec_instruction(0x84, address, 0x00); // sta address 1407 | CHECK(pc, 0x0202); 1408 | CHECKMEM(address, 0x01); 1409 | 1410 | return 0; 1411 | } 1412 | 1413 | int stz_opcode() { 1414 | 1415 | pc = 0x200; 1416 | status = 0x00; 1417 | uint8 address = 0xff; 1418 | 1419 | write6502(0xff, 0x03); 1420 | 1421 | exec_instruction(0x64, address, 0x00); // stz address 1422 | CHECK(pc, 0x0202); 1423 | CHECKMEM(address, 0); 1424 | 1425 | return 0; 1426 | } 1427 | 1428 | int sre_opcode() { 1429 | 1430 | 1431 | a = 0x3; 1432 | pc = 0x200; 1433 | write6502(0x01, 0x02); 1434 | 1435 | exec_instruction(0x47, 0x01, 0x00); // LSE $01 1436 | CHECKMEM(0x01, 0x01); 1437 | 1438 | CHECK(pc, 0x0202); 1439 | CHECK(ea, 0x0001); 1440 | CHECK(a, 0x02); 1441 | return 0; 1442 | } 1443 | 1444 | int lax_opcode() { 1445 | 1446 | pc = 0x200; 1447 | 1448 | write6502(0xab, 0x00); 1449 | exec_instruction(0xa7, 0xab, 0x00); // lax $ab 1450 | CHECKFLAG(FLAG_ZERO, 1); 1451 | CHECKFLAG(FLAG_SIGN, 0); 1452 | 1453 | write6502(0xab, 0x7b); 1454 | exec_instruction(0xa7, 0xab, 0x00); // lax $ab 1455 | CHECKFLAG(FLAG_ZERO, 0); 1456 | CHECKFLAG(FLAG_SIGN, 0); 1457 | 1458 | write6502(0xab, 0x8a); 1459 | exec_instruction(0xa7, 0xab, 0x00); // lax $ab 1460 | CHECKFLAG(FLAG_ZERO, 0); 1461 | CHECKFLAG(FLAG_SIGN, 1); 1462 | 1463 | return 0; 1464 | } 1465 | 1466 | /* 1467 | See this document: 1468 | http://www.zimmers.net/anonftp/pub/cbm/documents/chipdata/6502-NMOS.extra.opcodes 1469 | for information about how illegal opcodes work 1470 | */ 1471 | 1472 | int cmos_jmp_indirect() { 1473 | 1474 | pc = 0x200; 1475 | write6502(0x8000, 0x01); 1476 | write6502(0x80fe, 0x02); 1477 | write6502(0x80ff, 0x03); 1478 | write6502(0x8100, 0x04); 1479 | 1480 | clockticks6502 = 0; 1481 | exec_instruction(0x6c, 0xff, 0x80); 1482 | CHECK(pc, 0x0403); 1483 | CHECK(clockticks6502, 6); 1484 | 1485 | return 0; 1486 | } 1487 | 1488 | int cmos_jmp_absxi() { 1489 | 1490 | pc = 0x0200; 1491 | x = 0xff; 1492 | write6502(0x1456, 0xcd); 1493 | write6502(0x1457, 0xab); 1494 | exec_instruction(0x7c, 0x57, 0x13); 1495 | CHECK(pc, 0xabcd); 1496 | CHECK(clockticks6502, 6); 1497 | return 0; 1498 | } 1499 | 1500 | int pushme_pullyou() { 1501 | 1502 | reset6502(); 1503 | pc = 0x0200; 1504 | x = 0xff; 1505 | y = 0x01; 1506 | a = 0x00; 1507 | status = 0x00; 1508 | exec_instruction(0xda, 0x0, 0x0); // phx 1509 | CHECK(sp, 0xfc); 1510 | CHECKMEM(0x1fd, 0xff); 1511 | exec_instruction(0x5a, 0x0, 0x0); // phy 1512 | CHECK(sp, 0xfb); 1513 | CHECKMEM(0x1fc, 0x01); 1514 | exec_instruction(0x48, 0x0, 0x0); // pha 1515 | CHECK(sp, 0xfa); 1516 | CHECKMEM(0x1fb, 0x00); 1517 | exec_instruction(0x7a, 0x0, 0x0); // ply 1518 | CHECK(sp, 0xfb); 1519 | CHECK(y, 0x00); 1520 | exec_instruction(0x28, 0x0, 0x0); // plp 1521 | CHECK(sp, 0xfc); 1522 | CHECKFLAG(FLAG_CARRY, 1); 1523 | CHECKFLAG(FLAG_ZERO, 0); 1524 | CHECKFLAG(FLAG_INTERRUPT, 0); 1525 | CHECKFLAG(FLAG_DECIMAL, 0); 1526 | CHECKFLAG(FLAG_SIGN, 0); 1527 | CHECKFLAG(FLAG_OVERFLOW, 0); 1528 | exec_instruction(0x68, 0x0, 0x0); // pla 1529 | CHECK(sp, 0xfd); 1530 | CHECK(a, 0xff); 1531 | exec_instruction(0x08, 0x0, 0x0); // php 1532 | CHECK(sp, 0xfc); 1533 | exec_instruction(0xfa, 0x0, 0x0); // plx 1534 | CHECK(sp, 0xfd); 1535 | CHECK(x, (FLAG_SIGN | FLAG_CARRY | FLAG_CONSTANT | FLAG_BREAK)); 1536 | 1537 | return 0; 1538 | } 1539 | 1540 | typedef struct { 1541 | char *testname; 1542 | int (*fp)(); 1543 | } test_t; 1544 | 1545 | test_t normal_tests[] = {{"interrupts", &interrupt}, 1546 | {"zero page addressing", &test_zp}, 1547 | {"indexed zero page addressing", &test_zpx}, 1548 | {"absolute addressing", &absolute}, 1549 | {"absolute,x addressing", &absolute_x}, 1550 | {"absolute,y addressing", &absolute_y}, 1551 | {"indirect,y addressing", &indirect_y}, 1552 | {"indirect,x addressing", &indirect_x}, 1553 | {"decimal mode", decimal_mode}, 1554 | {"flags set & reset", flags}, 1555 | {"binary mode", binary_mode}, 1556 | {"push & pull", &pushpull}, 1557 | {"rotations", &rotations}, 1558 | {"branches", &branches}, 1559 | {"comparisons", &comparisons}, 1560 | {"increments and decrements", &incdec}, 1561 | {"loads", &loads}, 1562 | {"transfers", &transfers}, 1563 | {"and", &and_opcode}, 1564 | {"asl", &asl_opcode}, 1565 | {"bit", &bit_opcode}, 1566 | {"brk", &brk_opcode}, 1567 | {"eor", &eor_opcode}, 1568 | {"jsr & rts", &jsr_opcode}, 1569 | {"nop", &nop_opcode}, 1570 | {"ora", &ora_opcode}, 1571 | {"rol", &rol_opcode}, 1572 | {"rti", &rti_opcode}, 1573 | {"sta", &sta_opcode}, 1574 | {"stx", &stx_opcode}, 1575 | {"sty", &sty_opcode}, 1576 | {NULL, NULL}, 1577 | }; 1578 | 1579 | test_t nmos_tests[] = {{"indirect addressing", &indirect}, 1580 | {"rra", &rra_opcode}, 1581 | {"rla", &rla_opcode}, 1582 | {"sre", &sre_opcode}, 1583 | {"sax", &sax_opcode}, 1584 | {"lax", &lax_opcode}, 1585 | {NULL, NULL}}; 1586 | 1587 | test_t cmos_tests[] = {{"CMOS jmp indirect", &cmos_jmp_indirect}, 1588 | {"Immediate BIT", &bit_imm_opcode}, 1589 | {"(absolute,x)", &cmos_jmp_absxi}, 1590 | {"(zp) addressing", &test_zpi}, 1591 | {"pushes and pulls", &pushme_pullyou}, 1592 | {"stz", &stz_opcode}, 1593 | {NULL, NULL}}; 1594 | 1595 | int run_tests(test_t tests[]) { 1596 | int i; 1597 | for (i = 0; tests[i].fp != NULL 1598 | && tests[i].testname != NULL; i++ 1599 | ) 1600 | { 1601 | 1602 | if(tests[i].testname == NULL)break; 1603 | if(tests[i].fp){ 1604 | if (tests[i].fp()) { 1605 | printf("\033[0;31m%s failed\033[0m\n", tests[i].testname); 1606 | exit(1); 1607 | } 1608 | printf("\033[0;33m%s okay\033[0m\n", tests[i].testname); 1609 | } 1610 | } 1611 | printf("\r\n\r\n"); 1612 | return 0; 1613 | } 1614 | 1615 | int main(int argc, char **argv) { 1616 | (void)argc; 1617 | (void)argv; 1618 | run_tests(normal_tests); 1619 | run_tests(nmos_tests); 1620 | /* 1621 | if(argc > 1) 1622 | if (!strcmp(argv[1], "cmos")) 1623 | run_tests(cmos_tests); 1624 | */ 1625 | 1626 | return EXIT_SUCCESS; 1627 | } 1628 | -------------------------------------------------------------------------------- /update_fake6502.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | IFS=$'\n' FILES=$(find .. -name "fake6502.h") 3 | ME="../fake6502_cchads/fake6502.h" 4 | for filename in $FILES 5 | do 6 | if [[ "$filename" != "$ME" ]]; then 7 | echo " Copying to $filename" 8 | cp "$ME" "$filename" 9 | else 10 | echo "Found ourselves!" 11 | fi 12 | done 13 | -------------------------------------------------------------------------------- /update_fake65c02.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | IFS=$'\n' FILES=$(find .. -name "fake65c02.h") 3 | ME="../fake6502_cchads/fake65c02.h" 4 | for filename in $FILES 5 | do 6 | if [[ "$filename" != "$ME" ]]; then 7 | echo " Copying to $filename" 8 | cp "$ME" "$filename" 9 | else 10 | echo "Found ourselves!" 11 | fi 12 | done 13 | --------------------------------------------------------------------------------