[
  {
    "path": ".gitignore",
    "content": "*.o"
  },
  {
    "path": "LICENSE.txt",
    "content": "MIT License\n\nCopyright (c) 2017 Gianluca Ghettini\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "## MOS6502 Emulator in C++\n\nThis is my C++ implementation of the MOS Technology 6502 CPU. The code is written to be more readable than fast, however some minor tricks have been introduced to greatly reduce the overall execution time.\n\nmain features:\n\n- 100% coverage of legal opcodes\n- decimal mode implemented\n- read/write bus callback\n- jump table opcode selection\n\nstill to implement\n\n- 100% cycle accuracy\n- illegal opcodes\n- hardware glitches, the known ones of course :-)\n\nThe emulator was extensively tested against this test suite:\n\nhttps://github.com/Klaus2m5/6502_65C02_functional_tests\n\nand in parallel emulation with Fake6502 http://rubbermallet.org/fake6502.c\n\nso expect nearly 100% compliance with the real deal... at least on the normal behavior: as I said stuff like illegal opcodes or hardware glitches are currently not implemented.\n\n## Why yet another 6502 emulator?\n\nJust for fun :). This CPU (and its many derivatives) powered machines such as:\n\n- Apple II\n- Nintendo Entertainment system (NES)\n- Atari 2600\n- Commodore PET and VIC-20\n- Commodore 64 (6510 chip actually, but fully compatible emulator-wise)\n- BBC Micro\n\nand many other embedded devices still used today.\nYou can use this emulator in your machine emulator project. However cycle accuracy is not yet implemented so mid-frame register update tricks cannot be reliably emulated.\n\n## Some things emulators: emulator types\n\nYou can group all the CPU emulators out there in 4 main categories:\n\n- switch-case based\n- jump-table based\n- PLA or microcode emulation based\n- graph based\n\nThe latter are the most accurate as they emulate the connections between transistors inside the die of the CPU. They emulate even the unwanted glitches, known and still unknown. However, the complexity of such emulators is non-linear with the number of transistors: in other word, _you don't want to emulate a modern Intel quad core using this approach!!!_\n\nfor an example check this out: http://visual6502.org/JSSim/index.html\n\nThe PLA/microcode based are the best as they offer both speed and limited complexity.\nThe switch-case based are the simpler ones but also the slowest: the opcode value is thrown inside a huge switch case which selects the code snippet to execute; compilers can optimize switch case to reach near O(log(n)) complexity but they hardly do it when dealing with sparse integers (like most of the CPU opcode tables).\n\n## Emulator features\n\nMy project is a simple jump-table based emulator: the actual value of the opcode (let's say 0x80) is used to address a function pointer table, each entry of such table is a C++ function which emulates the behavior of the corresponding real instruction.\n\nAll the 13 addressing modes are emulated:\n\n```\n// addressing modes\nuint16_t Addr_ACC(); // ACCUMULATOR\nuint16_t Addr_IMM(); // IMMEDIATE\nuint16_t Addr_ABS(); // ABSOLUTE\nuint16_t Addr_ZER(); // ZERO PAGE\nuint16_t Addr_ZEX(); // INDEXED-X ZERO PAGE\nuint16_t Addr_ZEY(); // INDEXED-Y ZERO PAGE\nuint16_t Addr_ABX(); // INDEXED-X ABSOLUTE\nuint16_t Addr_ABY(); // INDEXED-Y ABSOLUTE\nuint16_t Addr_IMP(); // IMPLIED\nuint16_t Addr_REL(); // RELATIVE\nuint16_t Addr_INX(); // INDEXED-X INDIRECT\nuint16_t Addr_INY(); // INDEXED-Y INDIRECT\nuint16_t Addr_ABI(); // ABSOLUTE INDIRECT\n```\n\nAll the 151 opcodes are emulated. Since the 6502 CPU uses 8 bit to encode the opcode value it also has a lot of \"illegal opcodes\" (i.e. opcode values other than the designed 151). Such opcodes perform weird operations, write multiple registers at the same time, sometimes are the combination of two or more \"valid\" opcodes. Such illegals were used to enforce software copy protection or to discover the exact CPU type.\n\nThe illegals are not supported yet, so instead a simple NOP is executed.\n\n## Inner main loop\n\nIt's a classic fetch-decode-execute loop:\n\n```\nwhile(start + n > cycles && !illegalOpcode)\n{\n\t// fetch\n\topcode = Read(pc++);\n\n\t// decode\n\tinstr = InstrTable[opcode];\n\n\t// execute\n\tExec(instr);\n}\n```\n\nThe next instruction (the opcode value) is retrieved from memory. Then it's decoded (i.e. the opcode is used to address the instruction table) and the resulting code block is executed.\n\n## Public methods\n\nThe emulator comes as a single C++ class with five public methods:\n\n```\nmos6502(BusRead r, BusWrite w);\nvoid NMI();\nvoid IRQ();\nvoid Reset();\nvoid Run(uint32_t n);\n```\n\n`mos6502(BusRead r, BusWrite w);`\n\nit's the class constructor. It requires you to pass two external functions:\n\n```\nuint8_t MemoryRead(uint16_t address);\nvoid MemoryWrite(uint16_t address, uint8_t value);\n```\n\nrespectively to read/write from/to a memory location (16 bit address, 8 bit value). In such functions you can define your address decoding logic (if any) to address memory mapped I/O, external virtual devices and such.\n\n```\nvoid NMI();\n```\n\ntriggers a Non-Mascherable Interrupt request, as done by the external pin of the real chip\n\n```\nvoid IRQ();\n```\n\ntriggers an Interrupt ReQuest, as done by the external pin of the real chip\n\n```\nvoid Reset();\n```\n\nperforms an hardware reset, as done by the external pin of the real chip\n\n```\nvoid Run(uint32_t n);\n```\n\nIt runs the CPU for the next 'n' machine instructions.\n\n## Links\n\nSome useful stuff I used...\n\nhttp://en.wikipedia.org/wiki/MOS_Technology_6502\n\nhttp://www.6502.org/documents/datasheets/mos/\n\nhttp://www.mdawson.net/vic20chrome/cpu/mos_6500_mpu_preliminary_may_1976.pdf\n\nhttp://rubbermallet.org/fake6502.c\n"
  },
  {
    "path": "mos6502.cpp",
    "content": "#include \"mos6502.h\"\n\n#define NEGATIVE  0x80\n#define OVERFLOW  0x40\n#define CONSTANT  0x20\n#define BREAK     0x10\n#define DECIMAL   0x08\n#define INTERRUPT 0x04\n#define ZERO      0x02\n#define CARRY     0x01\n\n#define SET_NEGATIVE(x)  ((x) ? (status |= NEGATIVE)  : (status &= (~NEGATIVE)) )\n#define SET_OVERFLOW(x)  ((x) ? (status |= OVERFLOW)  : (status &= (~OVERFLOW)) )\n#define SET_CONSTANT(x)  ((x) ? (status |= CONSTANT)  : (status &= (~CONSTANT)) )\n#define SET_BREAK(x)     ((x) ? (status |= BREAK)     : (status &= (~BREAK)) )\n#define SET_DECIMAL(x)   ((x) ? (status |= DECIMAL)   : (status &= (~DECIMAL)) )\n#define SET_INTERRUPT(x) ((x) ? (status |= INTERRUPT) : (status &= (~INTERRUPT)) )\n#define SET_ZERO(x)      ((x) ? (status |= ZERO)      : (status &= (~ZERO)) )\n#define SET_CARRY(x)     ((x) ? (status |= CARRY)     : (status &= (~CARRY)) )\n\n#define IF_NEGATIVE()  ((status & NEGATIVE) ? true : false)\n#define IF_OVERFLOW()  ((status & OVERFLOW) ? true : false)\n#define IF_CONSTANT()  ((status & CONSTANT) ? true : false)\n#define IF_BREAK()     ((status & BREAK) ? true : false)\n#define IF_DECIMAL()   ((status & DECIMAL) ? true : false)\n#define IF_INTERRUPT() ((status & INTERRUPT) ? true : false)\n#define IF_ZERO()      ((status & ZERO) ? true : false)\n#define IF_CARRY()     ((status & CARRY) ? true : false)\n\nmos6502::Instr mos6502::InstrTable[256];\n\nmos6502::mos6502(BusRead r, BusWrite w, ClockCycle c)\n   : reset_A(0x00)\n   , reset_X(0x00)\n   , reset_Y(0x00)\n   , reset_sp(0xFD)\n   , reset_status(CONSTANT)\n   , irq_line(true)\n   , nmi_request(false)\n   , nmi_inhibit(false)\n   , nmi_line(true)\n{\n   Write = (BusWrite)w;\n   Read = (BusRead)r;\n   Cycle = (ClockCycle)c;\n\n   static bool initialized = false;\n   if (initialized) return;\n   initialized = true;\n\n   Instr instr;\n   // fill jump table with ILLEGALs\n   instr.addr = &mos6502::Addr_IMP;\n   instr.saddr = \"(null)\";\n   instr.code = &mos6502::Op_ILLEGAL;\n   instr.scode = \"(null)\";\n   instr.penalty = false;\n   instr.cycles = 0;\n   for(int i = 0; i < 256; i++)\n   {\n      InstrTable[i] = instr;\n   }\n\n   // insert opcodes\n#define MAKE_INSTR(HEX, CODE, MODE, CYCLES, PENALTY) \\\n   instr.code = &mos6502::Op_ ## CODE; \\\n   instr.scode = # CODE; \\\n   instr.addr = &mos6502::Addr_ ## MODE; \\\n   instr.saddr = # MODE; \\\n   instr.cycles = CYCLES; \\\n   instr.penalty = PENALTY; \\\n   InstrTable[HEX] = instr;\n\n// ADC\n// Add Memory to Accumulator with Carry\n//\n// A + M + C -> A, C\n// N    Z       C       I       D       V\n// +    +       +       -       -       +\n// addressing   assembler       opc     bytes   cycles\n// immediate    ADC #oper       69      2       2\n// zeropage     ADC oper        65      2       3\n// zeropage,X   ADC oper,X      75      2       4\n// absolute     ADC oper        6D      3       4\n// absolute,X   ADC oper,X      7D      3       4*\n// absolute,Y   ADC oper,Y      79      3       4*\n// (indirect,X) ADC (oper,X)    61      2       6\n// (indirect),Y ADC (oper),Y    71      2       5*\n\n   MAKE_INSTR(0x69, ADC, IMM, 2, false);\n   MAKE_INSTR(0x65, ADC, ZER, 3, false);\n   MAKE_INSTR(0x75, ADC, ZEX, 4, false);\n   MAKE_INSTR(0x6D, ADC, ABS, 4, false);\n   MAKE_INSTR(0x7D, ADC, ABX, 4, true);\n   MAKE_INSTR(0x79, ADC, ABY, 4, true);\n   MAKE_INSTR(0x61, ADC, INX, 6, false);\n   MAKE_INSTR(0x71, ADC, INY, 5, true);\n\n// AND\n// AND Memory with Accumulator\n//\n// A AND M -> A\n// N    Z       C       I       D       V\n// +    +       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// immediate    AND #oper       29      2       2\n// zeropage     AND oper        25      2       3\n// zeropage,X   AND oper,X      35      2       4\n// absolute     AND oper        2D      3       4\n// absolute,X   AND oper,X      3D      3       4*\n// absolute,Y   AND oper,Y      39      3       4*\n// (indirect,X) AND (oper,X)    21      2       6\n// (indirect),Y AND (oper),Y    31      2       5*\n\n   MAKE_INSTR(0x29, AND, IMM, 2, false);\n   MAKE_INSTR(0x25, AND, ZER, 3, false);\n   MAKE_INSTR(0x35, AND, ZEX, 4, false);\n   MAKE_INSTR(0x2D, AND, ABS, 4, false);\n   MAKE_INSTR(0x3D, AND, ABX, 4, true);\n   MAKE_INSTR(0x39, AND, ABY, 4, true);\n   MAKE_INSTR(0x21, AND, INX, 6, false);\n   MAKE_INSTR(0x31, AND, INY, 5, true);\n\n// ASL\n// Shift Left One Bit (Memory or Accumulator)\n//\n// C <- [76543210] <- 0\n// N    Z       C       I       D       V\n// +    +       +       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// accumulator  ASL A           0A      1       2\n// zeropage     ASL oper        06      2       5\n// zeropage,X   ASL oper,X      16      2       6\n// absolute     ASL oper        0E      3       6\n// absolute,X   ASL oper,X      1E      3       7\n\n   MAKE_INSTR(0x0A, ASL_ACC, ACC, 2, false);\n   MAKE_INSTR(0x06, ASL, ZER, 5, false);\n   MAKE_INSTR(0x16, ASL, ZEX, 6, false);\n   MAKE_INSTR(0x0E, ASL, ABS, 6, false);\n   MAKE_INSTR(0x1E, ASL, ABX, 7, false);\n\n// BCC\n// Branch on Carry Clear\n//\n// branch on C = 0\n// N    Z       C       I       D       V\n// -    -       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// relative     BCC oper        90      2       2**\n\n   MAKE_INSTR(0x90, BCC, REL, 2, true);\n\n// BCS\n// Branch on Carry Set\n//\n// branch on C = 1\n// N    Z       C       I       D       V\n// -    -       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// relative     BCS oper        B0      2       2**\n\n   MAKE_INSTR(0xB0, BCS, REL, 2, true);\n\n// BEQ\n// Branch on Result Zero\n//\n// branch on Z = 1\n// N    Z       C       I       D       V\n// -    -       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// relative     BEQ oper        F0      2       2**\n\n   MAKE_INSTR(0xF0, BEQ, REL, 2, true);\n\n// BIT\n// Test Bits in Memory with Accumulator\n//\n// bits 7 and 6 of operand are transfered to bit 7 and 6 of SR (N,V);\n// the zero-flag is set according to the result of the operand AND\n// the accumulator (set, if the result is zero, unset otherwise).\n// This allows a quick check of a few bits at once without affecting\n// any of the registers, other than the status register (SR).\n//\n// → Further details.\n//\n// A AND M -> Z, M7 -> N, M6 -> V\n// N    Z       C       I       D       V\n// M7   +       -       -       -       M6\n// addressing   assembler       opc     bytes   cycles\n// zeropage     BIT oper        24      2       3\n// absolute     BIT oper        2C      3       4\n\n   MAKE_INSTR(0x24, BIT, ZER, 3, false);\n   MAKE_INSTR(0x2C, BIT, ABS, 4, false);\n\n// BMI\n// Branch on Result Minus\n//\n// branch on N = 1\n// N    Z       C       I       D       V\n// -    -       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// relative     BMI oper        30      2       2**\n\n   MAKE_INSTR(0x30, BMI, REL, 2, true);\n\n// BNE\n// Branch on Result not Zero\n//\n// branch on Z = 0\n// N    Z       C       I       D       V\n// -    -       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// relative     BNE oper        D0      2       2**\n\n   MAKE_INSTR(0xD0, BNE, REL, 2, true);\n\n// BPL\n// Branch on Result Plus\n//\n// branch on N = 0\n// N    Z       C       I       D       V\n// -    -       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// relative     BPL oper        10      2       2**\n\n   MAKE_INSTR(0x10, BPL, REL, 2, true);\n\n// BRK\n// Force Break\n//\n// BRK initiates a software interrupt similar to a hardware\n// interrupt (IRQ). The return address pushed to the stack is\n// PC+2, providing an extra byte of spacing for a break mark\n// (identifying a reason for the break.)\n// The status register will be pushed to the stack with the break\n// flag set to 1. However, when retrieved during RTI or by a PLP\n// instruction, the break flag will be ignored.\n// The interrupt disable flag is not set automatically.\n//\n// → Further details.\n//\n// interrupt,\n// push PC+2, push SR\n// N    Z       C       I       D       V\n// -    -       -       1       -       -\n// addressing   assembler       opc     bytes   cycles\n// implied      BRK     00      1       7\n\n   MAKE_INSTR(0x00, BRK, IMP, 7, false);\n\n// BVC\n// Branch on Overflow Clear\n//\n// branch on V = 0\n// N    Z       C       I       D       V\n// -    -       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// relative     BVC oper        50      2       2**\n\n   MAKE_INSTR(0x50, BVC, REL, 2, true);\n\n// BVS\n// Branch on Overflow Set\n//\n// branch on V = 1\n// N    Z       C       I       D       V\n// -    -       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// relative     BVS oper        70      2       2**\n\n   MAKE_INSTR(0x70, BVS, REL, 2, true);\n\n// CLC\n// Clear Carry Flag\n//\n// 0 -> C\n// N    Z       C       I       D       V\n// -    -       0       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// implied      CLC     18      1       2\n\n   MAKE_INSTR(0x18, CLC, IMP, 2, false);\n\n// CLD\n// Clear Decimal Mode\n//\n// 0 -> D\n// N    Z       C       I       D       V\n// -    -       -       -       0       -\n// addressing   assembler       opc     bytes   cycles\n// implied      CLD     D8      1       2\n\n   MAKE_INSTR(0xD8, CLD, IMP, 2, false);\n\n// CLI\n// Clear Interrupt Disable Bit\n//\n// 0 -> I\n// N    Z       C       I       D       V\n// -    -       -       0       -       -\n// addressing   assembler       opc     bytes   cycles\n// implied      CLI     58      1       2\n\n   MAKE_INSTR(0x58, CLI, IMP, 2, false);\n\n// CLV\n// Clear Overflow Flag\n//\n// 0 -> V\n// N    Z       C       I       D       V\n// -    -       -       -       -       0\n// addressing   assembler       opc     bytes   cycles\n// implied      CLV     B8      1       2\n\n   MAKE_INSTR(0xB8, CLV, IMP, 2, false);\n\n// CMP\n// Compare Memory with Accumulator\n//\n// A - M\n// N    Z       C       I       D       V\n// +    +       +       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// immediate    CMP #oper       C9      2       2\n// zeropage     CMP oper        C5      2       3\n// zeropage,X   CMP oper,X      D5      2       4\n// absolute     CMP oper        CD      3       4\n// absolute,X   CMP oper,X      DD      3       4*\n// absolute,Y   CMP oper,Y      D9      3       4*\n// (indirect,X) CMP (oper,X)    C1      2       6\n// (indirect),Y CMP (oper),Y    D1      2       5*\n\n   MAKE_INSTR(0xC9, CMP, IMM, 2, false);\n   MAKE_INSTR(0xC5, CMP, ZER, 3, false);\n   MAKE_INSTR(0xD5, CMP, ZEX, 4, false);\n   MAKE_INSTR(0xCD, CMP, ABS, 4, false);\n   MAKE_INSTR(0xDD, CMP, ABX, 4, true);\n   MAKE_INSTR(0xD9, CMP, ABY, 4, true);\n   MAKE_INSTR(0xC1, CMP, INX, 6, false);\n   MAKE_INSTR(0xD1, CMP, INY, 5, true);\n\n// CPX\n// Compare Memory and Index X\n//\n// X - M\n// N    Z       C       I       D       V\n// +    +       +       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// immediate    CPX #oper       E0      2       2\n// zeropage     CPX oper        E4      2       3\n// absolute     CPX oper        EC      3       4\n\n   MAKE_INSTR(0xE0, CPX, IMM, 2, false);\n   MAKE_INSTR(0xE4, CPX, ZER, 3, false);\n   MAKE_INSTR(0xEC, CPX, ABS, 4, false);\n\n// CPY\n// Compare Memory and Index Y\n//\n// Y - M\n// N    Z       C       I       D       V\n// +    +       +       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// immediate    CPY #oper       C0      2       2\n// zeropage     CPY oper        C4      2       3\n// absolute     CPY oper        CC      3       4\n\n   MAKE_INSTR(0xC0, CPY, IMM, 2, false);\n   MAKE_INSTR(0xC4, CPY, ZER, 3, false);\n   MAKE_INSTR(0xCC, CPY, ABS, 4, false);\n\n// DEC\n// Decrement Memory by One\n//\n// M - 1 -> M\n// N    Z       C       I       D       V\n// +    +       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// zeropage     DEC oper        C6      2       5\n// zeropage,X   DEC oper,X      D6      2       6\n// absolute     DEC oper        CE      3       6\n// absolute,X   DEC oper,X      DE      3       7\n\n   MAKE_INSTR(0xC6, DEC, ZER, 5, false);\n   MAKE_INSTR(0xD6, DEC, ZEX, 6, false);\n   MAKE_INSTR(0xCE, DEC, ABS, 6, false);\n   MAKE_INSTR(0xDE, DEC, ABX, 7, false);\n\n// DEX\n// Decrement Index X by One\n//\n// X - 1 -> X\n// N    Z       C       I       D       V\n// +    +       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// implied      DEX     CA      1       2\n\n   MAKE_INSTR(0xCA, DEX, IMP, 2, false);\n\n// DEY\n// Decrement Index Y by One\n//\n// Y - 1 -> Y\n// N    Z       C       I       D       V\n// +    +       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// implied      DEY     88      1       2\n\n   MAKE_INSTR(0x88, DEY, IMP, 2, false);\n\n// EOR\n// Exclusive-OR Memory with Accumulator\n//\n// A EOR M -> A\n// N    Z       C       I       D       V\n// +    +       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// immediate    EOR #oper       49      2       2\n// zeropage     EOR oper        45      2       3\n// zeropage,X   EOR oper,X      55      2       4\n// absolute     EOR oper        4D      3       4\n// absolute,X   EOR oper,X      5D      3       4*\n// absolute,Y   EOR oper,Y      59      3       4*\n// (indirect,X) EOR (oper,X)    41      2       6\n// (indirect),Y EOR (oper),Y    51      2       5*\n\n   MAKE_INSTR(0x49, EOR, IMM, 2, false);\n   MAKE_INSTR(0x45, EOR, ZER, 3, false);\n   MAKE_INSTR(0x55, EOR, ZEX, 4, false);\n   MAKE_INSTR(0x4D, EOR, ABS, 4, false);\n   MAKE_INSTR(0x5D, EOR, ABX, 4, true);\n   MAKE_INSTR(0x59, EOR, ABY, 4, true);\n   MAKE_INSTR(0x41, EOR, INX, 6, false);\n   MAKE_INSTR(0x51, EOR, INY, 5, true);\n\n// INC\n// Increment Memory by One\n//\n// M + 1 -> M\n// N    Z       C       I       D       V\n// +    +       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// zeropage     INC oper        E6      2       5\n// zeropage,X   INC oper,X      F6      2       6\n// absolute     INC oper        EE      3       6\n// absolute,X   INC oper,X      FE      3       7\n\n   MAKE_INSTR(0xE6, INC, ZER, 5, false);\n   MAKE_INSTR(0xF6, INC, ZEX, 6, false);\n   MAKE_INSTR(0xEE, INC, ABS, 6, false);\n   MAKE_INSTR(0xFE, INC, ABX, 7, false);\n\n// INX\n// Increment Index X by One\n//\n// X + 1 -> X\n// N    Z       C       I       D       V\n// +    +       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// implied      INX     E8      1       2\n\n   MAKE_INSTR(0xE8, INX, IMP, 2, false);\n\n// INY\n// Increment Index Y by One\n//\n// Y + 1 -> Y\n// N    Z       C       I       D       V\n// +    +       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// implied      INY     C8      1       2\n\n   MAKE_INSTR(0xC8, INY, IMP, 2, false);\n\n// JMP\n// Jump to New Location\n//\n// operand 1st byte -> PCL\n// operand 2nd byte -> PCH\n// N    Z       C       I       D       V\n// -    -       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// absolute     JMP oper        4C      3       3\n// indirect     JMP (oper)      6C      3       5\n\n   MAKE_INSTR(0x4C, JMP, ABS, 3, false);\n   MAKE_INSTR(0x6C, JMP, ABI, 5, false);\n\n// JSR\n// Jump to New Location Saving Return Address\n//\n// push (PC+2),\n// operand 1st byte -> PCL\n// operand 2nd byte -> PCH\n// N    Z       C       I       D       V\n// -    -       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// absolute     JSR oper        20      3       6\n\n   MAKE_INSTR(0x20, JSR, ABS, 6, false);\n\n// LDA\n// Load Accumulator with Memory\n//\n// M -> A\n// N    Z       C       I       D       V\n// +    +       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// immediate    LDA #oper       A9      2       2\n// zeropage     LDA oper        A5      2       3\n// zeropage,X   LDA oper,X      B5      2       4\n// absolute     LDA oper        AD      3       4\n// absolute,X   LDA oper,X      BD      3       4*\n// absolute,Y   LDA oper,Y      B9      3       4*\n// (indirect,X) LDA (oper,X)    A1      2       6\n// (indirect),Y LDA (oper),Y    B1      2       5*\n\n   MAKE_INSTR(0xA9, LDA, IMM, 2, false);\n   MAKE_INSTR(0xA5, LDA, ZER, 3, false);\n   MAKE_INSTR(0xB5, LDA, ZEX, 4, false);\n   MAKE_INSTR(0xAD, LDA, ABS, 4, false);\n   MAKE_INSTR(0xBD, LDA, ABX, 4, true);\n   MAKE_INSTR(0xB9, LDA, ABY, 4, true);\n   MAKE_INSTR(0xA1, LDA, INX, 6, false);\n   MAKE_INSTR(0xB1, LDA, INY, 5, true);\n\n// LDX\n// Load Index X with Memory\n//\n// M -> X\n// N    Z       C       I       D       V\n// +    +       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// immediate    LDX #oper       A2      2       2\n// zeropage     LDX oper        A6      2       3\n// zeropage,Y   LDX oper,Y      B6      2       4\n// absolute     LDX oper        AE      3       4\n// absolute,Y   LDX oper,Y      BE      3       4*\n\n   MAKE_INSTR(0xA2, LDX, IMM, 2, false);\n   MAKE_INSTR(0xA6, LDX, ZER, 3, false);\n   MAKE_INSTR(0xB6, LDX, ZEY, 4, false);\n   MAKE_INSTR(0xAE, LDX, ABS, 4, false);\n   MAKE_INSTR(0xBE, LDX, ABY, 4, true);\n\n// LDY\n// Load Index Y with Memory\n//\n// M -> Y\n// N    Z       C       I       D       V\n// +    +       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// immediate    LDY #oper       A0      2       2\n// zeropage     LDY oper        A4      2       3\n// zeropage,X   LDY oper,X      B4      2       4\n// absolute     LDY oper        AC      3       4\n// absolute,X   LDY oper,X      BC      3       4*\n\n   MAKE_INSTR(0xA0, LDY, IMM, 2, false);\n   MAKE_INSTR(0xA4, LDY, ZER, 3, false);\n   MAKE_INSTR(0xB4, LDY, ZEX, 4, false);\n   MAKE_INSTR(0xAC, LDY, ABS, 4, false);\n   MAKE_INSTR(0xBC, LDY, ABX, 4, true);\n\n// LSR\n// Shift One Bit Right (Memory or Accumulator)\n//\n// 0 -> [76543210] -> C\n// N    Z       C       I       D       V\n// 0    +       +       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// accumulator  LSR A           4A      1       2\n// zeropage     LSR oper        46      2       5\n// zeropage,X   LSR oper,X      56      2       6\n// absolute     LSR oper        4E      3       6\n// absolute,X   LSR oper,X      5E      3       7\n\n   MAKE_INSTR(0x4A, LSR_ACC, ACC, 2, false);\n   MAKE_INSTR(0x46, LSR, ZER, 5, false);\n   MAKE_INSTR(0x56, LSR, ZEX, 6, false);\n   MAKE_INSTR(0x4E, LSR, ABS, 6, false);\n   MAKE_INSTR(0x5E, LSR, ABX, 7, false);\n\n// NOP\n// No Operation\n//\n// ---\n// N    Z       C       I       D       V\n// -    -       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// implied      NOP     EA      1       2\n\n   MAKE_INSTR(0xEA, NOP, IMP, 2, false);\n\n// ORA\n// OR Memory with Accumulator\n//\n// A OR M -> A\n// N    Z       C       I       D       V\n// +    +       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// immediate    ORA #oper       09      2       2\n// zeropage     ORA oper        05      2       3\n// zeropage,X   ORA oper,X      15      2       4\n// absolute     ORA oper        0D      3       4\n// absolute,X   ORA oper,X      1D      3       4*\n// absolute,Y   ORA oper,Y      19      3       4*\n// (indirect,X) ORA (oper,X)    01      2       6\n// (indirect),Y ORA (oper),Y    11      2       5*\n\n   MAKE_INSTR(0x09, ORA, IMM, 2, false);\n   MAKE_INSTR(0x05, ORA, ZER, 3, false);\n   MAKE_INSTR(0x15, ORA, ZEX, 4, false);\n   MAKE_INSTR(0x0D, ORA, ABS, 4, false);\n   MAKE_INSTR(0x1D, ORA, ABX, 4, true);\n   MAKE_INSTR(0x19, ORA, ABY, 4, true);\n   MAKE_INSTR(0x01, ORA, INX, 6, false);\n   MAKE_INSTR(0x11, ORA, INY, 5, true);\n\n// PHA\n// Push Accumulator on Stack\n//\n// push A\n// N    Z       C       I       D       V\n// -    -       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// implied      PHA     48      1       3\n\n   MAKE_INSTR(0x48, PHA, IMP, 3, false);\n\n// PHP\n// Push Processor Status on Stack\n//\n// The status register will be pushed with the break\n// flag and bit 5 set to 1.\n//\n// push SR\n// N    Z       C       I       D       V\n// -    -       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// implied      PHP     08      1       3\n\n   MAKE_INSTR(0x08, PHP, IMP, 3, false);\n\n// PLA\n// Pull Accumulator from Stack\n//\n// pull A\n// N    Z       C       I       D       V\n// +    +       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// implied      PLA     68      1       4\n\n   MAKE_INSTR(0x68, PLA, IMP, 4, false);\n\n// PLP\n// Pull Processor Status from Stack\n//\n// The status register will be pulled with the break\n// flag and bit 5 ignored.\n//\n// pull SR\n// N    Z       C       I       D       V\n// from stack\n// addressing   assembler       opc     bytes   cycles\n// implied      PLP     28      1       4\n\n   MAKE_INSTR(0x28, PLP, IMP, 4, false);\n\n// ROL\n// Rotate One Bit Left (Memory or Accumulator)\n//\n// C <- [76543210] <- C\n// N    Z       C       I       D       V\n// +    +       +       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// accumulator  ROL A           2A      1       2\n// zeropage     ROL oper        26      2       5\n// zeropage,X   ROL oper,X      36      2       6\n// absolute     ROL oper        2E      3       6\n// absolute,X   ROL oper,X      3E      3       7\n\n   MAKE_INSTR(0x2A, ROL_ACC, ACC, 2, false);\n   MAKE_INSTR(0x26, ROL, ZER, 5, false);\n   MAKE_INSTR(0x36, ROL, ZEX, 6, false);\n   MAKE_INSTR(0x2E, ROL, ABS, 6, false);\n   MAKE_INSTR(0x3E, ROL, ABX, 7, false);\n\n// ROR\n// Rotate One Bit Right (Memory or Accumulator)\n//\n// C -> [76543210] -> C\n// N    Z       C       I       D       V\n// +    +       +       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// accumulator  ROR A           6A      1       2\n// zeropage     ROR oper        66      2       5\n// zeropage,X   ROR oper,X      76      2       6\n// absolute     ROR oper        6E      3       6\n// absolute,X   ROR oper,X      7E      3       7\n\n   MAKE_INSTR(0x6A, ROR_ACC, ACC, 2, false);\n   MAKE_INSTR(0x66, ROR, ZER, 5, false);\n   MAKE_INSTR(0x76, ROR, ZEX, 6, false);\n   MAKE_INSTR(0x6E, ROR, ABS, 6, false);\n   MAKE_INSTR(0x7E, ROR, ABX, 7, false);\n\n// RTI\n// Return from Interrupt\n//\n// The status register is pulled with the break flag\n// and bit 5 ignored. Then PC is pulled from the stack.\n//\n// pull SR, pull PC\n// N    Z       C       I       D       V\n// from stack\n// addressing   assembler       opc     bytes   cycles\n// implied      RTI     40      1       6\n\n   MAKE_INSTR(0x40, RTI, IMP, 6, false);\n\n// RTS\n// Return from Subroutine\n//\n// pull PC, PC+1 -> PC\n// N    Z       C       I       D       V\n// -    -       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// implied      RTS     60      1       6\n\n   MAKE_INSTR(0x60, RTS, IMP, 6, false);\n\n// SBC\n// Subtract Memory from Accumulator with Borrow\n//\n// A - M - C̅ -> A\n// N    Z       C       I       D       V\n// +    +       +       -       -       +\n// addressing   assembler       opc     bytes   cycles\n// immediate    SBC #oper       E9      2       2\n// zeropage     SBC oper        E5      2       3\n// zeropage,X   SBC oper,X      F5      2       4\n// absolute     SBC oper        ED      3       4\n// absolute,X   SBC oper,X      FD      3       4*\n// absolute,Y   SBC oper,Y      F9      3       4*\n// (indirect,X) SBC (oper,X)    E1      2       6\n// (indirect),Y SBC (oper),Y    F1      2       5*\n\n   MAKE_INSTR(0xE9, SBC, IMM, 2, false);\n   MAKE_INSTR(0xE5, SBC, ZER, 3, false);\n   MAKE_INSTR(0xF5, SBC, ZEX, 4, false);\n   MAKE_INSTR(0xED, SBC, ABS, 4, false);\n   MAKE_INSTR(0xFD, SBC, ABX, 4, true);\n   MAKE_INSTR(0xF9, SBC, ABY, 4, true);\n   MAKE_INSTR(0xE1, SBC, INX, 6, false);\n   MAKE_INSTR(0xF1, SBC, INY, 5, true);\n\n// SEC\n// Set Carry Flag\n//\n// 1 -> C\n// N    Z       C       I       D       V\n// -    -       1       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// implied      SEC     38      1       2\n\n   MAKE_INSTR(0x38, SEC, IMP, 2, false);\n\n// SED\n// Set Decimal Flag\n//\n// 1 -> D\n// N    Z       C       I       D       V\n// -    -       -       -       1       -\n// addressing   assembler       opc     bytes   cycles\n// implied      SED     F8      1       2\n\n   MAKE_INSTR(0xF8, SED, IMP, 2, false);\n\n// SEI\n// Set Interrupt Disable Status\n//\n// 1 -> I\n// N    Z       C       I       D       V\n// -    -       -       1       -       -\n// addressing   assembler       opc     bytes   cycles\n// implied      SEI     78      1       2\n\n   MAKE_INSTR(0x78, SEI, IMP, 2, false);\n\n// STA\n// Store Accumulator in Memory\n//\n// A -> M\n// N    Z       C       I       D       V\n// -    -       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// zeropage     STA oper        85      2       3\n// zeropage,X   STA oper,X      95      2       4\n// absolute     STA oper        8D      3       4\n// absolute,X   STA oper,X      9D      3       5\n// absolute,Y   STA oper,Y      99      3       5\n// (indirect,X) STA (oper,X)    81      2       6\n// (indirect),Y STA (oper),Y    91      2       6\n\n   MAKE_INSTR(0x85, STA, ZER, 3, false);\n   MAKE_INSTR(0x95, STA, ZEX, 4, false);\n   MAKE_INSTR(0x8D, STA, ABS, 4, false);\n   MAKE_INSTR(0x9D, STA, ABX, 5, false);\n   MAKE_INSTR(0x99, STA, ABY, 5, false);\n   MAKE_INSTR(0x81, STA, INX, 6, false);\n   MAKE_INSTR(0x91, STA, INY, 6, false);\n\n// STX\n// Store Index X in Memory\n//\n// X -> M\n// N    Z       C       I       D       V\n// -    -       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// zeropage     STX oper        86      2       3\n// zeropage,Y   STX oper,Y      96      2       4\n// absolute     STX oper        8E      3       4\n\n   MAKE_INSTR(0x86, STX, ZER, 3, false);\n   MAKE_INSTR(0x96, STX, ZEY, 4, false);\n   MAKE_INSTR(0x8E, STX, ABS, 4, false);\n\n// STY\n// Sore Index Y in Memory\n//\n// Y -> M\n// N    Z       C       I       D       V\n// -    -       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// zeropage     STY oper        84      2       3\n// zeropage,X   STY oper,X      94      2       4\n// absolute     STY oper        8C      3       4\n\n   MAKE_INSTR(0x84, STY, ZER, 3, false);\n   MAKE_INSTR(0x94, STY, ZEX, 4, false);\n   MAKE_INSTR(0x8C, STY, ABS, 4, false);\n\n// TAX\n// Transfer Accumulator to Index X\n//\n// A -> X\n// N    Z       C       I       D       V\n// +    +       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// implied      TAX     AA      1       2\n\n   MAKE_INSTR(0xAA, TAX, IMP, 2, false);\n\n// TAY\n// Transfer Accumulator to Index Y\n//\n// A -> Y\n// N    Z       C       I       D       V\n// +    +       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// implied      TAY     A8      1       2\n\n   MAKE_INSTR(0xA8, TAY, IMP, 2, false);\n\n// TSX\n// Transfer Stack Pointer to Index X\n//\n// SP -> X\n// N    Z       C       I       D       V\n// +    +       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// implied      TSX     BA      1       2\n\n   MAKE_INSTR(0xBA, TSX, IMP, 2, false);\n\n// TXA\n// Transfer Index X to Accumulator\n//\n// X -> A\n// N    Z       C       I       D       V\n// +    +       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// implied      TXA     8A      1       2\n\n   MAKE_INSTR(0x8A, TXA, IMP, 2, false);\n\n// TXS\n// Transfer Index X to Stack Register\n//\n// X -> SP\n// N    Z       C       I       D       V\n// -    -       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// implied      TXS     9A      1       2\n\n   MAKE_INSTR(0x9A, TXS, IMP, 2, false);\n\n// TYA\n// Transfer Index Y to Accumulator\n//\n// Y -> A\n// N    Z       C       I       D       V\n// +    +       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// implied      TYA     98      1       2\n\n   MAKE_INSTR(0x98, TYA, IMP, 2, false);\n\n#ifdef ILLEGAL_OPCODES\n\n// ALR (ASR)\n// AND oper + LSR\n//\n// A AND oper, 0 -> [76543210] -> C\n//\n// N    Z       C       I       D       V\n// +    +       +       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// immediate    ALR #oper       4B      2       2\n\n   MAKE_INSTR(0x4B, ALR, IMM, 2, false);\n\n// ANC\n// AND oper + set C as ASL\n//\n// A AND oper, bit(7) -> C\n//\n// N    Z       C       I       D       V\n// +    +       +       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// immediate    ANC #oper       0B      2       2\n\n   MAKE_INSTR(0x0B, ANC, IMM, 2, false);\n\n// ANC (ANC2)\n// AND oper + set C as ROL\n//\n// effectively the same as instr. 0B\n//\n// A AND oper, bit(7) -> C\n//\n// N    Z       C       I       D       V\n// +    +       +       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// immediate    ANC #oper       2B      2       2\n\n   MAKE_INSTR(0x2B, ANC, IMM, 2, false);\n\n// ANE (XAA)\n// * OR X + AND oper\n//\n// Highly unstable, do not use.\n//\n// A base value in A is determined based on the contets of A and a constant,\n// which may be typically $00, $ff, $ee, etc. The value of this constant\n// depends on temperature, the chip series, and maybe other factors, as well.\n// In order to eliminate these uncertaincies from the equation, use either 0\n// as the operand or a value of $FF in the accumulator.\n//\n// (A OR CONST) AND X AND oper -> A\n//\n// N    Z       C       I       D       V\n// +    +       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// immediate    ANE #oper       8B      2       2       ††\n\n   MAKE_INSTR(0x8B, ANE, IMM, 2, false);\n\n// ARR\n// AND oper + ROR\n//\n// This operation involves the adder:\n// V-flag is set according to (A AND oper) + oper\n// The carry is not set, but bit 7 (sign) is exchanged with the carry\n//\n// A AND oper, C -> [76543210] -> C\n//\n// N    Z       C       I       D       V\n// +    +       +       -       -       +\n// addressing   assembler       opc     bytes   cycles\n// immediate    ARR #oper       6B      2       2\n\n   MAKE_INSTR(0x6B, ARR, IMM, 2, false);\n\n// DCP (DCM)\n// DEC oper + CMP oper\n//\n// M - 1 -> M, A - M\n//\n// Decrements the operand and then compares the result to the accumulator.\n//\n// N    Z       C       I       D       V\n// +    +       +       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// zeropage     DCP oper        C7      2       5\n// zeropage,X   DCP oper,X      D7      2       6\n// absolute     DCP oper        CF      3       6\n// absolute,X   DCP oper,X      DF      3       7\n// absolute,Y   DCP oper,Y      DB      3       7\n// (indirect,X) DCP (oper,X)    C3      2       8\n// (indirect),Y DCP (oper),Y    D3      2       8\n\n   MAKE_INSTR(0xC7, DCP, ZER, 5, false);\n   MAKE_INSTR(0xD7, DCP, ZEX, 6, false);\n   MAKE_INSTR(0xCF, DCP, ABS, 6, false);\n   MAKE_INSTR(0xDF, DCP, ABX, 7, false);\n   MAKE_INSTR(0xDB, DCP, ABY, 7, false);\n   MAKE_INSTR(0xC3, DCP, INX, 8, false);\n   MAKE_INSTR(0xD3, DCP, INY, 8, false);\n\n// ISC (ISB, INS)\n// INC oper + SBC oper\n//\n// M + 1 -> M, A - M - C̅ -> A\n//\n// N    Z       C       I       D       V\n// +    +       +       -       -       +\n// addressing   assembler       opc     bytes   cycles\n// zeropage     ISC oper        E7      2       5\n// zeropage,X   ISC oper,X      F7      2       6\n// absolute     ISC oper        EF      3       6\n// absolute,X   ISC oper,X      FF      3       7\n// absolute,Y   ISC oper,Y      FB      3       7\n// (indirect,X) ISC (oper,X)    E3      2       8\n// (indirect),Y ISC (oper),Y    F3      2       8\n\n   MAKE_INSTR(0xE7, ISC, ZER, 5, false);\n   MAKE_INSTR(0xF7, ISC, ZEX, 6, false);\n   MAKE_INSTR(0xEF, ISC, ABS, 6, false);\n   MAKE_INSTR(0xFF, ISC, ABX, 7, false);\n   MAKE_INSTR(0xFB, ISC, ABY, 7, false);\n   MAKE_INSTR(0xE3, ISC, INX, 8, false);\n   MAKE_INSTR(0xF3, ISC, INY, 8, false);\n\n// LAS (LAR)\n// LDA/TSX oper\n//\n// M AND SP -> A, X, SP\n//\n// N    Z       C       I       D       V\n// +    +       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// absolute,Y   LAS oper,Y      BB      3       4*\n\n   MAKE_INSTR(0xBB, LAS, ABY, 4, true);\n\n// LAX\n// LDA oper + LDX oper\n//\n// M -> A -> X\n//\n// N    Z       C       I       D       V\n// +    +       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// zeropage     LAX oper        A7      2       3\n// zeropage,Y   LAX oper,Y      B7      2       4\n// absolute     LAX oper        AF      3       4\n// absolute,Y   LAX oper,Y      BF      3       4*\n// (indirect,X) LAX (oper,X)    A3      2       6\n// (indirect),Y LAX (oper),Y    B3      2       5*\n\n   MAKE_INSTR(0xA7, LAX, ZER, 3, false);\n   MAKE_INSTR(0xB7, LAX, ZEY, 4, false);\n   MAKE_INSTR(0xAF, LAX, ABS, 4, false);\n   MAKE_INSTR(0xBF, LAX, ABY, 4, true);\n   MAKE_INSTR(0xA3, LAX, INX, 6, false);\n   MAKE_INSTR(0xB3, LAX, INY, 5, true);\n\n// LXA (LAX immediate)\n// Store * AND oper in A and X\n//\n// Highly unstable, involves a 'magic' constant, see ANE\n//\n// (A OR CONST) AND oper -> A -> X\n//\n// N    Z       C       I       D       V\n// +    +       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// immediate    LXA #oper       AB      2       2       ††\n\n   MAKE_INSTR(0xAB, LXA, IMM, 2, false);\n\n// RLA\n// ROL oper + AND oper\n//\n// M = C <- [76543210] <- C, A AND M -> A\n//\n// N    Z       C       I       D       V\n// +    +       +       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// zeropage     RLA oper        27      2       5\n// zeropage,X   RLA oper,X      37      2       6\n// absolute     RLA oper        2F      3       6\n// absolute,X   RLA oper,X      3F      3       7\n// absolute,Y   RLA oper,Y      3B      3       7\n// (indirect,X) RLA (oper,X)    23      2       8\n// (indirect),Y RLA (oper),Y    33      2       8\n\n   MAKE_INSTR(0x27, RLA, ZER, 5, false);\n   MAKE_INSTR(0x37, RLA, ZEX, 6, false);\n   MAKE_INSTR(0x2F, RLA, ABS, 6, false);\n   MAKE_INSTR(0x3F, RLA, ABX, 7, false);\n   MAKE_INSTR(0x3B, RLA, ABY, 7, false);\n   MAKE_INSTR(0x23, RLA, INX, 8, false);\n   MAKE_INSTR(0x33, RLA, INY, 8, false);\n\n// RRA\n// ROR oper + ADC oper\n//\n// M = C -> [76543210] -> C, A + M + C -> A, C\n//\n// N    Z       C       I       D       V\n// +    +       +       -       -       +\n// addressing   assembler       opc     bytes   cycles\n// zeropage     RRA oper        67      2       5\n// zeropage,X   RRA oper,X      77      2       6\n// absolute     RRA oper        6F      3       6\n// absolute,X   RRA oper,X      7F      3       7\n// absolute,Y   RRA oper,Y      7B      3       7\n// (indirect,X) RRA (oper,X)    63      2       8\n// (indirect),Y RRA (oper),Y    73      2       8\n\n   MAKE_INSTR(0x67, RRA, ZER, 5, false);\n   MAKE_INSTR(0x77, RRA, ZEX, 6, false);\n   MAKE_INSTR(0x6F, RRA, ABS, 6, false);\n   MAKE_INSTR(0x7F, RRA, ABX, 7, false);\n   MAKE_INSTR(0x7B, RRA, ABY, 7, false);\n   MAKE_INSTR(0x63, RRA, INX, 8, false);\n   MAKE_INSTR(0x73, RRA, INY, 8, false);\n\n// SAX (AXS, AAX)\n// A and X are put on the bus at the same time (resulting effectively in an AND operation) and stored in M\n//\n// A AND X -> M\n//\n// N    Z       C       I       D       V\n// -    -       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// zeropage     SAX oper        87      2       3\n// zeropage,Y   SAX oper,Y      97      2       4\n// absolute     SAX oper        8F      3       4\n// (indirect,X) SAX (oper,X)    83      2       6\n\n   MAKE_INSTR(0x87, SAX, ZER, 3, false);\n   MAKE_INSTR(0x97, SAX, ZEY, 4, false);\n   MAKE_INSTR(0x8F, SAX, ABS, 4, false);\n   MAKE_INSTR(0x83, SAX, INX, 6, false);\n\n// SBX (AXS, SAX)\n// CMP and DEX at once, sets flags like CMP\n//\n// (A AND X) - oper -> X\n//\n// N    Z       C       I       D       V\n// +    +       +       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// immediate    SBX #oper       CB      2       2\n\n   MAKE_INSTR(0xCB, SBX, IMM, 2, false);\n\n// SHA (AHX, AXA)\n// Stores A AND X AND (high-byte of addr. + 1) at addr.\n//\n// unstable: sometimes 'AND (H+1)' is dropped, page boundary crossings\n// may not work (with the high-byte of the value used as the high-byte of the address)\n//\n// A AND X AND (H+1) -> M\n//\n// N    Z       C       I       D       V\n// -    -       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// absolute,Y   SHA oper,Y      9F      3       5       †\n// (indirect),Y SHA (oper),Y    93      2       6       †\n\n   MAKE_INSTR(0x9F, SHA, ABY, 5, false);\n   MAKE_INSTR(0x93, SHA, INY, 6, false);\n\n// SHX (A11, SXA, XAS)\n// Stores X AND (high-byte of addr. + 1) at addr.\n//\n// unstable: sometimes 'AND (H+1)' is dropped, page boundary\n// crossings may not work (with the high-byte of the value used as the high-byte of the address)\n//\n// X AND (H+1) -> M\n//\n// N    Z       C       I       D       V\n// -    -       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// absolute,Y   SHX oper,Y      9E      3       5       †\n\n   MAKE_INSTR(0x9E, SHX, ABY, 5, false);\n\n// SHY (A11, SYA, SAY)\n// Stores Y AND (high-byte of addr. + 1) at addr.\n//\n// unstable: sometimes 'AND (H+1)' is dropped, page boundary\n// crossings may not work (with the high-byte of the value used as the high-byte of the address)\n//\n// Y AND (H+1) -> M\n//\n// N    Z       C       I       D       V\n// -    -       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// absolute,X   SHY oper,X      9C      3       5       †\n\n   MAKE_INSTR(0x9C, SHY, ABX, 5, false);\n\n// SLO (ASO)\n// ASL oper + ORA oper\n//\n// M = C <- [76543210] <- 0, A OR M -> A\n//\n// N    Z       C       I       D       V\n// +    +       +       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// zeropage     SLO oper        07      2       5\n// zeropage,X   SLO oper,X      17      2       6\n// absolute     SLO oper        0F      3       6\n// absolute,X   SLO oper,X      1F      3       7\n// absolute,Y   SLO oper,Y      1B      3       7\n// (indirect,X) SLO (oper,X)    03      2       8\n// (indirect),Y SLO (oper),Y    13      2       8\n\n   MAKE_INSTR(0x07, SLO, ZER, 5, false);\n   MAKE_INSTR(0x17, SLO, ZEX, 6, false);\n   MAKE_INSTR(0x0F, SLO, ABS, 6, false);\n   MAKE_INSTR(0x1F, SLO, ABX, 7, false);\n   MAKE_INSTR(0x1B, SLO, ABY, 7, false);\n   MAKE_INSTR(0x03, SLO, INX, 8, false);\n   MAKE_INSTR(0x13, SLO, INY, 8, false);\n\n// SRE (LSE)\n// LSR oper + EOR oper\n//\n// M = 0 -> [76543210] -> C, A EOR M -> A\n//\n// N    Z       C       I       D       V\n// +    +       +       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// zeropage     SRE oper        47      2       5\n// zeropage,X   SRE oper,X      57      2       6\n// absolute     SRE oper        4F      3       6\n// absolute,X   SRE oper,X      5F      3       7\n// absolute,Y   SRE oper,Y      5B      3       7\n// (indirect,X) SRE (oper,X)    43      2       8\n// (indirect),Y SRE (oper),Y    53      2       8\n\n   MAKE_INSTR(0x47, SRE, ZER, 5, false);\n   MAKE_INSTR(0x57, SRE, ZEX, 6, false);\n   MAKE_INSTR(0x4F, SRE, ABS, 6, false);\n   MAKE_INSTR(0x5F, SRE, ABX, 7, false);\n   MAKE_INSTR(0x5B, SRE, ABY, 7, false);\n   MAKE_INSTR(0x43, SRE, INX, 8, false);\n   MAKE_INSTR(0x53, SRE, INY, 8, false);\n\n// TAS (XAS, SHS)\n// Puts A AND X in SP and stores A AND X AND (high-byte of addr. + 1) at addr.\n//\n// unstable: sometimes 'AND (H+1)' is dropped, page boundary\n// crossings may not work (with the high-byte of the value used as the high-byte of the address)\n//\n// A AND X -> SP, A AND X AND (H+1) -> M\n//\n// N    Z       C       I       D       V\n// -    -       -       -       -       -\n// addressing   assembler       opc     bytes   cycles\n// absolute,Y   TAS oper,Y      9B      3       5       †\n\n   MAKE_INSTR(0x9B, TAS, ABY, 5, false);\n\n// USBC (SBC)\n// SBC oper + NOP\n//\n// effectively same as normal SBC immediate, instr. E9.\n//\n// A - M - C̅ -> A\n//\n// N    Z       C       I       D       V\n// +    +       +       -       -       +\n// addressing   assembler       opc     bytes   cycles\n// immediate    USBC #oper      EB      2       2\n\n   MAKE_INSTR(0xEB, SBC, IMM, 2, false);\n\n// NOPs (including DOP, TOP)\n// Instructions effecting in 'no operations' in various address modes. Operands are ignored.\n//\n// N    Z       C       I       D       V\n// -    -       -       -       -       -\n// opc  addressing      bytes   cycles\n// 1A   implied         1       2\n// 3A   implied         1       2\n// 5A   implied         1       2\n// 7A   implied         1       2\n// DA   implied         1       2\n// FA   implied         1       2\n\n   MAKE_INSTR(0x1A, NOP, IMP, 2, false);\n   MAKE_INSTR(0x3A, NOP, IMP, 2, false);\n   MAKE_INSTR(0x5A, NOP, IMP, 2, false);\n   MAKE_INSTR(0x7A, NOP, IMP, 2, false);\n   MAKE_INSTR(0xDA, NOP, IMP, 2, false);\n   MAKE_INSTR(0xFA, NOP, IMP, 2, false);\n\n// 80   immediate       2       2\n// 82   immediate       2       2\n// 89   immediate       2       2\n// C2   immediate       2       2\n// E2   immediate       2       2\n\n   MAKE_INSTR(0x80, NOP, IMM, 2, false);\n   MAKE_INSTR(0x82, NOP, IMM, 2, false);\n   MAKE_INSTR(0x89, NOP, IMM, 2, false);\n   MAKE_INSTR(0xC2, NOP, IMM, 2, false);\n   MAKE_INSTR(0xE2, NOP, IMM, 2, false);\n\n// 04   zeropage        2       3\n// 44   zeropage        2       3\n// 64   zeropage        2       3\n\n   MAKE_INSTR(0x04, NOP, ZER, 3, false);\n   MAKE_INSTR(0x44, NOP, ZER, 3, false);\n   MAKE_INSTR(0x64, NOP, ZER, 3, false);\n\n// 14   zeropage,X      2       4\n// 34   zeropage,X      2       4\n// 54   zeropage,X      2       4\n// 74   zeropage,X      2       4\n// D4   zeropage,X      2       4\n// F4   zeropage,X      2       4\n\n   MAKE_INSTR(0x14, NOP, ZEX, 4, false);\n   MAKE_INSTR(0x34, NOP, ZEX, 4, false);\n   MAKE_INSTR(0x54, NOP, ZEX, 4, false);\n   MAKE_INSTR(0x74, NOP, ZEX, 4, false);\n   MAKE_INSTR(0xD4, NOP, ZEX, 4, false);\n   MAKE_INSTR(0xF4, NOP, ZEX, 4, false);\n\n// 0C   absolute        3       4\n\n   MAKE_INSTR(0x0C, NOP, ABS, 4, false);\n\n// 1C   absolute,X      3       4*\n// 3C   absolute,X      3       4*\n// 5C   absolute,X      3       4*\n// 7C   absolute,X      3       4*\n// DC   absolute,X      3       4*\n// FC   absolute,X      3       4*\n\n   MAKE_INSTR(0x1C, NOP, ABX, 4, true);\n   MAKE_INSTR(0x3C, NOP, ABX, 4, true);\n   MAKE_INSTR(0x5C, NOP, ABX, 4, true);\n   MAKE_INSTR(0x7C, NOP, ABX, 4, true);\n   MAKE_INSTR(0xDC, NOP, ABX, 4, true);\n   MAKE_INSTR(0xFC, NOP, ABX, 4, true);\n\n// JAM (KIL, HLT)\n// These instructions freeze the CPU.\n//\n// The processor will be trapped infinitely in T1 phase with $FF on the data bus. — Reset required.\n//\n// Instruction codes: 02, 12, 22, 32, 42, 52, 62, 72, 92, B2, D2, F2\n\n   MAKE_INSTR(0x02, ILLEGAL, IMP, 0, false);\n   MAKE_INSTR(0x12, ILLEGAL, IMP, 0, false);\n   MAKE_INSTR(0x22, ILLEGAL, IMP, 0, false);\n   MAKE_INSTR(0x32, ILLEGAL, IMP, 0, false);\n   MAKE_INSTR(0x42, ILLEGAL, IMP, 0, false);\n   MAKE_INSTR(0x52, ILLEGAL, IMP, 0, false);\n   MAKE_INSTR(0x62, ILLEGAL, IMP, 0, false);\n   MAKE_INSTR(0x72, ILLEGAL, IMP, 0, false);\n   MAKE_INSTR(0x92, ILLEGAL, IMP, 0, false);\n   MAKE_INSTR(0xB2, ILLEGAL, IMP, 0, false);\n   MAKE_INSTR(0xD2, ILLEGAL, IMP, 0, false);\n   MAKE_INSTR(0xF2, ILLEGAL, IMP, 0, false);\n\n#endif\n\n   return;\n}\n\nuint16_t mos6502::Addr_ACC()\n{\n   return 0; // not used\n}\n\nuint16_t mos6502::Addr_IMM()\n{\n   return pc++;\n}\n\nuint16_t mos6502::Addr_ABS()\n{\n   uint16_t addrL;\n   uint16_t addrH;\n   uint16_t addr;\n\n   addrL = Read(pc++);\n   addrH = Read(pc++);\n\n   addr = addrL + (addrH << 8);\n\n   return addr;\n}\n\nuint16_t mos6502::Addr_ZER()\n{\n   return Read(pc++);\n}\n\nuint16_t mos6502::Addr_IMP()\n{\n   return 0; // not used\n}\n\nuint16_t mos6502::Addr_REL()\n{\n   uint16_t offset;\n   uint16_t addr;\n\n   offset = (uint16_t)Read(pc++);\n   if (offset & 0x80) offset |= 0xFF00;\n   addr = pc + (int16_t)offset;\n   crossed = (addr & 0xFF00) != (pc & 0xFF00);\n\n   return addr;\n}\n\nuint16_t mos6502::Addr_ABI()\n{\n   uint16_t addrL;\n   uint16_t addrH;\n   uint16_t effL;\n   uint16_t effH;\n   uint16_t abs;\n   uint16_t addr;\n\n   addrL = Read(pc++);\n   addrH = Read(pc++);\n\n   abs = (addrH << 8) | addrL;\n\n   effL = Read(abs);\n\n#ifndef CMOS_INDIRECT_JMP_FIX\n   effH = Read((abs & 0xFF00) + ((abs + 1) & 0x00FF) );\n#else\n   effH = Read(abs + 1);\n#endif\n\n   addr = effL + 0x100 * effH;\n\n   return addr;\n}\n\nuint16_t mos6502::Addr_ZEX()\n{\n   uint16_t addr = (Read(pc++) + X) & 0xFF;\n   return addr;\n}\n\nuint16_t mos6502::Addr_ZEY()\n{\n   uint16_t addr = (Read(pc++) + Y) & 0xFF;\n   return addr;\n}\n\nuint16_t mos6502::Addr_ABX()\n{\n   uint16_t addr;\n   uint16_t addrL;\n   uint16_t addrH;\n\n   addrL = Read(pc++);\n   addrH = Read(pc++);\n\n   addr = addrL + (addrH << 8) + X;\n   crossed = (addrL + X) > 255;\n   return addr;\n}\n\nuint16_t mos6502::Addr_ABY()\n{\n   uint16_t addr;\n   uint16_t addrL;\n   uint16_t addrH;\n\n   addrL = Read(pc++);\n   addrH = Read(pc++);\n\n   addr = addrL + (addrH << 8) + Y;\n   crossed = (addrL + Y) > 255;\n   return addr;\n}\n\nuint16_t mos6502::Addr_INX()\n{\n   uint16_t zeroL;\n   uint16_t zeroH;\n   uint16_t addr;\n\n   zeroL = (Read(pc++) + X) & 0xFF;\n   zeroH = (zeroL + 1) & 0xFF;\n   addr = Read(zeroL) + (Read(zeroH) << 8);\n\n   return addr;\n}\n\nuint16_t mos6502::Addr_INY()\n{\n   uint16_t baseL;\n   uint16_t zeroL;\n   uint16_t zeroH;\n   uint16_t addr;\n\n   zeroL = Read(pc++);\n   zeroH = (zeroL + 1) & 0xFF;\n   addr = (baseL = /* ASSIGN */ Read(zeroL)) + (Read(zeroH) << 8) + Y;\n   crossed = (baseL + Y) > 255;\n\n   return addr;\n}\n\nvoid mos6502::IRQ(bool line)\n{\n   irq_line = line;\n}\n\nvoid mos6502::NMI(bool line)\n{\n   // falling edge triggered\n   if (nmi_line == true && line == false) {\n      if (!nmi_inhibit) {\n         nmi_request = true;\n      }\n   }\n   nmi_line = line;\n}\n\nvoid mos6502::Reset()\n{\n   // do not set or clear irq_line, that's external to us\n   // do not set or clear nmi_line, that's external to us\n   nmi_request = false;\n   nmi_inhibit = false;\n\n   A = reset_A;\n   Y = reset_Y;\n   X = reset_X;\n\n   // load PC from reset vector\n   uint8_t pcl = Read(rstVectorL);\n   uint8_t pch = Read(rstVectorH);\n   pc = (pch << 8) + pcl;\n\n   sp = reset_sp;\n\n   status = reset_status | CONSTANT | BREAK;\n\n   illegalOpcode = false;\n\n   return;\n}\n\nvoid mos6502::StackPush(uint8_t byte)\n{\n   Write(0x0100 + sp, byte);\n   if(sp == 0x00) sp = 0xFF;\n   else sp--;\n}\n\nuint8_t mos6502::StackPop()\n{\n   if(sp == 0xFF) sp = 0x00;\n   else sp++;\n   return Read(0x0100 + sp);\n}\n\nvoid mos6502::Svc_IRQ()\n{\n   //SET_BREAK(0);\n   StackPush((pc >> 8) & 0xFF);\n   StackPush(pc & 0xFF);\n   StackPush((status & ~BREAK) | CONSTANT);\n   SET_INTERRUPT(1);\n\n   // load PC from interrupt request vector\n   uint8_t pcl = Read(irqVectorL);\n   uint8_t pch = Read(irqVectorH);\n   pc = (pch << 8) + pcl;\n   return;\n}\n\nvoid mos6502::Svc_NMI()\n{\n   //SET_BREAK(0);\n   StackPush((pc >> 8) & 0xFF);\n   StackPush(pc & 0xFF);\n   StackPush((status & ~BREAK) | CONSTANT);\n   SET_INTERRUPT(1);\n\n   // load PC from non-maskable interrupt vector\n   uint8_t pcl = Read(nmiVectorL);\n   uint8_t pch = Read(nmiVectorH);\n   pc = (pch << 8) + pcl;\n   return;\n}\n\nbool mos6502::CheckInterrupts() {\n\n   // NMI is edge triggered\n   if (nmi_request && !nmi_inhibit) {\n      nmi_request = false;\n      nmi_inhibit = true;\n      Svc_NMI();\n      return true;\n   }\n\n   // check disabled bit\n   if(!IF_INTERRUPT()) {\n      // IRQ is level triggered\n      if (irq_line == false && !nmi_inhibit) {\n         Svc_IRQ();\n         return true;\n      }\n   }\n\n   return false;\n}\n\nvoid mos6502::Run(\n      int32_t cyclesRemaining,\n      uint64_t& cycleCount,\n      CycleMethod cycleMethod)\n{\n   uint8_t opcode;\n   Instr instr;\n\n   while(cyclesRemaining > 0 && !illegalOpcode)\n   {\n      if (CheckInterrupts()) {\n         cycleCount += 6; // TODO FIX verify this is correct\n      }\n\n      // fetch\n      opcode = Read(pc++);\n\n      // decode\n      instr = InstrTable[opcode];\n\n      // execute\n      Exec(instr);\n\n      cycleCount += instr.cycles;\n      if (branched) {\n         cycleCount++;\n      }\n      if (instr.penalty && crossed) {\n         cycleCount++;\n      }\n      cyclesRemaining -=\n         cycleMethod == CYCLE_COUNT        ? instr.cycles\n         /* cycleMethod == INST_COUNT */   : 1;\n\n      // run clock cycle callback\n      if (Cycle)\n         for(int i = 0; i < instr.cycles; i++)\n            Cycle(this);\n   }\n}\n\nvoid mos6502::RunEternally()\n{\n   uint8_t opcode;\n   Instr instr;\n\n   while(!illegalOpcode)\n   {\n      CheckInterrupts();\n\n      // fetch\n      opcode = Read(pc++);\n\n      // decode\n      instr = InstrTable[opcode];\n\n      // execute\n      Exec(instr);\n\n      // run clock cycle callback\n      if (Cycle)\n         for(int i = 0; i < instr.cycles; i++)\n            Cycle(this);\n   }\n}\n\nvoid mos6502::Exec(Instr i)\n{\n   crossed = false;\n   branched = false;\n   uint16_t src = (this->*i.addr)();\n   (this->*i.code)(src);\n}\n\nuint16_t mos6502::GetPC()\n{\n   return pc;\n}\n\nuint8_t mos6502::GetS()\n{\n   return sp;\n}\n\nuint8_t mos6502::GetP()\n{\n   return status;\n}\n\nuint8_t mos6502::GetA()\n{\n   return A;\n}\n\nuint8_t mos6502::GetX()\n{\n   return X;\n}\n\nuint8_t mos6502::GetY()\n{\n   return Y;\n}\n\nvoid mos6502::SetPC(uint16_t n)\n{\n   pc = n;\n}\n\nvoid mos6502::SetS (uint8_t n)\n{\n   sp = n;\n}\n\nvoid mos6502::SetP (uint8_t n)\n{\n   status = n;\n}\n\nvoid mos6502::SetA (uint8_t n)\n{\n   A = n;\n}\n\nvoid mos6502::SetX (uint8_t n)\n{\n   X = n;\n}\n\nvoid mos6502::SetY (uint8_t n)\n{\n   Y = n;\n}\n\nvoid mos6502::SetResetS(uint8_t value)\n{\n   reset_sp = value;\n}\n\nvoid mos6502::SetResetA(uint8_t value)\n{\n   reset_A = value;\n}\n\nvoid mos6502::SetResetX(uint8_t value)\n{\n   reset_X = value;\n}\n\nvoid mos6502::SetResetY(uint8_t value)\n{\n   reset_Y = value;\n}\n\nvoid mos6502::SetResetP(uint8_t value)\n{\n   reset_status = value | CONSTANT | BREAK;\n}\n\nuint8_t mos6502::GetResetS()\n{\n   return reset_sp;\n}\n\nuint8_t mos6502::GetResetP()\n{\n   return reset_status;\n}\n\nuint8_t mos6502::GetResetA()\n{\n   return reset_A;\n}\n\nuint8_t mos6502::GetResetX()\n{\n   return reset_X;\n}\n\nuint8_t mos6502::GetResetY()\n{\n   return reset_Y;\n}\n\nvoid mos6502::Op_ILLEGAL(uint16_t src)\n{\n   illegalOpcode = true;\n}\n\nvoid mos6502::Op_ADC(uint16_t src)\n{\n   uint8_t m = Read(src);\n   unsigned int tmp = m + A + (IF_CARRY() ? 1 : 0);\n\n   // N V Z computed *BEFORE* adjustment\n   SET_OVERFLOW(!((A ^ m) & 0x80) && ((A ^ tmp) & 0x80));\n   SET_NEGATIVE(tmp & 0x80);\n   SET_ZERO(!(tmp & 0xFF));\n\n   if (IF_DECIMAL())\n   {\n      // see http://www.6502.org/tutorials/decimal_mode.html\n      int AL = ((A & 0xF) + (m & 0xF) + (IF_CARRY() ? 1 : 0));\n      if (AL >= 0xA) {\n         AL = ((AL + 6) & 0xF) + 0x10;\n      }\n      tmp = (m & 0xF0) + (A & 0xF0) + AL;\n\n      // N V recomputed\n      SET_OVERFLOW(!((A ^ m) & 0x80) && ((A ^ tmp) & 0x80));\n      SET_NEGATIVE(tmp & 0x80);\n\n      if (tmp >= 0xA0) tmp += 0x60;\n   }\n\n   // C computed *AFTER* adjustment\n   SET_CARRY(tmp > 0xFF);\n   A = tmp & 0xFF;\n   return;\n}\n\n\n\nvoid mos6502::Op_AND(uint16_t src)\n{\n   uint8_t m = Read(src);\n   uint8_t res = m & A;\n   SET_NEGATIVE(res & 0x80);\n   SET_ZERO(!res);\n   A = res;\n   return;\n}\n\n\nvoid mos6502::Op_ASL(uint16_t src)\n{\n   uint8_t m = Read(src);\n   SET_CARRY(m & 0x80);\n   m <<= 1;\n   m &= 0xFF;\n   SET_NEGATIVE(m & 0x80);\n   SET_ZERO(!m);\n   Write(src, m);\n   return;\n}\n\nvoid mos6502::Op_ASL_ACC(uint16_t src)\n{\n   uint8_t m = A;\n   SET_CARRY(m & 0x80);\n   m <<= 1;\n   m &= 0xFF;\n   SET_NEGATIVE(m & 0x80);\n   SET_ZERO(!m);\n   A = m;\n   return;\n}\n\nvoid mos6502::Op_BCC(uint16_t src)\n{\n   if (!IF_CARRY())\n   {\n      pc = src;\n      branched = true; // indicate we did branch\n   }\n   else {\n      crossed = false; // branch not taken does not suffer penalty\n   }\n   return;\n}\n\nvoid mos6502::Op_BCS(uint16_t src)\n{\n   if (IF_CARRY())\n   {\n      pc = src;\n      branched = true; // indicate we did branch\n   }\n   else {\n      crossed = false; // branch not taken does not suffer penalty\n   }\n   return;\n}\n\nvoid mos6502::Op_BEQ(uint16_t src)\n{\n   if (IF_ZERO())\n   {\n      pc = src;\n      branched = true; // indicate we did branch\n   }\n   else {\n      crossed = false; // branch not taken does not suffer penalty\n   }\n   return;\n}\n\nvoid mos6502::Op_BIT(uint16_t src)\n{\n   uint8_t m = Read(src);\n   uint8_t res = m & A;\n   status = (status & 0x3F) | (uint8_t)(m & 0xC0);\n   SET_ZERO(!res);\n   return;\n}\n\nvoid mos6502::Op_BMI(uint16_t src)\n{\n   if (IF_NEGATIVE())\n   {\n      pc = src;\n      branched = true; // indicate we did branch\n   }\n   else {\n      crossed = false; // branch not taken does not suffer penalty\n   }\n   return;\n}\n\nvoid mos6502::Op_BNE(uint16_t src)\n{\n   if (!IF_ZERO())\n   {\n      pc = src;\n      branched = true; // indicate we did branch\n   }\n   else {\n      crossed = false; // branch not taken does not suffer penalty\n   }\n   return;\n}\n\nvoid mos6502::Op_BPL(uint16_t src)\n{\n   if (!IF_NEGATIVE())\n   {\n      pc = src;\n      branched = true; // indicate we did branch\n   }\n   else {\n      crossed = false; // branch not taken does not suffer penalty\n   }\n   return;\n}\n\nvoid mos6502::Op_BRK(uint16_t src)\n{\n   pc++;\n   StackPush((pc >> 8) & 0xFF);\n   StackPush(pc & 0xFF);\n   StackPush(status | CONSTANT | BREAK);\n   SET_INTERRUPT(1);\n   pc = (Read(irqVectorH) << 8) + Read(irqVectorL);\n   return;\n}\n\nvoid mos6502::Op_BVC(uint16_t src)\n{\n   if (!IF_OVERFLOW())\n   {\n      pc = src;\n      branched = true; // indicate we did branch\n   }\n   else {\n      crossed = false; // branch not taken does not suffer penalty\n   }\n   return;\n}\n\nvoid mos6502::Op_BVS(uint16_t src)\n{\n   if (IF_OVERFLOW())\n   {\n      pc = src;\n      branched = true; // indicate we did branch\n   }\n   else {\n      crossed = false; // branch not taken does not suffer penalty\n   }\n   return;\n}\n\nvoid mos6502::Op_CLC(uint16_t src)\n{\n   SET_CARRY(0);\n   return;\n}\n\nvoid mos6502::Op_CLD(uint16_t src)\n{\n   SET_DECIMAL(0);\n   return;\n}\n\nvoid mos6502::Op_CLI(uint16_t src)\n{\n   SET_INTERRUPT(0);\n   return;\n}\n\nvoid mos6502::Op_CLV(uint16_t src)\n{\n   SET_OVERFLOW(0);\n   return;\n}\n\nvoid mos6502::Op_CMP(uint16_t src)\n{\n   unsigned int tmp = A - Read(src);\n   SET_CARRY(tmp < 0x100);\n   SET_NEGATIVE(tmp & 0x80);\n   SET_ZERO(!(tmp & 0xFF));\n   return;\n}\n\nvoid mos6502::Op_CPX(uint16_t src)\n{\n   unsigned int tmp = X - Read(src);\n   SET_CARRY(tmp < 0x100);\n   SET_NEGATIVE(tmp & 0x80);\n   SET_ZERO(!(tmp & 0xFF));\n   return;\n}\n\nvoid mos6502::Op_CPY(uint16_t src)\n{\n   unsigned int tmp = Y - Read(src);\n   SET_CARRY(tmp < 0x100);\n   SET_NEGATIVE(tmp & 0x80);\n   SET_ZERO(!(tmp & 0xFF));\n   return;\n}\n\nvoid mos6502::Op_DEC(uint16_t src)\n{\n   uint8_t m = Read(src);\n   m = (m - 1) & 0xFF;\n   SET_NEGATIVE(m & 0x80);\n   SET_ZERO(!m);\n   Write(src, m);\n   return;\n}\n\nvoid mos6502::Op_DEX(uint16_t src)\n{\n   uint8_t m = X;\n   m = (m - 1) & 0xFF;\n   SET_NEGATIVE(m & 0x80);\n   SET_ZERO(!m);\n   X = m;\n   return;\n}\n\nvoid mos6502::Op_DEY(uint16_t src)\n{\n   uint8_t m = Y;\n   m = (m - 1) & 0xFF;\n   SET_NEGATIVE(m & 0x80);\n   SET_ZERO(!m);\n   Y = m;\n   return;\n}\n\nvoid mos6502::Op_EOR(uint16_t src)\n{\n   uint8_t m = Read(src);\n   m = A ^ m;\n   SET_NEGATIVE(m & 0x80);\n   SET_ZERO(!m);\n   A = m;\n}\n\nvoid mos6502::Op_INC(uint16_t src)\n{\n   uint8_t m = Read(src);\n   m = (m + 1) & 0xFF;\n   SET_NEGATIVE(m & 0x80);\n   SET_ZERO(!m);\n   Write(src, m);\n}\n\nvoid mos6502::Op_INX(uint16_t src)\n{\n   uint8_t m = X;\n   m = (m + 1) & 0xFF;\n   SET_NEGATIVE(m & 0x80);\n   SET_ZERO(!m);\n   X = m;\n}\n\nvoid mos6502::Op_INY(uint16_t src)\n{\n   uint8_t m = Y;\n   m = (m + 1) & 0xFF;\n   SET_NEGATIVE(m & 0x80);\n   SET_ZERO(!m);\n   Y = m;\n}\n\nvoid mos6502::Op_JMP(uint16_t src)\n{\n   pc = src;\n}\n\nvoid mos6502::Op_JSR(uint16_t src)\n{\n   pc--;\n   StackPush((pc >> 8) & 0xFF);\n   StackPush(pc & 0xFF);\n\n   // this fixes an obscure problem that only happens when\n   // the operand and the processor stack are at the same\n   // place...\n   src = (src & 0xFF) | (Read(pc) << 8);\n\n   pc = src;\n}\n\nvoid mos6502::Op_LDA(uint16_t src)\n{\n   uint8_t m = Read(src);\n   SET_NEGATIVE(m & 0x80);\n   SET_ZERO(!m);\n   A = m;\n}\n\nvoid mos6502::Op_LDX(uint16_t src)\n{\n   uint8_t m = Read(src);\n   SET_NEGATIVE(m & 0x80);\n   SET_ZERO(!m);\n   X = m;\n}\n\nvoid mos6502::Op_LDY(uint16_t src)\n{\n   uint8_t m = Read(src);\n   SET_NEGATIVE(m & 0x80);\n   SET_ZERO(!m);\n   Y = m;\n}\n\nvoid mos6502::Op_LSR(uint16_t src)\n{\n   uint8_t m = Read(src);\n   SET_CARRY(m & 0x01);\n   m >>= 1;\n   SET_NEGATIVE(0);\n   SET_ZERO(!m);\n   Write(src, m);\n}\n\nvoid mos6502::Op_LSR_ACC(uint16_t src)\n{\n   uint8_t m = A;\n   SET_CARRY(m & 0x01);\n   m >>= 1;\n   SET_NEGATIVE(0);\n   SET_ZERO(!m);\n   A = m;\n}\n\nvoid mos6502::Op_NOP(uint16_t src)\n{\n   return;\n}\n\nvoid mos6502::Op_ORA(uint16_t src)\n{\n   uint8_t m = Read(src);\n   m = A | m;\n   SET_NEGATIVE(m & 0x80);\n   SET_ZERO(!m);\n   A = m;\n}\n\nvoid mos6502::Op_PHA(uint16_t src)\n{\n   StackPush(A);\n   return;\n}\n\nvoid mos6502::Op_PHP(uint16_t src)\n{\n   StackPush(status | CONSTANT | BREAK);\n   return;\n}\n\nvoid mos6502::Op_PLA(uint16_t src)\n{\n   A = StackPop();\n   SET_NEGATIVE(A & 0x80);\n   SET_ZERO(!A);\n   return;\n}\n\nvoid mos6502::Op_PLP(uint16_t src)\n{\n   status = (status & (CONSTANT | BREAK)) | (StackPop() & ~(CONSTANT | BREAK));\n   return;\n}\n\nvoid mos6502::Op_ROL(uint16_t src)\n{\n   uint16_t m = Read(src);\n   m <<= 1;\n   if (IF_CARRY()) m |= 0x01;\n   SET_CARRY(m > 0xFF);\n   m &= 0xFF;\n   SET_NEGATIVE(m & 0x80);\n   SET_ZERO(!m);\n   Write(src, m);\n   return;\n}\n\nvoid mos6502::Op_ROL_ACC(uint16_t src)\n{\n   uint16_t m = A;\n   m <<= 1;\n   if (IF_CARRY()) m |= 0x01;\n   SET_CARRY(m > 0xFF);\n   m &= 0xFF;\n   SET_NEGATIVE(m & 0x80);\n   SET_ZERO(!m);\n   A = m;\n   return;\n}\n\nvoid mos6502::Op_ROR(uint16_t src)\n{\n   uint16_t m = Read(src);\n   if (IF_CARRY()) m |= 0x100;\n   SET_CARRY(m & 0x01);\n   m >>= 1;\n   m &= 0xFF;\n   SET_NEGATIVE(m & 0x80);\n   SET_ZERO(!m);\n   Write(src, m);\n   return;\n}\n\nvoid mos6502::Op_ROR_ACC(uint16_t src)\n{\n   uint16_t m = A;\n   if (IF_CARRY()) m |= 0x100;\n   SET_CARRY(m & 0x01);\n   m >>= 1;\n   m &= 0xFF;\n   SET_NEGATIVE(m & 0x80);\n   SET_ZERO(!m);\n   A = m;\n   return;\n}\n\nvoid mos6502::Op_RTI(uint16_t src)\n{\n   uint8_t lo, hi;\n\n   status = (status & (CONSTANT | BREAK)) | (StackPop() & ~(CONSTANT | BREAK));\n\n   lo = StackPop();\n   hi = StackPop();\n\n   pc = (hi << 8) | lo;\n\n   nmi_inhibit = false; // always, more efficient than if()\n\n   return;\n}\n\nvoid mos6502::Op_RTS(uint16_t src)\n{\n   uint8_t lo, hi;\n\n   lo = StackPop();\n   hi = StackPop();\n\n   pc = ((hi << 8) | lo) + 1;\n   return;\n}\n\nvoid mos6502::Op_SBC(uint16_t src)\n{\n   uint8_t m   = Read(src);\n   int tmp     = A - m - (IF_CARRY() ? 0 : 1);\n\n   // N V Z computed *BEFORE* adjustment (binary semantics)\n   SET_OVERFLOW(((A ^ m) & (A ^ tmp) & 0x80) != 0);\n   SET_NEGATIVE(tmp & 0x80 );\n   SET_ZERO(!(tmp & 0xFF));\n\n   if (IF_DECIMAL())\n   {\n      // see http://www.6502.org/tutorials/decimal_mode.html\n      int AL = (A & 0x0F) - (m & 0x0F) - (IF_CARRY() ? 0 : 1);\n      if (AL < 0) {\n         AL = ((AL - 6) & 0x0F) - 0x10;\n      }\n      tmp = (A & 0xF0) - (m & 0xF0) + AL;\n\n      // N V recompute\n      SET_OVERFLOW(((A ^ m) & (A ^ tmp) & 0x80) != 0);\n      SET_NEGATIVE(tmp & 0x80 );\n\n      if (tmp < 0) tmp -= 0x60; // ???? huh ????\n   }\n\n   // C computed *AFTER* adjustment\n   SET_CARRY( tmp >= 0 );\n\n   A = tmp & 0xFF;\n   return;\n}\n\nvoid mos6502::Op_SEC(uint16_t src)\n{\n   SET_CARRY(1);\n   return;\n}\n\nvoid mos6502::Op_SED(uint16_t src)\n{\n   SET_DECIMAL(1);\n   return;\n}\n\nvoid mos6502::Op_SEI(uint16_t src)\n{\n   SET_INTERRUPT(1);\n   return;\n}\n\nvoid mos6502::Op_STA(uint16_t src)\n{\n   Write(src, A);\n   return;\n}\n\nvoid mos6502::Op_STX(uint16_t src)\n{\n   Write(src, X);\n   return;\n}\n\nvoid mos6502::Op_STY(uint16_t src)\n{\n   Write(src, Y);\n   return;\n}\n\nvoid mos6502::Op_TAX(uint16_t src)\n{\n   uint8_t m = A;\n   SET_NEGATIVE(m & 0x80);\n   SET_ZERO(!m);\n   X = m;\n   return;\n}\n\nvoid mos6502::Op_TAY(uint16_t src)\n{\n   uint8_t m = A;\n   SET_NEGATIVE(m & 0x80);\n   SET_ZERO(!m);\n   Y = m;\n   return;\n}\n\nvoid mos6502::Op_TSX(uint16_t src)\n{\n   uint8_t m = sp;\n   SET_NEGATIVE(m & 0x80);\n   SET_ZERO(!m);\n   X = m;\n   return;\n}\n\nvoid mos6502::Op_TXA(uint16_t src)\n{\n   uint8_t m = X;\n   SET_NEGATIVE(m & 0x80);\n   SET_ZERO(!m);\n   A = m;\n   return;\n}\n\nvoid mos6502::Op_TXS(uint16_t src)\n{\n   sp = X;\n   return;\n}\n\nvoid mos6502::Op_TYA(uint16_t src)\n{\n   uint8_t m = Y;\n   SET_NEGATIVE(m & 0x80);\n   SET_ZERO(!m);\n   A = m;\n   return;\n}\n\n#ifdef ILLEGAL_OPCODES\n\nvoid mos6502::Op_ALR(uint16_t src)\n{\n   uint8_t m = Read(src);\n   uint8_t res = m & A;\n   SET_CARRY(res & 1);\n   res >>= 1;\n   SET_NEGATIVE(res & 0x80);\n   SET_ZERO(!res);\n   A = res;\n   return;\n}\n\nvoid mos6502::Op_ANC(uint16_t src)\n{\n   uint8_t m = Read(src);\n   uint8_t res = m & A;\n   SET_CARRY(res & 0x80);\n   SET_NEGATIVE(res & 0x80);\n   SET_ZERO(!res);\n   A = res;\n   return;\n}\n\nvoid mos6502::Op_ANE(uint16_t src)\n{\n// A base value in A is determined based on the contets of A and a constant,\n// which may be typically $00, $ff, $ee, etc. The value of this constant\n// depends on temperature, the chip series, and maybe other factors, as well.\n   const uint8_t constant = 0xee;\n\n   uint8_t m = Read(src);\n   uint8_t res = ((A | constant) & X & m);\n   SET_NEGATIVE(res & 0x80);\n   SET_ZERO(!res);\n   A = res;\n   return;\n}\n\nvoid mos6502::Op_ARR(uint16_t src)\n{\n   bool carry = IF_CARRY();\n   uint8_t m = Read(src);\n   uint8_t res = m & A;\n   res >>= 1;\n   if (carry) {\n      res |= 0x80;\n   }\n   SET_CARRY((res >> 6) & 1);\n   SET_OVERFLOW(((res >> 6) ^ (res >> 5)) & 1);\n   SET_NEGATIVE(res & 0x80);\n   SET_ZERO(!res);\n\n   if (IF_DECIMAL())\n   {\n      // ARR in decimal mode routes signals through the ALU’s decimal\n      // adder path, but with no valid carry-in, so the outputs are\n      // garbage. It’s not emulatable in a meaningful way.\n\n      if ((res & 0xF) >= 0xA) {\n         res += 6;\n      }\n      if (res >= 0xA0) {\n         res += 0x60;\n      }\n   }\n\n   A = res;\n   return;\n}\n\nvoid mos6502::Op_DCP(uint16_t src)\n{\n   uint8_t m = Read(src);\n   m = (m - 1) & 0xFF;\n   Write(src, m);\n\n   unsigned int tmp = A - m;\n   SET_CARRY(tmp < 0x100);\n   SET_NEGATIVE(tmp & 0x80);\n   SET_ZERO(!(tmp & 0xFF));\n   return;\n}\n\nvoid mos6502::Op_ISC(uint16_t src)\n{\n   uint8_t m = Read(src);\n\n   // from Op_INC\n   m = (m + 1) & 0xFF;\n   Write(src, m);\n\n   // from here on is Op_SBC\n   int tmp     = A - m - (IF_CARRY() ? 0 : 1);\n\n   // N V Z computed *BEFORE* adjustment (binary semantics)\n   SET_OVERFLOW(((A ^ m) & (A ^ tmp) & 0x80) != 0);\n   SET_NEGATIVE(tmp & 0x80 );\n   SET_ZERO(!(tmp & 0xFF));\n\n   if (IF_DECIMAL())\n   {\n      // see http://www.6502.org/tutorials/decimal_mode.html\n      int AL = (A & 0x0F) - (m & 0x0F) - (IF_CARRY() ? 0 : 1);\n      if (AL < 0) {\n         AL = ((AL - 6) & 0x0F) - 0x10;\n      }\n      tmp = (A & 0xF0) - (m & 0xF0) + AL;\n\n      // N V recompute\n      SET_OVERFLOW(((A ^ m) & (A ^ tmp) & 0x80) != 0);\n      SET_NEGATIVE(tmp & 0x80 );\n\n      if (tmp < 0) tmp -= 0x60; // ???? huh ????\n   }\n\n   // C computed *AFTER* adjustment\n   SET_CARRY( tmp >= 0 );\n\n   A = tmp & 0xFF;\n   return;\n}\n\nvoid mos6502::Op_LAS(uint16_t src)\n{\n   uint8_t tmp = Read(src);\n   tmp &= sp;\n   A = X = sp = tmp;\n   SET_NEGATIVE(tmp & 0x80);\n   SET_ZERO(tmp == 0);\n   return;\n}\n\nvoid mos6502::Op_LAX(uint16_t src)\n{\n   uint8_t m = Read(src);\n   SET_NEGATIVE(m & 0x80);\n   SET_ZERO(!m);\n   A = X = m;\n}\n\nvoid mos6502::Op_LXA(uint16_t src)\n{\n   uint8_t m = Read(src);\n   A = (A | 0xee) & m;  // like ANE, unstable mystery constant\n   X = A;\n   SET_NEGATIVE(A & 0x80);\n   SET_ZERO(!A);\n}\n\nvoid mos6502::Op_RLA(uint16_t src)\n{\n   uint16_t m = Read(src);\n   m <<= 1;\n   if (IF_CARRY()) m |= 0x01;\n   SET_CARRY(m > 0xFF);\n   m &= 0xFF;\n   Write(src, m);\n\n   A &= m;\n\n   SET_NEGATIVE(A & 0x80);\n   SET_ZERO(!A);\n\n   return;\n}\n\nvoid mos6502::Op_RRA(uint16_t src)\n{\n   uint16_t m = Read(src);\n   if (IF_CARRY()) {\n      m |= 0x100;\n   }\n   SET_CARRY(m & 0x01);\n   m >>= 1;\n   m &= 0xFF;\n   Write(src, m);\n\n   // TODO FIX, the rest of this is just Op_ADC\n   // combine common code into a helper function\n   unsigned int tmp = m + A + (IF_CARRY() ? 1 : 0);\n\n   // N V Z computed *BEFORE* adjustment\n   SET_OVERFLOW(!((A ^ m) & 0x80) && ((A ^ tmp) & 0x80));\n   SET_NEGATIVE(tmp & 0x80);\n   SET_ZERO(!(tmp & 0xFF));\n\n   if (IF_DECIMAL())\n   {\n      // see http://www.6502.org/tutorials/decimal_mode.html\n      int AL = ((A & 0xF) + (m & 0xF) + (IF_CARRY() ? 1 : 0));\n      if (AL >= 0xA) {\n         AL = ((AL + 6) & 0xF) + 0x10;\n      }\n      tmp = (m & 0xF0) + (A & 0xF0) + AL;\n\n      // N V recomputed\n      SET_OVERFLOW(!((A ^ m) & 0x80) && ((A ^ tmp) & 0x80));\n      SET_NEGATIVE(tmp & 0x80);\n\n      if (tmp >= 0xA0) tmp += 0x60;\n   }\n\n   // C computed *AFTER* adjustment\n   SET_CARRY(tmp > 0xFF);\n   A = tmp & 0xFF;\n   return;\n}\n\nvoid mos6502::Op_SAX(uint16_t src)\n{\n   Write(src, A & X); // most simple illegal here\n}\n\nvoid mos6502::Op_SBX(uint16_t src)\n{\n   uint16_t m = Read(src);\n   uint8_t tmp = A & X;\n\n   SET_CARRY(tmp >= m);\n   SET_ZERO(tmp == m);\n\n   X = tmp - m;\n\n   SET_NEGATIVE(X & 0x80);\n}\n\nvoid mos6502::Op_SHA(uint16_t src)\n{\n   // unstable, but this is the stable behavior\n   uint8_t tmp = A & X & ((src >> 8) + 1);\n   Write(src, tmp);\n}\n\nvoid mos6502::Op_SHX(uint16_t src)\n{\n   // unstable, but this is the stable behavior\n   uint8_t tmp = X & ((src >> 8) + 1);\n   Write(src, tmp);\n}\n\nvoid mos6502::Op_SHY(uint16_t src)\n{\n   // unstable, but this is the stable behavior\n   uint8_t tmp = Y & ((src >> 8) + 1);\n   Write(src, tmp);\n}\n\nvoid mos6502::Op_SLO(uint16_t src)\n{\n   uint8_t m = Read(src);\n   SET_CARRY(m & 0x80);\n   m <<= 1;\n   m &= 0xFF;\n   Write(src, m);\n\n   A |= m;\n\n   SET_NEGATIVE(A & 0x80);\n   SET_ZERO(!A);\n   return;\n}\n\nvoid mos6502::Op_SRE(uint16_t src)\n{\n   uint8_t m = Read(src);\n   SET_CARRY(m & 0x01);\n   m >>= 1;\n   Write(src, m);\n\n   A ^= m;\n\n   SET_NEGATIVE(A & 0x80);\n   SET_ZERO(!A);\n   return;\n}\n\nvoid mos6502::Op_TAS(uint16_t src)\n{\n   // unstable, but this is the stable behavior\n   sp = A & X;\n\n   uint8_t tmp = A & X & ((src >> 8) + 1);\n   Write(src, tmp);\n}\n\n#endif\n"
  },
  {
    "path": "mos6502.h",
    "content": "//============================================================================\n// Name        : mos6502\n// Author      : Gianluca Ghettini\n// Version     : 1.0\n// Copyright   :\n// Description : A MOS 6502 CPU emulator written in C++\n//============================================================================\n\n#pragma once\n#include <stdint.h>\n#include <stdbool.h>\n\nclass mos6502\n{\n   private:\n      // register reset values\n      uint8_t reset_A;\n      uint8_t reset_X;\n      uint8_t reset_Y;\n      uint8_t reset_sp;\n      uint8_t reset_status;\n\n      // registers\n      uint8_t A; // accumulator\n      uint8_t X; // X-index\n      uint8_t Y; // Y-index\n\n      // stack pointer\n      uint8_t sp;\n\n      // program counter\n      uint16_t pc;\n\n      // status register\n      uint8_t status;\n\n      typedef void (mos6502::*CodeExec)(uint16_t);\n      typedef uint16_t (mos6502::*AddrExec)();\n\n      struct Instr\n      {\n         AddrExec addr;\n         const char * saddr;\n         CodeExec code;\n         const char * scode;\n         uint8_t cycles;\n         bool penalty;\n      };\n\n      static Instr InstrTable[256];\n\n      void Exec(Instr i);\n\n      bool illegalOpcode;\n\n      bool crossed;\n      bool branched;\n\n      bool irq_line;      // current state of the line\n\n      bool nmi_request;   // is there an NMI pending?\n      bool nmi_inhibit;  // are we currently handling an NMI?\n      bool nmi_line;      // current state of the NMI line\n\n      bool CheckInterrupts();\n\n      // addressing modes\n      uint16_t Addr_ACC(); // ACCUMULATOR\n      uint16_t Addr_IMM(); // IMMEDIATE\n      uint16_t Addr_ABS(); // ABSOLUTE\n      uint16_t Addr_ZER(); // ZERO PAGE\n      uint16_t Addr_ZEX(); // INDEXED-X ZERO PAGE\n      uint16_t Addr_ZEY(); // INDEXED-Y ZERO PAGE\n      uint16_t Addr_ABX(); // INDEXED-X ABSOLUTE\n      uint16_t Addr_ABY(); // INDEXED-Y ABSOLUTE\n      uint16_t Addr_IMP(); // IMPLIED\n      uint16_t Addr_REL(); // RELATIVE\n      uint16_t Addr_INX(); // INDEXED-X INDIRECT\n      uint16_t Addr_INY(); // INDEXED-Y INDIRECT\n      uint16_t Addr_ABI(); // ABSOLUTE INDIRECT\n\n      // opcodes (grouped as per datasheet)\n      void Op_ADC(uint16_t src);\n      void Op_AND(uint16_t src);\n      void Op_ASL(uint16_t src);    void Op_ASL_ACC(uint16_t src);\n      void Op_BCC(uint16_t src);\n      void Op_BCS(uint16_t src);\n\n      void Op_BEQ(uint16_t src);\n      void Op_BIT(uint16_t src);\n      void Op_BMI(uint16_t src);\n      void Op_BNE(uint16_t src);\n      void Op_BPL(uint16_t src);\n\n      void Op_BRK(uint16_t src);\n      void Op_BVC(uint16_t src);\n      void Op_BVS(uint16_t src);\n      void Op_CLC(uint16_t src);\n      void Op_CLD(uint16_t src);\n\n      void Op_CLI(uint16_t src);\n      void Op_CLV(uint16_t src);\n      void Op_CMP(uint16_t src);\n      void Op_CPX(uint16_t src);\n      void Op_CPY(uint16_t src);\n\n      void Op_DEC(uint16_t src);\n      void Op_DEX(uint16_t src);\n      void Op_DEY(uint16_t src);\n      void Op_EOR(uint16_t src);\n      void Op_INC(uint16_t src);\n\n      void Op_INX(uint16_t src);\n      void Op_INY(uint16_t src);\n      void Op_JMP(uint16_t src);\n      void Op_JSR(uint16_t src);\n      void Op_LDA(uint16_t src);\n\n      void Op_LDX(uint16_t src);\n      void Op_LDY(uint16_t src);\n      void Op_LSR(uint16_t src);    void Op_LSR_ACC(uint16_t src);\n      void Op_NOP(uint16_t src);\n      void Op_ORA(uint16_t src);\n\n      void Op_PHA(uint16_t src);\n      void Op_PHP(uint16_t src);\n      void Op_PLA(uint16_t src);\n      void Op_PLP(uint16_t src);\n      void Op_ROL(uint16_t src);    void Op_ROL_ACC(uint16_t src);\n\n      void Op_ROR(uint16_t src);    void Op_ROR_ACC(uint16_t src);\n      void Op_RTI(uint16_t src);\n      void Op_RTS(uint16_t src);\n      void Op_SBC(uint16_t src);\n      void Op_SEC(uint16_t src);\n      void Op_SED(uint16_t src);\n\n      void Op_SEI(uint16_t src);\n      void Op_STA(uint16_t src);\n      void Op_STX(uint16_t src);\n      void Op_STY(uint16_t src);\n      void Op_TAX(uint16_t src);\n\n      void Op_TAY(uint16_t src);\n      void Op_TSX(uint16_t src);\n      void Op_TXA(uint16_t src);\n      void Op_TXS(uint16_t src);\n      void Op_TYA(uint16_t src);\n\n#ifdef ILLEGAL_OPCODES\n      void Op_ALR(uint16_t src);\n      void Op_ANC(uint16_t src);\n      void Op_ANE(uint16_t src);\n      void Op_ARR(uint16_t src);\n      void Op_DCP(uint16_t src);\n      void Op_ISC(uint16_t src);\n      void Op_LAS(uint16_t src);\n      void Op_LAX(uint16_t src);\n      void Op_LXA(uint16_t src);\n      void Op_RLA(uint16_t src);\n      void Op_RRA(uint16_t src);\n      void Op_SAX(uint16_t src);\n      void Op_SBX(uint16_t src);\n      void Op_SHA(uint16_t src);\n      void Op_SHX(uint16_t src);\n      void Op_SHY(uint16_t src);\n      void Op_SLO(uint16_t src);\n      void Op_SRE(uint16_t src);\n      void Op_TAS(uint16_t src);\n#endif\n\n      void Op_ILLEGAL(uint16_t src);\n\n      void Svc_NMI();\n      void Svc_IRQ();\n\n      // IRQ, reset, NMI vectors\n      static const uint16_t irqVectorH = 0xFFFF;\n      static const uint16_t irqVectorL = 0xFFFE;\n      static const uint16_t rstVectorH = 0xFFFD;\n      static const uint16_t rstVectorL = 0xFFFC;\n      static const uint16_t nmiVectorH = 0xFFFB;\n      static const uint16_t nmiVectorL = 0xFFFA;\n\n      // read/write/clock-cycle callbacks\n      typedef void (*BusWrite)(uint16_t, uint8_t);\n      typedef uint8_t (*BusRead)(uint16_t);\n      typedef void (*ClockCycle)(mos6502*);\n      BusRead Read;\n      BusWrite Write;\n      ClockCycle Cycle;\n\n      // stack operations\n      inline void StackPush(uint8_t byte);\n      inline uint8_t StackPop();\n\n   public:\n      enum CycleMethod {\n         INST_COUNT,\n         CYCLE_COUNT,\n      };\n      mos6502(BusRead r, BusWrite w, ClockCycle c = nullptr);\n\n      // set or clear the NMI line.  this is an input to the processor.\n      // a high to low edge transition will trigger an interrupt.\n      // line state is NOT cleared by Reset()\n      void NMI(bool line);\n\n      // set or clear the IRQ line.  this is an input to the processor.\n      // a low level will trigger an interrupt.\n      // line state is NOT cleared by Reset()\n      void IRQ(bool line);\n\n      void Reset();\n      void Run(\n            int32_t cycles,\n            uint64_t& cycleCount,\n            CycleMethod cycleMethod = CYCLE_COUNT);\n      void RunEternally(); // until it encounters a illegal opcode\n                           // useful when running e.g. WOZ Monitor\n                           // no need to worry about cycle exhaus-\n                           // tion\n\n      // Various getter/setters\n\n      uint16_t GetPC();\n      uint8_t GetS();\n      uint8_t GetP();\n      uint8_t GetA();\n      uint8_t GetX();\n      uint8_t GetY();\n\n      void SetPC(uint16_t n);\n      void SetS(uint8_t n);\n      void SetP(uint8_t n);\n      void SetA(uint8_t n);\n      void SetX(uint8_t n);\n      void SetY(uint8_t n);\n\n      void SetResetS(uint8_t value);\n      void SetResetP(uint8_t value);\n      void SetResetA(uint8_t value);\n      void SetResetX(uint8_t value);\n      void SetResetY(uint8_t value);\n\n      uint8_t GetResetS();\n      uint8_t GetResetP();\n      uint8_t GetResetA();\n      uint8_t GetResetX();\n      uint8_t GetResetY();\n};\n"
  },
  {
    "path": "tests/Makefile",
    "content": "all:\n\t( cd functional && make )\n\t( cd singlestep && make )\n\t@echo ===============================\n\t@echo === ALL TESTS COMPLETE: success\n\t@echo ===============================\n"
  },
  {
    "path": "tests/functional/.gitignore",
    "content": "*.hex\n*.lst\n*.a65\nmain\n6502_65C02_functional_tests\n"
  },
  {
    "path": "tests/functional/Makefile",
    "content": "# Makefile to run unit tests\n\n# Fail early if required tools are missing\nifeq (, $(shell which git 2>/dev/null))\n$(error \"Error: git not found in PATH\")\nendif\n\nifeq (, $(shell which dosbox 2>/dev/null))\n$(error \"Error: dosbox not found in PATH\")\nendif\n\nSHELL := /bin/bash\n.SHELLFLAGS := -e -o pipefail -c\n\nBASE := 6502_65C02_functional_tests\n\nall: $(BASE) $(BASE)/as65_142 main tests tests\n\t@echo ======================================\n\t@echo === FUNCTIONAL TESTS COMPLETE: success\n\t@echo ======================================\n\nclean:\n\trm -rf $(BASE) ft.* dt.* it.*\n\n$(BASE):\n\t@test ! -e \"$@\" || { echo \"do NOT use 'make -B', use 'make clean ; make' instead\"; exit 1; }\n\t@echo \"Fetching functional tests from GitHub...\"\n\tgit clone https://github.com/Klaus2m5/6502_65C02_functional_tests\n\n$(BASE)/as65_142:\n\techo \"Unpacking assembler...\"\n\tmkdir -p $(BASE)/as65_142\n\t( cd $(BASE)/as65_142 && unzip ../as65_142.zip )\n\nmain: main.cpp ../../mos6502.cpp ../../mos6502.h\n\tg++ -Wall -O3 -o main ../../mos6502.cpp main.cpp\n\ntests: 6502_functional_test 6502_decimal_test 6502_interrupt_test\n\nft.hex:\n\tcp $(BASE)/6502_functional_test.a65 ft.a65\n\t./as65.sh ft.a65\n\n6502_functional_test: main ft.hex\n\t@echo \"================ Running $@\"\n\t./main ft.hex 0x400 0x3469 quiet # magic numbers come from comments and examination of *.lst\n\ndt.hex:\n\tcp $(BASE)/6502_decimal_test.a65 dt.a65\n\tpatch -p0 < decimal.patch\n\t./as65.sh dt.a65\n\n6502_decimal_test: main dt.hex\n\t@echo \"================ Running $@\"\n\t./main dt.hex 0x200 ?0x000b quiet # magic numbers come from comments and examination of *.lst\n\nit.hex:\n\tcp $(BASE)/6502_interrupt_test.a65 it.a65\n\t./as65.sh it.a65\n\n6502_interrupt_test: main it.hex\n\t@echo \"================ Running $@\"\n\t./main it.hex 0x400 0x06f5 quiet # magic numbers come from comments and examination of *.lst\n"
  },
  {
    "path": "tests/functional/as65.sh",
    "content": "#!/bin/bash\nset -euo pipefail\n\nsrc_file=\"$1\"\nsrc_dir=$(dirname \"$(realpath \"$src_file\")\")\nbase_name=$(basename \"${src_file%.*}\")\ndest_dir=\"6502_65C02_functional_tests/as65_142\"\n\n# Sanity checks\n[[ -f \"$src_file\" ]] || { echo \"Error: $src_file not found\"; exit 1; }\n[[ -d \"$dest_dir\" ]] || { echo \"Error: $dest_dir not found\"; exit 1; }\n\n# ---- Collision check (case-insensitive) ----\nshopt -s nullglob\ndeclare -a collisions=()\ncheck_fn_lc=\"$(basename \"$src_file\")\"\ncheck_fn_lc=\"${check_fn_lc,,}\"         # full filename (with extension), lowercased\ncheck_base_lc=\"${base_name,,}\"         # basename (no extension), lowercased\n\nfor f in \"$dest_dir\"/*; do\n    [[ -f \"$f\" ]] || continue\n    bn=\"$(basename \"$f\")\"\n    bn_lc=\"${bn,,}\"\n    name=\"${bn%.*}\"\n    name_lc=\"${name,,}\"\n    if [[ \"$bn_lc\" == \"$check_fn_lc\" || \"$name_lc\" == \"$check_base_lc\" ]]; then\n        collisions+=(\"$bn\")\n    fi\ndone\nshopt -u nullglob\n\nif (( ${#collisions[@]} > 0 )); then\n    echo \"Error: collision(s) in '$dest_dir' (case-insensitive): ${collisions[*]}\" >&2\n    echo \"Refusing to copy to avoid overwriting existing files.\" >&2\n    exit 1\nfi\n\necho \"Copying $src_file to $dest_dir\"\ncp \"$src_file\" \"$dest_dir/\" || { echo \"Copy failed\"; exit 1; }\n\n(\n  cd \"$(dirname \"$dest_dir\")\"\n  echo \"Running assembler under DOSBox...\"\n  dosbox \\\n         -c \"config -set core=dynamic\" \\\n         -c \"config -set cycles=max\" \\\n         -c \"config -set cycleup=100000\" \\\n         -c \"config -set cycledown=100000\" \\\n         -c \"config -set frameskip=5\" \\\n         -c \"config -set output=surface\" \\\n         -c \"mount c $(pwd)\" \\\n         -c \"c:\" \\\n         -c \"cd as65_142\" \\\n         -c \"AS65-DOS.EXE -x1 -o${base_name}.hex -l -m -s2 -w -h0 -c -i -t -u -z $(basename \"$src_file\")\" \\\n         -c \"exit\" # change that \"exit\" to a \"pause\" if you need to debug\n) || { echo \"Assembler failed\"; exit 1; }\n\n# ---- Artifact move block ----\necho \"Moving artifacts back to $src_dir with normalized names...\"\nshopt -s nullglob\nfound=0\nsrc_base_lc=\"${base_name,,}\"\n\nfor f in \"$dest_dir\"/*; do\n    [[ -f \"$f\" ]] || continue\n    fn=\"$(basename \"$f\")\"\n    name=\"${fn%.*}\"           # basename without extension\n    ext=\"${fn##*.}\"           # extension\n    name_lc=\"${name,,}\"       # lowercased basename\n\n    if [[ \"$name_lc\" == \"$src_base_lc\" ]]; then\n        echo \"Moving $f → $src_dir/${base_name}.${ext,,}\"\n        mv \"$f\" \"$src_dir/${base_name}.${ext,,}\" || { echo \"Failed to move $fn\"; exit 1; }\n        ((found++)) || true    # prevent 'set -e' from exiting since found++ returns old value (non-zero on later iterations)\n    fi\ndone\nshopt -u nullglob\n\n(( found > 0 )) || { echo \"No artifacts for '$base_name' found in $dest_dir\" >&2; exit 1; }\n\necho \"Success: moved $found artifact(s) to $src_dir\"\n"
  },
  {
    "path": "tests/functional/decimal.patch",
    "content": "--- dt.a65\t2025-10-12 17:08:27.391206657 -0700\n+++ dt.a65\t2025-10-12 17:10:37.155949816 -0700\n@@ -34,7 +34,7 @@ chk_z   = 0         ; check zero flag\n chk_c   = 1         ; check carry flag\n \n end_of_test macro\n-                db  $db     ;execute 65C02 stop instruction\n+                jmp *       ; 6502 has no stop instruction\n             endm\n \n         bss\n"
  },
  {
    "path": "tests/functional/main.cpp",
    "content": "// compile with \"g++ main.cpp ../../mos6502.cpp -o main\"\n\n#include \"../../mos6502.h\"\n\n#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n#include <stddef.h>\n#include <stdint.h>\n#include <stdbool.h>\n\nbool quiet = false;\n\nuint8_t ram[65536] = {0};\n\nmos6502 *cpu = NULL;\n\nint start = -1;\nint success = -1;\nint retaddr = -1;\n\nvoid writeRam(uint16_t addr, uint8_t val)\n{\n   ram[addr] = val;\n\n   // feedback\n   if (addr == 0xbffc) {\n      // things are inverted here\n      cpu->IRQ((val & 1) ? false : true);\n      cpu->NMI((val & 2) ? false : true);\n   }\n}\n\nuint8_t readRam(uint16_t addr)\n{\n   return ram[addr];\n}\n\nvoid tick(mos6502*)\n{\n   static uint16_t lastpc = 0xFFFF;\n   static int count = 0;\n   uint16_t pc = cpu->GetPC();\n   if (pc != lastpc) {\n      if (!quiet) {\n         printf(\"PC=%04x\\r\", pc);\n      }\n   }\n   if (pc == success) {\n      printf(\"\\nsuccess\\n\");\n      exit(0);\n   }\n   if (pc == lastpc) {\n      count++;\n      if (count > 100) {\n         if (retaddr != -1) {\n            if (ram[retaddr]) {\n               printf(\"\\ncode %02X\\n\", ram[retaddr]);\n               printf(\"Y=%02x\\n\", cpu->GetY());\n               printf(\"N1=%02x N2=%02x\\n\", ram[0], ram[1]);\n               printf(\"HA=%02x HNVZC=%02x\\n\", ram[2], ram[3]);\n               printf(\"DA=%02x DNVZC=%02x\\n\", ram[4], ram[5]);\n               printf(\"AR=%02x NF=%02x VF=%02x ZF=%02x CF=%02x\\n\", ram[6], ram[7], ram[8], ram[9],\n                      ram[10]);\n               printf(\"FAIL\\n\");\n               exit(-1);\n            }\n            else {\n               printf(\"\\nsuccess\\n\");\n               exit(0);\n            }\n         }\n         else {\n            printf(\"\\nFAIL\\n\");\n            exit(-1);\n         }\n      }\n   }\n   else {\n      count = 0;\n   }\n   lastpc = pc;\n}\n\nvoid bail(const char *s)\n{\n   fprintf(stderr, \"%s\\n\", s);\n   exit(-1);\n}\n\nuint32_t fetch(const char *s, uint16_t offset, uint8_t count)\n{\n   uint32_t ret = 0;\n   uint32_t val = 0;\n   for (int i = 0; i < count; i++) {\n      ret <<= 4;\n      if (s[offset + i] <= '9') {\n         val = s[offset + i] - '0';\n      }\n      else if (s[offset + i] <= 'F') {\n         val = s[offset + i] - 'A' + 10;\n      }\n      else if (s[offset + i] <= 'f') {\n         val = s[offset + i] - 'a' + 10;\n      }\n      ret |= val;\n   }\n   return ret;\n}\n\nvoid handle_hex(const char *fname)\n{\n   char buf[1024];\n   FILE *f = fopen(fname, \"r\");\n   if (!f) {\n      bail(\"could not open hex file\");\n   }\n   while (NULL != fgets(buf, sizeof(buf), f)) {\n      if (buf[0] != ':') {\n         bail(\"unexpected start code in hex file\");\n      }\n\n      // TODO FIX verify checksum for line here!\n\n      int length = fetch(buf, 1, 2);\n      int address = fetch(buf, 3, 4);\n      int record = fetch(buf, 7, 2);\n      if (record == 0x00) {\n         for (int i = 0; i < length; i++) {\n            ram[address + i] = fetch(buf, 9 + 2 * i, 2);\n         }\n      }\n      else if (record == 0x01) {\n         // do nothing\n      }\n      else {\n         bail(\"unexpected record type in hex file\");\n      }\n   }\n   fclose(f);\n}\n\nint main(int argc, char **argv) {\n   if (argc != 4 && argc != 5) {\n      fprintf(stderr, \"Usage: %s <file>.hex <start> <success> [quiet]\\n\", argv[0]);\n      return -1;\n   }\n\n   if (argc == 5 && !strcmp(argv[4], \"quiet\")) {\n      quiet = true;\n   }\n\n   handle_hex(argv[1]);\n   start = strtoul(argv[2], NULL, 0);\n\n   if (argv[3][0] == '?') {\n      retaddr = strtoul(argv[3]+1, NULL, 0);\n   }\n   else {\n      success = strtoul(argv[3], NULL, 0);\n   }\n\n   printf(\"start=%04X\\n\", start);\n   ram[0xFFFC] = start & 0xFF;\n   ram[0xFFFD] = start >> 8;\n\n   cpu = new mos6502(readRam, writeRam, tick);\n   cpu->Reset();\n   cpu->RunEternally();\n\n   return 0;\n}\n"
  },
  {
    "path": "tests/functional/notes.txt",
    "content": "assembler originally came from https://www.kingswood-consulting.co.uk/assemblers/\nauthor does NOT distribute source code.\n"
  },
  {
    "path": "tests/functional/readme.md",
    "content": "just type \"make\"\n"
  },
  {
    "path": "tests/singlestep/.gitignore",
    "content": "65x02\nmain\n"
  },
  {
    "path": "tests/singlestep/Makefile",
    "content": "# Makefile to run unit tests\n\n# Fail early if required tools are missing\nifeq (, $(shell which git 2>/dev/null))\n$(error \"Error: git not found in PATH\")\nendif\n\nSHELL := /bin/bash\n.SHELLFLAGS := -e -o pipefail -c\n\nBASE := 65x02\n\nall: $(BASE) main tests\n   @echo TEST COMPLETE: success\n\nclean:\n\trm -rf $(BASE) ft.* dt.* it.*\n\n$(BASE):\n\t@test ! -e \"$@\" || { echo \"do NOT use 'make -B', use 'make clean ; make' instead\"; exit 1; }\n\t@echo \"Fetching functional tests from GitHub...\"\n\tgit clone https://github.com/SingleStepTests/65x02.git\n\nmain: main.cpp ../../mos6502.cpp ../../mos6502.h\n\tg++ -O3 -Wall -o main -DILLEGAL_OPCODES ../../mos6502.cpp main.cpp\n\ntests: main\n\tfor f in 65x02/6502/v1/*.json; do echo == \"$$f\" ; ./main \"$$f\"; done\n\t@echo ======================================\n\t@echo === SINGLESTEP TESTS COMPLETE: success\n\t@echo ======================================\n\n\n"
  },
  {
    "path": "tests/singlestep/main.cpp",
    "content": "// compile with \"g++ main.cpp ../../mos6502.cpp -o main\"\n\n#include \"../../mos6502.h\"\n\n#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n#include <stddef.h>\n#include <stdint.h>\n#include <stdbool.h>\n\n#define NAME \"\\\"name\\\": \\\"\"\n#define CYCLES \"\\\"cycles\\\": [\"\n#define INITIAL \"\\\"initial\\\": {\"\n#define FINAL \"\\\"final\\\": {\"\n\n#define PC \"\\\"pc\\\": \"\n#define S  \"\\\"s\\\": \"\n#define A  \"\\\"a\\\": \"\n#define X  \"\\\"x\\\": \"\n#define Y  \"\\\"y\\\": \"\n#define P  \"\\\"p\\\": \"\n#define RAM  \"\\\"ram\\\": [\"\n\n#define PMASK (~0x00)\n\nstatic const char *unstable = \"\\x6b\\x93\\x9b\\x9c\\x9e\\x9f\";\n\nbool quiet = false;\nuint8_t ram[65536] = {0};\nmos6502 *cpu = NULL;\nint linenum;\nuint64_t cycles;\nuint64_t actual_cycles;\nint failures = 0;\nbool is_unstable = false;\nconst char *name = NULL;\n\nchar linebuf[4096];\nchar hexbuf[4096];\n\nvoid writeRam(uint16_t addr, uint8_t val)\n{\n   ram[addr] = val;\n}\n\nuint8_t readRam(uint16_t addr)\n{\n   return ram[addr];\n}\n\nvoid tick(mos6502*)\n{\n}\n\nvoid translate(void)\n{\n   char *p = linebuf;\n   char *q = hexbuf;\n\n   if (!strchr(p, 'n')) {\n      strcpy(q, p);\n      return;\n   }\n\n   while (*p != ',') {\n      *q++ = *p++;\n   }\n\n   bool valid = false;\n   int n = 0;\n\n   while (*p) {\n      if (*p == ' ' && !strncmp(p+1, INITIAL, strlen(INITIAL))) {\n         *q++ = '\\n';\n         p++;\n      }\n      else if (*p == ' ' && !strncmp(p+1, FINAL, strlen(FINAL))) {\n         *q++ = '\\n';\n         p++;\n      }\n      else if (*p == ' ' && !strncmp(p+1, CYCLES, strlen(CYCLES))) {\n         *q++ = '\\n';\n         p++;\n      }\n      else if (*p == ' ' && !strncmp(p+1, RAM, strlen(RAM))) {\n         *q++ = '\\n';\n         p++;\n      }\n      else if (*p >= '0' && *p <= '9') {\n         n = n * 10 + (*p - '0');\n         valid = true;\n         p++;\n      }\n      else {\n         if (valid) {\n            if (n < 256) {\n               sprintf(q, \"%02x\", n);\n               q += 2;\n            }\n            else {\n               sprintf(q, \"%04x\", n);\n               q += 4;\n            }\n\n            n = 0;\n            valid = false;\n         }\n         *q++ = *p++;\n      }\n   }\n   *q = 0;\n}\n\nbool failed = false;\n\nvoid reset_fail(void) {\n   failed = false;\n}\n\nvoid fail(const char *s)\n{\n   if (!failed) {\n      failed = true;\n      fprintf(stderr, \"NAME: %s\\n\", name);\n      fprintf(stderr, \"%s\\n\", hexbuf);\n   }\n   fprintf(stderr, \"%s\\n\", s);\n   failures++;\n}\n\nvoid bail(const char *s)\n{\n   fprintf(stderr, \"%s\\n\", s);\n   exit(-1);\n}\n\nconst char *locate(const char *haystack, const char *needle, const char *name)\n{\n   const char *ret = strstr(haystack, needle);\n   if (ret) {\n      ret += strlen(needle);\n      return ret;\n   }\n   else {\n      char buf[1024];\n      sprintf(buf, \"cannot find %s at line %d\", name, linenum);\n      bail(buf);\n   }\n   return NULL;\n}\n\nvoid handle_name(char *copy)\n{\n   char *p = (char *) locate(copy, NAME, \"NAME\");\n   char *q = strchr(p, '\\\"');\n   if (q) {\n      *q = 0;\n      //printf(\"NAME: %s\\n\", p);\n      if (name) {\n         free((void *)name);\n      }\n      name = strdup(p);\n   }\n   else {\n      char buf[1024];\n      sprintf(buf, \"cannot parse NAME at line %d\", linenum);\n      bail(buf);\n   }\n   free(copy);\n}\n\nvoid handle_cycles(char *copy)\n{\n   char *p = (char *) locate(copy, CYCLES, \"CYCLES\");\n   cycles = 0;\n\n   while (*p) {\n      if (*p == '[') {\n         cycles++;\n      }\n      p++;\n   }\n\n   if (cycles < 2) {\n      char buf[1024];\n      sprintf(buf, \"cannot parse CYCLES at line %d\", linenum);\n      bail(buf);\n   }\n\n   // printf(\"CYCLES: %d\\n\", (int)cycles);\n\n   free(copy);\n}\n\nvoid handle_initial(char *copy)\n{\n   char *p = (char *) locate(copy, INITIAL, \"INITIAL\");\n\n   cpu->SetPC(atoi(locate(p, PC, \"INITIAL_PC\")));\n   cpu->SetS(atoi(locate(p, S, \"INITIAL_S\")));\n   cpu->SetA(atoi(locate(p, A, \"INITIAL_A\")));\n   cpu->SetX(atoi(locate(p, X, \"INITIAL_X\")));\n   cpu->SetY(atoi(locate(p, Y, \"INITIAL_Y\")));\n   cpu->SetP(atoi(locate(p, P, \"INITIAL_P\")));\n\n   bzero(ram, sizeof(ram));\n   const char *q = locate(p, RAM, \"INITIAL_RAM\");\n\n   while (*q != '}') {\n      int addr, val;\n      if (*q == '[') {\n         sscanf(q+1, \"%d, %d\", &addr, &val);\n         ram[addr] = val;\n      }\n      q++;\n   }\n\n   free(copy);\n}\n\nbool is_jam(uint8_t val) {\n   // Instruction codes: 02, 12, 22, 32, 42, 52, 62, 72, 92, B2, D2, F2\n   if ((val & 0x0F) == 0x02) {\n      if ((val & 0x80) == 0) {\n         return true;\n      }\n      if ((val & 0x10) == 0x10) {\n         return true;\n      }\n   }\n   return false;\n}\n\nvoid handle_final(char *copy, bool jammed)\n{\n   char buf[1024];\n   char *p = (char *) locate(copy, FINAL, \"FINAL\");\n   uint8_t val;\n   uint16_t pcval;\n\n   reset_fail();\n\n   if (!jammed && cpu->GetPC() != (pcval = /* ASSIGN */ atoi(locate(p, PC, \"FINAL_PC\")))) {\n      sprintf(buf, \"FAIL: PC %04x != %04x at line %d\", cpu->GetPC(), pcval, linenum);\n      fail(buf);\n   }\n   if (cpu->GetS() != (val = /* ASSIGN */ atoi(locate(p, S, \"FINAL_S\")))) {\n      sprintf(buf, \"FAIL: S %02x != %02x at line %d\", cpu->GetS(), val, linenum);\n      fail(buf);\n   }\n   if (cpu->GetA() != (val = /* ASSIGN */ atoi(locate(p, A, \"FINAL_A\")))) {\n      sprintf(buf, \"FAIL: A %02x != %02x at line %d\", cpu->GetA(), val, linenum);\n      fail(buf);\n   }\n   if (cpu->GetX() != (val = /* ASSIGN */ atoi(locate(p, X, \"FINAL_X\")))) {\n      sprintf(buf, \"FAIL: X %02x != %02x at line %d\", cpu->GetX(), val, linenum);\n      fail(buf);\n   }\n   if (cpu->GetY() != (val = /* ASSIGN */ atoi(locate(p, Y, \"FINAL_Y\")))) {\n      sprintf(buf, \"FAIL: Y %02x != %02x at line %d\", cpu->GetY(), val, linenum);\n      fail(buf);\n   }\n   if ((cpu->GetP() & PMASK) != (val = /* ASSIGN */ (atoi(locate(p, P, \"FINAL_P\")) & PMASK))) {\n      sprintf(buf, \"FAIL: P %02x != %02x at line %d\", cpu->GetP(), val, linenum);\n      fail(buf);\n   }\n\n   const char *q = locate(p, RAM, \"FINAL_RAM\");\n\n   while (*q != '}') {\n      int addr, val;\n      if (*q == '[') {\n         sscanf(q+1, \"%d, %d\", &addr, &val);\n         if (ram[addr] != val) {\n            sprintf(buf, \"FAIL: RAM[%04x] %02x != %02x at line %d\", addr, ram[addr], val, linenum);\n            fail(buf);\n         }\n      }\n      q++;\n   }\n\n   free(copy);\n\n   //printf(\"pass\\n\");\n}\n\nvoid handle_line(const char *line)\n{\n   handle_name(strdup(line));\n   handle_cycles(strdup(line));\n   handle_initial(strdup(line));\n\n   actual_cycles = 0;\n\n   bool jammed = false;\n\n   if (!is_jam(ram[cpu->GetPC()])) {\n      cpu->Run(1, actual_cycles, mos6502::INST_COUNT);\n\n      if (cycles != actual_cycles) {\n         char buf[1024];\n         sprintf(buf, \"FAIL: actual %d != %d cycles at %d\", (int) actual_cycles, (int) cycles, linenum);\n         fail(buf);\n      }\n   }\n   else {\n      jammed = true;\n   }\n   \n   handle_final(strdup(line), jammed);\n}\n\nvoid handle_json(const char *fname)\n{\n   const char *p = strstr(fname, \".json\");\n   if (p) {\n      p -= 2;\n      unsigned char x = strtoul(p, NULL, 16);\n      if (strchr(unstable, x)) {\n         is_unstable = true;\n      }\n   }\n\n   linenum = 1;\n   FILE *f = fopen(fname, \"r\");\n   if (!f) {\n      bail(\"could not open json file\");\n   }\n   while (NULL != fgets(linebuf, sizeof(linebuf), f)) {\n      translate();\n\n      // we assume a lot here...\n\n      p = linebuf;\n      if (p[0] == '[') {\n         p++;\n      }\n\n      switch(p[0]) {\n         case 0:\n         case 0x0A:\n         case 0x0D:\n         case '[':\n         case ']':\n            break;\n         case '{':\n            handle_line(p);\n            break;\n         default:\n            {\n               char buf[1024];\n               sprintf(buf, \"parse error at line %d, %02x\", linenum, p[0]);\n               bail(buf);\n            }\n            break;\n      }\n      linenum++;\n   }\n   fclose(f);\n}\n\nint main(int argc, char **argv) {\n   if (argc != 2 && argc != 3) {\n      fprintf(stderr, \"Usage: %s <file>.json [quiet]\\n\", argv[0]);\n      return -1;\n   }\n\n   if (argc == 3 && !strcmp(argv[2], \"quiet\")) {\n      quiet = true;\n   }\n\n   cpu = new mos6502(readRam, writeRam, tick);\n   cpu->Reset();\n\n   handle_json(argv[1]);\n\n   if (failures) {\n      printf(\"%d %sfailure%s\\n\", failures, is_unstable ? \"unstable \" : \"\", failures > 1 ? \"s\" : \"\");\n   }\n\n   return (failures && !is_unstable) ? -1 : 0;\n}\n"
  }
]