/*
  puce6502 - MOS 6502 cpu emulator
  Last modified 21st of June 2021
  Copyright (c) 2018 Arthur Ferreira (arthur.ferreira2@gmail.com)

  This version has been modified for reinette II plus, a french Apple II plus
  emulator using SDL2 (https://github.com/ArthurFerreira2/reinette-II-plus).

  Please download the latest version from
  https://github.com/ArthurFerreira2/puce6502

  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.
*/


// set to zero for 'normal' use
// or to 1 if you want to run the functionnal tests
#define _FUNCTIONNAL_TESTS 0

#include "puce6502.h"

#define CARRY 0x01
#define ZERO  0x02
#define INTR  0x04
#define DECIM 0x08
#define BREAK 0x10
#define UNDEF 0x20
#define OFLOW 0x40
#define SIGN  0x80

#if _FUNCTIONNAL_TESTS

	// for functionnal tests, see main()
	uint8_t RAM[65536];
	inline uint8_t readMem(uint16_t address) { return RAM[address]; }
	inline void writeMem(uint16_t address, uint8_t value) { RAM[address] = value; }

#else

	// user provided functions
	extern uint8_t readMem(uint16_t address);
	extern void writeMem(uint16_t address, uint8_t value);

#endif


unsigned long long int ticks = 0;  // accumulated number of clock cycles


static uint16_t PC;  //  Program Counter
static uint8_t A, X, Y, SP;  // Accumulator, X and y indexes and Stack Pointer
static union {
	uint8_t byte;
  struct {
    uint8_t C : 1;  // Carry
    uint8_t Z : 1;  // Zero
    uint8_t I : 1;  // Interupt-disable
    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

void puce6502RST() {  // Reset
	PC = readMem(0xFFFC) | (readMem(0xFFFD) << 8);
	SP = 0xFD;
	P.I = 1;
	P.U = 1;
	ticks += 7;
}


void puce6502IRQ() {  // Interupt Request
	if (!P.I) return;
	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(0xFFFE) | (readMem(0xFFFF) << 8);
	ticks += 7;
}


void puce6502NMI() {  // Non Maskable Interupt
	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 down below :

  IMP	: Implied or Implicit : DEX, RTS, CLC - 25 instructions
  ACC	: Accumulator : ASL A, ROR A, DEC A - 4 instructions
  IMM	: Immediate : LDA #$A5 - 11
  ZPG	: Zero Page : LDA $81 - 21 instructions
  ZPX	: Zero Page Indexed with X : LDA $55,X - 16 instructions
  ZPY	: Zero Page Indexed with Y : LDX $55,Y - 2 instructions
  REL	: Relative : BEQ LABEL12 - 8 instructions
  ABS	: Absolute : LDA $2000 - 23 instructions
  ABX	: Absolute Indexed with X : LDA $2000,X - 15 instructions
  ABY	: Absolute Indexed with Y : LDA $2000,Y - 9 instructions
  IND	: Indirect : JMP ($1020) - 1 instruction
	IZX	: ZP Indexed Indirect with X (Preindexed) : LDA ($55,X) - 8 instructions
	IZY	: ZP Indirect Indexed with Y (Postindexed) : LDA ($55),Y - 8 instructions
 */

uint16_t puce6502Exec(unsigned long long int cycleCount) {
	register uint16_t address;
	register uint8_t  value8;
	register uint16_t value16;

	cycleCount += ticks;	// cycleCount becomes the targeted ticks value
	while (ticks < cycleCount) {

		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 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 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;
				P.C = value16 > 0xFF;
				A = value16 & 0xFF;
				P.Z = A == 0;
				P.S = A > 0x7F;
				ticks += 2;
			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 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 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 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 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++;
				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;
				ticks += 7;
			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 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 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 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 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 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 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 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++;
				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;
				ticks += 7;
			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 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 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 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 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 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 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 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++;
				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;
				ticks += 7;
			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 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 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 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 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 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 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 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++;
				address |= readMem(PC) << 8;
				PC++;
				address += X;
				value8 = readMem(address);
				value16 = (value8 >> 1) | (P.C << 7);
				P.C = (value8 & 0x1) != 0;                          // TBR
				value16 = value16 & 0xFF;
				writeMem(address, value16);
				P.Z = value16 == 0;
				P.S = value16 > 0x7F;
				ticks += 7;
			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 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 0x88 :  // IMP DEY
				Y--;
				P.Z = (Y & 0xFF) == 0;
				P.S = (Y & SIGN) != 0;
				ticks += 2;
			break;

			case 0x8A :  // IMP TXA
				A = X;
				P.Z = A == 0;
				P.S = A > 0x7F;
				ticks += 2;
			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 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 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 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 0x9D :  // ABX STA
				address = readMem(PC);
				PC++;
				address |= readMem(PC) << 8;
				PC++;
				address += X;
				writeMem(address, A);
				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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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;

			default:  // invalid / undocumented opcode
				ticks += 2;  // as NOP
			break;
    }  // end of switch
  }
	return PC;
}



// the code below was used during developpment for test and debug
// and is not required for normal operation

#include <stdio.h>

static const char* mn[256] = {
	"BRK","ORA","UND","UND","UND","ORA","ASL","UND","PHP","ORA","ASL","UND","UND","ORA","ASL","UND",
  "BPL","ORA","UND","UND","UND","ORA","ASL","UND","CLC","ORA","UND","UND","UND","ORA","ASL","UND",
  "JSR","AND","UND","UND","BIT","AND","ROL","UND","PLP","AND","ROL","UND","BIT","AND","ROL","UND",
  "BMI","AND","UND","UND","UND","AND","ROL","UND","SEC","AND","UND","UND","UND","AND","ROL","UND",
  "RTI","EOR","UND","UND","UND","EOR","LSR","UND","PHA","EOR","LSR","UND","JMP","EOR","LSR","UND",
  "BVC","EOR","UND","UND","UND","EOR","LSR","UND","CLI","EOR","UND","UND","UND","EOR","LSR","UND",
  "RTS","ADC","UND","UND","UND","ADC","ROR","UND","PLA","ADC","ROR","UND","JMP","ADC","ROR","UND",
  "BVS","ADC","UND","UND","UND","ADC","ROR","UND","SEI","ADC","UND","UND","UND","ADC","ROR","UND",
  "UND","STA","UND","UND","STY","STA","STX","UND","DEY","UND","TXA","UND","STY","STA","STX","UND",
  "BCC","STA","UND","UND","STY","STA","STX","UND","TYA","STA","TXS","UND","UND","STA","UND","UND",
  "LDY","LDA","LDX","UND","LDY","LDA","LDX","UND","TAY","LDA","TAX","UND","LDY","LDA","LDX","UND",
  "BCS","LDA","UND","UND","LDY","LDA","LDX","UND","CLV","LDA","TSX","UND","LDY","LDA","LDX","UND",
  "CPY","CMP","UND","UND","CPY","CMP","DEC","UND","INY","CMP","DEX","UND","CPY","CMP","DEC","UND",
  "BNE","CMP","UND","UND","UND","CMP","DEC","UND","CLD","CMP","UND","UND","UND","CMP","DEC","UND",
  "CPX","SBC","UND","UND","CPX","SBC","INC","UND","INX","SBC","NOP","UND","CPX","SBC","INC","UND",
  "BEQ","SBC","UND","UND","UND","SBC","INC","UND","SED","SBC","UND","UND","UND","SBC","INC","UND"
};

static const int am[256] = {
   0x0 , 0xC , 0x0 , 0x0 , 0x0 , 0x3 , 0x3 , 0x0 , 0x0 , 0x2 , 0x1 , 0x0 , 0x0 , 0x7 , 0x7 , 0x0 ,
   0x6 , 0xD , 0x0 , 0x0 , 0x0 , 0x4 , 0x4 , 0x0 , 0x0 , 0x9 , 0x0 , 0x0 , 0x0 , 0x8 , 0x8 , 0x0 ,
   0x7 , 0xC , 0x0 , 0x0 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x2 , 0x1 , 0x0 , 0x7 , 0x7 , 0x7 , 0x0 ,
   0x6 , 0xD , 0x0 , 0x0 , 0x0 , 0x4 , 0x4 , 0x0 , 0x0 , 0x9 , 0x0 , 0x0 , 0x0 , 0x8 , 0x8 , 0x0 ,
   0x0 , 0xC , 0x0 , 0x0 , 0x0 , 0x3 , 0x3 , 0x0 , 0x0 , 0x2 , 0x1 , 0x0 , 0x7 , 0x7 , 0x7 , 0x0 ,
   0x6 , 0xD , 0x0 , 0x0 , 0x0 , 0x4 , 0x4 , 0x0 , 0x0 , 0x9 , 0x0 , 0x0 , 0x0 , 0x8 , 0x8 , 0x0 ,
   0x0 , 0xC , 0x0 , 0x0 , 0x0 , 0x3 , 0x3 , 0x0 , 0x0 , 0x2 , 0x1 , 0x0 , 0xA , 0x7 , 0x7 , 0x0 ,
   0x6 , 0xD , 0x0 , 0x0 , 0x0 , 0x4 , 0x4 , 0x0 , 0x0 , 0x9 , 0x0 , 0x0 , 0x0 , 0x8 , 0x8 , 0x0 ,
   0x0 , 0xC , 0x0 , 0x0 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x7 , 0x7 , 0x7 , 0x0 ,
   0x6 , 0xD , 0x0 , 0x0 , 0x4 , 0x4 , 0x5 , 0x0 , 0x0 , 0x9 , 0x0 , 0x0 , 0x0 , 0x8 , 0x0 , 0x0 ,
   0x2 , 0xC , 0x2 , 0x0 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x2 , 0x0 , 0x0 , 0x7 , 0x7 , 0x7 , 0x0 ,
   0x6 , 0xD , 0x0 , 0x0 , 0x4 , 0x4 , 0x5 , 0x0 , 0x0 , 0x9 , 0x0 , 0x0 , 0x8 , 0x8 , 0x9 , 0x0 ,
   0x2 , 0xC , 0x0 , 0x0 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x2 , 0x0 , 0x0 , 0x7 , 0x7 , 0x7 , 0x0 ,
   0x6 , 0xD , 0x0 , 0x0 , 0x0 , 0x4 , 0x4 , 0x0 , 0x0 , 0x9 , 0x0 , 0x0 , 0x0 , 0x8 , 0x8 , 0x0 ,
   0x2 , 0xC , 0x0 , 0x0 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x2 , 0x0 , 0x0 , 0x7 , 0x7 , 0x7 , 0x0 ,
   0x6 , 0xD , 0x0 , 0x0 , 0x0 , 0x4 , 0x4 , 0x0 , 0x0 , 0x9 , 0x0 , 0x0 , 0x0 , 0x8 , 0x8 , 0x0
 };

void dasm(uint16_t address) {

  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 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
  }
}

void printRegs() {
  printf("A=%02X  X=%02X  Y=%02X  S=%02X  *S=%02X  %c%c%c%c%c%c%c%c", \
	A, X, Y, SP, readMem(0x100 + SP), \
	P.S?'N':'-', P.V?'V':'-', P.U?'U':'.', P.B?'B':'-', \
	P.D?'D':'-', P.I?'I':'-', P.Z?'Z':'-', P.C?'C':'-');
}

void setPC(uint16_t address) {
	PC = address;
}

uint16_t getPC(){
	return PC;
}



#if _FUNCTIONNAL_TESTS

		// 6502 functonnal tests
		// using Klaus Dormann's functonnal tests published at :
		// https://github.com/Klaus2m5/6502_65C02_functional_tests

		int main(int argc, char* argv[]){

			char *filename = "6502_functional_test.bin";
			FILE *f = fopen(filename, "rb");
		  if (!f || fread(RAM, 1, 65536, f) != 65536) {
		    printf("ERROR : can't load %s\n", filename);
		    return(0);
		  }
		  fclose(f);

			puce6502RST();  // reset the CPU
			PC = 0x400;  // set Program Counter to start of code

			unsigned long long int oldticks = 0;
			uint16_t oldPC = PC, newPC = PC;  // to detect the BNE $FE when an error occurs

			// while(1) {
			// 	dasm(newPC);
			// 	printf("  ");
			// 	newPC = puce6502Exec(1);
			// 	printRegs();
			// 	printf("   Cycles: %llu   Total: %llu\n", ticks - oldticks, ticks);
			// 	oldticks = ticks;
			//
			//   if (newPC == 0x3469){  // 6502_functional_test SUCCESS
			// 		printf("\nReached end of 6502_functional_test @ %04X : SUCCESS !\n", newPC);
			// 	  break;
			// 	}
			//
			// 	if (newPC == oldPC ) {
			// 		printf("\n\nLoop detected @ %04X - Press ENTER to proceed with next test or CTRL<C> to stop\n\n", newPC);
			// 		return(-1);
			// 		getchar();
			// 		PC = newPC + 2;
			// 	}
			// 	oldPC = newPC;
			// }

		  // Benchmark : replace the above while loop by this one
			  while(puce6502Exec(100) != 0x3469);
			  printf("%llu\n", ticks);
		  // and use the time utility to avaluate the speed the emulated 65C02

			return(0);
		}

#endif


/*   test results :


## Using 6502_functional_test.bin :

<...>
3457 69 55     ADC #$55       A=AA  X=0E  Y=FF  S=FF  *S=34  NVUB----   Cycles: 2   Total: 96240555
3459 C9 AA     CMP #$AA       A=AA  X=0E  Y=FF  S=FF  *S=34  -VUB--ZC   Cycles: 2   Total: 96240557
345B D0 FE     BNE $FE        A=AA  X=0E  Y=FF  S=FF  *S=34  -VUB--ZC   Cycles: 2   Total: 96240559
345D AD 0002   LDA $0200      A=2B  X=0E  Y=FF  S=FF  *S=34  -VUB---C   Cycles: 4   Total: 96240563
3460 C9 2B     CMP #$2B       A=2B  X=0E  Y=FF  S=FF  *S=34  -VUB--ZC   Cycles: 2   Total: 96240565
3462 D0 FE     BNE $FE        A=2B  X=0E  Y=FF  S=FF  *S=34  -VUB--ZC   Cycles: 2   Total: 96240567
3464 A9 F0     LDA #$F0       A=F0  X=0E  Y=FF  S=FF  *S=34  NVUB---C   Cycles: 2   Total: 96240569
3466 8D 0002   STA $0200      A=F0  X=0E  Y=FF  S=FF  *S=34  NVUB---C   Cycles: 4   Total: 96240573

Reached end of 6502_functional_test @ 3469 : SUCCESS !


=> which means puce65c02 passes the 6502 functionnal tests :

                        ; S U C C E S S ************************************************
                        ; -------------
                                success         ;if you get here everything went well
3469 : 4c6934          >        jmp *           ;test passed, no errors

                        ; -------------
                        ; S U C C E S S ************************************************




## Benchmarks

  No info printed during execution
	Using gcc -O3 option

	$ gcc -O3 puce6502.c -o puce6502
	$ time ./puce6502
	96240630

	real    0m0,093s
	user    0m0,000s
	sys     0m0,000s
	$ time ./puce6502
	96240630

	real    0m0,107s
	user    0m0,000s
	sys     0m0,000s
	$ time ./puce6502
	96240630

	real    0m0,096s
	user    0m0,000s
	sys     0m0,000s
	$ time ./puce6502
	96240630

	real    0m0,097s
	user    0m0,000s
	sys     0m0,000s
	$ time ./puce6502
	96240630

	real    0m0,110s
	user    0m0,000s
	sys     0m0,000s
	$ time ./puce6502
	96240630

	real    0m0,110s
	user    0m0,000s
	sys     0m0,000s
	$ time ./puce6502
	96240630

	real    0m0,095s
	user    0m0,000s
	sys     0m0,000s


--> emulated CPU running at around 1 GHz !!!?!???

*/
