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:
+1
-1
@@ -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"
|
||||
|
||||
@@ -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.
@@ -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
@@ -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
@@ -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
@@ -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)),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user