Add JP HL instruction
parent
49f2e53e4e
commit
cd05183bb3
|
@ -6,6 +6,7 @@ pub enum ControlFlowInstruction {
|
||||||
JumpToImmediate {
|
JumpToImmediate {
|
||||||
addr: u16,
|
addr: u16,
|
||||||
},
|
},
|
||||||
|
JumpToAddressInHL,
|
||||||
Call {
|
Call {
|
||||||
addr: u16,
|
addr: u16,
|
||||||
},
|
},
|
||||||
|
|
|
@ -24,11 +24,11 @@ impl OpcodeParser for Parser {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_unconditional_control_operation(data: &View) -> ParseResult {
|
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)
|
.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);
|
let opcode = parse::get_opcode_from_data(data);
|
||||||
match opcode {
|
match opcode {
|
||||||
0xC3 => Ok(build_immediate_parameterized_control_flow_data(
|
0xC3 => Ok(build_immediate_parameterized_control_flow_data(
|
||||||
|
@ -39,6 +39,10 @@ fn parse_unconditional_immediate_based_control_flow_operation(data: &View) -> Pa
|
||||||
data,
|
data,
|
||||||
|addr| ControlFlowInstruction::Call { addr },
|
|addr| ControlFlowInstruction::Call { addr },
|
||||||
)),
|
)),
|
||||||
|
0xE9 => Ok((
|
||||||
|
Instruction::ControlFlow(ControlFlowInstruction::JumpToAddressInHL),
|
||||||
|
1,
|
||||||
|
)),
|
||||||
_ => Err(parse::Error::UnknownOpcode(opcode)),
|
_ => Err(parse::Error::UnknownOpcode(opcode)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,19 +6,24 @@ impl Run for ControlFlowInstruction {
|
||||||
fn run_on(&self, processor: &mut Processor) -> Result<Cycles, super::Error> {
|
fn run_on(&self, processor: &mut Processor) -> Result<Cycles, super::Error> {
|
||||||
match *self {
|
match *self {
|
||||||
Self::JumpToImmediate { addr } => {
|
Self::JumpToImmediate { addr } => {
|
||||||
processor
|
jump_to(processor, addr);
|
||||||
.registers
|
|
||||||
.set_single_16bit_register(register::SingleSixteenBit::ProgramCounter, addr);
|
|
||||||
|
|
||||||
Ok(Cycles(16))
|
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 } => {
|
Self::JumpToImmediateIfFlagMatches { flag, value, addr } => {
|
||||||
if processor.registers.get_flag_bit(flag) == value {
|
if processor.registers.get_flag_bit(flag) == value {
|
||||||
processor.registers.set_single_16bit_register(
|
jump_to(processor, addr);
|
||||||
register::SingleSixteenBit::ProgramCounter,
|
|
||||||
addr,
|
|
||||||
);
|
|
||||||
Ok(Cycles(16))
|
Ok(Cycles(16))
|
||||||
} else {
|
} else {
|
||||||
Ok(Cycles(12))
|
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.
|
// 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.push_16bit_value_to_stack(current_pc.wrapping_add(3))?;
|
||||||
|
|
||||||
processor
|
jump_to(processor, to_addr);
|
||||||
.registers
|
|
||||||
.set_single_16bit_register(register::SingleSixteenBit::ProgramCounter, to_addr);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn jump_to(processor: &mut Processor, to_addr: u16) {
|
||||||
|
processor
|
||||||
|
.registers
|
||||||
|
.set_single_16bit_register(register::SingleSixteenBit::ProgramCounter, to_addr);
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use ferris_boi::{
|
use ferris_boi::{
|
||||||
cpu::{instructions::Instruction, Processor},
|
cpu::{instructions::Instruction, Processor},
|
||||||
register::Flag,
|
register::{self, Flag},
|
||||||
};
|
};
|
||||||
use test_case::test_case;
|
use test_case::test_case;
|
||||||
|
|
||||||
|
@ -18,6 +18,23 @@ fn test_can_jump_to_immediate() {
|
||||||
assert_eq!(0x1337, processor.registers.program_counter);
|
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]
|
#[test]
|
||||||
fn test_call_adjusts_program_counter_to_given_value() {
|
fn test_call_adjusts_program_counter_to_given_value() {
|
||||||
let mut processor = Processor::default();
|
let mut processor = Processor::default();
|
||||||
|
|
Loading…
Reference in New Issue