mirror of
https://github.com/FranLMSP/snes.git
synced 2026-08-03 08:30:01 -04:00
Refactor increment cycles function
This commit is contained in:
+125
-75
@@ -1,68 +1,108 @@
|
||||
use super::cpu::CPU;
|
||||
use crate::utils::addressing::{AddressingMode, IndexRegister};
|
||||
|
||||
type A = AddressingMode;
|
||||
type I = IndexRegister;
|
||||
|
||||
enum Condition {
|
||||
MemorySelectFlag,
|
||||
DirectPageZero,
|
||||
IndexCrossesPageBoundary,
|
||||
DecimalMode,
|
||||
}
|
||||
|
||||
const ALL_CONDITIONS: [Condition; 4] = [
|
||||
Condition::MemorySelectFlag,
|
||||
Condition::DirectPageZero,
|
||||
Condition::IndexCrossesPageBoundary,
|
||||
Condition::DecimalMode,
|
||||
];
|
||||
|
||||
impl CPU {
|
||||
pub fn increment_cycles_adc_sbc(&mut self, addressing_mode: AddressingMode) {
|
||||
type A = AddressingMode;
|
||||
type I = IndexRegister;
|
||||
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;
|
||||
}
|
||||
fn common_conditions(&mut self, addressing_mode: AddressingMode, conditions: &[Condition]) {
|
||||
let mut bytes = 0;
|
||||
let mut cycles = 0;
|
||||
|
||||
// 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(index) | A::DirectPageIndirectIndexed(index) => {
|
||||
let page = (self.registers.get_pc_address() - (bytes as u32)) & 0xFF;
|
||||
let index = match index {
|
||||
I::X => self.registers.x,
|
||||
I::Y => self.registers.y,
|
||||
};
|
||||
if (page + index as u32) > 0xFF {
|
||||
cycles += 1
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
};
|
||||
|
||||
// condition 4: Add 1 cycle if 65C02 and d = 1 (decimal mode, 65C02)
|
||||
if self.registers.get_decimal_mode_flag() {
|
||||
cycles += 1;
|
||||
for condition in conditions {
|
||||
match condition {
|
||||
// Add 1 byte and 1 cycle if m = 0 (16-bit memory/accumulator)
|
||||
Condition::MemorySelectFlag => {
|
||||
if !self.registers.get_memory_select_flag() {
|
||||
cycles += 1;
|
||||
match addressing_mode {
|
||||
A::Immediate => bytes += 1,
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
},
|
||||
// Add 1 cycle if low byte of Direct Page register is other than zero (DL< >0)
|
||||
Condition::DirectPageZero => {
|
||||
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;
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
};
|
||||
},
|
||||
// Add 1 cycle if adding index crosses a page boundary
|
||||
Condition::IndexCrossesPageBoundary => {
|
||||
match addressing_mode {
|
||||
A::AbsoluteIndexed(index) | A::DirectPageIndirectIndexed(index) => {
|
||||
let page = self.registers.get_pc_address() & 0xFF;
|
||||
let index = match index {
|
||||
I::X => self.registers.x,
|
||||
I::Y => self.registers.y,
|
||||
};
|
||||
if (page + index as u32) > 0xFF {
|
||||
cycles += 1
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
};
|
||||
},
|
||||
// Add 1 cycle if 65C02 and d = 1 (decimal mode, 65C02)
|
||||
Condition::DecimalMode => {
|
||||
if self.registers.get_decimal_mode_flag() {
|
||||
cycles += 1;
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
self.registers.increment_pc(bytes);
|
||||
self.cycles += cycles;
|
||||
}
|
||||
|
||||
fn common_bytes_cycles_arithmetic(addressing_mode: AddressingMode) -> (u16, usize) {
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn increment_cycles_arithmetic(&mut self, addressing_mode: AddressingMode) {
|
||||
let (bytes, cycles) = CPU::common_bytes_cycles_arithmetic(addressing_mode);
|
||||
self.registers.increment_pc(bytes);
|
||||
self.cycles += cycles;
|
||||
self.common_conditions(addressing_mode, &ALL_CONDITIONS);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -70,7 +110,7 @@ mod cpu_instructions_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_increment_cycles_adc_sbc() {
|
||||
fn test_common_conditions() {
|
||||
let mut cpu = CPU::new();
|
||||
|
||||
// 16-bit Memory/accumulator flag condition
|
||||
@@ -78,25 +118,25 @@ mod cpu_instructions_tests {
|
||||
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.common_conditions(AddressingMode::Immediate, &ALL_CONDITIONS);
|
||||
assert_eq!(cpu.registers.pc, 0);
|
||||
assert_eq!(cpu.cycles, 0);
|
||||
|
||||
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.common_conditions(AddressingMode::Immediate, &ALL_CONDITIONS);
|
||||
assert_eq!(cpu.registers.pc, 1);
|
||||
assert_eq!(cpu.cycles, 1);
|
||||
|
||||
// Decimal flag condition
|
||||
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.common_conditions(AddressingMode::Immediate, &ALL_CONDITIONS);
|
||||
assert_eq!(cpu.registers.pc, 1);
|
||||
assert_eq!(cpu.cycles, 2);
|
||||
|
||||
// Low byte of direct page register other than zero condition
|
||||
cpu.registers.pc = 0;
|
||||
@@ -104,18 +144,18 @@ mod cpu_instructions_tests {
|
||||
cpu.registers.set_16bit_mode(false);
|
||||
cpu.registers.set_decimal_mode_flag(false);
|
||||
cpu.registers.d = 0x0000;
|
||||
cpu.increment_cycles_adc_sbc(AddressingMode::DirectPage);
|
||||
assert_eq!(cpu.registers.pc, 2);
|
||||
assert_eq!(cpu.cycles, 3);
|
||||
cpu.common_conditions(AddressingMode::DirectPage, &ALL_CONDITIONS);
|
||||
assert_eq!(cpu.registers.pc, 0);
|
||||
assert_eq!(cpu.cycles, 0);
|
||||
|
||||
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);
|
||||
cpu.common_conditions(AddressingMode::DirectPage, &ALL_CONDITIONS);
|
||||
assert_eq!(cpu.registers.pc, 0);
|
||||
assert_eq!(cpu.cycles, 1);
|
||||
|
||||
// Adding index crosses a page boundary condition
|
||||
cpu.registers.pc = 0xFE;
|
||||
@@ -123,17 +163,27 @@ mod cpu_instructions_tests {
|
||||
cpu.cycles = 0;
|
||||
cpu.registers.set_16bit_mode(false);
|
||||
cpu.registers.set_decimal_mode_flag(false);
|
||||
cpu.increment_cycles_adc_sbc(AddressingMode::AbsoluteIndexed(IndexRegister::X));
|
||||
assert_eq!(cpu.registers.pc, 0xFE + 3);
|
||||
assert_eq!(cpu.cycles, 4);
|
||||
cpu.common_conditions(AddressingMode::AbsoluteIndexed(IndexRegister::X), &ALL_CONDITIONS);
|
||||
assert_eq!(cpu.registers.pc, 0xFE);
|
||||
assert_eq!(cpu.cycles, 0); // Doesn't cross boundary
|
||||
|
||||
cpu.registers.pc = 0xFE;
|
||||
cpu.registers.x = 0x0010;
|
||||
cpu.cycles = 0;
|
||||
cpu.registers.set_16bit_mode(false);
|
||||
cpu.registers.set_decimal_mode_flag(false);
|
||||
cpu.increment_cycles_adc_sbc(AddressingMode::AbsoluteIndexed(IndexRegister::X));
|
||||
assert_eq!(cpu.registers.pc, 0xFE + 3);
|
||||
cpu.common_conditions(AddressingMode::AbsoluteIndexed(IndexRegister::X), &ALL_CONDITIONS);
|
||||
assert_eq!(cpu.registers.pc, 0xFE);
|
||||
assert_eq!(cpu.cycles, 1); // Crosses boundary
|
||||
|
||||
// Test common and aritmetic together
|
||||
cpu.registers.pc = 0xF5;
|
||||
cpu.registers.x = 0x0010;
|
||||
cpu.cycles = 0;
|
||||
cpu.registers.set_16bit_mode(false);
|
||||
cpu.registers.set_decimal_mode_flag(false);
|
||||
cpu.increment_cycles_arithmetic(AddressingMode::AbsoluteIndexed(IndexRegister::X));
|
||||
assert_eq!(cpu.registers.pc, 0xF5 + 3);
|
||||
assert_eq!(cpu.cycles, 5);
|
||||
}
|
||||
}
|
||||
@@ -55,6 +55,7 @@ impl CPU {
|
||||
self.registers.set_zero_flag(is_zero);
|
||||
}
|
||||
};
|
||||
self.increment_cycles_arithmetic(addressing_mode);
|
||||
}
|
||||
|
||||
fn sbc(&mut self, bus: &Bus, addressing_mode: AddressingMode) {
|
||||
@@ -88,6 +89,7 @@ impl CPU {
|
||||
self.registers.set_zero_flag(is_zero);
|
||||
}
|
||||
};
|
||||
self.increment_cycles_arithmetic(addressing_mode);
|
||||
}
|
||||
|
||||
pub fn execute_opcode(&mut self, opcode: u8, bus: &Bus) {
|
||||
@@ -146,6 +148,8 @@ mod cpu_instructions_tests {
|
||||
bus.write(0x000001, 0x40);
|
||||
cpu.adc(&bus, AddressingMode::Immediate);
|
||||
assert_eq!(cpu.registers.a, 0x40);
|
||||
assert_eq!(cpu.registers.pc, 0x02);
|
||||
assert_eq!(cpu.cycles, 2);
|
||||
assert!(!cpu.registers.get_carry_flag());
|
||||
}
|
||||
|
||||
@@ -160,6 +164,8 @@ mod cpu_instructions_tests {
|
||||
bus.write(0x000001, 1);
|
||||
cpu.sbc(&bus, AddressingMode::Immediate);
|
||||
assert_eq!(cpu.registers.a, 0);
|
||||
assert_eq!(cpu.registers.pc, 0x02);
|
||||
assert_eq!(cpu.cycles, 2);
|
||||
assert!(!cpu.registers.get_carry_flag());
|
||||
assert!(cpu.registers.get_zero_flag());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user