1
0
mirror of https://github.com/mre/mos6502.git synced 2026-04-20 21:16:48 +00:00

Add support for illegal opcodes (#126)

This adds support for illegal opcodes:
lax, dcp, isc, slo, rla, sre, rra, arr, sbx, las, usbc, jam, nops, sax, xaa, alr, anc

The illegal opcodes implementation caused README doc tests to hang:

1. First example used 0xff as program terminator
   - Previously 0xff was unimplemented (returned None), stopping execution
   - Now 0xff is ISC (INC+SBC) AbsoluteX, causing infinite loop
   - Fix: Use 0x02 (JAM) which explicitly halts the CPU

2. Second example loads euclid.bin which uses BRK (0x00) to terminate
   - BRK jumps to IRQ vector at $FFFE/$FFFF
   - Uninitialized memory contains 0x00, jumping to address $0000
   - This causes infinite loop executing whatever is in low memory
   - Fix: Mark as no_run since it depends on external file anyway

- fix: make euclid.bin example runnable as doc test
- Replace BRK with JAM ($02) to halt CPU after illegal opcodes impl
- Fix assembly label structure (algo/algo_ were dead infinite loops)
- Simplify linker.cfg to output raw binary at $0010 (was 64KB image)
- Enable README doc test (remove no_run marker)

The old linker config produced a 64KB memory image with code at $0400,
but README loaded at $0010 causing address mismatch. New config outputs
only the code segment at the correct load address.

Co-authored-by: mlund <mlund@localhost>
This commit is contained in:
omarandlorraine
2026-02-06 15:19:01 +00:00
committed by GitHub
parent 3d4af71c8a
commit c91d604efe
7 changed files with 832 additions and 160 deletions
+1 -1
View File
@@ -30,7 +30,7 @@
name = "mos6502"
description = "A MOS 6502 Emulator"
license = "BSD-3-Clause"
version = "0.6.2"
version = "0.6.3"
authors = ["The 6502-rs Developers"]
exclude = ["examples/**"]
edition = "2024"
+1 -1
View File
@@ -49,7 +49,7 @@ fn main() {
0x4c, 0x12, 0x00, // Jump to .algo_
// .end
0xa5, 0x00, // Load from S to A
0xff,
0x02, // JAM - halt CPU to end program
// .swap
0xa6, 0x00, // load F to X
0xa4, 0x01, // load S to Y
Binary file not shown.
+19 -27
View File
@@ -1,33 +1,25 @@
; euclid.ca65
; A program to find the greatest common divisor of two numbers
.ORG $1000
; .algo
LDA $00 ; Load from F to A
; .algo_
SEC ; Set carry flag
SBC $01 ; Subtract S from the number in A (from F)
BEQ end ; Jump to .end if the difference is zero
BMI swap ; Jump to .swap if the difference is negative
STA $00 ; Load A to F
JMP algo_ ; Jump to .algo_
; .end
end:
LDA $00 ; Load from F to A
BRK ; Break (end program)
; .swap
swap:
LDX $00 ; Load F to X
LDY $01 ; Load S to Y
STX $01 ; Store X to S
STY $00 ; Store Y to F
JMP algo ; Jump to .algo
.segment "CODE"
algo:
JMP algo ; Infinite loop to prevent program from ending
LDA $00 ; Load from F to A
algo_:
JMP algo_ ; Infinite loop to prevent program from ending
SEC ; Set carry flag
SBC $01 ; Subtract S from A
BEQ end ; Jump to end if zero
BMI swap ; Jump to swap if negative
STA $00 ; Store A to F
JMP algo_ ; Continue algorithm
end:
LDA $00 ; Load result to A
.byte $02 ; JAM - halt CPU
swap:
LDX $00 ; Load F to X
LDY $01 ; Load S to Y
STX $01 ; Store X to S
STY $00 ; Store Y to F
JMP algo ; Restart algorithm
+11 -25
View File
@@ -1,32 +1,18 @@
# Linker configuration for cc65 (ld65)
# Defines memory layout and segment placement for 6502 programs
# Outputs raw binary containing only the code segment
MEMORY {
# RAM: 32KB from $0000-$7FFF
# Used for zero page, stack, data, and code in this emulator config
RAM: start = $0000, size=$8000, type = rw, fill = yes, fillval = $FF, file = %O;
# ROM: Almost 32KB from $8000-$FFF9
# Defined but not used in examples (code runs from RAM instead)
ROM: start = $8000, size=$7FFA, type = ro, fill = yes, fillval = $FF, file = %O;
# ROM_VECTORS: 6 bytes at $FFFA-$FFFF
# Holds the three 16-bit interrupt vectors: NMI, RESET, IRQ
ROM_VECTORS: start = $FFFA, size=6, type = ro, fill = yes, fillval = $FF, file = %O;
# Code region starting at $0010, matching README examples
CODE: start = $0010, size = $1000, type = rw, file = %O;
}
SEGMENTS {
# Zero page ($00-$FF): Fast access variables
ZEROPAGE: load=RAM, type=rw;
# Code segment: Contains the program
CODE: load = CODE, type = ro;
# Data segment: Starts at $0200 (page 2)
# Page 1 ($0100-$01FF) is reserved for the stack
DATA: load=RAM, type=rw, offset=$0200;
# Code segment: Starts at $0400 (page 4)
# Leaves room for zero page, stack, and data
CODE: load=RAM, type=rw, offset=$0400;
# Interrupt vectors at top of memory
VECTORS: load=ROM_VECTORS, type=ro;
}
# Optional segments
ZEROPAGE: load = CODE, type = rw, optional = yes;
DATA: load = CODE, type = rw, optional = yes;
RODATA: load = CODE, type = rw, optional = yes;
BSS: load = CODE, type = rw, optional = yes;
}
+536 -1
View File
@@ -818,7 +818,10 @@ impl<M: Bus, V: Variant> CPU<M, V> {
(Instruction::SEI, OpInput::UseImplied) => {
self.set_flag(Status::PS_DISABLE_INTERRUPTS);
}
(Instruction::SAX, OpInput::UseAddress { address: addr, .. }) => {
self.memory
.set_byte(addr, self.registers.accumulator & self.registers.index_x);
}
(Instruction::STA, OpInput::UseAddress { address: addr, .. }) => {
self.memory.set_byte(addr, self.registers.accumulator);
}
@@ -891,6 +894,10 @@ impl<M: Bus, V: Variant> CPU<M, V> {
let val = self.registers.index_y;
self.load_accumulator(val);
}
(Instruction::XAA, OpInput::UseImmediate(val)) => {
self.load_accumulator(self.registers.index_x);
self.and(val);
}
(Instruction::WAI, OpInput::UseImplied) => {
// Wait for Interrupt (65C02)
@@ -909,6 +916,151 @@ impl<M: Bus, V: Variant> CPU<M, V> {
(Instruction::NOP, OpInput::UseImplied) => {
log::debug!("NOP instruction");
}
(Instruction::ALR, OpInput::UseImmediate(val)) => {
self.and(val);
let mut val = self.registers.accumulator;
CPU::<M, V>::shift_right_with_flags(&mut val, &mut self.registers.status);
self.registers.accumulator = val;
}
(Instruction::ANC, OpInput::UseImmediate(val)) => {
self.and(val);
if self.registers.accumulator & 0x80 != 0 {
self.set_flag(Status::PS_CARRY);
} else {
self.unset_flag(Status::PS_CARRY);
}
}
// ARR - AND with immediate, then ROR with special flag handling.
// Unlike normal ROR, ARR sets C and V flags based on result bits,
// not from the shift operation. This quirk stems from how the 6502's
// internal buses interact during this undocumented instruction.
(Instruction::ARR, OpInput::UseImmediate(val)) => {
self.and(val);
let a = self.registers.accumulator;
let carry = self.registers.status.contains(Status::PS_CARRY);
// ROR the accumulator
let result = (a >> 1) | (if carry { 0x80 } else { 0 });
self.registers.accumulator = result;
// Set N and Z flags from result
CPU::<M, V>::set_flags_from_u8(&mut self.registers.status, result);
// Carry is set from bit 6 (not bit 0 as in normal ROR)
if result & 0x40 != 0 {
self.set_flag(Status::PS_CARRY);
} else {
self.unset_flag(Status::PS_CARRY);
}
// Overflow is set when bits 6 and 5 differ. This unusual behavior
// detects a "sign change" between the two highest result bits,
// useful for BCD fixup in some algorithms.
if ((result >> 6) ^ (result >> 5)) & 1 != 0 {
self.set_flag(Status::PS_OVERFLOW);
} else {
self.unset_flag(Status::PS_OVERFLOW);
}
}
// DCP - Decrement memory, then compare with accumulator
(Instruction::DCP, OpInput::UseAddress { address: addr, .. }) => {
let val = self.memory.get_byte(addr).wrapping_sub(1);
self.memory.set_byte(addr, val);
self.compare_with_a_register(val);
}
// ISC - Increment memory, then SBC from accumulator
(Instruction::ISC, OpInput::UseAddress { address: addr, .. }) => {
let val = self.memory.get_byte(addr).wrapping_add(1);
self.memory.set_byte(addr, val);
self.subtract_with_carry(val);
}
// JAM - Halt the CPU (requires reset)
(Instruction::JAM, OpInput::UseImplied) => {
self.halted = true;
}
// LAS - AND memory with SP, load to A, X, SP
(Instruction::LAS, OpInput::UseAddress { address: addr, .. }) => {
let val = self.memory.get_byte(addr) & self.registers.stack_pointer.0;
self.registers.accumulator = val;
self.registers.index_x = val;
self.registers.stack_pointer = StackPointer(val);
CPU::<M, V>::set_flags_from_u8(&mut self.registers.status, val);
}
// LAX - Load A and X with the same value from memory
(Instruction::LAX, OpInput::UseAddress { address: addr, .. }) => {
let val = self.memory.get_byte(addr);
self.load_accumulator(val);
self.load_x_register(val);
}
// NOP variants - read memory but do nothing
(Instruction::NOPI, OpInput::UseImmediate(_)) => {}
(Instruction::NOPZ, OpInput::UseAddress { .. }) => {}
(Instruction::NOPZX, OpInput::UseAddress { .. }) => {}
(Instruction::NOPA, OpInput::UseAddress { .. }) => {}
(Instruction::NOPAX, OpInput::UseAddress { .. }) => {}
// RLA - Rotate left memory, then AND with accumulator
(Instruction::RLA, OpInput::UseAddress { address: addr, .. }) => {
let mut val = self.memory.get_byte(addr);
CPU::<M, V>::rotate_left_with_flags(&mut val, &mut self.registers.status);
self.memory.set_byte(addr, val);
self.and(val);
}
// RRA - Rotate right memory, then ADC with accumulator
(Instruction::RRA, OpInput::UseAddress { address: addr, .. }) => {
let mut val = self.memory.get_byte(addr);
CPU::<M, V>::rotate_right_with_flags(&mut val, &mut self.registers.status);
self.memory.set_byte(addr, val);
self.add_with_carry(val);
}
// SBX - (A AND X) - immediate -> X, flags like CMP
(Instruction::SBX, OpInput::UseImmediate(val)) => {
let ax = self.registers.accumulator & self.registers.index_x;
let result = ax.wrapping_sub(val);
self.registers.index_x = result;
// Set carry if no borrow (ax >= val)
if ax >= val {
self.set_flag(Status::PS_CARRY);
} else {
self.unset_flag(Status::PS_CARRY);
}
CPU::<M, V>::set_flags_from_u8(&mut self.registers.status, result);
}
// SLO - Shift left memory, then OR with accumulator
(Instruction::SLO, OpInput::UseAddress { address: addr, .. }) => {
let mut val = self.memory.get_byte(addr);
CPU::<M, V>::shift_left_with_flags(&mut val, &mut self.registers.status);
self.memory.set_byte(addr, val);
self.inclusive_or(val);
}
// SRE - Shift right memory, then EOR with accumulator
(Instruction::SRE, OpInput::UseAddress { address: addr, .. }) => {
let mut val = self.memory.get_byte(addr);
CPU::<M, V>::shift_right_with_flags(&mut val, &mut self.registers.status);
self.memory.set_byte(addr, val);
self.exclusive_or(val);
}
// USBC - Same as SBC immediate
(Instruction::USBC, OpInput::UseImmediate(val)) => {
self.subtract_with_carry(val);
}
(_, _) => {
log::debug!(
"attempting to execute unimplemented or invalid \
@@ -2733,6 +2885,389 @@ mod tests {
cpu.single_step(); // Execute LDA
assert_eq!(cpu.registers.accumulator, 0x99);
}
// ==================== Illegal Opcode Tests ====================
/// Execute instruction with zero-page addressing
macro_rules! exec_zp {
($cpu:expr, $instr:ident, $addr:expr) => {
$cpu.execute_instruction((
Instruction::$instr,
AddressingMode::ZeroPage,
OpInput::UseAddress {
address: $addr,
page_crossed: false,
},
))
};
}
/// Execute instruction with immediate addressing
macro_rules! exec_imm {
($cpu:expr, $instr:ident, $val:expr) => {
$cpu.execute_instruction((
Instruction::$instr,
AddressingMode::Immediate,
OpInput::UseImmediate($val),
))
};
}
/// Execute instruction with implied addressing
macro_rules! exec_impl {
($cpu:expr, $instr:ident) => {
$cpu.execute_instruction((
Instruction::$instr,
AddressingMode::Implied,
OpInput::UseImplied,
))
};
}
/// Execute instruction with absolute,Y addressing
macro_rules! exec_aby {
($cpu:expr, $instr:ident, $addr:expr) => {
$cpu.execute_instruction((
Instruction::$instr,
AddressingMode::AbsoluteY,
OpInput::UseAddress {
address: $addr,
page_crossed: false,
},
))
};
}
#[test]
fn lax_test() {
let mut cpu = CPU::new(Ram::new(), Nmos6502);
cpu.memory.set_byte(0x42, 0x55);
exec_zp!(cpu, LAX, 0x42);
assert_eq!(cpu.registers.accumulator, 0x55);
assert_eq!(cpu.registers.index_x, 0x55);
assert!(!cpu.registers.status.contains(Status::PS_ZERO));
assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE));
cpu.memory.set_byte(0x10, 0x00);
exec_zp!(cpu, LAX, 0x10);
assert_eq!(cpu.registers.accumulator, 0x00);
assert_eq!(cpu.registers.index_x, 0x00);
assert!(cpu.registers.status.contains(Status::PS_ZERO));
cpu.memory.set_byte(0x20, 0x80);
exec_zp!(cpu, LAX, 0x20);
assert_eq!(cpu.registers.accumulator, 0x80);
assert_eq!(cpu.registers.index_x, 0x80);
assert!(cpu.registers.status.contains(Status::PS_NEGATIVE));
}
#[test]
fn sax_test() {
let mut cpu = CPU::new(Ram::new(), Nmos6502);
cpu.registers.accumulator = 0xFF;
cpu.registers.index_x = 0x0F;
exec_zp!(cpu, SAX, 0x42);
assert_eq!(cpu.memory.get_byte(0x42), 0x0F);
cpu.registers.accumulator = 0xAA;
cpu.registers.index_x = 0x55;
exec_zp!(cpu, SAX, 0x43);
assert_eq!(cpu.memory.get_byte(0x43), 0x00);
}
#[test]
fn dcp_test() {
let mut cpu = CPU::new(Ram::new(), Nmos6502);
cpu.memory.set_byte(0x42, 0x10);
cpu.registers.accumulator = 0x0F;
exec_zp!(cpu, DCP, 0x42);
assert_eq!(cpu.memory.get_byte(0x42), 0x0F);
assert!(cpu.registers.status.contains(Status::PS_ZERO));
assert!(cpu.registers.status.contains(Status::PS_CARRY));
cpu.memory.set_byte(0x43, 0x05);
cpu.registers.accumulator = 0x10;
exec_zp!(cpu, DCP, 0x43);
assert_eq!(cpu.memory.get_byte(0x43), 0x04);
assert!(!cpu.registers.status.contains(Status::PS_ZERO));
assert!(cpu.registers.status.contains(Status::PS_CARRY));
cpu.memory.set_byte(0x44, 0x00);
cpu.registers.accumulator = 0x00;
exec_zp!(cpu, DCP, 0x44);
assert_eq!(cpu.memory.get_byte(0x44), 0xFF);
assert!(!cpu.registers.status.contains(Status::PS_CARRY));
}
#[test]
fn isc_test() {
let mut cpu = CPU::new(Ram::new(), Nmos6502);
cpu.registers.status.remove(Status::PS_DECIMAL_MODE);
cpu.memory.set_byte(0x42, 0x09);
cpu.registers.accumulator = 0x20;
cpu.registers.status.insert(Status::PS_CARRY);
exec_zp!(cpu, ISC, 0x42);
assert_eq!(cpu.memory.get_byte(0x42), 0x0A);
assert_eq!(cpu.registers.accumulator, 0x16);
assert!(cpu.registers.status.contains(Status::PS_CARRY));
cpu.memory.set_byte(0x43, 0xFF);
cpu.registers.accumulator = 0x10;
cpu.registers.status.insert(Status::PS_CARRY);
exec_zp!(cpu, ISC, 0x43);
assert_eq!(cpu.memory.get_byte(0x43), 0x00);
assert_eq!(cpu.registers.accumulator, 0x10);
}
#[test]
fn slo_test() {
let mut cpu = CPU::new(Ram::new(), Nmos6502);
cpu.memory.set_byte(0x42, 0x40);
cpu.registers.accumulator = 0x01;
exec_zp!(cpu, SLO, 0x42);
assert_eq!(cpu.memory.get_byte(0x42), 0x80);
assert_eq!(cpu.registers.accumulator, 0x81);
assert!(!cpu.registers.status.contains(Status::PS_CARRY));
assert!(cpu.registers.status.contains(Status::PS_NEGATIVE));
cpu.memory.set_byte(0x43, 0x80);
cpu.registers.accumulator = 0x00;
exec_zp!(cpu, SLO, 0x43);
assert_eq!(cpu.memory.get_byte(0x43), 0x00);
assert_eq!(cpu.registers.accumulator, 0x00);
assert!(cpu.registers.status.contains(Status::PS_CARRY));
assert!(cpu.registers.status.contains(Status::PS_ZERO));
}
#[test]
fn rla_test() {
let mut cpu = CPU::new(Ram::new(), Nmos6502);
cpu.registers.status.remove(Status::PS_CARRY);
cpu.memory.set_byte(0x42, 0x40);
cpu.registers.accumulator = 0xFF;
exec_zp!(cpu, RLA, 0x42);
assert_eq!(cpu.memory.get_byte(0x42), 0x80);
assert_eq!(cpu.registers.accumulator, 0x80);
assert!(!cpu.registers.status.contains(Status::PS_CARRY));
cpu.registers.status.insert(Status::PS_CARRY);
cpu.memory.set_byte(0x43, 0x40);
cpu.registers.accumulator = 0xFF;
exec_zp!(cpu, RLA, 0x43);
assert_eq!(cpu.memory.get_byte(0x43), 0x81);
assert_eq!(cpu.registers.accumulator, 0x81);
}
#[test]
fn sre_test() {
let mut cpu = CPU::new(Ram::new(), Nmos6502);
cpu.memory.set_byte(0x42, 0x02);
cpu.registers.accumulator = 0xFF;
exec_zp!(cpu, SRE, 0x42);
assert_eq!(cpu.memory.get_byte(0x42), 0x01);
assert_eq!(cpu.registers.accumulator, 0xFE);
assert!(!cpu.registers.status.contains(Status::PS_CARRY));
cpu.memory.set_byte(0x43, 0x01);
cpu.registers.accumulator = 0x00;
exec_zp!(cpu, SRE, 0x43);
assert_eq!(cpu.memory.get_byte(0x43), 0x00);
assert_eq!(cpu.registers.accumulator, 0x00);
assert!(cpu.registers.status.contains(Status::PS_CARRY));
}
#[test]
fn rra_test() {
let mut cpu = CPU::new(Ram::new(), Nmos6502);
cpu.registers.status.remove(Status::PS_DECIMAL_MODE);
cpu.registers.status.remove(Status::PS_CARRY);
cpu.memory.set_byte(0x42, 0x02);
cpu.registers.accumulator = 0x10;
exec_zp!(cpu, RRA, 0x42);
assert_eq!(cpu.memory.get_byte(0x42), 0x01);
assert_eq!(cpu.registers.accumulator, 0x11);
cpu.registers.status.insert(Status::PS_CARRY);
cpu.memory.set_byte(0x43, 0x02);
cpu.registers.accumulator = 0x00;
exec_zp!(cpu, RRA, 0x43);
assert_eq!(cpu.memory.get_byte(0x43), 0x81);
assert_eq!(cpu.registers.accumulator, 0x81);
}
#[test]
fn arr_test() {
let mut cpu = CPU::new(Ram::new(), Nmos6502);
cpu.registers.accumulator = 0xFF;
cpu.registers.status.remove(Status::PS_CARRY);
exec_imm!(cpu, ARR, 0x55);
assert_eq!(cpu.registers.accumulator, 0x2A);
assert!(!cpu.registers.status.contains(Status::PS_CARRY));
cpu.registers.accumulator = 0xFF;
cpu.registers.status.insert(Status::PS_CARRY);
exec_imm!(cpu, ARR, 0x55);
assert_eq!(cpu.registers.accumulator, 0xAA);
assert!(!cpu.registers.status.contains(Status::PS_CARRY));
}
#[test]
fn sbx_test() {
let mut cpu = CPU::new(Ram::new(), Nmos6502);
cpu.registers.accumulator = 0xFF;
cpu.registers.index_x = 0x0F;
exec_imm!(cpu, SBX, 0x05);
assert_eq!(cpu.registers.index_x, 0x0A);
assert!(cpu.registers.status.contains(Status::PS_CARRY));
cpu.registers.accumulator = 0xFF;
cpu.registers.index_x = 0x0F;
exec_imm!(cpu, SBX, 0x10);
assert_eq!(cpu.registers.index_x, 0xFF);
assert!(!cpu.registers.status.contains(Status::PS_CARRY));
assert!(cpu.registers.status.contains(Status::PS_NEGATIVE));
}
#[test]
fn alr_test() {
let mut cpu = CPU::new(Ram::new(), Nmos6502);
cpu.registers.accumulator = 0xFF;
exec_imm!(cpu, ALR, 0xAA);
assert_eq!(cpu.registers.accumulator, 0x55);
assert!(!cpu.registers.status.contains(Status::PS_CARRY));
cpu.registers.accumulator = 0xFF;
exec_imm!(cpu, ALR, 0x55);
assert_eq!(cpu.registers.accumulator, 0x2A);
assert!(cpu.registers.status.contains(Status::PS_CARRY));
}
#[test]
fn anc_test() {
let mut cpu = CPU::new(Ram::new(), Nmos6502);
cpu.registers.accumulator = 0xFF;
exec_imm!(cpu, ANC, 0x80);
assert_eq!(cpu.registers.accumulator, 0x80);
assert!(cpu.registers.status.contains(Status::PS_CARRY));
assert!(cpu.registers.status.contains(Status::PS_NEGATIVE));
cpu.registers.accumulator = 0xFF;
exec_imm!(cpu, ANC, 0x7F);
assert_eq!(cpu.registers.accumulator, 0x7F);
assert!(!cpu.registers.status.contains(Status::PS_CARRY));
assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE));
}
#[test]
fn xaa_test() {
let mut cpu = CPU::new(Ram::new(), Nmos6502);
// XAA transfers X to A, then ANDs with immediate
cpu.registers.index_x = 0xFF;
cpu.registers.accumulator = 0x00;
exec_imm!(cpu, XAA, 0x0F);
assert_eq!(cpu.registers.accumulator, 0x0F);
assert!(!cpu.registers.status.contains(Status::PS_ZERO));
assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE));
cpu.registers.index_x = 0xAA;
exec_imm!(cpu, XAA, 0xF0);
assert_eq!(cpu.registers.accumulator, 0xA0);
assert!(cpu.registers.status.contains(Status::PS_NEGATIVE));
cpu.registers.index_x = 0x55;
exec_imm!(cpu, XAA, 0xAA);
assert_eq!(cpu.registers.accumulator, 0x00);
assert!(cpu.registers.status.contains(Status::PS_ZERO));
}
#[test]
fn las_test() {
let mut cpu = CPU::new(Ram::new(), Nmos6502);
cpu.registers.stack_pointer = StackPointer(0xFF);
cpu.memory.set_byte(0x1000, 0x0F);
exec_aby!(cpu, LAS, 0x1000);
assert_eq!(cpu.registers.accumulator, 0x0F);
assert_eq!(cpu.registers.index_x, 0x0F);
assert_eq!(cpu.registers.stack_pointer.0, 0x0F);
assert!(!cpu.registers.status.contains(Status::PS_ZERO));
assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE));
}
#[test]
fn usbc_test() {
let mut cpu = CPU::new(Ram::new(), Nmos6502);
cpu.registers.status.remove(Status::PS_DECIMAL_MODE);
cpu.registers.accumulator = 0x20;
cpu.registers.status.insert(Status::PS_CARRY);
exec_imm!(cpu, USBC, 0x10);
assert_eq!(cpu.registers.accumulator, 0x10);
assert!(cpu.registers.status.contains(Status::PS_CARRY));
}
#[test]
fn jam_test() {
let mut cpu = CPU::new(Ram::new(), Nmos6502);
assert!(!cpu.halted);
exec_impl!(cpu, JAM);
assert!(cpu.halted);
assert!(!cpu.single_step());
}
#[test]
fn nop_variants_test() {
let mut cpu = CPU::new(Ram::new(), Nmos6502);
let initial_a = 0x42;
let initial_x = 0x13;
let initial_y = 0x37;
cpu.registers.accumulator = initial_a;
cpu.registers.index_x = initial_x;
cpu.registers.index_y = initial_y;
exec_imm!(cpu, NOPI, 0xFF);
assert_eq!(cpu.registers.accumulator, initial_a);
assert_eq!(cpu.registers.index_x, initial_x);
assert_eq!(cpu.registers.index_y, initial_y);
exec_zp!(cpu, NOPZ, 0x42);
assert_eq!(cpu.registers.accumulator, initial_a);
cpu.execute_instruction((
Instruction::NOPA,
AddressingMode::Absolute,
OpInput::UseAddress {
address: 0x1234,
page_crossed: false,
},
));
assert_eq!(cpu.registers.accumulator, initial_a);
cpu.execute_instruction((
Instruction::NOPAX,
AddressingMode::AbsoluteX,
OpInput::UseAddress {
address: 0x1234,
page_crossed: false,
},
));
assert_eq!(cpu.registers.accumulator, initial_a);
}
}
#[cfg(test)]
+264 -105
View File
@@ -208,6 +208,9 @@ pub enum Instruction {
// STore Zero
STZ,
// STore A & X
SAX,
// Transfer Accumulator to X
TAX,
@@ -237,6 +240,63 @@ pub enum Instruction {
// SToP processor (65C02 only)
STP,
// XAA, (transfer X to A, and then ANDs the accumulator with an immediate value)
XAA,
// ALR, (ANDs the accumulator with an immediate value, and then does LSR)
ALR,
// ANC, (ANDs the accumulator and then copies bit 7 into carry flag)
ANC,
// ARR, (ANDs the accumulator with an immediate value, then RORs with special flag handling)
ARR,
// DCP (DCM), (Decrements memory, then compares with accumulator)
DCP,
// ISC (ISB/INS), (Increments memory, then SBCs from accumulator)
ISC,
// JAM (KIL/HLT), (Halts the CPU - requires reset)
JAM,
// LAS (LAR), (ANDs memory with SP, loads result into A, X, and SP)
LAS,
// LAX, (Loads both A and X with the same value from memory)
LAX,
// NOP with addressing modes (reads memory but does nothing)
// Immediate NOP
NOPI,
// Zero Page NOP
NOPZ,
// Zero Page,X NOP
NOPZX,
// Absolute NOP
NOPA,
// Absolute,X NOP
NOPAX,
// RLA, (Rotates memory left, then ANDs with accumulator)
RLA,
// RRA, (Rotates memory right, then ADCs with accumulator)
RRA,
// SBX (AXS), ((A AND X) - immediate -> X, sets flags like CMP)
SBX,
// SLO (ASO), (Shifts memory left, then ORs with accumulator)
SLO,
// SRE (LSE), (Shifts memory right, then EORs with accumulator)
SRE,
// USBC, (Same as SBC immediate)
USBC,
}
impl Instruction {
@@ -491,6 +551,105 @@ impl Instruction {
// WAI - Wait for Interrupt (65C02 only, 3 cycles before waiting)
(WAI, Implied) => 3,
// Illegal opcodes - SAX (Store A AND X)
(SAX, ZeroPage) => 3,
(SAX, ZeroPageY) => 4,
(SAX, Absolute) => 4,
(SAX, IndexedIndirectX) => 6,
// Illegal opcodes - XAA (Transfer X to A, then AND)
(XAA, Immediate) => 2,
// Illegal opcodes - ALR (AND then LSR)
(ALR, Immediate) => 2,
// Illegal opcodes - ANC (AND then copy bit 7 to carry)
(ANC, Immediate) => 2,
// Illegal opcodes - ARR (AND then ROR with special flags)
(ARR, Immediate) => 2,
// Illegal opcodes - DCP (Decrement then Compare)
(DCP, ZeroPage) => 5,
(DCP, ZeroPageX) => 6,
(DCP, Absolute) => 6,
(DCP, AbsoluteX) => 7,
(DCP, AbsoluteY) => 7,
(DCP, IndexedIndirectX) => 8,
(DCP, IndirectIndexedY) => 8,
// Illegal opcodes - ISC (Increment then SBC)
(ISC, ZeroPage) => 5,
(ISC, ZeroPageX) => 6,
(ISC, Absolute) => 6,
(ISC, AbsoluteX) => 7,
(ISC, AbsoluteY) => 7,
(ISC, IndexedIndirectX) => 8,
(ISC, IndirectIndexedY) => 8,
// Illegal opcodes - JAM (Halt CPU)
(JAM, Implied) => 2,
// Illegal opcodes - LAS (AND with SP, load to A, X, SP)
(LAS, AbsoluteY) => 4, // +1 if page crossed
// Illegal opcodes - LAX (Load A and X)
(LAX, ZeroPage) => 3,
(LAX, ZeroPageY) => 4,
(LAX, Absolute) => 4,
(LAX, AbsoluteY) => 4, // +1 if page crossed
(LAX, IndexedIndirectX) => 6,
(LAX, IndirectIndexedY) => 5, // +1 if page crossed
// Illegal opcodes - NOP variants
(NOPI, Immediate) => 2,
(NOPZ, ZeroPage) => 3,
(NOPZX, ZeroPageX) => 4,
(NOPA, Absolute) => 4,
(NOPAX, AbsoluteX) => 4, // +1 if page crossed
// Illegal opcodes - RLA (Rotate Left then AND)
(RLA, ZeroPage) => 5,
(RLA, ZeroPageX) => 6,
(RLA, Absolute) => 6,
(RLA, AbsoluteX) => 7,
(RLA, AbsoluteY) => 7,
(RLA, IndexedIndirectX) => 8,
(RLA, IndirectIndexedY) => 8,
// Illegal opcodes - RRA (Rotate Right then ADC)
(RRA, ZeroPage) => 5,
(RRA, ZeroPageX) => 6,
(RRA, Absolute) => 6,
(RRA, AbsoluteX) => 7,
(RRA, AbsoluteY) => 7,
(RRA, IndexedIndirectX) => 8,
(RRA, IndirectIndexedY) => 8,
// Illegal opcodes - SBX (A AND X - immediate -> X)
(SBX, Immediate) => 2,
// Illegal opcodes - SLO (Shift Left then OR)
(SLO, ZeroPage) => 5,
(SLO, ZeroPageX) => 6,
(SLO, Absolute) => 6,
(SLO, AbsoluteX) => 7,
(SLO, AbsoluteY) => 7,
(SLO, IndexedIndirectX) => 8,
(SLO, IndirectIndexedY) => 8,
// Illegal opcodes - SRE (Shift Right then EOR)
(SRE, ZeroPage) => 5,
(SRE, ZeroPageX) => 6,
(SRE, Absolute) => 6,
(SRE, AbsoluteX) => 7,
(SRE, AbsoluteY) => 7,
(SRE, IndexedIndirectX) => 8,
(SRE, IndirectIndexedY) => 8,
// Illegal opcodes - USBC (Same as SBC)
(USBC, Immediate) => 2,
// Invalid combinations cause a panic to indicate a bug in the decoder
_ => unreachable!("undecoded instruction"),
}
@@ -618,260 +777,260 @@ impl crate::Variant for Nmos6502 {
match opcode {
0x00 => Some((Instruction::BRK, AddressingMode::Implied)),
0x01 => Some((Instruction::ORA, AddressingMode::IndexedIndirectX)),
0x02 => None,
0x03 => None,
0x04 => None,
0x02 => Some((Instruction::JAM, AddressingMode::Implied)),
0x03 => Some((Instruction::SLO, AddressingMode::IndexedIndirectX)),
0x04 => Some((Instruction::NOPZ, AddressingMode::ZeroPage)),
0x05 => Some((Instruction::ORA, AddressingMode::ZeroPage)),
0x06 => Some((Instruction::ASL, AddressingMode::ZeroPage)),
0x07 => None,
0x07 => Some((Instruction::SLO, AddressingMode::ZeroPage)),
0x08 => Some((Instruction::PHP, AddressingMode::Implied)),
0x09 => Some((Instruction::ORA, AddressingMode::Immediate)),
0x0a => Some((Instruction::ASL, AddressingMode::Accumulator)),
0x0b => None,
0x0c => None,
0x0b => Some((Instruction::ANC, AddressingMode::Immediate)),
0x0c => Some((Instruction::NOPA, AddressingMode::Absolute)),
0x0d => Some((Instruction::ORA, AddressingMode::Absolute)),
0x0e => Some((Instruction::ASL, AddressingMode::Absolute)),
0x0f => None,
0x0f => Some((Instruction::SLO, AddressingMode::Absolute)),
0x10 => Some((Instruction::BPL, AddressingMode::Relative)),
0x11 => Some((Instruction::ORA, AddressingMode::IndirectIndexedY)),
0x12 => None,
0x13 => None,
0x14 => None,
0x12 => Some((Instruction::JAM, AddressingMode::Implied)),
0x13 => Some((Instruction::SLO, AddressingMode::IndirectIndexedY)),
0x14 => Some((Instruction::NOPZX, AddressingMode::ZeroPageX)),
0x15 => Some((Instruction::ORA, AddressingMode::ZeroPageX)),
0x16 => Some((Instruction::ASL, AddressingMode::ZeroPageX)),
0x17 => None,
0x17 => Some((Instruction::SLO, AddressingMode::ZeroPageX)),
0x18 => Some((Instruction::CLC, AddressingMode::Implied)),
0x19 => Some((Instruction::ORA, AddressingMode::AbsoluteY)),
0x1a => None,
0x1b => None,
0x1c => None,
0x1a => Some((Instruction::NOP, AddressingMode::Implied)),
0x1b => Some((Instruction::SLO, AddressingMode::AbsoluteY)),
0x1c => Some((Instruction::NOPAX, AddressingMode::AbsoluteX)),
0x1d => Some((Instruction::ORA, AddressingMode::AbsoluteX)),
0x1e => Some((Instruction::ASL, AddressingMode::AbsoluteX)),
0x1f => None,
0x1f => Some((Instruction::SLO, AddressingMode::AbsoluteX)),
0x20 => Some((Instruction::JSR, AddressingMode::Absolute)),
0x21 => Some((Instruction::AND, AddressingMode::IndexedIndirectX)),
0x22 => None,
0x23 => None,
0x22 => Some((Instruction::JAM, AddressingMode::Implied)),
0x23 => Some((Instruction::RLA, AddressingMode::IndexedIndirectX)),
0x24 => Some((Instruction::BIT, AddressingMode::ZeroPage)),
0x25 => Some((Instruction::AND, AddressingMode::ZeroPage)),
0x26 => Some((Instruction::ROL, AddressingMode::ZeroPage)),
0x27 => None,
0x27 => Some((Instruction::RLA, AddressingMode::ZeroPage)),
0x28 => Some((Instruction::PLP, AddressingMode::Implied)),
0x29 => Some((Instruction::AND, AddressingMode::Immediate)),
0x2a => Some((Instruction::ROL, AddressingMode::Accumulator)),
0x2b => None,
0x2b => Some((Instruction::ANC, AddressingMode::Immediate)),
0x2c => Some((Instruction::BIT, AddressingMode::Absolute)),
0x2d => Some((Instruction::AND, AddressingMode::Absolute)),
0x2e => Some((Instruction::ROL, AddressingMode::Absolute)),
0x2f => None,
0x2f => Some((Instruction::RLA, AddressingMode::Absolute)),
0x30 => Some((Instruction::BMI, AddressingMode::Relative)),
0x31 => Some((Instruction::AND, AddressingMode::IndirectIndexedY)),
0x32 => None,
0x33 => None,
0x34 => None,
0x32 => Some((Instruction::JAM, AddressingMode::Implied)),
0x33 => Some((Instruction::RLA, AddressingMode::IndirectIndexedY)),
0x34 => Some((Instruction::NOPZX, AddressingMode::ZeroPageX)),
0x35 => Some((Instruction::AND, AddressingMode::ZeroPageX)),
0x36 => Some((Instruction::ROL, AddressingMode::ZeroPageX)),
0x37 => None,
0x37 => Some((Instruction::RLA, AddressingMode::ZeroPageX)),
0x38 => Some((Instruction::SEC, AddressingMode::Implied)),
0x39 => Some((Instruction::AND, AddressingMode::AbsoluteY)),
0x3a => None,
0x3b => None,
0x3c => None,
0x3a => Some((Instruction::NOP, AddressingMode::Implied)),
0x3b => Some((Instruction::RLA, AddressingMode::AbsoluteY)),
0x3c => Some((Instruction::NOPAX, AddressingMode::AbsoluteX)),
0x3d => Some((Instruction::AND, AddressingMode::AbsoluteX)),
0x3e => Some((Instruction::ROL, AddressingMode::AbsoluteX)),
0x3f => None,
0x3f => Some((Instruction::RLA, AddressingMode::AbsoluteX)),
0x40 => Some((Instruction::RTI, AddressingMode::Implied)),
0x41 => Some((Instruction::EOR, AddressingMode::IndexedIndirectX)),
0x42 => None,
0x43 => None,
0x44 => None,
0x42 => Some((Instruction::JAM, AddressingMode::Implied)),
0x43 => Some((Instruction::SRE, AddressingMode::IndexedIndirectX)),
0x44 => Some((Instruction::NOPZ, AddressingMode::ZeroPage)),
0x45 => Some((Instruction::EOR, AddressingMode::ZeroPage)),
0x46 => Some((Instruction::LSR, AddressingMode::ZeroPage)),
0x47 => None,
0x47 => Some((Instruction::SRE, AddressingMode::ZeroPage)),
0x48 => Some((Instruction::PHA, AddressingMode::Implied)),
0x49 => Some((Instruction::EOR, AddressingMode::Immediate)),
0x4a => Some((Instruction::LSR, AddressingMode::Accumulator)),
0x4b => None,
0x4b => Some((Instruction::ALR, AddressingMode::Immediate)),
0x4c => Some((Instruction::JMP, AddressingMode::Absolute)),
0x4d => Some((Instruction::EOR, AddressingMode::Absolute)),
0x4e => Some((Instruction::LSR, AddressingMode::Absolute)),
0x4f => None,
0x4f => Some((Instruction::SRE, AddressingMode::Absolute)),
0x50 => Some((Instruction::BVC, AddressingMode::Relative)),
0x51 => Some((Instruction::EOR, AddressingMode::IndirectIndexedY)),
0x52 => None,
0x53 => None,
0x54 => None,
0x52 => Some((Instruction::JAM, AddressingMode::Implied)),
0x53 => Some((Instruction::SRE, AddressingMode::IndirectIndexedY)),
0x54 => Some((Instruction::NOPZX, AddressingMode::ZeroPageX)),
0x55 => Some((Instruction::EOR, AddressingMode::ZeroPageX)),
0x56 => Some((Instruction::LSR, AddressingMode::ZeroPageX)),
0x57 => None,
0x57 => Some((Instruction::SRE, AddressingMode::ZeroPageX)),
0x58 => Some((Instruction::CLI, AddressingMode::Implied)),
0x59 => Some((Instruction::EOR, AddressingMode::AbsoluteY)),
0x5a => None,
0x5b => None,
0x5c => None,
0x5a => Some((Instruction::NOP, AddressingMode::Implied)),
0x5b => Some((Instruction::SRE, AddressingMode::AbsoluteY)),
0x5c => Some((Instruction::NOPAX, AddressingMode::AbsoluteX)),
0x5d => Some((Instruction::EOR, AddressingMode::AbsoluteX)),
0x5e => Some((Instruction::LSR, AddressingMode::AbsoluteX)),
0x5f => None,
0x5f => Some((Instruction::SRE, AddressingMode::AbsoluteX)),
0x60 => Some((Instruction::RTS, AddressingMode::Implied)),
0x61 => Some((Instruction::ADC, AddressingMode::IndexedIndirectX)),
0x62 => None,
0x63 => None,
0x64 => None,
0x62 => Some((Instruction::JAM, AddressingMode::Implied)),
0x63 => Some((Instruction::RRA, AddressingMode::IndexedIndirectX)),
0x64 => Some((Instruction::NOPZ, AddressingMode::ZeroPage)),
0x65 => Some((Instruction::ADC, AddressingMode::ZeroPage)),
0x66 => Some((Instruction::ROR, AddressingMode::ZeroPage)),
0x67 => None,
0x67 => Some((Instruction::RRA, AddressingMode::ZeroPage)),
0x68 => Some((Instruction::PLA, AddressingMode::Implied)),
0x69 => Some((Instruction::ADC, AddressingMode::Immediate)),
0x6a => Some((Instruction::ROR, AddressingMode::Accumulator)),
0x6b => None,
0x6b => Some((Instruction::ARR, AddressingMode::Immediate)),
0x6c => Some((Instruction::JMP, AddressingMode::BuggyIndirect)),
0x6d => Some((Instruction::ADC, AddressingMode::Absolute)),
0x6e => Some((Instruction::ROR, AddressingMode::Absolute)),
0x6f => None,
0x6f => Some((Instruction::RRA, AddressingMode::Absolute)),
0x70 => Some((Instruction::BVS, AddressingMode::Relative)),
0x71 => Some((Instruction::ADC, AddressingMode::IndirectIndexedY)),
0x72 => None,
0x73 => None,
0x74 => None,
0x72 => Some((Instruction::JAM, AddressingMode::Implied)),
0x73 => Some((Instruction::RRA, AddressingMode::IndirectIndexedY)),
0x74 => Some((Instruction::NOPZX, AddressingMode::ZeroPageX)),
0x75 => Some((Instruction::ADC, AddressingMode::ZeroPageX)),
0x76 => Some((Instruction::ROR, AddressingMode::ZeroPageX)),
0x77 => None,
0x77 => Some((Instruction::RRA, AddressingMode::ZeroPageX)),
0x78 => Some((Instruction::SEI, AddressingMode::Implied)),
0x79 => Some((Instruction::ADC, AddressingMode::AbsoluteY)),
0x7a => None,
0x7b => None,
0x7c => None,
0x7a => Some((Instruction::NOP, AddressingMode::Implied)),
0x7b => Some((Instruction::RRA, AddressingMode::AbsoluteY)),
0x7c => Some((Instruction::NOPAX, AddressingMode::AbsoluteX)),
0x7d => Some((Instruction::ADC, AddressingMode::AbsoluteX)),
0x7e => Some((Instruction::ROR, AddressingMode::AbsoluteX)),
0x7f => None,
0x80 => None,
0x7f => Some((Instruction::RRA, AddressingMode::AbsoluteX)),
0x80 => Some((Instruction::NOPI, AddressingMode::Immediate)),
0x81 => Some((Instruction::STA, AddressingMode::IndexedIndirectX)),
0x82 => None,
0x83 => None,
0x82 => Some((Instruction::NOPI, AddressingMode::Immediate)),
0x83 => Some((Instruction::SAX, AddressingMode::IndexedIndirectX)),
0x84 => Some((Instruction::STY, AddressingMode::ZeroPage)),
0x85 => Some((Instruction::STA, AddressingMode::ZeroPage)),
0x86 => Some((Instruction::STX, AddressingMode::ZeroPage)),
0x87 => None,
0x87 => Some((Instruction::SAX, AddressingMode::ZeroPage)),
0x88 => Some((Instruction::DEY, AddressingMode::Implied)),
0x89 => None,
0x89 => Some((Instruction::NOPI, AddressingMode::Immediate)),
0x8a => Some((Instruction::TXA, AddressingMode::Implied)),
0x8b => None,
0x8b => Some((Instruction::XAA, AddressingMode::Immediate)),
0x8c => Some((Instruction::STY, AddressingMode::Absolute)),
0x8d => Some((Instruction::STA, AddressingMode::Absolute)),
0x8e => Some((Instruction::STX, AddressingMode::Absolute)),
0x8f => None,
0x8f => Some((Instruction::SAX, AddressingMode::Absolute)),
0x90 => Some((Instruction::BCC, AddressingMode::Relative)),
0x91 => Some((Instruction::STA, AddressingMode::IndirectIndexedY)),
0x92 => None,
0x93 => None,
0x92 => Some((Instruction::JAM, AddressingMode::Implied)),
0x93 => None, // SHA (zp),Y - unstable, skip
0x94 => Some((Instruction::STY, AddressingMode::ZeroPageX)),
0x95 => Some((Instruction::STA, AddressingMode::ZeroPageX)),
0x96 => Some((Instruction::STX, AddressingMode::ZeroPageY)),
0x97 => None,
0x97 => Some((Instruction::SAX, AddressingMode::ZeroPageY)),
0x98 => Some((Instruction::TYA, AddressingMode::Implied)),
0x99 => Some((Instruction::STA, AddressingMode::AbsoluteY)),
0x9a => Some((Instruction::TXS, AddressingMode::Implied)),
0x9b => None,
0x9c => None,
0x9b => None, // TAS abs,Y - unstable, skip
0x9c => None, // SHY abs,X - unstable, skip
0x9d => Some((Instruction::STA, AddressingMode::AbsoluteX)),
0x9e => None,
0x9f => None,
0x9e => None, // SHX abs,Y - unstable, skip
0x9f => None, // SHA abs,Y - unstable, skip
0xa0 => Some((Instruction::LDY, AddressingMode::Immediate)),
0xa1 => Some((Instruction::LDA, AddressingMode::IndexedIndirectX)),
0xa2 => Some((Instruction::LDX, AddressingMode::Immediate)),
0xa3 => None,
0xa3 => Some((Instruction::LAX, AddressingMode::IndexedIndirectX)),
0xa4 => Some((Instruction::LDY, AddressingMode::ZeroPage)),
0xa5 => Some((Instruction::LDA, AddressingMode::ZeroPage)),
0xa6 => Some((Instruction::LDX, AddressingMode::ZeroPage)),
0xa7 => None,
0xa7 => Some((Instruction::LAX, AddressingMode::ZeroPage)),
0xa8 => Some((Instruction::TAY, AddressingMode::Implied)),
0xa9 => Some((Instruction::LDA, AddressingMode::Immediate)),
0xaa => Some((Instruction::TAX, AddressingMode::Implied)),
0xab => None,
0xab => None, // LXA #imm - unstable, skip
0xac => Some((Instruction::LDY, AddressingMode::Absolute)),
0xad => Some((Instruction::LDA, AddressingMode::Absolute)),
0xae => Some((Instruction::LDX, AddressingMode::Absolute)),
0xaf => None,
0xaf => Some((Instruction::LAX, AddressingMode::Absolute)),
0xb0 => Some((Instruction::BCS, AddressingMode::Relative)),
0xb1 => Some((Instruction::LDA, AddressingMode::IndirectIndexedY)),
0xb2 => None,
0xb3 => None,
0xb2 => Some((Instruction::JAM, AddressingMode::Implied)),
0xb3 => Some((Instruction::LAX, AddressingMode::IndirectIndexedY)),
0xb4 => Some((Instruction::LDY, AddressingMode::ZeroPageX)),
0xb5 => Some((Instruction::LDA, AddressingMode::ZeroPageX)),
0xb6 => Some((Instruction::LDX, AddressingMode::ZeroPageY)),
0xb7 => None,
0xb7 => Some((Instruction::LAX, AddressingMode::ZeroPageY)),
0xb8 => Some((Instruction::CLV, AddressingMode::Implied)),
0xb9 => Some((Instruction::LDA, AddressingMode::AbsoluteY)),
0xba => Some((Instruction::TSX, AddressingMode::Implied)),
0xbb => None,
0xbb => Some((Instruction::LAS, AddressingMode::AbsoluteY)),
0xbc => Some((Instruction::LDY, AddressingMode::AbsoluteX)),
0xbd => Some((Instruction::LDA, AddressingMode::AbsoluteX)),
0xbe => Some((Instruction::LDX, AddressingMode::AbsoluteY)),
0xbf => None,
0xbf => Some((Instruction::LAX, AddressingMode::AbsoluteY)),
0xc0 => Some((Instruction::CPY, AddressingMode::Immediate)),
0xc1 => Some((Instruction::CMP, AddressingMode::IndexedIndirectX)),
0xc2 => None,
0xc3 => None,
0xc2 => Some((Instruction::NOPI, AddressingMode::Immediate)),
0xc3 => Some((Instruction::DCP, AddressingMode::IndexedIndirectX)),
0xc4 => Some((Instruction::CPY, AddressingMode::ZeroPage)),
0xc5 => Some((Instruction::CMP, AddressingMode::ZeroPage)),
0xc6 => Some((Instruction::DEC, AddressingMode::ZeroPage)),
0xc7 => None,
0xc7 => Some((Instruction::DCP, AddressingMode::ZeroPage)),
0xc8 => Some((Instruction::INY, AddressingMode::Implied)),
0xc9 => Some((Instruction::CMP, AddressingMode::Immediate)),
0xca => Some((Instruction::DEX, AddressingMode::Implied)),
0xcb => None,
0xcb => Some((Instruction::SBX, AddressingMode::Immediate)),
0xcc => Some((Instruction::CPY, AddressingMode::Absolute)),
0xcd => Some((Instruction::CMP, AddressingMode::Absolute)),
0xce => Some((Instruction::DEC, AddressingMode::Absolute)),
0xcf => None,
0xcf => Some((Instruction::DCP, AddressingMode::Absolute)),
0xd0 => Some((Instruction::BNE, AddressingMode::Relative)),
0xd1 => Some((Instruction::CMP, AddressingMode::IndirectIndexedY)),
0xd2 => None,
0xd3 => None,
0xd4 => None,
0xd2 => Some((Instruction::JAM, AddressingMode::Implied)),
0xd3 => Some((Instruction::DCP, AddressingMode::IndirectIndexedY)),
0xd4 => Some((Instruction::NOPZX, AddressingMode::ZeroPageX)),
0xd5 => Some((Instruction::CMP, AddressingMode::ZeroPageX)),
0xd6 => Some((Instruction::DEC, AddressingMode::ZeroPageX)),
0xd7 => None,
0xd7 => Some((Instruction::DCP, AddressingMode::ZeroPageX)),
0xd8 => Some((Instruction::CLD, AddressingMode::Implied)),
0xd9 => Some((Instruction::CMP, AddressingMode::AbsoluteY)),
0xda => None,
0xdb => None,
0xdc => None,
0xda => Some((Instruction::NOP, AddressingMode::Implied)),
0xdb => Some((Instruction::DCP, AddressingMode::AbsoluteY)),
0xdc => Some((Instruction::NOPAX, AddressingMode::AbsoluteX)),
0xdd => Some((Instruction::CMP, AddressingMode::AbsoluteX)),
0xde => Some((Instruction::DEC, AddressingMode::AbsoluteX)),
0xdf => None,
0xdf => Some((Instruction::DCP, AddressingMode::AbsoluteX)),
0xe0 => Some((Instruction::CPX, AddressingMode::Immediate)),
0xe1 => Some((Instruction::SBC, AddressingMode::IndexedIndirectX)),
0xe2 => None,
0xe3 => None,
0xe2 => Some((Instruction::NOPI, AddressingMode::Immediate)),
0xe3 => Some((Instruction::ISC, AddressingMode::IndexedIndirectX)),
0xe4 => Some((Instruction::CPX, AddressingMode::ZeroPage)),
0xe5 => Some((Instruction::SBC, AddressingMode::ZeroPage)),
0xe6 => Some((Instruction::INC, AddressingMode::ZeroPage)),
0xe7 => None,
0xe7 => Some((Instruction::ISC, AddressingMode::ZeroPage)),
0xe8 => Some((Instruction::INX, AddressingMode::Implied)),
0xe9 => Some((Instruction::SBC, AddressingMode::Immediate)),
0xea => Some((Instruction::NOP, AddressingMode::Implied)),
0xeb => None,
0xeb => Some((Instruction::USBC, AddressingMode::Immediate)),
0xec => Some((Instruction::CPX, AddressingMode::Absolute)),
0xed => Some((Instruction::SBC, AddressingMode::Absolute)),
0xee => Some((Instruction::INC, AddressingMode::Absolute)),
0xef => None,
0xef => Some((Instruction::ISC, AddressingMode::Absolute)),
0xf0 => Some((Instruction::BEQ, AddressingMode::Relative)),
0xf1 => Some((Instruction::SBC, AddressingMode::IndirectIndexedY)),
0xf2 => None,
0xf3 => None,
0xf4 => None,
0xf2 => Some((Instruction::JAM, AddressingMode::Implied)),
0xf3 => Some((Instruction::ISC, AddressingMode::IndirectIndexedY)),
0xf4 => Some((Instruction::NOPZX, AddressingMode::ZeroPageX)),
0xf5 => Some((Instruction::SBC, AddressingMode::ZeroPageX)),
0xf6 => Some((Instruction::INC, AddressingMode::ZeroPageX)),
0xf7 => None,
0xf7 => Some((Instruction::ISC, AddressingMode::ZeroPageX)),
0xf8 => Some((Instruction::SED, AddressingMode::Implied)),
0xf9 => Some((Instruction::SBC, AddressingMode::AbsoluteY)),
0xfa => None,
0xfb => None,
0xfc => None,
0xfa => Some((Instruction::NOP, AddressingMode::Implied)),
0xfb => Some((Instruction::ISC, AddressingMode::AbsoluteY)),
0xfc => Some((Instruction::NOPAX, AddressingMode::AbsoluteX)),
0xfd => Some((Instruction::SBC, AddressingMode::AbsoluteX)),
0xfe => Some((Instruction::INC, AddressingMode::AbsoluteX)),
0xff => None,
0xff => Some((Instruction::ISC, AddressingMode::AbsoluteX)),
}
}