/*
  puce65c02, a WDC 65c02 cpu emulator, based on puce6502 by the same author

  Last modified 1st of July 2021

  Copyright (c) 2021 Arthur Ferreira (arthur.ferreira2@gmail.com)

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.
*/

/*
  This version is slightly modified for reinette IIe, a french Apple IIe
  emulator using SDL2 (https://github.com/ArthurFerreira2/reinette-IIe).
  Please download the latest version from
  https://github.com/ArthurFerreira2/puce65c02
*/

#include "reinette.h"  // provides mmu->readMem and mmu->writeMem


void puce65c02::RST() {
  PC = mmu->readMem(0xFFFC) | (mmu->readMem(0xFFFD) << 8);
  SP = 0xFF;
  P.bits.I = 1;
  P.bits.U = 1;
  state = run;
  ticks += 7;
}


void puce65c02::IRQ() {
  state = run;                    // always ?
  if (!P.bits.I) return;          // consume 0 clock cycle ?
  PC++;
  mmu->writeMem(0x100 + SP, (PC >> 8) & 0xFF);
  SP--;
  mmu->writeMem(0x100 + SP, PC & 0xFF);
  SP--;
  mmu->writeMem(0x100 + SP, P.byte & ~BREAK);
  SP--;
  PC = mmu->readMem(0xFFFE) | (mmu->readMem(0xFFFF) << 8);
  ticks += 7;
}


void puce65c02::NMI() {
  state = run;
  P.bits.I = 1;  // ???
  PC++;
  mmu->writeMem(0x100 + SP, (PC >> 8) & 0xFF);
  SP--;
  mmu->writeMem(0x100 + SP, PC & 0xFF);
  SP--;
  mmu->writeMem(0x100 + SP, P.byte & ~BREAK);
  SP--;
  PC = mmu->readMem(0xFFFA) | (mmu->readMem(0xFFFB) << 8);
  ticks += 7;
}


puce65c02::puce65c02() {
  ticks = 0LL;
}


uint16_t puce65c02::getPC() {
  return PC;
}


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


/*
  Addressing modes abreviations used in the comments below :

  IMP  : Implied or Implicit : DEX, RTS, CLC - 61 instructions
  ACC  : Accumulator : ASL A, ROR A, DEC A - 6 instructions
  IMM  : Immediate : LDA #$A5 - 19 instructions
  ZPG  : Zero Page : LDA $81 - 41 instructions
  ZPX  : Zero Page Indexed with X : LDA $55,X - 21 instructions
  ZPY  : Zero Page Indexed with Y : LDX $55,Y - 2 instructions
  REL  : Relative : BEQ LABEL12 - 9 instructions
  ABS  : Absolute : LDA $2000 - 29 instructions
  ABX  : Absolute Indexed with X : LDA $2000,X - 17 instructions
  ABY  : Absolute Indexed with Y : LDA $2000,Y - 9 instructions
  IND  : Indirect : JMP ($1020) - 1 instruction
  IZP  : Indirect Zero Page : LDA ($55) (65c02 only) - 8 instructions
  IZX  : ZP Indexed Indirect with X (Preindexed) : LDA ($55,X) - 8 instructions
  IZY  : ZP Indirect Indexed with Y (Postindexed) : LDA ($55),Y - 8 instructions
  IAX  : Absolute Indexed Indirect : JMP ($2000,X) (65c02 only) - 1 instruction
  ZPR  : Zero Page Relative : BBS0 $23, LABEL (65c02 only) - 16 instructions
*/


uint16_t puce65c02::exec(unsigned long long int cycleCount) {
  cycleCount += ticks;  // cycleCount becomes the targeted ticks value8
  while (ticks < cycleCount) {
    if (state == run || state == step) {

      uint8_t value8;
      uint16_t value16;
      uint16_t address;

      switch(mmu->readMem(PC++)) {  // fetch instruction and increment Program Counter

        case 0x00 :  // IMP BRK
          PC++;
          mmu->writeMem(0x100 + SP, ((PC) >> 8) & 0xFF);
          SP--;
          mmu->writeMem(0x100 + SP, PC & 0xFF);
          SP--;
          mmu->writeMem(0x100 + SP, P.byte | BREAK);
          SP--;
          P.bits.I = 1;
          P.bits.D = 0;
          PC = mmu->readMem(0xFFFE) | (mmu->readMem(0xFFFF) << 8);
          ticks += 7;
        break;

        case 0x01 :  // IZX ORA
          value8 = mmu->readMem(PC) + X;
          PC++;
          address = mmu->readMem(value8);
          value8++;
          address |= mmu->readMem(value8) << 8;
          A |= mmu->readMem(address);
          P.bits.Z = A == 0;
          P.bits.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 = mmu->readMem(PC);
          PC++;
          value8 = mmu->readMem(address);
          P.bits.Z = (value8 & A) == 0;
          mmu->writeMem(address, value8 | A);
          ticks += 5;
        break;

        case 0x05 :  // ZPG ORA
          A |= mmu->readMem(mmu->readMem(PC));
          PC++;
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 3;
        break;

        case 0x06 :  // ZPG ASL
          address = mmu->readMem(PC);
          PC++;
          value16 = mmu->readMem(address) << 1;
          P.bits.C = value16 > 0xFF;
          value16 &= 0xFF;
          mmu->writeMem(address, value16);
          P.bits.Z = value16 == 0;
          P.bits.S = value16 > 0x7F;
          ticks += 5;
        break;

        case 0x07 :  // ZPG RMB
          address = mmu->readMem(PC);
          PC++;
          mmu->writeMem(address, mmu->readMem(address) & ~1);
          ticks += 5;
        break;

        case 0x08 :  // IMP PHP
          mmu->writeMem(0x100 + SP, P.byte | BREAK);
          SP--;
          ticks += 3;
        break;

        case 0x09 :  // IMM ORA
          A |= mmu->readMem(PC);
          PC++;
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 2;
        break;

        case 0x0A :  // ACC ASL
          value16 = A << 1;
          A = value16 & 0xFF;
          P.bits.C = value16 > 0xFF;
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 2;
        break;

        case 0x0B :  // IMP NOP
          ticks++;
        break;

        case 0x0C :  // ABS TSB
          address = mmu->readMem(PC);
          PC++;
          address |= mmu->readMem(PC) << 8;
          PC++;
          value8 = mmu->readMem(address);
          P.bits.Z = (value8 & A) == 0;
          mmu->writeMem(address, value8 | A);
          ticks += 6;
        break;

        case 0x0D :  // ABS ORA
          address = mmu->readMem(PC);
          PC++;
          address |= mmu->readMem(PC) << 8;
          PC++;
          A |= mmu->readMem(address);
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 4;
        break;

        case 0x0E :  // ABS ASL
          address = mmu->readMem(PC);
          PC++;
          address |= mmu->readMem(PC) << 8;
          PC++;
          value16 = mmu->readMem(address) << 1;
          P.bits.C = value16 > 0xFF;
          value16 &= 0xFF;
          mmu->writeMem(address, value16);
          P.bits.Z = value16 == 0;
          P.bits.S = value16 > 0x7F;
          ticks += 6;
        break;

        case 0x0F :  // ZPR BBR
          value8 = mmu->readMem(mmu->readMem(PC));
          PC++;
          address = mmu->readMem(PC);
          PC++;
          if (address & SIGN)
            address |= 0xFF00;  // jump backward
          if (!(value8 & 1))
            PC += address;
          ticks += 5;
        break;

        case 0x10 :  // REL BPL
          address = mmu->readMem(PC);
          PC++;
          if (!P.bits.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 = mmu->readMem(PC);
          PC++;
          address = mmu->readMem(value8);
          value8++;
          address |= mmu->readMem(value8) << 8;
          ticks += (((address & 0xFF) + Y) & 0xFF00) ? 6 : 5;  // page crossing
          address += Y;
          A |= mmu->readMem(address);
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
        break;

        case 0x12 :  // IZP ORA
          value8 = mmu->readMem(PC);
          PC++;
          address = mmu->readMem(value8);
          value8++;
          address |= mmu->readMem(value8) << 8;
          A |= mmu->readMem(address);
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 5;
        break;

        case 0x13 :  // IMP NOP
          ticks++;
        break;

        case 0x14 :  // ZPG TRB
          address = mmu->readMem(PC);
          PC++;
          value8 = mmu->readMem(address);
          mmu->writeMem(address, value8 & ~A);
          P.bits.Z = (value8 & A) == 0;
          ticks += 5;
        break;

        case 0x15 :  // ZPX ORA
          A |= mmu->readMem(mmu->readMem(PC) + X);
          PC++;
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 4;
        break;

        case 0x16 :  // ZPX ASL
          address = mmu->readMem(PC) + X;
          PC++;
          value16 = mmu->readMem(address) << 1;
          mmu->writeMem(address, value16 & 0xFF);
          P.bits.C = value16 > 0xFF;
          P.bits.Z = value16 == 0;
          P.bits.S = (value16 & 0xFF) > 0x7F;
          ticks += 6;
        break;

        case 0x17 :  // ZPG RMB
          address = mmu->readMem(PC);
          PC++;
          mmu->writeMem(address, mmu->readMem(address) & ~2);
          ticks += 5;
        break;

        case 0x18 :  // IMP CLC
          P.bits.C = 0;
          ticks += 2;
        break;

        case 0x19 :  // ABY ORA
          address = mmu->readMem(PC);
          PC++;
          ticks += ((address + Y) & 0xFF00) ? 5 : 4;  // page crossing
          address |= mmu->readMem(PC) << 8;
          PC++;
          address += Y;
          A |= mmu->readMem(address);
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
        break;

        case 0x1A :  // ACC INC
          A++;
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 2;
        break;

        case 0x1B :  // IMP NOP
          ticks++;
        break;

        case 0x1C :  // ABS TRB
          address = mmu->readMem(PC);
          PC++;
          address |= mmu->readMem(PC) << 8;
          PC++;
          value8 = mmu->readMem(address);
          P.bits.Z = (value8 & A) == 0;
          mmu->writeMem(address, value8 & ~A);
          ticks += 6;
        break;

        case 0x1D :  // ABX ORA
          address = mmu->readMem(PC);
          PC++;
          ticks += ((address + X) & 0xFF00) ? 5 : 4;  // page crossing
          address |= mmu->readMem(PC) << 8;
          PC++;
          address += X;
          A |= mmu->readMem(address);
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
        break;

        case 0x1E :  // ABX ASL
          address = mmu->readMem(PC);
          PC++;
          ticks += address + X > 0xFF ? 7 : 6;
          address |= mmu->readMem(PC) << 8;
          PC++;
          address += X;
          value16 = mmu->readMem(address) << 1;
          P.bits.C = value16 > 0xFF;
          value16 &= 0xFF;
          mmu->writeMem(address, value16);
          P.bits.Z = value16 == 0;
          P.bits.S = value16 > 0x7F;
        break;

        case 0x1F :  // ZPR BBR
          value8 = mmu->readMem(mmu->readMem(PC));
          PC++;
          address = mmu->readMem(PC);
          PC++;
          if (address & SIGN)
            address |= 0xFF00;  // jump backward
          if (!(value8 & 2))
            PC += address;
          ticks += 5;
        break;

        case 0x20 :  // ABS JSR
          address = mmu->readMem(PC);
          PC++;
          address |= mmu->readMem(PC) << 8;
          mmu->writeMem(0x100 + SP, (PC >> 8) & 0xFF);
          SP--;
          mmu->writeMem(0x100 + SP, PC & 0xFF);
          SP--;
          PC = address;
          ticks += 6;
        break;

        case 0x21 :  // IZX AND
          value8 = mmu->readMem(PC) + X;
          PC++;
          address = mmu->readMem(value8);
          value8++;
          address |= mmu->readMem(value8) << 8;
          A &= mmu->readMem(address);
          P.bits.Z = A == 0;
          P.bits.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 = mmu->readMem(PC);
          PC++;
          value8 = mmu->readMem(address);
          P.bits.Z = (A & value8) == 0;
          P.byte = (P.byte & 0x3F) | (value8 & 0xC0);
          ticks += 3;
        break;

        case 0x25 :  // ZPG AND
          A &= mmu->readMem(mmu->readMem(PC));
          PC++;
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 3;
        break;

        case 0x26 :  // ZPG ROL
          address = mmu->readMem(PC);
          PC++;
          value16 = (mmu->readMem(address) << 1) | P.bits.C;
          P.bits.C = (value16 & 0x100) != 0;
          value16 &= 0xFF;
          mmu->writeMem(address, value16);
          P.bits.Z = value16 == 0;
          P.bits.S = value16 > 0x7F;
          ticks += 5;
        break;

        case 0x27 :  // ZPG RMB
          address = mmu->readMem(PC);
          PC++;
          mmu->writeMem(address, mmu->readMem(address) & ~4);
          ticks += 5;
        break;

        case 0x28 :  // IMP PLP
          SP++;
          P.byte = mmu->readMem(0x100 + SP) | UNDEF;
          ticks += 4;
        break;

        case 0x29 :  // IMM AND
          A &= mmu->readMem(PC);
          PC++;
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 2;
        break;

        case 0x2A :  // ACC ROL
          value16 = (A << 1) | P.bits.C;
          P.bits.C = (value16 & 0x100) != 0;
          A = value16 & 0xFF;
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 2;
        break;

        case 0x2B :  // IMP NOP
          ticks++;
        break;

        case 0x2C :  // ABS BIT
          address = mmu->readMem(PC);
          PC++;
          address |= mmu->readMem(PC) << 8;
          PC++;
          value8 = mmu->readMem(address);
          P.bits.Z = (A & value8) == 0;
          P.byte = (P.byte & 0x3F) | (value8 & 0xC0);
          ticks += 4;
        break;

        case 0x2D :  // ABS AND
          address = mmu->readMem(PC);
          PC++;
          address |= mmu->readMem(PC) << 8;
          PC++;
          A &= mmu->readMem(address);
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 4;
        break;

        case 0x2E :  // ABS ROL
          address = mmu->readMem(PC);
          PC++;
          address |= mmu->readMem(PC) << 8;
          PC++;
          value16 = (mmu->readMem(address) << 1) | P.bits.C;
          P.bits.C = (value16 & 0x100) != 0;
          value16 &= 0xFF;
          mmu->writeMem(address, value16);
          P.bits.Z = value16 == 0;
          P.bits.S = value16 > 0x7F;
          ticks += 6;
        break;

        case 0x2F :  // ZPR BBR
          value8 = mmu->readMem(mmu->readMem(PC));
          PC++;
          address = mmu->readMem(PC);
          PC++;
          if (address & SIGN)
            address |= 0xFF00;  // jump backward
          if (!(value8 & 4))
            PC += address;
          ticks += 5;
        break;

        case 0x30 :  // REL BMI
          address = mmu->readMem(PC);
          PC++;
          if (P.bits.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 = mmu->readMem(PC);
          PC++;
          address = mmu->readMem(value8);
          value8++;
          address |= mmu->readMem(value8) << 8;
          ticks += (((address & 0xFF) + Y) & 0xFF00) ? 6 : 5;  // page crossing
          address += Y;
          A &= mmu->readMem(address);
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
        break;

        case 0x32 :  // IZP AND
          value8 = mmu->readMem(PC);
          PC++;
          address = mmu->readMem(value8);
          value8++;
          address |= mmu->readMem(value8) << 8;
          A &= mmu->readMem(address);
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 5;
        break;

        case 0x33 :  // IMP NOP
          ticks++;
        break;

        case 0x34 :  // ZPX BIT
          address = (mmu->readMem(PC) + X) & 0xFF;
          PC++;
          value8 = mmu->readMem(address);
          P.bits.Z = (A & value8) == 0;
          P.byte = (P.byte & 0x3F) | (value8 & 0xC0);
          ticks += 4;
        break;

        case 0x35 :  // ZPX AND
          address = (mmu->readMem(PC) + X) & 0xFF;
          PC++;
          A &= mmu->readMem(address);
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 4;
        break;

        case 0x36 :  // ZPX ROL
          address = (mmu->readMem(PC) + X) & 0xFF;
          PC++;
          value16 = (mmu->readMem(address) << 1) | P.bits.C;
          P.bits.C = value16 > 0xFF;
          value16 &= 0xFF;
          mmu->writeMem(address, value16);
          P.bits.Z = value16 == 0;
          P.bits.S = value16 > 0x7F;
          ticks += 6;
        break;

        case 0x37 :  // ZPG RMB
          address = mmu->readMem(PC);
          PC++;
          mmu->writeMem(address, mmu->readMem(address) & ~8);
          ticks += 5;
        break;

        case 0x38 :  // IMP SEC
          P.bits.C = 1;
          ticks += 2;
        break;

        case 0x39 :  // ABY AND
          address = mmu->readMem(PC);
          PC++;
          ticks += ((address + Y) & 0xFF00) ? 5 : 4;  // page crossing
          address |= mmu->readMem(PC) << 8;
          PC++;
          address += Y;
          A &= mmu->readMem(address);
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
        break;

        case 0x3A :  // ACC DEC
          --A;
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 2;
        break;

        case 0x3B :  // IMP NOP
          ticks++;
        break;

        case 0x3C :  // ABX BIT
          ticks += mmu->readMem(PC) + X > 0xFF ? 5 : 4;
          address = mmu->readMem(PC);
          PC++;
          address |= (mmu->readMem(PC) << 8) + X;
          PC++;
          value8 = mmu->readMem(address);
          P.bits.Z = (A & value8) == 0;
          P.byte = (P.byte & 0x3F) | (value8 & 0xC0);
        break;

        case 0x3D :  // ABX AND
          address = mmu->readMem(PC);
          PC++;
          ticks += ((address + X) & 0xFF00) ? 5 : 4;  // page crossing
          address |= mmu->readMem(PC) << 8;
          PC++;
          address += X;
          A &= mmu->readMem(address);
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
        break;

        case 0x3E :  // ABX ROL
          address = mmu->readMem(PC);
          PC++;
          ticks += address + X > 0xFF ? 7 : 6;
          address |= mmu->readMem(PC) << 8;
          PC++;
          address += X;
          value16 = (mmu->readMem(address) << 1) | P.bits.C;
          P.bits.C = value16 > 0xFF;
          value16 &= 0xFF;
          mmu->writeMem(address, value16);
          P.bits.Z = value16 == 0;
          P.bits.S = value16 > 0x7F;
        break;

        case 0x3F :  // ZPR BBR
          value8 = mmu->readMem(mmu->readMem(PC));
          PC++;
          address = mmu->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 = mmu->readMem(0x100 + SP);
          SP++;
          PC = mmu->readMem(0x100 + SP);
          SP++;
          PC |= mmu->readMem(0x100 + SP) << 8;
          ticks += 6;
        break;

        case 0x41 :  // IZX EOR
          value8 = mmu->readMem(PC) + X;
          PC++;
          address = mmu->readMem(value8);
          value8++;
          address |= mmu->readMem(value8) << 8;
          A ^= mmu->readMem(address);
          P.bits.Z = A == 0;
          P.bits.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 = mmu->readMem(PC);
          PC++;
          A ^= mmu->readMem(address);
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 3;
        break;

        case 0x46 :  // ZPG LSR
          address = mmu->readMem(PC);
          PC++;
          value8 = mmu->readMem(address);
          P.bits.C = (value8 & 1) != 0;
          value8 = value8 >> 1;
          mmu->writeMem(address, value8);
          P.bits.Z = value8 == 0;
          P.bits.S = value8 > 0x7F;
          ticks += 5;
        break;

        case 0x47 :  // ZPG RMB
          address = mmu->readMem(PC);
          PC++;
          mmu->writeMem(address, mmu->readMem(address) & ~16);
          ticks += 5;
        break;

        case 0x48 :  // IMP PHA
          mmu->writeMem(0x100 + SP, A);
          SP--;
          ticks += 3;
        break;

        case 0x49 :  // IMM EOR
          A ^= mmu->readMem(PC);
          PC++;
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 2;
        break;

        case 0x4A :  // ACC LSR
          P.bits.C = (A & 1) != 0;
          A = A >> 1;
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 2;
        break;

        case 0x4B :  // IMP NOP
          ticks++;
        break;

        case 0x4C :  // ABS JMP
          PC = mmu->readMem(PC) | (mmu->readMem(PC + 1) << 8);
          ticks += 3;
        break;

        case 0x4D :  // ABS EOR
          address = mmu->readMem(PC);
          PC++;
          address |= mmu->readMem(PC) << 8;
          PC++;
          A ^= mmu->readMem(address);
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 4;
        break;

        case 0x4E :  // ABS LSR
          address = mmu->readMem(PC);
          PC++;
          address |= mmu->readMem(PC) << 8;
          PC++;
          value8 = mmu->readMem(address);
          P.bits.C = (value8 & 1) != 0;
          value8 = value8 >> 1;
          mmu->writeMem(address, value8);
          P.bits.Z = value8 == 0;
          P.bits.S = value8 > 0x7F;
          ticks += 6;
        break;

        case 0x4F :  // ZPR BBR
          value8 = mmu->readMem(mmu->readMem(PC));
          PC++;
          address = mmu->readMem(PC);
          PC++;
          if (address & SIGN)
            address |= 0xFF00;  // jump backward
          if (!(value8 & 16))
            PC += address;
          ticks += 5;
        break;

        case 0x50 :  // REL BVC
          address = mmu->readMem(PC);
          PC++;
          if (!P.bits.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 = mmu->readMem(PC);
          PC++;
          address = mmu->readMem(value8);
          value8++;
          address |= mmu->readMem(value8) << 8;
          ticks += (((address & 0xFF) + Y) & 0xFF00) ? 6 : 5;  // page crossing
          A ^= mmu->readMem(address + Y);
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
        break;

        case 0x52 :  // IZP EOR
          value8 = mmu->readMem(PC);
          PC++;
          address = mmu->readMem(value8);
          value8++;
          address |= mmu->readMem(value8) << 8;
          A ^= mmu->readMem(address);
          P.bits.Z = A == 0;
          P.bits.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 = (mmu->readMem(PC) + X) & 0xFF;
          PC++;
          A ^= mmu->readMem(address);
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 4;
        break;

        case 0x56 :  // ZPX LSR
          address = (mmu->readMem(PC) + X) & 0xFF;
          PC++;
          value8 = mmu->readMem(address);
          P.bits.C = (value8 & 1) != 0;
          value8 = value8 >> 1;
          mmu->writeMem(address, value8);
          P.bits.Z = value8 == 0;
          P.bits.S = value8 > 0x7F;
          ticks += 6;
        break;

        case 0x57 :  // ZPG RMB
          address = mmu->readMem(PC);
          PC++;
          mmu->writeMem(address, mmu->readMem(address) & ~32);
          ticks += 5;
        break;

        case 0x58 :  // IMP CLI
          P.bits.I = 0;
          ticks += 2;
        break;

        case 0x59 :  // ABY EOR
          address = mmu->readMem(PC);
          PC++;
          ticks += ((address + Y) & 0xFF00) ? 5 : 4;  // page crossing
          address |= mmu->readMem(PC) << 8;
          PC++;
          address += Y;
          A ^= mmu->readMem(address);
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
        break;

        case 0x5A :  // IMP PHY
          mmu->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 = mmu->readMem(PC);
          PC++;
          ticks += ((address + X) & 0xFF00) ? 5 : 4;  // page crossing
          address |= mmu->readMem(PC) << 8;
          PC++;
          address += X;
          A ^= mmu->readMem(address);
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
        break;

        case 0x5E :  // ABX LSR
          address = mmu->readMem(PC);
          PC++;
          ticks += address + X > 0xFF ? 7 : 6;
          address |= mmu->readMem(PC) << 8;
          PC++;
          address += X;
          value8 = mmu->readMem(address);
          P.bits.C = (value8 & 1) != 0;
          value8 = value8 >> 1;
          mmu->writeMem(address, value8);
          P.bits.Z = value8 == 0;
          P.bits.S = value8 > 0x7F;
        break;

        case 0x5F :  // ZPR BBR
          value8 = mmu->readMem(mmu->readMem(PC));
          PC++;
          address = mmu->readMem(PC);
          PC++;
          if (address & SIGN)
            address |= 0xFF00;  // jump backward
          if (!(value8 & 32))
            PC += address;
          ticks += 5;
        break;

        case 0x60 :  // IMP RTS
          SP++;
          PC = mmu->readMem(0x100 + SP);
          SP++;
          PC |= mmu->readMem(0x100 + SP) << 8;
          PC++;
          ticks += 6;
        break;

        case 0x61 :  // IZX ADC
          value8 = mmu->readMem(PC) + X;
          PC++;
          address = mmu->readMem(value8);
          value8++;
          address |= mmu->readMem(value8) << 8;
          value8 = mmu->readMem(address);
          value16 = A + value8 + P.bits.C;
          P.bits.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0;
          if (P.bits.D)
            value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3;
          P.bits.C = value16 > 0xFF;
          A = value16 & 0xFF;
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 6;
        break;

        case 0x62 :  // IMM NOP
          PC++;
          ticks += 2;
        break;

        case 0x63 :  // IMP NOP
          ticks++;
        break;

        case 0x64 :  // ZPG STZ
          mmu->writeMem(mmu->readMem(PC), 0x00);
          PC++;
          ticks += 3;
        break;

        case 0x65 :  // ZPG ADC
          address = mmu->readMem(PC);
          PC++;
          value8 = mmu->readMem(address);
          value16 = A + value8 + P.bits.C;
          P.bits.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0;
          if (P.bits.D)
            value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3;
          P.bits.C = value16 > 0xFF;
          A = value16 & 0xFF;
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 3;
        break;

        case 0x66 :  // ZPG ROR
          address = mmu->readMem(PC);
          PC++;
          value8 = mmu->readMem(address);
          value16 = (value8 >> 1) | (P.bits.C << 7);
          P.bits.C = (value8 & 0x1) != 0;
          value16 &= 0xFF;
          mmu->writeMem(address, value16);
          P.bits.Z = value16 == 0;
          P.bits.S = value16 > 0x7F;
          ticks += 5;
        break;

        case 0x67 :  // ZPG RMB
          address = mmu->readMem(PC);
          PC++;
          mmu->writeMem(address, mmu->readMem(address) & ~64);
          ticks += 5;
        break;

        case 0x68 :  // IMP PLA
          SP++;
          A = mmu->readMem(0x100 + SP);
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 4;
        break;

        case 0x69 :  // IMM ADC
          value8 = mmu->readMem(PC);
          PC++;
          value16 = A + value8 + P.bits.C;
          P.bits.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0;
          if (P.bits.D)
            value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3;
          P.bits.C = value16 > 0xFF;
          A = value16 & 0xFF;
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 2;
        break;

        case 0x6A :  // ACC ROR
          value16 = (A >> 1) | (P.bits.C << 7);
          P.bits.C = (A & 0x1) != 0;
          A = value16 & 0xFF;
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 2;
        break;

        case 0x6B :  // IMP NOP
          ticks++;
        break;

        case 0x6C :  // IND JMP
          address = mmu->readMem(PC) | mmu->readMem(PC + 1) << 8;
          PC = mmu->readMem(address) | (mmu->readMem(address + 1) << 8);
          ticks += 5;
        break;

        case 0x6D :  // ABS ADC
          address = mmu->readMem(PC);
          PC++;
          address |= mmu->readMem(PC) << 8;
          PC++;
          value8 = mmu->readMem(address);
          value16 = A + value8 + P.bits.C;
          P.bits.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0;
          if (P.bits.D)
            value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3;
          P.bits.C = value16 > 0xFF;
          A = value16 & 0xFF;
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 4;
        break;

        case 0x6E :  // ABS ROR
          address = mmu->readMem(PC);
          PC++;
          address |= mmu->readMem(PC) << 8;
          PC++;
          value8 = mmu->readMem(address);
          value16 = (value8 >> 1) | (P.bits.C << 7);
          P.bits.C = (value8 & 0x1) != 0;
          value16 = value16 & 0xFF;
          mmu->writeMem(address, value16);
          P.bits.Z = value16 == 0;
          P.bits.S = value16 > 0x7F;
          ticks += 6;
        break;

        case 0x6F :  // ZPR BBR
          value8 = mmu->readMem(mmu->readMem(PC));
          PC++;
          address = mmu->readMem(PC);
          PC++;
          if (address & SIGN)
            address |= 0xFF00;  // jump backward
          if (!(value8 & 64))
            PC += address;
          ticks += 5;
        break;

        case 0x70 :  // REL BVS
          address = mmu->readMem(PC);
          PC++;
          if (P.bits.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 = mmu->readMem(PC);
          PC++;
          address = mmu->readMem(value8);
          if ((address + Y) & 0xFF00)  // page crossing
            ticks++;
          value8++;
          address |= mmu->readMem(value8) << 8;
          address += Y;
          value8 = mmu->readMem(address);
          value16 = A + value8 + P.bits.C;
          P.bits.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0;
          if (P.bits.D)
            value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3;
          P.bits.C = value16 > 0xFF;
          A = value16 & 0xFF;
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 5;
        break;

        case 0x72 :  // IZP ADC
          value8 = mmu->readMem(PC);
          PC++;
          address = mmu->readMem(value8);
          value8++;
          address |= mmu->readMem(value8) << 8;
          value8 = mmu->readMem(address);
          value16 = A + value8 + P.bits.C;
          P.bits.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0;
          if (P.bits.D)
            value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3;
          P.bits.C = value16 > 0xFF;
          A = value16 & 0xFF;
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 5;
        break;

        case 0x73 :  // IMP NOP
          ticks++;
        break;

        case 0x74 :  // ZPX STZ
          value8 = mmu->readMem(PC) + X;  // 8bit -> zp wrap around
          PC++;
          mmu->writeMem(value8, 0x00);
          ticks += 4;
        break;

        case 0x75 :  // ZPX ADC
          address = (mmu->readMem(PC) + X) & 0xFF;
          PC++;
          value8 = mmu->readMem(address);
          value16 = A + value8 + P.bits.C;
          P.bits.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0;
          if (P.bits.D)
            value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3;
          P.bits.C = value16 > 0xFF;
          A = value16 & 0xFF;
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 4;
        break;

        case 0x76 :  // ZPX ROR
          address = (mmu->readMem(PC) + X) & 0xFF;
          PC++;
          value8 = mmu->readMem(address);
          value16 = (value8 >> 1) | (P.bits.C << 7);
          P.bits.C = (value8 & 0x1) != 0;
          value16 = value16 & 0xFF;
          mmu->writeMem(address, value16);
          P.bits.Z = value16 == 0;
          P.bits.S = value16 > 0x7F;
          ticks += 6;
        break;

        case 0x77 :  // ZPG RMB
          address = mmu->readMem(PC);
          PC++;
          mmu->writeMem(address, mmu->readMem(address) & ~128);
          ticks += 5;
        break;

        case 0x78 :  // IMP SEI
          P.bits.I = 1;
          ticks += 2;
        break;

        case 0x79 :  // ABY ADC
          if ((mmu->readMem(PC) + Y) & 0xFF00)
            ticks++;
          address = mmu->readMem(PC);
          PC++;
          address |= mmu->readMem(PC) << 8;
          PC++;
          address += Y;
          value8 = mmu->readMem(address);
          value16 = A + value8 + P.bits.C;
          P.bits.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0;
          if (P.bits.D)
            value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3;
          P.bits.C = value16 > 0xFF;
          A = value16 & 0xFF;
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 4;
        break;

        case 0x7A :  // IMP PLY
          SP++;
          Y = mmu->readMem(0x100 + SP);
          P.bits.Z = Y == 0;
          P.bits.S = Y > 0x7F;
          ticks += 4;
        break;

        case 0x7B :  // IMP NOP
          ticks++;
        break;

        case 0x7C :  // IAX JMP
          ticks += ((PC & 0xFF) + X) > 0xFF ? 7 : 6;
          address = (mmu->readMem((PC + 1) & 0xFFFF) << 8) + mmu->readMem(PC) + X;
          PC = (mmu->readMem(address) | (mmu->readMem((address + 1) & 0xFFFF) << 8));
        break;

        case 0x7D :  // ABX ADC
          if ((mmu->readMem(PC) + X) & 0xFF00)
            ticks++;
          address = mmu->readMem(PC);
          PC++;
          address |= mmu->readMem(PC) << 8;
          PC++;
          address += X;
          value8 = mmu->readMem(address);
          value16 = A + value8 + P.bits.C;
          P.bits.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0;
          if (P.bits.D)
            value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3;
          P.bits.C = value16 > 0xFF;
          A = value16 & 0xFF;
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 4;
        break;

        case 0x7E :  // ABX ROR
          address = mmu->readMem(PC);
          PC++;
          ticks += address + X > 0xFF ? 7 : 6;
          address |= mmu->readMem(PC) << 8;
          PC++;
          address += X;
          value8 = mmu->readMem(address);
          value16 = (value8 >> 1) | (P.bits.C << 7);
          P.bits.C = (value8 & 0x1) != 0;
          value16 = value16 & 0xFF;
          mmu->writeMem(address, value16);
          P.bits.Z = value16 == 0;
          P.bits.S = value16 > 0x7F;
        break;

        case 0x7F :  // ZPR BBR
          value8 = mmu->readMem(mmu->readMem(PC));
          PC++;
          address = mmu->readMem(PC);
          PC++;
          if (address & SIGN)
            address |= 0xFF00;  // jump backward
          if (!(value8 & 128))
            PC += address;
          ticks += 5;
        break;

        case 0x80 :  // REL BRA
          address = mmu->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 = mmu->readMem(PC) + X;
          PC++;
          address = mmu->readMem(value8);
          value8++;
          address |= mmu->readMem(value8) << 8;
          mmu->writeMem(address, A);
          ticks += 6;
        break;

        case 0x82 :  // IMM NOP
          PC++;
          ticks += 2;
        break;

        case 0x83 :  // IMP NOP
          ticks++;
        break;

        case 0x84 :  // ZPG STY
          mmu->writeMem(mmu->readMem(PC), Y);
          PC++;
          ticks += 3;
        break;

        case 0x85 :  // ZPG STA
          mmu->writeMem(mmu->readMem(PC), A);
          PC++;
          ticks += 3;
        break;

        case 0x86 :  // ZPG STX
          mmu->writeMem(mmu->readMem(PC), X);
          PC++;
          ticks += 3;
        break;

        case 0x87 :  // ZPG SMB
          address = mmu->readMem(PC);
          PC++;
          mmu->writeMem(address, mmu->readMem(address) | 1);
          ticks += 5;
        break;

        case 0x88 :  // IMP DEY
          Y--;
          P.bits.Z = (Y & 0xFF) == 0;
          P.bits.S = (Y & SIGN) != 0;
          ticks += 2;
        break;

        case 0x89 :  // IMM BIT
          P.bits.Z = (A & mmu->readMem(PC)) == 0;
          PC++;
          ticks += 2;
        break;

        case 0x8A :  // IMP TXA
          A = X;
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 2;
        break;

        case 0x8B :  // IMP NOP
          ticks++;
        break;

        case 0x8C :  // ABS STY
          address = mmu->readMem(PC);
          PC++;
          address |= mmu->readMem(PC) << 8;
          PC++;
          mmu->writeMem(address, Y);
          ticks += 4;
        break;

        case 0x8D :  // ABS STA
          address = mmu->readMem(PC);
          PC++;
          address |= mmu->readMem(PC) << 8;
          PC++;
          mmu->writeMem(address, A);
          ticks += 4;
        break;

        case 0x8E :  // ABS STX
          address = mmu->readMem(PC);
          PC++;
          address |= mmu->readMem(PC) << 8;
          PC++;
          mmu->writeMem(address, X);
          ticks += 4;
        break;

        case 0x8F :  // ZPR BBS
          value8 = mmu->readMem(mmu->readMem(PC));
          PC++;
          address = mmu->readMem(PC);
          PC++;
          if (address & SIGN)
            address |= 0xFF00;  // jump backward
          if (value8 & 1)
            PC += address;
          ticks += 5;
        break;

        case 0x90 :  // REL BCC
          address = mmu->readMem(PC);
          PC++;
          if (!P.bits.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 = mmu->readMem(PC);
          PC++;
          address = mmu->readMem(value8);
          value8++;
          address |= mmu->readMem(value8) << 8;
          address += Y;
          mmu->writeMem(address, A);
          ticks += 6;
        break;

        case 0x92 :  // IZP STA
          value8 = mmu->readMem(PC);
          PC++;
          address = mmu->readMem(value8);
          value8++;
          address |= mmu->readMem(value8) << 8;
          mmu->writeMem(address, A);
          ticks += 5;
        break;

        case 0x93 :  // IMP NOP
          ticks++;
        break;

        case 0x94 :  // ZPX STY
          address = (mmu->readMem(PC) + X) & 0xFF;
          PC++;
          mmu->writeMem(address, Y);
          ticks += 4;
        break;

        case 0x95 :  // ZPX STA
          mmu->writeMem((mmu->readMem(PC) + X) & 0xFF, A);
          PC++;
          ticks += 4;
        break;

        case 0x96 :  // ZPY STX
          mmu->writeMem((mmu->readMem(PC) + Y) & 0xFF, X);
          PC++;
          ticks += 4;
        break;

        case 0x97 :  // ZPG SMB
          address = mmu->readMem(PC);
          PC++;
          mmu->writeMem(address, mmu->readMem(address) | 2);
          ticks += 5;
        break;

        case 0x98 :  // IMP TYA
          A = Y;
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 2;
        break;

        case 0x99 :  // ABY STA
          address = mmu->readMem(PC);
          PC++;
          address |= mmu->readMem(PC) << 8;
          PC++;
          address += Y;
          mmu->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 = mmu->readMem(PC);
          PC++;
          address |= mmu->readMem(PC) << 8;
          PC++;
          mmu->writeMem(address, 0x00);
          ticks += 4;
        break;

        case 0x9D :  // ABX STA
          address = mmu->readMem(PC);
          PC++;
          address |= mmu->readMem(PC) << 8;
          PC++;
          address += X;
          mmu->writeMem(address, A);
          ticks += 5;
        break;

        case 0x9E :  // ABX STZ
          ticks +=  mmu->readMem(PC) + X > 0xFF ? 6 : 5;
          address = mmu->readMem(PC);
          PC++;
          address |= mmu->readMem(PC) << 8;
          PC++;
          address += X;
          mmu->writeMem(address, 0x00);
        break;

        case 0x9F :  // ZPR BBS
          value8 = mmu->readMem(mmu->readMem(PC));
          PC++;
          address = mmu->readMem(PC);
          PC++;
          if (address & SIGN)
            address |= 0xFF00;  // jump backward
          if (value8 & 2)
            PC += address;
          ticks += 5;
        break;

        case 0xA0 :  // IMM LDY
          Y = mmu->readMem(PC);
          PC++;
          P.bits.Z = Y == 0;
          P.bits.S = Y > 0x7F;
          ticks += 2;
        break;

        case 0xA1 :  // IZX LDA
          value8 = mmu->readMem(PC) + X;
          PC++;
          address = mmu->readMem(value8);
          value8++;
          address |= mmu->readMem(value8) << 8;
          A = mmu->readMem(address);
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 6;
        break;

        case 0xA2 :  // IMM LDX
          address = PC;
          PC++;
          X = mmu->readMem(address);
          P.bits.Z = X == 0;
          P.bits.S = X > 0x7F;
          ticks += 2;
        break;

        case 0xA4 :  // ZPG LDY
          Y = mmu->readMem(mmu->readMem(PC));
          PC++;
          P.bits.Z = Y == 0;
          P.bits.S = Y > 0x7F;
          ticks += 3;
        break;

        case 0xA5 :  // ZPG LDA
          A = mmu->readMem(mmu->readMem(PC));
          PC++;
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 3;
        break;

        case 0xA6 :  // ZPG LDX
          X = mmu->readMem(mmu->readMem(PC));
          PC++;
          P.bits.Z = X == 0;
          P.bits.S = X > 0x7F;
          ticks += 3;
        break;

        case 0xA7 :  // ZPG SMB
          address = mmu->readMem(PC);
          PC++;
          mmu->writeMem(address, mmu->readMem(address) | 4);
          ticks += 5;
        break;

        case 0xA8 :  // IMP TAY
          Y = A;
          P.bits.Z = Y == 0;
          P.bits.S = Y > 0x7F;
          ticks += 2;
        break;

        case 0xA9 :  // IMM LDA
          A = mmu->readMem(PC);
          PC++;
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 2;
        break;

        case 0xAA :  // IMP TAX
          X = A;
          P.bits.Z = X == 0;
          P.bits.S = X > 0x7F;
          ticks += 2;
        break;

        case 0xAB :  // IMP NOP
          ticks++;
        break;

        case 0xAC :  // ABS LDY
          address = mmu->readMem(PC);
          PC++;
          address |= mmu->readMem(PC) << 8;
          PC++;
          Y = mmu->readMem(address);
          P.bits.Z = Y == 0;
          P.bits.S = Y > 0x7F;
          ticks += 4;
        break;

        case 0xAD :  // ABS LDA
          address = mmu->readMem(PC);
          PC++;
          address |= mmu->readMem(PC) << 8;
          PC++;
          A = mmu->readMem(address);
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 4;
        break;

        case 0xAE :  // ABS LDX
          address = mmu->readMem(PC);
          PC++;
          address |= mmu->readMem(PC) << 8;
          PC++;
          X = mmu->readMem(address);
          P.bits.Z = X == 0;
          P.bits.S = X > 0x7F;
          ticks += 4;
        break;

        case 0xAF :  // ZPR BBS
          value8 = mmu->readMem(mmu->readMem(PC));
          PC++;
          address = mmu->readMem(PC);
          PC++;
          if (address & SIGN)
            address |= 0xFF00;  // jump backward
          if (value8 & 4)
            PC += address;
          ticks += 5;
        break;

        case 0xB0 :  // REL BCS
          address = mmu->readMem(PC);
          PC++;
          if (P.bits.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 = mmu->readMem(PC);
          PC++;
          address = mmu->readMem(value8);
          value8++;
          address |= mmu->readMem(value8) << 8;
          A = mmu->readMem(address + Y);
          ticks += (((address & 0xFF) + Y) & 0xFF00) ? 6 : 5;  // page crossing
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
        break;

        case 0xB2 :  // IZP LDA
          value8 = mmu->readMem(PC);
          PC++;
          address = mmu->readMem(value8);
          value8++;
          address |= mmu->readMem(value8) << 8;
          A = mmu->readMem(address);
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 5;
        break;

        case 0xB3 :  // IMP NOP
          ticks++;
        break;

        case 0xB4 :  // ZPX LDY
          address = (mmu->readMem(PC) + X) & 0xFF;
          PC++;
          Y = mmu->readMem(address);
          P.bits.Z = Y == 0;
          P.bits.S = Y > 0x7F;
          ticks += 4;
        break;

        case 0xB5 :  // ZPX LDA
          address = (mmu->readMem(PC) + X) & 0xFF;
          PC++;
          A = mmu->readMem(address);
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 4;
        break;

        case 0xB6 :  // ZPY LDX
          address = (mmu->readMem(PC) + Y) & 0xFF;
          PC++;
          X = mmu->readMem(address);
          P.bits.Z = X == 0;
          P.bits.S = X > 0x7F;
          ticks += 4;
        break;

        case 0xB7 :  // ZPG SMB
          address = mmu->readMem(PC);
          PC++;
          mmu->writeMem(address, mmu->readMem(address) | 8);
          ticks += 5;
        break;

        case 0xB8 :  // IMP CLV
          P.bits.V = 0;
          ticks += 2;
        break;

        case 0xB9 :  // ABY LDA
          address = mmu->readMem(PC);
          PC++;
          ticks += ((address + Y) & 0xFF00) ? 5 : 4;  // page crossing
          address |= mmu->readMem(PC) << 8;
          PC++;
          address += Y;
          A = mmu->readMem(address);
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
        break;

        case 0xBA :  // IMP TSX
          X = SP;
          P.bits.Z = X == 0;
          P.bits.S = X > 0x7F;
          ticks += 2;
        break;

        case 0xBB :  // IMP NOP
          ticks++;
        break;

        case 0xBC :  // ABX LDY
          address = mmu->readMem(PC);
          PC++;
          ticks += ((address + X) & 0xFF00) ? 5 : 4;  // page crossing
          address |= mmu->readMem(PC) << 8;
          PC++;
          address += X;
          Y = mmu->readMem(address);
          P.bits.Z = Y == 0;
          P.bits.S = Y > 0x7F;
        break;

        case 0xBD :  // ABX LDA
          address = mmu->readMem(PC);
          PC++;
          ticks += ((address + X) & 0xFF00) ? 5 : 4;  // page crossing
          address |= mmu->readMem(PC) << 8;
          PC++;
          address += X;
          A = mmu->readMem(address);
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
        break;

        case 0xBE :  // ABY LDX
          address = mmu->readMem(PC);
          PC++;
          ticks += ((address + Y) & 0xFF00) ? 5 : 4;  // page crossing
          address |= mmu->readMem(PC) << 8;
          PC++;
          address += Y;
          X = mmu->readMem(address);
          P.bits.Z = X == 0;
          P.bits.S = X > 0x7F;
        break;

        case 0xBF :  // ZPR BBS
          value8 = mmu->readMem(mmu->readMem(PC));
          PC++;
          address = mmu->readMem(PC);
          PC++;
          if (address & SIGN)
            address |= 0xFF00;  // jump backward
          if (value8 & 8)
            PC += address;
          ticks += 5;
        break;

        case 0xC0 :  // IMM CPY
          value8 = mmu->readMem(PC);
          PC++;
          P.bits.Z = ((Y - value8) & 0xFF) == 0;
          P.bits.S = ((Y - value8) & SIGN) != 0;
          P.bits.C = (Y >= value8) != 0;
          ticks += 2;
        break;

        case 0xC1 :  // IZX CMP
          value8 = mmu->readMem(PC) + X;
          PC++;
          address = mmu->readMem(value8);
          value8++;
          address |= mmu->readMem(value8) << 8;
          value8 = mmu->readMem(address);
          P.bits.Z = ((A - value8) & 0xFF) == 0;
          P.bits.S = ((A - value8) & SIGN) != 0;
          P.bits.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 = mmu->readMem(mmu->readMem(PC));
          PC++;
          P.bits.Z = ((Y - value8) & 0xFF) == 0;
          P.bits.S = ((Y - value8) & SIGN) != 0;
          P.bits.C = (Y >= value8) != 0;
          ticks += 3;
        break;

        case 0xC5 :  // ZPG CMP
          value8 = mmu->readMem(mmu->readMem(PC));
          PC++;
          P.bits.Z = ((A - value8) & 0xFF) == 0;
          P.bits.S = ((A - value8) & SIGN) != 0;
          P.bits.C = (A >= value8) != 0;
          ticks += 3;
        break;

        case 0xC6 :  // ZPG DEC
          address = mmu->readMem(PC);
          PC++;
          value8 = mmu->readMem(address);
          --value8;
          mmu->writeMem(address, value8);
          P.bits.Z = value8 == 0;
          P.bits.S = value8 > 0x7F;
          ticks += 5;
        break;

        case 0xC7 :  // ZPG SMB
          address = mmu->readMem(PC);
          PC++;
          mmu->writeMem(address, mmu->readMem(address) | 16);
          ticks += 5;
        break;

        case 0xC8 :  // IMP INY
          Y++;
          P.bits.Z = Y  == 0;
          P.bits.S = Y > 0x7F;
          ticks += 2;
        break;

        case 0xC9 :  // IMM CMP
          value8 = mmu->readMem(PC);
          PC++;
          P.bits.Z = ((A - value8) & 0xFF) == 0;
          P.bits.S = ((A - value8) & SIGN) != 0;
          P.bits.C = (A >= value8) != 0;
          ticks += 2;
        break;

        case 0xCA :  // IMP DEX
          X--;
          P.bits.Z = (X & 0xFF) == 0;
          P.bits.S = X > 0x7F;
          ticks += 2;
        break;

        case 0xCB :  // IMP WAI
          state = wait;
          ticks += 3;
        break;

        case 0xCC :  // ABS CPY
          address = mmu->readMem(PC);
          PC++;
          address |= mmu->readMem(PC) << 8;
          PC++;
          value8 = mmu->readMem(address);
          P.bits.Z = ((Y - value8) & 0xFF) == 0;
          P.bits.S = ((Y - value8) & SIGN) != 0;
          P.bits.C = (Y >= value8) != 0;
          ticks += 4;
        break;

        case 0xCD :  // ABS CMP
          address = mmu->readMem(PC);
          PC++;
          address |= mmu->readMem(PC) << 8;
          PC++;
          value8 = mmu->readMem(address);
          P.bits.Z = ((A - value8) & 0xFF) == 0;
          P.bits.S = ((A - value8) & SIGN) != 0;
          P.bits.C = (A >= value8) != 0;
          ticks += 4;
        break;

        case 0xCE :  // ABS DEC
          address = mmu->readMem(PC);
          PC++;
          address |= mmu->readMem(PC) << 8;
          PC++;
          value8 = mmu->readMem(address);
          value8--;
          mmu->writeMem(address, value8);
          P.bits.Z = value8 == 0;
          P.bits.S = value8 > 0x7F;
          ticks += 3;
        break;

        case 0xCF :  // ZPR BBS
          value8 = mmu->readMem(mmu->readMem(PC));
          PC++;
          address = mmu->readMem(PC);
          PC++;
          if (address & SIGN)
            address |= 0xFF00;  // jump backward
          if (value8 & 16)
            PC += address;
          ticks += 5;
        break;

        case 0xD0 :  // REL BNE
          address = mmu->readMem(PC);
          PC++;
          if (!P.bits.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 = mmu->readMem(PC);
          PC++;
          address = mmu->readMem(value8);
          ticks += ((address + Y) & 0xFF00) ? 6 : 5;  // page crossing
          value8++;
          address |= mmu->readMem(value8) << 8;
          address += Y;
          value8 = mmu->readMem(address);
          P.bits.Z = ((A - value8) & 0xFF) == 0;
          P.bits.S = ((A - value8) & SIGN) != 0;
          P.bits.C = (A >= value8) != 0;
        break;

        case 0xD2 :  // IZP CMP
          value8 = mmu->readMem(PC);
          PC++;
          address = mmu->readMem(value8);
          value8++;
          address |= mmu->readMem(value8) << 8;
          value8 = mmu->readMem(address);
          P.bits.Z = ((A - value8) & 0xFF) == 0;
          P.bits.S = ((A - value8) & SIGN) != 0;
          P.bits.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 = (mmu->readMem(PC) + X) & 0xFF;
          PC++;
          value8 = mmu->readMem(address);
          P.bits.Z = ((A - value8) & 0xFF) == 0;
          P.bits.S = ((A - value8) & SIGN) != 0;
          P.bits.C = (A >= value8) != 0;
          ticks += 4;
        break;

        case 0xD6 :  // ZPX DEC
          address = (mmu->readMem(PC) + X) & 0xFF;
          PC++;
          value8 = mmu->readMem(address);
          value8--;
          mmu->writeMem(address, value8);
          P.bits.Z = value8 == 0;
          P.bits.S = value8 > 0x7F;
          ticks += 6;
        break;

        case 0xD7 :  // ZPG SMB
          address = mmu->readMem(PC);
          PC++;
          mmu->writeMem(address, mmu->readMem(address) | 32);
          ticks += 5;
        break;

        case 0xD8 :  // IMP CLD
          P.bits.D = 0;
          ticks += 2;
        break;

        case 0xD9 :  // ABY CMP
          address = mmu->readMem(PC);
          PC++;
          ticks += ((address + Y) & 0xFF00) ? 5 : 4;  // page crossing
          address |= mmu->readMem(PC) << 8;
          PC++;
          address += Y;
          value8 = mmu->readMem(address);
          P.bits.Z = ((A - value8) & 0xFF) == 0;
          P.bits.S = ((A - value8) & SIGN) != 0;
          P.bits.C = (A >= value8) != 0;
        break;

        case 0xDA :  // IMP PHX
          mmu->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 = mmu->readMem(PC);
          PC++;
          ticks += ((address + X) & 0xFF00) ? 5 : 4;  // page crossing
          address |= mmu->readMem(PC) << 8;
          PC++;
          address += X;
          value8 = mmu->readMem(address);
          P.bits.Z = ((A - value8) & 0xFF) == 0;
          P.bits.S = ((A - value8) & SIGN) != 0;
          P.bits.C = (A >= value8) != 0;
        break;

        case 0xDE :  // ABX DEC
          address = mmu->readMem(PC);
          PC++;
          address |= mmu->readMem(PC) << 8;
          PC++;
          address += X;
          value8 = mmu->readMem(address);
          value8--;
          mmu->writeMem(address, value8);
          P.bits.Z = value8 == 0;
          P.bits.S = (value8 & SIGN) != 0;
          ticks += 7;
        break;

        case 0xDF :  // ZPR BBS
          value8 = mmu->readMem(mmu->readMem(PC));
          PC++;
          address = mmu->readMem(PC);
          PC++;
          if (address & SIGN)
            address |= 0xFF00;  // jump backward
          if (value8 & 32)
            PC += address;
          ticks += 5;
        break;

        case 0xE0 :  // IMM CPX
          value8 = mmu->readMem(PC);
          PC++;
          P.bits.Z = ((X - value8) & 0xFF) == 0;
          P.bits.S = ((X - value8) & SIGN) != 0;
          P.bits.C = (X >= value8) != 0;
          ticks += 2;
        break;

        case 0xE1 :  // IZX SBC
          value8 = mmu->readMem(PC) + X;
          PC++;
          address = mmu->readMem(value8);
          value8++;
          address |= mmu->readMem(value8) << 8;
          value8 = mmu->readMem(address);
          value8 ^= 0xFF;
          if (P.bits.D)
            value8 -= 0x0066;
          value16 = A + value8 + P.bits.C;
          P.bits.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0;
          if (P.bits.D)
            value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3;
          P.bits.C = value16 > 0xFF;
          A = value16 & 0xFF;
          P.bits.Z = A == 0;
          P.bits.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 = mmu->readMem(mmu->readMem(PC));
          PC++;
          P.bits.Z = ((X - value8) & 0xFF) == 0;
          P.bits.S = ((X - value8) & SIGN) != 0;
          P.bits.C = (X >= value8) != 0;
          ticks += 3;
        break;

        case 0xE5 :  // ZPG SBC
          value8 = mmu->readMem(mmu->readMem(PC));
          PC++;
          value8 ^= 0xFF;
          if (P.bits.D)
            value8 -= 0x0066;
          value16 = A + value8 + P.bits.C;
          P.bits.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0;
          if (P.bits.D)
            value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3;
          P.bits.C = value16 > 0xFF;
          A = value16 & 0xFF;
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 3;
        break;

        case 0xE6 :  // ZPG INC
          address = mmu->readMem(PC);
          PC++;
          value8 = mmu->readMem(address);
          value8++;
          mmu->writeMem(address, value8);
          P.bits.Z = value8 == 0;
          P.bits.S = value8 > 0x7F;
          ticks += 5;
        break;

        case 0xE7 :  // ZPG SMB
          address = mmu->readMem(PC);
          PC++;
          mmu->writeMem(address, mmu->readMem(address) | 64);
          ticks += 5;
        break;

        case 0xE8 :  // IMP INX
          X++;
          P.bits.Z = X == 0;
          P.bits.S = X > 0x7F;
          ticks += 2;
        break;

        case 0xE9 :  // IMM SBC
          value8 = mmu->readMem(PC);
          PC++;
          value8 ^= 0xFF;
          if (P.bits.D)
            value8 -= 0x0066;
          value16 = A + value8 + (P.bits.C);
          P.bits.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0;
          if (P.bits.D)
            value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3;
          P.bits.C = value16 > 0xFF;
          A = value16 & 0xFF;
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 2;
        break;

        case 0xEA:  // IMP NOP
          ticks += 2;
        break;

        case 0xEB :  // IMP NOP
          ticks++;
        break;

        case 0xEC :  // ABS CPX
          address = mmu->readMem(PC);
          PC++;
          address |= mmu->readMem(PC) << 8;
          PC++;
          value8 = mmu->readMem(address);
          P.bits.Z = ((X - value8) & 0xFF) == 0;
          P.bits.S = ((X - value8) & SIGN) != 0;
          P.bits.C = (X >= value8) != 0;
          ticks += 4;
        break;

        case 0xED :  // ABS SBC
          address = mmu->readMem(PC);
          PC++;
          address |= mmu->readMem(PC) << 8;
          PC++;
          value8 = mmu->readMem(address);
          value8 ^= 0xFF;
          if (P.bits.D)
            value8 -= 0x0066;
          value16 = A + value8 + P.bits.C;
          P.bits.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0;
          if (P.bits.D)
            value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3;
          P.bits.C = value16 > 0xFF;
          A = value16 & 0xFF;
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 4;
        break;

        case 0xEE :  // ABS INC
          address = mmu->readMem(PC);
          PC++;
          address |= mmu->readMem(PC) << 8;
          PC++;
          value8 = mmu->readMem(address);
          value8++;
          mmu->writeMem(address, value8);
          P.bits.Z = value8 == 0;
          P.bits.S = value8 > 0x7F;
          ticks += 6;
        break;

        case 0xEF :  // ZPR BBS
          value8 = mmu->readMem(mmu->readMem(PC));
          PC++;
          address = mmu->readMem(PC);
          PC++;
          if (address & SIGN)
            address |= 0xFF00;  // jump backward
          if (value8 & 64)
            PC += address;
          ticks += 5;
        break;

        case 0xF0 :  // REL BEQ
          address = mmu->readMem(PC);
          PC++;
          if (P.bits.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 = mmu->readMem(PC);
          PC++;
          address = mmu->readMem(value8);
          if ((address + Y) & 0xFF00)  // page crossing
            ticks++;
          value8++;
          address |= mmu->readMem(value8) << 8;
          address += Y;
          value8 = mmu->readMem(address);
          value8 ^= 0xFF;
          if (P.bits.D)
            value8 -= 0x0066;
          value16 = A + value8 + P.bits.C;
          P.bits.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0;
          if (P.bits.D)
            value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3;
          P.bits.C = value16 > 0xFF;
          A = value16 & 0xFF;
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 5;
        break;

        case 0xF2 :  // IZP SBC
          value8 = mmu->readMem(PC);
          PC++;
          address = mmu->readMem(value8);
          value8++;
          address |= mmu->readMem(value8) << 8;
          value8 = mmu->readMem(address);
          value8 ^= 0xFF;
          if (P.bits.D)
            value8 -= 0x0066;
          value16 = A + value8 + P.bits.C;
          P.bits.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0;
          if (P.bits.D)
            value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3;
          P.bits.C = value16 > 0xFF;
          A = value16 & 0xFF;
          P.bits.Z = A == 0;
          P.bits.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 = (mmu->readMem(PC) + X) & 0xFF;
          PC++;
          value8 = mmu->readMem(address);
          value8 ^= 0xFF;
          if (P.bits.D)
            value8 -= 0x0066;
          value16 = A + value8 + P.bits.C;
          P.bits.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0;
          if (P.bits.D)
            value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3;
          P.bits.C = value16 > 0xFF;
          A = value16 & 0xFF;
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 4;
        break;

        case 0xF6 :  // ZPX INC
          address = (mmu->readMem(PC) + X) & 0xFF;
          PC++;
          value8 = mmu->readMem(address);
          value8++;
          mmu->writeMem(address, value8);
          P.bits.Z = value8 == 0;
          P.bits.S = value8 > 0x7F;
          ticks += 6;
        break;

        case 0xF7 :  // ZPG SMB
          address = mmu->readMem(PC);
          PC++;
          mmu->writeMem(address, mmu->readMem(address) | 128);
          ticks += 5;
        break;

        case 0xF8 :  // IMP SED
          P.bits.D = 1;
          ticks += 2;
        break;

        case 0xF9 :  // ABY SBC
          address = mmu->readMem(PC);
          PC++;
          if ((address + Y) & 0xFF00)  // page crossing
            ticks++;
          address |= mmu->readMem(PC) << 8;
          PC++;
          address += Y;
          value8 = mmu->readMem(address);
          value8 ^= 0xFF;
          if (P.bits.D)
            value8 -= 0x0066;
          value16 = A + value8 + P.bits.C;
          P.bits.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0;
          if (P.bits.D)
            value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3;
          P.bits.C = value16 > 0xFF;
          A = value16 & 0xFF;
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 4;
        break;

        case 0xFA :  // IMP PLX
          SP++;
          X = mmu->readMem(0x100 + SP);
          P.bits.Z = X == 0;
          P.bits.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 = mmu->readMem(PC);
          PC++;
          if ((address + X) & 0xFF00)  // page crossing
            ticks++;
          address |= mmu->readMem(PC) << 8;
          PC++;
          address += X;
          value8 = mmu->readMem(address);
          value8 ^= 0xFF;
          if (P.bits.D)
            value8 -= 0x0066;
          value16 = A + value8 + P.bits.C;
          P.bits.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0;
          if (P.bits.D)
            value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3;
          P.bits.C =  (value16 & 0xFF00) != 0;
          A = value16 & 0xFF;
          P.bits.Z = A == 0;
          P.bits.S = A > 0x7F;
          ticks += 4;
        break;

        case 0xFE :  // ABX INC
          address = mmu->readMem(PC);
          PC++;
          address |= mmu->readMem(PC) << 8;
          PC++;
          address += X;
          value8 = mmu->readMem(address);
          value8++;
          mmu->writeMem(address, value8);
          P.bits.Z = value8 == 0;
          P.bits.S = value8 > 0x7F;
          ticks += 7;
        break;

        case 0xFF :  // ZPR BBS
          value8 = mmu->readMem(mmu->readMem(PC));
          PC++;
          address = mmu->readMem(PC);
          PC++;
          if (address & SIGN)
            address |= 0xFF00;  // jump backward
          if (value8 & 128)
            PC += address;
          ticks += 5;
        break;

      } // end of switch
    }  // end of if
  }  // end of while
  return PC;
}


// utilities  // to be moved into gui ??

#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","NOP","NOP","CPY","CMP","DEC","SMB","INY","CMP","DEX","WAI","CPY","CMP","DEC","BBS",
  "BNE","CMP","CMP","NOP","NOP","CMP","DEC","SMB","CLD","CMP","PHX","STP","NOP","CMP","DEC","BBS",
  "CPX","SBC","NOP","NOP","CPX","SBC","INC","SMB","INX","SBC","NOP","NOP","CPX","SBC","INC","BBS",
  "BEQ","SBC","SBC","NOP","NOP","SBC","INC","SMB","SED","SBC","PLX","NOP","NOP","SBC","INC","BBS"
};

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

int puce65c02::getCode(uint16_t address, char* buffer, int size, int numLines) {
  int consumed = 0;

  for (int i=0; i<numLines; i++) {
    uint8_t op = mmu->readMem(address);
    uint8_t b1 = mmu->readMem((address + 1) & 0xFFFF);
    uint8_t b2 = mmu->readMem((address + 2) & 0xFFFF);
    consumed += snprintf(buffer + consumed, size - consumed, "%04X %02X ", address, op);
    switch(am[op]) {
      case 0x0: consumed += snprintf(buffer + consumed, size - consumed, "       %s          ",              mn[op]      ); address+=1; break;  // implied
      case 0x1: consumed += snprintf(buffer + consumed, size - consumed, "       %s A        ",              mn[op]      ); address+=1; break;  // accumulator
      case 0x2: consumed += snprintf(buffer + consumed, size - consumed, "%02X     %s #$%02X     ",    b1,   mn[op],b1   ); address+=2; break;  // immediate
      case 0x3: consumed += snprintf(buffer + consumed, size - consumed, "%02X     %s $%02X      ",    b1,   mn[op],b1   ); address+=2; break;  // zero page
      case 0x4: consumed += snprintf(buffer + consumed, size - consumed, "%02X     %s $%02X,X    ",    b1,   mn[op],b1   ); address+=2; break;  // zero page, X indexed
      case 0x5: consumed += snprintf(buffer + consumed, size - consumed, "%02X     %s $%02X,Y    ",    b1,   mn[op],b1   ); address+=2; break;  // zero page, Y indexed
      case 0x6: consumed += snprintf(buffer + consumed, size - consumed, "%02X     %s $%02X      ",    b1,   mn[op],b1   ); address+=2; break;  // relative
      case 0xB: consumed += snprintf(buffer + consumed, size - consumed, "%02X     %s ($%02X)    ",    b1,   mn[op],b1   ); address+=2; break;  // izp ($00)
      case 0xC: consumed += snprintf(buffer + consumed, size - consumed, "%02X     %s ($%02X,X)  ",    b1,   mn[op],b1   ); address+=2; break;  // X indexed, indirect
      case 0xD: consumed += snprintf(buffer + consumed, size - consumed, "%02X     %s ($%02X),Y  ",    b1,   mn[op],b1   ); address+=2; break;  // indirect, Y indexed
      case 0x7: consumed += snprintf(buffer + consumed, size - consumed, "%02X%02X   %s $%02X%02X    ",b1,b2,mn[op],b2,b1); address+=3; break;  // absolute
      case 0x8: consumed += snprintf(buffer + consumed, size - consumed, "%02X%02X   %s $%02X%02X,X  ",b1,b2,mn[op],b2,b1); address+=3; break;  // absolute, X indexed
      case 0x9: consumed += snprintf(buffer + consumed, size - consumed, "%02X%02X   %s $%02X%02X,Y  ",b1,b2,mn[op],b2,b1); address+=3; break;  // absolute, Y indexed
      case 0xA: consumed += snprintf(buffer + consumed, size - consumed, "%02X%02X   %s ($%02X%02X)  ",b1,b2,mn[op],b2,b1); address+=3; break;  // indirect
      case 0xF: consumed += snprintf(buffer + consumed, size - consumed, "%02X%02X   %s ($%02X%02X,X)",b1,b2,mn[op],b2,b1); address+=3; break;  // iax ($0000,X)
      case 0xE: consumed += snprintf(buffer + consumed, size - consumed, "%02X%02X   %s $%02X,$%02X  ",b1,b2,mn[op],b2,b1); address+=3; break;  // zpr $00,$00
    }
    consumed += snprintf(buffer + consumed, size - consumed, "\n");
  }
  return consumed;
}

int puce65c02::getRegs(char* buffer) {
  return (snprintf(buffer, 100, "A=%02X  X=%02X  Y=%02X  S=%02X  *S=%02X\nPC=%04X  P=%c%c%c%c%c%c%c%c", \
  A, X, Y, SP, mmu->readMem(0x100 + SP), PC, \
  P.bits.S?'N':'-', P.bits.V?'V':'-', P.bits.U?'U':'.', P.bits.B?'B':'-', P.bits.D?'D':'-', P.bits.I?'I':'-', P.bits.Z?'Z':'-', P.bits.C?'C':'-'));
}
