diff --git a/snes-core/src/cpu/instructionstemp/bcc.rs b/snes-core/src/cpu/instructionstemp/bcc.rs new file mode 100644 index 0000000..cbbb147 --- /dev/null +++ b/snes-core/src/cpu/instructionstemp/bcc.rs @@ -0,0 +1,67 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::{CPUInstruction, Decode}; +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) { + 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 !registers.get_carry_flag() { + 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; + } + } +} + +impl Decode for BCC { + 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_bcc() { + 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); + } +} diff --git a/snes-core/src/cpu/instructionstemp/branch_common.rs b/snes-core/src/cpu/instructionstemp/branch_common.rs new file mode 100644 index 0000000..1e8a6b7 --- /dev/null +++ b/snes-core/src/cpu/instructionstemp/branch_common.rs @@ -0,0 +1,15 @@ +use crate::cpu::registers::Registers; + +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 +} diff --git a/snes-core/src/cpu/instructionstemp/decoder_common.rs b/snes-core/src/cpu/instructionstemp/decoder_common.rs index 48ab4b0..ec3a2a4 100644 --- a/snes-core/src/cpu/instructionstemp/decoder_common.rs +++ b/snes-core/src/cpu/instructionstemp/decoder_common.rs @@ -111,3 +111,8 @@ pub fn mnemonic_stack_relative_indirect_indexed(opcode: u8, instr_name: &str, in 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) +} diff --git a/snes-core/src/cpu/instructionstemp/mod.rs b/snes-core/src/cpu/instructionstemp/mod.rs index 6742268..87d4da9 100644 --- a/snes-core/src/cpu/instructionstemp/mod.rs +++ b/snes-core/src/cpu/instructionstemp/mod.rs @@ -5,7 +5,9 @@ use crate::utils::addressing::AddressingMode; pub mod adc; pub mod and; pub mod asl; +pub mod bcc; pub mod decoder_common; +pub mod branch_common; pub trait CPUInstruction { fn execute(&self, registers: &mut Registers, bus: &mut Bus);