mirror of
https://github.com/FranLMSP/snes.git
synced 2026-01-01 07:21:35 -05:00
Refactor CPU instructions (#2)
This commit is contained in:
@@ -1,19 +1,53 @@
|
||||
use super::registers::Registers;
|
||||
use super::{registers::Registers, bus::Bus, cycles, dma, instructions::mapper::map_opcode_to_instruction};
|
||||
|
||||
pub struct CPU {
|
||||
pub registers: Registers,
|
||||
pub cycles: usize,
|
||||
pub is_stopped: bool,
|
||||
pub is_waiting_interrupt: bool,
|
||||
}
|
||||
|
||||
impl CPU {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
registers: Registers::new(),
|
||||
cycles: 0,
|
||||
is_stopped: false,
|
||||
is_waiting_interrupt: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn check_running_state(&mut self, bus: &mut Bus) -> bool {
|
||||
// Each byte in a DMA transfer takes 8 master cycles.
|
||||
// And each CPU can take either 6, 8 or 12 master cycles depending
|
||||
// on what's being read from memory. So this won't be accurate.
|
||||
if bus.dma.is_active() {
|
||||
let pending_bus_writes = bus.dma.tick();
|
||||
for (src, dst) in pending_bus_writes {
|
||||
let byte = bus.read(src);
|
||||
bus.write(dst, byte);
|
||||
let (bytes, cycles) = cycles::increment_cycles_while_stopped();
|
||||
self.registers.increment_pc(bytes); self.registers.cycles += cycles;
|
||||
}
|
||||
if !bus.dma.is_active() {
|
||||
bus.write(dma::MDMAEN as u32, 0x00)
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if self.registers.is_cpu_stopped {
|
||||
let (bytes, cycles) = cycles::increment_cycles_while_stopped();
|
||||
self.registers.increment_pc(bytes); self.registers.cycles += cycles;
|
||||
return false;
|
||||
}
|
||||
if self.registers.is_cpu_waiting_interrupt {
|
||||
// TODO: check for interrupts here
|
||||
let (bytes, cycles) = cycles::increment_cycles_while_stopped();
|
||||
self.registers.increment_pc(bytes); self.registers.cycles += cycles;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn tick(&mut self, bus: &mut Bus) {
|
||||
if !self.check_running_state(bus) {
|
||||
return;
|
||||
}
|
||||
let opcode = bus.read(self.registers.get_pc_address());
|
||||
let instruction = map_opcode_to_instruction(opcode);
|
||||
instruction.execute(&mut self.registers, bus);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
207
snes-core/src/cpu/instructions/adc.rs
Normal file
207
snes-core/src/cpu/instructions/adc.rs
Normal file
@@ -0,0 +1,207 @@
|
||||
use crate::{cpu::{bus::Bus, registers::Registers}, utils::{alu, addressing::AddressingMode}};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::{CPUInstruction, read_write_common::{read_8bit_from_address, read_16bit_from_address}};
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "ADC";
|
||||
|
||||
pub struct ADC {
|
||||
pub addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl ADC {
|
||||
fn determine_instruction(&self, registers: &Registers) -> Box<dyn CPUInstruction> {
|
||||
let is_decimal_mode = registers.get_decimal_mode_flag();
|
||||
match registers.is_16bit_mode() {
|
||||
true => match is_decimal_mode {
|
||||
true => Box::new(ADC16BCD{addressing_mode: self.addressing_mode}),
|
||||
false => Box::new(ADC16BIN{addressing_mode: self.addressing_mode}),
|
||||
}
|
||||
false => match is_decimal_mode {
|
||||
true => Box::new(ADC8BCD{addressing_mode: self.addressing_mode}),
|
||||
false => Box::new(ADC8BIN{addressing_mode: self.addressing_mode}),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPUInstruction for ADC {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.execute(registers, bus);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.mnemonic(registers, bus, opcode)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ADC8BIN {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for ADC8BIN {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let (result, affected_flags) = alu::adc_bin(
|
||||
registers.a as u8,
|
||||
read_8bit_from_address(registers, bus, self.addressing_mode),
|
||||
registers.get_carry_flag(),
|
||||
);
|
||||
registers.set_low_a(result);
|
||||
registers.set_flags(&affected_flags);
|
||||
let (bytes, cycles) = cycles::increment_cycles_arithmetic(®isters, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ADC16BIN {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for ADC16BIN {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let (result, affected_flags) = alu::adc_bin(
|
||||
registers.a,
|
||||
read_16bit_from_address(registers, bus, self.addressing_mode),
|
||||
registers.get_carry_flag(),
|
||||
);
|
||||
registers.a = result;
|
||||
registers.set_flags(&affected_flags);
|
||||
let (bytes, cycles) = cycles::increment_cycles_arithmetic(®isters, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ADC8BCD {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for ADC8BCD {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let (result, affected_flags) = alu::adc_bcd(
|
||||
registers.a as u8,
|
||||
read_8bit_from_address(registers, bus, self.addressing_mode),
|
||||
registers.get_carry_flag(),
|
||||
);
|
||||
registers.set_low_a(result);
|
||||
registers.set_flags(&affected_flags);
|
||||
let (bytes, cycles) = cycles::increment_cycles_arithmetic(®isters, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ADC16BCD {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for ADC16BCD {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let (result, affected_flags) = alu::adc_bcd(
|
||||
registers.a,
|
||||
read_16bit_from_address(registers, bus, self.addressing_mode),
|
||||
registers.get_carry_flag(),
|
||||
);
|
||||
registers.a = result;
|
||||
registers.set_flags(&affected_flags);
|
||||
let (bytes, cycles) = cycles::increment_cycles_arithmetic(®isters, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_adc_bin_8bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.a = 0x0000;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
registers.set_memory_select_flag(true);
|
||||
bus.write(0x000001, 0x40);
|
||||
let instruction = ADC8BIN{addressing_mode: AddressingMode::Immediate};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.a, 0x40);
|
||||
assert_eq!(registers.pc, 0x02);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
assert!(!registers.get_carry_flag());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_adc_bin_16bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.a = 0x0000;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
registers.set_memory_select_flag(false);
|
||||
bus.write(0x000001, 0x00);
|
||||
bus.write(0x000002, 0x40);
|
||||
let instruction = ADC16BIN{addressing_mode: AddressingMode::Immediate};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.a, 0x4000);
|
||||
assert_eq!(registers.pc, 0x03);
|
||||
assert_eq!(registers.cycles, 3);
|
||||
assert!(!registers.get_carry_flag());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_adc_bcd_8bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.a = 0x0000;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
registers.set_memory_select_flag(true);
|
||||
bus.write(0x000001, 0x40);
|
||||
let instruction = ADC8BCD{addressing_mode: AddressingMode::Immediate};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.a, 0x40);
|
||||
assert_eq!(registers.pc, 0x02);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
assert!(!registers.get_carry_flag());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_adc_bcd_16bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.a = 0x0000;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
registers.set_memory_select_flag(false);
|
||||
bus.write(0x000001, 0x00);
|
||||
bus.write(0x000002, 0x40);
|
||||
let instruction = ADC16BCD{addressing_mode: AddressingMode::Immediate};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.a, 0x4000);
|
||||
assert_eq!(registers.pc, 0x03);
|
||||
assert_eq!(registers.cycles, 3);
|
||||
assert!(!registers.get_carry_flag());
|
||||
}
|
||||
}
|
||||
119
snes-core/src/cpu/instructions/and.rs
Normal file
119
snes-core/src/cpu/instructions/and.rs
Normal file
@@ -0,0 +1,119 @@
|
||||
use crate::{cpu::{bus::Bus, registers::Registers}, utils::{alu, addressing::AddressingMode}};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::{CPUInstruction, read_write_common::{read_8bit_from_address, read_16bit_from_address}};
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "AND";
|
||||
|
||||
pub struct AND {
|
||||
pub addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl AND {
|
||||
fn determine_instruction(&self, registers: &Registers) -> Box<dyn CPUInstruction> {
|
||||
match registers.is_16bit_mode() {
|
||||
true => Box::new(AND16{addressing_mode: self.addressing_mode}),
|
||||
false => Box::new(AND8{addressing_mode: self.addressing_mode}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPUInstruction for AND {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.execute(registers, bus);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.mnemonic(registers, bus, opcode)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AND8 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for AND8 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let (result, affected_flags) = alu::and(
|
||||
registers.a as u8,
|
||||
read_8bit_from_address(registers, bus, self.addressing_mode),
|
||||
);
|
||||
registers.set_low_a(result);
|
||||
registers.set_flags(&affected_flags);
|
||||
let (bytes, cycles) = cycles::increment_cycles_bitwise(®isters, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AND16 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for AND16 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let (result, affected_flags) = alu::and(
|
||||
registers.a,
|
||||
read_16bit_from_address(registers, bus, self.addressing_mode),
|
||||
);
|
||||
registers.a = result;
|
||||
registers.set_flags(&affected_flags);
|
||||
let (bytes, cycles) = cycles::increment_cycles_bitwise(®isters, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_and_8bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.a = 0x0101;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
registers.set_memory_select_flag(true);
|
||||
bus.write(0x000001, 0x00);
|
||||
let instruction = AND8{addressing_mode: AddressingMode::Immediate};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.a, 0x0100);
|
||||
assert_eq!(registers.pc, 0x02);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
assert!(!registers.get_carry_flag());
|
||||
assert!(registers.get_zero_flag());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_and_16bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.a = 0x0101;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
registers.set_memory_select_flag(false);
|
||||
bus.write(0x000001, 0x01);
|
||||
bus.write(0x000002, 0x01);
|
||||
let instruction = AND16{addressing_mode: AddressingMode::Immediate};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.a, 0x0101);
|
||||
assert_eq!(registers.pc, 0x03);
|
||||
assert_eq!(registers.cycles, 3);
|
||||
assert!(!registers.get_carry_flag());
|
||||
assert!(!registers.get_zero_flag());
|
||||
}
|
||||
}
|
||||
116
snes-core/src/cpu/instructions/asl.rs
Normal file
116
snes-core/src/cpu/instructions/asl.rs
Normal file
@@ -0,0 +1,116 @@
|
||||
use crate::{cpu::{bus::Bus, registers::Registers}, utils::{alu, addressing::AddressingMode}};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "ASL";
|
||||
|
||||
pub struct ASL {
|
||||
pub addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl ASL {
|
||||
fn determine_instruction(&self, registers: &Registers) -> Box<dyn CPUInstruction> {
|
||||
match registers.is_16bit_mode() {
|
||||
true => Box::new(ASL8{addressing_mode: self.addressing_mode}),
|
||||
false => Box::new(ASL16{addressing_mode: self.addressing_mode}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPUInstruction for ASL {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.execute(registers, bus);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.mnemonic(registers, bus, opcode)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ASL8 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for ASL8 {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
let (result, affected_flags) = alu::asl(
|
||||
registers.a as u8,
|
||||
);
|
||||
registers.set_low_a(result);
|
||||
registers.set_flags(&affected_flags);
|
||||
let (bytes, cycles) = cycles::increment_cycles_bitwise(®isters, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ASL16 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for ASL16 {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
let (result, affected_flags) = alu::asl(
|
||||
registers.a,
|
||||
);
|
||||
registers.a = result;
|
||||
registers.set_flags(&affected_flags);
|
||||
let (bytes, cycles) = cycles::increment_cycles_bitwise(®isters, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_asl_8bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.a = 0b01010000;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
registers.set_memory_select_flag(true);
|
||||
let instruction = ASL8{addressing_mode: AddressingMode::Immediate};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.a, 0b10100000);
|
||||
assert_eq!(registers.pc, 0x02);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
assert!(!registers.get_carry_flag());
|
||||
assert!(!registers.get_zero_flag());
|
||||
assert!(registers.get_negative_flag());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_asl_16bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.a = 0b01010000_00000000;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
registers.set_memory_select_flag(false);
|
||||
let instruction = ASL16{addressing_mode: AddressingMode::Immediate};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.a, 0b10100000_00000000);
|
||||
assert_eq!(registers.pc, 0x03);
|
||||
assert_eq!(registers.cycles, 3);
|
||||
assert!(!registers.get_carry_flag());
|
||||
assert!(!registers.get_zero_flag());
|
||||
assert!(registers.get_negative_flag());
|
||||
}
|
||||
}
|
||||
57
snes-core/src/cpu/instructions/bcc.rs
Normal file
57
snes-core/src/cpu/instructions/bcc.rs
Normal file
@@ -0,0 +1,57 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
use super::branch_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "BCC";
|
||||
|
||||
pub struct BCC {}
|
||||
|
||||
impl CPUInstruction for BCC {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
branch_common::do_branch_instr(registers, bus, !registers.get_carry_flag());
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_branch_nearlabel(opcode, INSTR_NAME, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let instruction = BCC{};
|
||||
// test with positive nearlabel
|
||||
// branch not taken
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.cycles = 0;
|
||||
registers.set_carry_flag(true);
|
||||
bus.write(0x02, 0b00001111);
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0x02);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
// branch taken
|
||||
registers.pc = 0x0000;
|
||||
registers.cycles = 0;
|
||||
registers.set_carry_flag(false);
|
||||
bus.write(0x01, 0b00001111);
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0x02 + 0b00001111);
|
||||
assert_eq!(registers.cycles, 3);
|
||||
// test with negative nearlabel and boundary cross
|
||||
registers.pc = 0x0100;
|
||||
registers.cycles = 0;
|
||||
registers.set_carry_flag(false);
|
||||
bus.write(0x101, 0xFB); // write -5
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0xFD);
|
||||
assert_eq!(registers.cycles, 4);
|
||||
}
|
||||
}
|
||||
57
snes-core/src/cpu/instructions/bcs.rs
Normal file
57
snes-core/src/cpu/instructions/bcs.rs
Normal file
@@ -0,0 +1,57 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
use super::branch_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "BCS";
|
||||
|
||||
pub struct BCS {}
|
||||
|
||||
impl CPUInstruction for BCS {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
branch_common::do_branch_instr(registers, bus, registers.get_carry_flag());
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_branch_nearlabel(opcode, INSTR_NAME, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let instruction = BCS{};
|
||||
// test with positive nearlabel
|
||||
// branch not taken
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.cycles = 0;
|
||||
registers.set_carry_flag(false);
|
||||
bus.write(0x02, 0b00001111);
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0x02);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
// branch taken
|
||||
registers.pc = 0x0000;
|
||||
registers.cycles = 0;
|
||||
registers.set_carry_flag(true);
|
||||
bus.write(0x01, 0b00001111);
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0x02 + 0b00001111);
|
||||
assert_eq!(registers.cycles, 3);
|
||||
// test with negative nearlabel and boundary cross
|
||||
registers.pc = 0x0100;
|
||||
registers.cycles = 0;
|
||||
registers.set_carry_flag(true);
|
||||
bus.write(0x101, 0xFB); // write -5
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0xFD);
|
||||
assert_eq!(registers.cycles, 4);
|
||||
}
|
||||
}
|
||||
57
snes-core/src/cpu/instructions/beq.rs
Normal file
57
snes-core/src/cpu/instructions/beq.rs
Normal file
@@ -0,0 +1,57 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
use super::branch_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "BEQ";
|
||||
|
||||
pub struct BEQ {}
|
||||
|
||||
impl CPUInstruction for BEQ {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
branch_common::do_branch_instr(registers, bus, registers.get_zero_flag());
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_branch_nearlabel(opcode, INSTR_NAME, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let instruction = BEQ{};
|
||||
// test with positive nearlabel
|
||||
// branch not taken
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.cycles = 0;
|
||||
registers.set_zero_flag(false);
|
||||
bus.write(0x02, 0b00001111);
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0x02);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
// branch taken
|
||||
registers.pc = 0x0000;
|
||||
registers.cycles = 0;
|
||||
registers.set_zero_flag(true);
|
||||
bus.write(0x01, 0b00001111);
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0x02 + 0b00001111);
|
||||
assert_eq!(registers.cycles, 3);
|
||||
// test with negative nearlabel and boundary cross
|
||||
registers.pc = 0x0100;
|
||||
registers.cycles = 0;
|
||||
registers.set_zero_flag(true);
|
||||
bus.write(0x101, 0xFB); // write -5
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0xFD);
|
||||
assert_eq!(registers.cycles, 4);
|
||||
}
|
||||
}
|
||||
128
snes-core/src/cpu/instructions/bit.rs
Normal file
128
snes-core/src/cpu/instructions/bit.rs
Normal file
@@ -0,0 +1,128 @@
|
||||
use crate::{cpu::{bus::Bus, registers::Registers}, utils::addressing::AddressingMode};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::{CPUInstruction, bit_common, read_write_common::{read_8bit_from_address, read_16bit_from_address}};
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "BIT";
|
||||
|
||||
pub struct BIT {
|
||||
pub addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl BIT {
|
||||
fn determine_instruction(&self, registers: &Registers) -> Box<dyn CPUInstruction> {
|
||||
match registers.is_16bit_mode() {
|
||||
true => Box::new(BIT16{addressing_mode: self.addressing_mode}),
|
||||
false => Box::new(BIT8{addressing_mode: self.addressing_mode}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPUInstruction for BIT {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.execute(registers, bus);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.mnemonic(registers, bus, opcode)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BIT8 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for BIT8 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
bit_common::do_bit(
|
||||
registers,
|
||||
registers.a as u8,
|
||||
read_8bit_from_address(registers, bus, self.addressing_mode),
|
||||
self.addressing_mode,
|
||||
);
|
||||
let (bytes, cycles) = cycles::increment_cycles_bit(®isters, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BIT16 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for BIT16 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
bit_common::do_bit(
|
||||
registers,
|
||||
registers.a,
|
||||
read_16bit_from_address(registers, bus, self.addressing_mode),
|
||||
self.addressing_mode,
|
||||
);
|
||||
let (bytes, cycles) = cycles::increment_cycles_bit(®isters, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_8bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.a = 0b1111_0000;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
registers.p = 0x00;
|
||||
bus.write(0x000001, 0b0000_1111);
|
||||
registers.set_16bit_mode(false);
|
||||
let instruction = BIT8{addressing_mode: AddressingMode::Immediate};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
// Check that it only affects the zero flag on immediate mode
|
||||
assert_eq!(registers.a, 0b1111_0000); // Check that A is not altered
|
||||
assert_eq!(registers.p, 0b0010_0010); // Only zero flag was altered (bit 6 is memory select mode)
|
||||
assert_eq!(registers.pc, 0x02);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
assert!(registers.get_zero_flag());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_16bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.a = 0b00110000_00000000;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
registers.p = 0x00;
|
||||
registers.cycles = 0;
|
||||
// Write absolute address
|
||||
bus.write(0x000001, 0x04);
|
||||
bus.write(0x000002, 0x00);
|
||||
// Write effective value of address
|
||||
bus.write(0x000004, 0x00);
|
||||
bus.write(0x000005, 0b1100_0000);
|
||||
registers.set_16bit_mode(true);
|
||||
let instruction = BIT16{addressing_mode: AddressingMode::Immediate};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
// Check that it only affects the zero flag on immediate mode
|
||||
assert_eq!(registers.a, 0b00110000_00000000); // Check that A is not altered
|
||||
assert_eq!(registers.p, 0b0000_0010);
|
||||
assert_eq!(registers.pc, 0x03);
|
||||
assert_eq!(registers.cycles, 3);
|
||||
assert!(registers.get_zero_flag());
|
||||
}
|
||||
}
|
||||
14
snes-core/src/cpu/instructions/bit_common.rs
Normal file
14
snes-core/src/cpu/instructions/bit_common.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
use crate::{cpu::registers::Registers, utils::{num_trait::SnesNum, addressing::AddressingMode, alu}};
|
||||
|
||||
pub fn do_bit<T: SnesNum>(registers: &mut Registers, accumulator: T, value: T, addressing_mode: AddressingMode) {
|
||||
let (result, _) = alu::and(accumulator, value);
|
||||
// Immediate addressing affects only the zero flag
|
||||
match addressing_mode {
|
||||
AddressingMode::Immediate => registers.set_zero_flag(result.is_zero()),
|
||||
_ => {
|
||||
registers.set_zero_flag(result.is_zero());
|
||||
registers.set_negative_flag(value.is_negative());
|
||||
registers.set_overflow_flag(value.next_to_highest_bit());
|
||||
}
|
||||
};
|
||||
}
|
||||
57
snes-core/src/cpu/instructions/bmi.rs
Normal file
57
snes-core/src/cpu/instructions/bmi.rs
Normal file
@@ -0,0 +1,57 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
use super::branch_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "BMI";
|
||||
|
||||
pub struct BMI {}
|
||||
|
||||
impl CPUInstruction for BMI {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
branch_common::do_branch_instr(registers, bus, registers.get_negative_flag());
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_branch_nearlabel(opcode, INSTR_NAME, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let instruction = BMI{};
|
||||
// test with positive nearlabel
|
||||
// branch not taken
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.cycles = 0;
|
||||
registers.set_negative_flag(false);
|
||||
bus.write(0x02, 0b00001111);
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0x02);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
// branch taken
|
||||
registers.pc = 0x0000;
|
||||
registers.cycles = 0;
|
||||
registers.set_negative_flag(true);
|
||||
bus.write(0x01, 0b00001111);
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0x02 + 0b00001111);
|
||||
assert_eq!(registers.cycles, 3);
|
||||
// test with negative nearlabel and boundary cross
|
||||
registers.pc = 0x0100;
|
||||
registers.cycles = 0;
|
||||
registers.set_negative_flag(true);
|
||||
bus.write(0x101, 0xFB); // write -5
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0xFD);
|
||||
assert_eq!(registers.cycles, 4);
|
||||
}
|
||||
}
|
||||
57
snes-core/src/cpu/instructions/bne.rs
Normal file
57
snes-core/src/cpu/instructions/bne.rs
Normal file
@@ -0,0 +1,57 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
use super::branch_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "BNE";
|
||||
|
||||
pub struct BNE {}
|
||||
|
||||
impl CPUInstruction for BNE {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
branch_common::do_branch_instr(registers, bus, !registers.get_zero_flag());
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_branch_nearlabel(opcode, INSTR_NAME, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let instruction = BNE{};
|
||||
// test with positive nearlabel
|
||||
// branch not taken
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.cycles = 0;
|
||||
registers.set_zero_flag(true);
|
||||
bus.write(0x02, 0b00001111);
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0x02);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
// branch taken
|
||||
registers.pc = 0x0000;
|
||||
registers.cycles = 0;
|
||||
registers.set_zero_flag(false);
|
||||
bus.write(0x01, 0b00001111);
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0x02 + 0b00001111);
|
||||
assert_eq!(registers.cycles, 3);
|
||||
// test with negative nearlabel and boundary cross
|
||||
registers.pc = 0x0100;
|
||||
registers.cycles = 0;
|
||||
registers.set_zero_flag(false);
|
||||
bus.write(0x101, 0xFB); // write -5
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0xFD);
|
||||
assert_eq!(registers.cycles, 4);
|
||||
}
|
||||
}
|
||||
57
snes-core/src/cpu/instructions/bpl.rs
Normal file
57
snes-core/src/cpu/instructions/bpl.rs
Normal file
@@ -0,0 +1,57 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
use super::branch_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "BPL";
|
||||
|
||||
pub struct BPL {}
|
||||
|
||||
impl CPUInstruction for BPL {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
branch_common::do_branch_instr(registers, bus, !registers.get_negative_flag());
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_branch_nearlabel(opcode, INSTR_NAME, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let instruction = BPL{};
|
||||
// test with positive nearlabel
|
||||
// branch not taken
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.cycles = 0;
|
||||
registers.set_negative_flag(true);
|
||||
bus.write(0x02, 0b00001111);
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0x02);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
// branch taken
|
||||
registers.pc = 0x0000;
|
||||
registers.cycles = 0;
|
||||
registers.set_negative_flag(false);
|
||||
bus.write(0x01, 0b00001111);
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0x02 + 0b00001111);
|
||||
assert_eq!(registers.cycles, 3);
|
||||
// test with negative nearlabel and boundary cross
|
||||
registers.pc = 0x0100;
|
||||
registers.cycles = 0;
|
||||
registers.set_negative_flag(false);
|
||||
bus.write(0x101, 0xFB); // write -5
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0xFD);
|
||||
assert_eq!(registers.cycles, 4);
|
||||
}
|
||||
}
|
||||
53
snes-core/src/cpu/instructions/bra.rs
Normal file
53
snes-core/src/cpu/instructions/bra.rs
Normal file
@@ -0,0 +1,53 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
use super::branch_common;
|
||||
use crate::cpu::cycles;
|
||||
|
||||
static INSTR_NAME: &'static str = "BRA";
|
||||
|
||||
pub struct BRA {}
|
||||
|
||||
impl CPUInstruction for BRA {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let nearlabel = bus.read(registers.get_pc_address().wrapping_add(1));
|
||||
let (bytes, cycles) = cycles::increment_cycles_branch();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
let page_boundary_crossed = branch_common::do_branch(nearlabel, registers);
|
||||
let (bytes, cycles) = cycles::increment_cycles_branch_taken(page_boundary_crossed);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_branch_nearlabel(opcode, INSTR_NAME, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let instruction = BRA{};
|
||||
// test with positive nearlabel
|
||||
// branch always taken
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.cycles = 0;
|
||||
bus.write(0x01, 0b00001111);
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0x02 + 0b00001111);
|
||||
assert_eq!(registers.cycles, 3);
|
||||
// test with negative nearlabel and boundary cross
|
||||
registers.pc = 0x0100;
|
||||
registers.cycles = 0;
|
||||
bus.write(0x101, 0xFB); // write -5
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0xFD);
|
||||
assert_eq!(registers.cycles, 4);
|
||||
}
|
||||
}
|
||||
27
snes-core/src/cpu/instructions/branch_common.rs
Normal file
27
snes-core/src/cpu/instructions/branch_common.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
use crate::cpu::{registers::Registers, bus::Bus};
|
||||
use crate::cpu::cycles;
|
||||
|
||||
pub fn do_branch_instr(registers: &mut Registers, bus: &mut Bus, condition: bool) {
|
||||
let nearlabel = bus.read(registers.get_pc_address().wrapping_add(1));
|
||||
let (bytes, cycles) = cycles::increment_cycles_branch();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
if condition {
|
||||
let page_boundary_crossed = do_branch(nearlabel, registers);
|
||||
let (bytes, cycles) = cycles::increment_cycles_branch_taken(page_boundary_crossed);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn do_branch(nearlabel: u8, registers: &mut Registers) -> bool {
|
||||
let is_negative = (nearlabel >> 7) != 0;
|
||||
let old_pc = registers.get_pc_address();
|
||||
if is_negative {
|
||||
let nearlabel = !nearlabel + 1;
|
||||
registers.decrement_pc(nearlabel as u16);
|
||||
} else {
|
||||
registers.increment_pc(nearlabel as u16);
|
||||
}
|
||||
let new_pc = registers.get_pc_address();
|
||||
let page_boundary_crossed = (old_pc & 0xFF00) != (new_pc & 0xFF00);
|
||||
return page_boundary_crossed
|
||||
}
|
||||
36
snes-core/src/cpu/instructions/brk.rs
Normal file
36
snes-core/src/cpu/instructions/brk.rs
Normal file
@@ -0,0 +1,36 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
use super::push_common;
|
||||
use crate::cpu::cycles;
|
||||
|
||||
static INSTR_NAME: &'static str = "BRK";
|
||||
|
||||
pub struct BRK {}
|
||||
|
||||
impl CPUInstruction for BRK {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
push_common::do_push(registers, bus, &[registers.pbr]);
|
||||
push_common::do_push(registers, bus, &[(registers.pc >> 8) as u8, registers.pc as u8]);
|
||||
push_common::do_push(registers, bus, &[registers.p]);
|
||||
registers.set_decimal_mode_flag(false);
|
||||
registers.set_irq_disable_flag(true);
|
||||
let (bytes, cycles) = cycles::increment_cycles_brk(registers.emulation_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
}
|
||||
}
|
||||
59
snes-core/src/cpu/instructions/brl.rs
Normal file
59
snes-core/src/cpu/instructions/brl.rs
Normal file
@@ -0,0 +1,59 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
use crate::cpu::cycles;
|
||||
|
||||
static INSTR_NAME: &'static str = "BRL";
|
||||
|
||||
pub struct BRL {}
|
||||
|
||||
impl CPUInstruction for BRL {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let label = bus.read(registers.get_pc_address()) as u16 |
|
||||
((bus.read(registers.get_pc_address() + 1) as u16) << 8);
|
||||
let is_negative = (label >> 15) != 0;
|
||||
if is_negative {
|
||||
let label = !label + 1;
|
||||
registers.decrement_pc(label);
|
||||
} else {
|
||||
registers.increment_pc(label);
|
||||
}
|
||||
let (bytes, cycles) = cycles::increment_cycles_branch_long();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_absolute(opcode, INSTR_NAME, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let instruction = BRL{};
|
||||
// test with positive nearlabel
|
||||
// branch always taken
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0001;
|
||||
registers.cycles = 0;
|
||||
bus.write(0x01, 0b00000000);
|
||||
bus.write(0x02, 0b00001111);
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0x04 + 0b00001111_00000000);
|
||||
assert_eq!(registers.cycles, 4);
|
||||
// test with negative nearlabel and boundary cross
|
||||
registers.pc = 0x00FD;
|
||||
registers.cycles = 0;
|
||||
bus.write(0xFD, 0xFF); // write -1
|
||||
bus.write(0xFE, 0xFF); // write -1
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0xFF);
|
||||
assert_eq!(registers.cycles, 4);
|
||||
}
|
||||
}
|
||||
57
snes-core/src/cpu/instructions/bvc.rs
Normal file
57
snes-core/src/cpu/instructions/bvc.rs
Normal file
@@ -0,0 +1,57 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
use super::branch_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "BVC";
|
||||
|
||||
pub struct BVC {}
|
||||
|
||||
impl CPUInstruction for BVC {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
branch_common::do_branch_instr(registers, bus, !registers.get_overflow_flag());
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_branch_nearlabel(opcode, INSTR_NAME, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let instruction = BVC{};
|
||||
// test with positive nearlabel
|
||||
// branch not taken
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.cycles = 0;
|
||||
registers.set_overflow_flag(true);
|
||||
bus.write(0x02, 0b00001111);
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0x02);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
// branch taken
|
||||
registers.pc = 0x0000;
|
||||
registers.cycles = 0;
|
||||
registers.set_overflow_flag(false);
|
||||
bus.write(0x01, 0b00001111);
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0x02 + 0b00001111);
|
||||
assert_eq!(registers.cycles, 3);
|
||||
// test with negative nearlabel and boundary cross
|
||||
registers.pc = 0x0100;
|
||||
registers.cycles = 0;
|
||||
registers.set_overflow_flag(false);
|
||||
bus.write(0x101, 0xFB); // write -5
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0xFD);
|
||||
assert_eq!(registers.cycles, 4);
|
||||
}
|
||||
}
|
||||
57
snes-core/src/cpu/instructions/bvs.rs
Normal file
57
snes-core/src/cpu/instructions/bvs.rs
Normal file
@@ -0,0 +1,57 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
use super::branch_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "BVS";
|
||||
|
||||
pub struct BVS {}
|
||||
|
||||
impl CPUInstruction for BVS {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
branch_common::do_branch_instr(registers, bus, registers.get_overflow_flag());
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_branch_nearlabel(opcode, INSTR_NAME, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let instruction = BVS{};
|
||||
// test with positive nearlabel
|
||||
// branch not taken
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.cycles = 0;
|
||||
registers.set_overflow_flag(false);
|
||||
bus.write(0x02, 0b00001111);
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0x02);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
// branch taken
|
||||
registers.pc = 0x0000;
|
||||
registers.cycles = 0;
|
||||
registers.set_overflow_flag(true);
|
||||
bus.write(0x01, 0b00001111);
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0x02 + 0b00001111);
|
||||
assert_eq!(registers.cycles, 3);
|
||||
// test with negative nearlabel and boundary cross
|
||||
registers.pc = 0x0100;
|
||||
registers.cycles = 0;
|
||||
registers.set_overflow_flag(true);
|
||||
bus.write(0x101, 0xFB); // write -5
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0xFD);
|
||||
assert_eq!(registers.cycles, 4);
|
||||
}
|
||||
}
|
||||
40
snes-core/src/cpu/instructions/clc.rs
Normal file
40
snes-core/src/cpu/instructions/clc.rs
Normal file
@@ -0,0 +1,40 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "CLC";
|
||||
|
||||
pub struct CLC {}
|
||||
|
||||
impl CPUInstruction for CLC {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
registers.set_carry_flag(false);
|
||||
let (bytes, cycles) = cycles::increment_cycles_clear();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.set_carry_flag(true);
|
||||
registers.pc = 0x0000;
|
||||
let instruction = CLC{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert!(!registers.get_carry_flag());
|
||||
assert_eq!(registers.pc, 1);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
}
|
||||
}
|
||||
40
snes-core/src/cpu/instructions/cld.rs
Normal file
40
snes-core/src/cpu/instructions/cld.rs
Normal file
@@ -0,0 +1,40 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "CLD";
|
||||
|
||||
pub struct CLD {}
|
||||
|
||||
impl CPUInstruction for CLD {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
registers.set_decimal_mode_flag(false);
|
||||
let (bytes, cycles) = cycles::increment_cycles_clear();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.set_decimal_mode_flag(true);
|
||||
registers.pc = 0x0000;
|
||||
let instruction = CLD{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert!(!registers.get_decimal_mode_flag());
|
||||
assert_eq!(registers.pc, 1);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
}
|
||||
}
|
||||
40
snes-core/src/cpu/instructions/cli.rs
Normal file
40
snes-core/src/cpu/instructions/cli.rs
Normal file
@@ -0,0 +1,40 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "CLI";
|
||||
|
||||
pub struct CLI {}
|
||||
|
||||
impl CPUInstruction for CLI {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
registers.set_irq_disable_flag(false);
|
||||
let (bytes, cycles) = cycles::increment_cycles_clear();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.set_irq_disable_flag(true);
|
||||
registers.pc = 0x0000;
|
||||
let instruction = CLI{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert!(!registers.get_irq_disable_flag());
|
||||
assert_eq!(registers.pc, 1);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
}
|
||||
}
|
||||
40
snes-core/src/cpu/instructions/clv.rs
Normal file
40
snes-core/src/cpu/instructions/clv.rs
Normal file
@@ -0,0 +1,40 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "CLV";
|
||||
|
||||
pub struct CLV {}
|
||||
|
||||
impl CPUInstruction for CLV {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
registers.set_overflow_flag(false);
|
||||
let (bytes, cycles) = cycles::increment_cycles_clear();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.set_overflow_flag(true);
|
||||
registers.pc = 0x0000;
|
||||
let instruction = CLV{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert!(!registers.get_overflow_flag());
|
||||
assert_eq!(registers.pc, 1);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
}
|
||||
}
|
||||
119
snes-core/src/cpu/instructions/cmp.rs
Normal file
119
snes-core/src/cpu/instructions/cmp.rs
Normal file
@@ -0,0 +1,119 @@
|
||||
use crate::{cpu::{bus::Bus, registers::Registers}, utils::addressing::AddressingMode};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::{CPUInstruction, read_write_common::{read_8bit_from_address, read_16bit_from_address}};
|
||||
use super::decoder_common;
|
||||
use super::comp_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "CMP";
|
||||
|
||||
pub struct CMP {
|
||||
pub addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CMP {
|
||||
fn determine_instruction(&self, registers: &Registers) -> Box<dyn CPUInstruction> {
|
||||
match registers.is_16bit_mode() {
|
||||
true => Box::new(CMP16{addressing_mode: self.addressing_mode}),
|
||||
false => Box::new(CMP8{addressing_mode: self.addressing_mode}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPUInstruction for CMP {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.execute(registers, bus);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.mnemonic(registers, bus, opcode)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CMP8 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for CMP8 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
comp_common::do_comp(
|
||||
registers,
|
||||
registers.a as u8,
|
||||
read_8bit_from_address(registers, bus, self.addressing_mode),
|
||||
);
|
||||
let (bytes, cycles) = cycles::increment_cycles_arithmetic(®isters, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CMP16 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for CMP16 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
comp_common::do_comp(
|
||||
registers,
|
||||
registers.a,
|
||||
read_16bit_from_address(registers, bus, self.addressing_mode),
|
||||
);
|
||||
let (bytes, cycles) = cycles::increment_cycles_arithmetic(®isters, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_8bit() {
|
||||
// CMP is basically an SBC instruction but it doesn't
|
||||
// store the result nor it affects the overflow flag
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.a = 0x0001;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
registers.set_memory_select_flag(true);
|
||||
bus.write(0x000001, 1);
|
||||
let instruction = CMP8{addressing_mode: AddressingMode::Immediate};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.a, 0x0001); // check A is not affected
|
||||
assert_eq!(registers.pc, 0x02);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
assert!(!registers.get_carry_flag());
|
||||
assert!(registers.get_zero_flag());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_and_16bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.a = 0x0050;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
registers.set_16bit_mode(false);
|
||||
registers.set_overflow_flag(false);
|
||||
bus.write(0x000001, 0xB0);
|
||||
let instruction = CMP16{addressing_mode: AddressingMode::Immediate};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.a, 0x0050); // check A is not affected
|
||||
assert_eq!(registers.pc, 0x02);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
assert!(registers.get_carry_flag());
|
||||
assert!(!registers.get_zero_flag());
|
||||
assert!(!registers.get_overflow_flag());
|
||||
}
|
||||
}
|
||||
12
snes-core/src/cpu/instructions/comp_common.rs
Normal file
12
snes-core/src/cpu/instructions/comp_common.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
use crate::{cpu::registers::Registers, utils::{num_trait::SnesNum, alu}, common::flags::Flags};
|
||||
|
||||
|
||||
pub fn do_comp<T: SnesNum>(registers: &mut Registers, target: T, value: T) {
|
||||
let (_, affected_flags) = alu::sbc_bin(target, value, false);
|
||||
for flag in affected_flags {
|
||||
match flag {
|
||||
Flags::Overflow(_) => {},
|
||||
_ => registers.set_flags(&[flag]),
|
||||
}
|
||||
}
|
||||
}
|
||||
36
snes-core/src/cpu/instructions/cop.rs
Normal file
36
snes-core/src/cpu/instructions/cop.rs
Normal file
@@ -0,0 +1,36 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
use super::push_common;
|
||||
use crate::cpu::cycles;
|
||||
|
||||
static INSTR_NAME: &'static str = "COP";
|
||||
|
||||
pub struct COP {}
|
||||
|
||||
impl CPUInstruction for COP {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
push_common::do_push(registers, bus, &[registers.pbr]);
|
||||
push_common::do_push(registers, bus, &[(registers.pc >> 8) as u8, registers.pc as u8]);
|
||||
push_common::do_push(registers, bus, &[registers.p]);
|
||||
registers.set_decimal_mode_flag(false);
|
||||
registers.set_irq_disable_flag(true);
|
||||
let (bytes, cycles) = cycles::increment_cycles_brk(registers.emulation_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
}
|
||||
}
|
||||
126
snes-core/src/cpu/instructions/cpx.rs
Normal file
126
snes-core/src/cpu/instructions/cpx.rs
Normal file
@@ -0,0 +1,126 @@
|
||||
use crate::{cpu::{bus::Bus, registers::Registers}, utils::addressing::AddressingMode};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::{CPUInstruction, read_write_common::{read_8bit_from_address, read_16bit_from_address}};
|
||||
use super::decoder_common;
|
||||
use super::comp_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "CPX";
|
||||
|
||||
pub struct CPX {
|
||||
pub addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPX {
|
||||
fn determine_instruction(&self, registers: &Registers) -> Box<dyn CPUInstruction> {
|
||||
match registers.is_16bit_index() {
|
||||
true => Box::new(CPX16{addressing_mode: self.addressing_mode}),
|
||||
false => Box::new(CPX8{addressing_mode: self.addressing_mode}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPUInstruction for CPX {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.execute(registers, bus);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.mnemonic(registers, bus, opcode)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CPX8 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for CPX8 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
comp_common::do_comp(
|
||||
registers,
|
||||
registers.x as u8,
|
||||
read_8bit_from_address(registers, bus, self.addressing_mode),
|
||||
);
|
||||
let (bytes, cycles) = cycles::increment_cycles_comp_index(®isters, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CPX16 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for CPX16 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
comp_common::do_comp(
|
||||
registers,
|
||||
registers.x,
|
||||
read_16bit_from_address(registers, bus, self.addressing_mode),
|
||||
);
|
||||
let (bytes, cycles) = cycles::increment_cycles_comp_index(®isters, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_8bit() {
|
||||
// CMP is basically an SBC instruction but it doesn't
|
||||
// store the result nor it affects the overflow flag
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.a = 0x10;
|
||||
registers.x = 0x01;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
registers.set_16bit_index(false);
|
||||
bus.write(0x000001, 1);
|
||||
let instruction = CPX8{addressing_mode: AddressingMode::Immediate};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.x, 0x01);
|
||||
assert_eq!(registers.pc, 0x02);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
assert!(!registers.get_carry_flag());
|
||||
assert!(registers.get_zero_flag());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_and_16bit() {
|
||||
// check overflow flag is not affected
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.cycles = 0;
|
||||
registers.a = 0x10;
|
||||
registers.x = 0x50;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
registers.emulation_mode = false;
|
||||
registers.set_16bit_index(true);
|
||||
registers.set_overflow_flag(false);
|
||||
bus.write(0x000002, 0xB0);
|
||||
bus.write(0x000001, 0x00);
|
||||
let instruction = CPX16{addressing_mode: AddressingMode::Immediate};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.x, 0x50); // check X is not affected
|
||||
assert_eq!(registers.pc, 0x03);
|
||||
assert_eq!(registers.cycles, 3);
|
||||
assert!(registers.get_carry_flag());
|
||||
assert!(!registers.get_zero_flag());
|
||||
assert!(!registers.get_overflow_flag());
|
||||
}
|
||||
}
|
||||
126
snes-core/src/cpu/instructions/cpy.rs
Normal file
126
snes-core/src/cpu/instructions/cpy.rs
Normal file
@@ -0,0 +1,126 @@
|
||||
use crate::{cpu::{bus::Bus, registers::Registers}, utils::addressing::AddressingMode};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::{CPUInstruction, read_write_common::{read_8bit_from_address, read_16bit_from_address}};
|
||||
use super::decoder_common;
|
||||
use super::comp_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "CPY";
|
||||
|
||||
pub struct CPY {
|
||||
pub addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPY {
|
||||
fn determine_instruction(&self, registers: &Registers) -> Box<dyn CPUInstruction> {
|
||||
match registers.is_16bit_index() {
|
||||
true => Box::new(CPY16{addressing_mode: self.addressing_mode}),
|
||||
false => Box::new(CPY8{addressing_mode: self.addressing_mode}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPUInstruction for CPY {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.execute(registers, bus);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.mnemonic(registers, bus, opcode)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CPY8 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for CPY8 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
comp_common::do_comp(
|
||||
registers,
|
||||
registers.y as u8,
|
||||
read_8bit_from_address(registers, bus, self.addressing_mode),
|
||||
);
|
||||
let (bytes, cycles) = cycles::increment_cycles_comp_index(®isters, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CPY16 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for CPY16 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
comp_common::do_comp(
|
||||
registers,
|
||||
registers.y,
|
||||
read_16bit_from_address(registers, bus, self.addressing_mode),
|
||||
);
|
||||
let (bytes, cycles) = cycles::increment_cycles_comp_index(®isters, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_8bit() {
|
||||
// CMP is basically an SBC instruction but it doesn't
|
||||
// store the result nor it affects the overflow flag
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.a = 0x10;
|
||||
registers.y = 0x01;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
registers.set_16bit_index(false);
|
||||
bus.write(0x000001, 1);
|
||||
let instruction = CPY8{addressing_mode: AddressingMode::Immediate};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.y, 0x01);
|
||||
assert_eq!(registers.pc, 0x02);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
assert!(!registers.get_carry_flag());
|
||||
assert!(registers.get_zero_flag());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_and_16bit() {
|
||||
// check overflow flag is not affected
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.cycles = 0;
|
||||
registers.a = 0x10;
|
||||
registers.y = 0x50;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
registers.emulation_mode = false;
|
||||
registers.set_16bit_index(true);
|
||||
registers.set_overflow_flag(false);
|
||||
bus.write(0x000002, 0xB0);
|
||||
bus.write(0x000001, 0x00);
|
||||
let instruction = CPY16{addressing_mode: AddressingMode::Immediate};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.y, 0x50); // check Y is not affected
|
||||
assert_eq!(registers.pc, 0x03);
|
||||
assert_eq!(registers.cycles, 3);
|
||||
assert!(registers.get_carry_flag());
|
||||
assert!(!registers.get_zero_flag());
|
||||
assert!(!registers.get_overflow_flag());
|
||||
}
|
||||
}
|
||||
112
snes-core/src/cpu/instructions/dec.rs
Normal file
112
snes-core/src/cpu/instructions/dec.rs
Normal file
@@ -0,0 +1,112 @@
|
||||
use crate::{cpu::{bus::Bus, registers::Registers}, utils::addressing::AddressingMode};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::{CPUInstruction, dec_common, read_write_common::{read_8bit_from_address, write_8bit_to_address, read_16bit_from_address, write_16bit_to_address}};
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "DEC";
|
||||
|
||||
pub struct DEC {
|
||||
pub addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl DEC {
|
||||
fn determine_instruction(&self, registers: &Registers) -> Box<dyn CPUInstruction> {
|
||||
match registers.is_16bit_mode() {
|
||||
true => Box::new(DEC16{addressing_mode: self.addressing_mode}),
|
||||
false => Box::new(DEC8{addressing_mode: self.addressing_mode}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPUInstruction for DEC {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.execute(registers, bus);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.mnemonic(registers, bus, opcode)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DEC8 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for DEC8 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let result = dec_common::do_dec(
|
||||
registers,
|
||||
read_8bit_from_address(registers, bus, self.addressing_mode),
|
||||
) as u8;
|
||||
write_8bit_to_address(registers, bus, self.addressing_mode, result);
|
||||
let (bytes, cycles) = cycles::increment_cycles_inc_dec(®isters, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DEC16 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for DEC16 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let result = dec_common::do_dec(
|
||||
registers,
|
||||
read_16bit_from_address(registers, bus, self.addressing_mode),
|
||||
) as u16;
|
||||
write_16bit_to_address(registers, bus, self.addressing_mode, result);
|
||||
let (bytes, cycles) = cycles::increment_cycles_inc_dec(®isters, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_8bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.a = 0x0001;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
registers.set_memory_select_flag(true);
|
||||
let instruction = DEC8{addressing_mode: AddressingMode::Accumulator};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.a, 0);
|
||||
assert_eq!(registers.pc, 0x01);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
assert!(!registers.get_negative_flag());
|
||||
assert!(registers.get_zero_flag());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_16bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.a = 0x0001;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
registers.set_memory_select_flag(true);
|
||||
let instruction = DEC16{addressing_mode: AddressingMode::Accumulator};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.a, 0);
|
||||
assert_eq!(registers.pc, 0x01);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
assert!(!registers.get_negative_flag());
|
||||
assert!(registers.get_zero_flag());
|
||||
}
|
||||
}
|
||||
23
snes-core/src/cpu/instructions/dec_common.rs
Normal file
23
snes-core/src/cpu/instructions/dec_common.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
use crate::{cpu::registers::Registers, utils::{num_trait::SnesNum, alu}, common::flags::Flags};
|
||||
|
||||
pub fn do_dec<T: SnesNum>(registers: &mut Registers, target: T) -> T {
|
||||
let (result, affected_flags) = alu::sbc_bin(target, T::from_u32(1), false);
|
||||
for flag in affected_flags {
|
||||
match flag {
|
||||
Flags::Negative(_) | Flags::Zero(_) => registers.set_flags(&[flag]),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub fn do_inc<T: SnesNum>(registers: &mut Registers, target: T) -> T {
|
||||
let (result, affected_flags) = alu::adc_bin(target, T::from_u32(1), false);
|
||||
for flag in affected_flags {
|
||||
match flag {
|
||||
Flags::Negative(_) | Flags::Zero(_) => registers.set_flags(&[flag]),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
128
snes-core/src/cpu/instructions/decoder_common.rs
Normal file
128
snes-core/src/cpu/instructions/decoder_common.rs
Normal file
@@ -0,0 +1,128 @@
|
||||
use crate::cpu::registers::Registers;
|
||||
use crate::cpu::bus::Bus;
|
||||
use crate::utils::addressing::{IndexRegister, AddressingMode};
|
||||
|
||||
pub fn mnemonic_arithmetic(is_16bit: bool, opcode: u8, instr_name: &str, addressing_mode: AddressingMode, registers: &Registers, bus: &Bus) -> String {
|
||||
type A = AddressingMode;
|
||||
match addressing_mode {
|
||||
A::Accumulator => mnemonic_accumulator(opcode, instr_name),
|
||||
A::Immediate => match is_16bit {
|
||||
true => mnemonic_16bit_immediate(opcode, instr_name, registers, bus),
|
||||
false => mnemonic_8bit_immediate(opcode, instr_name, registers, bus),
|
||||
},
|
||||
A::Absolute => mnemonic_absolute(opcode, instr_name, registers, bus),
|
||||
A::AbsoluteLong => mnemonic_absolute_long(opcode, instr_name, registers, bus),
|
||||
A::DirectPage => mnemonic_direct_page(opcode, instr_name, registers, bus),
|
||||
A::DirectPageIndirect => mnemonic_direct_page_indirect(opcode, instr_name, registers, bus),
|
||||
A::DirectPageIndirectLong => mnemonic_direct_page_indirect_long(opcode, instr_name, registers, bus),
|
||||
A::AbsoluteIndexed(idx) => mnemonic_absolute_indexed(opcode, instr_name, idx, registers, bus),
|
||||
A::AbsoluteLongIndexed(idx) => mnemonic_absolute_long_indexed(opcode, instr_name, idx, registers, bus),
|
||||
A::DirectPageIndexed(idx) => mnemonic_direct_page_indexed(opcode, instr_name, idx, registers, bus),
|
||||
A::DirectPageIndexedIndirect(idx) => mnemonic_direct_page_indexed_indirect(opcode, instr_name, idx, registers, bus),
|
||||
A::DirectPageIndirectLongIndexed(idx) => mnemonic_direct_page_indirect_long_indexed(opcode, instr_name, idx, registers, bus),
|
||||
A::StackRelative => mnemonic_stack_relative(opcode, instr_name, registers, bus),
|
||||
A::StackRelativeIndirectIndexed(idx)=> mnemonic_stack_relative_indirect_indexed(opcode, instr_name, idx, registers, bus),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mnemonic_accumulator(opcode: u8, instr_name: &str) -> String {
|
||||
format!("{:02X} __ __ __ | {} A", opcode, instr_name)
|
||||
}
|
||||
|
||||
pub fn mnemonic_8bit_immediate(opcode: u8, instr_name: &str, registers: &Registers, bus: &Bus) -> String {
|
||||
let next_byte = bus.read_external(registers.get_pc_address() + 1);
|
||||
format!("{:02X} {:02X} __ __ | {} #${:04X}", opcode, next_byte, instr_name, next_byte)
|
||||
}
|
||||
|
||||
pub fn mnemonic_16bit_immediate(opcode: u8, instr_name: &str, registers: &Registers, bus: &Bus) -> String {
|
||||
let next_byte = bus.read_external(registers.get_pc_address() + 1);
|
||||
let next_second_byte = bus.read_external(registers.get_pc_address() + 2);
|
||||
let word = (next_byte as u16) | ((next_byte as u16) << 8);
|
||||
format!("{:02X} {:02X} {:02X} __ | {} #${:04X}", opcode, next_byte, next_second_byte, instr_name, word)
|
||||
}
|
||||
|
||||
pub fn mnemonic_absolute(opcode: u8, instr_name: &str, registers: &Registers, bus: &Bus) -> String {
|
||||
let next_byte = bus.read_external(registers.get_pc_address() + 1);
|
||||
let next_second_byte = bus.read_external(registers.get_pc_address() + 2);
|
||||
let word = (next_byte as u16) | ((next_byte as u16) << 8);
|
||||
format!("{:02X} {:02X} {:02X} __ | {} ${:04X}", opcode, next_byte, next_second_byte, instr_name, word)
|
||||
}
|
||||
|
||||
pub fn mnemonic_absolute_long(opcode: u8, instr_name: &str, registers: &Registers, bus: &Bus) -> String {
|
||||
let next_byte = bus.read_external(registers.get_pc_address() + 1);
|
||||
let next_second_byte = bus.read_external(registers.get_pc_address() + 2);
|
||||
let next_third_byte = bus.read_external(registers.get_pc_address() + 3);
|
||||
let word_long = (next_byte as u32) | ((next_second_byte as u32) << 8) | ((next_third_byte as u32) << 16);
|
||||
format!("{:02X} {:02X} {:02X} {:02X} | {} ${:06X}", opcode, next_byte, next_second_byte, next_third_byte, instr_name, word_long)
|
||||
}
|
||||
|
||||
pub fn mnemonic_direct_page(opcode: u8, instr_name: &str, registers: &Registers, bus: &Bus) -> String {
|
||||
let next_byte = bus.read_external(registers.get_pc_address() + 1);
|
||||
format!("{:02X} {:02X} __ __ | {} ${:02X} | dp", opcode, next_byte, instr_name, next_byte)
|
||||
}
|
||||
|
||||
pub fn mnemonic_direct_page_indirect(opcode: u8, instr_name: &str, registers: &Registers, bus: &Bus) -> String {
|
||||
let next_byte = bus.read_external(registers.get_pc_address() + 1);
|
||||
format!("{:02X} {:02X} __ __ | {} (${:02X})", opcode, next_byte, instr_name, next_byte)
|
||||
}
|
||||
|
||||
pub fn mnemonic_direct_page_indirect_long(opcode: u8, instr_name: &str, registers: &Registers, bus: &Bus) -> String {
|
||||
let next_byte = bus.read_external(registers.get_pc_address() + 1);
|
||||
format!("{:02X} {:02X} __ __ | {} [${:02X}]", opcode, next_byte, instr_name, next_byte)
|
||||
}
|
||||
|
||||
pub fn mnemonic_absolute_indexed(opcode: u8, instr_name: &str, index: IndexRegister, registers: &Registers, bus: &Bus) -> String {
|
||||
let next_byte = bus.read_external(registers.get_pc_address() + 1);
|
||||
let next_second_byte = bus.read_external(registers.get_pc_address() + 2);
|
||||
let word = (next_byte as u16) | ((next_byte as u16) << 8);
|
||||
format!("{:02X} {:02X} {:02X} __ | {} ${:04X}, {}", opcode, next_byte, next_second_byte, instr_name, word, index)
|
||||
}
|
||||
|
||||
pub fn mnemonic_absolute_long_indexed(opcode: u8, instr_name: &str, index: IndexRegister, registers: &Registers, bus: &Bus) -> String {
|
||||
let next_byte = bus.read_external(registers.get_pc_address() + 1);
|
||||
let next_second_byte = bus.read_external(registers.get_pc_address() + 2);
|
||||
let next_third_byte = bus.read_external(registers.get_pc_address() + 3);
|
||||
let word_long = (next_byte as u32) | ((next_second_byte as u32) << 8) | ((next_third_byte as u32) << 16);
|
||||
format!("{:02X} {:02X} {:02X} {:02X} | {} ${:06X}, {}", opcode, next_byte, next_second_byte, next_third_byte, instr_name, word_long, index)
|
||||
}
|
||||
|
||||
pub fn mnemonic_direct_page_indexed(opcode: u8, instr_name: &str, index: IndexRegister, registers: &Registers, bus: &Bus) -> String {
|
||||
let next_byte = bus.read_external(registers.get_pc_address() + 1);
|
||||
format!("{:02X} {:02X} __ __ | {} ${:02X}, {} | dp", opcode, next_byte, instr_name, next_byte, index)
|
||||
}
|
||||
|
||||
pub fn mnemonic_direct_page_indexed_indirect(opcode: u8, instr_name: &str, index: IndexRegister, registers: &Registers, bus: &Bus) -> String {
|
||||
let next_byte = bus.read_external(registers.get_pc_address() + 1);
|
||||
format!("{:02X} {:02X} __ __ | {} (${:02X}, {}) | dp", opcode, next_byte, instr_name, next_byte, index)
|
||||
}
|
||||
|
||||
pub fn mnemonic_direct_page_indirect_long_indexed(opcode: u8, instr_name: &str, index: IndexRegister, registers: &Registers, bus: &Bus) -> String {
|
||||
let next_byte = bus.read_external(registers.get_pc_address() + 1);
|
||||
format!("{:02X} {:02X} __ __ | {} [${:02X}], {} | dp", opcode, next_byte, instr_name, next_byte, index)
|
||||
}
|
||||
|
||||
pub fn mnemonic_stack_relative(opcode: u8, instr_name: &str, registers: &Registers, bus: &Bus) -> String {
|
||||
let next_byte = bus.read_external(registers.get_pc_address() + 1);
|
||||
format!("{:02X} {:02X} __ __ | {} ${:02X}, S", opcode, next_byte, instr_name, next_byte)
|
||||
}
|
||||
|
||||
pub fn mnemonic_stack_relative_indirect_indexed(opcode: u8, instr_name: &str, index: IndexRegister, registers: &Registers, bus: &Bus) -> String {
|
||||
let next_byte = bus.read_external(registers.get_pc_address() + 1);
|
||||
format!("{:02X} {:02X} __ __ | {} (${:02X}, S), {}", opcode, next_byte, instr_name, next_byte, index)
|
||||
}
|
||||
|
||||
pub fn mnemonic_branch_nearlabel(opcode: u8, instr_name: &str, registers: &Registers, bus: &Bus) -> String {
|
||||
let nearlabel = bus.read_external(registers.get_pc_address() + 1);
|
||||
format!("{:02X} {:02X} __ __ | {} ${:02X}", opcode, nearlabel, instr_name, nearlabel)
|
||||
}
|
||||
|
||||
pub fn mnemonic_single_byte_instr(opcode: u8, instr_name: &str) -> String {
|
||||
format!("{:02X} __ __ __ | {}", opcode, instr_name)
|
||||
}
|
||||
|
||||
pub fn mnemonic_move(opcode: u8, instr_name: &str, registers: &Registers, bus: &Bus) -> String {
|
||||
let next_byte = bus.read_external(registers.get_pc_address() + 1);
|
||||
let next_second_byte = bus.read_external(registers.get_pc_address() + 2);
|
||||
format!("{:02X} {:02X} {:02X} __ | {} {:02X},{:02X}", opcode, next_byte, next_second_byte, instr_name, next_second_byte, next_byte)
|
||||
}
|
||||
104
snes-core/src/cpu/instructions/dex.rs
Normal file
104
snes-core/src/cpu/instructions/dex.rs
Normal file
@@ -0,0 +1,104 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::{CPUInstruction, dec_common};
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "DEX";
|
||||
|
||||
pub struct DEX {}
|
||||
|
||||
impl DEX {
|
||||
fn determine_instruction(&self, registers: &Registers) -> Box<dyn CPUInstruction> {
|
||||
match registers.is_16bit_index() {
|
||||
true => Box::new(DEX16{}),
|
||||
false => Box::new(DEX8{}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPUInstruction for DEX {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.execute(registers, bus);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.mnemonic(registers, bus, opcode)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DEX8 {}
|
||||
|
||||
impl CPUInstruction for DEX8 {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
let result = dec_common::do_dec(
|
||||
registers,
|
||||
registers.x,
|
||||
) as u8;
|
||||
registers.set_low_x(result);
|
||||
let (bytes, cycles) = cycles::increment_cycles_inc_dec_index();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DEX16 {}
|
||||
|
||||
impl CPUInstruction for DEX16 {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
let result = dec_common::do_dec(
|
||||
registers,
|
||||
registers.x,
|
||||
) as u16;
|
||||
registers.x = result;
|
||||
let (bytes, cycles) = cycles::increment_cycles_inc_dec_index();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_8bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.x = 0x0001;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
let instruction = DEX8{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.x, 0);
|
||||
assert_eq!(registers.pc, 0x01);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
assert!(!registers.get_negative_flag());
|
||||
assert!(registers.get_zero_flag());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_16bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.x = 0x0001;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
let instruction = DEX16{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.x, 0);
|
||||
assert_eq!(registers.pc, 0x01);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
assert!(!registers.get_negative_flag());
|
||||
assert!(registers.get_zero_flag());
|
||||
}
|
||||
}
|
||||
104
snes-core/src/cpu/instructions/dey.rs
Normal file
104
snes-core/src/cpu/instructions/dey.rs
Normal file
@@ -0,0 +1,104 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::{CPUInstruction, dec_common};
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "DEY";
|
||||
|
||||
pub struct DEY {}
|
||||
|
||||
impl DEY {
|
||||
fn determine_instruction(&self, registers: &Registers) -> Box<dyn CPUInstruction> {
|
||||
match registers.is_16bit_index() {
|
||||
true => Box::new(DEY16{}),
|
||||
false => Box::new(DEY8{}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPUInstruction for DEY {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.execute(registers, bus);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.mnemonic(registers, bus, opcode)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DEY8 {}
|
||||
|
||||
impl CPUInstruction for DEY8 {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
let result = dec_common::do_dec(
|
||||
registers,
|
||||
registers.y,
|
||||
) as u8;
|
||||
registers.set_low_y(result);
|
||||
let (bytes, cycles) = cycles::increment_cycles_inc_dec_index();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DEY16 {}
|
||||
|
||||
impl CPUInstruction for DEY16 {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
let result = dec_common::do_dec(
|
||||
registers,
|
||||
registers.y,
|
||||
) as u16;
|
||||
registers.y = result;
|
||||
let (bytes, cycles) = cycles::increment_cycles_inc_dec_index();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_8bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.y = 0x0001;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
let instruction = DEY8{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.y, 0);
|
||||
assert_eq!(registers.pc, 0x01);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
assert!(!registers.get_negative_flag());
|
||||
assert!(registers.get_zero_flag());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_16bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.y = 0x0001;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
let instruction = DEY16{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.y, 0);
|
||||
assert_eq!(registers.pc, 0x01);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
assert!(!registers.get_negative_flag());
|
||||
assert!(registers.get_zero_flag());
|
||||
}
|
||||
}
|
||||
113
snes-core/src/cpu/instructions/eor.rs
Normal file
113
snes-core/src/cpu/instructions/eor.rs
Normal file
@@ -0,0 +1,113 @@
|
||||
use crate::{cpu::{bus::Bus, registers::Registers}, utils::{alu, addressing::AddressingMode}};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::{CPUInstruction, read_write_common::{read_8bit_from_address, read_16bit_from_address}};
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "EOR";
|
||||
|
||||
pub struct EOR {
|
||||
pub addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl EOR {
|
||||
fn determine_instruction(&self, registers: &Registers) -> Box<dyn CPUInstruction> {
|
||||
match registers.is_16bit_mode() {
|
||||
true => Box::new(EOR16{addressing_mode: self.addressing_mode}),
|
||||
false => Box::new(EOR8{addressing_mode: self.addressing_mode}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPUInstruction for EOR {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.execute(registers, bus);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.mnemonic(registers, bus, opcode)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EOR8 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for EOR8 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let value = read_8bit_from_address(registers, bus, self.addressing_mode);
|
||||
let (result, affected_flags) = alu::eor(registers.a as u8, value);
|
||||
registers.set_low_a(result);
|
||||
registers.set_flags(&affected_flags);
|
||||
let (bytes, cycles) = cycles::increment_cycles_bitwise(®isters, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EOR16 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for EOR16 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let value = read_16bit_from_address(registers, bus, self.addressing_mode);
|
||||
let (result, affected_flags) = alu::eor(registers.a, value);
|
||||
registers.a = result;
|
||||
registers.set_flags(&affected_flags);
|
||||
let (bytes, cycles) = cycles::increment_cycles_bitwise(®isters, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_8bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.a = 0x0F;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
registers.set_16bit_mode(false);
|
||||
bus.write(0x000001, 0xF0);
|
||||
let instruction = EOR8{addressing_mode: AddressingMode::Immediate};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.a, 0xFF);
|
||||
assert_eq!(registers.pc, 0x02);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
assert!(registers.get_negative_flag());
|
||||
assert!(!registers.get_zero_flag());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_and_16bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.a = 0x0FFF;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
registers.set_16bit_mode(false);
|
||||
bus.write(0x000002, 0xF0);
|
||||
bus.write(0x000001, 0x00);
|
||||
let instruction = EOR16{addressing_mode: AddressingMode::Immediate};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.a, 0xFFFF);
|
||||
assert_eq!(registers.pc, 0x02);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
assert!(registers.get_negative_flag());
|
||||
assert!(!registers.get_zero_flag());
|
||||
}
|
||||
}
|
||||
112
snes-core/src/cpu/instructions/inc.rs
Normal file
112
snes-core/src/cpu/instructions/inc.rs
Normal file
@@ -0,0 +1,112 @@
|
||||
use crate::{cpu::{bus::Bus, registers::Registers}, utils::addressing::AddressingMode};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::{CPUInstruction, dec_common, read_write_common::{read_8bit_from_address, write_8bit_to_address, read_16bit_from_address, write_16bit_to_address}};
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "INC";
|
||||
|
||||
pub struct INC {
|
||||
pub addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl INC {
|
||||
fn determine_instruction(&self, registers: &Registers) -> Box<dyn CPUInstruction> {
|
||||
match registers.is_16bit_mode() {
|
||||
true => Box::new(INC16{addressing_mode: self.addressing_mode}),
|
||||
false => Box::new(INC8{addressing_mode: self.addressing_mode}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPUInstruction for INC {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.execute(registers, bus);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.mnemonic(registers, bus, opcode)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct INC8 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for INC8 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let result = dec_common::do_inc(
|
||||
registers,
|
||||
read_8bit_from_address(registers, bus, self.addressing_mode),
|
||||
) as u8;
|
||||
write_8bit_to_address(registers, bus, self.addressing_mode, result);
|
||||
let (bytes, cycles) = cycles::increment_cycles_inc_dec(®isters, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct INC16 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for INC16 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let result = dec_common::do_inc(
|
||||
registers,
|
||||
read_16bit_from_address(registers, bus, self.addressing_mode),
|
||||
) as u16;
|
||||
write_16bit_to_address(registers, bus, self.addressing_mode, result);
|
||||
let (bytes, cycles) = cycles::increment_cycles_inc_dec(®isters, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_8bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.a = 0x0001;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
registers.set_memory_select_flag(true);
|
||||
let instruction = INC8{addressing_mode: AddressingMode::Accumulator};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.a, 2);
|
||||
assert_eq!(registers.pc, 0x01);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
assert!(!registers.get_negative_flag());
|
||||
assert!(!registers.get_zero_flag());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_16bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.a = 0x0001;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
registers.set_memory_select_flag(false);
|
||||
let instruction = INC16{addressing_mode: AddressingMode::Accumulator};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.a, 2);
|
||||
assert_eq!(registers.pc, 0x01);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
assert!(!registers.get_negative_flag());
|
||||
assert!(!registers.get_zero_flag());
|
||||
}
|
||||
}
|
||||
104
snes-core/src/cpu/instructions/inx.rs
Normal file
104
snes-core/src/cpu/instructions/inx.rs
Normal file
@@ -0,0 +1,104 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::{CPUInstruction, dec_common};
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "INX";
|
||||
|
||||
pub struct INX {}
|
||||
|
||||
impl INX {
|
||||
fn determine_instruction(&self, registers: &Registers) -> Box<dyn CPUInstruction> {
|
||||
match registers.is_16bit_index() {
|
||||
true => Box::new(INX16{}),
|
||||
false => Box::new(INX8{}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPUInstruction for INX {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.execute(registers, bus);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.mnemonic(registers, bus, opcode)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct INX8 {}
|
||||
|
||||
impl CPUInstruction for INX8 {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
let result = dec_common::do_inc(
|
||||
registers,
|
||||
registers.x,
|
||||
) as u8;
|
||||
registers.set_low_x(result);
|
||||
let (bytes, cycles) = cycles::increment_cycles_inc_dec_index();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct INX16 {}
|
||||
|
||||
impl CPUInstruction for INX16 {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
let result = dec_common::do_inc(
|
||||
registers,
|
||||
registers.x,
|
||||
) as u16;
|
||||
registers.x = result;
|
||||
let (bytes, cycles) = cycles::increment_cycles_inc_dec_index();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_8bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.x = 0x0001;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
let instruction = INX8{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.x, 2);
|
||||
assert_eq!(registers.pc, 0x01);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
assert!(!registers.get_negative_flag());
|
||||
assert!(!registers.get_zero_flag());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_16bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.x = 0x0001;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
let instruction = INX16{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.x, 2);
|
||||
assert_eq!(registers.pc, 0x01);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
assert!(!registers.get_negative_flag());
|
||||
assert!(!registers.get_zero_flag());
|
||||
}
|
||||
}
|
||||
104
snes-core/src/cpu/instructions/iny.rs
Normal file
104
snes-core/src/cpu/instructions/iny.rs
Normal file
@@ -0,0 +1,104 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::{CPUInstruction, dec_common};
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "INY";
|
||||
|
||||
pub struct INY {}
|
||||
|
||||
impl INY {
|
||||
fn determine_instruction(&self, registers: &Registers) -> Box<dyn CPUInstruction> {
|
||||
match registers.is_16bit_index() {
|
||||
true => Box::new(INY16{}),
|
||||
false => Box::new(INY8{}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPUInstruction for INY {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.execute(registers, bus);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.mnemonic(registers, bus, opcode)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct INY8 {}
|
||||
|
||||
impl CPUInstruction for INY8 {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
let result = dec_common::do_inc(
|
||||
registers,
|
||||
registers.y,
|
||||
) as u8;
|
||||
registers.set_low_y(result);
|
||||
let (bytes, cycles) = cycles::increment_cycles_inc_dec_index();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct INY16 {}
|
||||
|
||||
impl CPUInstruction for INY16 {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
let result = dec_common::do_inc(
|
||||
registers,
|
||||
registers.y,
|
||||
) as u16;
|
||||
registers.y = result;
|
||||
let (bytes, cycles) = cycles::increment_cycles_inc_dec_index();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_8bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.y = 0x0001;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
let instruction = INY8{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.y, 2);
|
||||
assert_eq!(registers.pc, 0x01);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
assert!(!registers.get_negative_flag());
|
||||
assert!(!registers.get_zero_flag());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_16bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.y = 0x0001;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
let instruction = INY16{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.y, 2);
|
||||
assert_eq!(registers.pc, 0x01);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
assert!(!registers.get_negative_flag());
|
||||
assert!(!registers.get_zero_flag());
|
||||
}
|
||||
}
|
||||
67
snes-core/src/cpu/instructions/jmp.rs
Normal file
67
snes-core/src/cpu/instructions/jmp.rs
Normal file
@@ -0,0 +1,67 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
use crate::utils::addressing::AddressingMode;
|
||||
|
||||
use super::read_write_common::get_effective_address;
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
use crate::cpu::cycles;
|
||||
|
||||
static INSTR_NAME: &'static str = "JMP";
|
||||
|
||||
pub struct JMP {
|
||||
pub addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for JMP {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let effective_address = get_effective_address(®isters, bus, self.addressing_mode);
|
||||
let is_long = match self.addressing_mode {
|
||||
AddressingMode::AbsoluteLong |
|
||||
AddressingMode::AbsoluteIndirectLong => true,
|
||||
_ => false,
|
||||
};
|
||||
registers.pc = effective_address as u16;
|
||||
if is_long {
|
||||
registers.pbr = (effective_address >> 16) as u8;
|
||||
}
|
||||
let (bytes, cycles) = cycles::increment_cycles_jmp(self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
bus.write(0x000002, 0xAA);
|
||||
bus.write(0x000001, 0xBB);
|
||||
let instruction = JMP{addressing_mode: AddressingMode::Absolute};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0xAABB);
|
||||
assert_eq!(registers.cycles, 3);
|
||||
|
||||
// Test a long address
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.pbr = 0x00;
|
||||
bus.write(0x000003, 0xAA);
|
||||
bus.write(0x000002, 0xBB);
|
||||
bus.write(0x000001, 0xCC);
|
||||
let instruction = JMP{addressing_mode: AddressingMode::AbsoluteLong};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pbr, 0xAA);
|
||||
assert_eq!(registers.pc, 0xBBCC);
|
||||
assert_eq!(registers.cycles, 4);
|
||||
}
|
||||
}
|
||||
77
snes-core/src/cpu/instructions/jsr.rs
Normal file
77
snes-core/src/cpu/instructions/jsr.rs
Normal file
@@ -0,0 +1,77 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
use crate::utils::addressing::AddressingMode;
|
||||
|
||||
use super::read_write_common::get_effective_address;
|
||||
use super::{CPUInstruction, push_common};
|
||||
use super::decoder_common;
|
||||
use crate::cpu::cycles;
|
||||
|
||||
static INSTR_NAME: &'static str = "JSR";
|
||||
|
||||
pub struct JSR {
|
||||
pub addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for JSR {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let effective_address = get_effective_address(registers, bus, self.addressing_mode);
|
||||
let is_long = match self.addressing_mode {
|
||||
AddressingMode::AbsoluteLong |
|
||||
AddressingMode::AbsoluteIndirectLong => true,
|
||||
_ => false,
|
||||
};
|
||||
// We need to push the *next* instruction onto the stack
|
||||
let (bytes, cycles) = cycles::increment_cycles_jsr(self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
let value = registers.get_pc_address();
|
||||
if is_long {
|
||||
push_common::do_push(registers, bus, &[
|
||||
(value >> 16) as u8,
|
||||
(value >> 8) as u8,
|
||||
value as u8,
|
||||
]);
|
||||
} else {
|
||||
push_common::do_push(registers, bus, &[
|
||||
(value >> 8) as u8,
|
||||
value as u8,
|
||||
]);
|
||||
}
|
||||
registers.pc = effective_address as u16;
|
||||
if is_long {
|
||||
registers.pbr = (effective_address >> 16) as u8;
|
||||
}
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
// Test a long address
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x1234;
|
||||
registers.pbr = 0x00;
|
||||
registers.sp = 0x1FC;
|
||||
bus.write(registers.get_pc_address() + 3, 0xAA);
|
||||
bus.write(registers.get_pc_address() + 2, 0xBB);
|
||||
bus.write(registers.get_pc_address() + 1, 0xCC);
|
||||
// write next instruction
|
||||
let instruction = JSR{addressing_mode: AddressingMode::AbsoluteLong};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(bus.read(0x1FC), 0x00);
|
||||
assert_eq!(bus.read(0x1FB), 0x12);
|
||||
assert_eq!(bus.read(0x1FA), 0x38); // we should store the NEXT instruction
|
||||
assert_eq!(registers.pbr, 0xAA);
|
||||
assert_eq!(registers.pc, 0xBBCC);
|
||||
assert_eq!(registers.sp, 0x1F9);
|
||||
assert_eq!(registers.cycles, 8);
|
||||
}
|
||||
}
|
||||
126
snes-core/src/cpu/instructions/lda.rs
Normal file
126
snes-core/src/cpu/instructions/lda.rs
Normal file
@@ -0,0 +1,126 @@
|
||||
use crate::common::flags::Flags;
|
||||
use crate::cpu::cycles;
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
use crate::utils::addressing::AddressingMode;
|
||||
|
||||
use super::read_write_common::{read_8bit_from_address, read_16bit_from_address};
|
||||
use super::{CPUInstruction, bit_common};
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "LDA";
|
||||
|
||||
pub struct LDA {
|
||||
pub addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl LDA {
|
||||
fn determine_instruction(&self, registers: &Registers) -> Box<dyn CPUInstruction> {
|
||||
match registers.is_16bit_mode() {
|
||||
true => Box::new(LDA16{addressing_mode: self.addressing_mode}),
|
||||
false => Box::new(LDA8{addressing_mode: self.addressing_mode}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPUInstruction for LDA {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.execute(registers, bus);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.mnemonic(registers, bus, opcode)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LDA8 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for LDA8 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let value = read_8bit_from_address(registers, bus, self.addressing_mode);
|
||||
registers.set_flags(&[
|
||||
Flags::Negative(value >> 7 == 1),
|
||||
Flags::Zero(value == 0),
|
||||
]);
|
||||
registers.set_low_a(value);
|
||||
bit_common::do_bit(registers, registers.a as u8, value, self.addressing_mode);
|
||||
let (bytes, cycles) = cycles::increment_cycles_lda(®isters, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LDA16 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for LDA16 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let value = read_16bit_from_address(registers, bus, self.addressing_mode);
|
||||
registers.a = value;
|
||||
registers.set_flags(&[
|
||||
Flags::Negative(value >> 15 == 1),
|
||||
Flags::Zero(value == 0),
|
||||
]);
|
||||
let (bytes, cycles) = cycles::increment_cycles_lda(®isters, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_8bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.a = 0x0000;
|
||||
registers.pc = 0x0000;
|
||||
registers.pbr = 0x00;
|
||||
registers.set_negative_flag(false);
|
||||
registers.set_zero_flag(true);
|
||||
registers.set_16bit_mode(false);
|
||||
bus.write(0x0001, 0xFF);
|
||||
let instruction = LDA8{addressing_mode: AddressingMode::Immediate};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0x0002);
|
||||
assert_eq!(registers.a, 0x00FF);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
assert!(registers.get_negative_flag());
|
||||
assert!(!registers.get_zero_flag());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_16bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.a = 0x0000;
|
||||
registers.pc = 0x0000;
|
||||
registers.pbr = 0x00;
|
||||
registers.emulation_mode = false;
|
||||
registers.set_negative_flag(false);
|
||||
registers.set_zero_flag(true);
|
||||
registers.set_16bit_mode(true);
|
||||
bus.write(0x0001, 0xFF);
|
||||
bus.write(0x0002, 0xFF);
|
||||
let instruction = LDA16{addressing_mode: AddressingMode::Immediate};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0x0003);
|
||||
assert_eq!(registers.a, 0xFFFF);
|
||||
assert_eq!(registers.cycles, 3);
|
||||
assert!(registers.get_negative_flag());
|
||||
assert!(!registers.get_zero_flag());
|
||||
}
|
||||
}
|
||||
126
snes-core/src/cpu/instructions/ldx.rs
Normal file
126
snes-core/src/cpu/instructions/ldx.rs
Normal file
@@ -0,0 +1,126 @@
|
||||
use crate::common::flags::Flags;
|
||||
use crate::cpu::cycles;
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
use crate::utils::addressing::AddressingMode;
|
||||
|
||||
use super::read_write_common::{read_8bit_from_address, read_16bit_from_address};
|
||||
use super::{CPUInstruction, bit_common};
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "LDX";
|
||||
|
||||
pub struct LDX {
|
||||
pub addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl LDX {
|
||||
fn determine_instruction(&self, registers: &Registers) -> Box<dyn CPUInstruction> {
|
||||
match registers.is_16bit_index() {
|
||||
true => Box::new(LDX16{addressing_mode: self.addressing_mode}),
|
||||
false => Box::new(LDX8{addressing_mode: self.addressing_mode}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPUInstruction for LDX {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.execute(registers, bus);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.mnemonic(registers, bus, opcode)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LDX8 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for LDX8 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let value = read_8bit_from_address(registers, bus, self.addressing_mode);
|
||||
registers.set_flags(&[
|
||||
Flags::Negative(value >> 7 == 1),
|
||||
Flags::Zero(value == 0),
|
||||
]);
|
||||
registers.set_low_x(value);
|
||||
bit_common::do_bit(registers, registers.x as u8, value, self.addressing_mode);
|
||||
let (bytes, cycles) = cycles::increment_cycles_ld_index(®isters, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LDX16 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for LDX16 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let value = read_16bit_from_address(registers, bus, self.addressing_mode);
|
||||
registers.x = value;
|
||||
registers.set_flags(&[
|
||||
Flags::Negative(value >> 15 == 1),
|
||||
Flags::Zero(value == 0),
|
||||
]);
|
||||
let (bytes, cycles) = cycles::increment_cycles_ld_index(®isters, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_8bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.x = 0x0000;
|
||||
registers.pc = 0x0000;
|
||||
registers.pbr = 0x00;
|
||||
registers.set_negative_flag(false);
|
||||
registers.set_zero_flag(true);
|
||||
registers.set_16bit_index(false);
|
||||
bus.write(0x0001, 0xFF);
|
||||
let instruction = LDX8{addressing_mode: AddressingMode::Immediate};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0x0002);
|
||||
assert_eq!(registers.x, 0x00FF);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
assert!(registers.get_negative_flag());
|
||||
assert!(!registers.get_zero_flag());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_16bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.x = 0x0000;
|
||||
registers.pc = 0x0000;
|
||||
registers.pbr = 0x00;
|
||||
registers.emulation_mode = false;
|
||||
registers.set_negative_flag(false);
|
||||
registers.set_zero_flag(true);
|
||||
registers.set_16bit_index(true);
|
||||
bus.write(0x0001, 0xFF);
|
||||
bus.write(0x0002, 0xFF);
|
||||
let instruction = LDX16{addressing_mode: AddressingMode::Immediate};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0x0003);
|
||||
assert_eq!(registers.x, 0xFFFF);
|
||||
assert_eq!(registers.cycles, 3);
|
||||
assert!(registers.get_negative_flag());
|
||||
assert!(!registers.get_zero_flag());
|
||||
}
|
||||
}
|
||||
126
snes-core/src/cpu/instructions/ldy.rs
Normal file
126
snes-core/src/cpu/instructions/ldy.rs
Normal file
@@ -0,0 +1,126 @@
|
||||
use crate::common::flags::Flags;
|
||||
use crate::cpu::cycles;
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
use crate::utils::addressing::AddressingMode;
|
||||
|
||||
use super::read_write_common::{read_8bit_from_address, read_16bit_from_address};
|
||||
use super::{CPUInstruction, bit_common};
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "LDY";
|
||||
|
||||
pub struct LDY {
|
||||
pub addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl LDY {
|
||||
fn determine_instruction(&self, registers: &Registers) -> Box<dyn CPUInstruction> {
|
||||
match registers.is_16bit_index() {
|
||||
true => Box::new(LDY16{addressing_mode: self.addressing_mode}),
|
||||
false => Box::new(LDY8{addressing_mode: self.addressing_mode}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPUInstruction for LDY {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.execute(registers, bus);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.mnemonic(registers, bus, opcode)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LDY8 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for LDY8 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let value = read_8bit_from_address(registers, bus, self.addressing_mode);
|
||||
registers.set_flags(&[
|
||||
Flags::Negative(value >> 7 == 1),
|
||||
Flags::Zero(value == 0),
|
||||
]);
|
||||
registers.set_low_y(value);
|
||||
bit_common::do_bit(registers, registers.y as u8, value, self.addressing_mode);
|
||||
let (bytes, cycles) = cycles::increment_cycles_ld_index(®isters, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LDY16 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for LDY16 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let value = read_16bit_from_address(registers, bus, self.addressing_mode);
|
||||
registers.y = value;
|
||||
registers.set_flags(&[
|
||||
Flags::Negative(value >> 15 == 1),
|
||||
Flags::Zero(value == 0),
|
||||
]);
|
||||
let (bytes, cycles) = cycles::increment_cycles_ld_index(®isters, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_8bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.y = 0x0000;
|
||||
registers.pc = 0x0000;
|
||||
registers.pbr = 0x00;
|
||||
registers.set_negative_flag(false);
|
||||
registers.set_zero_flag(true);
|
||||
registers.set_16bit_index(false);
|
||||
bus.write(0x0001, 0xFF);
|
||||
let instruction = LDY8{addressing_mode: AddressingMode::Immediate};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0x0002);
|
||||
assert_eq!(registers.y, 0x00FF);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
assert!(registers.get_negative_flag());
|
||||
assert!(!registers.get_zero_flag());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_16bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.y = 0x0000;
|
||||
registers.pc = 0x0000;
|
||||
registers.pbr = 0x00;
|
||||
registers.emulation_mode = false;
|
||||
registers.set_negative_flag(false);
|
||||
registers.set_zero_flag(true);
|
||||
registers.set_16bit_index(true);
|
||||
bus.write(0x0001, 0xFF);
|
||||
bus.write(0x0002, 0xFF);
|
||||
let instruction = LDY16{addressing_mode: AddressingMode::Immediate};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0x0003);
|
||||
assert_eq!(registers.y, 0xFFFF);
|
||||
assert_eq!(registers.cycles, 3);
|
||||
assert!(registers.get_negative_flag());
|
||||
assert!(!registers.get_zero_flag());
|
||||
}
|
||||
}
|
||||
152
snes-core/src/cpu/instructions/lsr.rs
Normal file
152
snes-core/src/cpu/instructions/lsr.rs
Normal file
@@ -0,0 +1,152 @@
|
||||
use crate::{cpu::{bus::Bus, registers::Registers}, utils::{alu, addressing::AddressingMode}};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::{CPUInstruction, read_write_common::{write_8bit_to_address, read_8bit_from_address, read_16bit_from_address, write_16bit_to_address}};
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "LSR";
|
||||
|
||||
pub struct LSR {
|
||||
pub addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl LSR {
|
||||
fn determine_instruction(&self, registers: &Registers) -> Box<dyn CPUInstruction> {
|
||||
match registers.is_16bit_mode() {
|
||||
true => Box::new(LSR16{addressing_mode: self.addressing_mode}),
|
||||
false => Box::new(LSR8{addressing_mode: self.addressing_mode}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPUInstruction for LSR {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.execute(registers, bus);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.mnemonic(registers, bus, opcode)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LSR8 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for LSR8 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let (result, affected_flags) = alu::lsr(read_8bit_from_address(registers, bus, self.addressing_mode));
|
||||
write_8bit_to_address(registers, bus, self.addressing_mode, result);
|
||||
registers.set_flags(&affected_flags);
|
||||
let (bytes, cycles) = cycles::increment_cycles_shift(®isters, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LSR16 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for LSR16 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let (result, affected_flags) = alu::lsr(read_16bit_from_address(registers, bus, self.addressing_mode));
|
||||
write_16bit_to_address(registers, bus, self.addressing_mode, result);
|
||||
registers.set_flags(&affected_flags);
|
||||
let (bytes, cycles) = cycles::increment_cycles_shift(®isters, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_8bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.a = 0b00000000_00000011;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
registers.set_16bit_mode(false);
|
||||
registers.set_negative_flag(true);
|
||||
registers.set_carry_flag(false);
|
||||
let instruction = LSR8{addressing_mode: AddressingMode::Accumulator};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.a, 0b00000000_00000001);
|
||||
assert_eq!(registers.pc, 0x01);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
assert!(registers.get_carry_flag());
|
||||
assert!(!registers.get_zero_flag());
|
||||
assert!(!registers.get_negative_flag());
|
||||
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.a = 0b00000001_10000011;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
registers.set_16bit_mode(false);
|
||||
registers.set_negative_flag(true);
|
||||
registers.set_carry_flag(false);
|
||||
let instruction = LSR8{addressing_mode: AddressingMode::Accumulator};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.a, 0b00000001_01000001);
|
||||
assert_eq!(registers.pc, 0x01);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
assert!(registers.get_carry_flag());
|
||||
assert!(!registers.get_zero_flag());
|
||||
assert!(!registers.get_negative_flag());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_16bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.a = 0b00000000_00000011;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
registers.set_16bit_mode(true);
|
||||
registers.set_negative_flag(true);
|
||||
registers.set_carry_flag(false);
|
||||
let instruction = LSR16{addressing_mode: AddressingMode::Accumulator};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.a, 0b00000000_00000001);
|
||||
assert_eq!(registers.pc, 0x01);
|
||||
assert_eq!(registers.cycles, 4);
|
||||
assert!(registers.get_carry_flag());
|
||||
assert!(!registers.get_zero_flag());
|
||||
assert!(!registers.get_negative_flag());
|
||||
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.a = 0b10000000_00000011;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
registers.set_16bit_mode(true);
|
||||
registers.set_negative_flag(true);
|
||||
registers.set_carry_flag(true);
|
||||
let instruction = LSR16{addressing_mode: AddressingMode::Accumulator};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.a, 0b01000000_00000001);
|
||||
assert_eq!(registers.pc, 0x01);
|
||||
assert_eq!(registers.cycles, 4);
|
||||
assert!(registers.get_carry_flag());
|
||||
assert!(!registers.get_zero_flag());
|
||||
assert!(!registers.get_negative_flag());
|
||||
}
|
||||
}
|
||||
356
snes-core/src/cpu/instructions/mapper.rs
Normal file
356
snes-core/src/cpu/instructions/mapper.rs
Normal file
@@ -0,0 +1,356 @@
|
||||
use crate::{utils::addressing::{AddressingMode, IndexRegister}, cpu::instructions::{adc::ADC, and::AND, asl::ASL, bcc::BCC, bcs::BCS, beq::BEQ, bne::BNE, bmi::BMI, bpl::BPL, bra::BRA, brk::BRK, brl::BRL, bvc::BVC, bvs::BVS, bit::BIT, clc::CLC, cld::CLD, cli::CLI, clv::CLV, cmp::CMP, cop::COP, cpx::CPX, cpy::CPY, dec::DEC, dex::DEX, dey::DEY, eor::EOR, inc::INC, iny::INY, inx::INX, jmp::JMP, jsr::JSR, lda::LDA, ldx::LDX, ldy::LDY, lsr::LSR, mvn::MVN, mvp::MVP, nop::NOP, ora::ORA, pea::PEA, pei::PEI, per::PER, pha::PHA, phb::PHB, phd::PHD, phk::PHK, php::PHP, phx::PHX, phy::PHY, pla::PLA, plb::PLB, pld::PLD, plp::PLP, plx::PLX, ply::PLY, rep::REP, rol::ROL, ror::ROR, rti::RTI, rtl::RTL, rts::RTS, sbc::SBC, sec::SEC, sed::SED, sei::SEI, sep::SEP, sta::STA, stp::STP, stx::STX, sty::STY, stz::STZ, tax::TAX, tay::TAY, tcd::TCD, tcs::TCS, trb::TRB, tsb::TSB, tdc::TDC, tsc::TSC, tsx::TSX, txa::TXA, txs::TXS, txy::TXY, tya::TYA, tyx::TYX, wai::WAI, wdm::WDM, xba::XBA, xce::XCE}};
|
||||
|
||||
use super::CPUInstruction;
|
||||
|
||||
pub fn map_opcode_to_instruction(opcode: u8) -> Box<dyn CPUInstruction> {
|
||||
type A = AddressingMode;
|
||||
type I = IndexRegister;
|
||||
match opcode {
|
||||
// ADC
|
||||
0x69 => Box::new(ADC{addressing_mode: A::Immediate}),
|
||||
0x6D => Box::new(ADC{addressing_mode: A::Absolute}),
|
||||
0x6F => Box::new(ADC{addressing_mode: A::AbsoluteLong}),
|
||||
0x65 => Box::new(ADC{addressing_mode: A::DirectPage}),
|
||||
0x72 => Box::new(ADC{addressing_mode: A::DirectPageIndirect}),
|
||||
0x67 => Box::new(ADC{addressing_mode: A::DirectPageIndirectLong}),
|
||||
0x7D => Box::new(ADC{addressing_mode: A::AbsoluteIndexed(I::X)}),
|
||||
0x7F => Box::new(ADC{addressing_mode: A::AbsoluteLongIndexed(I::X)}),
|
||||
0x79 => Box::new(ADC{addressing_mode: A::AbsoluteIndexed(I::Y)}),
|
||||
0x75 => Box::new(ADC{addressing_mode: A::DirectPageIndexed(I::X)}),
|
||||
0x61 => Box::new(ADC{addressing_mode: A::DirectPageIndexedIndirect(I::X)}),
|
||||
0x71 => Box::new(ADC{addressing_mode: A::DirectPageIndirectIndexed(I::Y)}),
|
||||
0x77 => Box::new(ADC{addressing_mode: A::DirectPageIndirectLongIndexed(I::Y)}),
|
||||
0x63 => Box::new(ADC{addressing_mode: A::StackRelative}),
|
||||
0x73 => Box::new(ADC{addressing_mode: A::StackRelativeIndirectIndexed(I::Y)}),
|
||||
// AND
|
||||
0x29 => Box::new(AND{addressing_mode: A::Immediate}),
|
||||
0x2D => Box::new(AND{addressing_mode: A::Absolute}),
|
||||
0x2F => Box::new(AND{addressing_mode: A::AbsoluteLong}),
|
||||
0x25 => Box::new(AND{addressing_mode: A::DirectPage}),
|
||||
0x32 => Box::new(AND{addressing_mode: A::DirectPageIndirect}),
|
||||
0x27 => Box::new(AND{addressing_mode: A::DirectPageIndirectLong}),
|
||||
0x3D => Box::new(AND{addressing_mode: A::AbsoluteIndexed(I::X)}),
|
||||
0x3F => Box::new(AND{addressing_mode: A::AbsoluteLongIndexed(I::X)}),
|
||||
0x39 => Box::new(AND{addressing_mode: A::AbsoluteIndexed(I::Y)}),
|
||||
0x35 => Box::new(AND{addressing_mode: A::DirectPageIndexed(I::X)}),
|
||||
0x21 => Box::new(AND{addressing_mode: A::DirectPageIndexedIndirect(I::X)}),
|
||||
0x31 => Box::new(AND{addressing_mode: A::DirectPageIndirectIndexed(I::Y)}),
|
||||
0x37 => Box::new(AND{addressing_mode: A::DirectPageIndirectLongIndexed(I::Y)}),
|
||||
0x23 => Box::new(AND{addressing_mode: A::StackRelative}),
|
||||
0x33 => Box::new(AND{addressing_mode: A::StackRelativeIndirectIndexed(I::Y)}),
|
||||
// ASL
|
||||
0x0A => Box::new(ASL{addressing_mode: A::Accumulator}),
|
||||
0x0E => Box::new(ASL{addressing_mode: A::Absolute}),
|
||||
0x06 => Box::new(ASL{addressing_mode: A::DirectPage}),
|
||||
0x1E => Box::new(ASL{addressing_mode: A::AbsoluteIndexed(I::X)}),
|
||||
0x16 => Box::new(ASL{addressing_mode: A::DirectPageIndexed(I::X)}),
|
||||
// BCC
|
||||
0x90 => Box::new(BCC{}),
|
||||
// BCS
|
||||
0xB0 => Box::new(BCS{}),
|
||||
// BEQ
|
||||
0xF0 => Box::new(BEQ{}),
|
||||
// BNE
|
||||
0xD0 => Box::new(BNE{}),
|
||||
// BMI
|
||||
0x30 => Box::new(BMI{}),
|
||||
// BPL
|
||||
0x10 => Box::new(BPL{}),
|
||||
// BRA
|
||||
0x80 => Box::new(BRA{}),
|
||||
// BRK
|
||||
0x00 => Box::new(BRK{}),
|
||||
// BRL
|
||||
0x82 => Box::new(BRL{}),
|
||||
// BVC
|
||||
0x50 => Box::new(BVC{}),
|
||||
// BVS
|
||||
0x70 => Box::new(BVS{}),
|
||||
// BIT
|
||||
0x89 => Box::new(BIT{addressing_mode: A::Immediate}),
|
||||
0x2C => Box::new(BIT{addressing_mode: A::Absolute}),
|
||||
0x24 => Box::new(BIT{addressing_mode: A::DirectPage}),
|
||||
0x3C => Box::new(BIT{addressing_mode: A::AbsoluteIndexed(I::X)}),
|
||||
0x34 => Box::new(BIT{addressing_mode: A::DirectPageIndexed(I::X)}),
|
||||
// CLC
|
||||
0x18 => Box::new(CLC{}),
|
||||
// CLD
|
||||
0xD8 => Box::new(CLD{}),
|
||||
// CLI
|
||||
0x58 => Box::new(CLI{}),
|
||||
// CLV
|
||||
0xB8 => Box::new(CLV{}),
|
||||
// CMP
|
||||
0xC9 => Box::new(CMP{addressing_mode: A::Immediate}),
|
||||
0xCD => Box::new(CMP{addressing_mode: A::Absolute}),
|
||||
0xCF => Box::new(CMP{addressing_mode: A::AbsoluteLong}),
|
||||
0xC5 => Box::new(CMP{addressing_mode: A::DirectPage}),
|
||||
0xD2 => Box::new(CMP{addressing_mode: A::DirectPageIndirect}),
|
||||
0xC7 => Box::new(CMP{addressing_mode: A::DirectPageIndirectLong}),
|
||||
0xDD => Box::new(CMP{addressing_mode: A::AbsoluteIndexed(I::X)}),
|
||||
0xDF => Box::new(CMP{addressing_mode: A::AbsoluteLongIndexed(I::X)}),
|
||||
0xD9 => Box::new(CMP{addressing_mode: A::AbsoluteIndexed(I::Y)}),
|
||||
0xD5 => Box::new(CMP{addressing_mode: A::DirectPageIndexed(I::X)}),
|
||||
0xC1 => Box::new(CMP{addressing_mode: A::DirectPageIndexedIndirect(I::X)}),
|
||||
0xD1 => Box::new(CMP{addressing_mode: A::DirectPageIndirectIndexed(I::Y)}),
|
||||
0xD7 => Box::new(CMP{addressing_mode: A::DirectPageIndirectLongIndexed(I::Y)}),
|
||||
0xC3 => Box::new(CMP{addressing_mode: A::StackRelative}),
|
||||
0xD3 => Box::new(CMP{addressing_mode: A::StackRelativeIndirectIndexed(I::Y)}),
|
||||
// COP
|
||||
0x02 => Box::new(COP{}),
|
||||
// CPX
|
||||
0xE0 => Box::new(CPX{addressing_mode: A::Immediate}),
|
||||
0xEC => Box::new(CPX{addressing_mode: A::Absolute}),
|
||||
0xE4 => Box::new(CPX{addressing_mode: A::DirectPage}),
|
||||
// CPY
|
||||
0xC0 => Box::new(CPY{addressing_mode: A::Immediate}),
|
||||
0xCC => Box::new(CPY{addressing_mode: A::Absolute}),
|
||||
0xC4 => Box::new(CPY{addressing_mode: A::DirectPage}),
|
||||
// DEC
|
||||
0x3A => Box::new(DEC{addressing_mode: A::Accumulator}),
|
||||
0xCE => Box::new(DEC{addressing_mode: A::Absolute}),
|
||||
0xC6 => Box::new(DEC{addressing_mode: A::DirectPage}),
|
||||
0xDE => Box::new(DEC{addressing_mode: A::AbsoluteIndexed(I::X)}),
|
||||
0xD6 => Box::new(DEC{addressing_mode: A::DirectPageIndexed(I::X)}),
|
||||
// DEX
|
||||
0xCA => Box::new(DEX{}),
|
||||
// DEY
|
||||
0x88 => Box::new(DEY{}),
|
||||
// EOR
|
||||
0x49 => Box::new(EOR{addressing_mode: A::Immediate}),
|
||||
0x4D => Box::new(EOR{addressing_mode: A::Absolute}),
|
||||
0x4F => Box::new(EOR{addressing_mode: A::AbsoluteLong}),
|
||||
0x45 => Box::new(EOR{addressing_mode: A::DirectPage}),
|
||||
0x52 => Box::new(EOR{addressing_mode: A::DirectPageIndirect}),
|
||||
0x47 => Box::new(EOR{addressing_mode: A::DirectPageIndirectLong}),
|
||||
0x5D => Box::new(EOR{addressing_mode: A::AbsoluteIndexed(I::X)}),
|
||||
0x5F => Box::new(EOR{addressing_mode: A::AbsoluteLongIndexed(I::X)}),
|
||||
0x59 => Box::new(EOR{addressing_mode: A::AbsoluteIndexed(I::Y)}),
|
||||
0x55 => Box::new(EOR{addressing_mode: A::DirectPageIndexed(I::X)}),
|
||||
0x41 => Box::new(EOR{addressing_mode: A::DirectPageIndexedIndirect(I::X)}),
|
||||
0x51 => Box::new(EOR{addressing_mode: A::DirectPageIndirectIndexed(I::Y)}),
|
||||
0x57 => Box::new(EOR{addressing_mode: A::DirectPageIndirectLongIndexed(I::Y)}),
|
||||
0x43 => Box::new(EOR{addressing_mode: A::StackRelative}),
|
||||
0x53 => Box::new(EOR{addressing_mode: A::StackRelativeIndirectIndexed(I::Y)}),
|
||||
// INC
|
||||
0x1A => Box::new(INC{addressing_mode: A::Accumulator}),
|
||||
0xEE => Box::new(INC{addressing_mode: A::Absolute}),
|
||||
0xE6 => Box::new(INC{addressing_mode: A::DirectPage}),
|
||||
0xFE => Box::new(INC{addressing_mode: A::AbsoluteIndexed(I::X)}),
|
||||
0xF6 => Box::new(INC{addressing_mode: A::DirectPageIndexed(I::X)}),
|
||||
// INX
|
||||
0xE8 => Box::new(INX{}),
|
||||
// INY
|
||||
0xC8 => Box::new(INY{}),
|
||||
// JMP
|
||||
0x4C => Box::new(JMP{addressing_mode: A::Absolute}),
|
||||
0x6C => Box::new(JMP{addressing_mode: A::AbsoluteIndirect}),
|
||||
0x7C => Box::new(JMP{addressing_mode: A::AbsoluteIndexedIndirect(I::X)}),
|
||||
0x5C => Box::new(JMP{addressing_mode: A::AbsoluteLong}),
|
||||
0xDC => Box::new(JMP{addressing_mode: A::AbsoluteIndirectLong}),
|
||||
// JSR
|
||||
0x20 => Box::new(JSR{addressing_mode: A::Absolute}),
|
||||
0xFC => Box::new(JSR{addressing_mode: A::AbsoluteIndexedIndirect(I::X)}),
|
||||
0x22 => Box::new(JSR{addressing_mode: A::AbsoluteLong}), // same as JSL
|
||||
// LDA
|
||||
0xA9 => Box::new(LDA{addressing_mode: A::Immediate}),
|
||||
0xAD => Box::new(LDA{addressing_mode: A::Absolute}),
|
||||
0xAF => Box::new(LDA{addressing_mode: A::AbsoluteLong}),
|
||||
0xA5 => Box::new(LDA{addressing_mode: A::DirectPage}),
|
||||
0xB2 => Box::new(LDA{addressing_mode: A::DirectPageIndirect}),
|
||||
0xA7 => Box::new(LDA{addressing_mode: A::DirectPageIndirectLong}),
|
||||
0xBD => Box::new(LDA{addressing_mode: A::AbsoluteIndexed(I::X)}),
|
||||
0xBF => Box::new(LDA{addressing_mode: A::AbsoluteLongIndexed(I::X)}),
|
||||
0xB9 => Box::new(LDA{addressing_mode: A::AbsoluteIndexed(I::Y)}),
|
||||
0xB5 => Box::new(LDA{addressing_mode: A::DirectPageIndexed(I::X)}),
|
||||
0xA1 => Box::new(LDA{addressing_mode: A::DirectPageIndexedIndirect(I::X)}),
|
||||
0xB1 => Box::new(LDA{addressing_mode: A::DirectPageIndirectIndexed(I::Y)}),
|
||||
0xB7 => Box::new(LDA{addressing_mode: A::DirectPageIndirectLongIndexed(I::Y)}),
|
||||
0xA3 => Box::new(LDA{addressing_mode: A::StackRelative}),
|
||||
0xB3 => Box::new(LDA{addressing_mode: A::StackRelativeIndirectIndexed(I::Y)}),
|
||||
// LDX
|
||||
0xA2 => Box::new(LDX{addressing_mode: A::Immediate}),
|
||||
0xAE => Box::new(LDX{addressing_mode: A::Absolute}),
|
||||
0xA6 => Box::new(LDX{addressing_mode: A::DirectPage}),
|
||||
0xBE => Box::new(LDX{addressing_mode: A::AbsoluteIndexed(I::Y)}),
|
||||
0xB6 => Box::new(LDX{addressing_mode: A::DirectPageIndexed(I::Y)}),
|
||||
// LDY
|
||||
0xA0 => Box::new(LDY{addressing_mode: A::Immediate}),
|
||||
0xAC => Box::new(LDY{addressing_mode: A::Absolute}),
|
||||
0xA4 => Box::new(LDY{addressing_mode: A::DirectPage}),
|
||||
0xB4 => Box::new(LDY{addressing_mode: A::AbsoluteIndexed(I::Y)}),
|
||||
0xBC => Box::new(LDY{addressing_mode: A::DirectPageIndexed(I::Y)}),
|
||||
// LSR
|
||||
0x4A => Box::new(LSR{addressing_mode: A::Accumulator}),
|
||||
0x4E => Box::new(LSR{addressing_mode: A::Absolute}),
|
||||
0x46 => Box::new(LSR{addressing_mode: A::DirectPage}),
|
||||
0x5E => Box::new(LSR{addressing_mode: A::AbsoluteIndexed(I::X)}),
|
||||
0x56 => Box::new(LSR{addressing_mode: A::DirectPageIndexed(I::X)}),
|
||||
// MVN
|
||||
0x54 => Box::new(MVN{}),
|
||||
// MVP
|
||||
0x44 => Box::new(MVP{}),
|
||||
// NOP
|
||||
0xEA => Box::new(NOP{}),
|
||||
// ORA
|
||||
0x09 => Box::new(ORA{addressing_mode: A::Immediate}),
|
||||
0x0D => Box::new(ORA{addressing_mode: A::Absolute}),
|
||||
0x0F => Box::new(ORA{addressing_mode: A::AbsoluteLong}),
|
||||
0x05 => Box::new(ORA{addressing_mode: A::DirectPage}),
|
||||
0x12 => Box::new(ORA{addressing_mode: A::DirectPageIndirect}),
|
||||
0x07 => Box::new(ORA{addressing_mode: A::DirectPageIndirectLong}),
|
||||
0x1D => Box::new(ORA{addressing_mode: A::AbsoluteIndexed(I::X)}),
|
||||
0x1F => Box::new(ORA{addressing_mode: A::AbsoluteLongIndexed(I::X)}),
|
||||
0x19 => Box::new(ORA{addressing_mode: A::AbsoluteIndexed(I::Y)}),
|
||||
0x15 => Box::new(ORA{addressing_mode: A::DirectPageIndexed(I::X)}),
|
||||
0x01 => Box::new(ORA{addressing_mode: A::DirectPageIndexedIndirect(I::X)}),
|
||||
0x11 => Box::new(ORA{addressing_mode: A::DirectPageIndirectIndexed(I::Y)}),
|
||||
0x17 => Box::new(ORA{addressing_mode: A::DirectPageIndirectLongIndexed(I::Y)}),
|
||||
0x03 => Box::new(ORA{addressing_mode: A::StackRelative}),
|
||||
0x13 => Box::new(ORA{addressing_mode: A::StackRelativeIndirectIndexed(I::Y)}),
|
||||
// PEA
|
||||
0xF4 => Box::new(PEA{}),
|
||||
// PEI
|
||||
0xD4 => Box::new(PEI{}),
|
||||
// PER
|
||||
0x62 => Box::new(PER{}),
|
||||
// PHA
|
||||
0x48 => Box::new(PHA{}),
|
||||
// PHB
|
||||
0x8B => Box::new(PHB{}),
|
||||
// PHD
|
||||
0x0B => Box::new(PHD{}),
|
||||
// PHK
|
||||
0x4B => Box::new(PHK{}),
|
||||
// PHP
|
||||
0x08 => Box::new(PHP{}),
|
||||
// PHX
|
||||
0xDA => Box::new(PHX{}),
|
||||
// PHY
|
||||
0x5A => Box::new(PHY{}),
|
||||
// PLA
|
||||
0x68 => Box::new(PLA{}),
|
||||
// PLB
|
||||
0xAB => Box::new(PLB{}),
|
||||
// PLD
|
||||
0x2B => Box::new(PLD{}),
|
||||
// PLP
|
||||
0x28 => Box::new(PLP{}),
|
||||
// PLX
|
||||
0xFA => Box::new(PLX{}),
|
||||
// PLY
|
||||
0x7A => Box::new(PLY{}),
|
||||
// REP
|
||||
0xC2 => Box::new(REP{}),
|
||||
// ROL
|
||||
0x2A => Box::new(ROL{addressing_mode: AddressingMode::Accumulator}),
|
||||
0x2E => Box::new(ROL{addressing_mode: AddressingMode::Absolute}),
|
||||
0x26 => Box::new(ROL{addressing_mode: AddressingMode::DirectPage}),
|
||||
0x3E => Box::new(ROL{addressing_mode: AddressingMode::AbsoluteIndexed(I::X)}),
|
||||
0x36 => Box::new(ROL{addressing_mode: AddressingMode::DirectPageIndexed(I::X)}),
|
||||
// ROR
|
||||
0x6A => Box::new(ROR{addressing_mode: AddressingMode::Accumulator}),
|
||||
0x6E => Box::new(ROR{addressing_mode: AddressingMode::Absolute}),
|
||||
0x66 => Box::new(ROR{addressing_mode: AddressingMode::DirectPage}),
|
||||
0x7E => Box::new(ROR{addressing_mode: AddressingMode::AbsoluteIndexed(I::X)}),
|
||||
0x76 => Box::new(ROR{addressing_mode: AddressingMode::DirectPageIndexed(I::X)}),
|
||||
// RTI
|
||||
0x40 => Box::new(RTI{}),
|
||||
// RTL
|
||||
0x6B => Box::new(RTL{}),
|
||||
// RTS
|
||||
0x60 => Box::new(RTS{}),
|
||||
// SBC
|
||||
0xE9 => Box::new(SBC{addressing_mode: A::Immediate}),
|
||||
0xED => Box::new(SBC{addressing_mode: A::Absolute}),
|
||||
0xEF => Box::new(SBC{addressing_mode: A::AbsoluteLong}),
|
||||
0xE5 => Box::new(SBC{addressing_mode: A::DirectPage}),
|
||||
0xF2 => Box::new(SBC{addressing_mode: A::DirectPageIndirect}),
|
||||
0xE7 => Box::new(SBC{addressing_mode: A::DirectPageIndirectLong}),
|
||||
0xFD => Box::new(SBC{addressing_mode: A::AbsoluteIndexed(I::X)}),
|
||||
0xFF => Box::new(SBC{addressing_mode: A::AbsoluteLongIndexed(I::X)}),
|
||||
0xF9 => Box::new(SBC{addressing_mode: A::AbsoluteIndexed(I::Y)}),
|
||||
0xF5 => Box::new(SBC{addressing_mode: A::DirectPageIndexed(I::X)}),
|
||||
0xE1 => Box::new(SBC{addressing_mode: A::DirectPageIndexedIndirect(I::X)}),
|
||||
0xF1 => Box::new(SBC{addressing_mode: A::DirectPageIndirectIndexed(I::Y)}),
|
||||
0xF7 => Box::new(SBC{addressing_mode: A::DirectPageIndirectLongIndexed(I::Y)}),
|
||||
0xE3 => Box::new(SBC{addressing_mode: A::StackRelative}),
|
||||
0xF3 => Box::new(SBC{addressing_mode: A::StackRelativeIndirectIndexed(I::Y)}),
|
||||
// SEC
|
||||
0x38 => Box::new(SEC{}),
|
||||
// SED
|
||||
0xF8 => Box::new(SED{}),
|
||||
// SEI
|
||||
0x78 => Box::new(SEI{}),
|
||||
// SEP
|
||||
0xE2 => Box::new(SEP{}),
|
||||
// STA
|
||||
0x8D => Box::new(STA{addressing_mode: A::Absolute}),
|
||||
0x8F => Box::new(STA{addressing_mode: A::AbsoluteLong}),
|
||||
0x85 => Box::new(STA{addressing_mode: A::DirectPage}),
|
||||
0x92 => Box::new(STA{addressing_mode: A::DirectPageIndirect}),
|
||||
0x87 => Box::new(STA{addressing_mode: A::DirectPageIndirectLong}),
|
||||
0x9D => Box::new(STA{addressing_mode: A::AbsoluteIndexed(I::X)}),
|
||||
0x9F => Box::new(STA{addressing_mode: A::AbsoluteLongIndexed(I::X)}),
|
||||
0x99 => Box::new(STA{addressing_mode: A::AbsoluteIndexed(I::Y)}),
|
||||
0x95 => Box::new(STA{addressing_mode: A::DirectPageIndexed(I::X)}),
|
||||
0x81 => Box::new(STA{addressing_mode: A::DirectPageIndexedIndirect(I::X)}),
|
||||
0x91 => Box::new(STA{addressing_mode: A::DirectPageIndirectIndexed(I::Y)}),
|
||||
0x97 => Box::new(STA{addressing_mode: A::DirectPageIndirectLongIndexed(I::Y)}),
|
||||
0x83 => Box::new(STA{addressing_mode: A::StackRelative}),
|
||||
0x93 => Box::new(STA{addressing_mode: A::StackRelativeIndirectIndexed(I::Y)}),
|
||||
// STP
|
||||
0xDB => Box::new(STP{}),
|
||||
// STX
|
||||
0x8E => Box::new(STX{addressing_mode: A::Absolute}),
|
||||
0x86 => Box::new(STX{addressing_mode: A::DirectPage}),
|
||||
0x96 => Box::new(STX{addressing_mode: A::DirectPageIndexed(I::Y)}),
|
||||
// STY
|
||||
0x8C => Box::new(STY{addressing_mode: A::Absolute}),
|
||||
0x84 => Box::new(STY{addressing_mode: A::DirectPage}),
|
||||
0x94 => Box::new(STY{addressing_mode: A::DirectPageIndexed(I::X)}),
|
||||
// STZ
|
||||
0x9C => Box::new(STZ{addressing_mode: A::Absolute}),
|
||||
0x64 => Box::new(STZ{addressing_mode: A::DirectPage}),
|
||||
0x9E => Box::new(STZ{addressing_mode: A::AbsoluteIndexed(I::X)}),
|
||||
0x74 => Box::new(STZ{addressing_mode: A::DirectPageIndexed(I::X)}),
|
||||
// TAX
|
||||
0xAA => Box::new(TAX{}),
|
||||
// TAY
|
||||
0xA8 => Box::new(TAY{}),
|
||||
// TCD
|
||||
0x5B => Box::new(TCD{}),
|
||||
// TCS
|
||||
0x1B => Box::new(TCS{}),
|
||||
// TDC
|
||||
0x7B => Box::new(TDC{}),
|
||||
// TRB
|
||||
0x1C => Box::new(TRB{addressing_mode: A::Absolute}),
|
||||
0x14 => Box::new(TRB{addressing_mode: A::DirectPage}),
|
||||
// TSB
|
||||
0x0C => Box::new(TSB{addressing_mode: A::Absolute}),
|
||||
0x04 => Box::new(TSB{addressing_mode: A::DirectPage}),
|
||||
// TSC
|
||||
0x3B => Box::new(TSC{}),
|
||||
// TSX
|
||||
0xBA => Box::new(TSX{}),
|
||||
// TXA
|
||||
0x8A => Box::new(TXA{}),
|
||||
// TXS
|
||||
0x9A => Box::new(TXS{}),
|
||||
// TXY
|
||||
0x9B => Box::new(TXY{}),
|
||||
// TYA
|
||||
0x98 => Box::new(TYA{}),
|
||||
// TYX
|
||||
0xBB => Box::new(TYX{}),
|
||||
// WAI
|
||||
0xCB => Box::new(WAI{}),
|
||||
// WDM
|
||||
0x42 => Box::new(WDM{}),
|
||||
// XBA
|
||||
0xEB => Box::new(XBA{}),
|
||||
// XCE
|
||||
0xFB => Box::new(XCE{}),
|
||||
}
|
||||
}
|
||||
108
snes-core/src/cpu/instructions/mod.rs
Normal file
108
snes-core/src/cpu/instructions/mod.rs
Normal file
@@ -0,0 +1,108 @@
|
||||
use crate::cpu::bus::Bus;
|
||||
use crate::cpu::registers::Registers;
|
||||
|
||||
pub mod adc;
|
||||
pub mod and;
|
||||
pub mod asl;
|
||||
pub mod bcc;
|
||||
pub mod bcs;
|
||||
pub mod beq;
|
||||
pub mod bne;
|
||||
pub mod bmi;
|
||||
pub mod bpl;
|
||||
pub mod bra;
|
||||
pub mod brk;
|
||||
pub mod brl;
|
||||
pub mod bvc;
|
||||
pub mod bvs;
|
||||
pub mod bit;
|
||||
pub mod clc;
|
||||
pub mod cld;
|
||||
pub mod cli;
|
||||
pub mod clv;
|
||||
pub mod cmp;
|
||||
pub mod cop;
|
||||
pub mod cpx;
|
||||
pub mod cpy;
|
||||
pub mod dec;
|
||||
pub mod dex;
|
||||
pub mod dey;
|
||||
pub mod eor;
|
||||
pub mod inc;
|
||||
pub mod inx;
|
||||
pub mod iny;
|
||||
pub mod jmp;
|
||||
pub mod jsr;
|
||||
pub mod lda;
|
||||
pub mod ldx;
|
||||
pub mod ldy;
|
||||
pub mod lsr;
|
||||
pub mod mvn;
|
||||
pub mod mvp;
|
||||
pub mod nop;
|
||||
pub mod ora;
|
||||
pub mod pea;
|
||||
pub mod pei;
|
||||
pub mod per;
|
||||
pub mod pha;
|
||||
pub mod phb;
|
||||
pub mod phd;
|
||||
pub mod phk;
|
||||
pub mod php;
|
||||
pub mod phx;
|
||||
pub mod phy;
|
||||
pub mod pla;
|
||||
pub mod plb;
|
||||
pub mod pld;
|
||||
pub mod plp;
|
||||
pub mod plx;
|
||||
pub mod ply;
|
||||
pub mod rep;
|
||||
pub mod rol;
|
||||
pub mod ror;
|
||||
pub mod rti;
|
||||
pub mod rtl;
|
||||
pub mod rts;
|
||||
pub mod sbc;
|
||||
pub mod sec;
|
||||
pub mod sed;
|
||||
pub mod sei;
|
||||
pub mod sep;
|
||||
pub mod sta;
|
||||
pub mod stx;
|
||||
pub mod sty;
|
||||
pub mod stp;
|
||||
pub mod stz;
|
||||
pub mod tax;
|
||||
pub mod tay;
|
||||
pub mod tcd;
|
||||
pub mod tcs;
|
||||
pub mod tdc;
|
||||
pub mod trb;
|
||||
pub mod tsb;
|
||||
pub mod tsc;
|
||||
pub mod tsx;
|
||||
pub mod txa;
|
||||
pub mod txs;
|
||||
pub mod txy;
|
||||
pub mod tya;
|
||||
pub mod tyx;
|
||||
pub mod wai;
|
||||
pub mod wdm;
|
||||
pub mod xba;
|
||||
pub mod xce;
|
||||
pub mod bit_common;
|
||||
pub mod dec_common;
|
||||
pub mod decoder_common;
|
||||
pub mod branch_common;
|
||||
pub mod push_common;
|
||||
pub mod pull_common;
|
||||
pub mod comp_common;
|
||||
pub mod move_common;
|
||||
pub mod read_write_common;
|
||||
pub mod mapper;
|
||||
|
||||
pub trait CPUInstruction {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus);
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String;
|
||||
}
|
||||
29
snes-core/src/cpu/instructions/move_common.rs
Normal file
29
snes-core/src/cpu/instructions/move_common.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
use crate::cpu::{registers::Registers, bus::Bus, cycles};
|
||||
|
||||
pub fn do_move(registers: &mut Registers, bus: &mut Bus, is_next: bool) {
|
||||
let pc = registers.get_pc_address();
|
||||
let source_bank = bus.read(pc + 2);
|
||||
let dest_bank = bus.read(pc + 1);
|
||||
let mut count = 0;
|
||||
while registers.a != 0xFFFF {
|
||||
let (x, y) = match registers.is_16bit_index() {
|
||||
true => (registers.x, registers.y),
|
||||
false => (registers.x & 0x00FF, registers.y & 0x00FF),
|
||||
};
|
||||
let source_address = ((source_bank as u32) << 16) | (x as u32);
|
||||
let dest_address = ((dest_bank as u32) << 16) | (y as u32);
|
||||
let byte = bus.read(source_address);
|
||||
bus.write(dest_address, byte);
|
||||
registers.a = registers.a.wrapping_sub(1);
|
||||
if is_next {
|
||||
registers.x = registers.x.wrapping_add(1);
|
||||
registers.y = registers.y.wrapping_add(1);
|
||||
} else {
|
||||
registers.x = registers.x.wrapping_sub(1);
|
||||
registers.y = registers.y.wrapping_sub(1);
|
||||
}
|
||||
count += 1;
|
||||
}
|
||||
let (bytes, cycles) = cycles::increment_cycles_move(count);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
28
snes-core/src/cpu/instructions/mvn.rs
Normal file
28
snes-core/src/cpu/instructions/mvn.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use super::{CPUInstruction, move_common};
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "MVN";
|
||||
|
||||
pub struct MVN {}
|
||||
|
||||
impl CPUInstruction for MVN {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
move_common::do_move(registers, bus, true);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_move(opcode, INSTR_NAME, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
}
|
||||
}
|
||||
28
snes-core/src/cpu/instructions/mvp.rs
Normal file
28
snes-core/src/cpu/instructions/mvp.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use super::{CPUInstruction, move_common};
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "MVP";
|
||||
|
||||
pub struct MVP {}
|
||||
|
||||
impl CPUInstruction for MVP {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
move_common::do_move(registers, bus, false);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_move(opcode, INSTR_NAME, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
}
|
||||
}
|
||||
37
snes-core/src/cpu/instructions/nop.rs
Normal file
37
snes-core/src/cpu/instructions/nop.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
use crate::cpu::cycles;
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "NOP";
|
||||
|
||||
pub struct NOP {}
|
||||
|
||||
impl CPUInstruction for NOP {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
let (bytes, cycles) = cycles::increment_cycles_nop();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
let instruction = NOP{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0x01);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
}
|
||||
}
|
||||
113
snes-core/src/cpu/instructions/ora.rs
Normal file
113
snes-core/src/cpu/instructions/ora.rs
Normal file
@@ -0,0 +1,113 @@
|
||||
use crate::{cpu::{bus::Bus, registers::Registers}, utils::{alu, addressing::AddressingMode}};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::{CPUInstruction, read_write_common::{read_8bit_from_address, read_16bit_from_address}};
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "ORA";
|
||||
|
||||
pub struct ORA {
|
||||
pub addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl ORA {
|
||||
fn determine_instruction(&self, registers: &Registers) -> Box<dyn CPUInstruction> {
|
||||
match registers.is_16bit_mode() {
|
||||
true => Box::new(ORA16{addressing_mode: self.addressing_mode}),
|
||||
false => Box::new(ORA8{addressing_mode: self.addressing_mode}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPUInstruction for ORA {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.execute(registers, bus);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.mnemonic(registers, bus, opcode)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ORA8 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for ORA8 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let value = read_8bit_from_address(registers, bus, self.addressing_mode);
|
||||
let (result, affected_flags) = alu::ora(registers.a as u8, value);
|
||||
registers.set_low_a(result);
|
||||
registers.set_flags(&affected_flags);
|
||||
let (bytes, cycles) = cycles::increment_cycles_bitwise(registers, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ORA16 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for ORA16 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let value = read_16bit_from_address(registers, bus, self.addressing_mode);
|
||||
let (result, affected_flags) = alu::ora(registers.a, value);
|
||||
registers.a = result;
|
||||
registers.set_flags(&affected_flags);
|
||||
let (bytes, cycles) = cycles::increment_cycles_bitwise(registers, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_8bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.a = 0x0F;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
registers.set_memory_select_flag(true);
|
||||
bus.write(0x000001, 0xF0);
|
||||
let instruction = ORA8{addressing_mode: AddressingMode::Immediate};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.a, 0xFF);
|
||||
assert_eq!(registers.pc, 0x02);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
assert!(!registers.get_zero_flag());
|
||||
assert!(registers.get_negative_flag());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_16bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.a = 0x00FF;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
registers.emulation_mode = false;
|
||||
registers.set_memory_select_flag(false);
|
||||
bus.write(0x000002, 0x11);
|
||||
bus.write(0x000001, 0x00);
|
||||
let instruction = ORA16{addressing_mode: AddressingMode::Immediate};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.a, 0x11FF);
|
||||
assert_eq!(registers.pc, 0x03);
|
||||
assert_eq!(registers.cycles, 3);
|
||||
assert!(!registers.get_zero_flag());
|
||||
}
|
||||
}
|
||||
46
snes-core/src/cpu/instructions/pea.rs
Normal file
46
snes-core/src/cpu/instructions/pea.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use crate::utils::addressing::AddressingMode;
|
||||
use super::read_write_common::get_effective_address;
|
||||
use super::{CPUInstruction, push_common};
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "PEA";
|
||||
|
||||
pub struct PEA {}
|
||||
|
||||
impl CPUInstruction for PEA {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let address = get_effective_address(registers, bus, AddressingMode::Absolute);
|
||||
push_common::do_push(registers, bus, &[(address >> 8) as u8, address as u8]);
|
||||
let (bytes, cycles) = cycles::increment_cycles_pea();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_absolute(opcode, INSTR_NAME, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.sp = 0x1FC;
|
||||
bus.write(0x000002, 0xAA);
|
||||
bus.write(0x000001, 0xBB);
|
||||
let instruction = PEA{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(bus.read(0x1FC), 0xAA);
|
||||
assert_eq!(bus.read(0x1FB), 0xBB);
|
||||
assert_eq!(registers.pc, 0x0003);
|
||||
assert_eq!(registers.cycles, 5);
|
||||
}
|
||||
}
|
||||
48
snes-core/src/cpu/instructions/pei.rs
Normal file
48
snes-core/src/cpu/instructions/pei.rs
Normal file
@@ -0,0 +1,48 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use crate::utils::addressing::AddressingMode;
|
||||
use super::read_write_common::get_effective_address;
|
||||
use super::{CPUInstruction, push_common};
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "PEI";
|
||||
|
||||
pub struct PEI {}
|
||||
|
||||
impl CPUInstruction for PEI {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let address = get_effective_address(registers, bus, AddressingMode::DirectPageIndirect);
|
||||
push_common::do_push(registers, bus, &[(address >> 8) as u8, address as u8]);
|
||||
let (bytes, cycles) = cycles::increment_cycles_pei(registers);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_direct_page_indirect(opcode, INSTR_NAME, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.sp = 0x1FC;
|
||||
registers.d = 0x00;
|
||||
bus.write(0x000001, 0x02); // Direct page address
|
||||
bus.write(0x000002, 0xAA);
|
||||
bus.write(0x000003, 0xBB);
|
||||
let instruction = PEI{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(bus.read(0x1FC), 0xAA);
|
||||
assert_eq!(bus.read(0x1FB), 0xBB);
|
||||
assert_eq!(registers.pc, 0x0002);
|
||||
assert_eq!(registers.cycles, 6);
|
||||
}
|
||||
}
|
||||
51
snes-core/src/cpu/instructions/per.rs
Normal file
51
snes-core/src/cpu/instructions/per.rs
Normal file
@@ -0,0 +1,51 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use crate::utils::addressing::AddressingMode;
|
||||
use super::read_write_common::get_effective_address;
|
||||
use super::{CPUInstruction, push_common};
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "PER";
|
||||
|
||||
pub struct PER {}
|
||||
|
||||
impl CPUInstruction for PER {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let label = get_effective_address(registers, bus, AddressingMode::Absolute) as u16;
|
||||
let is_negative = (label>> 15) == 1;
|
||||
let (bytes, cycles) = cycles::increment_cycles_per();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
let address = match is_negative {
|
||||
true => registers.pc.wrapping_sub(!label + 1),
|
||||
false => registers.pc.wrapping_add(label),
|
||||
};
|
||||
push_common::do_push(registers, bus, &[(address >> 8) as u8, address as u8]);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_absolute(opcode, INSTR_NAME, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.sp = 0x1FC;
|
||||
bus.write(0x000002, 0x00);
|
||||
bus.write(0x000001, 0x01);
|
||||
let instruction = PER{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(bus.read(0x1FC), 0x00);
|
||||
assert_eq!(bus.read(0x1FB), 0x04);
|
||||
assert_eq!(registers.pc, 0x0003);
|
||||
assert_eq!(registers.cycles, 6);
|
||||
}
|
||||
}
|
||||
48
snes-core/src/cpu/instructions/pha.rs
Normal file
48
snes-core/src/cpu/instructions/pha.rs
Normal file
@@ -0,0 +1,48 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::{CPUInstruction, push_common};
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "PHA";
|
||||
|
||||
pub struct PHA {}
|
||||
|
||||
impl CPUInstruction for PHA {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let value = registers.a;
|
||||
if registers.is_16bit_mode() {
|
||||
push_common::do_push(registers, bus, &[(value >> 8) as u8, value as u8]);
|
||||
} else {
|
||||
push_common::do_push(registers, bus, &[value as u8]);
|
||||
}
|
||||
let (bytes, cycles) = cycles::increment_cycles_pha(registers.is_16bit_mode());
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.sp = 0x1FC;
|
||||
registers.a = 0x1234;
|
||||
registers.set_16bit_mode(false);
|
||||
let instruction = PHA{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(bus.read(0x1FC), 0x34);
|
||||
assert_eq!(registers.sp, 0x1FB);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.cycles, 3);
|
||||
}
|
||||
}
|
||||
42
snes-core/src/cpu/instructions/phb.rs
Normal file
42
snes-core/src/cpu/instructions/phb.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::{CPUInstruction, push_common};
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "PHB";
|
||||
|
||||
pub struct PHB {}
|
||||
|
||||
impl CPUInstruction for PHB {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
push_common::do_push(registers, bus, &[registers.dbr]);
|
||||
let (bytes, cycles) = cycles::increment_cycles_phb();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.sp = 0x1FC;
|
||||
registers.dbr = 0x12;
|
||||
let instruction = PHB{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(bus.read(0x1FC), 0x12);
|
||||
assert_eq!(registers.sp, 0x1FB);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.cycles, 3);
|
||||
}
|
||||
}
|
||||
44
snes-core/src/cpu/instructions/phd.rs
Normal file
44
snes-core/src/cpu/instructions/phd.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::{CPUInstruction, push_common};
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "PHD";
|
||||
|
||||
pub struct PHD {}
|
||||
|
||||
impl CPUInstruction for PHD {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let value = registers.d;
|
||||
push_common::do_push(registers, bus, &[(value >> 8) as u8, value as u8]);
|
||||
let (bytes, cycles) = cycles::increment_cycles_phd();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.sp = 0x1FC;
|
||||
registers.d = 0x1234;
|
||||
let instruction = PHD{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(bus.read(0x1FC), 0x12);
|
||||
assert_eq!(bus.read(0x1FB), 0x34);
|
||||
assert_eq!(registers.sp, 0x1FA);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.cycles, 4);
|
||||
}
|
||||
}
|
||||
43
snes-core/src/cpu/instructions/phk.rs
Normal file
43
snes-core/src/cpu/instructions/phk.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::{CPUInstruction, push_common};
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "PHK";
|
||||
|
||||
pub struct PHK {}
|
||||
|
||||
impl CPUInstruction for PHK {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
push_common::do_push(registers, bus, &[registers.pbr]);
|
||||
let (bytes, cycles) = cycles::increment_cycles_phk();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.pbr = 0x00;
|
||||
registers.sp = 0x1FC;
|
||||
bus.write(0x1FC, 0xFF);
|
||||
let instruction = PHK{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(bus.read(0x1FC), 0x00);
|
||||
assert_eq!(registers.sp, 0x1FB);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.cycles, 3);
|
||||
}
|
||||
}
|
||||
42
snes-core/src/cpu/instructions/php.rs
Normal file
42
snes-core/src/cpu/instructions/php.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::{CPUInstruction, push_common};
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "PHP";
|
||||
|
||||
pub struct PHP {}
|
||||
|
||||
impl CPUInstruction for PHP {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
push_common::do_push(registers, bus, &[registers.p]);
|
||||
let (bytes, cycles) = cycles::increment_cycles_php();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.p = 0x12;
|
||||
registers.sp = 0x1FC;
|
||||
let instruction = PHP{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(bus.read(0x1FC), 0x12);
|
||||
assert_eq!(registers.sp, 0x1FB);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.cycles, 3);
|
||||
}
|
||||
}
|
||||
50
snes-core/src/cpu/instructions/phx.rs
Normal file
50
snes-core/src/cpu/instructions/phx.rs
Normal file
@@ -0,0 +1,50 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::{CPUInstruction, push_common};
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "PHX";
|
||||
|
||||
pub struct PHX {}
|
||||
|
||||
impl CPUInstruction for PHX {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let value = registers.x;
|
||||
if registers.is_16bit_index() {
|
||||
push_common::do_push(registers, bus, &[(value >> 8) as u8, value as u8]);
|
||||
} else {
|
||||
push_common::do_push(registers, bus, &[value as u8]);
|
||||
}
|
||||
let (bytes, cycles) = cycles::increment_cycles_push_index(registers.is_16bit_index());
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.set_16bit_index(true);
|
||||
registers.pc = 0x0000;
|
||||
registers.x = 0x1234;
|
||||
registers.sp = 0x1FC;
|
||||
let instruction = PHX{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(bus.read(0x1FC), 0x12);
|
||||
assert_eq!(bus.read(0x1FB), 0x34);
|
||||
assert_eq!(registers.sp, 0x1FA);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.cycles, 4);
|
||||
}
|
||||
}
|
||||
50
snes-core/src/cpu/instructions/phy.rs
Normal file
50
snes-core/src/cpu/instructions/phy.rs
Normal file
@@ -0,0 +1,50 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::{CPUInstruction, push_common};
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "PHY";
|
||||
|
||||
pub struct PHY {}
|
||||
|
||||
impl CPUInstruction for PHY {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let value = registers.y;
|
||||
if registers.is_16bit_index() {
|
||||
push_common::do_push(registers, bus, &[(value >> 8) as u8, value as u8]);
|
||||
} else {
|
||||
push_common::do_push(registers, bus, &[value as u8]);
|
||||
}
|
||||
let (bytes, cycles) = cycles::increment_cycles_push_index(registers.is_16bit_index());
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.set_16bit_index(true);
|
||||
registers.pc = 0x0000;
|
||||
registers.y = 0x1234;
|
||||
registers.sp = 0x1FC;
|
||||
let instruction = PHY{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(bus.read(0x1FC), 0x12);
|
||||
assert_eq!(bus.read(0x1FB), 0x34);
|
||||
assert_eq!(registers.sp, 0x1FA);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.cycles, 4);
|
||||
}
|
||||
}
|
||||
56
snes-core/src/cpu/instructions/pla.rs
Normal file
56
snes-core/src/cpu/instructions/pla.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::{CPUInstruction, pull_common};
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "PLA";
|
||||
|
||||
pub struct PLA {}
|
||||
|
||||
impl CPUInstruction for PLA {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
if registers.is_16bit_mode() {
|
||||
let bytes = pull_common::do_pull(registers, bus, 2);
|
||||
registers.a = (bytes[0] as u16) | ((bytes[1] as u16) << 8);
|
||||
} else {
|
||||
let bytes = pull_common::do_pull(registers, bus, 1);
|
||||
registers.set_low_a(bytes[0]);
|
||||
}
|
||||
let (bytes, cycles) = cycles::increment_cycles_pla(registers.is_16bit_mode());
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.pc = 0x0000;
|
||||
registers.y = 0x1234;
|
||||
registers.set_16bit_mode(true);
|
||||
registers.set_negative_flag(true);
|
||||
registers.set_zero_flag(true);
|
||||
bus.write(0x1FB, 0x34);
|
||||
bus.write(0x1FC, 0x12);
|
||||
registers.sp = 0x1FA;
|
||||
let instruction = PLA{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.a, 0x1234);
|
||||
assert_eq!(registers.sp, 0x1FC);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.get_negative_flag(), false);
|
||||
assert_eq!(registers.get_zero_flag(), false);
|
||||
assert_eq!(registers.cycles, 5);
|
||||
}
|
||||
}
|
||||
47
snes-core/src/cpu/instructions/plb.rs
Normal file
47
snes-core/src/cpu/instructions/plb.rs
Normal file
@@ -0,0 +1,47 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::{CPUInstruction, pull_common};
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "PLB";
|
||||
|
||||
pub struct PLB {}
|
||||
|
||||
impl CPUInstruction for PLB {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
registers.dbr = pull_common::do_pull(registers, bus, 1)[0];
|
||||
let (bytes, cycles) = cycles::increment_cycles_plb();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.dbr = 0x00;
|
||||
registers.set_negative_flag(true);
|
||||
registers.set_zero_flag(true);
|
||||
bus.write(0x1FC, 0x12);
|
||||
registers.sp = 0x1FB;
|
||||
let instruction = PLB{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.dbr, 0x12);
|
||||
assert_eq!(registers.sp, 0x1FC);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.get_negative_flag(), false);
|
||||
assert_eq!(registers.get_zero_flag(), false);
|
||||
assert_eq!(registers.cycles, 4);
|
||||
}
|
||||
}
|
||||
49
snes-core/src/cpu/instructions/pld.rs
Normal file
49
snes-core/src/cpu/instructions/pld.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::{CPUInstruction, pull_common};
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "PLD";
|
||||
|
||||
pub struct PLD {}
|
||||
|
||||
impl CPUInstruction for PLD {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let bytes = pull_common::do_pull(registers, bus, 2);
|
||||
registers.d = (bytes[0] as u16) | ((bytes[1] as u16) << 8);
|
||||
let (bytes, cycles) = cycles::increment_cycles_pld();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.d = 0x1234;
|
||||
registers.set_negative_flag(true);
|
||||
registers.set_zero_flag(true);
|
||||
bus.write(0x1FB, 0x34);
|
||||
bus.write(0x1FC, 0x12);
|
||||
registers.sp = 0x1FA;
|
||||
let instruction = PLD{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.d, 0x1234);
|
||||
assert_eq!(registers.sp, 0x1FC);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.get_negative_flag(), false);
|
||||
assert_eq!(registers.get_zero_flag(), false);
|
||||
assert_eq!(registers.cycles, 5);
|
||||
}
|
||||
}
|
||||
44
snes-core/src/cpu/instructions/plp.rs
Normal file
44
snes-core/src/cpu/instructions/plp.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::{CPUInstruction, pull_common};
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "PLP";
|
||||
|
||||
pub struct PLP {}
|
||||
|
||||
impl CPUInstruction for PLP {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let bytes = pull_common::do_pull(registers, bus, 1);
|
||||
registers.p = bytes[0];
|
||||
let (bytes, cycles) = cycles::increment_cycles_plp();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.p = 0x00;
|
||||
bus.write(0x1FC, 0xFF);
|
||||
registers.sp = 0x1FB;
|
||||
let instruction = PLP{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.p, 0xFF);
|
||||
assert_eq!(registers.sp, 0x1FC);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.cycles, 4);
|
||||
}
|
||||
}
|
||||
56
snes-core/src/cpu/instructions/plx.rs
Normal file
56
snes-core/src/cpu/instructions/plx.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::{CPUInstruction, pull_common};
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "PLX";
|
||||
|
||||
pub struct PLX {}
|
||||
|
||||
impl CPUInstruction for PLX {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
if registers.is_16bit_index() {
|
||||
let bytes = pull_common::do_pull(registers, bus, 2);
|
||||
registers.x = (bytes[0] as u16) | ((bytes[1] as u16) << 8);
|
||||
} else {
|
||||
let bytes = pull_common::do_pull(registers, bus, 1);
|
||||
registers.set_low_x(bytes[0]);
|
||||
}
|
||||
let (bytes, cycles) = cycles::increment_cycles_pl_index(registers.is_16bit_index());
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.pc = 0x0000;
|
||||
registers.x = 0x1234;
|
||||
registers.set_16bit_index(true);
|
||||
registers.set_negative_flag(true);
|
||||
registers.set_zero_flag(true);
|
||||
bus.write(0x1FB, 0x34);
|
||||
bus.write(0x1FC, 0x12);
|
||||
registers.sp = 0x1FA;
|
||||
let instruction = PLX{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.x, 0x1234);
|
||||
assert_eq!(registers.sp, 0x1FC);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.get_negative_flag(), false);
|
||||
assert_eq!(registers.get_zero_flag(), false);
|
||||
assert_eq!(registers.cycles, 5);
|
||||
}
|
||||
}
|
||||
56
snes-core/src/cpu/instructions/ply.rs
Normal file
56
snes-core/src/cpu/instructions/ply.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::{CPUInstruction, pull_common};
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "PLY";
|
||||
|
||||
pub struct PLY {}
|
||||
|
||||
impl CPUInstruction for PLY {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
if registers.is_16bit_index() {
|
||||
let bytes = pull_common::do_pull(registers, bus, 2);
|
||||
registers.y = (bytes[0] as u16) | ((bytes[1] as u16) << 8);
|
||||
} else {
|
||||
let bytes = pull_common::do_pull(registers, bus, 1);
|
||||
registers.set_low_y(bytes[0]);
|
||||
}
|
||||
let (bytes, cycles) = cycles::increment_cycles_pl_index(registers.is_16bit_index());
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.pc = 0x0000;
|
||||
registers.y = 0x1234;
|
||||
registers.set_16bit_index(true);
|
||||
registers.set_negative_flag(true);
|
||||
registers.set_zero_flag(true);
|
||||
bus.write(0x1FB, 0x34);
|
||||
bus.write(0x1FC, 0x12);
|
||||
registers.sp = 0x1FA;
|
||||
let instruction = PLY{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.y, 0x1234);
|
||||
assert_eq!(registers.sp, 0x1FC);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.get_negative_flag(), false);
|
||||
assert_eq!(registers.get_zero_flag(), false);
|
||||
assert_eq!(registers.cycles, 5);
|
||||
}
|
||||
}
|
||||
22
snes-core/src/cpu/instructions/pull_common.rs
Normal file
22
snes-core/src/cpu/instructions/pull_common.rs
Normal file
@@ -0,0 +1,22 @@
|
||||
use crate::cpu::{registers::Registers, bus::Bus};
|
||||
|
||||
|
||||
pub fn do_pull(registers: &mut Registers, bus: &mut Bus, count: usize) -> Vec<u8> {
|
||||
let mut bytes = vec![];
|
||||
let mut is_zero = true;
|
||||
for _ in 0..count {
|
||||
registers.increment_sp(1);
|
||||
let byte = bus.read(registers.sp as u32);
|
||||
if byte != 0 {
|
||||
is_zero = false;
|
||||
}
|
||||
bytes.push(byte);
|
||||
}
|
||||
registers.set_zero_flag(is_zero);
|
||||
if bytes.len() > 0 {
|
||||
// Low byte is pulled first, so we need to check
|
||||
// for the last byte that we pull
|
||||
registers.set_negative_flag((bytes[bytes.len() - 1] >> 7) == 1);
|
||||
}
|
||||
bytes
|
||||
}
|
||||
9
snes-core/src/cpu/instructions/push_common.rs
Normal file
9
snes-core/src/cpu/instructions/push_common.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
use crate::cpu::{registers::Registers, bus::Bus};
|
||||
|
||||
pub fn do_push(registers: &mut Registers, bus: &mut Bus, bytes: &[u8]) {
|
||||
for byte in bytes {
|
||||
let address = registers.sp as u32;
|
||||
bus.write(address, *byte);
|
||||
registers.decrement_sp(1);
|
||||
}
|
||||
}
|
||||
67
snes-core/src/cpu/instructions/read_write_common.rs
Normal file
67
snes-core/src/cpu/instructions/read_write_common.rs
Normal file
@@ -0,0 +1,67 @@
|
||||
use crate::{cpu::{registers::Registers, bus::Bus}, utils::addressing::AddressingMode};
|
||||
|
||||
pub fn get_effective_address(registers: &Registers, bus: &mut Bus, addressing_mode: AddressingMode) -> u32 {
|
||||
addressing_mode.effective_address(
|
||||
bus,
|
||||
registers.get_pc_address(),
|
||||
registers.d,
|
||||
registers.sp,
|
||||
registers.x, registers.y,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn read_8bit_from_address(registers: &Registers, bus: &mut Bus, addressing_mode: AddressingMode) -> u8 {
|
||||
match addressing_mode {
|
||||
AddressingMode::Accumulator => registers.a as u8,
|
||||
_ => addressing_mode.value_8bit(
|
||||
bus,
|
||||
registers.get_pc_address(),
|
||||
registers.d,
|
||||
registers.sp,
|
||||
registers.x,
|
||||
registers.y,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_16bit_from_address(registers: &Registers, bus: &mut Bus, addressing_mode: AddressingMode) -> u16 {
|
||||
match addressing_mode {
|
||||
AddressingMode::Accumulator => registers.a,
|
||||
_ => addressing_mode.value_16bit(
|
||||
bus,
|
||||
registers.get_pc_address(),
|
||||
registers.d,
|
||||
registers.sp,
|
||||
registers.x,
|
||||
registers.y,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_8bit_to_address(registers: &mut Registers, bus: &mut Bus, addressing_mode: AddressingMode, value: u8) {
|
||||
match addressing_mode {
|
||||
AddressingMode::Accumulator => registers.set_low_a(value),
|
||||
_ => addressing_mode.store_8bit(
|
||||
bus,
|
||||
registers.get_pc_address(),
|
||||
registers.d,
|
||||
registers.sp,
|
||||
registers.x, registers.y,
|
||||
value,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn write_16bit_to_address(registers: &mut Registers, bus: &mut Bus, addressing_mode: AddressingMode, value: u16) {
|
||||
match addressing_mode {
|
||||
AddressingMode::Accumulator => registers.a = value,
|
||||
_ => addressing_mode.store_16bit(
|
||||
bus,
|
||||
registers.get_pc_address(),
|
||||
registers.d,
|
||||
registers.sp,
|
||||
registers.x, registers.y,
|
||||
value,
|
||||
),
|
||||
};
|
||||
}
|
||||
45
snes-core/src/cpu/instructions/rep.rs
Normal file
45
snes-core/src/cpu/instructions/rep.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use crate::utils::addressing::AddressingMode;
|
||||
use super::read_write_common::read_8bit_from_address;
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "REP";
|
||||
|
||||
pub struct REP {}
|
||||
|
||||
impl CPUInstruction for REP {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let byte = read_8bit_from_address(registers, bus, AddressingMode::Immediate);
|
||||
registers.reset_rep_byte(byte);
|
||||
let (bytes, cycles) = cycles::increment_cycles_rep();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_8bit_immediate(opcode, INSTR_NAME, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.pc = 0x0000;
|
||||
registers.p = 0xFF;
|
||||
bus.write(0x0001, 0xFF);
|
||||
let instruction = REP{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.p, 0x00);
|
||||
assert_eq!(registers.pc, 0x0002);
|
||||
assert_eq!(registers.cycles, 3);
|
||||
}
|
||||
}
|
||||
110
snes-core/src/cpu/instructions/rol.rs
Normal file
110
snes-core/src/cpu/instructions/rol.rs
Normal file
@@ -0,0 +1,110 @@
|
||||
use crate::{cpu::{bus::Bus, registers::Registers}, utils::{alu, addressing::AddressingMode}};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::{CPUInstruction, read_write_common::{read_8bit_from_address, write_8bit_to_address, read_16bit_from_address, write_16bit_to_address}};
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "ROL";
|
||||
|
||||
pub struct ROL {
|
||||
pub addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl ROL {
|
||||
fn determine_instruction(&self, registers: &Registers) -> Box<dyn CPUInstruction> {
|
||||
match registers.is_16bit_mode() {
|
||||
true => Box::new(ROL16{addressing_mode: self.addressing_mode}),
|
||||
false => Box::new(ROL8{addressing_mode: self.addressing_mode}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPUInstruction for ROL {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.execute(registers, bus);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.mnemonic(registers, bus, opcode)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ROL8 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for ROL8 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let target = read_8bit_from_address(registers, bus, self.addressing_mode);
|
||||
let (result, affected_flags) = alu::rol(target, registers.get_carry_flag());
|
||||
write_8bit_to_address(registers, bus, self.addressing_mode, result);
|
||||
registers.set_flags(&affected_flags);
|
||||
let (bytes, cycles) = cycles::increment_cycles_shift(registers, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ROL16 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for ROL16 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let target = read_16bit_from_address(registers, bus, self.addressing_mode);
|
||||
let (result, affected_flags) = alu::rol(target, registers.get_carry_flag());
|
||||
write_16bit_to_address(registers, bus, self.addressing_mode, result);
|
||||
registers.set_flags(&affected_flags);
|
||||
let (bytes, cycles) = cycles::increment_cycles_shift(registers, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_8bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.set_16bit_mode(false);
|
||||
registers.a = 0b0100_0000;
|
||||
registers.pc = 0x0000;
|
||||
let instruction = ROL8{addressing_mode: AddressingMode::Accumulator};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.get_negative_flag(), true);
|
||||
assert_eq!(registers.get_zero_flag(), false);
|
||||
assert_eq!(registers.a, 0b1000_0000);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_and_16bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.set_16bit_mode(true);
|
||||
registers.a = 0b01000000_00000000;
|
||||
registers.pc = 0x0000;
|
||||
let instruction = ROL16{addressing_mode: AddressingMode::Accumulator};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.get_negative_flag(), true);
|
||||
assert_eq!(registers.get_zero_flag(), false);
|
||||
assert_eq!(registers.a, 0b10000000_00000000);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.cycles, 4);
|
||||
}
|
||||
}
|
||||
112
snes-core/src/cpu/instructions/ror.rs
Normal file
112
snes-core/src/cpu/instructions/ror.rs
Normal file
@@ -0,0 +1,112 @@
|
||||
use crate::{cpu::{bus::Bus, registers::Registers}, utils::{alu, addressing::AddressingMode}};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::{CPUInstruction, read_write_common::{read_8bit_from_address, write_8bit_to_address, read_16bit_from_address, write_16bit_to_address}};
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "ROR";
|
||||
|
||||
pub struct ROR {
|
||||
pub addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl ROR {
|
||||
fn determine_instruction(&self, registers: &Registers) -> Box<dyn CPUInstruction> {
|
||||
match registers.is_16bit_mode() {
|
||||
true => Box::new(ROR16{addressing_mode: self.addressing_mode}),
|
||||
false => Box::new(ROR8{addressing_mode: self.addressing_mode}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPUInstruction for ROR {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.execute(registers, bus);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.mnemonic(registers, bus, opcode)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ROR8 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for ROR8 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let target = read_8bit_from_address(registers, bus, self.addressing_mode);
|
||||
let (result, affected_flags) = alu::ror(target, registers.get_carry_flag());
|
||||
write_8bit_to_address(registers, bus, self.addressing_mode, result);
|
||||
registers.set_flags(&affected_flags);
|
||||
let (bytes, cycles) = cycles::increment_cycles_shift(registers, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ROR16 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for ROR16 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let target = read_16bit_from_address(registers, bus, self.addressing_mode);
|
||||
let (result, affected_flags) = alu::ror(target, registers.get_carry_flag());
|
||||
write_16bit_to_address(registers, bus, self.addressing_mode, result);
|
||||
registers.set_flags(&affected_flags);
|
||||
let (bytes, cycles) = cycles::increment_cycles_shift(registers, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_8bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.set_16bit_mode(false);
|
||||
registers.set_carry_flag(true);
|
||||
registers.a = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
let instruction = ROR8{addressing_mode: AddressingMode::Accumulator};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.get_carry_flag(), false);
|
||||
assert_eq!(registers.get_zero_flag(), false);
|
||||
assert_eq!(registers.a, 0b1000_0000);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_and_16bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.set_16bit_mode(true);
|
||||
registers.set_carry_flag(true);
|
||||
registers.a = 0b00000000_00000000;
|
||||
registers.pc = 0x0000;
|
||||
let instruction = ROR16{addressing_mode: AddressingMode::Accumulator};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.get_negative_flag(), true);
|
||||
assert_eq!(registers.get_zero_flag(), false);
|
||||
assert_eq!(registers.a, 0b10000000_00000000);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.cycles, 4);
|
||||
}
|
||||
}
|
||||
36
snes-core/src/cpu/instructions/rti.rs
Normal file
36
snes-core/src/cpu/instructions/rti.rs
Normal file
@@ -0,0 +1,36 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::{CPUInstruction, pull_common};
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "RTI";
|
||||
|
||||
pub struct RTI {}
|
||||
|
||||
impl CPUInstruction for RTI {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
registers.p = pull_common::do_pull(registers, bus, 1)[0];
|
||||
let pc_bytes = pull_common::do_pull(registers, bus, 2);
|
||||
registers.pc = (pc_bytes[0] as u16) | ((pc_bytes[1] as u16) << 8);
|
||||
if !registers.emulation_mode {
|
||||
registers.pbr = pull_common::do_pull(registers, bus, 1)[0];
|
||||
}
|
||||
let (bytes, cycles) = cycles::increment_cycles_return_interrupt(registers.emulation_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
}
|
||||
}
|
||||
47
snes-core/src/cpu/instructions/rtl.rs
Normal file
47
snes-core/src/cpu/instructions/rtl.rs
Normal file
@@ -0,0 +1,47 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::{CPUInstruction, pull_common};
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "RTL";
|
||||
|
||||
pub struct RTL {}
|
||||
|
||||
impl CPUInstruction for RTL {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let bytes = pull_common::do_pull(registers, bus, 3);
|
||||
// Low byte of PC is pulled first, then high byte and then PBR
|
||||
registers.pc = (bytes[0] as u16) | ((bytes[1] as u16) << 8);
|
||||
registers.pbr = bytes[2];
|
||||
let (bytes, cycles) = cycles::increment_cycles_return_subroutine();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
registers.sp = 0x1F9;
|
||||
bus.write(0x1FC, 0x12);
|
||||
bus.write(0x1FB, 0x34);
|
||||
bus.write(0x1FA, 0x56);
|
||||
let instruction = RTL{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pbr, 0x12);
|
||||
assert_eq!(registers.pc, 0x3456);
|
||||
assert_eq!(registers.cycles, 6);
|
||||
}
|
||||
}
|
||||
45
snes-core/src/cpu/instructions/rts.rs
Normal file
45
snes-core/src/cpu/instructions/rts.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::{CPUInstruction, pull_common};
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "RTS";
|
||||
|
||||
pub struct RTS {}
|
||||
|
||||
impl CPUInstruction for RTS {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let bytes = pull_common::do_pull(registers, bus, 2);
|
||||
// Low byte of PC is pulled first, then high byte
|
||||
registers.pc = (bytes[0] as u16) | ((bytes[1] as u16) << 8);
|
||||
let (bytes, cycles) = cycles::increment_cycles_return_subroutine();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
registers.sp = 0x1FA;
|
||||
bus.write(0x1FC, 0x12);
|
||||
bus.write(0x1FB, 0x34);
|
||||
let instruction = RTS{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pbr, 0x00);
|
||||
assert_eq!(registers.pc, 0x1234);
|
||||
assert_eq!(registers.cycles, 6);
|
||||
}
|
||||
}
|
||||
211
snes-core/src/cpu/instructions/sbc.rs
Normal file
211
snes-core/src/cpu/instructions/sbc.rs
Normal file
@@ -0,0 +1,211 @@
|
||||
use crate::{cpu::{bus::Bus, registers::Registers}, utils::{alu, addressing::AddressingMode}};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::{CPUInstruction, read_write_common::{read_8bit_from_address, read_16bit_from_address}};
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "SBC";
|
||||
|
||||
pub struct SBC {
|
||||
pub addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl SBC {
|
||||
fn determine_instruction(&self, registers: &Registers) -> Box<dyn CPUInstruction> {
|
||||
let is_decimal_mode = registers.get_decimal_mode_flag();
|
||||
match registers.is_16bit_mode() {
|
||||
true => match is_decimal_mode {
|
||||
true => Box::new(SBC16BCD{addressing_mode: self.addressing_mode}),
|
||||
false => Box::new(SBC16BIN{addressing_mode: self.addressing_mode}),
|
||||
}
|
||||
false => match is_decimal_mode {
|
||||
true => Box::new(SBC8BCD{addressing_mode: self.addressing_mode}),
|
||||
false => Box::new(SBC8BIN{addressing_mode: self.addressing_mode}),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPUInstruction for SBC {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.execute(registers, bus);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.mnemonic(registers, bus, opcode)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SBC8BIN {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for SBC8BIN {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let (result, affected_flags) = alu::sbc_bin(
|
||||
registers.a as u8,
|
||||
read_8bit_from_address(registers, bus, self.addressing_mode),
|
||||
registers.get_carry_flag(),
|
||||
);
|
||||
registers.set_low_a(result);
|
||||
registers.set_flags(&affected_flags);
|
||||
let (bytes, cycles) = cycles::increment_cycles_arithmetic(®isters, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SBC16BIN {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for SBC16BIN {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let (result, affected_flags) = alu::sbc_bin(
|
||||
registers.a,
|
||||
read_16bit_from_address(registers, bus, self.addressing_mode),
|
||||
registers.get_carry_flag(),
|
||||
);
|
||||
registers.a = result;
|
||||
registers.set_flags(&affected_flags);
|
||||
let (bytes, cycles) = cycles::increment_cycles_arithmetic(®isters, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SBC8BCD {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for SBC8BCD {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let (result, affected_flags) = alu::sbc_bcd(
|
||||
registers.a as u8,
|
||||
read_8bit_from_address(registers, bus, self.addressing_mode),
|
||||
registers.get_carry_flag(),
|
||||
);
|
||||
registers.set_low_a(result);
|
||||
registers.set_flags(&affected_flags);
|
||||
let (bytes, cycles) = cycles::increment_cycles_arithmetic(®isters, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SBC16BCD {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for SBC16BCD {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let (result, affected_flags) = alu::sbc_bcd(
|
||||
registers.a,
|
||||
read_16bit_from_address(registers, bus, self.addressing_mode),
|
||||
registers.get_carry_flag(),
|
||||
);
|
||||
registers.a = result;
|
||||
registers.set_flags(&affected_flags);
|
||||
let (bytes, cycles) = cycles::increment_cycles_arithmetic(®isters, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_sbc_bin_8bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.a = 0x0040;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
registers.set_memory_select_flag(true);
|
||||
bus.write(0x000001, 0x40);
|
||||
let instruction = SBC8BIN{addressing_mode: AddressingMode::Immediate};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.a, 0x00);
|
||||
assert_eq!(registers.pc, 0x02);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
assert!(registers.get_zero_flag());
|
||||
assert!(!registers.get_carry_flag());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sbc_bin_16bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.a = 0x4000;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
registers.set_memory_select_flag(false);
|
||||
bus.write(0x000001, 0x00);
|
||||
bus.write(0x000002, 0x40);
|
||||
let instruction = SBC16BIN{addressing_mode: AddressingMode::Immediate};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.a, 0x0000);
|
||||
assert_eq!(registers.pc, 0x03);
|
||||
assert_eq!(registers.cycles, 3);
|
||||
assert!(registers.get_zero_flag());
|
||||
assert!(!registers.get_carry_flag());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sbc_bcd_8bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.a = 0x0049;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
registers.set_16bit_mode(false);
|
||||
bus.write(0x000001, 0x48);
|
||||
let instruction = SBC8BCD{addressing_mode: AddressingMode::Immediate};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.a, 0x00);
|
||||
assert_eq!(registers.pc, 0x02);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
assert!(registers.get_zero_flag());
|
||||
assert!(registers.get_carry_flag());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sbc_bcd_16bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.a = 0x0049;
|
||||
registers.pbr = 0x00;
|
||||
registers.pc = 0x0000;
|
||||
registers.set_16bit_mode(true);
|
||||
bus.write(0x000001, 0x48);
|
||||
bus.write(0x000002, 0x00);
|
||||
let instruction = SBC16BCD{addressing_mode: AddressingMode::Immediate};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.a, 0x0000);
|
||||
assert_eq!(registers.pc, 0x03);
|
||||
assert_eq!(registers.cycles, 3);
|
||||
assert!(registers.get_zero_flag());
|
||||
assert!(registers.get_carry_flag());
|
||||
}
|
||||
}
|
||||
40
snes-core/src/cpu/instructions/sec.rs
Normal file
40
snes-core/src/cpu/instructions/sec.rs
Normal file
@@ -0,0 +1,40 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "SEC";
|
||||
|
||||
pub struct SEC {}
|
||||
|
||||
impl CPUInstruction for SEC {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
registers.set_carry_flag(true);
|
||||
let (bytes, cycles) = cycles::increment_cycles_set_flag();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.set_carry_flag(false);
|
||||
let instruction = SEC{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.get_carry_flag(), true);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
}
|
||||
}
|
||||
40
snes-core/src/cpu/instructions/sed.rs
Normal file
40
snes-core/src/cpu/instructions/sed.rs
Normal file
@@ -0,0 +1,40 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "SED";
|
||||
|
||||
pub struct SED {}
|
||||
|
||||
impl CPUInstruction for SED {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
registers.set_decimal_mode_flag(true);
|
||||
let (bytes, cycles) = cycles::increment_cycles_set_flag();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.set_decimal_mode_flag(false);
|
||||
let instruction = SED{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.get_decimal_mode_flag(), true);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
}
|
||||
}
|
||||
40
snes-core/src/cpu/instructions/sei.rs
Normal file
40
snes-core/src/cpu/instructions/sei.rs
Normal file
@@ -0,0 +1,40 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "SEI";
|
||||
|
||||
pub struct SEI {}
|
||||
|
||||
impl CPUInstruction for SEI {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
registers.set_irq_disable_flag(true);
|
||||
let (bytes, cycles) = cycles::increment_cycles_set_flag();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.set_irq_disable_flag(false);
|
||||
let instruction = SEI{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.get_irq_disable_flag(), true);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
}
|
||||
}
|
||||
44
snes-core/src/cpu/instructions/sep.rs
Normal file
44
snes-core/src/cpu/instructions/sep.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use crate::utils::addressing::AddressingMode;
|
||||
use super::read_write_common::read_8bit_from_address;
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "SEP";
|
||||
|
||||
pub struct SEP {}
|
||||
|
||||
impl CPUInstruction for SEP {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let byte = read_8bit_from_address(registers, bus, AddressingMode::Immediate);
|
||||
registers.set_sep_byte(byte);
|
||||
let (bytes, cycles) = cycles::increment_cycles_sep();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_8bit_immediate(opcode, INSTR_NAME, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.p = 0x00;
|
||||
bus.write(0x0001, 0xFF);
|
||||
let instruction = SEP{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.p, 0xFF);
|
||||
assert_eq!(registers.pc, 0x0002);
|
||||
assert_eq!(registers.cycles, 3);
|
||||
}
|
||||
}
|
||||
105
snes-core/src/cpu/instructions/sta.rs
Normal file
105
snes-core/src/cpu/instructions/sta.rs
Normal file
@@ -0,0 +1,105 @@
|
||||
use crate::cpu::cycles;
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
use crate::utils::addressing::AddressingMode;
|
||||
|
||||
use super::read_write_common::{write_8bit_to_address, write_16bit_to_address};
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "STA";
|
||||
|
||||
pub struct STA {
|
||||
pub addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl STA {
|
||||
fn determine_instruction(&self, registers: &Registers) -> Box<dyn CPUInstruction> {
|
||||
match registers.is_16bit_mode() {
|
||||
true => Box::new(STA16{addressing_mode: self.addressing_mode}),
|
||||
false => Box::new(STA8{addressing_mode: self.addressing_mode}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPUInstruction for STA {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.execute(registers, bus);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.mnemonic(registers, bus, opcode)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct STA8 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for STA8 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
write_8bit_to_address(registers, bus, self.addressing_mode, registers.a as u8);
|
||||
let (bytes, cycles) = cycles::increment_cycles_sta(registers, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct STA16 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for STA16 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
write_16bit_to_address(registers, bus, self.addressing_mode, registers.a);
|
||||
let (bytes, cycles) = cycles::increment_cycles_sta(registers, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_8bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.a = 0x12;
|
||||
registers.set_16bit_mode(false);
|
||||
bus.write(0x0002, 0x00);
|
||||
bus.write(0x0001, 0x03);
|
||||
let instruction = STA8{addressing_mode: AddressingMode::Absolute};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(bus.read(0x0003), 0x12);
|
||||
assert_eq!(registers.pc, 0x0003);
|
||||
assert_eq!(registers.cycles, 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_16bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.a = 0x1234;
|
||||
registers.set_16bit_mode(true);
|
||||
bus.write(0x0002, 0x00);
|
||||
bus.write(0x0001, 0x03);
|
||||
let instruction = STA16{addressing_mode: AddressingMode::Absolute};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(bus.read(0x0003), 0x34);
|
||||
assert_eq!(bus.read(0x0004), 0x12);
|
||||
assert_eq!(registers.pc, 0x0003);
|
||||
assert_eq!(registers.cycles, 4);
|
||||
}
|
||||
}
|
||||
40
snes-core/src/cpu/instructions/stp.rs
Normal file
40
snes-core/src/cpu/instructions/stp.rs
Normal file
@@ -0,0 +1,40 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "STP";
|
||||
|
||||
pub struct STP {}
|
||||
|
||||
impl CPUInstruction for STP {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
registers.is_cpu_stopped = true;
|
||||
let (bytes, cycles) = cycles::increment_cycles_stp();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.is_cpu_stopped = false;
|
||||
registers.pc = 0x0000;
|
||||
let instruction = STP{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.is_cpu_stopped, true);
|
||||
assert_eq!(registers.cycles, 3);
|
||||
}
|
||||
}
|
||||
105
snes-core/src/cpu/instructions/stx.rs
Normal file
105
snes-core/src/cpu/instructions/stx.rs
Normal file
@@ -0,0 +1,105 @@
|
||||
use crate::cpu::cycles;
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
use crate::utils::addressing::AddressingMode;
|
||||
|
||||
use super::read_write_common::{write_8bit_to_address, write_16bit_to_address};
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "STX";
|
||||
|
||||
pub struct STX {
|
||||
pub addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl STX {
|
||||
fn determine_instruction(&self, registers: &Registers) -> Box<dyn CPUInstruction> {
|
||||
match registers.is_16bit_index() {
|
||||
true => Box::new(STX16{addressing_mode: self.addressing_mode}),
|
||||
false => Box::new(STX8{addressing_mode: self.addressing_mode}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPUInstruction for STX {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.execute(registers, bus);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.mnemonic(registers, bus, opcode)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct STX8 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for STX8 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
write_8bit_to_address(registers, bus, self.addressing_mode, registers.x as u8);
|
||||
let (bytes, cycles) = cycles::increment_cycles_st_index(registers, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct STX16 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for STX16 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
write_16bit_to_address(registers, bus, self.addressing_mode, registers.x);
|
||||
let (bytes, cycles) = cycles::increment_cycles_st_index(registers, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_8bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.x = 0x12;
|
||||
registers.set_16bit_mode(false);
|
||||
bus.write(0x0002, 0x00);
|
||||
bus.write(0x0001, 0x03);
|
||||
let instruction = STX8{addressing_mode: AddressingMode::Absolute};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(bus.read(0x0003), 0x12);
|
||||
assert_eq!(registers.pc, 0x0003);
|
||||
assert_eq!(registers.cycles, 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_16bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.x = 0x1234;
|
||||
registers.set_16bit_mode(true);
|
||||
bus.write(0x0002, 0x00);
|
||||
bus.write(0x0001, 0x03);
|
||||
let instruction = STX16{addressing_mode: AddressingMode::Absolute};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(bus.read(0x0003), 0x34);
|
||||
assert_eq!(bus.read(0x0004), 0x12);
|
||||
assert_eq!(registers.pc, 0x0003);
|
||||
assert_eq!(registers.cycles, 4);
|
||||
}
|
||||
}
|
||||
105
snes-core/src/cpu/instructions/sty.rs
Normal file
105
snes-core/src/cpu/instructions/sty.rs
Normal file
@@ -0,0 +1,105 @@
|
||||
use crate::cpu::cycles;
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
use crate::utils::addressing::AddressingMode;
|
||||
|
||||
use super::read_write_common::{write_8bit_to_address, write_16bit_to_address};
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "STY";
|
||||
|
||||
pub struct STY {
|
||||
pub addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl STY {
|
||||
fn determine_instruction(&self, registers: &Registers) -> Box<dyn CPUInstruction> {
|
||||
match registers.is_16bit_index() {
|
||||
true => Box::new(STY16{addressing_mode: self.addressing_mode}),
|
||||
false => Box::new(STY8{addressing_mode: self.addressing_mode}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPUInstruction for STY {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.execute(registers, bus);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.mnemonic(registers, bus, opcode)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct STY8 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for STY8 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
write_8bit_to_address(registers, bus, self.addressing_mode, registers.y as u8);
|
||||
let (bytes, cycles) = cycles::increment_cycles_st_index(registers, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct STY16 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for STY16 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
write_16bit_to_address(registers, bus, self.addressing_mode, registers.y);
|
||||
let (bytes, cycles) = cycles::increment_cycles_st_index(registers, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_8bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.y = 0x12;
|
||||
registers.set_16bit_mode(false);
|
||||
bus.write(0x0002, 0x00);
|
||||
bus.write(0x0001, 0x03);
|
||||
let instruction = STY8{addressing_mode: AddressingMode::Absolute};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(bus.read(0x0003), 0x12);
|
||||
assert_eq!(registers.pc, 0x0003);
|
||||
assert_eq!(registers.cycles, 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_16bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.y = 0x1234;
|
||||
registers.set_16bit_mode(true);
|
||||
bus.write(0x0002, 0x00);
|
||||
bus.write(0x0001, 0x03);
|
||||
let instruction = STY16{addressing_mode: AddressingMode::Absolute};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(bus.read(0x0003), 0x34);
|
||||
assert_eq!(bus.read(0x0004), 0x12);
|
||||
assert_eq!(registers.pc, 0x0003);
|
||||
assert_eq!(registers.cycles, 4);
|
||||
}
|
||||
}
|
||||
106
snes-core/src/cpu/instructions/stz.rs
Normal file
106
snes-core/src/cpu/instructions/stz.rs
Normal file
@@ -0,0 +1,106 @@
|
||||
use crate::cpu::cycles;
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
use crate::utils::addressing::AddressingMode;
|
||||
|
||||
use super::read_write_common::{write_8bit_to_address, write_16bit_to_address};
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "STZ";
|
||||
|
||||
pub struct STZ {
|
||||
pub addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl STZ {
|
||||
fn determine_instruction(&self, registers: &Registers) -> Box<dyn CPUInstruction> {
|
||||
match registers.is_16bit_mode() {
|
||||
true => Box::new(STZ16{addressing_mode: self.addressing_mode}),
|
||||
false => Box::new(STZ8{addressing_mode: self.addressing_mode}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPUInstruction for STZ {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.execute(registers, bus);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.mnemonic(registers, bus, opcode)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct STZ8 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for STZ8 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
write_8bit_to_address(registers, bus, self.addressing_mode, 0);
|
||||
let (bytes, cycles) = cycles::increment_cycles_st_index(registers, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct STZ16 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for STZ16 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
write_16bit_to_address(registers, bus, self.addressing_mode, 0);
|
||||
let (bytes, cycles) = cycles::increment_cycles_st_index(registers, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_8bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.set_16bit_mode(false);
|
||||
bus.write(0x0002, 0x00);
|
||||
bus.write(0x0001, 0x03);
|
||||
bus.write(0x0003, 0xFF);
|
||||
let instruction = STZ8{addressing_mode: AddressingMode::Absolute};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(bus.read(0x0003), 0x00);
|
||||
assert_eq!(registers.pc, 0x0003);
|
||||
assert_eq!(registers.cycles, 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_16bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.set_16bit_mode(true);
|
||||
bus.write(0x0002, 0x00);
|
||||
bus.write(0x0001, 0x03);
|
||||
bus.write(0x0003, 0xFF);
|
||||
bus.write(0x0004, 0xFF);
|
||||
let instruction = STZ16{addressing_mode: AddressingMode::Absolute};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(bus.read(0x0003), 0x00);
|
||||
assert_eq!(bus.read(0x0004), 0x00);
|
||||
assert_eq!(registers.pc, 0x0003);
|
||||
assert_eq!(registers.cycles, 4);
|
||||
}
|
||||
}
|
||||
106
snes-core/src/cpu/instructions/tax.rs
Normal file
106
snes-core/src/cpu/instructions/tax.rs
Normal file
@@ -0,0 +1,106 @@
|
||||
use crate::cpu::cycles;
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "TAX";
|
||||
|
||||
pub struct TAX {}
|
||||
|
||||
impl TAX {
|
||||
fn determine_instruction(&self, registers: &Registers) -> Box<dyn CPUInstruction> {
|
||||
match registers.is_16bit_index() {
|
||||
true => Box::new(TAX16{}),
|
||||
false => Box::new(TAX8{}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPUInstruction for TAX {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.execute(registers, bus);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.mnemonic(registers, bus, opcode)
|
||||
}
|
||||
}
|
||||
pub struct TAX8 {}
|
||||
|
||||
impl CPUInstruction for TAX8 {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
let result = registers.a as u8;
|
||||
registers.set_low_x(result);
|
||||
registers.set_negative_flag((result >> 7) == 1);
|
||||
registers.set_zero_flag(result == 0);
|
||||
let (bytes, cycles) = cycles::increment_cycles_transfer();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TAX16 {}
|
||||
|
||||
impl CPUInstruction for TAX16 {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
registers.x = registers.a;
|
||||
registers.set_negative_flag((registers.x >> 15) == 1);
|
||||
registers.set_zero_flag(registers.x == 0);
|
||||
let (bytes, cycles) = cycles::increment_cycles_transfer();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_8bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.pc = 0x0000;
|
||||
registers.a = 0xFFFF;
|
||||
registers.x = 0x0000;
|
||||
registers.set_16bit_mode(false);
|
||||
registers.set_16bit_index(false);
|
||||
let instruction = TAX8{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.get_negative_flag(), true);
|
||||
assert_eq!(registers.get_zero_flag(), false);
|
||||
assert_eq!(registers.x, 0x00FF);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_16bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.pc = 0x0000;
|
||||
registers.a = 0xF0F0;
|
||||
registers.x = 0x0000;
|
||||
registers.set_16bit_mode(true);
|
||||
registers.set_16bit_index(true);
|
||||
let instruction = TAX16{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.get_negative_flag(), true);
|
||||
assert_eq!(registers.get_zero_flag(), false);
|
||||
assert_eq!(registers.x, 0xF0F0);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
}
|
||||
}
|
||||
107
snes-core/src/cpu/instructions/tay.rs
Normal file
107
snes-core/src/cpu/instructions/tay.rs
Normal file
@@ -0,0 +1,107 @@
|
||||
use crate::cpu::cycles;
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "TAY";
|
||||
|
||||
pub struct TAY {}
|
||||
|
||||
impl TAY {
|
||||
fn determine_instruction(&self, registers: &Registers) -> Box<dyn CPUInstruction> {
|
||||
match registers.is_16bit_index() {
|
||||
true => Box::new(TAY16{}),
|
||||
false => Box::new(TAY8{}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPUInstruction for TAY {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.execute(registers, bus);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.mnemonic(registers, bus, opcode)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TAY8 {}
|
||||
|
||||
impl CPUInstruction for TAY8 {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
let result = registers.a as u8;
|
||||
registers.set_low_y(result);
|
||||
registers.set_negative_flag((result >> 7) == 1);
|
||||
registers.set_zero_flag(result == 0);
|
||||
let (bytes, cycles) = cycles::increment_cycles_transfer();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TAY16 {}
|
||||
|
||||
impl CPUInstruction for TAY16 {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
registers.y = registers.a;
|
||||
registers.set_negative_flag((registers.y >> 15) == 1);
|
||||
registers.set_zero_flag(registers.y == 0);
|
||||
let (bytes, cycles) = cycles::increment_cycles_transfer();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_8bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.pc = 0x0000;
|
||||
registers.a = 0xFFFF;
|
||||
registers.y = 0x0000;
|
||||
registers.set_16bit_mode(false);
|
||||
registers.set_16bit_index(false);
|
||||
let instruction = TAY8{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.get_negative_flag(), true);
|
||||
assert_eq!(registers.get_zero_flag(), false);
|
||||
assert_eq!(registers.y, 0x00FF);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_16bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.pc = 0x0000;
|
||||
registers.a = 0xF0F0;
|
||||
registers.y = 0x0000;
|
||||
registers.set_16bit_mode(true);
|
||||
registers.set_16bit_index(true);
|
||||
let instruction = TAY16{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.get_negative_flag(), true);
|
||||
assert_eq!(registers.get_zero_flag(), false);
|
||||
assert_eq!(registers.y, 0xF0F0);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
}
|
||||
}
|
||||
44
snes-core/src/cpu/instructions/tcd.rs
Normal file
44
snes-core/src/cpu/instructions/tcd.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
use crate::cpu::cycles;
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "TCD";
|
||||
|
||||
pub struct TCD {}
|
||||
|
||||
impl CPUInstruction for TCD {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
let result = registers.a;
|
||||
registers.d = result;
|
||||
registers.set_negative_flag((result >> 7) == 1);
|
||||
registers.set_zero_flag(result == 0);
|
||||
let (bytes, cycles) = cycles::increment_cycles_transfer();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.a = 0xF0F0;
|
||||
registers.d = 0x0000;
|
||||
let instruction = TCD{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.d, 0xF0F0);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
}
|
||||
}
|
||||
44
snes-core/src/cpu/instructions/tcs.rs
Normal file
44
snes-core/src/cpu/instructions/tcs.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
use crate::cpu::cycles;
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "TCS";
|
||||
|
||||
pub struct TCS {}
|
||||
|
||||
impl CPUInstruction for TCS {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
let result = registers.a;
|
||||
registers.sp = result;
|
||||
registers.set_negative_flag((result >> 7) == 1);
|
||||
registers.set_zero_flag(result == 0);
|
||||
let (bytes, cycles) = cycles::increment_cycles_transfer();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.a = 0xF0F0;
|
||||
registers.sp = 0x0000;
|
||||
let instruction = TCS{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.sp, 0xF0F0);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
}
|
||||
}
|
||||
44
snes-core/src/cpu/instructions/tdc.rs
Normal file
44
snes-core/src/cpu/instructions/tdc.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
use crate::cpu::cycles;
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "TDC";
|
||||
|
||||
pub struct TDC {}
|
||||
|
||||
impl CPUInstruction for TDC {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
let result = registers.d;
|
||||
registers.a = result;
|
||||
registers.set_negative_flag((result >> 7) == 1);
|
||||
registers.set_zero_flag(result == 0);
|
||||
let (bytes, cycles) = cycles::increment_cycles_transfer();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.a = 0x0000;
|
||||
registers.d = 0xF0F0;
|
||||
let instruction = TDC{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.a, 0xF0F0);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
}
|
||||
}
|
||||
82
snes-core/src/cpu/instructions/trb.rs
Normal file
82
snes-core/src/cpu/instructions/trb.rs
Normal file
@@ -0,0 +1,82 @@
|
||||
use crate::cpu::cycles;
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
use crate::utils::addressing::AddressingMode;
|
||||
|
||||
use super::read_write_common::{read_8bit_from_address, write_8bit_to_address, read_16bit_from_address, write_16bit_to_address};
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "TRB";
|
||||
|
||||
pub struct TRB {
|
||||
pub addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl TRB {
|
||||
fn determine_instruction(&self, registers: &Registers) -> Box<dyn CPUInstruction> {
|
||||
match registers.is_16bit_mode() {
|
||||
true => Box::new(TRB16{addressing_mode: self.addressing_mode}),
|
||||
false => Box::new(TRB8{addressing_mode: self.addressing_mode}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPUInstruction for TRB {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.execute(registers, bus);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.mnemonic(registers, bus, opcode)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TRB8 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for TRB8 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let value = read_8bit_from_address(registers, bus, self.addressing_mode);
|
||||
let result = (registers.a as u8) & value;
|
||||
write_8bit_to_address(registers, bus, self.addressing_mode, result);
|
||||
registers.set_zero_flag(result == 0);
|
||||
let (bytes, cycles) = cycles::increment_cycles_test(registers, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TRB16 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for TRB16 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let value = read_16bit_from_address(registers, bus, self.addressing_mode);
|
||||
let result = (registers.a) & value;
|
||||
write_16bit_to_address(registers, bus, self.addressing_mode, result);
|
||||
registers.set_zero_flag(result == 0);
|
||||
let (bytes, cycles) = cycles::increment_cycles_test(registers, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
}
|
||||
}
|
||||
80
snes-core/src/cpu/instructions/tsb.rs
Normal file
80
snes-core/src/cpu/instructions/tsb.rs
Normal file
@@ -0,0 +1,80 @@
|
||||
use crate::cpu::cycles;
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
use crate::utils::addressing::AddressingMode;
|
||||
|
||||
use super::read_write_common::{read_8bit_from_address, write_8bit_to_address, read_16bit_from_address, write_16bit_to_address};
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "TSB";
|
||||
|
||||
pub struct TSB {
|
||||
pub addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl TSB {
|
||||
fn determine_instruction(&self, registers: &Registers) -> Box<dyn CPUInstruction> {
|
||||
match registers.is_16bit_mode() {
|
||||
true => Box::new(TSB16{addressing_mode: self.addressing_mode}),
|
||||
false => Box::new(TSB8{addressing_mode: self.addressing_mode}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPUInstruction for TSB {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.execute(registers, bus);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.mnemonic(registers, bus, opcode)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TSB8 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for TSB8 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let value = read_8bit_from_address(registers, bus, self.addressing_mode);
|
||||
let result = (registers.a as u8) | value;
|
||||
write_8bit_to_address(registers, bus, self.addressing_mode, result);
|
||||
registers.set_zero_flag(result == 0);
|
||||
let (bytes, cycles) = cycles::increment_cycles_test(registers, self.addressing_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TSB16 {
|
||||
addressing_mode: AddressingMode,
|
||||
}
|
||||
|
||||
impl CPUInstruction for TSB16 {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let value = read_16bit_from_address(registers, bus, self.addressing_mode);
|
||||
let result = registers.a & value;
|
||||
write_16bit_to_address(registers, bus, self.addressing_mode, result);
|
||||
registers.set_zero_flag(result == 0);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
}
|
||||
}
|
||||
44
snes-core/src/cpu/instructions/tsc.rs
Normal file
44
snes-core/src/cpu/instructions/tsc.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
use crate::cpu::cycles;
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "TSC";
|
||||
|
||||
pub struct TSC {}
|
||||
|
||||
impl CPUInstruction for TSC {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
let result = registers.sp;
|
||||
registers.a = result;
|
||||
registers.set_negative_flag((result >> 7) == 1);
|
||||
registers.set_zero_flag(result == 0);
|
||||
let (bytes, cycles) = cycles::increment_cycles_transfer();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.a = 0x0000;
|
||||
registers.sp = 0xF0F0;
|
||||
let instruction = TSC{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.a, 0xF0F0);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
}
|
||||
}
|
||||
102
snes-core/src/cpu/instructions/tsx.rs
Normal file
102
snes-core/src/cpu/instructions/tsx.rs
Normal file
@@ -0,0 +1,102 @@
|
||||
use crate::cpu::cycles;
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "TSX";
|
||||
|
||||
pub struct TSX {}
|
||||
|
||||
impl TSX {
|
||||
fn determine_instruction(&self, registers: &Registers) -> Box<dyn CPUInstruction> {
|
||||
match registers.is_16bit_index() {
|
||||
true => Box::new(TSX16{}),
|
||||
false => Box::new(TSX8{}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPUInstruction for TSX {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.execute(registers, bus);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.mnemonic(registers, bus, opcode)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TSX8 {}
|
||||
|
||||
impl CPUInstruction for TSX8 {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
let result = registers.sp as u8;
|
||||
registers.set_low_x(result);
|
||||
registers.set_negative_flag((result >> 7) == 1);
|
||||
registers.set_zero_flag(result == 0);
|
||||
let (bytes, cycles) = cycles::increment_cycles_transfer();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TSX16 {}
|
||||
|
||||
impl CPUInstruction for TSX16 {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
let result = registers.sp;
|
||||
registers.x = result;
|
||||
registers.set_negative_flag((result >> 15) == 1);
|
||||
registers.set_zero_flag(result == 0);
|
||||
let (bytes, cycles) = cycles::increment_cycles_transfer();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_8bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.pc = 0x0000;
|
||||
registers.x = 0x0000;
|
||||
registers.sp = 0xF0F0;
|
||||
registers.set_16bit_index(false);
|
||||
let instruction = TSX8{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.x, 0x00F0);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_16bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.pc = 0x0000;
|
||||
registers.x = 0x0000;
|
||||
registers.sp = 0xF0F0;
|
||||
registers.set_16bit_index(true);
|
||||
let instruction = TSX16{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.x, 0xF0F0);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
}
|
||||
}
|
||||
102
snes-core/src/cpu/instructions/txa.rs
Normal file
102
snes-core/src/cpu/instructions/txa.rs
Normal file
@@ -0,0 +1,102 @@
|
||||
use crate::cpu::cycles;
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "TXA";
|
||||
|
||||
pub struct TXA {}
|
||||
|
||||
impl TXA {
|
||||
fn determine_instruction(&self, registers: &Registers) -> Box<dyn CPUInstruction> {
|
||||
match registers.is_16bit_mode() {
|
||||
true => Box::new(TXA16{}),
|
||||
false => Box::new(TXA8{}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPUInstruction for TXA {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.execute(registers, bus);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.mnemonic(registers, bus, opcode)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TXA8 {}
|
||||
|
||||
impl CPUInstruction for TXA8 {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
let result = registers.x as u8;
|
||||
registers.set_low_a(result);
|
||||
registers.set_negative_flag((result >> 7) == 1);
|
||||
registers.set_zero_flag(result == 0);
|
||||
let (bytes, cycles) = cycles::increment_cycles_transfer();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TXA16 {}
|
||||
|
||||
impl CPUInstruction for TXA16 {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
let result = registers.x;
|
||||
registers.a = result;
|
||||
registers.set_negative_flag((result >> 15) == 1);
|
||||
registers.set_zero_flag(result == 0);
|
||||
let (bytes, cycles) = cycles::increment_cycles_transfer();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_8bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.pc = 0x0000;
|
||||
registers.a = 0x0000;
|
||||
registers.x = 0xF0F0;
|
||||
registers.set_16bit_mode(false);
|
||||
let instruction = TXA8{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.a, 0x00F0);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_16bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.pc = 0x0000;
|
||||
registers.a = 0x0000;
|
||||
registers.x = 0xF0F0;
|
||||
registers.set_16bit_mode(true);
|
||||
let instruction = TXA16{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.a, 0xF0F0);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
}
|
||||
}
|
||||
102
snes-core/src/cpu/instructions/txs.rs
Normal file
102
snes-core/src/cpu/instructions/txs.rs
Normal file
@@ -0,0 +1,102 @@
|
||||
use crate::cpu::cycles;
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "TXS";
|
||||
|
||||
pub struct TXS {}
|
||||
|
||||
impl TXS {
|
||||
fn determine_instruction(&self, registers: &Registers) -> Box<dyn CPUInstruction> {
|
||||
match registers.is_16bit_index() {
|
||||
true => Box::new(TXS16{}),
|
||||
false => Box::new(TXS8{}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPUInstruction for TXS {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.execute(registers, bus);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.mnemonic(registers, bus, opcode)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TXS8 {}
|
||||
|
||||
impl CPUInstruction for TXS8 {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
let result = registers.x as u8;
|
||||
registers.set_low_sp(result);
|
||||
registers.set_negative_flag((result >> 7) == 1);
|
||||
registers.set_zero_flag(result == 0);
|
||||
let (bytes, cycles) = cycles::increment_cycles_transfer();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TXS16 {}
|
||||
|
||||
impl CPUInstruction for TXS16 {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
let result = registers.x;
|
||||
registers.sp = result;
|
||||
registers.set_negative_flag((result >> 15) == 1);
|
||||
registers.set_zero_flag(result == 0);
|
||||
let (bytes, cycles) = cycles::increment_cycles_transfer();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_8bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.pc = 0x0000;
|
||||
registers.sp = 0x0000;
|
||||
registers.x = 0xF0F0;
|
||||
registers.set_16bit_index(false);
|
||||
let instruction = TXS8{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.sp, 0x00F0);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_16bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.pc = 0x0000;
|
||||
registers.sp = 0x0000;
|
||||
registers.x = 0xF0F0;
|
||||
registers.set_16bit_index(true);
|
||||
let instruction = TXS16{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.sp, 0xF0F0);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
}
|
||||
}
|
||||
102
snes-core/src/cpu/instructions/txy.rs
Normal file
102
snes-core/src/cpu/instructions/txy.rs
Normal file
@@ -0,0 +1,102 @@
|
||||
use crate::cpu::cycles;
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "TXY";
|
||||
|
||||
pub struct TXY {}
|
||||
|
||||
impl TXY {
|
||||
fn determine_instruction(&self, registers: &Registers) -> Box<dyn CPUInstruction> {
|
||||
match registers.is_16bit_index() {
|
||||
true => Box::new(TXY16{}),
|
||||
false => Box::new(TXY8{}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPUInstruction for TXY {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.execute(registers, bus);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.mnemonic(registers, bus, opcode)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TXY8 {}
|
||||
|
||||
impl CPUInstruction for TXY8 {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
let result = registers.x as u8;
|
||||
registers.set_low_y(result);
|
||||
registers.set_negative_flag((result >> 7) == 1);
|
||||
registers.set_zero_flag(result == 0);
|
||||
let (bytes, cycles) = cycles::increment_cycles_transfer();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TXY16 {}
|
||||
|
||||
impl CPUInstruction for TXY16 {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
let result = registers.x;
|
||||
registers.y = result;
|
||||
registers.set_negative_flag((result >> 15) == 1);
|
||||
registers.set_zero_flag(result == 0);
|
||||
let (bytes, cycles) = cycles::increment_cycles_transfer();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_8bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.pc = 0x0000;
|
||||
registers.y = 0x0000;
|
||||
registers.x = 0xF0F0;
|
||||
registers.set_16bit_index(false);
|
||||
let instruction = TXY8{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.y, 0x00F0);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_16bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.pc = 0x0000;
|
||||
registers.y = 0x0000;
|
||||
registers.x = 0xF0F0;
|
||||
registers.set_16bit_index(true);
|
||||
let instruction = TXY16{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.y, 0xF0F0);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
}
|
||||
}
|
||||
101
snes-core/src/cpu/instructions/tya.rs
Normal file
101
snes-core/src/cpu/instructions/tya.rs
Normal file
@@ -0,0 +1,101 @@
|
||||
use crate::cpu::cycles;
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "TYA";
|
||||
|
||||
pub struct TYA {}
|
||||
|
||||
impl TYA {
|
||||
fn determine_instruction(&self, registers: &Registers) -> Box<dyn CPUInstruction> {
|
||||
match registers.is_16bit_mode() {
|
||||
true => Box::new(TYA16{}),
|
||||
false => Box::new(TYA8{}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPUInstruction for TYA {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.execute(registers, bus);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.mnemonic(registers, bus, opcode)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TYA8 {}
|
||||
|
||||
impl CPUInstruction for TYA8 {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
let result = registers.y as u8;
|
||||
registers.set_low_a(result);
|
||||
registers.set_negative_flag((result >> 7) == 1);
|
||||
registers.set_zero_flag(result == 0);
|
||||
let (bytes, cycles) = cycles::increment_cycles_transfer();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TYA16 {}
|
||||
|
||||
impl CPUInstruction for TYA16 {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
registers.a = registers.y;
|
||||
registers.set_negative_flag((registers.a >> 15) == 1);
|
||||
registers.set_zero_flag(registers.a == 0);
|
||||
let (bytes, cycles) = cycles::increment_cycles_transfer();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_8bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.pc = 0x0000;
|
||||
registers.a = 0x0000;
|
||||
registers.y = 0xF0F0;
|
||||
registers.set_16bit_mode(false);
|
||||
let instruction = TYA8{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.a, 0x00F0);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_16bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.pc = 0x0000;
|
||||
registers.a = 0x0000;
|
||||
registers.y = 0xF0F0;
|
||||
registers.set_16bit_mode(true);
|
||||
let instruction = TYA16{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.a, 0xF0F0);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
}
|
||||
}
|
||||
102
snes-core/src/cpu/instructions/tyx.rs
Normal file
102
snes-core/src/cpu/instructions/tyx.rs
Normal file
@@ -0,0 +1,102 @@
|
||||
use crate::cpu::cycles;
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "TYX";
|
||||
|
||||
pub struct TYX {}
|
||||
|
||||
impl TYX {
|
||||
fn determine_instruction(&self, registers: &Registers) -> Box<dyn CPUInstruction> {
|
||||
match registers.is_16bit_index() {
|
||||
true => Box::new(TYX16{}),
|
||||
false => Box::new(TYX8{}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPUInstruction for TYX {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.execute(registers, bus);
|
||||
}
|
||||
|
||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||
let instruction = self.determine_instruction(registers);
|
||||
instruction.mnemonic(registers, bus, opcode)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TYX8 {}
|
||||
|
||||
impl CPUInstruction for TYX8 {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
let result = registers.y as u8;
|
||||
registers.set_low_x(result);
|
||||
registers.set_negative_flag((result >> 7) == 1);
|
||||
registers.set_zero_flag(result == 0);
|
||||
let (bytes, cycles) = cycles::increment_cycles_transfer();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TYX16 {}
|
||||
|
||||
impl CPUInstruction for TYX16 {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
let result = registers.y;
|
||||
registers.x = result;
|
||||
registers.set_negative_flag((result >> 15) == 1);
|
||||
registers.set_zero_flag(result == 0);
|
||||
let (bytes, cycles) = cycles::increment_cycles_transfer();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_8bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.pc = 0x0000;
|
||||
registers.x = 0x0000;
|
||||
registers.y = 0xF0F0;
|
||||
registers.set_16bit_index(false);
|
||||
let instruction = TYX8{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.x, 0x00F0);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_16bit() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.pc = 0x0000;
|
||||
registers.x = 0x0000;
|
||||
registers.y = 0xF0F0;
|
||||
registers.set_16bit_index(true);
|
||||
let instruction = TYX16{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.x, 0xF0F0);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
}
|
||||
}
|
||||
40
snes-core/src/cpu/instructions/wai.rs
Normal file
40
snes-core/src/cpu/instructions/wai.rs
Normal file
@@ -0,0 +1,40 @@
|
||||
use crate::cpu::{bus::Bus, registers::Registers};
|
||||
|
||||
use crate::cpu::cycles;
|
||||
use super::CPUInstruction;
|
||||
use super::decoder_common;
|
||||
|
||||
static INSTR_NAME: &'static str = "WAI";
|
||||
|
||||
pub struct WAI {}
|
||||
|
||||
impl CPUInstruction for WAI {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
registers.is_cpu_waiting_interrupt = true;
|
||||
let (bytes, cycles) = cycles::increment_cycles_stp();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String {
|
||||
decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.is_cpu_waiting_interrupt = false;
|
||||
registers.pc = 0x0000;
|
||||
let instruction = WAI{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.is_cpu_waiting_interrupt, true);
|
||||
assert_eq!(registers.cycles, 3);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user