From cd05183bb377e99b41a36f2cf3027ba245b80da7 Mon Sep 17 00:00:00 2001 From: Nick Krichevsky Date: Tue, 21 Nov 2023 23:20:40 -0500 Subject: [PATCH] Add JP HL instruction --- src/cpu/instructions/control.rs | 1 + src/cpu/parse/control.rs | 8 +++-- src/cpu/run/control.rs | 29 ++++++++++++------- tests/cpu/control.rs | 19 +++++++++++- .../cpu/jsmoo/testdata/{disabled => }/e9.json | 0 5 files changed, 44 insertions(+), 13 deletions(-) rename tests/cpu/jsmoo/testdata/{disabled => }/e9.json (100%) diff --git a/src/cpu/instructions/control.rs b/src/cpu/instructions/control.rs index 130da41..1ea42ff 100644 --- a/src/cpu/instructions/control.rs +++ b/src/cpu/instructions/control.rs @@ -6,6 +6,7 @@ pub enum ControlFlowInstruction { JumpToImmediate { addr: u16, }, + JumpToAddressInHL, Call { addr: u16, }, diff --git a/src/cpu/parse/control.rs b/src/cpu/parse/control.rs index f7636a2..486314e 100644 --- a/src/cpu/parse/control.rs +++ b/src/cpu/parse/control.rs @@ -24,11 +24,11 @@ impl OpcodeParser for Parser { } fn parse_unconditional_control_operation(data: &View) -> ParseResult { - parse_unconditional_immediate_based_control_flow_operation(data) + parse_unconditional_parameterized_control_flow_operation(data) .or_parse(parse_restart_instruction) } -fn parse_unconditional_immediate_based_control_flow_operation(data: &View) -> ParseResult { +fn parse_unconditional_parameterized_control_flow_operation(data: &View) -> ParseResult { let opcode = parse::get_opcode_from_data(data); match opcode { 0xC3 => Ok(build_immediate_parameterized_control_flow_data( @@ -39,6 +39,10 @@ fn parse_unconditional_immediate_based_control_flow_operation(data: &View) -> Pa data, |addr| ControlFlowInstruction::Call { addr }, )), + 0xE9 => Ok(( + Instruction::ControlFlow(ControlFlowInstruction::JumpToAddressInHL), + 1, + )), _ => Err(parse::Error::UnknownOpcode(opcode)), } } diff --git a/src/cpu/run/control.rs b/src/cpu/run/control.rs index bcd119f..5594d22 100644 --- a/src/cpu/run/control.rs +++ b/src/cpu/run/control.rs @@ -6,19 +6,24 @@ impl Run for ControlFlowInstruction { fn run_on(&self, processor: &mut Processor) -> Result { match *self { Self::JumpToImmediate { addr } => { - processor - .registers - .set_single_16bit_register(register::SingleSixteenBit::ProgramCounter, addr); + jump_to(processor, addr); Ok(Cycles(16)) } + Self::JumpToAddressInHL => { + let addr = processor + .registers + .get_combined_register(register::Combined::HL); + + jump_to(processor, addr); + + Ok(Cycles(4)) + } + Self::JumpToImmediateIfFlagMatches { flag, value, addr } => { if processor.registers.get_flag_bit(flag) == value { - processor.registers.set_single_16bit_register( - register::SingleSixteenBit::ProgramCounter, - addr, - ); + jump_to(processor, addr); Ok(Cycles(16)) } else { Ok(Cycles(12)) @@ -65,9 +70,13 @@ fn do_call(processor: &mut Processor, to_addr: u16) -> Result<(), super::Error> // We know the call instruction to be 3 bytes. A bit of a hack, but eh. processor.push_16bit_value_to_stack(current_pc.wrapping_add(3))?; - processor - .registers - .set_single_16bit_register(register::SingleSixteenBit::ProgramCounter, to_addr); + jump_to(processor, to_addr); Ok(()) } + +fn jump_to(processor: &mut Processor, to_addr: u16) { + processor + .registers + .set_single_16bit_register(register::SingleSixteenBit::ProgramCounter, to_addr); +} diff --git a/tests/cpu/control.rs b/tests/cpu/control.rs index 603db1a..13066c1 100644 --- a/tests/cpu/control.rs +++ b/tests/cpu/control.rs @@ -1,6 +1,6 @@ use ferris_boi::{ cpu::{instructions::Instruction, Processor}, - register::Flag, + register::{self, Flag}, }; use test_case::test_case; @@ -18,6 +18,23 @@ fn test_can_jump_to_immediate() { assert_eq!(0x1337, processor.registers.program_counter); } +#[test] +fn test_can_jump_to_address_in_hl() { + let mut processor = Processor::default(); + processor + .registers + .set_combined_register(register::Combined::HL, 0x1337); + + let data = [0xE9, 0x06]; + let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction"); + + assert_eq!(extra_data, &[0x06]); + + processor.run_instruction(ins); + + assert_eq!(0x1337, processor.registers.program_counter); +} + #[test] fn test_call_adjusts_program_counter_to_given_value() { let mut processor = Processor::default(); diff --git a/tests/cpu/jsmoo/testdata/disabled/e9.json b/tests/cpu/jsmoo/testdata/e9.json similarity index 100% rename from tests/cpu/jsmoo/testdata/disabled/e9.json rename to tests/cpu/jsmoo/testdata/e9.json