mirror of https://github.com/mre/mos6502.git
Starting on implementing different variants (#88)
* start on separating 6502 variants from cpu itself
* add a single variant: the NMOS one
* get examples & tests running again
* Add the Revision A variant, one that has no ROR
* disable failing lint in build-time dependencies
* Variant with no decimal mode
* Revert "disable failing lint in build-time dependencies"
This reverts commit c87975e937
.
* some doc comments
* specify the variant in unit test now the API has changed
---------
Co-authored-by: Sam M W <you@example.com>
This commit is contained in:
parent
b52e47bbb5
commit
11499b6bc8
|
@ -23,6 +23,7 @@ Source: [Wikipedia](https://en.wikipedia.org/wiki/MOS_Technology_6502)
|
||||||
```rust
|
```rust
|
||||||
use mos6502::memory::Bus;
|
use mos6502::memory::Bus;
|
||||||
use mos6502::memory::Memory;
|
use mos6502::memory::Memory;
|
||||||
|
use mos6502::instruction::Nmos6502;
|
||||||
use mos6502::cpu;
|
use mos6502::cpu;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -52,7 +53,7 @@ fn main() {
|
||||||
0x4c, 0x10, 0x00, // Jump to .algo
|
0x4c, 0x10, 0x00, // Jump to .algo
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut cpu = cpu::CPU::new(Memory::new());
|
let mut cpu = cpu::CPU::new(Memory::new(), Nmos6502);
|
||||||
|
|
||||||
cpu.memory.set_bytes(0x00, &zero_page_data);
|
cpu.memory.set_bytes(0x00, &zero_page_data);
|
||||||
cpu.memory.set_bytes(0x10, &program);
|
cpu.memory.set_bytes(0x10, &program);
|
||||||
|
@ -86,6 +87,7 @@ This will create a binary file `euclid.bin` that you can load into the emulator:
|
||||||
```rust
|
```rust
|
||||||
use mos6502::memory::Bus;
|
use mos6502::memory::Bus;
|
||||||
use mos6502::memory::Memory;
|
use mos6502::memory::Memory;
|
||||||
|
use mos6502::instruction::Nmos6502;
|
||||||
use mos6502::cpu;
|
use mos6502::cpu;
|
||||||
use std::fs::read;
|
use std::fs::read;
|
||||||
|
|
||||||
|
@ -103,7 +105,7 @@ fn main() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut cpu = cpu::CPU::new(Memory::new());
|
let mut cpu = cpu::CPU::new(Memory::new(), Nmos6502);
|
||||||
|
|
||||||
cpu.memory.set_bytes(0x00, &zero_page_data);
|
cpu.memory.set_bytes(0x00, &zero_page_data);
|
||||||
cpu.memory.set_bytes(0x10, &program);
|
cpu.memory.set_bytes(0x10, &program);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use mos6502::cpu;
|
use mos6502::cpu;
|
||||||
|
use mos6502::instruction::Nmos6502;
|
||||||
use mos6502::memory::Bus;
|
use mos6502::memory::Bus;
|
||||||
use mos6502::memory::Memory;
|
use mos6502::memory::Memory;
|
||||||
use std::fs::read;
|
use std::fs::read;
|
||||||
|
@ -22,7 +23,7 @@ fn main() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut cpu = cpu::CPU::new(Memory::new());
|
let mut cpu = cpu::CPU::new(Memory::new(), Nmos6502);
|
||||||
|
|
||||||
cpu.memory.set_bytes(0x00, &zero_page_data);
|
cpu.memory.set_bytes(0x00, &zero_page_data);
|
||||||
cpu.memory.set_bytes(0x10, &program);
|
cpu.memory.set_bytes(0x10, &program);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
extern crate mos6502;
|
extern crate mos6502;
|
||||||
|
|
||||||
use mos6502::cpu;
|
use mos6502::cpu;
|
||||||
|
use mos6502::instruction::Nmos6502;
|
||||||
use mos6502::memory::Bus;
|
use mos6502::memory::Bus;
|
||||||
use mos6502::memory::Memory;
|
use mos6502::memory::Memory;
|
||||||
|
|
||||||
|
@ -35,7 +36,7 @@ fn main() {
|
||||||
0x4c, 0x10, 0x00, // Jump to .algo
|
0x4c, 0x10, 0x00, // Jump to .algo
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut cpu = cpu::CPU::new(Memory::new());
|
let mut cpu = cpu::CPU::new(Memory::new(), Nmos6502);
|
||||||
|
|
||||||
cpu.memory.set_bytes(0x00, &zero_page_data);
|
cpu.memory.set_bytes(0x00, &zero_page_data);
|
||||||
cpu.memory.set_bytes(0x10, &program);
|
cpu.memory.set_bytes(0x10, &program);
|
||||||
|
|
|
@ -29,12 +29,13 @@ extern crate mos6502;
|
||||||
|
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
use mos6502::cpu;
|
use mos6502::cpu;
|
||||||
|
use mos6502::instruction::Nmos6502;
|
||||||
use mos6502::memory::Bus;
|
use mos6502::memory::Bus;
|
||||||
use mos6502::memory::Memory;
|
use mos6502::memory::Memory;
|
||||||
|
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut cpu = cpu::CPU::new(Memory::new());
|
let mut cpu = cpu::CPU::new(Memory::new(), Nmos6502);
|
||||||
|
|
||||||
// "Load" a program
|
// "Load" a program
|
||||||
|
|
||||||
|
|
233
src/cpu.rs
233
src/cpu.rs
|
@ -25,8 +25,9 @@
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
// POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
use crate::instruction::{self, AddressingMode, DecodedInstr, Instruction, OpInput};
|
use crate::instruction::{AddressingMode, DecodedInstr, Instruction, OpInput};
|
||||||
use crate::memory::Bus;
|
use crate::memory::Bus;
|
||||||
|
use crate::Variant;
|
||||||
|
|
||||||
use crate::registers::{Registers, StackPointer, Status, StatusArgs};
|
use crate::registers::{Registers, StackPointer, Status, StatusArgs};
|
||||||
|
|
||||||
|
@ -37,19 +38,22 @@ fn arr_to_addr(arr: &[u8]) -> u16 {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct CPU<M>
|
pub struct CPU<M, V>
|
||||||
where
|
where
|
||||||
M: Bus,
|
M: Bus,
|
||||||
|
V: Variant,
|
||||||
{
|
{
|
||||||
pub registers: Registers,
|
pub registers: Registers,
|
||||||
pub memory: M,
|
pub memory: M,
|
||||||
|
variant: core::marker::PhantomData<V>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<M: Bus> CPU<M> {
|
impl<M: Bus, V: Variant> CPU<M, V> {
|
||||||
pub fn new(memory: M) -> CPU<M> {
|
pub fn new(memory: M, _variant: V) -> CPU<M, V> {
|
||||||
CPU {
|
CPU {
|
||||||
registers: Registers::new(),
|
registers: Registers::new(),
|
||||||
memory,
|
memory,
|
||||||
|
variant: core::marker::PhantomData::<V>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,7 +64,7 @@ impl<M: Bus> CPU<M> {
|
||||||
pub fn fetch_next_and_decode(&mut self) -> Option<DecodedInstr> {
|
pub fn fetch_next_and_decode(&mut self) -> Option<DecodedInstr> {
|
||||||
let x: u8 = self.memory.get_byte(self.registers.program_counter);
|
let x: u8 = self.memory.get_byte(self.registers.program_counter);
|
||||||
|
|
||||||
match instruction::OPCODES[x as usize] {
|
match V::decode(x) {
|
||||||
Some((instr, am)) => {
|
Some((instr, am)) => {
|
||||||
let extra_bytes = am.extra_bytes();
|
let extra_bytes = am.extra_bytes();
|
||||||
let num_bytes = extra_bytes + 1;
|
let num_bytes = extra_bytes + 1;
|
||||||
|
@ -192,6 +196,15 @@ impl<M: Bus> CPU<M> {
|
||||||
debug!("add with carry. address: {:?}. value: {}", addr, val);
|
debug!("add with carry. address: {:?}. value: {}", addr, val);
|
||||||
self.add_with_carry(val);
|
self.add_with_carry(val);
|
||||||
}
|
}
|
||||||
|
(Instruction::ADCnd, OpInput::UseImmediate(val)) => {
|
||||||
|
debug!("add with carry immediate: {}", val);
|
||||||
|
self.add_with_no_decimal(val);
|
||||||
|
}
|
||||||
|
(Instruction::ADCnd, OpInput::UseAddress(addr)) => {
|
||||||
|
let val = self.memory.get_byte(addr);
|
||||||
|
debug!("add with carry. address: {:?}. value: {}", addr, val);
|
||||||
|
self.add_with_no_decimal(val);
|
||||||
|
}
|
||||||
|
|
||||||
(Instruction::AND, OpInput::UseImmediate(val)) => {
|
(Instruction::AND, OpInput::UseImmediate(val)) => {
|
||||||
self.and(val);
|
self.and(val);
|
||||||
|
@ -204,12 +217,12 @@ impl<M: Bus> CPU<M> {
|
||||||
(Instruction::ASL, OpInput::UseImplied) => {
|
(Instruction::ASL, OpInput::UseImplied) => {
|
||||||
// Accumulator mode
|
// Accumulator mode
|
||||||
let mut val = self.registers.accumulator;
|
let mut val = self.registers.accumulator;
|
||||||
CPU::<M>::shift_left_with_flags(&mut val, &mut self.registers.status);
|
CPU::<M, V>::shift_left_with_flags(&mut val, &mut self.registers.status);
|
||||||
self.registers.accumulator = val;
|
self.registers.accumulator = val;
|
||||||
}
|
}
|
||||||
(Instruction::ASL, OpInput::UseAddress(addr)) => {
|
(Instruction::ASL, OpInput::UseAddress(addr)) => {
|
||||||
let mut operand: u8 = self.memory.get_byte(addr);
|
let mut operand: u8 = self.memory.get_byte(addr);
|
||||||
CPU::<M>::shift_left_with_flags(&mut operand, &mut self.registers.status);
|
CPU::<M, V>::shift_left_with_flags(&mut operand, &mut self.registers.status);
|
||||||
self.memory.set_byte(addr, operand);
|
self.memory.set_byte(addr, operand);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -329,16 +342,16 @@ impl<M: Bus> CPU<M> {
|
||||||
|
|
||||||
(Instruction::DEC, OpInput::UseAddress(addr)) => {
|
(Instruction::DEC, OpInput::UseAddress(addr)) => {
|
||||||
let mut operand: u8 = self.memory.get_byte(addr);
|
let mut operand: u8 = self.memory.get_byte(addr);
|
||||||
CPU::<M>::decrement(&mut operand, &mut self.registers.status);
|
CPU::<M, V>::decrement(&mut operand, &mut self.registers.status);
|
||||||
self.memory.set_byte(addr, operand);
|
self.memory.set_byte(addr, operand);
|
||||||
}
|
}
|
||||||
|
|
||||||
(Instruction::DEY, OpInput::UseImplied) => {
|
(Instruction::DEY, OpInput::UseImplied) => {
|
||||||
CPU::<M>::decrement(&mut self.registers.index_y, &mut self.registers.status);
|
CPU::<M, V>::decrement(&mut self.registers.index_y, &mut self.registers.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
(Instruction::DEX, OpInput::UseImplied) => {
|
(Instruction::DEX, OpInput::UseImplied) => {
|
||||||
CPU::<M>::decrement(&mut self.registers.index_x, &mut self.registers.status);
|
CPU::<M, V>::decrement(&mut self.registers.index_x, &mut self.registers.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
(Instruction::EOR, OpInput::UseImmediate(val)) => {
|
(Instruction::EOR, OpInput::UseImmediate(val)) => {
|
||||||
|
@ -351,14 +364,14 @@ impl<M: Bus> CPU<M> {
|
||||||
|
|
||||||
(Instruction::INC, OpInput::UseAddress(addr)) => {
|
(Instruction::INC, OpInput::UseAddress(addr)) => {
|
||||||
let mut operand: u8 = self.memory.get_byte(addr);
|
let mut operand: u8 = self.memory.get_byte(addr);
|
||||||
CPU::<M>::increment(&mut operand, &mut self.registers.status);
|
CPU::<M, V>::increment(&mut operand, &mut self.registers.status);
|
||||||
self.memory.set_byte(addr, operand);
|
self.memory.set_byte(addr, operand);
|
||||||
}
|
}
|
||||||
(Instruction::INX, OpInput::UseImplied) => {
|
(Instruction::INX, OpInput::UseImplied) => {
|
||||||
CPU::<M>::increment(&mut self.registers.index_x, &mut self.registers.status);
|
CPU::<M, V>::increment(&mut self.registers.index_x, &mut self.registers.status);
|
||||||
}
|
}
|
||||||
(Instruction::INY, OpInput::UseImplied) => {
|
(Instruction::INY, OpInput::UseImplied) => {
|
||||||
CPU::<M>::increment(&mut self.registers.index_y, &mut self.registers.status);
|
CPU::<M, V>::increment(&mut self.registers.index_y, &mut self.registers.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
(Instruction::JMP, OpInput::UseAddress(addr)) => self.jump(addr),
|
(Instruction::JMP, OpInput::UseAddress(addr)) => self.jump(addr),
|
||||||
|
@ -403,12 +416,12 @@ impl<M: Bus> CPU<M> {
|
||||||
(Instruction::LSR, OpInput::UseImplied) => {
|
(Instruction::LSR, OpInput::UseImplied) => {
|
||||||
// Accumulator mode
|
// Accumulator mode
|
||||||
let mut val = self.registers.accumulator;
|
let mut val = self.registers.accumulator;
|
||||||
CPU::<M>::shift_right_with_flags(&mut val, &mut self.registers.status);
|
CPU::<M, V>::shift_right_with_flags(&mut val, &mut self.registers.status);
|
||||||
self.registers.accumulator = val;
|
self.registers.accumulator = val;
|
||||||
}
|
}
|
||||||
(Instruction::LSR, OpInput::UseAddress(addr)) => {
|
(Instruction::LSR, OpInput::UseAddress(addr)) => {
|
||||||
let mut operand: u8 = self.memory.get_byte(addr);
|
let mut operand: u8 = self.memory.get_byte(addr);
|
||||||
CPU::<M>::shift_right_with_flags(&mut operand, &mut self.registers.status);
|
CPU::<M, V>::shift_right_with_flags(&mut operand, &mut self.registers.status);
|
||||||
self.memory.set_byte(addr, operand);
|
self.memory.set_byte(addr, operand);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -457,23 +470,23 @@ impl<M: Bus> CPU<M> {
|
||||||
(Instruction::ROL, OpInput::UseImplied) => {
|
(Instruction::ROL, OpInput::UseImplied) => {
|
||||||
// Accumulator mode
|
// Accumulator mode
|
||||||
let mut val = self.registers.accumulator;
|
let mut val = self.registers.accumulator;
|
||||||
CPU::<M>::rotate_left_with_flags(&mut val, &mut self.registers.status);
|
CPU::<M, V>::rotate_left_with_flags(&mut val, &mut self.registers.status);
|
||||||
self.registers.accumulator = val;
|
self.registers.accumulator = val;
|
||||||
}
|
}
|
||||||
(Instruction::ROL, OpInput::UseAddress(addr)) => {
|
(Instruction::ROL, OpInput::UseAddress(addr)) => {
|
||||||
let mut operand: u8 = self.memory.get_byte(addr);
|
let mut operand: u8 = self.memory.get_byte(addr);
|
||||||
CPU::<M>::rotate_left_with_flags(&mut operand, &mut self.registers.status);
|
CPU::<M, V>::rotate_left_with_flags(&mut operand, &mut self.registers.status);
|
||||||
self.memory.set_byte(addr, operand);
|
self.memory.set_byte(addr, operand);
|
||||||
}
|
}
|
||||||
(Instruction::ROR, OpInput::UseImplied) => {
|
(Instruction::ROR, OpInput::UseImplied) => {
|
||||||
// Accumulator mode
|
// Accumulator mode
|
||||||
let mut val = self.registers.accumulator;
|
let mut val = self.registers.accumulator;
|
||||||
CPU::<M>::rotate_right_with_flags(&mut val, &mut self.registers.status);
|
CPU::<M, V>::rotate_right_with_flags(&mut val, &mut self.registers.status);
|
||||||
self.registers.accumulator = val;
|
self.registers.accumulator = val;
|
||||||
}
|
}
|
||||||
(Instruction::ROR, OpInput::UseAddress(addr)) => {
|
(Instruction::ROR, OpInput::UseAddress(addr)) => {
|
||||||
let mut operand: u8 = self.memory.get_byte(addr);
|
let mut operand: u8 = self.memory.get_byte(addr);
|
||||||
CPU::<M>::rotate_right_with_flags(&mut operand, &mut self.registers.status);
|
CPU::<M, V>::rotate_right_with_flags(&mut operand, &mut self.registers.status);
|
||||||
self.memory.set_byte(addr, operand);
|
self.memory.set_byte(addr, operand);
|
||||||
}
|
}
|
||||||
(Instruction::RTI, OpInput::UseImplied) => {
|
(Instruction::RTI, OpInput::UseImplied) => {
|
||||||
|
@ -505,6 +518,16 @@ impl<M: Bus> CPU<M> {
|
||||||
self.subtract_with_carry(val);
|
self.subtract_with_carry(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(Instruction::SBCnd, OpInput::UseImmediate(val)) => {
|
||||||
|
debug!("subtract with carry immediate: {}", val);
|
||||||
|
self.subtract_with_no_decimal(val);
|
||||||
|
}
|
||||||
|
(Instruction::SBCnd, OpInput::UseAddress(addr)) => {
|
||||||
|
let val = self.memory.get_byte(addr);
|
||||||
|
debug!("subtract with carry. address: {:?}. value: {}", addr, val);
|
||||||
|
self.subtract_with_no_decimal(val);
|
||||||
|
}
|
||||||
|
|
||||||
(Instruction::SEC, OpInput::UseImplied) => {
|
(Instruction::SEC, OpInput::UseImplied) => {
|
||||||
self.registers.status.or(Status::PS_CARRY);
|
self.registers.status.or(Status::PS_CARRY);
|
||||||
}
|
}
|
||||||
|
@ -617,7 +640,7 @@ impl<M: Bus> CPU<M> {
|
||||||
..StatusArgs::none()
|
..StatusArgs::none()
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
CPU::<M>::set_flags_from_i8(status, *p_val as i8);
|
CPU::<M, V>::set_flags_from_i8(status, *p_val as i8);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shift_right_with_flags(p_val: &mut u8, status: &mut Status) {
|
fn shift_right_with_flags(p_val: &mut u8, status: &mut Status) {
|
||||||
|
@ -631,7 +654,7 @@ impl<M: Bus> CPU<M> {
|
||||||
..StatusArgs::none()
|
..StatusArgs::none()
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
CPU::<M>::set_flags_from_i8(status, *p_val as i8);
|
CPU::<M, V>::set_flags_from_i8(status, *p_val as i8);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rotate_left_with_flags(p_val: &mut u8, status: &mut Status) {
|
fn rotate_left_with_flags(p_val: &mut u8, status: &mut Status) {
|
||||||
|
@ -647,7 +670,7 @@ impl<M: Bus> CPU<M> {
|
||||||
..StatusArgs::none()
|
..StatusArgs::none()
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
CPU::<M>::set_flags_from_i8(status, *p_val as i8);
|
CPU::<M, V>::set_flags_from_i8(status, *p_val as i8);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rotate_right_with_flags(p_val: &mut u8, status: &mut Status) {
|
fn rotate_right_with_flags(p_val: &mut u8, status: &mut Status) {
|
||||||
|
@ -663,16 +686,16 @@ impl<M: Bus> CPU<M> {
|
||||||
..StatusArgs::none()
|
..StatusArgs::none()
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
CPU::<M>::set_flags_from_i8(status, *p_val as i8);
|
CPU::<M, V>::set_flags_from_i8(status, *p_val as i8);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_u8_with_flags(mem: &mut u8, status: &mut Status, value: u8) {
|
fn set_u8_with_flags(mem: &mut u8, status: &mut Status, value: u8) {
|
||||||
*mem = value;
|
*mem = value;
|
||||||
CPU::<M>::set_flags_from_u8(status, value);
|
CPU::<M, V>::set_flags_from_u8(status, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_x_register(&mut self, value: u8) {
|
fn load_x_register(&mut self, value: u8) {
|
||||||
CPU::<M>::set_u8_with_flags(
|
CPU::<M, V>::set_u8_with_flags(
|
||||||
&mut self.registers.index_x,
|
&mut self.registers.index_x,
|
||||||
&mut self.registers.status,
|
&mut self.registers.status,
|
||||||
value,
|
value,
|
||||||
|
@ -680,7 +703,7 @@ impl<M: Bus> CPU<M> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_y_register(&mut self, value: u8) {
|
fn load_y_register(&mut self, value: u8) {
|
||||||
CPU::<M>::set_u8_with_flags(
|
CPU::<M, V>::set_u8_with_flags(
|
||||||
&mut self.registers.index_y,
|
&mut self.registers.index_y,
|
||||||
&mut self.registers.status,
|
&mut self.registers.status,
|
||||||
value,
|
value,
|
||||||
|
@ -688,7 +711,7 @@ impl<M: Bus> CPU<M> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_accumulator(&mut self, value: u8) {
|
fn load_accumulator(&mut self, value: u8) {
|
||||||
CPU::<M>::set_u8_with_flags(
|
CPU::<M, V>::set_u8_with_flags(
|
||||||
&mut self.registers.accumulator,
|
&mut self.registers.accumulator,
|
||||||
&mut self.registers.status,
|
&mut self.registers.status,
|
||||||
value,
|
value,
|
||||||
|
@ -696,7 +719,6 @@ impl<M: Bus> CPU<M> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_with_carry(&mut self, value: u8) {
|
fn add_with_carry(&mut self, value: u8) {
|
||||||
#[cfg(feature = "decimal_mode")]
|
|
||||||
fn decimal_adjust(result: u8) -> u8 {
|
fn decimal_adjust(result: u8) -> u8 {
|
||||||
let bcd1: u8 = if (result & 0x0f) > 0x09 { 0x06 } else { 0x00 };
|
let bcd1: u8 = if (result & 0x0f) > 0x09 { 0x06 } else { 0x00 };
|
||||||
|
|
||||||
|
@ -715,15 +737,43 @@ impl<M: Bus> CPU<M> {
|
||||||
|
|
||||||
debug_assert_eq!(a_after, a_before.wrapping_add(c_before).wrapping_add(value));
|
debug_assert_eq!(a_after, a_before.wrapping_add(c_before).wrapping_add(value));
|
||||||
|
|
||||||
#[cfg(feature = "decimal_mode")]
|
|
||||||
let result: u8 = if self.registers.status.contains(Status::PS_DECIMAL_MODE) {
|
let result: u8 = if self.registers.status.contains(Status::PS_DECIMAL_MODE) {
|
||||||
decimal_adjust(a_after)
|
decimal_adjust(a_after)
|
||||||
} else {
|
} else {
|
||||||
a_after
|
a_after
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "decimal_mode"))]
|
let did_carry = (result) < (a_before)
|
||||||
let result: u8 = a_after;
|
|| (a_after == 0 && c_before == 0x01)
|
||||||
|
|| (value == 0xff && c_before == 0x01);
|
||||||
|
|
||||||
|
let did_overflow = (a_before > 127 && value > 127 && a_after < 128)
|
||||||
|
|| (a_before < 128 && value < 128 && a_after > 127);
|
||||||
|
|
||||||
|
let mask = Status::PS_CARRY | Status::PS_OVERFLOW;
|
||||||
|
|
||||||
|
self.registers.status.set_with_mask(
|
||||||
|
mask,
|
||||||
|
Status::new(StatusArgs {
|
||||||
|
carry: did_carry,
|
||||||
|
overflow: did_overflow,
|
||||||
|
..StatusArgs::none()
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
self.load_accumulator(result);
|
||||||
|
|
||||||
|
debug!("accumulator: {}", self.registers.accumulator);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_with_no_decimal(&mut self, value: u8) {
|
||||||
|
let a_before: u8 = self.registers.accumulator;
|
||||||
|
let c_before: u8 = u8::from(self.registers.status.contains(Status::PS_CARRY));
|
||||||
|
let a_after: u8 = a_before.wrapping_add(c_before).wrapping_add(value);
|
||||||
|
|
||||||
|
debug_assert_eq!(a_after, a_before.wrapping_add(c_before).wrapping_add(value));
|
||||||
|
|
||||||
|
let result = a_after;
|
||||||
|
|
||||||
let did_carry = (result) < (a_before)
|
let did_carry = (result) < (a_before)
|
||||||
|| (a_after == 0 && c_before == 0x01)
|
|| (a_after == 0 && c_before == 0x01)
|
||||||
|
@ -753,6 +803,52 @@ impl<M: Bus> CPU<M> {
|
||||||
self.load_accumulator(a_after);
|
self.load_accumulator(a_after);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn subtract_with_no_decimal(&mut self, value: u8) {
|
||||||
|
// A - M - (1 - C)
|
||||||
|
|
||||||
|
// nc -- 'not carry'
|
||||||
|
let nc: u8 = if self.registers.status.contains(Status::PS_CARRY) {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
};
|
||||||
|
|
||||||
|
let a_before = self.registers.accumulator;
|
||||||
|
|
||||||
|
let a_after = a_before.wrapping_sub(value).wrapping_sub(nc);
|
||||||
|
|
||||||
|
// The overflow flag is set on two's-complement overflow.
|
||||||
|
//
|
||||||
|
// range of A is -128 to 127
|
||||||
|
// range of - M - (1 - C) is -128 to 128
|
||||||
|
// -(127 + 1) to -(-128 + 0)
|
||||||
|
//
|
||||||
|
let over = (nc == 0 && value > 127) && a_before < 128 && a_after > 127;
|
||||||
|
|
||||||
|
let under =
|
||||||
|
(a_before > 127) && (0u8.wrapping_sub(value).wrapping_sub(nc) > 127) && a_after < 128;
|
||||||
|
|
||||||
|
let did_overflow = over || under;
|
||||||
|
|
||||||
|
let mask = Status::PS_CARRY | Status::PS_OVERFLOW;
|
||||||
|
|
||||||
|
let result = a_after;
|
||||||
|
|
||||||
|
// The carry flag is set on unsigned overflow.
|
||||||
|
let did_carry = (result) > (a_before);
|
||||||
|
|
||||||
|
self.registers.status.set_with_mask(
|
||||||
|
mask,
|
||||||
|
Status::new(StatusArgs {
|
||||||
|
carry: did_carry,
|
||||||
|
overflow: did_overflow,
|
||||||
|
..StatusArgs::none()
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
self.load_accumulator(result);
|
||||||
|
}
|
||||||
|
|
||||||
fn subtract_with_carry(&mut self, value: u8) {
|
fn subtract_with_carry(&mut self, value: u8) {
|
||||||
// A - M - (1 - C)
|
// A - M - (1 - C)
|
||||||
|
|
||||||
|
@ -794,16 +890,12 @@ impl<M: Bus> CPU<M> {
|
||||||
0x00
|
0x00
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "decimal_mode")]
|
|
||||||
let result: u8 = if self.registers.status.contains(Status::PS_DECIMAL_MODE) {
|
let result: u8 = if self.registers.status.contains(Status::PS_DECIMAL_MODE) {
|
||||||
a_after.wrapping_sub(bcd1).wrapping_sub(bcd2)
|
a_after.wrapping_sub(bcd1).wrapping_sub(bcd2)
|
||||||
} else {
|
} else {
|
||||||
a_after
|
a_after
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "decimal_mode"))]
|
|
||||||
let result = a_after;
|
|
||||||
|
|
||||||
// The carry flag is set on unsigned overflow.
|
// The carry flag is set on unsigned overflow.
|
||||||
let did_carry = (result) > (a_before);
|
let did_carry = (result) > (a_before);
|
||||||
|
|
||||||
|
@ -980,7 +1072,7 @@ impl<M: Bus> CPU<M> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<M: Bus> core::fmt::Debug for CPU<M> {
|
impl<M: Bus, V: Variant> core::fmt::Debug for CPU<M, V> {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
|
@ -994,11 +1086,12 @@ impl<M: Bus> core::fmt::Debug for CPU<M> {
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::instruction::Nmos6502;
|
||||||
use crate::memory::Memory as Ram;
|
use crate::memory::Memory as Ram;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dont_panic_for_overflow() {
|
fn dont_panic_for_overflow() {
|
||||||
let mut cpu = CPU::new(Ram::new());
|
let mut cpu = CPU::new(Ram::new(), Nmos6502);
|
||||||
cpu.add_with_carry(0x80);
|
cpu.add_with_carry(0x80);
|
||||||
assert_eq!(cpu.registers.accumulator, 0x80);
|
assert_eq!(cpu.registers.accumulator, 0x80);
|
||||||
cpu.add_with_carry(0x80);
|
cpu.add_with_carry(0x80);
|
||||||
|
@ -1012,7 +1105,7 @@ mod tests {
|
||||||
|
|
||||||
#[cfg_attr(feature = "decimal_mode", test)]
|
#[cfg_attr(feature = "decimal_mode", test)]
|
||||||
fn decimal_add_test() {
|
fn decimal_add_test() {
|
||||||
let mut cpu = CPU::new(Ram::new());
|
let mut cpu = CPU::new(Ram::new(), Nmos6502);
|
||||||
cpu.registers.status.or(Status::PS_DECIMAL_MODE);
|
cpu.registers.status.or(Status::PS_DECIMAL_MODE);
|
||||||
|
|
||||||
cpu.add_with_carry(0x09);
|
cpu.add_with_carry(0x09);
|
||||||
|
@ -1039,7 +1132,7 @@ mod tests {
|
||||||
|
|
||||||
#[cfg_attr(feature = "decimal_mode", test)]
|
#[cfg_attr(feature = "decimal_mode", test)]
|
||||||
fn decimal_subtract_test() {
|
fn decimal_subtract_test() {
|
||||||
let mut cpu = CPU::new(Ram::new());
|
let mut cpu = CPU::new(Ram::new(), Nmos6502);
|
||||||
cpu.registers
|
cpu.registers
|
||||||
.status
|
.status
|
||||||
.or(Status::PS_DECIMAL_MODE | Status::PS_CARRY);
|
.or(Status::PS_DECIMAL_MODE | Status::PS_CARRY);
|
||||||
|
@ -1061,7 +1154,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add_with_carry_test() {
|
fn add_with_carry_test() {
|
||||||
let mut cpu = CPU::new(Ram::new());
|
let mut cpu = CPU::new(Ram::new(), Nmos6502);
|
||||||
|
|
||||||
cpu.add_with_carry(1);
|
cpu.add_with_carry(1);
|
||||||
assert_eq!(cpu.registers.accumulator, 1);
|
assert_eq!(cpu.registers.accumulator, 1);
|
||||||
|
@ -1084,7 +1177,7 @@ mod tests {
|
||||||
assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE));
|
assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE));
|
||||||
assert!(!cpu.registers.status.contains(Status::PS_OVERFLOW));
|
assert!(!cpu.registers.status.contains(Status::PS_OVERFLOW));
|
||||||
|
|
||||||
let mut cpu = CPU::new(Ram::new());
|
let mut cpu = CPU::new(Ram::new(), Nmos6502);
|
||||||
|
|
||||||
assert_eq!(cpu.registers.accumulator, 0);
|
assert_eq!(cpu.registers.accumulator, 0);
|
||||||
cpu.add_with_carry(127);
|
cpu.add_with_carry(127);
|
||||||
|
@ -1116,7 +1209,7 @@ mod tests {
|
||||||
assert!(cpu.registers.status.contains(Status::PS_NEGATIVE));
|
assert!(cpu.registers.status.contains(Status::PS_NEGATIVE));
|
||||||
assert!(!cpu.registers.status.contains(Status::PS_OVERFLOW));
|
assert!(!cpu.registers.status.contains(Status::PS_OVERFLOW));
|
||||||
|
|
||||||
let mut cpu = CPU::new(Ram::new());
|
let mut cpu = CPU::new(Ram::new(), Nmos6502);
|
||||||
|
|
||||||
cpu.add_with_carry(127);
|
cpu.add_with_carry(127);
|
||||||
assert_eq!(cpu.registers.accumulator, 127);
|
assert_eq!(cpu.registers.accumulator, 127);
|
||||||
|
@ -1132,7 +1225,7 @@ mod tests {
|
||||||
assert!(cpu.registers.status.contains(Status::PS_NEGATIVE));
|
assert!(cpu.registers.status.contains(Status::PS_NEGATIVE));
|
||||||
assert!(cpu.registers.status.contains(Status::PS_OVERFLOW));
|
assert!(cpu.registers.status.contains(Status::PS_OVERFLOW));
|
||||||
|
|
||||||
let mut cpu = CPU::new(Ram::new());
|
let mut cpu = CPU::new(Ram::new(), Nmos6502);
|
||||||
cpu.registers.status.or(Status::PS_CARRY);
|
cpu.registers.status.or(Status::PS_CARRY);
|
||||||
cpu.add_with_carry(0xff);
|
cpu.add_with_carry(0xff);
|
||||||
assert_eq!(cpu.registers.accumulator, 0);
|
assert_eq!(cpu.registers.accumulator, 0);
|
||||||
|
@ -1141,7 +1234,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn solid65_adc_immediate() {
|
fn solid65_adc_immediate() {
|
||||||
let mut cpu = CPU::new(Ram::new());
|
let mut cpu = CPU::new(Ram::new(), Nmos6502);
|
||||||
|
|
||||||
// Adding $FF plus carry should be the same as adding $00 and no carry, so these three
|
// Adding $FF plus carry should be the same as adding $00 and no carry, so these three
|
||||||
// instructions should leave the carry flags unaffected, i.e. set.
|
// instructions should leave the carry flags unaffected, i.e. set.
|
||||||
|
@ -1155,7 +1248,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn php_sets_bits_4_and_5() {
|
fn php_sets_bits_4_and_5() {
|
||||||
let mut cpu = CPU::new(Ram::new());
|
let mut cpu = CPU::new(Ram::new(), Nmos6502);
|
||||||
cpu.execute_instruction((Instruction::PHP, OpInput::UseImplied));
|
cpu.execute_instruction((Instruction::PHP, OpInput::UseImplied));
|
||||||
cpu.execute_instruction((Instruction::PLA, OpInput::UseImplied));
|
cpu.execute_instruction((Instruction::PLA, OpInput::UseImplied));
|
||||||
cpu.execute_instruction((Instruction::AND, OpInput::UseImmediate(0x30)));
|
cpu.execute_instruction((Instruction::AND, OpInput::UseImmediate(0x30)));
|
||||||
|
@ -1165,7 +1258,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn and_test() {
|
fn and_test() {
|
||||||
let mut cpu = CPU::new(Ram::new());
|
let mut cpu = CPU::new(Ram::new(), Nmos6502);
|
||||||
|
|
||||||
cpu.registers.accumulator = 0;
|
cpu.registers.accumulator = 0;
|
||||||
cpu.and(0xff);
|
cpu.and(0xff);
|
||||||
|
@ -1194,7 +1287,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn subtract_with_carry_test() {
|
fn subtract_with_carry_test() {
|
||||||
let mut cpu = CPU::new(Ram::new());
|
let mut cpu = CPU::new(Ram::new(), Nmos6502);
|
||||||
|
|
||||||
cpu.execute_instruction((Instruction::SEC, OpInput::UseImplied));
|
cpu.execute_instruction((Instruction::SEC, OpInput::UseImplied));
|
||||||
cpu.registers.accumulator = 0;
|
cpu.registers.accumulator = 0;
|
||||||
|
@ -1254,7 +1347,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn decrement_memory_test() {
|
fn decrement_memory_test() {
|
||||||
let mut cpu = CPU::new(Ram::new());
|
let mut cpu = CPU::new(Ram::new(), Nmos6502);
|
||||||
let addr: u16 = 0xA1B2;
|
let addr: u16 = 0xA1B2;
|
||||||
|
|
||||||
cpu.memory.set_byte(addr, 5);
|
cpu.memory.set_byte(addr, 5);
|
||||||
|
@ -1291,7 +1384,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn decrement_x_test() {
|
fn decrement_x_test() {
|
||||||
let mut cpu = CPU::new(Ram::new());
|
let mut cpu = CPU::new(Ram::new(), Nmos6502);
|
||||||
cpu.registers.index_x = 0x80;
|
cpu.registers.index_x = 0x80;
|
||||||
cpu.execute_instruction((Instruction::DEX, OpInput::UseImplied));
|
cpu.execute_instruction((Instruction::DEX, OpInput::UseImplied));
|
||||||
assert_eq!(cpu.registers.index_x, 127);
|
assert_eq!(cpu.registers.index_x, 127);
|
||||||
|
@ -1301,7 +1394,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn decrement_y_test() {
|
fn decrement_y_test() {
|
||||||
let mut cpu = CPU::new(Ram::new());
|
let mut cpu = CPU::new(Ram::new(), Nmos6502);
|
||||||
cpu.registers.index_y = 0x80;
|
cpu.registers.index_y = 0x80;
|
||||||
cpu.execute_instruction((Instruction::DEY, OpInput::UseImplied));
|
cpu.execute_instruction((Instruction::DEY, OpInput::UseImplied));
|
||||||
assert_eq!(cpu.registers.index_y, 127);
|
assert_eq!(cpu.registers.index_y, 127);
|
||||||
|
@ -1313,7 +1406,7 @@ mod tests {
|
||||||
fn logical_shift_right_test() {
|
fn logical_shift_right_test() {
|
||||||
// Testing UseImplied version (which targets the accumulator) only, for now
|
// Testing UseImplied version (which targets the accumulator) only, for now
|
||||||
|
|
||||||
let mut cpu = CPU::new(Ram::new());
|
let mut cpu = CPU::new(Ram::new(), Nmos6502);
|
||||||
cpu.execute_instruction((Instruction::LDA, OpInput::UseImmediate(0)));
|
cpu.execute_instruction((Instruction::LDA, OpInput::UseImmediate(0)));
|
||||||
cpu.execute_instruction((Instruction::LSR, OpInput::UseImplied));
|
cpu.execute_instruction((Instruction::LSR, OpInput::UseImplied));
|
||||||
assert_eq!(cpu.registers.accumulator, 0);
|
assert_eq!(cpu.registers.accumulator, 0);
|
||||||
|
@ -1349,7 +1442,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dec_x_test() {
|
fn dec_x_test() {
|
||||||
let mut cpu = CPU::new(Ram::new());
|
let mut cpu = CPU::new(Ram::new(), Nmos6502);
|
||||||
|
|
||||||
cpu.execute_instruction((Instruction::DEX, OpInput::UseImplied));
|
cpu.execute_instruction((Instruction::DEX, OpInput::UseImplied));
|
||||||
assert_eq!(cpu.registers.index_x, 0xff);
|
assert_eq!(cpu.registers.index_x, 0xff);
|
||||||
|
@ -1394,7 +1487,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn jump_test() {
|
fn jump_test() {
|
||||||
let mut cpu = CPU::new(Ram::new());
|
let mut cpu = CPU::new(Ram::new(), Nmos6502);
|
||||||
let addr: u16 = 0xA1B1;
|
let addr: u16 = 0xA1B1;
|
||||||
|
|
||||||
cpu.jump(addr);
|
cpu.jump(addr);
|
||||||
|
@ -1403,7 +1496,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn branch_if_carry_clear_test() {
|
fn branch_if_carry_clear_test() {
|
||||||
let mut cpu = CPU::new(Ram::new());
|
let mut cpu = CPU::new(Ram::new(), Nmos6502);
|
||||||
|
|
||||||
cpu.execute_instruction((Instruction::SEC, OpInput::UseImplied));
|
cpu.execute_instruction((Instruction::SEC, OpInput::UseImplied));
|
||||||
cpu.branch_if_carry_clear(0xABCD);
|
cpu.branch_if_carry_clear(0xABCD);
|
||||||
|
@ -1416,7 +1509,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn branch_if_carry_set_test() {
|
fn branch_if_carry_set_test() {
|
||||||
let mut cpu = CPU::new(Ram::new());
|
let mut cpu = CPU::new(Ram::new(), Nmos6502);
|
||||||
|
|
||||||
cpu.execute_instruction((Instruction::CLC, OpInput::UseImplied));
|
cpu.execute_instruction((Instruction::CLC, OpInput::UseImplied));
|
||||||
cpu.branch_if_carry_set(0xABCD);
|
cpu.branch_if_carry_set(0xABCD);
|
||||||
|
@ -1429,7 +1522,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn branch_if_equal_test() {
|
fn branch_if_equal_test() {
|
||||||
let mut cpu = CPU::new(Ram::new());
|
let mut cpu = CPU::new(Ram::new(), Nmos6502);
|
||||||
|
|
||||||
cpu.branch_if_equal(0xABCD);
|
cpu.branch_if_equal(0xABCD);
|
||||||
assert_eq!(cpu.registers.program_counter, (0));
|
assert_eq!(cpu.registers.program_counter, (0));
|
||||||
|
@ -1442,7 +1535,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn branch_if_minus_test() {
|
fn branch_if_minus_test() {
|
||||||
{
|
{
|
||||||
let mut cpu = CPU::new(Ram::new());
|
let mut cpu = CPU::new(Ram::new(), Nmos6502);
|
||||||
let registers_before = cpu.registers;
|
let registers_before = cpu.registers;
|
||||||
|
|
||||||
cpu.branch_if_minus(0xABCD);
|
cpu.branch_if_minus(0xABCD);
|
||||||
|
@ -1451,7 +1544,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut cpu = CPU::new(Ram::new());
|
let mut cpu = CPU::new(Ram::new(), Nmos6502);
|
||||||
|
|
||||||
cpu.registers.status.or(Status::PS_NEGATIVE);
|
cpu.registers.status.or(Status::PS_NEGATIVE);
|
||||||
let registers_before = cpu.registers;
|
let registers_before = cpu.registers;
|
||||||
|
@ -1464,7 +1557,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn branch_if_positive_test() {
|
fn branch_if_positive_test() {
|
||||||
let mut cpu = CPU::new(Ram::new());
|
let mut cpu = CPU::new(Ram::new(), Nmos6502);
|
||||||
|
|
||||||
cpu.registers.status.insert(Status::PS_NEGATIVE);
|
cpu.registers.status.insert(Status::PS_NEGATIVE);
|
||||||
cpu.branch_if_positive(0xABCD);
|
cpu.branch_if_positive(0xABCD);
|
||||||
|
@ -1477,7 +1570,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn branch_if_overflow_clear_test() {
|
fn branch_if_overflow_clear_test() {
|
||||||
let mut cpu = CPU::new(Ram::new());
|
let mut cpu = CPU::new(Ram::new(), Nmos6502);
|
||||||
|
|
||||||
cpu.registers.status.insert(Status::PS_OVERFLOW);
|
cpu.registers.status.insert(Status::PS_OVERFLOW);
|
||||||
cpu.branch_if_overflow_clear(0xABCD);
|
cpu.branch_if_overflow_clear(0xABCD);
|
||||||
|
@ -1490,7 +1583,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn branch_across_end_of_address_space() {
|
fn branch_across_end_of_address_space() {
|
||||||
let mut cpu = CPU::new(Ram::new());
|
let mut cpu = CPU::new(Ram::new(), Nmos6502);
|
||||||
cpu.registers.program_counter = 0xffff;
|
cpu.registers.program_counter = 0xffff;
|
||||||
|
|
||||||
cpu.registers.status.insert(Status::PS_OVERFLOW);
|
cpu.registers.status.insert(Status::PS_OVERFLOW);
|
||||||
|
@ -1500,7 +1593,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn branch_if_overflow_set_test() {
|
fn branch_if_overflow_set_test() {
|
||||||
let mut cpu = CPU::new(Ram::new());
|
let mut cpu = CPU::new(Ram::new(), Nmos6502);
|
||||||
|
|
||||||
cpu.branch_if_overflow_set(0xABCD);
|
cpu.branch_if_overflow_set(0xABCD);
|
||||||
assert_eq!(cpu.registers.program_counter, (0));
|
assert_eq!(cpu.registers.program_counter, (0));
|
||||||
|
@ -1513,9 +1606,9 @@ mod tests {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
fn compare_test_helper<F>(compare: &mut F, load_instruction: Instruction)
|
fn compare_test_helper<F>(compare: &mut F, load_instruction: Instruction)
|
||||||
where
|
where
|
||||||
F: FnMut(&mut CPU<Ram>, u8),
|
F: FnMut(&mut CPU<Ram, crate::instruction::Nmos6502>, u8),
|
||||||
{
|
{
|
||||||
let mut cpu = CPU::new(Ram::new());
|
let mut cpu = CPU::new(Ram::new(), Nmos6502);
|
||||||
|
|
||||||
cpu.execute_instruction((load_instruction, OpInput::UseImmediate(127)));
|
cpu.execute_instruction((load_instruction, OpInput::UseImmediate(127)));
|
||||||
|
|
||||||
|
@ -1563,7 +1656,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn compare_with_a_register_test() {
|
fn compare_with_a_register_test() {
|
||||||
compare_test_helper(
|
compare_test_helper(
|
||||||
&mut |cpu: &mut CPU<Ram>, val: u8| {
|
&mut |cpu: &mut CPU<Ram, Nmos6502>, val: u8| {
|
||||||
cpu.compare_with_a_register(val);
|
cpu.compare_with_a_register(val);
|
||||||
},
|
},
|
||||||
Instruction::LDA,
|
Instruction::LDA,
|
||||||
|
@ -1573,7 +1666,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn compare_with_x_register_test() {
|
fn compare_with_x_register_test() {
|
||||||
compare_test_helper(
|
compare_test_helper(
|
||||||
&mut |cpu: &mut CPU<Ram>, val: u8| {
|
&mut |cpu: &mut CPU<Ram, Nmos6502>, val: u8| {
|
||||||
cpu.compare_with_x_register(val);
|
cpu.compare_with_x_register(val);
|
||||||
},
|
},
|
||||||
Instruction::LDX,
|
Instruction::LDX,
|
||||||
|
@ -1583,7 +1676,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn compare_with_y_register_test() {
|
fn compare_with_y_register_test() {
|
||||||
compare_test_helper(
|
compare_test_helper(
|
||||||
&mut |cpu: &mut CPU<Ram>, val: u8| {
|
&mut |cpu: &mut CPU<Ram, Nmos6502>, val: u8| {
|
||||||
cpu.compare_with_y_register(val);
|
cpu.compare_with_y_register(val);
|
||||||
},
|
},
|
||||||
Instruction::LDY,
|
Instruction::LDY,
|
||||||
|
@ -1592,7 +1685,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn exclusive_or_test() {
|
fn exclusive_or_test() {
|
||||||
let mut cpu = CPU::new(Ram::new());
|
let mut cpu = CPU::new(Ram::new(), Nmos6502);
|
||||||
|
|
||||||
for a_before in 0u8..=255u8 {
|
for a_before in 0u8..=255u8 {
|
||||||
for val in 0u8..=255u8 {
|
for val in 0u8..=255u8 {
|
||||||
|
@ -1620,7 +1713,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn inclusive_or_test() {
|
fn inclusive_or_test() {
|
||||||
let mut cpu = CPU::new(Ram::new());
|
let mut cpu = CPU::new(Ram::new(), Nmos6502);
|
||||||
|
|
||||||
for a_before in 0u8..=255u8 {
|
for a_before in 0u8..=255u8 {
|
||||||
for val in 0u8..=255u8 {
|
for val in 0u8..=255u8 {
|
||||||
|
@ -1648,7 +1741,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn stack_underflow() {
|
fn stack_underflow() {
|
||||||
let mut cpu = CPU::new(Ram::new());
|
let mut cpu = CPU::new(Ram::new(), Nmos6502);
|
||||||
let _val: u8 = cpu.pull_from_stack();
|
let _val: u8 = cpu.pull_from_stack();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,39 +44,40 @@
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum Instruction {
|
pub enum Instruction {
|
||||||
ADC, // ADd with Carry................ | NV ...ZC A = A + M + C
|
ADC, // ADd with Carry................ | NV ...ZC A = A + M + C
|
||||||
AND, // logical AND (bitwise)......... | N. ...Z. A = A && M
|
ADCnd, // ADd with Carry................ | NV ...ZC A = A + M + C
|
||||||
ASL, // Arithmetic Shift Left......... | N. ...ZC A = M << 1
|
AND, // logical AND (bitwise)......... | N. ...Z. A = A && M
|
||||||
BCC, // Branch if Carry Clear......... | .. ..... PC = !C
|
ASL, // Arithmetic Shift Left......... | N. ...ZC A = M << 1
|
||||||
BCS, // Branch if Carry Set........... | .. ..... PC = C
|
BCC, // Branch if Carry Clear......... | .. ..... PC = !C
|
||||||
BEQ, // Branch if Equal (to zero?).... | .. ..... PC = Z
|
BCS, // Branch if Carry Set........... | .. ..... PC = C
|
||||||
BIT, // BIT test...................... | NV ...Z. = A & M
|
BEQ, // Branch if Equal (to zero?).... | .. ..... PC = Z
|
||||||
BMI, // Branch if Minus............... | .. ..... PC = N
|
BIT, // BIT test...................... | NV ...Z. = A & M
|
||||||
BNE, // Branch if Not Equal........... | .. ..... PC = !Z
|
BMI, // Branch if Minus............... | .. ..... PC = N
|
||||||
BPL, // Branch if Positive............ | .. ..... PC = Z
|
BNE, // Branch if Not Equal........... | .. ..... PC = !Z
|
||||||
BRK, // BReaK......................... | .. B.... S PC =
|
BPL, // Branch if Positive............ | .. ..... PC = Z
|
||||||
BVC, // Branch if oVerflow Clear...... | .. ..... PC = !V
|
BRK, // BReaK......................... | .. B.... S PC =
|
||||||
BVS, // Branch if oVerflow Set........ | .. ..... PC = V
|
BVC, // Branch if oVerflow Clear...... | .. ..... PC = !V
|
||||||
CLC, // CLear Carry flag.............. | .. ....C = 0
|
BVS, // Branch if oVerflow Set........ | .. ..... PC = V
|
||||||
CLD, // Clear Decimal Mode............ | .. .D... = 0
|
CLC, // CLear Carry flag.............. | .. ....C = 0
|
||||||
CLI, // Clear Interrupt Disable....... | .. ..I.. = 0
|
CLD, // Clear Decimal Mode............ | .. .D... = 0
|
||||||
CLV, // Clear oVerflow flag........... | .V ..... = 0
|
CLI, // Clear Interrupt Disable....... | .. ..I.. = 0
|
||||||
CMP, // Compare....................... | N. ...ZC = A - M
|
CLV, // Clear oVerflow flag........... | .V ..... = 0
|
||||||
CPX, // Compare X register............ | N. ...ZC = X - M
|
CMP, // Compare....................... | N. ...ZC = A - M
|
||||||
CPY, // Compare Y register............ | N. ...ZC = Y - M
|
CPX, // Compare X register............ | N. ...ZC = X - M
|
||||||
DEC, // DECrement memory.............. | N. ...Z. M = M - 1
|
CPY, // Compare Y register............ | N. ...ZC = Y - M
|
||||||
DEX, // DEcrement X register.......... | N. ...Z. X = X - 1
|
DEC, // DECrement memory.............. | N. ...Z. M = M - 1
|
||||||
DEY, // DEcrement Y register.......... | N. ...Z. Y = Y - 1
|
DEX, // DEcrement X register.......... | N. ...Z. X = X - 1
|
||||||
EOR, // Exclusive OR (bitwise)........ | N. ...Z. A = A ^ M
|
DEY, // DEcrement Y register.......... | N. ...Z. Y = Y - 1
|
||||||
INC, // INCrement memory.............. | N. ...Z. M = M + 1
|
EOR, // Exclusive OR (bitwise)........ | N. ...Z. A = A ^ M
|
||||||
INX, // INcrement X register.......... | N. ...Z. X = X + 1
|
INC, // INCrement memory.............. | N. ...Z. M = M + 1
|
||||||
INY, // INcrement Y register.......... | N. ...Z. Y = Y + 1
|
INX, // INcrement X register.......... | N. ...Z. X = X + 1
|
||||||
JMP, // JuMP.......................... | .. ..... S PC =
|
INY, // INcrement Y register.......... | N. ...Z. Y = Y + 1
|
||||||
JSR, // Jump to SubRoutine............ | .. ..... S PC =
|
JMP, // JuMP.......................... | .. ..... S PC =
|
||||||
LDA, // LoaD Accumulator.............. | N. ...Z. A = M
|
JSR, // Jump to SubRoutine............ | .. ..... S PC =
|
||||||
LDX, // LoaD X register............... | N. ...Z. X = M
|
LDA, // LoaD Accumulator.............. | N. ...Z. A = M
|
||||||
LDY, // LoaD Y register............... | N. ...Z. Y = M
|
LDX, // LoaD X register............... | N. ...Z. X = M
|
||||||
LSR, // Logical Shift Right........... | N. ...ZC A = A/2
|
LDY, // LoaD Y register............... | N. ...Z. Y = M
|
||||||
|
LSR, // Logical Shift Right........... | N. ...ZC A = A/2
|
||||||
// or N. ...ZC M = M/2
|
// or N. ...ZC M = M/2
|
||||||
NOP, // No OPeration.................. | .. ..... =
|
NOP, // No OPeration.................. | .. ..... =
|
||||||
ORA, // inclusive OR (bitwise)........ | N. ...Z. A = A | M
|
ORA, // inclusive OR (bitwise)........ | N. ...Z. A = A | M
|
||||||
|
@ -88,21 +89,22 @@ pub enum Instruction {
|
||||||
// or N. ...ZC M = C M rotated
|
// or N. ...ZC M = C M rotated
|
||||||
ROR, // ROtate Right.................. | N. ...ZC A = C A rotated
|
ROR, // ROtate Right.................. | N. ...ZC A = C A rotated
|
||||||
// or N. ...ZC M = C M rotated
|
// or N. ...ZC M = C M rotated
|
||||||
RTI, // ReTurn from Interrupt......... | NV BDIZC PC = M (stack)
|
RTI, // ReTurn from Interrupt......... | NV BDIZC PC = M (stack)
|
||||||
RTS, // ReTurn from Subroutine........ | .. ..... PC = M (stack)
|
RTS, // ReTurn from Subroutine........ | .. ..... PC = M (stack)
|
||||||
SBC, // SuBtract with Carry........... | NV ...ZC A = A-M-(1-C)
|
SBC, // SuBtract with Carry........... | NV ...ZC A = A-M-(1-C)
|
||||||
SEC, // SEt Carry flag................ | .. ....C = 1
|
SBCnd, // SuBtract with Carry........... | NV ...ZC A = A-M-(1-C)
|
||||||
SED, // SEt Decimal flag.............. | .. .D... = 1
|
SEC, // SEt Carry flag................ | .. ....C = 1
|
||||||
SEI, // SEt Interrupt disable......... | .. ..I.. = 1
|
SED, // SEt Decimal flag.............. | .. .D... = 1
|
||||||
STA, // STore Accumulator............. | .. ..... M = A
|
SEI, // SEt Interrupt disable......... | .. ..I.. = 1
|
||||||
STX, // STore X register.............. | .. ..... M = X
|
STA, // STore Accumulator............. | .. ..... M = A
|
||||||
STY, // STore Y register.............. | .. ..... M = Y
|
STX, // STore X register.............. | .. ..... M = X
|
||||||
TAX, // Transfer Accumulator to X..... | N. ...Z. X = A
|
STY, // STore Y register.............. | .. ..... M = Y
|
||||||
TAY, // Transfer Accumulator to Y..... | N. ...Z. Y = A
|
TAX, // Transfer Accumulator to X..... | N. ...Z. X = A
|
||||||
TSX, // Transfer Stack pointer to X... | N. ...Z. X = S
|
TAY, // Transfer Accumulator to Y..... | N. ...Z. Y = A
|
||||||
TXA, // Transfer X to Accumulator..... | N. ...Z. A = X
|
TSX, // Transfer Stack pointer to X... | N. ...Z. X = S
|
||||||
TXS, // Transfer X to Stack pointer... | .. ..... S = X
|
TXA, // Transfer X to Accumulator..... | N. ...Z. A = X
|
||||||
TYA, // Transfer Y to Accumulator..... | N. ...Z. A = Y
|
TXS, // Transfer X to Stack pointer... | .. ..... S = X
|
||||||
|
TYA, // Transfer Y to Accumulator..... | N. ...Z. A = Y
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
|
@ -154,517 +156,313 @@ impl AddressingMode {
|
||||||
|
|
||||||
pub type DecodedInstr = (Instruction, OpInput);
|
pub type DecodedInstr = (Instruction, OpInput);
|
||||||
|
|
||||||
pub static OPCODES: [Option<(Instruction, AddressingMode)>; 256] = [
|
/// The NMOS 6502 variant. This one is present in the Commodore 64, early Apple IIs, etc.
|
||||||
/*0x00*/
|
pub struct Nmos6502;
|
||||||
Some((Instruction::BRK, AddressingMode::Implied)),
|
|
||||||
/*0x01*/
|
impl crate::Variant for Nmos6502 {
|
||||||
Some((Instruction::ORA, AddressingMode::IndexedIndirectX)),
|
fn decode(opcode: u8) -> Option<(Instruction, AddressingMode)> {
|
||||||
/*0x02*/
|
match opcode {
|
||||||
None,
|
0x00 => Some((Instruction::BRK, AddressingMode::Implied)),
|
||||||
/*0x03*/
|
0x01 => Some((Instruction::ORA, AddressingMode::IndexedIndirectX)),
|
||||||
None,
|
0x02 => None,
|
||||||
/*0x04*/
|
0x03 => None,
|
||||||
None,
|
0x04 => None,
|
||||||
/*0x05*/
|
0x05 => Some((Instruction::ORA, AddressingMode::ZeroPage)),
|
||||||
Some((Instruction::ORA, AddressingMode::ZeroPage)),
|
0x06 => Some((Instruction::ASL, AddressingMode::ZeroPage)),
|
||||||
/*0x06*/
|
0x07 => None,
|
||||||
Some((Instruction::ASL, AddressingMode::ZeroPage)),
|
0x08 => Some((Instruction::PHP, AddressingMode::Implied)),
|
||||||
/*0x07*/
|
0x09 => Some((Instruction::ORA, AddressingMode::Immediate)),
|
||||||
None,
|
0x0a => Some((Instruction::ASL, AddressingMode::Accumulator)),
|
||||||
/*0x08*/
|
0x0b => None,
|
||||||
Some((Instruction::PHP, AddressingMode::Implied)),
|
0x0c => None,
|
||||||
/*0x09*/
|
0x0d => Some((Instruction::ORA, AddressingMode::Absolute)),
|
||||||
Some((Instruction::ORA, AddressingMode::Immediate)),
|
0x0e => Some((Instruction::ASL, AddressingMode::Absolute)),
|
||||||
/*0x0A*/
|
0x0f => None,
|
||||||
Some((Instruction::ASL, AddressingMode::Accumulator)),
|
0x10 => Some((Instruction::BPL, AddressingMode::Relative)),
|
||||||
/*0x0B*/
|
0x11 => Some((Instruction::ORA, AddressingMode::IndirectIndexedY)),
|
||||||
None,
|
0x12 => None,
|
||||||
/*0x0C*/
|
0x13 => None,
|
||||||
None,
|
0x14 => None,
|
||||||
/*0x0D*/
|
0x15 => Some((Instruction::ORA, AddressingMode::ZeroPageX)),
|
||||||
Some((Instruction::ORA, AddressingMode::Absolute)),
|
0x16 => Some((Instruction::ASL, AddressingMode::ZeroPageX)),
|
||||||
/*0x0E*/
|
0x17 => None,
|
||||||
Some((Instruction::ASL, AddressingMode::Absolute)),
|
0x18 => Some((Instruction::CLC, AddressingMode::Implied)),
|
||||||
/*0x0F*/
|
0x19 => Some((Instruction::ORA, AddressingMode::AbsoluteY)),
|
||||||
None,
|
0x1a => None,
|
||||||
/*0x10*/
|
0x1b => None,
|
||||||
Some((Instruction::BPL, AddressingMode::Relative)),
|
0x1c => None,
|
||||||
/*0x11*/
|
0x1d => Some((Instruction::ORA, AddressingMode::AbsoluteX)),
|
||||||
Some((Instruction::ORA, AddressingMode::IndirectIndexedY)),
|
0x1e => Some((Instruction::ASL, AddressingMode::AbsoluteX)),
|
||||||
/*0x12*/
|
0x1f => None,
|
||||||
None,
|
0x20 => Some((Instruction::JSR, AddressingMode::Absolute)),
|
||||||
/*0x13*/
|
0x21 => Some((Instruction::AND, AddressingMode::IndexedIndirectX)),
|
||||||
None,
|
0x22 => None,
|
||||||
/*0x14*/
|
0x23 => None,
|
||||||
None,
|
0x24 => Some((Instruction::BIT, AddressingMode::ZeroPage)),
|
||||||
/*0x15*/
|
0x25 => Some((Instruction::AND, AddressingMode::ZeroPage)),
|
||||||
Some((Instruction::ORA, AddressingMode::ZeroPageX)),
|
0x26 => Some((Instruction::ROL, AddressingMode::ZeroPage)),
|
||||||
/*0x16*/
|
0x27 => None,
|
||||||
Some((Instruction::ASL, AddressingMode::ZeroPageX)),
|
0x28 => Some((Instruction::PLP, AddressingMode::Implied)),
|
||||||
/*0x17*/
|
0x29 => Some((Instruction::AND, AddressingMode::Immediate)),
|
||||||
None,
|
0x2a => Some((Instruction::ROL, AddressingMode::Accumulator)),
|
||||||
/*0x18*/
|
0x2b => None,
|
||||||
Some((Instruction::CLC, AddressingMode::Implied)),
|
0x2c => Some((Instruction::BIT, AddressingMode::Absolute)),
|
||||||
/*0x19*/
|
0x2d => Some((Instruction::AND, AddressingMode::Absolute)),
|
||||||
Some((Instruction::ORA, AddressingMode::AbsoluteY)),
|
0x2e => Some((Instruction::ROL, AddressingMode::Absolute)),
|
||||||
/*0x1A*/
|
0x2f => None,
|
||||||
None,
|
0x30 => Some((Instruction::BMI, AddressingMode::Relative)),
|
||||||
/*0x1B*/
|
0x31 => Some((Instruction::AND, AddressingMode::IndirectIndexedY)),
|
||||||
None,
|
0x32 => None,
|
||||||
/*0x1C*/
|
0x33 => None,
|
||||||
None,
|
0x34 => None,
|
||||||
/*0x1D*/
|
0x35 => Some((Instruction::AND, AddressingMode::ZeroPageX)),
|
||||||
Some((Instruction::ORA, AddressingMode::AbsoluteX)),
|
0x36 => Some((Instruction::ROL, AddressingMode::ZeroPageX)),
|
||||||
/*0x1E*/
|
0x37 => None,
|
||||||
Some((Instruction::ASL, AddressingMode::AbsoluteX)),
|
0x38 => Some((Instruction::SEC, AddressingMode::Implied)),
|
||||||
/*0x1F*/
|
0x39 => Some((Instruction::AND, AddressingMode::AbsoluteY)),
|
||||||
None,
|
0x3a => None,
|
||||||
/*0x20*/
|
0x3b => None,
|
||||||
Some((Instruction::JSR, AddressingMode::Absolute)),
|
0x3c => None,
|
||||||
/*0x21*/
|
0x3d => Some((Instruction::AND, AddressingMode::AbsoluteX)),
|
||||||
Some((Instruction::AND, AddressingMode::IndexedIndirectX)),
|
0x3e => Some((Instruction::ROL, AddressingMode::AbsoluteX)),
|
||||||
/*0x22*/
|
0x3f => None,
|
||||||
None,
|
0x40 => Some((Instruction::RTI, AddressingMode::Implied)),
|
||||||
/*0x23*/
|
0x41 => Some((Instruction::EOR, AddressingMode::IndexedIndirectX)),
|
||||||
None,
|
0x42 => None,
|
||||||
/*0x24*/
|
0x43 => None,
|
||||||
Some((Instruction::BIT, AddressingMode::ZeroPage)),
|
0x44 => None,
|
||||||
/*0x25*/
|
0x45 => Some((Instruction::EOR, AddressingMode::ZeroPage)),
|
||||||
Some((Instruction::AND, AddressingMode::ZeroPage)),
|
0x46 => Some((Instruction::LSR, AddressingMode::ZeroPage)),
|
||||||
/*0x26*/
|
0x47 => None,
|
||||||
Some((Instruction::ROL, AddressingMode::ZeroPage)),
|
0x48 => Some((Instruction::PHA, AddressingMode::Implied)),
|
||||||
/*0x27*/
|
0x49 => Some((Instruction::EOR, AddressingMode::Immediate)),
|
||||||
None,
|
0x4a => Some((Instruction::LSR, AddressingMode::Accumulator)),
|
||||||
/*0x28*/
|
0x4b => None,
|
||||||
Some((Instruction::PLP, AddressingMode::Implied)),
|
0x4c => Some((Instruction::JMP, AddressingMode::Absolute)),
|
||||||
/*0x29*/
|
0x4d => Some((Instruction::EOR, AddressingMode::Absolute)),
|
||||||
Some((Instruction::AND, AddressingMode::Immediate)),
|
0x4e => Some((Instruction::LSR, AddressingMode::Absolute)),
|
||||||
/*0x2A*/
|
0x4f => None,
|
||||||
Some((Instruction::ROL, AddressingMode::Accumulator)),
|
0x50 => Some((Instruction::BVC, AddressingMode::Relative)),
|
||||||
/*0x2B*/
|
0x51 => Some((Instruction::EOR, AddressingMode::IndirectIndexedY)),
|
||||||
None,
|
0x52 => None,
|
||||||
/*0x2C*/
|
0x53 => None,
|
||||||
Some((Instruction::BIT, AddressingMode::Absolute)),
|
0x54 => None,
|
||||||
/*0x2D*/
|
0x55 => Some((Instruction::EOR, AddressingMode::ZeroPageX)),
|
||||||
Some((Instruction::AND, AddressingMode::Absolute)),
|
0x56 => Some((Instruction::LSR, AddressingMode::ZeroPageX)),
|
||||||
/*0x2E*/
|
0x57 => None,
|
||||||
Some((Instruction::ROL, AddressingMode::Absolute)),
|
0x58 => Some((Instruction::CLI, AddressingMode::Implied)),
|
||||||
/*0x2F*/
|
0x59 => Some((Instruction::EOR, AddressingMode::AbsoluteY)),
|
||||||
None,
|
0x5a => None,
|
||||||
/*0x30*/
|
0x5b => None,
|
||||||
Some((Instruction::BMI, AddressingMode::Relative)),
|
0x5c => None,
|
||||||
/*0x31*/
|
0x5d => Some((Instruction::EOR, AddressingMode::AbsoluteX)),
|
||||||
Some((Instruction::AND, AddressingMode::IndirectIndexedY)),
|
0x5e => Some((Instruction::LSR, AddressingMode::AbsoluteX)),
|
||||||
/*0x32*/
|
0x5f => None,
|
||||||
None,
|
0x60 => Some((Instruction::RTS, AddressingMode::Implied)),
|
||||||
/*0x33*/
|
0x61 => Some((Instruction::ADC, AddressingMode::IndexedIndirectX)),
|
||||||
None,
|
0x62 => None,
|
||||||
/*0x34*/
|
0x63 => None,
|
||||||
None,
|
0x64 => None,
|
||||||
/*0x35*/
|
0x65 => Some((Instruction::ADC, AddressingMode::ZeroPage)),
|
||||||
Some((Instruction::AND, AddressingMode::ZeroPageX)),
|
0x66 => Some((Instruction::ROR, AddressingMode::ZeroPage)),
|
||||||
/*0x36*/
|
0x67 => None,
|
||||||
Some((Instruction::ROL, AddressingMode::ZeroPageX)),
|
0x68 => Some((Instruction::PLA, AddressingMode::Implied)),
|
||||||
/*0x37*/
|
0x69 => Some((Instruction::ADC, AddressingMode::Immediate)),
|
||||||
None,
|
0x6a => Some((Instruction::ROR, AddressingMode::Accumulator)),
|
||||||
/*0x38*/
|
0x6b => None,
|
||||||
Some((Instruction::SEC, AddressingMode::Implied)),
|
0x6c => Some((Instruction::JMP, AddressingMode::Indirect)),
|
||||||
/*0x39*/
|
0x6d => Some((Instruction::ADC, AddressingMode::Absolute)),
|
||||||
Some((Instruction::AND, AddressingMode::AbsoluteY)),
|
0x6e => Some((Instruction::ROR, AddressingMode::Absolute)),
|
||||||
/*0x3A*/
|
0x6f => None,
|
||||||
None,
|
0x70 => Some((Instruction::BVS, AddressingMode::Relative)),
|
||||||
/*0x3B*/
|
0x71 => Some((Instruction::ADC, AddressingMode::IndirectIndexedY)),
|
||||||
None,
|
0x72 => None,
|
||||||
/*0x3C*/
|
0x73 => None,
|
||||||
None,
|
0x74 => None,
|
||||||
/*0x3D*/
|
0x75 => Some((Instruction::ADC, AddressingMode::ZeroPageX)),
|
||||||
Some((Instruction::AND, AddressingMode::AbsoluteX)),
|
0x76 => Some((Instruction::ROR, AddressingMode::ZeroPageX)),
|
||||||
/*0x3E*/
|
0x77 => None,
|
||||||
Some((Instruction::ROL, AddressingMode::AbsoluteX)),
|
0x78 => Some((Instruction::SEI, AddressingMode::Implied)),
|
||||||
/*0x3F*/
|
0x79 => Some((Instruction::ADC, AddressingMode::AbsoluteY)),
|
||||||
None,
|
0x7a => None,
|
||||||
/*0x40*/
|
0x7b => None,
|
||||||
Some((Instruction::RTI, AddressingMode::Implied)),
|
0x7c => None,
|
||||||
/*0x41*/
|
0x7d => Some((Instruction::ADC, AddressingMode::AbsoluteX)),
|
||||||
Some((Instruction::EOR, AddressingMode::IndexedIndirectX)),
|
0x7e => Some((Instruction::ROR, AddressingMode::AbsoluteX)),
|
||||||
/*0x42*/
|
0x7f => None,
|
||||||
None,
|
0x80 => None,
|
||||||
/*0x43*/
|
0x81 => Some((Instruction::STA, AddressingMode::IndexedIndirectX)),
|
||||||
None,
|
0x82 => None,
|
||||||
/*0x44*/
|
0x83 => None,
|
||||||
None,
|
0x84 => Some((Instruction::STY, AddressingMode::ZeroPage)),
|
||||||
/*0x45*/
|
0x85 => Some((Instruction::STA, AddressingMode::ZeroPage)),
|
||||||
Some((Instruction::EOR, AddressingMode::ZeroPage)),
|
0x86 => Some((Instruction::STX, AddressingMode::ZeroPage)),
|
||||||
/*0x46*/
|
0x87 => None,
|
||||||
Some((Instruction::LSR, AddressingMode::ZeroPage)),
|
0x88 => Some((Instruction::DEY, AddressingMode::Implied)),
|
||||||
/*0x47*/
|
0x89 => None,
|
||||||
None,
|
0x8a => Some((Instruction::TXA, AddressingMode::Implied)),
|
||||||
/*0x48*/
|
0x8b => None,
|
||||||
Some((Instruction::PHA, AddressingMode::Implied)),
|
0x8c => Some((Instruction::STY, AddressingMode::Absolute)),
|
||||||
/*0x49*/
|
0x8d => Some((Instruction::STA, AddressingMode::Absolute)),
|
||||||
Some((Instruction::EOR, AddressingMode::Immediate)),
|
0x8e => Some((Instruction::STX, AddressingMode::Absolute)),
|
||||||
/*0x4A*/
|
0x8f => None,
|
||||||
Some((Instruction::LSR, AddressingMode::Accumulator)),
|
0x90 => Some((Instruction::BCC, AddressingMode::Relative)),
|
||||||
/*0x4B*/
|
0x91 => Some((Instruction::STA, AddressingMode::IndirectIndexedY)),
|
||||||
None,
|
0x92 => None,
|
||||||
/*0x4C*/
|
0x93 => None,
|
||||||
Some((Instruction::JMP, AddressingMode::Absolute)),
|
0x94 => Some((Instruction::STY, AddressingMode::ZeroPageX)),
|
||||||
/*0x4D*/
|
0x95 => Some((Instruction::STA, AddressingMode::ZeroPageX)),
|
||||||
Some((Instruction::EOR, AddressingMode::Absolute)),
|
0x96 => Some((Instruction::STX, AddressingMode::ZeroPageY)),
|
||||||
/*0x4E*/
|
0x97 => None,
|
||||||
Some((Instruction::LSR, AddressingMode::Absolute)),
|
0x98 => Some((Instruction::TYA, AddressingMode::Implied)),
|
||||||
/*0x4F*/
|
0x99 => Some((Instruction::STA, AddressingMode::AbsoluteY)),
|
||||||
None,
|
0x9a => Some((Instruction::TXS, AddressingMode::Implied)),
|
||||||
/*0x50*/
|
0x9b => None,
|
||||||
Some((Instruction::BVC, AddressingMode::Relative)),
|
0x9c => None,
|
||||||
/*0x51*/
|
0x9d => Some((Instruction::STA, AddressingMode::AbsoluteX)),
|
||||||
Some((Instruction::EOR, AddressingMode::IndirectIndexedY)),
|
0x9e => None,
|
||||||
/*0x52*/
|
0x9f => None,
|
||||||
None,
|
0xa0 => Some((Instruction::LDY, AddressingMode::Immediate)),
|
||||||
/*0x53*/
|
0xa1 => Some((Instruction::LDA, AddressingMode::IndexedIndirectX)),
|
||||||
None,
|
0xa2 => Some((Instruction::LDX, AddressingMode::Immediate)),
|
||||||
/*0x54*/
|
0xa3 => None,
|
||||||
None,
|
0xa4 => Some((Instruction::LDY, AddressingMode::ZeroPage)),
|
||||||
/*0x55*/
|
0xa5 => Some((Instruction::LDA, AddressingMode::ZeroPage)),
|
||||||
Some((Instruction::EOR, AddressingMode::ZeroPageX)),
|
0xa6 => Some((Instruction::LDX, AddressingMode::ZeroPage)),
|
||||||
/*0x56*/
|
0xa7 => None,
|
||||||
Some((Instruction::LSR, AddressingMode::ZeroPageX)),
|
0xa8 => Some((Instruction::TAY, AddressingMode::Implied)),
|
||||||
/*0x57*/
|
0xa9 => Some((Instruction::LDA, AddressingMode::Immediate)),
|
||||||
None,
|
0xaa => Some((Instruction::TAX, AddressingMode::Implied)),
|
||||||
/*0x58*/
|
0xab => None,
|
||||||
Some((Instruction::CLI, AddressingMode::Implied)),
|
0xac => Some((Instruction::LDY, AddressingMode::Absolute)),
|
||||||
/*0x59*/
|
0xad => Some((Instruction::LDA, AddressingMode::Absolute)),
|
||||||
Some((Instruction::EOR, AddressingMode::AbsoluteY)),
|
0xae => Some((Instruction::LDX, AddressingMode::Absolute)),
|
||||||
/*0x5A*/
|
0xaf => None,
|
||||||
None,
|
0xb0 => Some((Instruction::BCS, AddressingMode::Relative)),
|
||||||
/*0x5B*/
|
0xb1 => Some((Instruction::LDA, AddressingMode::IndirectIndexedY)),
|
||||||
None,
|
0xb2 => None,
|
||||||
/*0x5C*/
|
0xb3 => None,
|
||||||
None,
|
0xb4 => Some((Instruction::LDY, AddressingMode::ZeroPageX)),
|
||||||
/*0x5D*/
|
0xb5 => Some((Instruction::LDA, AddressingMode::ZeroPageX)),
|
||||||
Some((Instruction::EOR, AddressingMode::AbsoluteX)),
|
0xb6 => Some((Instruction::LDX, AddressingMode::ZeroPageY)),
|
||||||
/*0x5E*/
|
0xb7 => None,
|
||||||
Some((Instruction::LSR, AddressingMode::AbsoluteX)),
|
0xb8 => Some((Instruction::CLV, AddressingMode::Implied)),
|
||||||
/*0x5F*/
|
0xb9 => Some((Instruction::LDA, AddressingMode::AbsoluteY)),
|
||||||
None,
|
0xba => Some((Instruction::TSX, AddressingMode::Implied)),
|
||||||
/*0x60*/
|
0xbb => None,
|
||||||
Some((Instruction::RTS, AddressingMode::Implied)),
|
0xbc => Some((Instruction::LDY, AddressingMode::AbsoluteX)),
|
||||||
/*0x61*/
|
0xbd => Some((Instruction::LDA, AddressingMode::AbsoluteX)),
|
||||||
Some((Instruction::ADC, AddressingMode::IndexedIndirectX)),
|
0xbe => Some((Instruction::LDX, AddressingMode::AbsoluteY)),
|
||||||
/*0x62*/
|
0xbf => None,
|
||||||
None,
|
0xc0 => Some((Instruction::CPY, AddressingMode::Immediate)),
|
||||||
/*0x63*/
|
0xc1 => Some((Instruction::CMP, AddressingMode::IndexedIndirectX)),
|
||||||
None,
|
0xc2 => None,
|
||||||
/*0x64*/
|
0xc3 => None,
|
||||||
None,
|
0xc4 => Some((Instruction::CPY, AddressingMode::ZeroPage)),
|
||||||
/*0x65*/
|
0xc5 => Some((Instruction::CMP, AddressingMode::ZeroPage)),
|
||||||
Some((Instruction::ADC, AddressingMode::ZeroPage)),
|
0xc6 => Some((Instruction::DEC, AddressingMode::ZeroPage)),
|
||||||
/*0x66*/
|
0xc7 => None,
|
||||||
Some((Instruction::ROR, AddressingMode::ZeroPage)),
|
0xc8 => Some((Instruction::INY, AddressingMode::Implied)),
|
||||||
/*0x67*/
|
0xc9 => Some((Instruction::CMP, AddressingMode::Immediate)),
|
||||||
None,
|
0xca => Some((Instruction::DEX, AddressingMode::Implied)),
|
||||||
/*0x68*/
|
0xcb => None,
|
||||||
Some((Instruction::PLA, AddressingMode::Implied)),
|
0xcc => Some((Instruction::CPY, AddressingMode::Absolute)),
|
||||||
/*0x69*/
|
0xcd => Some((Instruction::CMP, AddressingMode::Absolute)),
|
||||||
Some((Instruction::ADC, AddressingMode::Immediate)),
|
0xce => Some((Instruction::DEC, AddressingMode::Absolute)),
|
||||||
/*0x6A*/
|
0xcf => None,
|
||||||
Some((Instruction::ROR, AddressingMode::Accumulator)),
|
0xd0 => Some((Instruction::BNE, AddressingMode::Relative)),
|
||||||
/*0x6B*/
|
0xd1 => Some((Instruction::CMP, AddressingMode::IndirectIndexedY)),
|
||||||
None,
|
0xd2 => None,
|
||||||
/*0x6C*/
|
0xd3 => None,
|
||||||
Some((Instruction::JMP, AddressingMode::Indirect)),
|
0xd4 => None,
|
||||||
/*0x6D*/
|
0xd5 => Some((Instruction::CMP, AddressingMode::ZeroPageX)),
|
||||||
Some((Instruction::ADC, AddressingMode::Absolute)),
|
0xd6 => Some((Instruction::DEC, AddressingMode::ZeroPageX)),
|
||||||
/*0x6E*/
|
0xd7 => None,
|
||||||
Some((Instruction::ROR, AddressingMode::Absolute)),
|
0xd8 => Some((Instruction::CLD, AddressingMode::Implied)),
|
||||||
/*0x6F*/
|
0xd9 => Some((Instruction::CMP, AddressingMode::AbsoluteY)),
|
||||||
None,
|
0xda => None,
|
||||||
/*0x70*/
|
0xdb => None,
|
||||||
Some((Instruction::BVS, AddressingMode::Relative)),
|
0xdc => None,
|
||||||
/*0x71*/
|
0xdd => Some((Instruction::CMP, AddressingMode::AbsoluteX)),
|
||||||
Some((Instruction::ADC, AddressingMode::IndirectIndexedY)),
|
0xde => Some((Instruction::DEC, AddressingMode::AbsoluteX)),
|
||||||
/*0x72*/
|
0xdf => None,
|
||||||
None,
|
0xe0 => Some((Instruction::CPX, AddressingMode::Immediate)),
|
||||||
/*0x73*/
|
0xe1 => Some((Instruction::SBC, AddressingMode::IndexedIndirectX)),
|
||||||
None,
|
0xe2 => None,
|
||||||
/*0x74*/
|
0xe3 => None,
|
||||||
None,
|
0xe4 => Some((Instruction::CPX, AddressingMode::ZeroPage)),
|
||||||
/*0x75*/
|
0xe5 => Some((Instruction::SBC, AddressingMode::ZeroPage)),
|
||||||
Some((Instruction::ADC, AddressingMode::ZeroPageX)),
|
0xe6 => Some((Instruction::INC, AddressingMode::ZeroPage)),
|
||||||
/*0x76*/
|
0xe7 => None,
|
||||||
Some((Instruction::ROR, AddressingMode::ZeroPageX)),
|
0xe8 => Some((Instruction::INX, AddressingMode::Implied)),
|
||||||
/*0x77*/
|
0xe9 => Some((Instruction::SBC, AddressingMode::Immediate)),
|
||||||
None,
|
0xea => Some((Instruction::NOP, AddressingMode::Implied)),
|
||||||
/*0x78*/
|
0xeb => None,
|
||||||
Some((Instruction::SEI, AddressingMode::Implied)),
|
0xec => Some((Instruction::CPX, AddressingMode::Absolute)),
|
||||||
/*0x79*/
|
0xed => Some((Instruction::SBC, AddressingMode::Absolute)),
|
||||||
Some((Instruction::ADC, AddressingMode::AbsoluteY)),
|
0xee => Some((Instruction::INC, AddressingMode::Absolute)),
|
||||||
/*0x7A*/
|
0xef => None,
|
||||||
None,
|
0xf0 => Some((Instruction::BEQ, AddressingMode::Relative)),
|
||||||
/*0x7B*/
|
0xf1 => Some((Instruction::SBC, AddressingMode::IndirectIndexedY)),
|
||||||
None,
|
0xf2 => None,
|
||||||
/*0x7C*/
|
0xf3 => None,
|
||||||
None,
|
0xf4 => None,
|
||||||
/*0x7D*/
|
0xf5 => Some((Instruction::SBC, AddressingMode::ZeroPageX)),
|
||||||
Some((Instruction::ADC, AddressingMode::AbsoluteX)),
|
0xf6 => Some((Instruction::INC, AddressingMode::ZeroPageX)),
|
||||||
/*0x7E*/
|
0xf7 => None,
|
||||||
Some((Instruction::ROR, AddressingMode::AbsoluteX)),
|
0xf8 => Some((Instruction::SED, AddressingMode::Implied)),
|
||||||
/*0x7F*/
|
0xf9 => Some((Instruction::SBC, AddressingMode::AbsoluteY)),
|
||||||
None,
|
0xfa => None,
|
||||||
/*0x80*/
|
0xfb => None,
|
||||||
None,
|
0xfc => None,
|
||||||
/*0x81*/
|
0xfd => Some((Instruction::SBC, AddressingMode::AbsoluteX)),
|
||||||
Some((Instruction::STA, AddressingMode::IndexedIndirectX)),
|
0xfe => Some((Instruction::INC, AddressingMode::AbsoluteX)),
|
||||||
/*0x82*/
|
0xff => None,
|
||||||
None,
|
}
|
||||||
/*0x83*/
|
}
|
||||||
None,
|
}
|
||||||
/*0x84*/
|
|
||||||
Some((Instruction::STY, AddressingMode::ZeroPage)),
|
/// The Ricoh variant which has no decimal mode. This is what to use if you want to emulate the
|
||||||
/*0x85*/
|
/// NES.
|
||||||
Some((Instruction::STA, AddressingMode::ZeroPage)),
|
pub struct Ricoh2a03;
|
||||||
/*0x86*/
|
|
||||||
Some((Instruction::STX, AddressingMode::ZeroPage)),
|
impl crate::Variant for Ricoh2a03 {
|
||||||
/*0x87*/
|
fn decode(opcode: u8) -> Option<(Instruction, AddressingMode)> {
|
||||||
None,
|
match opcode {
|
||||||
/*0x88*/
|
0x61 => Some((Instruction::ADCnd, AddressingMode::IndexedIndirectX)),
|
||||||
Some((Instruction::DEY, AddressingMode::Implied)),
|
0x65 => Some((Instruction::ADCnd, AddressingMode::ZeroPage)),
|
||||||
/*0x89*/
|
0x69 => Some((Instruction::ADCnd, AddressingMode::Immediate)),
|
||||||
None,
|
0x6d => Some((Instruction::ADCnd, AddressingMode::Absolute)),
|
||||||
/*0x8A*/
|
0x71 => Some((Instruction::ADCnd, AddressingMode::IndirectIndexedY)),
|
||||||
Some((Instruction::TXA, AddressingMode::Implied)),
|
0x75 => Some((Instruction::ADCnd, AddressingMode::ZeroPageX)),
|
||||||
/*0x8B*/
|
0x79 => Some((Instruction::ADCnd, AddressingMode::AbsoluteY)),
|
||||||
None,
|
0x7d => Some((Instruction::ADCnd, AddressingMode::AbsoluteX)),
|
||||||
/*0x8C*/
|
0xe1 => Some((Instruction::SBCnd, AddressingMode::IndexedIndirectX)),
|
||||||
Some((Instruction::STY, AddressingMode::Absolute)),
|
0xe5 => Some((Instruction::SBCnd, AddressingMode::ZeroPage)),
|
||||||
/*0x8D*/
|
0xe9 => Some((Instruction::SBCnd, AddressingMode::Immediate)),
|
||||||
Some((Instruction::STA, AddressingMode::Absolute)),
|
0xed => Some((Instruction::SBCnd, AddressingMode::Absolute)),
|
||||||
/*0x8E*/
|
0xf1 => Some((Instruction::SBCnd, AddressingMode::IndirectIndexedY)),
|
||||||
Some((Instruction::STX, AddressingMode::Absolute)),
|
0xf5 => Some((Instruction::SBCnd, AddressingMode::ZeroPageX)),
|
||||||
/*0x8F*/
|
0xf9 => Some((Instruction::SBCnd, AddressingMode::AbsoluteY)),
|
||||||
None,
|
0xfd => Some((Instruction::SBCnd, AddressingMode::AbsoluteX)),
|
||||||
/*0x90*/
|
_ => Nmos6502::decode(opcode),
|
||||||
Some((Instruction::BCC, AddressingMode::Relative)),
|
}
|
||||||
/*0x91*/
|
}
|
||||||
Some((Instruction::STA, AddressingMode::IndirectIndexedY)),
|
}
|
||||||
/*0x92*/
|
|
||||||
None,
|
/// Emulates some very early 6502s which have no ROR instruction. This one is used in very early
|
||||||
/*0x93*/
|
/// KIM-1s.
|
||||||
None,
|
pub struct RevisionA;
|
||||||
/*0x94*/
|
|
||||||
Some((Instruction::STY, AddressingMode::ZeroPageX)),
|
impl crate::Variant for RevisionA {
|
||||||
/*0x95*/
|
fn decode(opcode: u8) -> Option<(Instruction, AddressingMode)> {
|
||||||
Some((Instruction::STA, AddressingMode::ZeroPageX)),
|
match opcode {
|
||||||
/*0x96*/
|
0x66 => None,
|
||||||
Some((Instruction::STX, AddressingMode::ZeroPageY)),
|
0x6a => None,
|
||||||
/*0x97*/
|
0x6e => None,
|
||||||
None,
|
0x76 => None,
|
||||||
/*0x98*/
|
0x7e => None,
|
||||||
Some((Instruction::TYA, AddressingMode::Implied)),
|
_ => Nmos6502::decode(opcode),
|
||||||
/*0x99*/
|
}
|
||||||
Some((Instruction::STA, AddressingMode::AbsoluteY)),
|
}
|
||||||
/*0x9A*/
|
}
|
||||||
Some((Instruction::TXS, AddressingMode::Implied)),
|
|
||||||
/*0x9B*/
|
|
||||||
None,
|
|
||||||
/*0x9C*/
|
|
||||||
None,
|
|
||||||
/*0x9D*/
|
|
||||||
Some((Instruction::STA, AddressingMode::AbsoluteX)),
|
|
||||||
/*0x9E*/
|
|
||||||
None,
|
|
||||||
/*0x9F*/
|
|
||||||
None,
|
|
||||||
/*0xA0*/
|
|
||||||
Some((Instruction::LDY, AddressingMode::Immediate)),
|
|
||||||
/*0xA1*/
|
|
||||||
Some((Instruction::LDA, AddressingMode::IndexedIndirectX)),
|
|
||||||
/*0xA2*/
|
|
||||||
Some((Instruction::LDX, AddressingMode::Immediate)),
|
|
||||||
/*0xA3*/
|
|
||||||
None,
|
|
||||||
/*0xA4*/
|
|
||||||
Some((Instruction::LDY, AddressingMode::ZeroPage)),
|
|
||||||
/*0xA5*/
|
|
||||||
Some((Instruction::LDA, AddressingMode::ZeroPage)),
|
|
||||||
/*0xA6*/
|
|
||||||
Some((Instruction::LDX, AddressingMode::ZeroPage)),
|
|
||||||
/*0xA7*/
|
|
||||||
None,
|
|
||||||
/*0xA8*/
|
|
||||||
Some((Instruction::TAY, AddressingMode::Implied)),
|
|
||||||
/*0xA9*/
|
|
||||||
Some((Instruction::LDA, AddressingMode::Immediate)),
|
|
||||||
/*0xAA*/
|
|
||||||
Some((Instruction::TAX, AddressingMode::Implied)),
|
|
||||||
/*0xAB*/
|
|
||||||
None,
|
|
||||||
/*0xAC*/
|
|
||||||
Some((Instruction::LDY, AddressingMode::Absolute)),
|
|
||||||
/*0xAD*/
|
|
||||||
Some((Instruction::LDA, AddressingMode::Absolute)),
|
|
||||||
/*0xAE*/
|
|
||||||
Some((Instruction::LDX, AddressingMode::Absolute)),
|
|
||||||
/*0xAF*/
|
|
||||||
None,
|
|
||||||
/*0xB0*/
|
|
||||||
Some((Instruction::BCS, AddressingMode::Relative)),
|
|
||||||
/*0xB1*/
|
|
||||||
Some((Instruction::LDA, AddressingMode::IndirectIndexedY)),
|
|
||||||
/*0xB2*/
|
|
||||||
None,
|
|
||||||
/*0xB3*/
|
|
||||||
None,
|
|
||||||
/*0xB4*/
|
|
||||||
Some((Instruction::LDY, AddressingMode::ZeroPageX)),
|
|
||||||
/*0xB5*/
|
|
||||||
Some((Instruction::LDA, AddressingMode::ZeroPageX)),
|
|
||||||
/*0xB6*/
|
|
||||||
Some((Instruction::LDX, AddressingMode::ZeroPageY)),
|
|
||||||
/*0xB7*/
|
|
||||||
None,
|
|
||||||
/*0xB8*/
|
|
||||||
Some((Instruction::CLV, AddressingMode::Implied)),
|
|
||||||
/*0xB9*/
|
|
||||||
Some((Instruction::LDA, AddressingMode::AbsoluteY)),
|
|
||||||
/*0xBA*/
|
|
||||||
Some((Instruction::TSX, AddressingMode::Implied)),
|
|
||||||
/*0xBB*/
|
|
||||||
None,
|
|
||||||
/*0xBC*/
|
|
||||||
Some((Instruction::LDY, AddressingMode::AbsoluteX)),
|
|
||||||
/*0xBD*/
|
|
||||||
Some((Instruction::LDA, AddressingMode::AbsoluteX)),
|
|
||||||
/*0xBE*/
|
|
||||||
Some((Instruction::LDX, AddressingMode::AbsoluteY)),
|
|
||||||
/*0xBF*/
|
|
||||||
None,
|
|
||||||
/*0xC0*/
|
|
||||||
Some((Instruction::CPY, AddressingMode::Immediate)),
|
|
||||||
/*0xC1*/
|
|
||||||
Some((Instruction::CMP, AddressingMode::IndexedIndirectX)),
|
|
||||||
/*0xC2*/
|
|
||||||
None,
|
|
||||||
/*0xC3*/
|
|
||||||
None,
|
|
||||||
/*0xC4*/
|
|
||||||
Some((Instruction::CPY, AddressingMode::ZeroPage)),
|
|
||||||
/*0xC5*/
|
|
||||||
Some((Instruction::CMP, AddressingMode::ZeroPage)),
|
|
||||||
/*0xC6*/
|
|
||||||
Some((Instruction::DEC, AddressingMode::ZeroPage)),
|
|
||||||
/*0xC7*/
|
|
||||||
None,
|
|
||||||
/*0xC8*/
|
|
||||||
Some((Instruction::INY, AddressingMode::Implied)),
|
|
||||||
/*0xC9*/
|
|
||||||
Some((Instruction::CMP, AddressingMode::Immediate)),
|
|
||||||
/*0xCA*/
|
|
||||||
Some((Instruction::DEX, AddressingMode::Implied)),
|
|
||||||
/*0xCB*/
|
|
||||||
None,
|
|
||||||
/*0xCC*/
|
|
||||||
Some((Instruction::CPY, AddressingMode::Absolute)),
|
|
||||||
/*0xCD*/
|
|
||||||
Some((Instruction::CMP, AddressingMode::Absolute)),
|
|
||||||
/*0xCE*/
|
|
||||||
Some((Instruction::DEC, AddressingMode::Absolute)),
|
|
||||||
/*0xCF*/
|
|
||||||
None,
|
|
||||||
/*0xD0*/
|
|
||||||
Some((Instruction::BNE, AddressingMode::Relative)),
|
|
||||||
/*0xD1*/
|
|
||||||
Some((Instruction::CMP, AddressingMode::IndirectIndexedY)),
|
|
||||||
/*0xD2*/
|
|
||||||
None,
|
|
||||||
/*0xD3*/
|
|
||||||
None,
|
|
||||||
/*0xD4*/
|
|
||||||
None,
|
|
||||||
/*0xD5*/
|
|
||||||
Some((Instruction::CMP, AddressingMode::ZeroPageX)),
|
|
||||||
/*0xD6*/
|
|
||||||
Some((Instruction::DEC, AddressingMode::ZeroPageX)),
|
|
||||||
/*0xD7*/
|
|
||||||
None,
|
|
||||||
/*0xD8*/
|
|
||||||
Some((Instruction::CLD, AddressingMode::Implied)),
|
|
||||||
/*0xD9*/
|
|
||||||
Some((Instruction::CMP, AddressingMode::AbsoluteY)),
|
|
||||||
/*0xDA*/
|
|
||||||
None,
|
|
||||||
/*0xDB*/
|
|
||||||
None,
|
|
||||||
/*0xDC*/
|
|
||||||
None,
|
|
||||||
/*0xDD*/
|
|
||||||
Some((Instruction::CMP, AddressingMode::AbsoluteX)),
|
|
||||||
/*0xDE*/
|
|
||||||
Some((Instruction::DEC, AddressingMode::AbsoluteX)),
|
|
||||||
/*0xDF*/
|
|
||||||
None,
|
|
||||||
/*0xE0*/
|
|
||||||
Some((Instruction::CPX, AddressingMode::Immediate)),
|
|
||||||
/*0xE1*/
|
|
||||||
Some((Instruction::SBC, AddressingMode::IndexedIndirectX)),
|
|
||||||
/*0xE2*/
|
|
||||||
None,
|
|
||||||
/*0xE3*/
|
|
||||||
None,
|
|
||||||
/*0xE4*/
|
|
||||||
Some((Instruction::CPX, AddressingMode::ZeroPage)),
|
|
||||||
/*0xE5*/
|
|
||||||
Some((Instruction::SBC, AddressingMode::ZeroPage)),
|
|
||||||
/*0xE6*/
|
|
||||||
Some((Instruction::INC, AddressingMode::ZeroPage)),
|
|
||||||
/*0xE7*/
|
|
||||||
None,
|
|
||||||
/*0xE8*/
|
|
||||||
Some((Instruction::INX, AddressingMode::Implied)),
|
|
||||||
/*0xE9*/
|
|
||||||
Some((Instruction::SBC, AddressingMode::Immediate)),
|
|
||||||
/*0xEA*/
|
|
||||||
Some((Instruction::NOP, AddressingMode::Implied)),
|
|
||||||
/*0xEB*/
|
|
||||||
None,
|
|
||||||
/*0xEC*/
|
|
||||||
Some((Instruction::CPX, AddressingMode::Absolute)),
|
|
||||||
/*0xED*/
|
|
||||||
Some((Instruction::SBC, AddressingMode::Absolute)),
|
|
||||||
/*0xEE*/
|
|
||||||
Some((Instruction::INC, AddressingMode::Absolute)),
|
|
||||||
/*0xEF*/
|
|
||||||
None,
|
|
||||||
/*0xF0*/
|
|
||||||
Some((Instruction::BEQ, AddressingMode::Relative)),
|
|
||||||
/*0xF1*/
|
|
||||||
Some((Instruction::SBC, AddressingMode::IndirectIndexedY)),
|
|
||||||
/*0xF2*/
|
|
||||||
None,
|
|
||||||
/*0xF3*/
|
|
||||||
None,
|
|
||||||
/*0xF4*/
|
|
||||||
None,
|
|
||||||
/*0xF5*/
|
|
||||||
Some((Instruction::SBC, AddressingMode::ZeroPageX)),
|
|
||||||
/*0xF6*/
|
|
||||||
Some((Instruction::INC, AddressingMode::ZeroPageX)),
|
|
||||||
/*0xF7*/
|
|
||||||
None,
|
|
||||||
/*0xF8*/
|
|
||||||
Some((Instruction::SED, AddressingMode::Implied)),
|
|
||||||
/*0xF9*/
|
|
||||||
Some((Instruction::SBC, AddressingMode::AbsoluteY)),
|
|
||||||
/*0xFA*/
|
|
||||||
None,
|
|
||||||
/*0xFB*/
|
|
||||||
None,
|
|
||||||
/*0xFC*/
|
|
||||||
None,
|
|
||||||
/*0xFD*/
|
|
||||||
Some((Instruction::SBC, AddressingMode::AbsoluteX)),
|
|
||||||
/*0xFE*/
|
|
||||||
Some((Instruction::INC, AddressingMode::AbsoluteX)),
|
|
||||||
/*0xFF*/
|
|
||||||
None,
|
|
||||||
];
|
|
||||||
|
|
11
src/lib.rs
11
src/lib.rs
|
@ -38,3 +38,14 @@ pub mod cpu;
|
||||||
pub mod instruction;
|
pub mod instruction;
|
||||||
pub mod memory;
|
pub mod memory;
|
||||||
pub mod registers;
|
pub mod registers;
|
||||||
|
|
||||||
|
/// Trait for 6502 variant. This is the mechanism allowing the different 6502-like CPUs to be
|
||||||
|
/// emulated. It allows a struct to decode an opcode into its instruction and addressing mode.
|
||||||
|
pub trait Variant {
|
||||||
|
fn decode(
|
||||||
|
opcode: u8,
|
||||||
|
) -> Option<(
|
||||||
|
crate::instruction::Instruction,
|
||||||
|
crate::instruction::AddressingMode,
|
||||||
|
)>;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue