diff --git a/puce65c02.cpp b/puce65c02.cpp new file mode 100644 index 0000000..9a0874b --- /dev/null +++ b/puce65c02.cpp @@ -0,0 +1,2813 @@ +/* + puce65c02, a WDC 65c02 cpu emulator, based on puce6502 by the same author + Last modified 1st of July 2021 + + Copyright (c) 2021 Arthur Ferreira (arthur.ferreira2@gmail.com) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions : + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include "puce65c02.h" + + +#if __TESTS__ + + // functionnal and timing tests - see also bottom of file + + // Theses tests require 65C02_extended_opcodes_test.bin and 6502_functional_test.bin + // from https://github.com/Klaus2m5/6502_65C02_functional_tests + // and timingtest-1.bin from http://forum.6502.org/viewtopic.php?f=8&t=3340 + + // compile and run : + + // g++ -Wall -O3 -D __TESTS__ puce65c02.cpp -o puce65c02 && ./puce65c02 + // or, slower but with detailled info : + // g++ -Wall -O3 -D __TESTS__ -D VERBOSE puce65c02.cpp -o puce65c02 && ./puce65c02 + // or, if you just want to check the cycle accuracy : + // g++ -Wall -O3 -D __TESTS__ -D TIMING puce65c02.cpp -o puce65c02 && ./puce65c02 + + uint8_t RAM[65536]; + inline uint8_t readMem(uint16_t address) { return RAM[address]; } + inline void writeMem(uint16_t address, uint8_t value8) { RAM[address] = value8; } + +#else + // user is supposed to provide these two functions + extern uint8_t readMem(uint16_t address); + extern void writeMem(uint16_t address, uint8_t value8); + +#endif + + +void puce65c02::RST() { + PC = readMem(0xFFFC) | (readMem(0xFFFD) << 8); + SP = 0xFF; + P.I = 1; + P.U = 1; + state = run; + ticks += 7; +} + + +void puce65c02::IRQ() { + state = run; // always ?? + if (!P.I) return; // consume clock cycles ? + PC++; + writeMem(0x100 + SP, (PC >> 8) & 0xFF); + SP--; + writeMem(0x100 + SP, PC & 0xFF); + SP--; + writeMem(0x100 + SP, P.byte & ~BREAK); + SP--; + PC = readMem(0xFFFE) | (readMem(0xFFFF) << 8); + ticks += 7; +} + + +void puce65c02::NMI() { + state = run; + P.I = 1; // ??? + PC++; + writeMem(0x100 + SP, (PC >> 8) & 0xFF); + SP--; + writeMem(0x100 + SP, PC & 0xFF); + SP--; + writeMem(0x100 + SP, P.byte & ~BREAK); + SP--; + PC = readMem(0xFFFA) | (readMem(0xFFFB) << 8); + ticks += 7; +} + + +/* + Addressing modes abreviations used in the comments below : + + IMP : Implied or Implicit : DEX, RTS, CLC - 61 instructions + ACC : Accumulator : ASL A, ROR A, DEC A - 6 instructions + IMM : Immediate : LDA #$A5 - 19 instructions + ZPG : Zero Page : LDA $81 - 41 instructions + ZPX : Zero Page Indexed with X : LDA $55,X - 21 instructions + ZPY : Zero Page Indexed with Y : LDX $55,Y - 2 instructions + REL : Relative : BEQ LABEL12 - 9 instructions + ABS : Absolute : LDA $2000 - 29 instructions + ABX : Absolute Indexed with X : LDA $2000,X - 17 instructions + ABY : Absolute Indexed with Y : LDA $2000,Y - 9 instructions + IND : Indirect : JMP ($1020) - 1 instruction + IZP : Indirect Zero Page : LDA ($55) (65c02 only) - 8 instructions + IZX : ZP Indexed Indirect with X (Preindexed) : LDA ($55,X) - 8 instructions + IZY : ZP Indirect Indexed with Y (Postindexed) : LDA ($55),Y - 8 instructions + IAX : Absolute Indexed Indirect : JMP ($2000,X) (65c02 only) - 1 instruction + ZPR : Zero Page Relative : BBS0 $23, LABEL (65c02 only) - 16 instructions +*/ + + +uint16_t puce65c02::exec(unsigned long long int cycleCount) { + + cycleCount += ticks; // cycleCount becomes the targeted ticks value8 + while (ticks < cycleCount) { + if (state == run || state == step) { + + uint8_t value8; + uint16_t value16; + uint16_t address; + + switch(readMem(PC++)) { // fetch instruction and increment Program Counter + + case 0x00 : // IMP BRK + PC++; + writeMem(0x100 + SP, ((PC) >> 8) & 0xFF); + SP--; + writeMem(0x100 + SP, PC & 0xFF); + SP--; + writeMem(0x100 + SP, P.byte | BREAK); + SP--; + P.I = 1; + P.D = 0; + PC = readMem(0xFFFE) | (readMem(0xFFFF) << 8); + ticks += 7; + break; + + case 0x01 : // IZX ORA + value8 = readMem(PC) + X; + PC++; + address = readMem(value8); + value8++; + address |= readMem(value8) << 8; + A |= readMem(address); + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 6; + break; + + case 0x02 : // IMM NOP + PC++; + ticks += 2; + break; + + case 0x03 : // IMP NOP + ticks++; + break; + + case 0x04 : // ZPG TSB + address = readMem(PC); + PC++; + value8 = readMem(address); + P.Z = (value8 & A) == 0; + writeMem(address, value8 | A); + ticks += 5; + break; + + case 0x05 : // ZPG ORA + A |= readMem(readMem(PC)); + PC++; + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 3; + break; + + case 0x06 : // ZPG ASL + address = readMem(PC); + PC++; + value16 = readMem(address) << 1; + P.C = value16 > 0xFF; + value16 &= 0xFF; + writeMem(address, value16); + P.Z = value16 == 0; + P.S = value16 > 0x7F; + ticks += 5; + break; + + case 0x07 : // ZPG RMB + address = readMem(PC); + PC++; + writeMem(address, readMem(address) & ~1); + ticks += 5; + break; + + case 0x08 : // IMP PHP + writeMem(0x100 + SP, P.byte | BREAK); + SP--; + ticks += 3; + break; + + case 0x09 : // IMM ORA + A |= readMem(PC); + PC++; + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 2; + break; + + case 0x0A : // ACC ASL + value16 = A << 1; + A = value16 & 0xFF; + P.C = value16 > 0xFF; + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 2; + break; + + case 0x0B : // IMP NOP + ticks++; + break; + + case 0x0C : // ABS TSB + address = readMem(PC); + PC++; + address |= readMem(PC) << 8; + PC++; + value8 = readMem(address); + P.Z = (value8 & A) == 0; + writeMem(address, value8 | A); + ticks += 6; + break; + + case 0x0D : // ABS ORA + address = readMem(PC); + PC++; + address |= readMem(PC) << 8; + PC++; + A |= readMem(address); + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 4; + break; + + case 0x0E : // ABS ASL + address = readMem(PC); + PC++; + address |= readMem(PC) << 8; + PC++; + value16 = readMem(address) << 1; + P.C = value16 > 0xFF; + value16 &= 0xFF; + writeMem(address, value16); + P.Z = value16 == 0; + P.S = value16 > 0x7F; + ticks += 6; + break; + + case 0x0F : // ZPR BBR + value8 = readMem(readMem(PC)); + PC++; + address = readMem(PC); + PC++; + if (address & SIGN) + address |= 0xFF00; // jump backward + if (!(value8 & 1)) + PC += address; + ticks += 5; + break; + + case 0x10 : // REL BPL + address = readMem(PC); + PC++; + if (!P.S) { // jump taken + ticks++; + if (address & SIGN) + address |= 0xFF00; // jump backward + if (((PC & 0xFF) + address) & 0xFF00) // page crossing + ticks++; + PC += address; + } + ticks += 2; + break; + + case 0x11 : // IZY ORA + value8 = readMem(PC); + PC++; + address = readMem(value8); + value8++; + address |= readMem(value8) << 8; + ticks += (((address & 0xFF) + Y) & 0xFF00) ? 6 : 5; // page crossing + address += Y; + A |= readMem(address); + P.Z = A == 0; + P.S = A > 0x7F; + break; + + case 0x12 : // IZP ORA + value8 = readMem(PC); + PC++; + address = readMem(value8); + value8++; + address |= readMem(value8) << 8; + A |= readMem(address); + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 5; + break; + + case 0x13 : // IMP NOP + ticks++; + break; + + case 0x14 : // ZPG TRB + address = readMem(PC); + PC++; + value8 = readMem(address); + writeMem(address, value8 & ~A); + P.Z = (value8 & A) == 0; + ticks += 5; + break; + + case 0x15 : // ZPX ORA + A |= readMem(readMem(PC) + X); + PC++; + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 4; + break; + + case 0x16 : // ZPX ASL + address = readMem(PC) + X; + PC++; + value16 = readMem(address) << 1; + writeMem(address, value16 & 0xFF); + P.C = value16 > 0xFF; + P.Z = value16 == 0; + P.S = (value16 & 0xFF) > 0x7F; + ticks += 6; + break; + + case 0x17 : // ZPG RMB + address = readMem(PC); + PC++; + writeMem(address, readMem(address) & ~2); + ticks += 5; + break; + + case 0x18 : // IMP CLC + P.C = 0; + ticks += 2; + break; + + case 0x19 : // ABY ORA + address = readMem(PC); + PC++; + ticks += ((address + Y) & 0xFF00) ? 5 : 4; // page crossing + address |= readMem(PC) << 8; + PC++; + address += Y; + A |= readMem(address); + P.Z = A == 0; + P.S = A > 0x7F; + break; + + case 0x1A : // ACC INC + A++; + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 2; + break; + + case 0x1B : // IMP NOP + ticks++; + break; + + case 0x1C : // ABS TRB + address = readMem(PC); + PC++; + address |= readMem(PC) << 8; + PC++; + value8 = readMem(address); + P.Z = (value8 & A) == 0; + writeMem(address, value8 & ~A); + ticks += 6; + break; + + case 0x1D : // ABX ORA + address = readMem(PC); + PC++; + ticks += ((address + X) & 0xFF00) ? 5 : 4; // page crossing + address |= readMem(PC) << 8; + PC++; + address += X; + A |= readMem(address); + P.Z = A == 0; + P.S = A > 0x7F; + break; + + case 0x1E : // ABX ASL + address = readMem(PC); + PC++; + ticks += address + X > 0xFF ? 7 : 6; + address |= readMem(PC) << 8; + PC++; + address += X; + value16 = readMem(address) << 1; + P.C = value16 > 0xFF; + value16 &= 0xFF; + writeMem(address, value16); + P.Z = value16 == 0; + P.S = value16 > 0x7F; + break; + + case 0x1F : // ZPR BBR + value8 = readMem(readMem(PC)); + PC++; + address = readMem(PC); + PC++; + if (address & SIGN) + address |= 0xFF00; // jump backward + if (!(value8 & 2)) + PC += address; + ticks += 5; + break; + + case 0x20 : // ABS JSR + address = readMem(PC); + PC++; + address |= readMem(PC) << 8; + writeMem(0x100 + SP, (PC >> 8) & 0xFF); + SP--; + writeMem(0x100 + SP, PC & 0xFF); + SP--; + PC = address; + ticks += 6; + break; + + case 0x21 : // IZX AND + value8 = readMem(PC) + X; + PC++; + address = readMem(value8); + value8++; + address |= readMem(value8) << 8; + A &= readMem(address); + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 6; + break; + + case 0x22 : // IMM NOP + PC++; + ticks += 2; + break; + + case 0x23 : // IMP NOP + ticks++; + break; + + case 0x24 : // ZPG BIT + address = readMem(PC); + PC++; + value8 = readMem(address); + P.Z = (A & value8) == 0; + P.byte = (P.byte & 0x3F) | (value8 & 0xC0); + ticks += 3; + break; + + case 0x25 : // ZPG AND + A &= readMem(readMem(PC)); + PC++; + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 3; + break; + + case 0x26 : // ZPG ROL + address = readMem(PC); + PC++; + value16 = (readMem(address) << 1) | P.C; + P.C = (value16 & 0x100) != 0; + value16 &= 0xFF; + writeMem(address, value16); + P.Z = value16 == 0; + P.S = value16 > 0x7F; + ticks += 5; + break; + + case 0x27 : // ZPG RMB + address = readMem(PC); + PC++; + writeMem(address, readMem(address) & ~4); + ticks += 5; + break; + + case 0x28 : // IMP PLP + SP++; + P.byte = readMem(0x100 + SP) | UNDEF; + ticks += 4; + break; + + case 0x29 : // IMM AND + A &= readMem(PC); + PC++; + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 2; + break; + + case 0x2A : // ACC ROL + value16 = (A << 1) | P.C; + P.C = (value16 & 0x100) != 0; + A = value16 & 0xFF; + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 2; + break; + + case 0x2B : // IMP NOP + ticks++; + break; + + case 0x2C : // ABS BIT + address = readMem(PC); + PC++; + address |= readMem(PC) << 8; + PC++; + value8 = readMem(address); + P.Z = (A & value8) == 0; + P.byte = (P.byte & 0x3F) | (value8 & 0xC0); + ticks += 4; + break; + + case 0x2D : // ABS AND + address = readMem(PC); + PC++; + address |= readMem(PC) << 8; + PC++; + A &= readMem(address); + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 4; + break; + + case 0x2E : // ABS ROL + address = readMem(PC); + PC++; + address |= readMem(PC) << 8; + PC++; + value16 = (readMem(address) << 1) | P.C; + P.C = (value16 & 0x100) != 0; + value16 &= 0xFF; + writeMem(address, value16); + P.Z = value16 == 0; + P.S = value16 > 0x7F; + ticks += 6; + break; + + case 0x2F : // ZPR BBR + value8 = readMem(readMem(PC)); + PC++; + address = readMem(PC); + PC++; + if (address & SIGN) + address |= 0xFF00; // jump backward + if (!(value8 & 4)) + PC += address; + ticks += 5; + break; + + case 0x30 : // REL BMI + address = readMem(PC); + PC++; + if (P.S) { // branch taken + ticks++; + if (address & SIGN) + address |= 0xFF00; // jump backward + if (((PC & 0xFF) + address) & 0xFF00) // page crossing + ticks++; + PC += address; + } + ticks += 2; + break; + + case 0x31 : // IZY AND + value8 = readMem(PC); + PC++; + address = readMem(value8); + value8++; + address |= readMem(value8) << 8; + ticks += (((address & 0xFF) + Y) & 0xFF00) ? 6 : 5; // page crossing + address += Y; + A &= readMem(address); + P.Z = A == 0; + P.S = A > 0x7F; + break; + + case 0x32 : // IZP AND + value8 = readMem(PC); + PC++; + address = readMem(value8); + value8++; + address |= readMem(value8) << 8; + A &= readMem(address); + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 5; + break; + + case 0x33 : // IMP NOP + ticks++; + break; + + case 0x34 : // ZPX BIT + address = (readMem(PC) + X) & 0xFF; + PC++; + value8 = readMem(address); + P.Z = (A & value8) == 0; + P.byte = (P.byte & 0x3F) | (value8 & 0xC0); + ticks += 4; + break; + + case 0x35 : // ZPX AND + address = (readMem(PC) + X) & 0xFF; + PC++; + A &= readMem(address); + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 4; + break; + + case 0x36 : // ZPX ROL + address = (readMem(PC) + X) & 0xFF; + PC++; + value16 = (readMem(address) << 1) | P.C; + P.C = value16 > 0xFF; + value16 &= 0xFF; + writeMem(address, value16); + P.Z = value16 == 0; + P.S = value16 > 0x7F; + ticks += 6; + break; + + case 0x37 : // ZPG RMB + address = readMem(PC); + PC++; + writeMem(address, readMem(address) & ~8); + ticks += 5; + break; + + case 0x38 : // IMP SEC + P.C = 1; + ticks += 2; + break; + + case 0x39 : // ABY AND + address = readMem(PC); + PC++; + ticks += ((address + Y) & 0xFF00) ? 5 : 4; // page crossing + address |= readMem(PC) << 8; + PC++; + address += Y; + A &= readMem(address); + P.Z = A == 0; + P.S = A > 0x7F; + break; + + case 0x3A : // ACC DEC + --A; + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 2; + break; + + case 0x3B : // IMP NOP + ticks++; + break; + + case 0x3C : // ABX BIT + ticks += readMem(PC) + X > 0xFF ? 5 : 4; + address = readMem(PC); + PC++; + address |= (readMem(PC) << 8) + X; + PC++; + value8 = readMem(address); + P.Z = (A & value8) == 0; + P.byte = (P.byte & 0x3F) | (value8 & 0xC0); + break; + + case 0x3D : // ABX AND + address = readMem(PC); + PC++; + ticks += ((address + X) & 0xFF00) ? 5 : 4; // page crossing + address |= readMem(PC) << 8; + PC++; + address += X; + A &= readMem(address); + P.Z = A == 0; + P.S = A > 0x7F; + break; + + case 0x3E : // ABX ROL + address = readMem(PC); + PC++; + ticks += address + X > 0xFF ? 7 : 6; + address |= readMem(PC) << 8; + PC++; + address += X; + value16 = (readMem(address) << 1) | P.C; + P.C = value16 > 0xFF; + value16 &= 0xFF; + writeMem(address, value16); + P.Z = value16 == 0; + P.S = value16 > 0x7F; + break; + + case 0x3F : // ZPR BBR + value8 = readMem(readMem(PC)); + PC++; + address = readMem(PC); + PC++; + if (address & SIGN) + address |= 0xFF00; // jump backward + if (!(value8 & 8)) + PC += address; + ticks += 5; + break; + + case 0x40 : // IMP RTI + SP++; + P.byte = readMem(0x100 + SP); + SP++; + PC = readMem(0x100 + SP); + SP++; + PC |= readMem(0x100 + SP) << 8; + ticks += 6; + break; + + case 0x41 : // IZX EOR + value8 = readMem(PC) + X; + PC++; + address = readMem(value8); + value8++; + address |= readMem(value8) << 8; + A ^= readMem(address); + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 6; + break; + + case 0x42 : // IMM NOP + PC++; + ticks += 2; + break; + + case 0x43 : // IMP NOP + ticks++; + break; + + case 0x44 : // ZPG NOP + PC++; + ticks += 3; + break; + + case 0x45 : // ZPG EOR + address = readMem(PC); + PC++; + A ^= readMem(address); + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 3; + break; + + case 0x46 : // ZPG LSR + address = readMem(PC); + PC++; + value8 = readMem(address); + P.C = (value8 & 1) != 0; + value8 = value8 >> 1; + writeMem(address, value8); + P.Z = value8 == 0; + P.S = value8 > 0x7F; + ticks += 5; + break; + + case 0x47 : // ZPG RMB + address = readMem(PC); + PC++; + writeMem(address, readMem(address) & ~16); + ticks += 5; + break; + + case 0x48 : // IMP PHA + writeMem(0x100 + SP, A); + SP--; + ticks += 3; + break; + + case 0x49 : // IMM EOR + A ^= readMem(PC); + PC++; + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 2; + break; + + case 0x4A : // ACC LSR + P.C = (A & 1) != 0; + A = A >> 1; + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 2; + break; + + case 0x4B : // IMP NOP + ticks++; + break; + + case 0x4C : // ABS JMP + PC = readMem(PC) | (readMem(PC + 1) << 8); + ticks += 3; + break; + + case 0x4D : // ABS EOR + address = readMem(PC); + PC++; + address |= readMem(PC) << 8; + PC++; + A ^= readMem(address); + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 4; + break; + + case 0x4E : // ABS LSR + address = readMem(PC); + PC++; + address |= readMem(PC) << 8; + PC++; + value8 = readMem(address); + P.C = (value8 & 1) != 0; + value8 = value8 >> 1; + writeMem(address, value8); + P.Z = value8 == 0; + P.S = value8 > 0x7F; + ticks += 6; + break; + + case 0x4F : // ZPR BBR + value8 = readMem(readMem(PC)); + PC++; + address = readMem(PC); + PC++; + if (address & SIGN) + address |= 0xFF00; // jump backward + if (!(value8 & 16)) + PC += address; + ticks += 5; + break; + + case 0x50 : // REL BVC + address = readMem(PC); + PC++; + if (!P.V) { // branch taken + ticks++; + if (address & SIGN) + address |= 0xFF00; // jump backward + if (((PC & 0xFF) + address) & 0xFF00) // page crossing + ticks++; + PC += address; + } + ticks += 2; + break; + + case 0x51 : // IZY EOR + value8 = readMem(PC); + PC++; + address = readMem(value8); + value8++; + address |= readMem(value8) << 8; + ticks += (((address & 0xFF) + Y) & 0xFF00) ? 6 : 5; // page crossing + A ^= readMem(address + Y); + P.Z = A == 0; + P.S = A > 0x7F; + break; + + case 0x52 : // IZP EOR + value8 = readMem(PC); + PC++; + address = readMem(value8); + value8++; + address |= readMem(value8) << 8; + A ^= readMem(address); + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 5; + break; + + case 0x53 : // IMP NOP + ticks++; + break; + + case 0x54 : // ZPX NOP + PC++; + ticks += 4; + break; + + case 0x55 : // ZPX EOR + address = (readMem(PC) + X) & 0xFF; + PC++; + A ^= readMem(address); + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 4; + break; + + case 0x56 : // ZPX LSR + address = (readMem(PC) + X) & 0xFF; + PC++; + value8 = readMem(address); + P.C = (value8 & 1) != 0; + value8 = value8 >> 1; + writeMem(address, value8); + P.Z = value8 == 0; + P.S = value8 > 0x7F; + ticks += 6; + break; + + case 0x57 : // ZPG RMB + address = readMem(PC); + PC++; + writeMem(address, readMem(address) & ~32); + ticks += 5; + break; + + case 0x58 : // IMP CLI + P.I = 0; + ticks += 2; + break; + + case 0x59 : // ABY EOR + address = readMem(PC); + PC++; + ticks += ((address + Y) & 0xFF00) ? 5 : 4; // page crossing + address |= readMem(PC) << 8; + PC++; + address += Y; + A ^= readMem(address); + P.Z = A == 0; + P.S = A > 0x7F; + break; + + case 0x5A : // IMP PHY + writeMem(0x100 + SP, Y); + SP--; + ticks += 3; + break; + + case 0x5B : // IMP NOP + ticks++; + break; + + case 0x5C : // ABS NOP + PC += 2; + ticks += 8; + break; + + case 0x5D : // ABX EOR + address = readMem(PC); + PC++; + ticks += ((address + X) & 0xFF00) ? 5 : 4; // page crossing + address |= readMem(PC) << 8; + PC++; + address += X; + A ^= readMem(address); + P.Z = A == 0; + P.S = A > 0x7F; + break; + + case 0x5E : // ABX LSR + address = readMem(PC); + PC++; + ticks += address + X > 0xFF ? 7 : 6; + address |= readMem(PC) << 8; + PC++; + address += X; + value8 = readMem(address); + P.C = (value8 & 1) != 0; + value8 = value8 >> 1; + writeMem(address, value8); + P.Z = value8 == 0; + P.S = value8 > 0x7F; + break; + + case 0x5F : // ZPR BBR + value8 = readMem(readMem(PC)); + PC++; + address = readMem(PC); + PC++; + if (address & SIGN) + address |= 0xFF00; // jump backward + if (!(value8 & 32)) + PC += address; + ticks += 5; + break; + + case 0x60 : // IMP RTS + SP++; + PC = readMem(0x100 + SP); + SP++; + PC |= readMem(0x100 + SP) << 8; + PC++; + ticks += 6; + break; + + case 0x61 : // IZX ADC + value8 = readMem(PC) + X; + PC++; + address = readMem(value8); + value8++; + address |= readMem(value8) << 8; + value8 = readMem(address); + value16 = A + value8 + P.C; + P.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0; + if (P.D) + value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3; + P.C = value16 > 0xFF; + A = value16 & 0xFF; + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 6; + break; + + case 0x62 : // IMM NOP + PC++; + ticks += 2; + break; + + case 0x63 : // IMP NOP + ticks++; + break; + + case 0x64 : // ZPG STZ + writeMem(readMem(PC), 0x00); + PC++; + ticks += 3; + break; + + case 0x65 : // ZPG ADC + address = readMem(PC); + PC++; + value8 = readMem(address); + value16 = A + value8 + P.C; + P.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0; + if (P.D) + value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3; + P.C = value16 > 0xFF; + A = value16 & 0xFF; + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 3; + break; + + case 0x66 : // ZPG ROR + address = readMem(PC); + PC++; + value8 = readMem(address); + value16 = (value8 >> 1) | (P.C << 7); + P.C = (value8 & 0x1) != 0; + value16 &= 0xFF; + writeMem(address, value16); + P.Z = value16 == 0; + P.S = value16 > 0x7F; + ticks += 5; + break; + + case 0x67 : // ZPG RMB + address = readMem(PC); + PC++; + writeMem(address, readMem(address) & ~64); + ticks += 5; + break; + + case 0x68 : // IMP PLA + SP++; + A = readMem(0x100 + SP); + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 4; + break; + + case 0x69 : // IMM ADC + value8 = readMem(PC); + PC++; + value16 = A + value8 + P.C; + P.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0; + if (P.D) + value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3; + P.C = value16 > 0xFF; + A = value16 & 0xFF; + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 2; + break; + + case 0x6A : // ACC ROR + value16 = (A >> 1) | (P.C << 7); + P.C = (A & 0x1) != 0; + A = value16 & 0xFF; + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 2; + break; + + case 0x6B : // IMP NOP + ticks++; + break; + + case 0x6C : // IND JMP + address = readMem(PC) | readMem(PC + 1) << 8; + PC = readMem(address) | (readMem(address + 1) << 8); + ticks += 5; + break; + + case 0x6D : // ABS ADC + address = readMem(PC); + PC++; + address |= readMem(PC) << 8; + PC++; + value8 = readMem(address); + value16 = A + value8 + P.C; + P.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0; + if (P.D) + value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3; + P.C = value16 > 0xFF; + A = value16 & 0xFF; + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 4; + break; + + case 0x6E : // ABS ROR + address = readMem(PC); + PC++; + address |= readMem(PC) << 8; + PC++; + value8 = readMem(address); + value16 = (value8 >> 1) | (P.C << 7); + P.C = (value8 & 0x1) != 0; + value16 = value16 & 0xFF; + writeMem(address, value16); + P.Z = value16 == 0; + P.S = value16 > 0x7F; + ticks += 6; + break; + + case 0x6F : // ZPR BBR + value8 = readMem(readMem(PC)); + PC++; + address = readMem(PC); + PC++; + if (address & SIGN) + address |= 0xFF00; // jump backward + if (!(value8 & 64)) + PC += address; + ticks += 5; + break; + + case 0x70 : // REL BVS + address = readMem(PC); + PC++; + if (P.V) { // branch taken + ticks++; + if (((PC & 0xFF) + address) & 0xFF00) // page crossing + ticks++; + if (address & SIGN) + address |= 0xFF00; // jump backward + PC += address; + } + ticks += 2; + break; + + case 0x71 : // IZY ADC + value8 = readMem(PC); + PC++; + address = readMem(value8); + if ((address + Y) & 0xFF00) // page crossing + ticks++; + value8++; + address |= readMem(value8) << 8; + address += Y; + value8 = readMem(address); + value16 = A + value8 + P.C; + P.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0; + if (P.D) + value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3; + P.C = value16 > 0xFF; + A = value16 & 0xFF; + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 5; + break; + + case 0x72 : // IZP ADC + value8 = readMem(PC); + PC++; + address = readMem(value8); + value8++; + address |= readMem(value8) << 8; + value8 = readMem(address); + value16 = A + value8 + P.C; + P.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0; + if (P.D) + value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3; + P.C = value16 > 0xFF; + A = value16 & 0xFF; + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 5; + break; + + case 0x73 : // IMP NOP + ticks++; + break; + + case 0x74 : // ZPX STZ + value8 = readMem(PC) + X; // 8bit -> zp wrap around + PC++; + writeMem(value8, 0x00); + ticks += 4; + break; + + case 0x75 : // ZPX ADC + address = (readMem(PC) + X) & 0xFF; + PC++; + value8 = readMem(address); + value16 = A + value8 + P.C; + P.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0; + if (P.D) + value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3; + P.C = value16 > 0xFF; + A = value16 & 0xFF; + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 4; + break; + + case 0x76 : // ZPX ROR + address = (readMem(PC) + X) & 0xFF; + PC++; + value8 = readMem(address); + value16 = (value8 >> 1) | (P.C << 7); + P.C = (value8 & 0x1) != 0; + value16 = value16 & 0xFF; + writeMem(address, value16); + P.Z = value16 == 0; + P.S = value16 > 0x7F; + ticks += 6; + break; + + case 0x77 : // ZPG RMB + address = readMem(PC); + PC++; + writeMem(address, readMem(address) & ~128); + ticks += 5; + break; + + case 0x78 : // IMP SEI + P.I = 1; + ticks += 2; + break; + + case 0x79 : // ABY ADC + if ((readMem(PC) + Y) & 0xFF00) + ticks++; + address = readMem(PC); + PC++; + address |= readMem(PC) << 8; + PC++; + address += Y; + value8 = readMem(address); + value16 = A + value8 + P.C; + P.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0; + if (P.D) + value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3; + P.C = value16 > 0xFF; + A = value16 & 0xFF; + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 4; + break; + + case 0x7A : // IMP PLY + SP++; + Y = readMem(0x100 + SP); + P.Z = Y == 0; + P.S = Y > 0x7F; + ticks += 4; + break; + + case 0x7B : // IMP NOP + ticks++; + break; + + case 0x7C : // IAX JMP + ticks += ((PC & 0xFF) + X) > 0xFF ? 7 : 6; + address = (readMem((PC + 1) & 0xFFFF) << 8) + readMem(PC) + X; + PC = (readMem(address) | (readMem((address + 1) & 0xFFFF) << 8)); + break; + + case 0x7D : // ABX ADC + if ((readMem(PC) + X) & 0xFF00) + ticks++; + address = readMem(PC); + PC++; + address |= readMem(PC) << 8; + PC++; + address += X; + value8 = readMem(address); + value16 = A + value8 + P.C; + P.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0; + if (P.D) + value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3; + P.C = value16 > 0xFF; + A = value16 & 0xFF; + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 4; + break; + + case 0x7E : // ABX ROR + address = readMem(PC); + PC++; + ticks += address + X > 0xFF ? 7 : 6; + address |= readMem(PC) << 8; + PC++; + address += X; + value8 = readMem(address); + value16 = (value8 >> 1) | (P.C << 7); + P.C = (value8 & 0x1) != 0; + value16 = value16 & 0xFF; + writeMem(address, value16); + P.Z = value16 == 0; + P.S = value16 > 0x7F; + break; + + case 0x7F : // ZPR BBR + value8 = readMem(readMem(PC)); + PC++; + address = readMem(PC); + PC++; + if (address & SIGN) + address |= 0xFF00; // jump backward + if (!(value8 & 128)) + PC += address; + ticks += 5; + break; + + case 0x80 : // REL BRA + address = readMem(PC); + PC++; + if (address & SIGN) + address |= 0xFF00; // jump backward + ticks += ((PC & 0xFF) + address) & 0xFF00 ? 4 : 3; + PC += address; + break; + + case 0x81 : // IZX STA + value8 = readMem(PC) + X; + PC++; + address = readMem(value8); + value8++; + address |= readMem(value8) << 8; + writeMem(address, A); + ticks += 6; + break; + + case 0x82 : // IMM NOP + PC++; + ticks += 2; + break; + + case 0x83 : // IMP NOP + ticks++; + break; + + case 0x84 : // ZPG STY + writeMem(readMem(PC), Y); + PC++; + ticks += 3; + break; + + case 0x85 : // ZPG STA + writeMem(readMem(PC), A); + PC++; + ticks += 3; + break; + + case 0x86 : // ZPG STX + writeMem(readMem(PC), X); + PC++; + ticks += 3; + break; + + case 0x87 : // ZPG SMB + address = readMem(PC); + PC++; + writeMem(address, readMem(address) | 1); + ticks += 5; + break; + + case 0x88 : // IMP DEY + Y--; + P.Z = (Y & 0xFF) == 0; + P.S = (Y & SIGN) != 0; + ticks += 2; + break; + + case 0x89 : // IMM BIT + P.Z = (A & readMem(PC)) == 0; + PC++; + ticks += 2; + break; + + case 0x8A : // IMP TXA + A = X; + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 2; + break; + + case 0x8B : // IMP NOP + ticks++; + break; + + case 0x8C : // ABS STY + address = readMem(PC); + PC++; + address |= readMem(PC) << 8; + PC++; + writeMem(address, Y); + ticks += 4; + break; + + case 0x8D : // ABS STA + address = readMem(PC); + PC++; + address |= readMem(PC) << 8; + PC++; + writeMem(address, A); + ticks += 4; + break; + + case 0x8E : // ABS STX + address = readMem(PC); + PC++; + address |= readMem(PC) << 8; + PC++; + writeMem(address, X); + ticks += 4; + break; + + case 0x8F : // ZPR BBS + value8 = readMem(readMem(PC)); + PC++; + address = readMem(PC); + PC++; + if (address & SIGN) + address |= 0xFF00; // jump backward + if (value8 & 1) + PC += address; + ticks += 5; + break; + + case 0x90 : // REL BCC + address = readMem(PC); + PC++; + if (!P.C) { // branch taken + ticks++; + if (((PC & 0xFF) + address) & 0xFF00) // page crossing + ticks++; + if (address & SIGN) + address |= 0xFF00; // jump backward + PC += address; + } + ticks += 2; + break; + + case 0x91 : // IZY STA + value8 = readMem(PC); + PC++; + address = readMem(value8); + value8++; + address |= readMem(value8) << 8; + address += Y; + writeMem(address, A); + ticks += 6; + break; + + case 0x92 : // IZP STA + value8 = readMem(PC); + PC++; + address = readMem(value8); + value8++; + address |= readMem(value8) << 8; + writeMem(address, A); + ticks += 5; + break; + + case 0x93 : // IMP NOP + ticks++; + break; + + case 0x94 : // ZPX STY + address = (readMem(PC) + X) & 0xFF; + PC++; + writeMem(address, Y); + ticks += 4; + break; + + case 0x95 : // ZPX STA + writeMem((readMem(PC) + X) & 0xFF, A); + PC++; + ticks += 4; + break; + + case 0x96 : // ZPY STX + writeMem((readMem(PC) + Y) & 0xFF, X); + PC++; + ticks += 4; + break; + + case 0x97 : // ZPG SMB + address = readMem(PC); + PC++; + writeMem(address, readMem(address) | 2); + ticks += 5; + break; + + case 0x98 : // IMP TYA + A = Y; + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 2; + break; + + case 0x99 : // ABY STA + address = readMem(PC); + PC++; + address |= readMem(PC) << 8; + PC++; + address += Y; + writeMem(address, A); + ticks += 5; + break; + + case 0x9A : // IMP TXS + SP = X; + ticks += 2; + break; + + case 0x9B : // IMP NOP + ticks++; + break; + + case 0x9C : // ABS STZ + address = readMem(PC); + PC++; + address |= readMem(PC) << 8; + PC++; + writeMem(address, 0x00); + ticks += 4; + break; + + case 0x9D : // ABX STA + address = readMem(PC); + PC++; + address |= readMem(PC) << 8; + PC++; + address += X; + writeMem(address, A); + ticks += 5; + break; + + case 0x9E : // ABX STZ + ticks += readMem(PC) + X > 0xFF ? 6 : 5; + address = readMem(PC); + PC++; + address |= readMem(PC) << 8; + PC++; + address += X; + writeMem(address, 0x00); + break; + + case 0x9F : // ZPR BBS + value8 = readMem(readMem(PC)); + PC++; + address = readMem(PC); + PC++; + if (address & SIGN) + address |= 0xFF00; // jump backward + if (value8 & 2) + PC += address; + ticks += 5; + break; + + case 0xA0 : // IMM LDY + Y = readMem(PC); + PC++; + P.Z = Y == 0; + P.S = Y > 0x7F; + ticks += 2; + break; + + case 0xA1 : // IZX LDA + value8 = readMem(PC) + X; + PC++; + address = readMem(value8); + value8++; + address |= readMem(value8) << 8; + A = readMem(address); + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 6; + break; + + case 0xA2 : // IMM LDX + address = PC; + PC++; + X = readMem(address); + P.Z = X == 0; + P.S = X > 0x7F; + ticks += 2; + break; + + case 0xA4 : // ZPG LDY + Y = readMem(readMem(PC)); + PC++; + P.Z = Y == 0; + P.S = Y > 0x7F; + ticks += 3; + break; + + case 0xA5 : // ZPG LDA + A = readMem(readMem(PC)); + PC++; + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 3; + break; + + case 0xA6 : // ZPG LDX + X = readMem(readMem(PC)); + PC++; + P.Z = X == 0; + P.S = X > 0x7F; + ticks += 3; + break; + + case 0xA7 : // ZPG SMB + address = readMem(PC); + PC++; + writeMem(address, readMem(address) | 4); + ticks += 5; + break; + + case 0xA8 : // IMP TAY + Y = A; + P.Z = Y == 0; + P.S = Y > 0x7F; + ticks += 2; + break; + + case 0xA9 : // IMM LDA + A = readMem(PC); + PC++; + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 2; + break; + + case 0xAA : // IMP TAX + X = A; + P.Z = X == 0; + P.S = X > 0x7F; + ticks += 2; + break; + + case 0xAB : // IMP NOP + ticks++; + break; + + case 0xAC : // ABS LDY + address = readMem(PC); + PC++; + address |= readMem(PC) << 8; + PC++; + Y = readMem(address); + P.Z = Y == 0; + P.S = Y > 0x7F; + ticks += 4; + break; + + case 0xAD : // ABS LDA + address = readMem(PC); + PC++; + address |= readMem(PC) << 8; + PC++; + A = readMem(address); + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 4; + break; + + case 0xAE : // ABS LDX + address = readMem(PC); + PC++; + address |= readMem(PC) << 8; + PC++; + X = readMem(address); + P.Z = X == 0; + P.S = X > 0x7F; + ticks += 4; + break; + + case 0xAF : // ZPR BBS + value8 = readMem(readMem(PC)); + PC++; + address = readMem(PC); + PC++; + if (address & SIGN) + address |= 0xFF00; // jump backward + if (value8 & 4) + PC += address; + ticks += 5; + break; + + case 0xB0 : // REL BCS + address = readMem(PC); + PC++; + if (P.C) { // branch taken + ticks++; + if (address & SIGN) + address |= 0xFF00; // jump backward + if (((PC & 0xFF) + address) & 0xFF00) // page crossing + ticks++; + PC += address; + } + ticks += 2; + break; + + case 0xB1 : // IZY LDA + value8 = readMem(PC); + PC++; + address = readMem(value8); + value8++; + address |= readMem(value8) << 8; + A = readMem(address + Y); + ticks += (((address & 0xFF) + Y) & 0xFF00) ? 6 : 5; // page crossing + P.Z = A == 0; + P.S = A > 0x7F; + break; + + case 0xB2 : // IZP LDA + value8 = readMem(PC); + PC++; + address = readMem(value8); + value8++; + address |= readMem(value8) << 8; + A = readMem(address); + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 5; + break; + + case 0xB3 : // IMP NOP + ticks++; + break; + + case 0xB4 : // ZPX LDY + address = (readMem(PC) + X) & 0xFF; + PC++; + Y = readMem(address); + P.Z = Y == 0; + P.S = Y > 0x7F; + ticks += 4; + break; + + case 0xB5 : // ZPX LDA + address = (readMem(PC) + X) & 0xFF; + PC++; + A = readMem(address); + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 4; + break; + + case 0xB6 : // ZPY LDX + address = (readMem(PC) + Y) & 0xFF; + PC++; + X = readMem(address); + P.Z = X == 0; + P.S = X > 0x7F; + ticks += 4; + break; + + case 0xB7 : // ZPG SMB + address = readMem(PC); + PC++; + writeMem(address, readMem(address) | 8); + ticks += 5; + break; + + case 0xB8 : // IMP CLV + P.V = 0; + ticks += 2; + break; + + case 0xB9 : // ABY LDA + address = readMem(PC); + PC++; + ticks += ((address + Y) & 0xFF00) ? 5 : 4; // page crossing + address |= readMem(PC) << 8; + PC++; + address += Y; + A = readMem(address); + P.Z = A == 0; + P.S = A > 0x7F; + break; + + case 0xBA : // IMP TSX + X = SP; + P.Z = X == 0; + P.S = X > 0x7F; + ticks += 2; + break; + + case 0xBB : // IMP NOP + ticks++; + break; + + case 0xBC : // ABX LDY + address = readMem(PC); + PC++; + ticks += ((address + X) & 0xFF00) ? 5 : 4; // page crossing + address |= readMem(PC) << 8; + PC++; + address += X; + Y = readMem(address); + P.Z = Y == 0; + P.S = Y > 0x7F; + break; + + case 0xBD : // ABX LDA + address = readMem(PC); + PC++; + ticks += ((address + X) & 0xFF00) ? 5 : 4; // page crossing + address |= readMem(PC) << 8; + PC++; + address += X; + A = readMem(address); + P.Z = A == 0; + P.S = A > 0x7F; + break; + + case 0xBE : // ABY LDX + address = readMem(PC); + PC++; + ticks += ((address + Y) & 0xFF00) ? 5 : 4; // page crossing + address |= readMem(PC) << 8; + PC++; + address += Y; + X = readMem(address); + P.Z = X == 0; + P.S = X > 0x7F; + break; + + case 0xBF : // ZPR BBS + value8 = readMem(readMem(PC)); + PC++; + address = readMem(PC); + PC++; + if (address & SIGN) + address |= 0xFF00; // jump backward + if (value8 & 8) + PC += address; + ticks += 5; + break; + + case 0xC0 : // IMM CPY + value8 = readMem(PC); + PC++; + P.Z = ((Y - value8) & 0xFF) == 0; + P.S = ((Y - value8) & SIGN) != 0; + P.C = (Y >= value8) != 0; + ticks += 2; + break; + + case 0xC1 : // IZX CMP + value8 = readMem(PC) + X; + PC++; + address = readMem(value8); + value8++; + address |= readMem(value8) << 8; + value8 = readMem(address); + P.Z = ((A - value8) & 0xFF) == 0; + P.S = ((A - value8) & SIGN) != 0; + P.C = (A >= value8) != 0; + ticks += 6; + break; + + case 0xC2 : // IMM NOP + PC++; + ticks += 2; + break; + + case 0xC3 : // IMP NOP + ticks++; + break; + + case 0xC4 : // ZPG CPY + value8 = readMem(readMem(PC)); + PC++; + P.Z = ((Y - value8) & 0xFF) == 0; + P.S = ((Y - value8) & SIGN) != 0; + P.C = (Y >= value8) != 0; + ticks += 3; + break; + + case 0xC5 : // ZPG CMP + value8 = readMem(readMem(PC)); + PC++; + P.Z = ((A - value8) & 0xFF) == 0; + P.S = ((A - value8) & SIGN) != 0; + P.C = (A >= value8) != 0; + ticks += 3; + break; + + case 0xC6 : // ZPG DEC + address = readMem(PC); + PC++; + value8 = readMem(address); + --value8; + writeMem(address, value8); + P.Z = value8 == 0; + P.S = value8 > 0x7F; + ticks += 5; + break; + + case 0xC7 : // ZPG SMB + address = readMem(PC); + PC++; + writeMem(address, readMem(address) | 16); + ticks += 5; + break; + + case 0xC8 : // IMP INY + Y++; + P.Z = Y == 0; + P.S = Y > 0x7F; + ticks += 2; + break; + + case 0xC9 : // IMM CMP + value8 = readMem(PC); + PC++; + P.Z = ((A - value8) & 0xFF) == 0; + P.S = ((A - value8) & SIGN) != 0; + P.C = (A >= value8) != 0; + ticks += 2; + break; + + case 0xCA : // IMP DEX + X--; + P.Z = (X & 0xFF) == 0; + P.S = X > 0x7F; + ticks += 2; + break; + + case 0xCB : // IMP WAI + state = wait; + ticks += 3; + break; + + case 0xCC : // ABS CPY + address = readMem(PC); + PC++; + address |= readMem(PC) << 8; + PC++; + value8 = readMem(address); + P.Z = ((Y - value8) & 0xFF) == 0; + P.S = ((Y - value8) & SIGN) != 0; + P.C = (Y >= value8) != 0; + ticks += 4; + break; + + case 0xCD : // ABS CMP + address = readMem(PC); + PC++; + address |= readMem(PC) << 8; + PC++; + value8 = readMem(address); + P.Z = ((A - value8) & 0xFF) == 0; + P.S = ((A - value8) & SIGN) != 0; + P.C = (A >= value8) != 0; + ticks += 4; + break; + + case 0xCE : // ABS DEC + address = readMem(PC); + PC++; + address |= readMem(PC) << 8; + PC++; + value8 = readMem(address); + value8--; + writeMem(address, value8); + P.Z = value8 == 0; + P.S = value8 > 0x7F; + ticks += 3; + break; + + case 0xCF : // ZPR BBS + value8 = readMem(readMem(PC)); + PC++; + address = readMem(PC); + PC++; + if (address & SIGN) + address |= 0xFF00; // jump backward + if (value8 & 16) + PC += address; + ticks += 5; + break; + + case 0xD0 : // REL BNE + address = readMem(PC); + PC++; + if (!P.Z) { // branch taken + ticks++; + if (address & SIGN) + address |= 0xFF00; // jump backward + if (((PC & 0xFF) + address) & 0xFF00) // page crossing + ticks++; + PC += address; + } + ticks += 2; + break; + + case 0xD1 : // IZY CMP + value8 = readMem(PC); + PC++; + address = readMem(value8); + ticks += ((address + Y) & 0xFF00) ? 6 : 5; // page crossing + value8++; + address |= readMem(value8) << 8; + address += Y; + value8 = readMem(address); + P.Z = ((A - value8) & 0xFF) == 0; + P.S = ((A - value8) & SIGN) != 0; + P.C = (A >= value8) != 0; + break; + + case 0xD2 : // IZP CMP + value8 = readMem(PC); + PC++; + address = readMem(value8); + value8++; + address |= readMem(value8) << 8; + value8 = readMem(address); + P.Z = ((A - value8) & 0xFF) == 0; + P.S = ((A - value8) & SIGN) != 0; + P.C = (A >= value8) != 0; + ticks += 5; + break; + + case 0xD3 : // IMP NOP + ticks++; + break; + + case 0xD4 : // ZPX NOP + PC++; + ticks += 4; + break; + + case 0xD5 : // ZPX CMP + address = (readMem(PC) + X) & 0xFF; + PC++; + value8 = readMem(address); + P.Z = ((A - value8) & 0xFF) == 0; + P.S = ((A - value8) & SIGN) != 0; + P.C = (A >= value8) != 0; + ticks += 4; + break; + + case 0xD6 : // ZPX DEC + address = (readMem(PC) + X) & 0xFF; + PC++; + value8 = readMem(address); + value8--; + writeMem(address, value8); + P.Z = value8 == 0; + P.S = value8 > 0x7F; + ticks += 6; + break; + + case 0xD7 : // ZPG SMB + address = readMem(PC); + PC++; + writeMem(address, readMem(address) | 32); + ticks += 5; + break; + + case 0xD8 : // IMP CLD + P.D = 0; + ticks += 2; + break; + + case 0xD9 : // ABY CMP + address = readMem(PC); + PC++; + ticks += ((address + Y) & 0xFF00) ? 5 : 4; // page crossing + address |= readMem(PC) << 8; + PC++; + address += Y; + value8 = readMem(address); + P.Z = ((A - value8) & 0xFF) == 0; + P.S = ((A - value8) & SIGN) != 0; + P.C = (A >= value8) != 0; + break; + + case 0xDA : // IMP PHX + writeMem(0x100 + SP, X); + SP--; + ticks += 3; + break; + + case 0xDB : // IMP STP + state = stop; + ticks += 3; + break; + + case 0xDC : // ABS NOP + PC += 2; + ticks += 4; + break; + + case 0xDD : // ABX CMP + address = readMem(PC); + PC++; + ticks += ((address + X) & 0xFF00) ? 5 : 4; // page crossing + address |= readMem(PC) << 8; + PC++; + address += X; + value8 = readMem(address); + P.Z = ((A - value8) & 0xFF) == 0; + P.S = ((A - value8) & SIGN) != 0; + P.C = (A >= value8) != 0; + break; + + case 0xDE : // ABX DEC + address = readMem(PC); + PC++; + address |= readMem(PC) << 8; + PC++; + address += X; + value8 = readMem(address); + value8--; + writeMem(address, value8); + P.Z = value8 == 0; + P.S = (value8 & SIGN) != 0; + ticks += 7; + break; + + case 0xDF : // ZPR BBS + value8 = readMem(readMem(PC)); + PC++; + address = readMem(PC); + PC++; + if (address & SIGN) + address |= 0xFF00; // jump backward + if (value8 & 32) + PC += address; + ticks += 5; + break; + + case 0xE0 : // IMM CPX + value8 = readMem(PC); + PC++; + P.Z = ((X - value8) & 0xFF) == 0; + P.S = ((X - value8) & SIGN) != 0; + P.C = (X >= value8) != 0; + ticks += 2; + break; + + case 0xE1 : // IZX SBC + value8 = readMem(PC) + X; + PC++; + address = readMem(value8); + value8++; + address |= readMem(value8) << 8; + value8 = readMem(address); + value8 ^= 0xFF; + if (P.D) + value8 -= 0x0066; + value16 = A + value8 + P.C; + P.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0; + if (P.D) + value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3; + P.C = value16 > 0xFF; + A = value16 & 0xFF; + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 6; + break; + + case 0xE2 : // IMM NOP + PC++; + ticks += 2; + break; + + case 0xE3 : // IMP NOP + ticks++; + break; + + case 0xE4 : // ZPG CPX + value8 = readMem(readMem(PC)); + PC++; + P.Z = ((X - value8) & 0xFF) == 0; + P.S = ((X - value8) & SIGN) != 0; + P.C = (X >= value8) != 0; + ticks += 3; + break; + + case 0xE5 : // ZPG SBC + value8 = readMem(readMem(PC)); + PC++; + value8 ^= 0xFF; + if (P.D) + value8 -= 0x0066; + value16 = A + value8 + P.C; + P.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0; + if (P.D) + value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3; + P.C = value16 > 0xFF; + A = value16 & 0xFF; + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 3; + break; + + case 0xE6 : // ZPG INC + address = readMem(PC); + PC++; + value8 = readMem(address); + value8++; + writeMem(address, value8); + P.Z = value8 == 0; + P.S = value8 > 0x7F; + ticks += 5; + break; + + case 0xE7 : // ZPG SMB + address = readMem(PC); + PC++; + writeMem(address, readMem(address) | 64); + ticks += 5; + break; + + case 0xE8 : // IMP INX + X++; + P.Z = X == 0; + P.S = X > 0x7F; + ticks += 2; + break; + + case 0xE9 : // IMM SBC + value8 = readMem(PC); + PC++; + value8 ^= 0xFF; + if (P.D) + value8 -= 0x0066; + value16 = A + value8 + (P.C); + P.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0; + if (P.D) + value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3; + P.C = value16 > 0xFF; + A = value16 & 0xFF; + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 2; + break; + + case 0xEA: // IMP NOP + ticks += 2; + break; + + case 0xEB : // IMP NOP + ticks++; + break; + + case 0xEC : // ABS CPX + address = readMem(PC); + PC++; + address |= readMem(PC) << 8; + PC++; + value8 = readMem(address); + P.Z = ((X - value8) & 0xFF) == 0; + P.S = ((X - value8) & SIGN) != 0; + P.C = (X >= value8) != 0; + ticks += 4; + break; + + case 0xED : // ABS SBC + address = readMem(PC); + PC++; + address |= readMem(PC) << 8; + PC++; + value8 = readMem(address); + value8 ^= 0xFF; + if (P.D) + value8 -= 0x0066; + value16 = A + value8 + P.C; + P.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0; + if (P.D) + value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3; + P.C = value16 > 0xFF; + A = value16 & 0xFF; + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 4; + break; + + case 0xEE : // ABS INC + address = readMem(PC); + PC++; + address |= readMem(PC) << 8; + PC++; + value8 = readMem(address); + value8++; + writeMem(address, value8); + P.Z = value8 == 0; + P.S = value8 > 0x7F; + ticks += 6; + break; + + case 0xEF : // ZPR BBS + value8 = readMem(readMem(PC)); + PC++; + address = readMem(PC); + PC++; + if (address & SIGN) + address |= 0xFF00; // jump backward + if (value8 & 64) + PC += address; + ticks += 5; + break; + + case 0xF0 : // REL BEQ + address = readMem(PC); + PC++; + if (P.Z) { // branch taken + ticks++; + if (address & SIGN) + address |= 0xFF00; // jump backward + if (((PC & 0xFF) + address) & 0xFF00) // page crossing + ticks++; + PC += address; + } + ticks += 2; + break; + + case 0xF1 : // IZY SBC + value8 = readMem(PC); + PC++; + address = readMem(value8); + if ((address + Y) & 0xFF00) // page crossing + ticks++; + value8++; + address |= readMem(value8) << 8; + address += Y; + value8 = readMem(address); + value8 ^= 0xFF; + if (P.D) + value8 -= 0x0066; + value16 = A + value8 + P.C; + P.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0; + if (P.D) + value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3; + P.C = value16 > 0xFF; + A = value16 & 0xFF; + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 5; + break; + + case 0xF2 : // IZP SBC + value8 = readMem(PC); + PC++; + address = readMem(value8); + value8++; + address |= readMem(value8) << 8; + value8 = readMem(address); + value8 ^= 0xFF; + if (P.D) + value8 -= 0x0066; + value16 = A + value8 + P.C; + P.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0; + if (P.D) + value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3; + P.C = value16 > 0xFF; + A = value16 & 0xFF; + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 5; + break; + + case 0xF3 : // IMP NOP + ticks++; + break; + + case 0xF4 : // ZPX NOP + PC++; + ticks += 4; + break; + + case 0xF5 : // ZPX SBC + address = (readMem(PC) + X) & 0xFF; + PC++; + value8 = readMem(address); + value8 ^= 0xFF; + if (P.D) + value8 -= 0x0066; + value16 = A + value8 + P.C; + P.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0; + if (P.D) + value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3; + P.C = value16 > 0xFF; + A = value16 & 0xFF; + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 4; + break; + + case 0xF6 : // ZPX INC + address = (readMem(PC) + X) & 0xFF; + PC++; + value8 = readMem(address); + value8++; + writeMem(address, value8); + P.Z = value8 == 0; + P.S = value8 > 0x7F; + ticks += 6; + break; + + case 0xF7 : // ZPG SMB + address = readMem(PC); + PC++; + writeMem(address, readMem(address) | 128); + ticks += 5; + break; + + case 0xF8 : // IMP SED + P.D = 1; + ticks += 2; + break; + + case 0xF9 : // ABY SBC + address = readMem(PC); + PC++; + if ((address + Y) & 0xFF00) // page crossing + ticks++; + address |= readMem(PC) << 8; + PC++; + address += Y; + value8 = readMem(address); + value8 ^= 0xFF; + if (P.D) + value8 -= 0x0066; + value16 = A + value8 + P.C; + P.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0; + if (P.D) + value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3; + P.C = value16 > 0xFF; + A = value16 & 0xFF; + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 4; + break; + + case 0xFA : // IMP PLX + SP++; + X = readMem(0x100 + SP); + P.Z = X == 0; + P.S = X > 0x7F; + ticks += 4; + break; + + case 0xFB : // IMP NOP + ticks++; + break; + + case 0xFC : // ABS NOP + PC += 2; + ticks += 4; + break; + + case 0xFD : // ABX SBC + address = readMem(PC); + PC++; + if ((address + X) & 0xFF00) // page crossing + ticks++; + address |= readMem(PC) << 8; + PC++; + address += X; + value8 = readMem(address); + value8 ^= 0xFF; + if (P.D) + value8 -= 0x0066; + value16 = A + value8 + P.C; + P.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0; + if (P.D) + value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3; + P.C = (value16 & 0xFF00) != 0; + A = value16 & 0xFF; + P.Z = A == 0; + P.S = A > 0x7F; + ticks += 4; + break; + + case 0xFE : // ABX INC + address = readMem(PC); + PC++; + address |= readMem(PC) << 8; + PC++; + address += X; + value8 = readMem(address); + value8++; + writeMem(address, value8); + P.Z = value8 == 0; + P.S = value8 > 0x7F; + ticks += 7; + break; + + case 0xFF : // ZPR BBS + value8 = readMem(readMem(PC)); + PC++; + address = readMem(PC); + PC++; + if (address & SIGN) + address |= 0xFF00; // jump backward + if (value8 & 128) + PC += address; + ticks += 5; + break; + + } // end of switch + } // end of if + } // end of while + return PC; +} + + + +//============================================================================== +// Everything down below are optionnal utilities used during devellopment +// mostly using Klaus Dormann's functonnal tests published at +// https://github.com/Klaus2m5/6502_65C02_functional_tests +// but also BigEd's timing tests code published at +// https://github.com/BigEd/6502timing +// and http://forum.6502.org/viewtopic.php?f=8&t=3340 +// +// for functionnal tests : +// g++ -Wall -O3 -D __TESTS__ puce65c02.cpp -o puce65c02 && ./puce65c02 +// or, for detailled info +// g++ -Wall -O3 -D __TESTS__ -D VERBOSE puce65c02.cpp -o puce65c02 && ./puce65c02 +// or, if you want to check the cycle accuracy : +// g++ -Wall -O3 -D __TESTS__ -D TIMING puce65c02.cpp -o puce65c02 && ./puce65c02 +// and see bottom of file for more info + + +#if __TESTS__ + #include + + static const char* mn[256] = { + "BRK","ORA","NOP","NOP","TSB","ORA","ASL","RMB","PHP","ORA","ASL","NOP","TSB","ORA","ASL","BBR", + "BPL","ORA","ORA","NOP","TRB","ORA","ASL","RMB","CLC","ORA","INC","NOP","TRB","ORA","ASL","BBR", + "JSR","AND","NOP","NOP","BIT","AND","ROL","RMB","PLP","AND","ROL","NOP","BIT","AND","ROL","BBR", + "BMI","AND","AND","NOP","BIT","AND","ROL","RMB","SEC","AND","DEC","NOP","BIT","AND","ROL","BBR", + "RTI","EOR","NOP","NOP","NOP","EOR","LSR","RMB","PHA","EOR","LSR","NOP","JMP","EOR","LSR","BBR", + "BVC","EOR","EOR","NOP","NOP","EOR","LSR","RMB","CLI","EOR","PHY","NOP","NOP","EOR","LSR","BBR", + "RTS","ADC","NOP","NOP","STZ","ADC","ROR","RMB","PLA","ADC","ROR","NOP","JMP","ADC","ROR","BBR", + "BVS","ADC","ADC","NOP","STZ","ADC","ROR","RMB","SEI","ADC","PLY","NOP","JMP","ADC","ROR","BBR", + "BRA","STA","NOP","NOP","STY","STA","STX","SMB","DEY","BIT","TXA","NOP","STY","STA","STX","BBS", + "BCC","STA","STA","NOP","STY","STA","STX","SMB","TYA","STA","TXS","NOP","STZ","STA","STZ","BBS", + "LDY","LDA","LDX","NOP","LDY","LDA","LDX","SMB","TAY","LDA","TAX","NOP","LDY","LDA","LDX","BBS", + "BCS","LDA","LDA","NOP","LDY","LDA","LDX","SMB","CLV","LDA","TSX","NOP","LDY","LDA","LDX","BBS", + "CPY","CMP","NOP","NOP","CPY","CMP","DEC","SMB","INY","CMP","DEX","WAI","CPY","CMP","DEC","BBS", + "BNE","CMP","CMP","NOP","NOP","CMP","DEC","SMB","CLD","CMP","PHX","STP","NOP","CMP","DEC","BBS", + "CPX","SBC","NOP","NOP","CPX","SBC","INC","SMB","INX","SBC","NOP","NOP","CPX","SBC","INC","BBS", + "BEQ","SBC","SBC","NOP","NOP","SBC","INC","SMB","SED","SBC","PLX","NOP","NOP","SBC","INC","BBS" + }; + + static const int am[256] = { + 0x0 , 0xC , 0x2 , 0x0 , 0x3 , 0x3 , 0x3 , 0x3 , 0x0 , 0x2 , 0x1 , 0x0 , 0x7 , 0x7 , 0x7 , 0xE , + 0x6 , 0xD , 0xB , 0x0 , 0x3 , 0x4 , 0x4 , 0x3 , 0x0 , 0x9 , 0x1 , 0x0 , 0x7 , 0x8 , 0x8 , 0xE , + 0x7 , 0xC , 0x2 , 0x0 , 0x3 , 0x3 , 0x3 , 0x3 , 0x0 , 0x2 , 0x1 , 0x0 , 0x7 , 0x7 , 0x7 , 0xE , + 0x6 , 0xD , 0xB , 0x0 , 0x4 , 0x4 , 0x4 , 0x3 , 0x0 , 0x9 , 0x1 , 0x0 , 0x8 , 0x8 , 0x8 , 0xE , + 0x0 , 0xC , 0x2 , 0x0 , 0x3 , 0x3 , 0x3 , 0x3 , 0x0 , 0x2 , 0x1 , 0x0 , 0x7 , 0x7 , 0x7 , 0xE , + 0x6 , 0xD , 0xB , 0x0 , 0x4 , 0x4 , 0x4 , 0x3 , 0x0 , 0x9 , 0x0 , 0x0 , 0x7 , 0x8 , 0x8 , 0xE , + 0x0 , 0xC , 0x2 , 0x0 , 0x3 , 0x3 , 0x3 , 0x3 , 0x0 , 0x2 , 0x1 , 0x0 , 0xA , 0x7 , 0x7 , 0xE , + 0x6 , 0xD , 0xB , 0x0 , 0x4 , 0x4 , 0x4 , 0x3 , 0x0 , 0x9 , 0x0 , 0x0 , 0xF , 0x8 , 0x8 , 0xE , + 0x6 , 0xC , 0x2 , 0x0 , 0x3 , 0x3 , 0x3 , 0x3 , 0x0 , 0x2 , 0x0 , 0x0 , 0x7 , 0x7 , 0x7 , 0xE , + 0x6 , 0xD , 0xB , 0x0 , 0x4 , 0x4 , 0x5 , 0x3 , 0x0 , 0x9 , 0x0 , 0x0 , 0x7 , 0x8 , 0x8 , 0xE , + 0x2 , 0xC , 0x2 , 0x0 , 0x3 , 0x3 , 0x3 , 0x3 , 0x0 , 0x2 , 0x0 , 0x0 , 0x7 , 0x7 , 0x7 , 0xE , + 0x6 , 0xD , 0xB , 0x0 , 0x4 , 0x4 , 0x5 , 0x3 , 0x0 , 0x9 , 0x0 , 0x0 , 0x8 , 0x8 , 0x9 , 0xE , + 0x2 , 0xC , 0x2 , 0x0 , 0x3 , 0x3 , 0x3 , 0x3 , 0x0 , 0x2 , 0x0 , 0x0 , 0x7 , 0x7 , 0x7 , 0xE , + 0x6 , 0xD , 0xB , 0x0 , 0x4 , 0x4 , 0x4 , 0x3 , 0x0 , 0x9 , 0x0 , 0x0 , 0x7 , 0x8 , 0x8 , 0xE , + 0x2 , 0xC , 0x2 , 0x0 , 0x3 , 0x3 , 0x3 , 0x3 , 0x0 , 0x2 , 0x0 , 0x0 , 0x7 , 0x7 , 0x7 , 0xE , + 0x6 , 0xD , 0xB , 0x0 , 0x4 , 0x4 , 0x4 , 0x3 , 0x0 , 0x9 , 0x0 , 0x0 , 0x7 , 0x8 , 0x8 , 0xE + }; + + void puce65c02::dasm(uint16_t address) { // TODO : give the bit# for BBR, BSS, RMB and SMB + uint8_t op = readMem(address); + uint8_t b1 = readMem((address + 1) & 0xFFFF); + uint8_t b2 = readMem((address + 2) & 0xFFFF); + printf("%04X %02X ", address, op); + switch(am[op]) { + case 0x0: printf(" %s ", mn[op] ); break; // implied + case 0x1: printf(" %s A ", mn[op] ); break; // accumulator + case 0x2: printf("%02X %s #$%02X ", b1, mn[op],b1 ); break; // immediate + case 0x3: printf("%02X %s $%02X ", b1, mn[op],b1 ); break; // zero page + case 0x4: printf("%02X %s $%02X,X ", b1, mn[op],b1 ); break; // zero page, X indexed + case 0x5: printf("%02X %s $%02X,Y ", b1, mn[op],b1 ); break; // zero page, Y indexed + case 0x6: printf("%02X %s $%02X ", b1, mn[op],b1 ); break; // relative + case 0xB: printf("%02X %s ($%02X) ", b1, mn[op],b1 ); break; // izp ($00) + case 0xC: printf("%02X %s ($%02X,X) ", b1, mn[op],b1 ); break; // X indexed, indirect + case 0xD: printf("%02X %s ($%02X),Y ", b1, mn[op],b1 ); break; // indirect, Y indexed + case 0x7: printf("%02X%02X %s $%02X%02X ",b1,b2,mn[op],b2,b1); break; // absolute + case 0x8: printf("%02X%02X %s $%02X%02X,X ",b1,b2,mn[op],b2,b1); break; // absolute, X indexed + case 0x9: printf("%02X%02X %s $%02X%02X,Y ",b1,b2,mn[op],b2,b1); break; // absolute, Y indexed + case 0xA: printf("%02X%02X %s ($%02X%02X) ",b1,b2,mn[op],b2,b1); break; // indirect + case 0xF: printf("%02X%02X %s ($%02X%02X,X)",b1,b2,mn[op],b2,b1); break; // iax ($0000,X) + case 0xE: printf("%02X%02X %s $%02X,$%02X ",b1,b2,mn[op],b2,b1); break; // zpr $00,$00 + } + return; + } + + + int puce65c02::getCode(uint16_t address, char* buffer, int size, int numLines) { + int consumed = 0; + int remaining = size; + + for (int i=0; iticks = 0; // we won't measure the 7 ticks the RESET took above + unsigned long long int oldticks = cpu->ticks; + + do { + cpu->dasm(cpu->PC); + printf(" "); + cpu->exec(1); + cpu->printRegs(); + printf(" Cycles: %llu Total: %llu\n", cpu->ticks - oldticks, cpu->ticks); + + oldticks = cpu->ticks; + } while(cpu->ticks < 1143); + +#else + // Running the two functionnal tests : + const char *filename = "6502_functional_test.bin"; + FILE *f = fopen(filename, "rb"); + if (!f || fread(RAM, 1, 65536, f) != 65536) { + printf("ERROR : can't load Klaus Dormann's functonnal tests binary file %s\n", filename); + return(0); + } + fclose(f); + + puce65c02 *cpu = new puce65c02(0x400); // instantiate, reset the cpu and set PC to 0x400 + +#if VERBOSE + unsigned long long int oldticks = cpu->ticks; +#endif + + uint16_t oldPC = 0x400, newPC = 0x400; // to detect loops (BNE $FE) when error occurs + + while(newPC != 0x3469) { + +#if VERBOSE + cpu->dasm(newPC); + printf(" "); +#endif + + newPC = cpu->exec(1); + +#if VERBOSE + cpu->printRegs(); + printf(" Cycles: %llu Total: %llu\n", cpu->ticks - oldticks, cpu->ticks); + oldticks = cpu->ticks; +#endif + + if (newPC == oldPC ) { + printf("\n\nLoop detected @ %04X - Press ENTER to proceed with next test or CTRL to abort\n\n", newPC); + getchar(); + cpu->setPC(newPC + 2); // step out the loop + } + oldPC = newPC; + } + printf("\nSUCCESS : Reached end of 6502_functional_test @ 0x3469 after %llu clock cycles\n", cpu->ticks); + + + const char *filename2 = "65C02_extended_opcodes_test.bin"; + f = fopen(filename2, "rb"); + if (!f || fread(RAM, 1, 65536, f) != 65536) { + printf("ERROR : can't load 65c02 Klaus Dormann's functonnal tests binary file %s\n", filename2); + return(0); + } + fclose(f); + + cpu->RST(); + cpu->setPC(0x400); + oldPC = newPC = 0x400; +#if VERBOSE + oldticks = cpu->ticks; +#endif + + while(newPC != 0x24f1) { +#if VERBOSE + cpu->dasm(newPC); + printf(" "); +#endif + newPC = cpu->exec(1); +#if VERBOSE + cpu->printRegs(); + printf(" Cycles: %llu Total: %llu\n", cpu->ticks - oldticks, cpu->ticks); + oldticks = cpu->ticks; +#endif + if (newPC == oldPC ) { + printf("\n\nLoop detected @ %04X - Press ENTER to proceed with next test or CTRL to abort\n\n", newPC); + getchar(); + cpu->setPC(newPC + 2); // step out the loop + } + oldPC = newPC; + } + printf("\nSUCCESS : Reached end of 65C02_extended_opcodes_test @ 0x24f1 after %llu clock cycles\n", cpu->ticks); + +#endif + return(0); + } +#endif diff --git a/puce65c02.h b/puce65c02.h new file mode 100644 index 0000000..6ffaf89 --- /dev/null +++ b/puce65c02.h @@ -0,0 +1,88 @@ +/* + puce65c02, a WDC 65c02 cpu emulator, based on puce6502 by the same author + Last modified 1st of July 2021 + + Copyright (c) 2021 Arthur Ferreira (arthur.ferreira2@gmail.com) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions : + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + + +#ifndef _PUCE65C02_H +#define _PUCE65C02_H + +#include + +typedef enum {run, step, stop, wait} status; + +#define CARRY 0x01 +#define ZERO 0x02 +#define INTR 0x04 +#define DECIM 0x08 +#define BREAK 0x10 +#define UNDEF 0x20 +#define OFLOW 0x40 +#define SIGN 0x80 + + +class puce65c02 { +public: + + unsigned long long int ticks; +// private: + status state; + uint16_t PC; // Program Counter + uint8_t A, X, Y, SP; // Accumulator, X and y indexes and Stack Pointer + union { + uint8_t byte; + struct { + uint8_t C : 1; // Carry + uint8_t Z : 1; // Zero + uint8_t I : 1; // Interupt disabled + uint8_t D : 1; // Decimal + uint8_t B : 1; // Break + uint8_t U : 1; // Undefined + uint8_t V : 1; // Overflow + uint8_t S : 1; // Sign + }; + } P; // Processor Status + +public: + puce65c02() { ticks = 0; RST(); } + puce65c02(uint16_t address) { ticks = 0; RST(); PC = address; } + ~puce65c02(); + + uint16_t getPC() { return PC; }; + void setPC(uint16_t address) { PC = address; }; + + void RST(); + void IRQ(); + void NMI(); + uint16_t exec(unsigned long long int cycleCount); + +#if __TESTS__ + void dasm(uint16_t address); + void printRegs(); + int getRegs(char* buffer); + int getCode(uint16_t address, char* buffer, int size, int numLines); +#endif + +}; + +#endif