puce65c02/puce65c02.cpp

2812 lines
78 KiB
C++

/*
puce65c02, a WDC 65c02 cpu emulator, based on puce6502 by the same author
Last modified 1st of July 2021
Copyright (c) 2021 Arthur Ferreira
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 && (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 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<cstdio>
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