From 794cbef477942170637d7d75851539e09cd98521 Mon Sep 17 00:00:00 2001 From: Franco Colmenarez Date: Sun, 20 Nov 2022 17:07:21 -0500 Subject: [PATCH] Base byte and cycles calculation based on addressing mode --- snes-core/src/cpu/cycles.rs | 107 ++++++++++++++++++++++++++++++ snes-core/src/cpu/mod.rs | 1 + snes-core/src/cpu/registers.rs | 41 ++++++++++++ snes-core/src/utils/addressing.rs | 1 + 4 files changed, 150 insertions(+) create mode 100644 snes-core/src/cpu/cycles.rs diff --git a/snes-core/src/cpu/cycles.rs b/snes-core/src/cpu/cycles.rs new file mode 100644 index 0000000..3cea88e --- /dev/null +++ b/snes-core/src/cpu/cycles.rs @@ -0,0 +1,107 @@ +use super::cpu::CPU; +use crate::utils::addressing::{AddressingMode}; + + +impl CPU { + pub fn increment_cycles_adc_sbc(&mut self, addressing_mode: AddressingMode) { + type A = AddressingMode; + let (mut bytes, mut cycles) = match addressing_mode { + A::Immediate => (2, 2), + A::Absolute => (3, 4), + A::AbsoluteLong => (4, 5), + A::DirectPage => (2, 3), + A::DirectPageIndirect => (2, 5), + A::DirectPageIndirectLong => (2, 6), + A::AbsoluteIndexed(_) => (3, 4), + A::AbsoluteLongIndexed(_) => (4, 5), + A::DirectPageIndexed(_) => (2, 4), + A::DirectPageIndexedIndirect(_) => (2, 6), + A::DirectPageIndirectIndexed(_) => (2, 5), + A::DirectPageIndirectLongIndexed(_) => (2, 6), + A::StackRelative => (2, 4), + A::StackRelativeIndirectIndexed(_) => (2, 7), + }; + // condition 1: Add 1 byte and 1 cycle if m = 0 (16-bit memory/accumulator) + if !self.registers.get_memory_select_flag() { + bytes += 1; + cycles += 1; + } + + // condition 2: Add 1 cycle if low byte of Direct Page register is other than zero (DL< >0) + match addressing_mode { + A::DirectPage | A::DirectPageIndirect | A::DirectPageIndirectLong | + A::DirectPageIndexed(_) | A::DirectPageIndexedIndirect(_) | + A::DirectPageIndirectIndexed(_) | A::DirectPageIndirectLongIndexed(_) => { + if self.registers.direct_page_low() != 0 { + cycles += 1; + } + }, + _ => {}, + }; + + // condition 3: Add 1 cycle if adding index crosses a page boundary + match addressing_mode { + A::AbsoluteIndexed(_) | A::DirectPageIndirectIndexed(_) => { + todo!("Check if index crosses a page boundary") + } + _ => {}, + }; + + // condition 4: Add 1 cycle if 65C02 and d = 1 (decimal mode, 65C02) + if self.registers.get_decimal_mode_flag() { + cycles += 1; + } + + self.registers.increment_pc(bytes); + self.cycles += cycles; + } +} + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test_increment_cycles_adc_sbc() { + let mut cpu = CPU::new(); + cpu.registers.pc = 0; + cpu.cycles = 0; + cpu.registers.set_decimal_mode_flag(false); + cpu.registers.set_16bit_mode(false); + cpu.increment_cycles_adc_sbc(AddressingMode::Immediate); + assert_eq!(cpu.registers.pc, 2); + assert_eq!(cpu.cycles, 2); + + cpu.registers.pc = 0; + cpu.cycles = 0; + cpu.registers.set_16bit_mode(true); + cpu.increment_cycles_adc_sbc(AddressingMode::Immediate); + assert_eq!(cpu.registers.pc, 3); + assert_eq!(cpu.cycles, 3); + + cpu.registers.pc = 0; + cpu.cycles = 0; + cpu.registers.set_16bit_mode(true); + cpu.registers.set_decimal_mode_flag(true); + cpu.increment_cycles_adc_sbc(AddressingMode::Immediate); + assert_eq!(cpu.registers.pc, 3); + assert_eq!(cpu.cycles, 4); + + cpu.registers.pc = 0; + cpu.cycles = 0; + cpu.registers.set_16bit_mode(false); + cpu.registers.set_decimal_mode_flag(false); + cpu.increment_cycles_adc_sbc(AddressingMode::DirectPage); + assert_eq!(cpu.registers.pc, 2); + assert_eq!(cpu.cycles, 3); + + cpu.registers.pc = 0; + cpu.cycles = 0; + cpu.registers.set_16bit_mode(false); + cpu.registers.set_decimal_mode_flag(false); + cpu.registers.d = 0x0001; + cpu.increment_cycles_adc_sbc(AddressingMode::DirectPage); + assert_eq!(cpu.registers.pc, 2); + assert_eq!(cpu.cycles, 4); + } +} \ No newline at end of file diff --git a/snes-core/src/cpu/mod.rs b/snes-core/src/cpu/mod.rs index 2cbe558..5e537d8 100644 --- a/snes-core/src/cpu/mod.rs +++ b/snes-core/src/cpu/mod.rs @@ -1,3 +1,4 @@ pub mod cpu; pub mod registers; pub mod instructions; +pub mod cycles; diff --git a/snes-core/src/cpu/registers.rs b/snes-core/src/cpu/registers.rs index cc0d66a..2e08685 100644 --- a/snes-core/src/cpu/registers.rs +++ b/snes-core/src/cpu/registers.rs @@ -115,12 +115,46 @@ impl Registers { pub fn get_pc_address(&self) -> u32 { ((self.pbr as u32) << 16) | (self.pc as u32) } + + pub fn is_16bit_mode(&self) -> bool { + !self.get_memory_select_flag() + } + + pub fn set_16bit_mode(&mut self, val: bool) { + self.set_memory_select_flag(!val); + } + + pub fn increment_pc(&mut self, bytes: u16) { + self.pc = self.pc.wrapping_add(bytes); + } + + pub fn direct_page_low(&self) -> u8 { + self.d as u8 + } } #[cfg(test)] mod registers_tests { use super::*; + #[test] + fn test_get_16bit_mode() { + let mut registers = Registers::new(); + registers.set_memory_select_flag(false); + assert!(registers.is_16bit_mode()); + registers.set_memory_select_flag(true); + assert!(!registers.is_16bit_mode()); + } + + #[test] + fn test_set_16bit_mode() { + let mut registers = Registers::new(); + registers.set_16bit_mode(true); + assert!(registers.is_16bit_mode()); + registers.set_16bit_mode(false); + assert!(!registers.is_16bit_mode()); + } + #[test] fn test_set_low_a() { let mut registers = Registers::new(); @@ -129,6 +163,13 @@ mod registers_tests { assert_eq!(registers.a, 0xA1FF); } + #[test] + fn test_direct_page_log() { + let mut registers = Registers::new(); + registers.d = 0xA1A1; + assert_eq!(registers.direct_page_low(), 0xA1); + } + #[test] fn test_get_pc_address() { let mut registers = Registers::new(); diff --git a/snes-core/src/utils/addressing.rs b/snes-core/src/utils/addressing.rs index 4f2a05e..e7e76cd 100644 --- a/snes-core/src/utils/addressing.rs +++ b/snes-core/src/utils/addressing.rs @@ -112,6 +112,7 @@ pub enum AddressingMode { impl AddressingMode { pub fn effective_address(self, bus: &Bus, pc_addr: u32, direct_page_register: u16, stack_pointer: u16, x: u16, y: u16) -> u32 { use IndexRegister::X as X; + // TODO: maybe use impl Immediate {pub fn effective_address} to prevent this match statement? match self { Self::Immediate => immediate(pc_addr), Self::Absolute => absolute(bus, pc_addr),