Full Code of gianlucag/mos6502 for AI

master 5790adce684a cached
16 files
95.4 KB
32.8k tokens
23 symbols
1 requests
Download .txt
Repository: gianlucag/mos6502
Branch: master
Commit: 5790adce684a
Files: 16
Total size: 95.4 KB

Directory structure:
gitextract_gtzs_4yo/

├── .gitignore
├── LICENSE.txt
├── README.md
├── mos6502.cpp
├── mos6502.h
└── tests/
    ├── Makefile
    ├── functional/
    │   ├── .gitignore
    │   ├── Makefile
    │   ├── as65.sh
    │   ├── decimal.patch
    │   ├── main.cpp
    │   ├── notes.txt
    │   └── readme.md
    └── singlestep/
        ├── .gitignore
        ├── Makefile
        └── main.cpp

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
*.o

================================================
FILE: LICENSE.txt
================================================
MIT License

Copyright (c) 2017 Gianluca Ghettini

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

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

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


================================================
FILE: README.md
================================================
## MOS6502 Emulator in C++

This 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.

main features:

- 100% coverage of legal opcodes
- decimal mode implemented
- read/write bus callback
- jump table opcode selection

still to implement

- 100% cycle accuracy
- illegal opcodes
- hardware glitches, the known ones of course :-)

The emulator was extensively tested against this test suite:

https://github.com/Klaus2m5/6502_65C02_functional_tests

and in parallel emulation with Fake6502 http://rubbermallet.org/fake6502.c

so 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.

## Why yet another 6502 emulator?

Just for fun :). This CPU (and its many derivatives) powered machines such as:

- Apple II
- Nintendo Entertainment system (NES)
- Atari 2600
- Commodore PET and VIC-20
- Commodore 64 (6510 chip actually, but fully compatible emulator-wise)
- BBC Micro

and many other embedded devices still used today.
You 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.

## Some things emulators: emulator types

You can group all the CPU emulators out there in 4 main categories:

- switch-case based
- jump-table based
- PLA or microcode emulation based
- graph based

The 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!!!_

for an example check this out: http://visual6502.org/JSSim/index.html

The PLA/microcode based are the best as they offer both speed and limited complexity.
The 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).

## Emulator features

My 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.

All the 13 addressing modes are emulated:

```
// addressing modes
uint16_t Addr_ACC(); // ACCUMULATOR
uint16_t Addr_IMM(); // IMMEDIATE
uint16_t Addr_ABS(); // ABSOLUTE
uint16_t Addr_ZER(); // ZERO PAGE
uint16_t Addr_ZEX(); // INDEXED-X ZERO PAGE
uint16_t Addr_ZEY(); // INDEXED-Y ZERO PAGE
uint16_t Addr_ABX(); // INDEXED-X ABSOLUTE
uint16_t Addr_ABY(); // INDEXED-Y ABSOLUTE
uint16_t Addr_IMP(); // IMPLIED
uint16_t Addr_REL(); // RELATIVE
uint16_t Addr_INX(); // INDEXED-X INDIRECT
uint16_t Addr_INY(); // INDEXED-Y INDIRECT
uint16_t Addr_ABI(); // ABSOLUTE INDIRECT
```

All 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.

The illegals are not supported yet, so instead a simple NOP is executed.

## Inner main loop

It's a classic fetch-decode-execute loop:

```
while(start + n > cycles && !illegalOpcode)
{
	// fetch
	opcode = Read(pc++);

	// decode
	instr = InstrTable[opcode];

	// execute
	Exec(instr);
}
```

The 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.

## Public methods

The emulator comes as a single C++ class with five public methods:

```
mos6502(BusRead r, BusWrite w);
void NMI();
void IRQ();
void Reset();
void Run(uint32_t n);
```

`mos6502(BusRead r, BusWrite w);`

it's the class constructor. It requires you to pass two external functions:

```
uint8_t MemoryRead(uint16_t address);
void MemoryWrite(uint16_t address, uint8_t value);
```

respectively 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.

```
void NMI();
```

triggers a Non-Mascherable Interrupt request, as done by the external pin of the real chip

```
void IRQ();
```

triggers an Interrupt ReQuest, as done by the external pin of the real chip

```
void Reset();
```

performs an hardware reset, as done by the external pin of the real chip

```
void Run(uint32_t n);
```

It runs the CPU for the next 'n' machine instructions.

## Links

Some useful stuff I used...

http://en.wikipedia.org/wiki/MOS_Technology_6502

http://www.6502.org/documents/datasheets/mos/

http://www.mdawson.net/vic20chrome/cpu/mos_6500_mpu_preliminary_may_1976.pdf

http://rubbermallet.org/fake6502.c


================================================
FILE: mos6502.cpp
================================================
#include "mos6502.h"

#define NEGATIVE  0x80
#define OVERFLOW  0x40
#define CONSTANT  0x20
#define BREAK     0x10
#define DECIMAL   0x08
#define INTERRUPT 0x04
#define ZERO      0x02
#define CARRY     0x01

#define SET_NEGATIVE(x)  ((x) ? (status |= NEGATIVE)  : (status &= (~NEGATIVE)) )
#define SET_OVERFLOW(x)  ((x) ? (status |= OVERFLOW)  : (status &= (~OVERFLOW)) )
#define SET_CONSTANT(x)  ((x) ? (status |= CONSTANT)  : (status &= (~CONSTANT)) )
#define SET_BREAK(x)     ((x) ? (status |= BREAK)     : (status &= (~BREAK)) )
#define SET_DECIMAL(x)   ((x) ? (status |= DECIMAL)   : (status &= (~DECIMAL)) )
#define SET_INTERRUPT(x) ((x) ? (status |= INTERRUPT) : (status &= (~INTERRUPT)) )
#define SET_ZERO(x)      ((x) ? (status |= ZERO)      : (status &= (~ZERO)) )
#define SET_CARRY(x)     ((x) ? (status |= CARRY)     : (status &= (~CARRY)) )

#define IF_NEGATIVE()  ((status & NEGATIVE) ? true : false)
#define IF_OVERFLOW()  ((status & OVERFLOW) ? true : false)
#define IF_CONSTANT()  ((status & CONSTANT) ? true : false)
#define IF_BREAK()     ((status & BREAK) ? true : false)
#define IF_DECIMAL()   ((status & DECIMAL) ? true : false)
#define IF_INTERRUPT() ((status & INTERRUPT) ? true : false)
#define IF_ZERO()      ((status & ZERO) ? true : false)
#define IF_CARRY()     ((status & CARRY) ? true : false)

mos6502::Instr mos6502::InstrTable[256];

mos6502::mos6502(BusRead r, BusWrite w, ClockCycle c)
   : reset_A(0x00)
   , reset_X(0x00)
   , reset_Y(0x00)
   , reset_sp(0xFD)
   , reset_status(CONSTANT)
   , irq_line(true)
   , nmi_request(false)
   , nmi_inhibit(false)
   , nmi_line(true)
{
   Write = (BusWrite)w;
   Read = (BusRead)r;
   Cycle = (ClockCycle)c;

   static bool initialized = false;
   if (initialized) return;
   initialized = true;

   Instr instr;
   // fill jump table with ILLEGALs
   instr.addr = &mos6502::Addr_IMP;
   instr.saddr = "(null)";
   instr.code = &mos6502::Op_ILLEGAL;
   instr.scode = "(null)";
   instr.penalty = false;
   instr.cycles = 0;
   for(int i = 0; i < 256; i++)
   {
      InstrTable[i] = instr;
   }

   // insert opcodes
#define MAKE_INSTR(HEX, CODE, MODE, CYCLES, PENALTY) \
   instr.code = &mos6502::Op_ ## CODE; \
   instr.scode = # CODE; \
   instr.addr = &mos6502::Addr_ ## MODE; \
   instr.saddr = # MODE; \
   instr.cycles = CYCLES; \
   instr.penalty = PENALTY; \
   InstrTable[HEX] = instr;

// ADC
// Add Memory to Accumulator with Carry
//
// A + M + C -> A, C
// N    Z       C       I       D       V
// +    +       +       -       -       +
// addressing   assembler       opc     bytes   cycles
// immediate    ADC #oper       69      2       2
// zeropage     ADC oper        65      2       3
// zeropage,X   ADC oper,X      75      2       4
// absolute     ADC oper        6D      3       4
// absolute,X   ADC oper,X      7D      3       4*
// absolute,Y   ADC oper,Y      79      3       4*
// (indirect,X) ADC (oper,X)    61      2       6
// (indirect),Y ADC (oper),Y    71      2       5*

   MAKE_INSTR(0x69, ADC, IMM, 2, false);
   MAKE_INSTR(0x65, ADC, ZER, 3, false);
   MAKE_INSTR(0x75, ADC, ZEX, 4, false);
   MAKE_INSTR(0x6D, ADC, ABS, 4, false);
   MAKE_INSTR(0x7D, ADC, ABX, 4, true);
   MAKE_INSTR(0x79, ADC, ABY, 4, true);
   MAKE_INSTR(0x61, ADC, INX, 6, false);
   MAKE_INSTR(0x71, ADC, INY, 5, true);

// AND
// AND Memory with Accumulator
//
// A AND M -> A
// N    Z       C       I       D       V
// +    +       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// immediate    AND #oper       29      2       2
// zeropage     AND oper        25      2       3
// zeropage,X   AND oper,X      35      2       4
// absolute     AND oper        2D      3       4
// absolute,X   AND oper,X      3D      3       4*
// absolute,Y   AND oper,Y      39      3       4*
// (indirect,X) AND (oper,X)    21      2       6
// (indirect),Y AND (oper),Y    31      2       5*

   MAKE_INSTR(0x29, AND, IMM, 2, false);
   MAKE_INSTR(0x25, AND, ZER, 3, false);
   MAKE_INSTR(0x35, AND, ZEX, 4, false);
   MAKE_INSTR(0x2D, AND, ABS, 4, false);
   MAKE_INSTR(0x3D, AND, ABX, 4, true);
   MAKE_INSTR(0x39, AND, ABY, 4, true);
   MAKE_INSTR(0x21, AND, INX, 6, false);
   MAKE_INSTR(0x31, AND, INY, 5, true);

// ASL
// Shift Left One Bit (Memory or Accumulator)
//
// C <- [76543210] <- 0
// N    Z       C       I       D       V
// +    +       +       -       -       -
// addressing   assembler       opc     bytes   cycles
// accumulator  ASL A           0A      1       2
// zeropage     ASL oper        06      2       5
// zeropage,X   ASL oper,X      16      2       6
// absolute     ASL oper        0E      3       6
// absolute,X   ASL oper,X      1E      3       7

   MAKE_INSTR(0x0A, ASL_ACC, ACC, 2, false);
   MAKE_INSTR(0x06, ASL, ZER, 5, false);
   MAKE_INSTR(0x16, ASL, ZEX, 6, false);
   MAKE_INSTR(0x0E, ASL, ABS, 6, false);
   MAKE_INSTR(0x1E, ASL, ABX, 7, false);

// BCC
// Branch on Carry Clear
//
// branch on C = 0
// N    Z       C       I       D       V
// -    -       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// relative     BCC oper        90      2       2**

   MAKE_INSTR(0x90, BCC, REL, 2, true);

// BCS
// Branch on Carry Set
//
// branch on C = 1
// N    Z       C       I       D       V
// -    -       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// relative     BCS oper        B0      2       2**

   MAKE_INSTR(0xB0, BCS, REL, 2, true);

// BEQ
// Branch on Result Zero
//
// branch on Z = 1
// N    Z       C       I       D       V
// -    -       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// relative     BEQ oper        F0      2       2**

   MAKE_INSTR(0xF0, BEQ, REL, 2, true);

// BIT
// Test Bits in Memory with Accumulator
//
// bits 7 and 6 of operand are transfered to bit 7 and 6 of SR (N,V);
// the zero-flag is set according to the result of the operand AND
// the accumulator (set, if the result is zero, unset otherwise).
// This allows a quick check of a few bits at once without affecting
// any of the registers, other than the status register (SR).
//
// → Further details.
//
// A AND M -> Z, M7 -> N, M6 -> V
// N    Z       C       I       D       V
// M7   +       -       -       -       M6
// addressing   assembler       opc     bytes   cycles
// zeropage     BIT oper        24      2       3
// absolute     BIT oper        2C      3       4

   MAKE_INSTR(0x24, BIT, ZER, 3, false);
   MAKE_INSTR(0x2C, BIT, ABS, 4, false);

// BMI
// Branch on Result Minus
//
// branch on N = 1
// N    Z       C       I       D       V
// -    -       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// relative     BMI oper        30      2       2**

   MAKE_INSTR(0x30, BMI, REL, 2, true);

// BNE
// Branch on Result not Zero
//
// branch on Z = 0
// N    Z       C       I       D       V
// -    -       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// relative     BNE oper        D0      2       2**

   MAKE_INSTR(0xD0, BNE, REL, 2, true);

// BPL
// Branch on Result Plus
//
// branch on N = 0
// N    Z       C       I       D       V
// -    -       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// relative     BPL oper        10      2       2**

   MAKE_INSTR(0x10, BPL, REL, 2, true);

// BRK
// Force Break
//
// BRK initiates a software interrupt similar to a hardware
// interrupt (IRQ). The return address pushed to the stack is
// PC+2, providing an extra byte of spacing for a break mark
// (identifying a reason for the break.)
// The status register will be pushed to the stack with the break
// flag set to 1. However, when retrieved during RTI or by a PLP
// instruction, the break flag will be ignored.
// The interrupt disable flag is not set automatically.
//
// → Further details.
//
// interrupt,
// push PC+2, push SR
// N    Z       C       I       D       V
// -    -       -       1       -       -
// addressing   assembler       opc     bytes   cycles
// implied      BRK     00      1       7

   MAKE_INSTR(0x00, BRK, IMP, 7, false);

// BVC
// Branch on Overflow Clear
//
// branch on V = 0
// N    Z       C       I       D       V
// -    -       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// relative     BVC oper        50      2       2**

   MAKE_INSTR(0x50, BVC, REL, 2, true);

// BVS
// Branch on Overflow Set
//
// branch on V = 1
// N    Z       C       I       D       V
// -    -       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// relative     BVS oper        70      2       2**

   MAKE_INSTR(0x70, BVS, REL, 2, true);

// CLC
// Clear Carry Flag
//
// 0 -> C
// N    Z       C       I       D       V
// -    -       0       -       -       -
// addressing   assembler       opc     bytes   cycles
// implied      CLC     18      1       2

   MAKE_INSTR(0x18, CLC, IMP, 2, false);

// CLD
// Clear Decimal Mode
//
// 0 -> D
// N    Z       C       I       D       V
// -    -       -       -       0       -
// addressing   assembler       opc     bytes   cycles
// implied      CLD     D8      1       2

   MAKE_INSTR(0xD8, CLD, IMP, 2, false);

// CLI
// Clear Interrupt Disable Bit
//
// 0 -> I
// N    Z       C       I       D       V
// -    -       -       0       -       -
// addressing   assembler       opc     bytes   cycles
// implied      CLI     58      1       2

   MAKE_INSTR(0x58, CLI, IMP, 2, false);

// CLV
// Clear Overflow Flag
//
// 0 -> V
// N    Z       C       I       D       V
// -    -       -       -       -       0
// addressing   assembler       opc     bytes   cycles
// implied      CLV     B8      1       2

   MAKE_INSTR(0xB8, CLV, IMP, 2, false);

// CMP
// Compare Memory with Accumulator
//
// A - M
// N    Z       C       I       D       V
// +    +       +       -       -       -
// addressing   assembler       opc     bytes   cycles
// immediate    CMP #oper       C9      2       2
// zeropage     CMP oper        C5      2       3
// zeropage,X   CMP oper,X      D5      2       4
// absolute     CMP oper        CD      3       4
// absolute,X   CMP oper,X      DD      3       4*
// absolute,Y   CMP oper,Y      D9      3       4*
// (indirect,X) CMP (oper,X)    C1      2       6
// (indirect),Y CMP (oper),Y    D1      2       5*

   MAKE_INSTR(0xC9, CMP, IMM, 2, false);
   MAKE_INSTR(0xC5, CMP, ZER, 3, false);
   MAKE_INSTR(0xD5, CMP, ZEX, 4, false);
   MAKE_INSTR(0xCD, CMP, ABS, 4, false);
   MAKE_INSTR(0xDD, CMP, ABX, 4, true);
   MAKE_INSTR(0xD9, CMP, ABY, 4, true);
   MAKE_INSTR(0xC1, CMP, INX, 6, false);
   MAKE_INSTR(0xD1, CMP, INY, 5, true);

// CPX
// Compare Memory and Index X
//
// X - M
// N    Z       C       I       D       V
// +    +       +       -       -       -
// addressing   assembler       opc     bytes   cycles
// immediate    CPX #oper       E0      2       2
// zeropage     CPX oper        E4      2       3
// absolute     CPX oper        EC      3       4

   MAKE_INSTR(0xE0, CPX, IMM, 2, false);
   MAKE_INSTR(0xE4, CPX, ZER, 3, false);
   MAKE_INSTR(0xEC, CPX, ABS, 4, false);

// CPY
// Compare Memory and Index Y
//
// Y - M
// N    Z       C       I       D       V
// +    +       +       -       -       -
// addressing   assembler       opc     bytes   cycles
// immediate    CPY #oper       C0      2       2
// zeropage     CPY oper        C4      2       3
// absolute     CPY oper        CC      3       4

   MAKE_INSTR(0xC0, CPY, IMM, 2, false);
   MAKE_INSTR(0xC4, CPY, ZER, 3, false);
   MAKE_INSTR(0xCC, CPY, ABS, 4, false);

// DEC
// Decrement Memory by One
//
// M - 1 -> M
// N    Z       C       I       D       V
// +    +       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// zeropage     DEC oper        C6      2       5
// zeropage,X   DEC oper,X      D6      2       6
// absolute     DEC oper        CE      3       6
// absolute,X   DEC oper,X      DE      3       7

   MAKE_INSTR(0xC6, DEC, ZER, 5, false);
   MAKE_INSTR(0xD6, DEC, ZEX, 6, false);
   MAKE_INSTR(0xCE, DEC, ABS, 6, false);
   MAKE_INSTR(0xDE, DEC, ABX, 7, false);

// DEX
// Decrement Index X by One
//
// X - 1 -> X
// N    Z       C       I       D       V
// +    +       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// implied      DEX     CA      1       2

   MAKE_INSTR(0xCA, DEX, IMP, 2, false);

// DEY
// Decrement Index Y by One
//
// Y - 1 -> Y
// N    Z       C       I       D       V
// +    +       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// implied      DEY     88      1       2

   MAKE_INSTR(0x88, DEY, IMP, 2, false);

// EOR
// Exclusive-OR Memory with Accumulator
//
// A EOR M -> A
// N    Z       C       I       D       V
// +    +       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// immediate    EOR #oper       49      2       2
// zeropage     EOR oper        45      2       3
// zeropage,X   EOR oper,X      55      2       4
// absolute     EOR oper        4D      3       4
// absolute,X   EOR oper,X      5D      3       4*
// absolute,Y   EOR oper,Y      59      3       4*
// (indirect,X) EOR (oper,X)    41      2       6
// (indirect),Y EOR (oper),Y    51      2       5*

   MAKE_INSTR(0x49, EOR, IMM, 2, false);
   MAKE_INSTR(0x45, EOR, ZER, 3, false);
   MAKE_INSTR(0x55, EOR, ZEX, 4, false);
   MAKE_INSTR(0x4D, EOR, ABS, 4, false);
   MAKE_INSTR(0x5D, EOR, ABX, 4, true);
   MAKE_INSTR(0x59, EOR, ABY, 4, true);
   MAKE_INSTR(0x41, EOR, INX, 6, false);
   MAKE_INSTR(0x51, EOR, INY, 5, true);

// INC
// Increment Memory by One
//
// M + 1 -> M
// N    Z       C       I       D       V
// +    +       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// zeropage     INC oper        E6      2       5
// zeropage,X   INC oper,X      F6      2       6
// absolute     INC oper        EE      3       6
// absolute,X   INC oper,X      FE      3       7

   MAKE_INSTR(0xE6, INC, ZER, 5, false);
   MAKE_INSTR(0xF6, INC, ZEX, 6, false);
   MAKE_INSTR(0xEE, INC, ABS, 6, false);
   MAKE_INSTR(0xFE, INC, ABX, 7, false);

// INX
// Increment Index X by One
//
// X + 1 -> X
// N    Z       C       I       D       V
// +    +       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// implied      INX     E8      1       2

   MAKE_INSTR(0xE8, INX, IMP, 2, false);

// INY
// Increment Index Y by One
//
// Y + 1 -> Y
// N    Z       C       I       D       V
// +    +       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// implied      INY     C8      1       2

   MAKE_INSTR(0xC8, INY, IMP, 2, false);

// JMP
// Jump to New Location
//
// operand 1st byte -> PCL
// operand 2nd byte -> PCH
// N    Z       C       I       D       V
// -    -       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// absolute     JMP oper        4C      3       3
// indirect     JMP (oper)      6C      3       5

   MAKE_INSTR(0x4C, JMP, ABS, 3, false);
   MAKE_INSTR(0x6C, JMP, ABI, 5, false);

// JSR
// Jump to New Location Saving Return Address
//
// push (PC+2),
// operand 1st byte -> PCL
// operand 2nd byte -> PCH
// N    Z       C       I       D       V
// -    -       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// absolute     JSR oper        20      3       6

   MAKE_INSTR(0x20, JSR, ABS, 6, false);

// LDA
// Load Accumulator with Memory
//
// M -> A
// N    Z       C       I       D       V
// +    +       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// immediate    LDA #oper       A9      2       2
// zeropage     LDA oper        A5      2       3
// zeropage,X   LDA oper,X      B5      2       4
// absolute     LDA oper        AD      3       4
// absolute,X   LDA oper,X      BD      3       4*
// absolute,Y   LDA oper,Y      B9      3       4*
// (indirect,X) LDA (oper,X)    A1      2       6
// (indirect),Y LDA (oper),Y    B1      2       5*

   MAKE_INSTR(0xA9, LDA, IMM, 2, false);
   MAKE_INSTR(0xA5, LDA, ZER, 3, false);
   MAKE_INSTR(0xB5, LDA, ZEX, 4, false);
   MAKE_INSTR(0xAD, LDA, ABS, 4, false);
   MAKE_INSTR(0xBD, LDA, ABX, 4, true);
   MAKE_INSTR(0xB9, LDA, ABY, 4, true);
   MAKE_INSTR(0xA1, LDA, INX, 6, false);
   MAKE_INSTR(0xB1, LDA, INY, 5, true);

// LDX
// Load Index X with Memory
//
// M -> X
// N    Z       C       I       D       V
// +    +       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// immediate    LDX #oper       A2      2       2
// zeropage     LDX oper        A6      2       3
// zeropage,Y   LDX oper,Y      B6      2       4
// absolute     LDX oper        AE      3       4
// absolute,Y   LDX oper,Y      BE      3       4*

   MAKE_INSTR(0xA2, LDX, IMM, 2, false);
   MAKE_INSTR(0xA6, LDX, ZER, 3, false);
   MAKE_INSTR(0xB6, LDX, ZEY, 4, false);
   MAKE_INSTR(0xAE, LDX, ABS, 4, false);
   MAKE_INSTR(0xBE, LDX, ABY, 4, true);

// LDY
// Load Index Y with Memory
//
// M -> Y
// N    Z       C       I       D       V
// +    +       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// immediate    LDY #oper       A0      2       2
// zeropage     LDY oper        A4      2       3
// zeropage,X   LDY oper,X      B4      2       4
// absolute     LDY oper        AC      3       4
// absolute,X   LDY oper,X      BC      3       4*

   MAKE_INSTR(0xA0, LDY, IMM, 2, false);
   MAKE_INSTR(0xA4, LDY, ZER, 3, false);
   MAKE_INSTR(0xB4, LDY, ZEX, 4, false);
   MAKE_INSTR(0xAC, LDY, ABS, 4, false);
   MAKE_INSTR(0xBC, LDY, ABX, 4, true);

// LSR
// Shift One Bit Right (Memory or Accumulator)
//
// 0 -> [76543210] -> C
// N    Z       C       I       D       V
// 0    +       +       -       -       -
// addressing   assembler       opc     bytes   cycles
// accumulator  LSR A           4A      1       2
// zeropage     LSR oper        46      2       5
// zeropage,X   LSR oper,X      56      2       6
// absolute     LSR oper        4E      3       6
// absolute,X   LSR oper,X      5E      3       7

   MAKE_INSTR(0x4A, LSR_ACC, ACC, 2, false);
   MAKE_INSTR(0x46, LSR, ZER, 5, false);
   MAKE_INSTR(0x56, LSR, ZEX, 6, false);
   MAKE_INSTR(0x4E, LSR, ABS, 6, false);
   MAKE_INSTR(0x5E, LSR, ABX, 7, false);

// NOP
// No Operation
//
// ---
// N    Z       C       I       D       V
// -    -       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// implied      NOP     EA      1       2

   MAKE_INSTR(0xEA, NOP, IMP, 2, false);

// ORA
// OR Memory with Accumulator
//
// A OR M -> A
// N    Z       C       I       D       V
// +    +       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// immediate    ORA #oper       09      2       2
// zeropage     ORA oper        05      2       3
// zeropage,X   ORA oper,X      15      2       4
// absolute     ORA oper        0D      3       4
// absolute,X   ORA oper,X      1D      3       4*
// absolute,Y   ORA oper,Y      19      3       4*
// (indirect,X) ORA (oper,X)    01      2       6
// (indirect),Y ORA (oper),Y    11      2       5*

   MAKE_INSTR(0x09, ORA, IMM, 2, false);
   MAKE_INSTR(0x05, ORA, ZER, 3, false);
   MAKE_INSTR(0x15, ORA, ZEX, 4, false);
   MAKE_INSTR(0x0D, ORA, ABS, 4, false);
   MAKE_INSTR(0x1D, ORA, ABX, 4, true);
   MAKE_INSTR(0x19, ORA, ABY, 4, true);
   MAKE_INSTR(0x01, ORA, INX, 6, false);
   MAKE_INSTR(0x11, ORA, INY, 5, true);

// PHA
// Push Accumulator on Stack
//
// push A
// N    Z       C       I       D       V
// -    -       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// implied      PHA     48      1       3

   MAKE_INSTR(0x48, PHA, IMP, 3, false);

// PHP
// Push Processor Status on Stack
//
// The status register will be pushed with the break
// flag and bit 5 set to 1.
//
// push SR
// N    Z       C       I       D       V
// -    -       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// implied      PHP     08      1       3

   MAKE_INSTR(0x08, PHP, IMP, 3, false);

// PLA
// Pull Accumulator from Stack
//
// pull A
// N    Z       C       I       D       V
// +    +       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// implied      PLA     68      1       4

   MAKE_INSTR(0x68, PLA, IMP, 4, false);

// PLP
// Pull Processor Status from Stack
//
// The status register will be pulled with the break
// flag and bit 5 ignored.
//
// pull SR
// N    Z       C       I       D       V
// from stack
// addressing   assembler       opc     bytes   cycles
// implied      PLP     28      1       4

   MAKE_INSTR(0x28, PLP, IMP, 4, false);

// ROL
// Rotate One Bit Left (Memory or Accumulator)
//
// C <- [76543210] <- C
// N    Z       C       I       D       V
// +    +       +       -       -       -
// addressing   assembler       opc     bytes   cycles
// accumulator  ROL A           2A      1       2
// zeropage     ROL oper        26      2       5
// zeropage,X   ROL oper,X      36      2       6
// absolute     ROL oper        2E      3       6
// absolute,X   ROL oper,X      3E      3       7

   MAKE_INSTR(0x2A, ROL_ACC, ACC, 2, false);
   MAKE_INSTR(0x26, ROL, ZER, 5, false);
   MAKE_INSTR(0x36, ROL, ZEX, 6, false);
   MAKE_INSTR(0x2E, ROL, ABS, 6, false);
   MAKE_INSTR(0x3E, ROL, ABX, 7, false);

// ROR
// Rotate One Bit Right (Memory or Accumulator)
//
// C -> [76543210] -> C
// N    Z       C       I       D       V
// +    +       +       -       -       -
// addressing   assembler       opc     bytes   cycles
// accumulator  ROR A           6A      1       2
// zeropage     ROR oper        66      2       5
// zeropage,X   ROR oper,X      76      2       6
// absolute     ROR oper        6E      3       6
// absolute,X   ROR oper,X      7E      3       7

   MAKE_INSTR(0x6A, ROR_ACC, ACC, 2, false);
   MAKE_INSTR(0x66, ROR, ZER, 5, false);
   MAKE_INSTR(0x76, ROR, ZEX, 6, false);
   MAKE_INSTR(0x6E, ROR, ABS, 6, false);
   MAKE_INSTR(0x7E, ROR, ABX, 7, false);

// RTI
// Return from Interrupt
//
// The status register is pulled with the break flag
// and bit 5 ignored. Then PC is pulled from the stack.
//
// pull SR, pull PC
// N    Z       C       I       D       V
// from stack
// addressing   assembler       opc     bytes   cycles
// implied      RTI     40      1       6

   MAKE_INSTR(0x40, RTI, IMP, 6, false);

// RTS
// Return from Subroutine
//
// pull PC, PC+1 -> PC
// N    Z       C       I       D       V
// -    -       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// implied      RTS     60      1       6

   MAKE_INSTR(0x60, RTS, IMP, 6, false);

// SBC
// Subtract Memory from Accumulator with Borrow
//
// A - M - C̅ -> A
// N    Z       C       I       D       V
// +    +       +       -       -       +
// addressing   assembler       opc     bytes   cycles
// immediate    SBC #oper       E9      2       2
// zeropage     SBC oper        E5      2       3
// zeropage,X   SBC oper,X      F5      2       4
// absolute     SBC oper        ED      3       4
// absolute,X   SBC oper,X      FD      3       4*
// absolute,Y   SBC oper,Y      F9      3       4*
// (indirect,X) SBC (oper,X)    E1      2       6
// (indirect),Y SBC (oper),Y    F1      2       5*

   MAKE_INSTR(0xE9, SBC, IMM, 2, false);
   MAKE_INSTR(0xE5, SBC, ZER, 3, false);
   MAKE_INSTR(0xF5, SBC, ZEX, 4, false);
   MAKE_INSTR(0xED, SBC, ABS, 4, false);
   MAKE_INSTR(0xFD, SBC, ABX, 4, true);
   MAKE_INSTR(0xF9, SBC, ABY, 4, true);
   MAKE_INSTR(0xE1, SBC, INX, 6, false);
   MAKE_INSTR(0xF1, SBC, INY, 5, true);

// SEC
// Set Carry Flag
//
// 1 -> C
// N    Z       C       I       D       V
// -    -       1       -       -       -
// addressing   assembler       opc     bytes   cycles
// implied      SEC     38      1       2

   MAKE_INSTR(0x38, SEC, IMP, 2, false);

// SED
// Set Decimal Flag
//
// 1 -> D
// N    Z       C       I       D       V
// -    -       -       -       1       -
// addressing   assembler       opc     bytes   cycles
// implied      SED     F8      1       2

   MAKE_INSTR(0xF8, SED, IMP, 2, false);

// SEI
// Set Interrupt Disable Status
//
// 1 -> I
// N    Z       C       I       D       V
// -    -       -       1       -       -
// addressing   assembler       opc     bytes   cycles
// implied      SEI     78      1       2

   MAKE_INSTR(0x78, SEI, IMP, 2, false);

// STA
// Store Accumulator in Memory
//
// A -> M
// N    Z       C       I       D       V
// -    -       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// zeropage     STA oper        85      2       3
// zeropage,X   STA oper,X      95      2       4
// absolute     STA oper        8D      3       4
// absolute,X   STA oper,X      9D      3       5
// absolute,Y   STA oper,Y      99      3       5
// (indirect,X) STA (oper,X)    81      2       6
// (indirect),Y STA (oper),Y    91      2       6

   MAKE_INSTR(0x85, STA, ZER, 3, false);
   MAKE_INSTR(0x95, STA, ZEX, 4, false);
   MAKE_INSTR(0x8D, STA, ABS, 4, false);
   MAKE_INSTR(0x9D, STA, ABX, 5, false);
   MAKE_INSTR(0x99, STA, ABY, 5, false);
   MAKE_INSTR(0x81, STA, INX, 6, false);
   MAKE_INSTR(0x91, STA, INY, 6, false);

// STX
// Store Index X in Memory
//
// X -> M
// N    Z       C       I       D       V
// -    -       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// zeropage     STX oper        86      2       3
// zeropage,Y   STX oper,Y      96      2       4
// absolute     STX oper        8E      3       4

   MAKE_INSTR(0x86, STX, ZER, 3, false);
   MAKE_INSTR(0x96, STX, ZEY, 4, false);
   MAKE_INSTR(0x8E, STX, ABS, 4, false);

// STY
// Sore Index Y in Memory
//
// Y -> M
// N    Z       C       I       D       V
// -    -       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// zeropage     STY oper        84      2       3
// zeropage,X   STY oper,X      94      2       4
// absolute     STY oper        8C      3       4

   MAKE_INSTR(0x84, STY, ZER, 3, false);
   MAKE_INSTR(0x94, STY, ZEX, 4, false);
   MAKE_INSTR(0x8C, STY, ABS, 4, false);

// TAX
// Transfer Accumulator to Index X
//
// A -> X
// N    Z       C       I       D       V
// +    +       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// implied      TAX     AA      1       2

   MAKE_INSTR(0xAA, TAX, IMP, 2, false);

// TAY
// Transfer Accumulator to Index Y
//
// A -> Y
// N    Z       C       I       D       V
// +    +       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// implied      TAY     A8      1       2

   MAKE_INSTR(0xA8, TAY, IMP, 2, false);

// TSX
// Transfer Stack Pointer to Index X
//
// SP -> X
// N    Z       C       I       D       V
// +    +       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// implied      TSX     BA      1       2

   MAKE_INSTR(0xBA, TSX, IMP, 2, false);

// TXA
// Transfer Index X to Accumulator
//
// X -> A
// N    Z       C       I       D       V
// +    +       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// implied      TXA     8A      1       2

   MAKE_INSTR(0x8A, TXA, IMP, 2, false);

// TXS
// Transfer Index X to Stack Register
//
// X -> SP
// N    Z       C       I       D       V
// -    -       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// implied      TXS     9A      1       2

   MAKE_INSTR(0x9A, TXS, IMP, 2, false);

// TYA
// Transfer Index Y to Accumulator
//
// Y -> A
// N    Z       C       I       D       V
// +    +       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// implied      TYA     98      1       2

   MAKE_INSTR(0x98, TYA, IMP, 2, false);

#ifdef ILLEGAL_OPCODES

// ALR (ASR)
// AND oper + LSR
//
// A AND oper, 0 -> [76543210] -> C
//
// N    Z       C       I       D       V
// +    +       +       -       -       -
// addressing   assembler       opc     bytes   cycles
// immediate    ALR #oper       4B      2       2

   MAKE_INSTR(0x4B, ALR, IMM, 2, false);

// ANC
// AND oper + set C as ASL
//
// A AND oper, bit(7) -> C
//
// N    Z       C       I       D       V
// +    +       +       -       -       -
// addressing   assembler       opc     bytes   cycles
// immediate    ANC #oper       0B      2       2

   MAKE_INSTR(0x0B, ANC, IMM, 2, false);

// ANC (ANC2)
// AND oper + set C as ROL
//
// effectively the same as instr. 0B
//
// A AND oper, bit(7) -> C
//
// N    Z       C       I       D       V
// +    +       +       -       -       -
// addressing   assembler       opc     bytes   cycles
// immediate    ANC #oper       2B      2       2

   MAKE_INSTR(0x2B, ANC, IMM, 2, false);

// ANE (XAA)
// * OR X + AND oper
//
// Highly unstable, do not use.
//
// A base value in A is determined based on the contets of A and a constant,
// which may be typically $00, $ff, $ee, etc. The value of this constant
// depends on temperature, the chip series, and maybe other factors, as well.
// In order to eliminate these uncertaincies from the equation, use either 0
// as the operand or a value of $FF in the accumulator.
//
// (A OR CONST) AND X AND oper -> A
//
// N    Z       C       I       D       V
// +    +       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// immediate    ANE #oper       8B      2       2       ††

   MAKE_INSTR(0x8B, ANE, IMM, 2, false);

// ARR
// AND oper + ROR
//
// This operation involves the adder:
// V-flag is set according to (A AND oper) + oper
// The carry is not set, but bit 7 (sign) is exchanged with the carry
//
// A AND oper, C -> [76543210] -> C
//
// N    Z       C       I       D       V
// +    +       +       -       -       +
// addressing   assembler       opc     bytes   cycles
// immediate    ARR #oper       6B      2       2

   MAKE_INSTR(0x6B, ARR, IMM, 2, false);

// DCP (DCM)
// DEC oper + CMP oper
//
// M - 1 -> M, A - M
//
// Decrements the operand and then compares the result to the accumulator.
//
// N    Z       C       I       D       V
// +    +       +       -       -       -
// addressing   assembler       opc     bytes   cycles
// zeropage     DCP oper        C7      2       5
// zeropage,X   DCP oper,X      D7      2       6
// absolute     DCP oper        CF      3       6
// absolute,X   DCP oper,X      DF      3       7
// absolute,Y   DCP oper,Y      DB      3       7
// (indirect,X) DCP (oper,X)    C3      2       8
// (indirect),Y DCP (oper),Y    D3      2       8

   MAKE_INSTR(0xC7, DCP, ZER, 5, false);
   MAKE_INSTR(0xD7, DCP, ZEX, 6, false);
   MAKE_INSTR(0xCF, DCP, ABS, 6, false);
   MAKE_INSTR(0xDF, DCP, ABX, 7, false);
   MAKE_INSTR(0xDB, DCP, ABY, 7, false);
   MAKE_INSTR(0xC3, DCP, INX, 8, false);
   MAKE_INSTR(0xD3, DCP, INY, 8, false);

// ISC (ISB, INS)
// INC oper + SBC oper
//
// M + 1 -> M, A - M - C̅ -> A
//
// N    Z       C       I       D       V
// +    +       +       -       -       +
// addressing   assembler       opc     bytes   cycles
// zeropage     ISC oper        E7      2       5
// zeropage,X   ISC oper,X      F7      2       6
// absolute     ISC oper        EF      3       6
// absolute,X   ISC oper,X      FF      3       7
// absolute,Y   ISC oper,Y      FB      3       7
// (indirect,X) ISC (oper,X)    E3      2       8
// (indirect),Y ISC (oper),Y    F3      2       8

   MAKE_INSTR(0xE7, ISC, ZER, 5, false);
   MAKE_INSTR(0xF7, ISC, ZEX, 6, false);
   MAKE_INSTR(0xEF, ISC, ABS, 6, false);
   MAKE_INSTR(0xFF, ISC, ABX, 7, false);
   MAKE_INSTR(0xFB, ISC, ABY, 7, false);
   MAKE_INSTR(0xE3, ISC, INX, 8, false);
   MAKE_INSTR(0xF3, ISC, INY, 8, false);

// LAS (LAR)
// LDA/TSX oper
//
// M AND SP -> A, X, SP
//
// N    Z       C       I       D       V
// +    +       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// absolute,Y   LAS oper,Y      BB      3       4*

   MAKE_INSTR(0xBB, LAS, ABY, 4, true);

// LAX
// LDA oper + LDX oper
//
// M -> A -> X
//
// N    Z       C       I       D       V
// +    +       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// zeropage     LAX oper        A7      2       3
// zeropage,Y   LAX oper,Y      B7      2       4
// absolute     LAX oper        AF      3       4
// absolute,Y   LAX oper,Y      BF      3       4*
// (indirect,X) LAX (oper,X)    A3      2       6
// (indirect),Y LAX (oper),Y    B3      2       5*

   MAKE_INSTR(0xA7, LAX, ZER, 3, false);
   MAKE_INSTR(0xB7, LAX, ZEY, 4, false);
   MAKE_INSTR(0xAF, LAX, ABS, 4, false);
   MAKE_INSTR(0xBF, LAX, ABY, 4, true);
   MAKE_INSTR(0xA3, LAX, INX, 6, false);
   MAKE_INSTR(0xB3, LAX, INY, 5, true);

// LXA (LAX immediate)
// Store * AND oper in A and X
//
// Highly unstable, involves a 'magic' constant, see ANE
//
// (A OR CONST) AND oper -> A -> X
//
// N    Z       C       I       D       V
// +    +       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// immediate    LXA #oper       AB      2       2       ††

   MAKE_INSTR(0xAB, LXA, IMM, 2, false);

// RLA
// ROL oper + AND oper
//
// M = C <- [76543210] <- C, A AND M -> A
//
// N    Z       C       I       D       V
// +    +       +       -       -       -
// addressing   assembler       opc     bytes   cycles
// zeropage     RLA oper        27      2       5
// zeropage,X   RLA oper,X      37      2       6
// absolute     RLA oper        2F      3       6
// absolute,X   RLA oper,X      3F      3       7
// absolute,Y   RLA oper,Y      3B      3       7
// (indirect,X) RLA (oper,X)    23      2       8
// (indirect),Y RLA (oper),Y    33      2       8

   MAKE_INSTR(0x27, RLA, ZER, 5, false);
   MAKE_INSTR(0x37, RLA, ZEX, 6, false);
   MAKE_INSTR(0x2F, RLA, ABS, 6, false);
   MAKE_INSTR(0x3F, RLA, ABX, 7, false);
   MAKE_INSTR(0x3B, RLA, ABY, 7, false);
   MAKE_INSTR(0x23, RLA, INX, 8, false);
   MAKE_INSTR(0x33, RLA, INY, 8, false);

// RRA
// ROR oper + ADC oper
//
// M = C -> [76543210] -> C, A + M + C -> A, C
//
// N    Z       C       I       D       V
// +    +       +       -       -       +
// addressing   assembler       opc     bytes   cycles
// zeropage     RRA oper        67      2       5
// zeropage,X   RRA oper,X      77      2       6
// absolute     RRA oper        6F      3       6
// absolute,X   RRA oper,X      7F      3       7
// absolute,Y   RRA oper,Y      7B      3       7
// (indirect,X) RRA (oper,X)    63      2       8
// (indirect),Y RRA (oper),Y    73      2       8

   MAKE_INSTR(0x67, RRA, ZER, 5, false);
   MAKE_INSTR(0x77, RRA, ZEX, 6, false);
   MAKE_INSTR(0x6F, RRA, ABS, 6, false);
   MAKE_INSTR(0x7F, RRA, ABX, 7, false);
   MAKE_INSTR(0x7B, RRA, ABY, 7, false);
   MAKE_INSTR(0x63, RRA, INX, 8, false);
   MAKE_INSTR(0x73, RRA, INY, 8, false);

// SAX (AXS, AAX)
// A and X are put on the bus at the same time (resulting effectively in an AND operation) and stored in M
//
// A AND X -> M
//
// N    Z       C       I       D       V
// -    -       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// zeropage     SAX oper        87      2       3
// zeropage,Y   SAX oper,Y      97      2       4
// absolute     SAX oper        8F      3       4
// (indirect,X) SAX (oper,X)    83      2       6

   MAKE_INSTR(0x87, SAX, ZER, 3, false);
   MAKE_INSTR(0x97, SAX, ZEY, 4, false);
   MAKE_INSTR(0x8F, SAX, ABS, 4, false);
   MAKE_INSTR(0x83, SAX, INX, 6, false);

// SBX (AXS, SAX)
// CMP and DEX at once, sets flags like CMP
//
// (A AND X) - oper -> X
//
// N    Z       C       I       D       V
// +    +       +       -       -       -
// addressing   assembler       opc     bytes   cycles
// immediate    SBX #oper       CB      2       2

   MAKE_INSTR(0xCB, SBX, IMM, 2, false);

// SHA (AHX, AXA)
// Stores A AND X AND (high-byte of addr. + 1) at addr.
//
// unstable: sometimes 'AND (H+1)' is dropped, page boundary crossings
// may not work (with the high-byte of the value used as the high-byte of the address)
//
// A AND X AND (H+1) -> M
//
// N    Z       C       I       D       V
// -    -       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// absolute,Y   SHA oper,Y      9F      3       5       †
// (indirect),Y SHA (oper),Y    93      2       6       †

   MAKE_INSTR(0x9F, SHA, ABY, 5, false);
   MAKE_INSTR(0x93, SHA, INY, 6, false);

// SHX (A11, SXA, XAS)
// Stores X AND (high-byte of addr. + 1) at addr.
//
// unstable: sometimes 'AND (H+1)' is dropped, page boundary
// crossings may not work (with the high-byte of the value used as the high-byte of the address)
//
// X AND (H+1) -> M
//
// N    Z       C       I       D       V
// -    -       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// absolute,Y   SHX oper,Y      9E      3       5       †

   MAKE_INSTR(0x9E, SHX, ABY, 5, false);

// SHY (A11, SYA, SAY)
// Stores Y AND (high-byte of addr. + 1) at addr.
//
// unstable: sometimes 'AND (H+1)' is dropped, page boundary
// crossings may not work (with the high-byte of the value used as the high-byte of the address)
//
// Y AND (H+1) -> M
//
// N    Z       C       I       D       V
// -    -       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// absolute,X   SHY oper,X      9C      3       5       †

   MAKE_INSTR(0x9C, SHY, ABX, 5, false);

// SLO (ASO)
// ASL oper + ORA oper
//
// M = C <- [76543210] <- 0, A OR M -> A
//
// N    Z       C       I       D       V
// +    +       +       -       -       -
// addressing   assembler       opc     bytes   cycles
// zeropage     SLO oper        07      2       5
// zeropage,X   SLO oper,X      17      2       6
// absolute     SLO oper        0F      3       6
// absolute,X   SLO oper,X      1F      3       7
// absolute,Y   SLO oper,Y      1B      3       7
// (indirect,X) SLO (oper,X)    03      2       8
// (indirect),Y SLO (oper),Y    13      2       8

   MAKE_INSTR(0x07, SLO, ZER, 5, false);
   MAKE_INSTR(0x17, SLO, ZEX, 6, false);
   MAKE_INSTR(0x0F, SLO, ABS, 6, false);
   MAKE_INSTR(0x1F, SLO, ABX, 7, false);
   MAKE_INSTR(0x1B, SLO, ABY, 7, false);
   MAKE_INSTR(0x03, SLO, INX, 8, false);
   MAKE_INSTR(0x13, SLO, INY, 8, false);

// SRE (LSE)
// LSR oper + EOR oper
//
// M = 0 -> [76543210] -> C, A EOR M -> A
//
// N    Z       C       I       D       V
// +    +       +       -       -       -
// addressing   assembler       opc     bytes   cycles
// zeropage     SRE oper        47      2       5
// zeropage,X   SRE oper,X      57      2       6
// absolute     SRE oper        4F      3       6
// absolute,X   SRE oper,X      5F      3       7
// absolute,Y   SRE oper,Y      5B      3       7
// (indirect,X) SRE (oper,X)    43      2       8
// (indirect),Y SRE (oper),Y    53      2       8

   MAKE_INSTR(0x47, SRE, ZER, 5, false);
   MAKE_INSTR(0x57, SRE, ZEX, 6, false);
   MAKE_INSTR(0x4F, SRE, ABS, 6, false);
   MAKE_INSTR(0x5F, SRE, ABX, 7, false);
   MAKE_INSTR(0x5B, SRE, ABY, 7, false);
   MAKE_INSTR(0x43, SRE, INX, 8, false);
   MAKE_INSTR(0x53, SRE, INY, 8, false);

// TAS (XAS, SHS)
// Puts A AND X in SP and stores A AND X AND (high-byte of addr. + 1) at addr.
//
// unstable: sometimes 'AND (H+1)' is dropped, page boundary
// crossings may not work (with the high-byte of the value used as the high-byte of the address)
//
// A AND X -> SP, A AND X AND (H+1) -> M
//
// N    Z       C       I       D       V
// -    -       -       -       -       -
// addressing   assembler       opc     bytes   cycles
// absolute,Y   TAS oper,Y      9B      3       5       †

   MAKE_INSTR(0x9B, TAS, ABY, 5, false);

// USBC (SBC)
// SBC oper + NOP
//
// effectively same as normal SBC immediate, instr. E9.
//
// A - M - C̅ -> A
//
// N    Z       C       I       D       V
// +    +       +       -       -       +
// addressing   assembler       opc     bytes   cycles
// immediate    USBC #oper      EB      2       2

   MAKE_INSTR(0xEB, SBC, IMM, 2, false);

// NOPs (including DOP, TOP)
// Instructions effecting in 'no operations' in various address modes. Operands are ignored.
//
// N    Z       C       I       D       V
// -    -       -       -       -       -
// opc  addressing      bytes   cycles
// 1A   implied         1       2
// 3A   implied         1       2
// 5A   implied         1       2
// 7A   implied         1       2
// DA   implied         1       2
// FA   implied         1       2

   MAKE_INSTR(0x1A, NOP, IMP, 2, false);
   MAKE_INSTR(0x3A, NOP, IMP, 2, false);
   MAKE_INSTR(0x5A, NOP, IMP, 2, false);
   MAKE_INSTR(0x7A, NOP, IMP, 2, false);
   MAKE_INSTR(0xDA, NOP, IMP, 2, false);
   MAKE_INSTR(0xFA, NOP, IMP, 2, false);

// 80   immediate       2       2
// 82   immediate       2       2
// 89   immediate       2       2
// C2   immediate       2       2
// E2   immediate       2       2

   MAKE_INSTR(0x80, NOP, IMM, 2, false);
   MAKE_INSTR(0x82, NOP, IMM, 2, false);
   MAKE_INSTR(0x89, NOP, IMM, 2, false);
   MAKE_INSTR(0xC2, NOP, IMM, 2, false);
   MAKE_INSTR(0xE2, NOP, IMM, 2, false);

// 04   zeropage        2       3
// 44   zeropage        2       3
// 64   zeropage        2       3

   MAKE_INSTR(0x04, NOP, ZER, 3, false);
   MAKE_INSTR(0x44, NOP, ZER, 3, false);
   MAKE_INSTR(0x64, NOP, ZER, 3, false);

// 14   zeropage,X      2       4
// 34   zeropage,X      2       4
// 54   zeropage,X      2       4
// 74   zeropage,X      2       4
// D4   zeropage,X      2       4
// F4   zeropage,X      2       4

   MAKE_INSTR(0x14, NOP, ZEX, 4, false);
   MAKE_INSTR(0x34, NOP, ZEX, 4, false);
   MAKE_INSTR(0x54, NOP, ZEX, 4, false);
   MAKE_INSTR(0x74, NOP, ZEX, 4, false);
   MAKE_INSTR(0xD4, NOP, ZEX, 4, false);
   MAKE_INSTR(0xF4, NOP, ZEX, 4, false);

// 0C   absolute        3       4

   MAKE_INSTR(0x0C, NOP, ABS, 4, false);

// 1C   absolute,X      3       4*
// 3C   absolute,X      3       4*
// 5C   absolute,X      3       4*
// 7C   absolute,X      3       4*
// DC   absolute,X      3       4*
// FC   absolute,X      3       4*

   MAKE_INSTR(0x1C, NOP, ABX, 4, true);
   MAKE_INSTR(0x3C, NOP, ABX, 4, true);
   MAKE_INSTR(0x5C, NOP, ABX, 4, true);
   MAKE_INSTR(0x7C, NOP, ABX, 4, true);
   MAKE_INSTR(0xDC, NOP, ABX, 4, true);
   MAKE_INSTR(0xFC, NOP, ABX, 4, true);

// JAM (KIL, HLT)
// These instructions freeze the CPU.
//
// The processor will be trapped infinitely in T1 phase with $FF on the data bus. — Reset required.
//
// Instruction codes: 02, 12, 22, 32, 42, 52, 62, 72, 92, B2, D2, F2

   MAKE_INSTR(0x02, ILLEGAL, IMP, 0, false);
   MAKE_INSTR(0x12, ILLEGAL, IMP, 0, false);
   MAKE_INSTR(0x22, ILLEGAL, IMP, 0, false);
   MAKE_INSTR(0x32, ILLEGAL, IMP, 0, false);
   MAKE_INSTR(0x42, ILLEGAL, IMP, 0, false);
   MAKE_INSTR(0x52, ILLEGAL, IMP, 0, false);
   MAKE_INSTR(0x62, ILLEGAL, IMP, 0, false);
   MAKE_INSTR(0x72, ILLEGAL, IMP, 0, false);
   MAKE_INSTR(0x92, ILLEGAL, IMP, 0, false);
   MAKE_INSTR(0xB2, ILLEGAL, IMP, 0, false);
   MAKE_INSTR(0xD2, ILLEGAL, IMP, 0, false);
   MAKE_INSTR(0xF2, ILLEGAL, IMP, 0, false);

#endif

   return;
}

uint16_t mos6502::Addr_ACC()
{
   return 0; // not used
}

uint16_t mos6502::Addr_IMM()
{
   return pc++;
}

uint16_t mos6502::Addr_ABS()
{
   uint16_t addrL;
   uint16_t addrH;
   uint16_t addr;

   addrL = Read(pc++);
   addrH = Read(pc++);

   addr = addrL + (addrH << 8);

   return addr;
}

uint16_t mos6502::Addr_ZER()
{
   return Read(pc++);
}

uint16_t mos6502::Addr_IMP()
{
   return 0; // not used
}

uint16_t mos6502::Addr_REL()
{
   uint16_t offset;
   uint16_t addr;

   offset = (uint16_t)Read(pc++);
   if (offset & 0x80) offset |= 0xFF00;
   addr = pc + (int16_t)offset;
   crossed = (addr & 0xFF00) != (pc & 0xFF00);

   return addr;
}

uint16_t mos6502::Addr_ABI()
{
   uint16_t addrL;
   uint16_t addrH;
   uint16_t effL;
   uint16_t effH;
   uint16_t abs;
   uint16_t addr;

   addrL = Read(pc++);
   addrH = Read(pc++);

   abs = (addrH << 8) | addrL;

   effL = Read(abs);

#ifndef CMOS_INDIRECT_JMP_FIX
   effH = Read((abs & 0xFF00) + ((abs + 1) & 0x00FF) );
#else
   effH = Read(abs + 1);
#endif

   addr = effL + 0x100 * effH;

   return addr;
}

uint16_t mos6502::Addr_ZEX()
{
   uint16_t addr = (Read(pc++) + X) & 0xFF;
   return addr;
}

uint16_t mos6502::Addr_ZEY()
{
   uint16_t addr = (Read(pc++) + Y) & 0xFF;
   return addr;
}

uint16_t mos6502::Addr_ABX()
{
   uint16_t addr;
   uint16_t addrL;
   uint16_t addrH;

   addrL = Read(pc++);
   addrH = Read(pc++);

   addr = addrL + (addrH << 8) + X;
   crossed = (addrL + X) > 255;
   return addr;
}

uint16_t mos6502::Addr_ABY()
{
   uint16_t addr;
   uint16_t addrL;
   uint16_t addrH;

   addrL = Read(pc++);
   addrH = Read(pc++);

   addr = addrL + (addrH << 8) + Y;
   crossed = (addrL + Y) > 255;
   return addr;
}

uint16_t mos6502::Addr_INX()
{
   uint16_t zeroL;
   uint16_t zeroH;
   uint16_t addr;

   zeroL = (Read(pc++) + X) & 0xFF;
   zeroH = (zeroL + 1) & 0xFF;
   addr = Read(zeroL) + (Read(zeroH) << 8);

   return addr;
}

uint16_t mos6502::Addr_INY()
{
   uint16_t baseL;
   uint16_t zeroL;
   uint16_t zeroH;
   uint16_t addr;

   zeroL = Read(pc++);
   zeroH = (zeroL + 1) & 0xFF;
   addr = (baseL = /* ASSIGN */ Read(zeroL)) + (Read(zeroH) << 8) + Y;
   crossed = (baseL + Y) > 255;

   return addr;
}

void mos6502::IRQ(bool line)
{
   irq_line = line;
}

void mos6502::NMI(bool line)
{
   // falling edge triggered
   if (nmi_line == true && line == false) {
      if (!nmi_inhibit) {
         nmi_request = true;
      }
   }
   nmi_line = line;
}

void mos6502::Reset()
{
   // do not set or clear irq_line, that's external to us
   // do not set or clear nmi_line, that's external to us
   nmi_request = false;
   nmi_inhibit = false;

   A = reset_A;
   Y = reset_Y;
   X = reset_X;

   // load PC from reset vector
   uint8_t pcl = Read(rstVectorL);
   uint8_t pch = Read(rstVectorH);
   pc = (pch << 8) + pcl;

   sp = reset_sp;

   status = reset_status | CONSTANT | BREAK;

   illegalOpcode = false;

   return;
}

void mos6502::StackPush(uint8_t byte)
{
   Write(0x0100 + sp, byte);
   if(sp == 0x00) sp = 0xFF;
   else sp--;
}

uint8_t mos6502::StackPop()
{
   if(sp == 0xFF) sp = 0x00;
   else sp++;
   return Read(0x0100 + sp);
}

void mos6502::Svc_IRQ()
{
   //SET_BREAK(0);
   StackPush((pc >> 8) & 0xFF);
   StackPush(pc & 0xFF);
   StackPush((status & ~BREAK) | CONSTANT);
   SET_INTERRUPT(1);

   // load PC from interrupt request vector
   uint8_t pcl = Read(irqVectorL);
   uint8_t pch = Read(irqVectorH);
   pc = (pch << 8) + pcl;
   return;
}

void mos6502::Svc_NMI()
{
   //SET_BREAK(0);
   StackPush((pc >> 8) & 0xFF);
   StackPush(pc & 0xFF);
   StackPush((status & ~BREAK) | CONSTANT);
   SET_INTERRUPT(1);

   // load PC from non-maskable interrupt vector
   uint8_t pcl = Read(nmiVectorL);
   uint8_t pch = Read(nmiVectorH);
   pc = (pch << 8) + pcl;
   return;
}

bool mos6502::CheckInterrupts() {

   // NMI is edge triggered
   if (nmi_request && !nmi_inhibit) {
      nmi_request = false;
      nmi_inhibit = true;
      Svc_NMI();
      return true;
   }

   // check disabled bit
   if(!IF_INTERRUPT()) {
      // IRQ is level triggered
      if (irq_line == false && !nmi_inhibit) {
         Svc_IRQ();
         return true;
      }
   }

   return false;
}

void mos6502::Run(
      int32_t cyclesRemaining,
      uint64_t& cycleCount,
      CycleMethod cycleMethod)
{
   uint8_t opcode;
   Instr instr;

   while(cyclesRemaining > 0 && !illegalOpcode)
   {
      if (CheckInterrupts()) {
         cycleCount += 6; // TODO FIX verify this is correct
      }

      // fetch
      opcode = Read(pc++);

      // decode
      instr = InstrTable[opcode];

      // execute
      Exec(instr);

      cycleCount += instr.cycles;
      if (branched) {
         cycleCount++;
      }
      if (instr.penalty && crossed) {
         cycleCount++;
      }
      cyclesRemaining -=
         cycleMethod == CYCLE_COUNT        ? instr.cycles
         /* cycleMethod == INST_COUNT */   : 1;

      // run clock cycle callback
      if (Cycle)
         for(int i = 0; i < instr.cycles; i++)
            Cycle(this);
   }
}

void mos6502::RunEternally()
{
   uint8_t opcode;
   Instr instr;

   while(!illegalOpcode)
   {
      CheckInterrupts();

      // fetch
      opcode = Read(pc++);

      // decode
      instr = InstrTable[opcode];

      // execute
      Exec(instr);

      // run clock cycle callback
      if (Cycle)
         for(int i = 0; i < instr.cycles; i++)
            Cycle(this);
   }
}

void mos6502::Exec(Instr i)
{
   crossed = false;
   branched = false;
   uint16_t src = (this->*i.addr)();
   (this->*i.code)(src);
}

uint16_t mos6502::GetPC()
{
   return pc;
}

uint8_t mos6502::GetS()
{
   return sp;
}

uint8_t mos6502::GetP()
{
   return status;
}

uint8_t mos6502::GetA()
{
   return A;
}

uint8_t mos6502::GetX()
{
   return X;
}

uint8_t mos6502::GetY()
{
   return Y;
}

void mos6502::SetPC(uint16_t n)
{
   pc = n;
}

void mos6502::SetS (uint8_t n)
{
   sp = n;
}

void mos6502::SetP (uint8_t n)
{
   status = n;
}

void mos6502::SetA (uint8_t n)
{
   A = n;
}

void mos6502::SetX (uint8_t n)
{
   X = n;
}

void mos6502::SetY (uint8_t n)
{
   Y = n;
}

void mos6502::SetResetS(uint8_t value)
{
   reset_sp = value;
}

void mos6502::SetResetA(uint8_t value)
{
   reset_A = value;
}

void mos6502::SetResetX(uint8_t value)
{
   reset_X = value;
}

void mos6502::SetResetY(uint8_t value)
{
   reset_Y = value;
}

void mos6502::SetResetP(uint8_t value)
{
   reset_status = value | CONSTANT | BREAK;
}

uint8_t mos6502::GetResetS()
{
   return reset_sp;
}

uint8_t mos6502::GetResetP()
{
   return reset_status;
}

uint8_t mos6502::GetResetA()
{
   return reset_A;
}

uint8_t mos6502::GetResetX()
{
   return reset_X;
}

uint8_t mos6502::GetResetY()
{
   return reset_Y;
}

void mos6502::Op_ILLEGAL(uint16_t src)
{
   illegalOpcode = true;
}

void mos6502::Op_ADC(uint16_t src)
{
   uint8_t m = Read(src);
   unsigned int tmp = m + A + (IF_CARRY() ? 1 : 0);

   // N V Z computed *BEFORE* adjustment
   SET_OVERFLOW(!((A ^ m) & 0x80) && ((A ^ tmp) & 0x80));
   SET_NEGATIVE(tmp & 0x80);
   SET_ZERO(!(tmp & 0xFF));

   if (IF_DECIMAL())
   {
      // see http://www.6502.org/tutorials/decimal_mode.html
      int AL = ((A & 0xF) + (m & 0xF) + (IF_CARRY() ? 1 : 0));
      if (AL >= 0xA) {
         AL = ((AL + 6) & 0xF) + 0x10;
      }
      tmp = (m & 0xF0) + (A & 0xF0) + AL;

      // N V recomputed
      SET_OVERFLOW(!((A ^ m) & 0x80) && ((A ^ tmp) & 0x80));
      SET_NEGATIVE(tmp & 0x80);

      if (tmp >= 0xA0) tmp += 0x60;
   }

   // C computed *AFTER* adjustment
   SET_CARRY(tmp > 0xFF);
   A = tmp & 0xFF;
   return;
}



void mos6502::Op_AND(uint16_t src)
{
   uint8_t m = Read(src);
   uint8_t res = m & A;
   SET_NEGATIVE(res & 0x80);
   SET_ZERO(!res);
   A = res;
   return;
}


void mos6502::Op_ASL(uint16_t src)
{
   uint8_t m = Read(src);
   SET_CARRY(m & 0x80);
   m <<= 1;
   m &= 0xFF;
   SET_NEGATIVE(m & 0x80);
   SET_ZERO(!m);
   Write(src, m);
   return;
}

void mos6502::Op_ASL_ACC(uint16_t src)
{
   uint8_t m = A;
   SET_CARRY(m & 0x80);
   m <<= 1;
   m &= 0xFF;
   SET_NEGATIVE(m & 0x80);
   SET_ZERO(!m);
   A = m;
   return;
}

void mos6502::Op_BCC(uint16_t src)
{
   if (!IF_CARRY())
   {
      pc = src;
      branched = true; // indicate we did branch
   }
   else {
      crossed = false; // branch not taken does not suffer penalty
   }
   return;
}

void mos6502::Op_BCS(uint16_t src)
{
   if (IF_CARRY())
   {
      pc = src;
      branched = true; // indicate we did branch
   }
   else {
      crossed = false; // branch not taken does not suffer penalty
   }
   return;
}

void mos6502::Op_BEQ(uint16_t src)
{
   if (IF_ZERO())
   {
      pc = src;
      branched = true; // indicate we did branch
   }
   else {
      crossed = false; // branch not taken does not suffer penalty
   }
   return;
}

void mos6502::Op_BIT(uint16_t src)
{
   uint8_t m = Read(src);
   uint8_t res = m & A;
   status = (status & 0x3F) | (uint8_t)(m & 0xC0);
   SET_ZERO(!res);
   return;
}

void mos6502::Op_BMI(uint16_t src)
{
   if (IF_NEGATIVE())
   {
      pc = src;
      branched = true; // indicate we did branch
   }
   else {
      crossed = false; // branch not taken does not suffer penalty
   }
   return;
}

void mos6502::Op_BNE(uint16_t src)
{
   if (!IF_ZERO())
   {
      pc = src;
      branched = true; // indicate we did branch
   }
   else {
      crossed = false; // branch not taken does not suffer penalty
   }
   return;
}

void mos6502::Op_BPL(uint16_t src)
{
   if (!IF_NEGATIVE())
   {
      pc = src;
      branched = true; // indicate we did branch
   }
   else {
      crossed = false; // branch not taken does not suffer penalty
   }
   return;
}

void mos6502::Op_BRK(uint16_t src)
{
   pc++;
   StackPush((pc >> 8) & 0xFF);
   StackPush(pc & 0xFF);
   StackPush(status | CONSTANT | BREAK);
   SET_INTERRUPT(1);
   pc = (Read(irqVectorH) << 8) + Read(irqVectorL);
   return;
}

void mos6502::Op_BVC(uint16_t src)
{
   if (!IF_OVERFLOW())
   {
      pc = src;
      branched = true; // indicate we did branch
   }
   else {
      crossed = false; // branch not taken does not suffer penalty
   }
   return;
}

void mos6502::Op_BVS(uint16_t src)
{
   if (IF_OVERFLOW())
   {
      pc = src;
      branched = true; // indicate we did branch
   }
   else {
      crossed = false; // branch not taken does not suffer penalty
   }
   return;
}

void mos6502::Op_CLC(uint16_t src)
{
   SET_CARRY(0);
   return;
}

void mos6502::Op_CLD(uint16_t src)
{
   SET_DECIMAL(0);
   return;
}

void mos6502::Op_CLI(uint16_t src)
{
   SET_INTERRUPT(0);
   return;
}

void mos6502::Op_CLV(uint16_t src)
{
   SET_OVERFLOW(0);
   return;
}

void mos6502::Op_CMP(uint16_t src)
{
   unsigned int tmp = A - Read(src);
   SET_CARRY(tmp < 0x100);
   SET_NEGATIVE(tmp & 0x80);
   SET_ZERO(!(tmp & 0xFF));
   return;
}

void mos6502::Op_CPX(uint16_t src)
{
   unsigned int tmp = X - Read(src);
   SET_CARRY(tmp < 0x100);
   SET_NEGATIVE(tmp & 0x80);
   SET_ZERO(!(tmp & 0xFF));
   return;
}

void mos6502::Op_CPY(uint16_t src)
{
   unsigned int tmp = Y - Read(src);
   SET_CARRY(tmp < 0x100);
   SET_NEGATIVE(tmp & 0x80);
   SET_ZERO(!(tmp & 0xFF));
   return;
}

void mos6502::Op_DEC(uint16_t src)
{
   uint8_t m = Read(src);
   m = (m - 1) & 0xFF;
   SET_NEGATIVE(m & 0x80);
   SET_ZERO(!m);
   Write(src, m);
   return;
}

void mos6502::Op_DEX(uint16_t src)
{
   uint8_t m = X;
   m = (m - 1) & 0xFF;
   SET_NEGATIVE(m & 0x80);
   SET_ZERO(!m);
   X = m;
   return;
}

void mos6502::Op_DEY(uint16_t src)
{
   uint8_t m = Y;
   m = (m - 1) & 0xFF;
   SET_NEGATIVE(m & 0x80);
   SET_ZERO(!m);
   Y = m;
   return;
}

void mos6502::Op_EOR(uint16_t src)
{
   uint8_t m = Read(src);
   m = A ^ m;
   SET_NEGATIVE(m & 0x80);
   SET_ZERO(!m);
   A = m;
}

void mos6502::Op_INC(uint16_t src)
{
   uint8_t m = Read(src);
   m = (m + 1) & 0xFF;
   SET_NEGATIVE(m & 0x80);
   SET_ZERO(!m);
   Write(src, m);
}

void mos6502::Op_INX(uint16_t src)
{
   uint8_t m = X;
   m = (m + 1) & 0xFF;
   SET_NEGATIVE(m & 0x80);
   SET_ZERO(!m);
   X = m;
}

void mos6502::Op_INY(uint16_t src)
{
   uint8_t m = Y;
   m = (m + 1) & 0xFF;
   SET_NEGATIVE(m & 0x80);
   SET_ZERO(!m);
   Y = m;
}

void mos6502::Op_JMP(uint16_t src)
{
   pc = src;
}

void mos6502::Op_JSR(uint16_t src)
{
   pc--;
   StackPush((pc >> 8) & 0xFF);
   StackPush(pc & 0xFF);

   // this fixes an obscure problem that only happens when
   // the operand and the processor stack are at the same
   // place...
   src = (src & 0xFF) | (Read(pc) << 8);

   pc = src;
}

void mos6502::Op_LDA(uint16_t src)
{
   uint8_t m = Read(src);
   SET_NEGATIVE(m & 0x80);
   SET_ZERO(!m);
   A = m;
}

void mos6502::Op_LDX(uint16_t src)
{
   uint8_t m = Read(src);
   SET_NEGATIVE(m & 0x80);
   SET_ZERO(!m);
   X = m;
}

void mos6502::Op_LDY(uint16_t src)
{
   uint8_t m = Read(src);
   SET_NEGATIVE(m & 0x80);
   SET_ZERO(!m);
   Y = m;
}

void mos6502::Op_LSR(uint16_t src)
{
   uint8_t m = Read(src);
   SET_CARRY(m & 0x01);
   m >>= 1;
   SET_NEGATIVE(0);
   SET_ZERO(!m);
   Write(src, m);
}

void mos6502::Op_LSR_ACC(uint16_t src)
{
   uint8_t m = A;
   SET_CARRY(m & 0x01);
   m >>= 1;
   SET_NEGATIVE(0);
   SET_ZERO(!m);
   A = m;
}

void mos6502::Op_NOP(uint16_t src)
{
   return;
}

void mos6502::Op_ORA(uint16_t src)
{
   uint8_t m = Read(src);
   m = A | m;
   SET_NEGATIVE(m & 0x80);
   SET_ZERO(!m);
   A = m;
}

void mos6502::Op_PHA(uint16_t src)
{
   StackPush(A);
   return;
}

void mos6502::Op_PHP(uint16_t src)
{
   StackPush(status | CONSTANT | BREAK);
   return;
}

void mos6502::Op_PLA(uint16_t src)
{
   A = StackPop();
   SET_NEGATIVE(A & 0x80);
   SET_ZERO(!A);
   return;
}

void mos6502::Op_PLP(uint16_t src)
{
   status = (status & (CONSTANT | BREAK)) | (StackPop() & ~(CONSTANT | BREAK));
   return;
}

void mos6502::Op_ROL(uint16_t src)
{
   uint16_t m = Read(src);
   m <<= 1;
   if (IF_CARRY()) m |= 0x01;
   SET_CARRY(m > 0xFF);
   m &= 0xFF;
   SET_NEGATIVE(m & 0x80);
   SET_ZERO(!m);
   Write(src, m);
   return;
}

void mos6502::Op_ROL_ACC(uint16_t src)
{
   uint16_t m = A;
   m <<= 1;
   if (IF_CARRY()) m |= 0x01;
   SET_CARRY(m > 0xFF);
   m &= 0xFF;
   SET_NEGATIVE(m & 0x80);
   SET_ZERO(!m);
   A = m;
   return;
}

void mos6502::Op_ROR(uint16_t src)
{
   uint16_t m = Read(src);
   if (IF_CARRY()) m |= 0x100;
   SET_CARRY(m & 0x01);
   m >>= 1;
   m &= 0xFF;
   SET_NEGATIVE(m & 0x80);
   SET_ZERO(!m);
   Write(src, m);
   return;
}

void mos6502::Op_ROR_ACC(uint16_t src)
{
   uint16_t m = A;
   if (IF_CARRY()) m |= 0x100;
   SET_CARRY(m & 0x01);
   m >>= 1;
   m &= 0xFF;
   SET_NEGATIVE(m & 0x80);
   SET_ZERO(!m);
   A = m;
   return;
}

void mos6502::Op_RTI(uint16_t src)
{
   uint8_t lo, hi;

   status = (status & (CONSTANT | BREAK)) | (StackPop() & ~(CONSTANT | BREAK));

   lo = StackPop();
   hi = StackPop();

   pc = (hi << 8) | lo;

   nmi_inhibit = false; // always, more efficient than if()

   return;
}

void mos6502::Op_RTS(uint16_t src)
{
   uint8_t lo, hi;

   lo = StackPop();
   hi = StackPop();

   pc = ((hi << 8) | lo) + 1;
   return;
}

void mos6502::Op_SBC(uint16_t src)
{
   uint8_t m   = Read(src);
   int tmp     = A - m - (IF_CARRY() ? 0 : 1);

   // N V Z computed *BEFORE* adjustment (binary semantics)
   SET_OVERFLOW(((A ^ m) & (A ^ tmp) & 0x80) != 0);
   SET_NEGATIVE(tmp & 0x80 );
   SET_ZERO(!(tmp & 0xFF));

   if (IF_DECIMAL())
   {
      // see http://www.6502.org/tutorials/decimal_mode.html
      int AL = (A & 0x0F) - (m & 0x0F) - (IF_CARRY() ? 0 : 1);
      if (AL < 0) {
         AL = ((AL - 6) & 0x0F) - 0x10;
      }
      tmp = (A & 0xF0) - (m & 0xF0) + AL;

      // N V recompute
      SET_OVERFLOW(((A ^ m) & (A ^ tmp) & 0x80) != 0);
      SET_NEGATIVE(tmp & 0x80 );

      if (tmp < 0) tmp -= 0x60; // ???? huh ????
   }

   // C computed *AFTER* adjustment
   SET_CARRY( tmp >= 0 );

   A = tmp & 0xFF;
   return;
}

void mos6502::Op_SEC(uint16_t src)
{
   SET_CARRY(1);
   return;
}

void mos6502::Op_SED(uint16_t src)
{
   SET_DECIMAL(1);
   return;
}

void mos6502::Op_SEI(uint16_t src)
{
   SET_INTERRUPT(1);
   return;
}

void mos6502::Op_STA(uint16_t src)
{
   Write(src, A);
   return;
}

void mos6502::Op_STX(uint16_t src)
{
   Write(src, X);
   return;
}

void mos6502::Op_STY(uint16_t src)
{
   Write(src, Y);
   return;
}

void mos6502::Op_TAX(uint16_t src)
{
   uint8_t m = A;
   SET_NEGATIVE(m & 0x80);
   SET_ZERO(!m);
   X = m;
   return;
}

void mos6502::Op_TAY(uint16_t src)
{
   uint8_t m = A;
   SET_NEGATIVE(m & 0x80);
   SET_ZERO(!m);
   Y = m;
   return;
}

void mos6502::Op_TSX(uint16_t src)
{
   uint8_t m = sp;
   SET_NEGATIVE(m & 0x80);
   SET_ZERO(!m);
   X = m;
   return;
}

void mos6502::Op_TXA(uint16_t src)
{
   uint8_t m = X;
   SET_NEGATIVE(m & 0x80);
   SET_ZERO(!m);
   A = m;
   return;
}

void mos6502::Op_TXS(uint16_t src)
{
   sp = X;
   return;
}

void mos6502::Op_TYA(uint16_t src)
{
   uint8_t m = Y;
   SET_NEGATIVE(m & 0x80);
   SET_ZERO(!m);
   A = m;
   return;
}

#ifdef ILLEGAL_OPCODES

void mos6502::Op_ALR(uint16_t src)
{
   uint8_t m = Read(src);
   uint8_t res = m & A;
   SET_CARRY(res & 1);
   res >>= 1;
   SET_NEGATIVE(res & 0x80);
   SET_ZERO(!res);
   A = res;
   return;
}

void mos6502::Op_ANC(uint16_t src)
{
   uint8_t m = Read(src);
   uint8_t res = m & A;
   SET_CARRY(res & 0x80);
   SET_NEGATIVE(res & 0x80);
   SET_ZERO(!res);
   A = res;
   return;
}

void mos6502::Op_ANE(uint16_t src)
{
// A base value in A is determined based on the contets of A and a constant,
// which may be typically $00, $ff, $ee, etc. The value of this constant
// depends on temperature, the chip series, and maybe other factors, as well.
   const uint8_t constant = 0xee;

   uint8_t m = Read(src);
   uint8_t res = ((A | constant) & X & m);
   SET_NEGATIVE(res & 0x80);
   SET_ZERO(!res);
   A = res;
   return;
}

void mos6502::Op_ARR(uint16_t src)
{
   bool carry = IF_CARRY();
   uint8_t m = Read(src);
   uint8_t res = m & A;
   res >>= 1;
   if (carry) {
      res |= 0x80;
   }
   SET_CARRY((res >> 6) & 1);
   SET_OVERFLOW(((res >> 6) ^ (res >> 5)) & 1);
   SET_NEGATIVE(res & 0x80);
   SET_ZERO(!res);

   if (IF_DECIMAL())
   {
      // ARR in decimal mode routes signals through the ALU’s decimal
      // adder path, but with no valid carry-in, so the outputs are
      // garbage. It’s not emulatable in a meaningful way.

      if ((res & 0xF) >= 0xA) {
         res += 6;
      }
      if (res >= 0xA0) {
         res += 0x60;
      }
   }

   A = res;
   return;
}

void mos6502::Op_DCP(uint16_t src)
{
   uint8_t m = Read(src);
   m = (m - 1) & 0xFF;
   Write(src, m);

   unsigned int tmp = A - m;
   SET_CARRY(tmp < 0x100);
   SET_NEGATIVE(tmp & 0x80);
   SET_ZERO(!(tmp & 0xFF));
   return;
}

void mos6502::Op_ISC(uint16_t src)
{
   uint8_t m = Read(src);

   // from Op_INC
   m = (m + 1) & 0xFF;
   Write(src, m);

   // from here on is Op_SBC
   int tmp     = A - m - (IF_CARRY() ? 0 : 1);

   // N V Z computed *BEFORE* adjustment (binary semantics)
   SET_OVERFLOW(((A ^ m) & (A ^ tmp) & 0x80) != 0);
   SET_NEGATIVE(tmp & 0x80 );
   SET_ZERO(!(tmp & 0xFF));

   if (IF_DECIMAL())
   {
      // see http://www.6502.org/tutorials/decimal_mode.html
      int AL = (A & 0x0F) - (m & 0x0F) - (IF_CARRY() ? 0 : 1);
      if (AL < 0) {
         AL = ((AL - 6) & 0x0F) - 0x10;
      }
      tmp = (A & 0xF0) - (m & 0xF0) + AL;

      // N V recompute
      SET_OVERFLOW(((A ^ m) & (A ^ tmp) & 0x80) != 0);
      SET_NEGATIVE(tmp & 0x80 );

      if (tmp < 0) tmp -= 0x60; // ???? huh ????
   }

   // C computed *AFTER* adjustment
   SET_CARRY( tmp >= 0 );

   A = tmp & 0xFF;
   return;
}

void mos6502::Op_LAS(uint16_t src)
{
   uint8_t tmp = Read(src);
   tmp &= sp;
   A = X = sp = tmp;
   SET_NEGATIVE(tmp & 0x80);
   SET_ZERO(tmp == 0);
   return;
}

void mos6502::Op_LAX(uint16_t src)
{
   uint8_t m = Read(src);
   SET_NEGATIVE(m & 0x80);
   SET_ZERO(!m);
   A = X = m;
}

void mos6502::Op_LXA(uint16_t src)
{
   uint8_t m = Read(src);
   A = (A | 0xee) & m;  // like ANE, unstable mystery constant
   X = A;
   SET_NEGATIVE(A & 0x80);
   SET_ZERO(!A);
}

void mos6502::Op_RLA(uint16_t src)
{
   uint16_t m = Read(src);
   m <<= 1;
   if (IF_CARRY()) m |= 0x01;
   SET_CARRY(m > 0xFF);
   m &= 0xFF;
   Write(src, m);

   A &= m;

   SET_NEGATIVE(A & 0x80);
   SET_ZERO(!A);

   return;
}

void mos6502::Op_RRA(uint16_t src)
{
   uint16_t m = Read(src);
   if (IF_CARRY()) {
      m |= 0x100;
   }
   SET_CARRY(m & 0x01);
   m >>= 1;
   m &= 0xFF;
   Write(src, m);

   // TODO FIX, the rest of this is just Op_ADC
   // combine common code into a helper function
   unsigned int tmp = m + A + (IF_CARRY() ? 1 : 0);

   // N V Z computed *BEFORE* adjustment
   SET_OVERFLOW(!((A ^ m) & 0x80) && ((A ^ tmp) & 0x80));
   SET_NEGATIVE(tmp & 0x80);
   SET_ZERO(!(tmp & 0xFF));

   if (IF_DECIMAL())
   {
      // see http://www.6502.org/tutorials/decimal_mode.html
      int AL = ((A & 0xF) + (m & 0xF) + (IF_CARRY() ? 1 : 0));
      if (AL >= 0xA) {
         AL = ((AL + 6) & 0xF) + 0x10;
      }
      tmp = (m & 0xF0) + (A & 0xF0) + AL;

      // N V recomputed
      SET_OVERFLOW(!((A ^ m) & 0x80) && ((A ^ tmp) & 0x80));
      SET_NEGATIVE(tmp & 0x80);

      if (tmp >= 0xA0) tmp += 0x60;
   }

   // C computed *AFTER* adjustment
   SET_CARRY(tmp > 0xFF);
   A = tmp & 0xFF;
   return;
}

void mos6502::Op_SAX(uint16_t src)
{
   Write(src, A & X); // most simple illegal here
}

void mos6502::Op_SBX(uint16_t src)
{
   uint16_t m = Read(src);
   uint8_t tmp = A & X;

   SET_CARRY(tmp >= m);
   SET_ZERO(tmp == m);

   X = tmp - m;

   SET_NEGATIVE(X & 0x80);
}

void mos6502::Op_SHA(uint16_t src)
{
   // unstable, but this is the stable behavior
   uint8_t tmp = A & X & ((src >> 8) + 1);
   Write(src, tmp);
}

void mos6502::Op_SHX(uint16_t src)
{
   // unstable, but this is the stable behavior
   uint8_t tmp = X & ((src >> 8) + 1);
   Write(src, tmp);
}

void mos6502::Op_SHY(uint16_t src)
{
   // unstable, but this is the stable behavior
   uint8_t tmp = Y & ((src >> 8) + 1);
   Write(src, tmp);
}

void mos6502::Op_SLO(uint16_t src)
{
   uint8_t m = Read(src);
   SET_CARRY(m & 0x80);
   m <<= 1;
   m &= 0xFF;
   Write(src, m);

   A |= m;

   SET_NEGATIVE(A & 0x80);
   SET_ZERO(!A);
   return;
}

void mos6502::Op_SRE(uint16_t src)
{
   uint8_t m = Read(src);
   SET_CARRY(m & 0x01);
   m >>= 1;
   Write(src, m);

   A ^= m;

   SET_NEGATIVE(A & 0x80);
   SET_ZERO(!A);
   return;
}

void mos6502::Op_TAS(uint16_t src)
{
   // unstable, but this is the stable behavior
   sp = A & X;

   uint8_t tmp = A & X & ((src >> 8) + 1);
   Write(src, tmp);
}

#endif


================================================
FILE: mos6502.h
================================================
//============================================================================
// Name        : mos6502
// Author      : Gianluca Ghettini
// Version     : 1.0
// Copyright   :
// Description : A MOS 6502 CPU emulator written in C++
//============================================================================

#pragma once
#include <stdint.h>
#include <stdbool.h>

class mos6502
{
   private:
      // register reset values
      uint8_t reset_A;
      uint8_t reset_X;
      uint8_t reset_Y;
      uint8_t reset_sp;
      uint8_t reset_status;

      // registers
      uint8_t A; // accumulator
      uint8_t X; // X-index
      uint8_t Y; // Y-index

      // stack pointer
      uint8_t sp;

      // program counter
      uint16_t pc;

      // status register
      uint8_t status;

      typedef void (mos6502::*CodeExec)(uint16_t);
      typedef uint16_t (mos6502::*AddrExec)();

      struct Instr
      {
         AddrExec addr;
         const char * saddr;
         CodeExec code;
         const char * scode;
         uint8_t cycles;
         bool penalty;
      };

      static Instr InstrTable[256];

      void Exec(Instr i);

      bool illegalOpcode;

      bool crossed;
      bool branched;

      bool irq_line;      // current state of the line

      bool nmi_request;   // is there an NMI pending?
      bool nmi_inhibit;  // are we currently handling an NMI?
      bool nmi_line;      // current state of the NMI line

      bool CheckInterrupts();

      // addressing modes
      uint16_t Addr_ACC(); // ACCUMULATOR
      uint16_t Addr_IMM(); // IMMEDIATE
      uint16_t Addr_ABS(); // ABSOLUTE
      uint16_t Addr_ZER(); // ZERO PAGE
      uint16_t Addr_ZEX(); // INDEXED-X ZERO PAGE
      uint16_t Addr_ZEY(); // INDEXED-Y ZERO PAGE
      uint16_t Addr_ABX(); // INDEXED-X ABSOLUTE
      uint16_t Addr_ABY(); // INDEXED-Y ABSOLUTE
      uint16_t Addr_IMP(); // IMPLIED
      uint16_t Addr_REL(); // RELATIVE
      uint16_t Addr_INX(); // INDEXED-X INDIRECT
      uint16_t Addr_INY(); // INDEXED-Y INDIRECT
      uint16_t Addr_ABI(); // ABSOLUTE INDIRECT

      // opcodes (grouped as per datasheet)
      void Op_ADC(uint16_t src);
      void Op_AND(uint16_t src);
      void Op_ASL(uint16_t src);    void Op_ASL_ACC(uint16_t src);
      void Op_BCC(uint16_t src);
      void Op_BCS(uint16_t src);

      void Op_BEQ(uint16_t src);
      void Op_BIT(uint16_t src);
      void Op_BMI(uint16_t src);
      void Op_BNE(uint16_t src);
      void Op_BPL(uint16_t src);

      void Op_BRK(uint16_t src);
      void Op_BVC(uint16_t src);
      void Op_BVS(uint16_t src);
      void Op_CLC(uint16_t src);
      void Op_CLD(uint16_t src);

      void Op_CLI(uint16_t src);
      void Op_CLV(uint16_t src);
      void Op_CMP(uint16_t src);
      void Op_CPX(uint16_t src);
      void Op_CPY(uint16_t src);

      void Op_DEC(uint16_t src);
      void Op_DEX(uint16_t src);
      void Op_DEY(uint16_t src);
      void Op_EOR(uint16_t src);
      void Op_INC(uint16_t src);

      void Op_INX(uint16_t src);
      void Op_INY(uint16_t src);
      void Op_JMP(uint16_t src);
      void Op_JSR(uint16_t src);
      void Op_LDA(uint16_t src);

      void Op_LDX(uint16_t src);
      void Op_LDY(uint16_t src);
      void Op_LSR(uint16_t src);    void Op_LSR_ACC(uint16_t src);
      void Op_NOP(uint16_t src);
      void Op_ORA(uint16_t src);

      void Op_PHA(uint16_t src);
      void Op_PHP(uint16_t src);
      void Op_PLA(uint16_t src);
      void Op_PLP(uint16_t src);
      void Op_ROL(uint16_t src);    void Op_ROL_ACC(uint16_t src);

      void Op_ROR(uint16_t src);    void Op_ROR_ACC(uint16_t src);
      void Op_RTI(uint16_t src);
      void Op_RTS(uint16_t src);
      void Op_SBC(uint16_t src);
      void Op_SEC(uint16_t src);
      void Op_SED(uint16_t src);

      void Op_SEI(uint16_t src);
      void Op_STA(uint16_t src);
      void Op_STX(uint16_t src);
      void Op_STY(uint16_t src);
      void Op_TAX(uint16_t src);

      void Op_TAY(uint16_t src);
      void Op_TSX(uint16_t src);
      void Op_TXA(uint16_t src);
      void Op_TXS(uint16_t src);
      void Op_TYA(uint16_t src);

#ifdef ILLEGAL_OPCODES
      void Op_ALR(uint16_t src);
      void Op_ANC(uint16_t src);
      void Op_ANE(uint16_t src);
      void Op_ARR(uint16_t src);
      void Op_DCP(uint16_t src);
      void Op_ISC(uint16_t src);
      void Op_LAS(uint16_t src);
      void Op_LAX(uint16_t src);
      void Op_LXA(uint16_t src);
      void Op_RLA(uint16_t src);
      void Op_RRA(uint16_t src);
      void Op_SAX(uint16_t src);
      void Op_SBX(uint16_t src);
      void Op_SHA(uint16_t src);
      void Op_SHX(uint16_t src);
      void Op_SHY(uint16_t src);
      void Op_SLO(uint16_t src);
      void Op_SRE(uint16_t src);
      void Op_TAS(uint16_t src);
#endif

      void Op_ILLEGAL(uint16_t src);

      void Svc_NMI();
      void Svc_IRQ();

      // IRQ, reset, NMI vectors
      static const uint16_t irqVectorH = 0xFFFF;
      static const uint16_t irqVectorL = 0xFFFE;
      static const uint16_t rstVectorH = 0xFFFD;
      static const uint16_t rstVectorL = 0xFFFC;
      static const uint16_t nmiVectorH = 0xFFFB;
      static const uint16_t nmiVectorL = 0xFFFA;

      // read/write/clock-cycle callbacks
      typedef void (*BusWrite)(uint16_t, uint8_t);
      typedef uint8_t (*BusRead)(uint16_t);
      typedef void (*ClockCycle)(mos6502*);
      BusRead Read;
      BusWrite Write;
      ClockCycle Cycle;

      // stack operations
      inline void StackPush(uint8_t byte);
      inline uint8_t StackPop();

   public:
      enum CycleMethod {
         INST_COUNT,
         CYCLE_COUNT,
      };
      mos6502(BusRead r, BusWrite w, ClockCycle c = nullptr);

      // set or clear the NMI line.  this is an input to the processor.
      // a high to low edge transition will trigger an interrupt.
      // line state is NOT cleared by Reset()
      void NMI(bool line);

      // set or clear the IRQ line.  this is an input to the processor.
      // a low level will trigger an interrupt.
      // line state is NOT cleared by Reset()
      void IRQ(bool line);

      void Reset();
      void Run(
            int32_t cycles,
            uint64_t& cycleCount,
            CycleMethod cycleMethod = CYCLE_COUNT);
      void RunEternally(); // until it encounters a illegal opcode
                           // useful when running e.g. WOZ Monitor
                           // no need to worry about cycle exhaus-
                           // tion

      // Various getter/setters

      uint16_t GetPC();
      uint8_t GetS();
      uint8_t GetP();
      uint8_t GetA();
      uint8_t GetX();
      uint8_t GetY();

      void SetPC(uint16_t n);
      void SetS(uint8_t n);
      void SetP(uint8_t n);
      void SetA(uint8_t n);
      void SetX(uint8_t n);
      void SetY(uint8_t n);

      void SetResetS(uint8_t value);
      void SetResetP(uint8_t value);
      void SetResetA(uint8_t value);
      void SetResetX(uint8_t value);
      void SetResetY(uint8_t value);

      uint8_t GetResetS();
      uint8_t GetResetP();
      uint8_t GetResetA();
      uint8_t GetResetX();
      uint8_t GetResetY();
};


================================================
FILE: tests/Makefile
================================================
all:
	( cd functional && make )
	( cd singlestep && make )
	@echo ===============================
	@echo === ALL TESTS COMPLETE: success
	@echo ===============================


================================================
FILE: tests/functional/.gitignore
================================================
*.hex
*.lst
*.a65
main
6502_65C02_functional_tests


================================================
FILE: tests/functional/Makefile
================================================
# Makefile to run unit tests

# Fail early if required tools are missing
ifeq (, $(shell which git 2>/dev/null))
$(error "Error: git not found in PATH")
endif

ifeq (, $(shell which dosbox 2>/dev/null))
$(error "Error: dosbox not found in PATH")
endif

SHELL := /bin/bash
.SHELLFLAGS := -e -o pipefail -c

BASE := 6502_65C02_functional_tests

all: $(BASE) $(BASE)/as65_142 main tests tests
	@echo ======================================
	@echo === FUNCTIONAL TESTS COMPLETE: success
	@echo ======================================

clean:
	rm -rf $(BASE) ft.* dt.* it.*

$(BASE):
	@test ! -e "$@" || { echo "do NOT use 'make -B', use 'make clean ; make' instead"; exit 1; }
	@echo "Fetching functional tests from GitHub..."
	git clone https://github.com/Klaus2m5/6502_65C02_functional_tests

$(BASE)/as65_142:
	echo "Unpacking assembler..."
	mkdir -p $(BASE)/as65_142
	( cd $(BASE)/as65_142 && unzip ../as65_142.zip )

main: main.cpp ../../mos6502.cpp ../../mos6502.h
	g++ -Wall -O3 -o main ../../mos6502.cpp main.cpp

tests: 6502_functional_test 6502_decimal_test 6502_interrupt_test

ft.hex:
	cp $(BASE)/6502_functional_test.a65 ft.a65
	./as65.sh ft.a65

6502_functional_test: main ft.hex
	@echo "================ Running $@"
	./main ft.hex 0x400 0x3469 quiet # magic numbers come from comments and examination of *.lst

dt.hex:
	cp $(BASE)/6502_decimal_test.a65 dt.a65
	patch -p0 < decimal.patch
	./as65.sh dt.a65

6502_decimal_test: main dt.hex
	@echo "================ Running $@"
	./main dt.hex 0x200 ?0x000b quiet # magic numbers come from comments and examination of *.lst

it.hex:
	cp $(BASE)/6502_interrupt_test.a65 it.a65
	./as65.sh it.a65

6502_interrupt_test: main it.hex
	@echo "================ Running $@"
	./main it.hex 0x400 0x06f5 quiet # magic numbers come from comments and examination of *.lst


================================================
FILE: tests/functional/as65.sh
================================================
#!/bin/bash
set -euo pipefail

src_file="$1"
src_dir=$(dirname "$(realpath "$src_file")")
base_name=$(basename "${src_file%.*}")
dest_dir="6502_65C02_functional_tests/as65_142"

# Sanity checks
[[ -f "$src_file" ]] || { echo "Error: $src_file not found"; exit 1; }
[[ -d "$dest_dir" ]] || { echo "Error: $dest_dir not found"; exit 1; }

# ---- Collision check (case-insensitive) ----
shopt -s nullglob
declare -a collisions=()
check_fn_lc="$(basename "$src_file")"
check_fn_lc="${check_fn_lc,,}"         # full filename (with extension), lowercased
check_base_lc="${base_name,,}"         # basename (no extension), lowercased

for f in "$dest_dir"/*; do
    [[ -f "$f" ]] || continue
    bn="$(basename "$f")"
    bn_lc="${bn,,}"
    name="${bn%.*}"
    name_lc="${name,,}"
    if [[ "$bn_lc" == "$check_fn_lc" || "$name_lc" == "$check_base_lc" ]]; then
        collisions+=("$bn")
    fi
done
shopt -u nullglob

if (( ${#collisions[@]} > 0 )); then
    echo "Error: collision(s) in '$dest_dir' (case-insensitive): ${collisions[*]}" >&2
    echo "Refusing to copy to avoid overwriting existing files." >&2
    exit 1
fi

echo "Copying $src_file to $dest_dir"
cp "$src_file" "$dest_dir/" || { echo "Copy failed"; exit 1; }

(
  cd "$(dirname "$dest_dir")"
  echo "Running assembler under DOSBox..."
  dosbox \
         -c "config -set core=dynamic" \
         -c "config -set cycles=max" \
         -c "config -set cycleup=100000" \
         -c "config -set cycledown=100000" \
         -c "config -set frameskip=5" \
         -c "config -set output=surface" \
         -c "mount c $(pwd)" \
         -c "c:" \
         -c "cd as65_142" \
         -c "AS65-DOS.EXE -x1 -o${base_name}.hex -l -m -s2 -w -h0 -c -i -t -u -z $(basename "$src_file")" \
         -c "exit" # change that "exit" to a "pause" if you need to debug
) || { echo "Assembler failed"; exit 1; }

# ---- Artifact move block ----
echo "Moving artifacts back to $src_dir with normalized names..."
shopt -s nullglob
found=0
src_base_lc="${base_name,,}"

for f in "$dest_dir"/*; do
    [[ -f "$f" ]] || continue
    fn="$(basename "$f")"
    name="${fn%.*}"           # basename without extension
    ext="${fn##*.}"           # extension
    name_lc="${name,,}"       # lowercased basename

    if [[ "$name_lc" == "$src_base_lc" ]]; then
        echo "Moving $f → $src_dir/${base_name}.${ext,,}"
        mv "$f" "$src_dir/${base_name}.${ext,,}" || { echo "Failed to move $fn"; exit 1; }
        ((found++)) || true    # prevent 'set -e' from exiting since found++ returns old value (non-zero on later iterations)
    fi
done
shopt -u nullglob

(( found > 0 )) || { echo "No artifacts for '$base_name' found in $dest_dir" >&2; exit 1; }

echo "Success: moved $found artifact(s) to $src_dir"


================================================
FILE: tests/functional/decimal.patch
================================================
--- dt.a65	2025-10-12 17:08:27.391206657 -0700
+++ dt.a65	2025-10-12 17:10:37.155949816 -0700
@@ -34,7 +34,7 @@ chk_z   = 0         ; check zero flag
 chk_c   = 1         ; check carry flag
 
 end_of_test macro
-                db  $db     ;execute 65C02 stop instruction
+                jmp *       ; 6502 has no stop instruction
             endm
 
         bss


================================================
FILE: tests/functional/main.cpp
================================================
// compile with "g++ main.cpp ../../mos6502.cpp -o main"

#include "../../mos6502.h"

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>

bool quiet = false;

uint8_t ram[65536] = {0};

mos6502 *cpu = NULL;

int start = -1;
int success = -1;
int retaddr = -1;

void writeRam(uint16_t addr, uint8_t val)
{
   ram[addr] = val;

   // feedback
   if (addr == 0xbffc) {
      // things are inverted here
      cpu->IRQ((val & 1) ? false : true);
      cpu->NMI((val & 2) ? false : true);
   }
}

uint8_t readRam(uint16_t addr)
{
   return ram[addr];
}

void tick(mos6502*)
{
   static uint16_t lastpc = 0xFFFF;
   static int count = 0;
   uint16_t pc = cpu->GetPC();
   if (pc != lastpc) {
      if (!quiet) {
         printf("PC=%04x\r", pc);
      }
   }
   if (pc == success) {
      printf("\nsuccess\n");
      exit(0);
   }
   if (pc == lastpc) {
      count++;
      if (count > 100) {
         if (retaddr != -1) {
            if (ram[retaddr]) {
               printf("\ncode %02X\n", ram[retaddr]);
               printf("Y=%02x\n", cpu->GetY());
               printf("N1=%02x N2=%02x\n", ram[0], ram[1]);
               printf("HA=%02x HNVZC=%02x\n", ram[2], ram[3]);
               printf("DA=%02x DNVZC=%02x\n", ram[4], ram[5]);
               printf("AR=%02x NF=%02x VF=%02x ZF=%02x CF=%02x\n", ram[6], ram[7], ram[8], ram[9],
                      ram[10]);
               printf("FAIL\n");
               exit(-1);
            }
            else {
               printf("\nsuccess\n");
               exit(0);
            }
         }
         else {
            printf("\nFAIL\n");
            exit(-1);
         }
      }
   }
   else {
      count = 0;
   }
   lastpc = pc;
}

void bail(const char *s)
{
   fprintf(stderr, "%s\n", s);
   exit(-1);
}

uint32_t fetch(const char *s, uint16_t offset, uint8_t count)
{
   uint32_t ret = 0;
   uint32_t val = 0;
   for (int i = 0; i < count; i++) {
      ret <<= 4;
      if (s[offset + i] <= '9') {
         val = s[offset + i] - '0';
      }
      else if (s[offset + i] <= 'F') {
         val = s[offset + i] - 'A' + 10;
      }
      else if (s[offset + i] <= 'f') {
         val = s[offset + i] - 'a' + 10;
      }
      ret |= val;
   }
   return ret;
}

void handle_hex(const char *fname)
{
   char buf[1024];
   FILE *f = fopen(fname, "r");
   if (!f) {
      bail("could not open hex file");
   }
   while (NULL != fgets(buf, sizeof(buf), f)) {
      if (buf[0] != ':') {
         bail("unexpected start code in hex file");
      }

      // TODO FIX verify checksum for line here!

      int length = fetch(buf, 1, 2);
      int address = fetch(buf, 3, 4);
      int record = fetch(buf, 7, 2);
      if (record == 0x00) {
         for (int i = 0; i < length; i++) {
            ram[address + i] = fetch(buf, 9 + 2 * i, 2);
         }
      }
      else if (record == 0x01) {
         // do nothing
      }
      else {
         bail("unexpected record type in hex file");
      }
   }
   fclose(f);
}

int main(int argc, char **argv) {
   if (argc != 4 && argc != 5) {
      fprintf(stderr, "Usage: %s <file>.hex <start> <success> [quiet]\n", argv[0]);
      return -1;
   }

   if (argc == 5 && !strcmp(argv[4], "quiet")) {
      quiet = true;
   }

   handle_hex(argv[1]);
   start = strtoul(argv[2], NULL, 0);

   if (argv[3][0] == '?') {
      retaddr = strtoul(argv[3]+1, NULL, 0);
   }
   else {
      success = strtoul(argv[3], NULL, 0);
   }

   printf("start=%04X\n", start);
   ram[0xFFFC] = start & 0xFF;
   ram[0xFFFD] = start >> 8;

   cpu = new mos6502(readRam, writeRam, tick);
   cpu->Reset();
   cpu->RunEternally();

   return 0;
}


================================================
FILE: tests/functional/notes.txt
================================================
assembler originally came from https://www.kingswood-consulting.co.uk/assemblers/
author does NOT distribute source code.


================================================
FILE: tests/functional/readme.md
================================================
just type "make"


================================================
FILE: tests/singlestep/.gitignore
================================================
65x02
main


================================================
FILE: tests/singlestep/Makefile
================================================
# Makefile to run unit tests

# Fail early if required tools are missing
ifeq (, $(shell which git 2>/dev/null))
$(error "Error: git not found in PATH")
endif

SHELL := /bin/bash
.SHELLFLAGS := -e -o pipefail -c

BASE := 65x02

all: $(BASE) main tests
   @echo TEST COMPLETE: success

clean:
	rm -rf $(BASE) ft.* dt.* it.*

$(BASE):
	@test ! -e "$@" || { echo "do NOT use 'make -B', use 'make clean ; make' instead"; exit 1; }
	@echo "Fetching functional tests from GitHub..."
	git clone https://github.com/SingleStepTests/65x02.git

main: main.cpp ../../mos6502.cpp ../../mos6502.h
	g++ -O3 -Wall -o main -DILLEGAL_OPCODES ../../mos6502.cpp main.cpp

tests: main
	for f in 65x02/6502/v1/*.json; do echo == "$$f" ; ./main "$$f"; done
	@echo ======================================
	@echo === SINGLESTEP TESTS COMPLETE: success
	@echo ======================================




================================================
FILE: tests/singlestep/main.cpp
================================================
// compile with "g++ main.cpp ../../mos6502.cpp -o main"

#include "../../mos6502.h"

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>

#define NAME "\"name\": \""
#define CYCLES "\"cycles\": ["
#define INITIAL "\"initial\": {"
#define FINAL "\"final\": {"

#define PC "\"pc\": "
#define S  "\"s\": "
#define A  "\"a\": "
#define X  "\"x\": "
#define Y  "\"y\": "
#define P  "\"p\": "
#define RAM  "\"ram\": ["

#define PMASK (~0x00)

static const char *unstable = "\x6b\x93\x9b\x9c\x9e\x9f";

bool quiet = false;
uint8_t ram[65536] = {0};
mos6502 *cpu = NULL;
int linenum;
uint64_t cycles;
uint64_t actual_cycles;
int failures = 0;
bool is_unstable = false;
const char *name = NULL;

char linebuf[4096];
char hexbuf[4096];

void writeRam(uint16_t addr, uint8_t val)
{
   ram[addr] = val;
}

uint8_t readRam(uint16_t addr)
{
   return ram[addr];
}

void tick(mos6502*)
{
}

void translate(void)
{
   char *p = linebuf;
   char *q = hexbuf;

   if (!strchr(p, 'n')) {
      strcpy(q, p);
      return;
   }

   while (*p != ',') {
      *q++ = *p++;
   }

   bool valid = false;
   int n = 0;

   while (*p) {
      if (*p == ' ' && !strncmp(p+1, INITIAL, strlen(INITIAL))) {
         *q++ = '\n';
         p++;
      }
      else if (*p == ' ' && !strncmp(p+1, FINAL, strlen(FINAL))) {
         *q++ = '\n';
         p++;
      }
      else if (*p == ' ' && !strncmp(p+1, CYCLES, strlen(CYCLES))) {
         *q++ = '\n';
         p++;
      }
      else if (*p == ' ' && !strncmp(p+1, RAM, strlen(RAM))) {
         *q++ = '\n';
         p++;
      }
      else if (*p >= '0' && *p <= '9') {
         n = n * 10 + (*p - '0');
         valid = true;
         p++;
      }
      else {
         if (valid) {
            if (n < 256) {
               sprintf(q, "%02x", n);
               q += 2;
            }
            else {
               sprintf(q, "%04x", n);
               q += 4;
            }

            n = 0;
            valid = false;
         }
         *q++ = *p++;
      }
   }
   *q = 0;
}

bool failed = false;

void reset_fail(void) {
   failed = false;
}

void fail(const char *s)
{
   if (!failed) {
      failed = true;
      fprintf(stderr, "NAME: %s\n", name);
      fprintf(stderr, "%s\n", hexbuf);
   }
   fprintf(stderr, "%s\n", s);
   failures++;
}

void bail(const char *s)
{
   fprintf(stderr, "%s\n", s);
   exit(-1);
}

const char *locate(const char *haystack, const char *needle, const char *name)
{
   const char *ret = strstr(haystack, needle);
   if (ret) {
      ret += strlen(needle);
      return ret;
   }
   else {
      char buf[1024];
      sprintf(buf, "cannot find %s at line %d", name, linenum);
      bail(buf);
   }
   return NULL;
}

void handle_name(char *copy)
{
   char *p = (char *) locate(copy, NAME, "NAME");
   char *q = strchr(p, '\"');
   if (q) {
      *q = 0;
      //printf("NAME: %s\n", p);
      if (name) {
         free((void *)name);
      }
      name = strdup(p);
   }
   else {
      char buf[1024];
      sprintf(buf, "cannot parse NAME at line %d", linenum);
      bail(buf);
   }
   free(copy);
}

void handle_cycles(char *copy)
{
   char *p = (char *) locate(copy, CYCLES, "CYCLES");
   cycles = 0;

   while (*p) {
      if (*p == '[') {
         cycles++;
      }
      p++;
   }

   if (cycles < 2) {
      char buf[1024];
      sprintf(buf, "cannot parse CYCLES at line %d", linenum);
      bail(buf);
   }

   // printf("CYCLES: %d\n", (int)cycles);

   free(copy);
}

void handle_initial(char *copy)
{
   char *p = (char *) locate(copy, INITIAL, "INITIAL");

   cpu->SetPC(atoi(locate(p, PC, "INITIAL_PC")));
   cpu->SetS(atoi(locate(p, S, "INITIAL_S")));
   cpu->SetA(atoi(locate(p, A, "INITIAL_A")));
   cpu->SetX(atoi(locate(p, X, "INITIAL_X")));
   cpu->SetY(atoi(locate(p, Y, "INITIAL_Y")));
   cpu->SetP(atoi(locate(p, P, "INITIAL_P")));

   bzero(ram, sizeof(ram));
   const char *q = locate(p, RAM, "INITIAL_RAM");

   while (*q != '}') {
      int addr, val;
      if (*q == '[') {
         sscanf(q+1, "%d, %d", &addr, &val);
         ram[addr] = val;
      }
      q++;
   }

   free(copy);
}

bool is_jam(uint8_t val) {
   // Instruction codes: 02, 12, 22, 32, 42, 52, 62, 72, 92, B2, D2, F2
   if ((val & 0x0F) == 0x02) {
      if ((val & 0x80) == 0) {
         return true;
      }
      if ((val & 0x10) == 0x10) {
         return true;
      }
   }
   return false;
}

void handle_final(char *copy, bool jammed)
{
   char buf[1024];
   char *p = (char *) locate(copy, FINAL, "FINAL");
   uint8_t val;
   uint16_t pcval;

   reset_fail();

   if (!jammed && cpu->GetPC() != (pcval = /* ASSIGN */ atoi(locate(p, PC, "FINAL_PC")))) {
      sprintf(buf, "FAIL: PC %04x != %04x at line %d", cpu->GetPC(), pcval, linenum);
      fail(buf);
   }
   if (cpu->GetS() != (val = /* ASSIGN */ atoi(locate(p, S, "FINAL_S")))) {
      sprintf(buf, "FAIL: S %02x != %02x at line %d", cpu->GetS(), val, linenum);
      fail(buf);
   }
   if (cpu->GetA() != (val = /* ASSIGN */ atoi(locate(p, A, "FINAL_A")))) {
      sprintf(buf, "FAIL: A %02x != %02x at line %d", cpu->GetA(), val, linenum);
      fail(buf);
   }
   if (cpu->GetX() != (val = /* ASSIGN */ atoi(locate(p, X, "FINAL_X")))) {
      sprintf(buf, "FAIL: X %02x != %02x at line %d", cpu->GetX(), val, linenum);
      fail(buf);
   }
   if (cpu->GetY() != (val = /* ASSIGN */ atoi(locate(p, Y, "FINAL_Y")))) {
      sprintf(buf, "FAIL: Y %02x != %02x at line %d", cpu->GetY(), val, linenum);
      fail(buf);
   }
   if ((cpu->GetP() & PMASK) != (val = /* ASSIGN */ (atoi(locate(p, P, "FINAL_P")) & PMASK))) {
      sprintf(buf, "FAIL: P %02x != %02x at line %d", cpu->GetP(), val, linenum);
      fail(buf);
   }

   const char *q = locate(p, RAM, "FINAL_RAM");

   while (*q != '}') {
      int addr, val;
      if (*q == '[') {
         sscanf(q+1, "%d, %d", &addr, &val);
         if (ram[addr] != val) {
            sprintf(buf, "FAIL: RAM[%04x] %02x != %02x at line %d", addr, ram[addr], val, linenum);
            fail(buf);
         }
      }
      q++;
   }

   free(copy);

   //printf("pass\n");
}

void handle_line(const char *line)
{
   handle_name(strdup(line));
   handle_cycles(strdup(line));
   handle_initial(strdup(line));

   actual_cycles = 0;

   bool jammed = false;

   if (!is_jam(ram[cpu->GetPC()])) {
      cpu->Run(1, actual_cycles, mos6502::INST_COUNT);

      if (cycles != actual_cycles) {
         char buf[1024];
         sprintf(buf, "FAIL: actual %d != %d cycles at %d", (int) actual_cycles, (int) cycles, linenum);
         fail(buf);
      }
   }
   else {
      jammed = true;
   }
   
   handle_final(strdup(line), jammed);
}

void handle_json(const char *fname)
{
   const char *p = strstr(fname, ".json");
   if (p) {
      p -= 2;
      unsigned char x = strtoul(p, NULL, 16);
      if (strchr(unstable, x)) {
         is_unstable = true;
      }
   }

   linenum = 1;
   FILE *f = fopen(fname, "r");
   if (!f) {
      bail("could not open json file");
   }
   while (NULL != fgets(linebuf, sizeof(linebuf), f)) {
      translate();

      // we assume a lot here...

      p = linebuf;
      if (p[0] == '[') {
         p++;
      }

      switch(p[0]) {
         case 0:
         case 0x0A:
         case 0x0D:
         case '[':
         case ']':
            break;
         case '{':
            handle_line(p);
            break;
         default:
            {
               char buf[1024];
               sprintf(buf, "parse error at line %d, %02x", linenum, p[0]);
               bail(buf);
            }
            break;
      }
      linenum++;
   }
   fclose(f);
}

int main(int argc, char **argv) {
   if (argc != 2 && argc != 3) {
      fprintf(stderr, "Usage: %s <file>.json [quiet]\n", argv[0]);
      return -1;
   }

   if (argc == 3 && !strcmp(argv[2], "quiet")) {
      quiet = true;
   }

   cpu = new mos6502(readRam, writeRam, tick);
   cpu->Reset();

   handle_json(argv[1]);

   if (failures) {
      printf("%d %sfailure%s\n", failures, is_unstable ? "unstable " : "", failures > 1 ? "s" : "");
   }

   return (failures && !is_unstable) ? -1 : 0;
}
Download .txt
gitextract_gtzs_4yo/

├── .gitignore
├── LICENSE.txt
├── README.md
├── mos6502.cpp
├── mos6502.h
└── tests/
    ├── Makefile
    ├── functional/
    │   ├── .gitignore
    │   ├── Makefile
    │   ├── as65.sh
    │   ├── decimal.patch
    │   ├── main.cpp
    │   ├── notes.txt
    │   └── readme.md
    └── singlestep/
        ├── .gitignore
        ├── Makefile
        └── main.cpp
Download .txt
SYMBOL INDEX (23 symbols across 3 files)

FILE: mos6502.h
  function class (line 13) | class mos6502

FILE: tests/functional/main.cpp
  function writeRam (line 22) | void writeRam(uint16_t addr, uint8_t val)
  function readRam (line 34) | uint8_t readRam(uint16_t addr)
  function tick (line 39) | void tick(mos6502*)
  function bail (line 85) | void bail(const char *s)
  function fetch (line 91) | uint32_t fetch(const char *s, uint16_t offset, uint8_t count)
  function handle_hex (line 111) | void handle_hex(const char *fname)
  function main (line 143) | int main(int argc, char **argv) {

FILE: tests/singlestep/main.cpp
  function writeRam (line 42) | void writeRam(uint16_t addr, uint8_t val)
  function readRam (line 47) | uint8_t readRam(uint16_t addr)
  function tick (line 52) | void tick(mos6502*)
  function translate (line 56) | void translate(void)
  function reset_fail (line 117) | void reset_fail(void) {
  function fail (line 121) | void fail(const char *s)
  function bail (line 132) | void bail(const char *s)
  function handle_name (line 153) | void handle_name(char *copy)
  function handle_cycles (line 173) | void handle_cycles(char *copy)
  function handle_initial (line 196) | void handle_initial(char *copy)
  function is_jam (line 222) | bool is_jam(uint8_t val) {
  function handle_final (line 235) | void handle_final(char *copy, bool jammed)
  function handle_line (line 288) | void handle_line(const char *line)
  function handle_json (line 314) | void handle_json(const char *fname)
  function main (line 363) | int main(int argc, char **argv) {
Condensed preview — 16 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (103K chars).
[
  {
    "path": ".gitignore",
    "chars": 3,
    "preview": "*.o"
  },
  {
    "path": "LICENSE.txt",
    "chars": 1074,
    "preview": "MIT License\n\nCopyright (c) 2017 Gianluca Ghettini\n\nPermission is hereby granted, free of charge, to any person obtaining"
  },
  {
    "path": "README.md",
    "chars": 5418,
    "preview": "## MOS6502 Emulator in C++\n\nThis is my C++ implementation of the MOS Technology 6502 CPU. The code is written to be more"
  },
  {
    "path": "mos6502.cpp",
    "chars": 66040,
    "preview": "#include \"mos6502.h\"\n\n#define NEGATIVE  0x80\n#define OVERFLOW  0x40\n#define CONSTANT  0x20\n#define BREAK     0x10\n#defin"
  },
  {
    "path": "mos6502.h",
    "chars": 7153,
    "preview": "//============================================================================\n// Name        : mos6502\n// Author      :"
  },
  {
    "path": "tests/Makefile",
    "chars": 176,
    "preview": "all:\n\t( cd functional && make )\n\t( cd singlestep && make )\n\t@echo ===============================\n\t@echo === ALL TESTS C"
  },
  {
    "path": "tests/functional/.gitignore",
    "chars": 51,
    "preview": "*.hex\n*.lst\n*.a65\nmain\n6502_65C02_functional_tests\n"
  },
  {
    "path": "tests/functional/Makefile",
    "chars": 1813,
    "preview": "# Makefile to run unit tests\n\n# Fail early if required tools are missing\nifeq (, $(shell which git 2>/dev/null))\n$(error"
  },
  {
    "path": "tests/functional/as65.sh",
    "chars": 2754,
    "preview": "#!/bin/bash\nset -euo pipefail\n\nsrc_file=\"$1\"\nsrc_dir=$(dirname \"$(realpath \"$src_file\")\")\nbase_name=$(basename \"${src_fi"
  },
  {
    "path": "tests/functional/decimal.patch",
    "chars": 365,
    "preview": "--- 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   "
  },
  {
    "path": "tests/functional/main.cpp",
    "chars": 3690,
    "preview": "// compile with \"g++ main.cpp ../../mos6502.cpp -o main\"\n\n#include \"../../mos6502.h\"\n\n#include <stdlib.h>\n#include <stri"
  },
  {
    "path": "tests/functional/notes.txt",
    "chars": 122,
    "preview": "assembler originally came from https://www.kingswood-consulting.co.uk/assemblers/\nauthor does NOT distribute source code"
  },
  {
    "path": "tests/functional/readme.md",
    "chars": 17,
    "preview": "just type \"make\"\n"
  },
  {
    "path": "tests/singlestep/.gitignore",
    "chars": 11,
    "preview": "65x02\nmain\n"
  },
  {
    "path": "tests/singlestep/Makefile",
    "chars": 874,
    "preview": "# Makefile to run unit tests\n\n# Fail early if required tools are missing\nifeq (, $(shell which git 2>/dev/null))\n$(error"
  },
  {
    "path": "tests/singlestep/main.cpp",
    "chars": 8133,
    "preview": "// compile with \"g++ main.cpp ../../mos6502.cpp -o main\"\n\n#include \"../../mos6502.h\"\n\n#include <stdlib.h>\n#include <stri"
  }
]

About this extraction

This page contains the full source code of the gianlucag/mos6502 GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 16 files (95.4 KB), approximately 32.8k tokens, and a symbol index with 23 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!