diff --git a/snes-core/src/cpu/instructionstemp/mod.rs b/snes-core/src/cpu/instructionstemp/mod.rs index 90ee04a..c8936ac 100644 --- a/snes-core/src/cpu/instructionstemp/mod.rs +++ b/snes-core/src/cpu/instructionstemp/mod.rs @@ -40,11 +40,30 @@ 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 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; diff --git a/snes-core/src/cpu/instructionstemp/nop.rs b/snes-core/src/cpu/instructionstemp/nop.rs new file mode 100644 index 0000000..106a365 --- /dev/null +++ b/snes-core/src/cpu/instructionstemp/nop.rs @@ -0,0 +1,39 @@ +use crate::cpu::cycles; +use crate::cpu::{bus::Bus, registers::Registers}; + +use super::{CPUInstruction, Decode}; +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; + } +} + +impl Decode for NOP { + 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); + } +} diff --git a/snes-core/src/cpu/instructionstemp/ora.rs b/snes-core/src/cpu/instructionstemp/ora.rs new file mode 100644 index 0000000..85bba1a --- /dev/null +++ b/snes-core/src/cpu/instructionstemp/ora.rs @@ -0,0 +1,92 @@ +use crate::{cpu::{bus::Bus, registers::Registers}, utils::{alu, addressing::AddressingMode}}; + +use crate::cpu::cycles; +use super::{CPUInstruction, Decode, read_8bit_from_address, read_16bit_from_address}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "ORA"; + +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; + } +} + +impl Decode for ORA8 { + 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; + } +} + +impl Decode for ORA16 { + 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()); + } +} diff --git a/snes-core/src/cpu/instructionstemp/pea.rs b/snes-core/src/cpu/instructionstemp/pea.rs new file mode 100644 index 0000000..7958cbb --- /dev/null +++ b/snes-core/src/cpu/instructionstemp/pea.rs @@ -0,0 +1,47 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use crate::utils::addressing::AddressingMode; +use super::{CPUInstruction, Decode, get_effective_address, 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; + } +} + +impl Decode for PEA { + 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); + } +} diff --git a/snes-core/src/cpu/instructionstemp/pei.rs b/snes-core/src/cpu/instructionstemp/pei.rs new file mode 100644 index 0000000..4744ad7 --- /dev/null +++ b/snes-core/src/cpu/instructionstemp/pei.rs @@ -0,0 +1,49 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use crate::utils::addressing::AddressingMode; +use super::{CPUInstruction, Decode, get_effective_address, 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; + } +} + +impl Decode for PEI { + 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); + } +} diff --git a/snes-core/src/cpu/instructionstemp/per.rs b/snes-core/src/cpu/instructionstemp/per.rs new file mode 100644 index 0000000..f8a3f82 --- /dev/null +++ b/snes-core/src/cpu/instructionstemp/per.rs @@ -0,0 +1,52 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use crate::utils::addressing::AddressingMode; +use super::{CPUInstruction, Decode, get_effective_address, 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]); + } +} + +impl Decode for PER { + 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); + } +} diff --git a/snes-core/src/cpu/instructionstemp/pha.rs b/snes-core/src/cpu/instructionstemp/pha.rs new file mode 100644 index 0000000..ff5dc7a --- /dev/null +++ b/snes-core/src/cpu/instructionstemp/pha.rs @@ -0,0 +1,50 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::{CPUInstruction, Decode, 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; + } +} + +impl Decode for PHA { + 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); + } +} diff --git a/snes-core/src/cpu/instructionstemp/phb.rs b/snes-core/src/cpu/instructionstemp/phb.rs new file mode 100644 index 0000000..a4a02fd --- /dev/null +++ b/snes-core/src/cpu/instructionstemp/phb.rs @@ -0,0 +1,44 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::{CPUInstruction, Decode, 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; + } +} + +impl Decode for PHB { + 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); + } +} diff --git a/snes-core/src/cpu/instructionstemp/phd.rs b/snes-core/src/cpu/instructionstemp/phd.rs new file mode 100644 index 0000000..63db4f1 --- /dev/null +++ b/snes-core/src/cpu/instructionstemp/phd.rs @@ -0,0 +1,46 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::{CPUInstruction, Decode, 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; + } +} + +impl Decode for PHD { + 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); + } +} diff --git a/snes-core/src/cpu/instructionstemp/phk.rs b/snes-core/src/cpu/instructionstemp/phk.rs new file mode 100644 index 0000000..7065ed2 --- /dev/null +++ b/snes-core/src/cpu/instructionstemp/phk.rs @@ -0,0 +1,45 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::{CPUInstruction, Decode, 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; + } +} + +impl Decode for PHK { + 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); + } +} diff --git a/snes-core/src/cpu/instructionstemp/php.rs b/snes-core/src/cpu/instructionstemp/php.rs new file mode 100644 index 0000000..bd03f75 --- /dev/null +++ b/snes-core/src/cpu/instructionstemp/php.rs @@ -0,0 +1,44 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::{CPUInstruction, Decode, 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; + } +} + +impl Decode for PHP { + 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); + } +} diff --git a/snes-core/src/cpu/instructionstemp/phx.rs b/snes-core/src/cpu/instructionstemp/phx.rs new file mode 100644 index 0000000..a4a4e05 --- /dev/null +++ b/snes-core/src/cpu/instructionstemp/phx.rs @@ -0,0 +1,52 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::{CPUInstruction, Decode, 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; + } +} + +impl Decode for PHX { + 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); + } +} diff --git a/snes-core/src/cpu/instructionstemp/phy.rs b/snes-core/src/cpu/instructionstemp/phy.rs new file mode 100644 index 0000000..91a4094 --- /dev/null +++ b/snes-core/src/cpu/instructionstemp/phy.rs @@ -0,0 +1,52 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::{CPUInstruction, Decode, 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; + } +} + +impl Decode for PHY { + 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); + } +} diff --git a/snes-core/src/cpu/instructionstemp/pla.rs b/snes-core/src/cpu/instructionstemp/pla.rs new file mode 100644 index 0000000..66de8be --- /dev/null +++ b/snes-core/src/cpu/instructionstemp/pla.rs @@ -0,0 +1,58 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::{CPUInstruction, Decode, 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; + } +} + +impl Decode for PLA { + 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); + } +} diff --git a/snes-core/src/cpu/instructionstemp/plb.rs b/snes-core/src/cpu/instructionstemp/plb.rs new file mode 100644 index 0000000..3f622db --- /dev/null +++ b/snes-core/src/cpu/instructionstemp/plb.rs @@ -0,0 +1,49 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::{CPUInstruction, Decode, 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; + } +} + +impl Decode for PLB { + 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); + } +} diff --git a/snes-core/src/cpu/instructionstemp/pld.rs b/snes-core/src/cpu/instructionstemp/pld.rs new file mode 100644 index 0000000..672e0b0 --- /dev/null +++ b/snes-core/src/cpu/instructionstemp/pld.rs @@ -0,0 +1,51 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::{CPUInstruction, Decode, 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; + } +} + +impl Decode for PLD { + 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); + } +} diff --git a/snes-core/src/cpu/instructionstemp/plp.rs b/snes-core/src/cpu/instructionstemp/plp.rs new file mode 100644 index 0000000..e9ec762 --- /dev/null +++ b/snes-core/src/cpu/instructionstemp/plp.rs @@ -0,0 +1,46 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::{CPUInstruction, Decode, 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; + } +} + +impl Decode for PLP { + 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); + } +} diff --git a/snes-core/src/cpu/instructionstemp/plx.rs b/snes-core/src/cpu/instructionstemp/plx.rs new file mode 100644 index 0000000..35bbb52 --- /dev/null +++ b/snes-core/src/cpu/instructionstemp/plx.rs @@ -0,0 +1,58 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::{CPUInstruction, Decode, 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; + } +} + +impl Decode for PLX { + 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); + } +} diff --git a/snes-core/src/cpu/instructionstemp/ply.rs b/snes-core/src/cpu/instructionstemp/ply.rs new file mode 100644 index 0000000..807bcf5 --- /dev/null +++ b/snes-core/src/cpu/instructionstemp/ply.rs @@ -0,0 +1,58 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::{CPUInstruction, Decode, 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; + } +} + +impl Decode for PLY { + 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); + } +} diff --git a/snes-core/src/cpu/instructionstemp/pull_common.rs b/snes-core/src/cpu/instructionstemp/pull_common.rs new file mode 100644 index 0000000..1adf47d --- /dev/null +++ b/snes-core/src/cpu/instructionstemp/pull_common.rs @@ -0,0 +1,22 @@ +use crate::cpu::{registers::Registers, bus::Bus}; + + +pub fn do_pull(registers: &mut Registers, bus: &mut Bus, count: usize) -> Vec { + 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 +} \ No newline at end of file