Implement JR instruction
parent
bc17febf78
commit
709b0dd82e
|
@ -6,25 +6,37 @@ pub enum ControlFlowInstruction {
|
||||||
JumpToImmediate {
|
JumpToImmediate {
|
||||||
addr: u16,
|
addr: u16,
|
||||||
},
|
},
|
||||||
|
|
||||||
JumpToAddressInHL,
|
JumpToAddressInHL,
|
||||||
|
|
||||||
Call {
|
Call {
|
||||||
addr: u16,
|
addr: u16,
|
||||||
},
|
},
|
||||||
|
|
||||||
RestartAtAddress {
|
RestartAtAddress {
|
||||||
addr: u16,
|
addr: u16,
|
||||||
},
|
},
|
||||||
|
|
||||||
CallIfFlagMatches {
|
CallIfFlagMatches {
|
||||||
flag: register::Flag,
|
flag: register::Flag,
|
||||||
value: u8,
|
value: u8,
|
||||||
addr: u16,
|
addr: u16,
|
||||||
},
|
},
|
||||||
|
|
||||||
JumpToImmediateIfFlagMatches {
|
JumpToImmediateIfFlagMatches {
|
||||||
flag: register::Flag,
|
flag: register::Flag,
|
||||||
value: u8,
|
value: u8,
|
||||||
addr: u16,
|
addr: u16,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
JumpRelative {
|
||||||
|
offset: i8,
|
||||||
|
},
|
||||||
|
|
||||||
Return,
|
Return,
|
||||||
|
|
||||||
ReturnAndEnableInterrupts,
|
ReturnAndEnableInterrupts,
|
||||||
|
|
||||||
ReturnIfFlagMatches {
|
ReturnIfFlagMatches {
|
||||||
flag: register::Flag,
|
flag: register::Flag,
|
||||||
value: u8,
|
value: u8,
|
||||||
|
|
|
@ -26,6 +26,7 @@ impl OpcodeParser for Parser {
|
||||||
|
|
||||||
fn parse_unconditional_control_operation(data: &View) -> ParseResult {
|
fn parse_unconditional_control_operation(data: &View) -> ParseResult {
|
||||||
parse_unconditional_parameterized_control_flow_operation(data)
|
parse_unconditional_parameterized_control_flow_operation(data)
|
||||||
|
.or_parse(|_opcode| parse_unconditional_relative_jump(data))
|
||||||
.or_parse(parse_unconditional_return)
|
.or_parse(parse_unconditional_return)
|
||||||
.or_parse(parse_restart_instruction)
|
.or_parse(parse_restart_instruction)
|
||||||
}
|
}
|
||||||
|
@ -71,6 +72,21 @@ fn parse_unconditional_return(opcode: u8) -> ParseResult {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_unconditional_relative_jump(data: &View) -> ParseResult {
|
||||||
|
let opcode = parse::get_opcode_from_data(data);
|
||||||
|
if opcode != 0x18 {
|
||||||
|
return Err(parse::Error::UnknownOpcode(opcode));
|
||||||
|
}
|
||||||
|
|
||||||
|
let (_opcode, twos_comp_offset) = data.get_tuple();
|
||||||
|
let offset = i8::from_be_bytes([twos_comp_offset]);
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
Instruction::ControlFlow(ControlFlowInstruction::JumpRelative { offset }),
|
||||||
|
2,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_conditional_control_operation(data: &View) -> ParseResult {
|
fn parse_conditional_control_operation(data: &View) -> ParseResult {
|
||||||
let opcode = parse::get_opcode_from_data(data);
|
let opcode = parse::get_opcode_from_data(data);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::cpu::{instructions::control::ControlFlowInstruction, register, Processor};
|
use crate::cpu::{instructions::control::ControlFlowInstruction, register, Processor};
|
||||||
|
|
||||||
use super::{Cycles, Run};
|
use super::{arithutil::CarryingAdd, Cycles, Run};
|
||||||
|
|
||||||
impl Run for ControlFlowInstruction {
|
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> {
|
||||||
|
@ -79,6 +79,20 @@ impl Run for ControlFlowInstruction {
|
||||||
Ok(Cycles(8))
|
Ok(Cycles(8))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Self::JumpRelative { offset } => {
|
||||||
|
let current_pc = processor
|
||||||
|
.registers
|
||||||
|
.get_single_16bit_register(register::SingleSixteenBit::ProgramCounter);
|
||||||
|
|
||||||
|
// We don't need the flags, but this trait does the propper adding for us
|
||||||
|
// TODO: Break this out into its own arithutil function?
|
||||||
|
let (updated_pc, _half_carry, _carry) = current_pc.add_with_carry(offset);
|
||||||
|
|
||||||
|
jump_to(processor, updated_pc);
|
||||||
|
|
||||||
|
Ok(Cycles(12))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -210,6 +210,28 @@ fn test_jump_to_same_addr_does_not_advance_pc() {
|
||||||
assert_eq!(0x1337, processor.registers.program_counter);
|
assert_eq!(0x1337, processor.registers.program_counter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test_case(10)]
|
||||||
|
#[test_case(0)]
|
||||||
|
#[test_case(-8)]
|
||||||
|
fn test_jump_relative_jumps_by_the_given_number_of_bytes(distance: i8) {
|
||||||
|
let mut processor = Processor::default();
|
||||||
|
|
||||||
|
let starting_pc = processor.registers.program_counter;
|
||||||
|
let data = [0x18, i8::to_be_bytes(distance)[0], 0x06];
|
||||||
|
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
|
||||||
|
|
||||||
|
assert_eq!(extra_data, &[0x06]);
|
||||||
|
|
||||||
|
processor.run_instruction(ins);
|
||||||
|
let expected_pc = if distance > 0 {
|
||||||
|
starting_pc + u16::from(distance.unsigned_abs())
|
||||||
|
} else {
|
||||||
|
starting_pc - u16::from(distance.unsigned_abs())
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(expected_pc, processor.registers.program_counter);
|
||||||
|
}
|
||||||
|
|
||||||
#[test_case(0xC9; "RET")]
|
#[test_case(0xC9; "RET")]
|
||||||
#[test_case(0xD9; "RETI")]
|
#[test_case(0xD9; "RETI")]
|
||||||
fn test_ret_jumps_to_address_on_stack(opcode: u8) {
|
fn test_ret_jumps_to_address_on_stack(opcode: u8) {
|
||||||
|
@ -351,6 +373,7 @@ fn test_conditional_ret_does_nothing_if_condition_fails(
|
||||||
|
|
||||||
processor.run_instruction(ins);
|
processor.run_instruction(ins);
|
||||||
|
|
||||||
|
// run_instruction does not advance the program_counter. Normally this would add one
|
||||||
assert_eq!(starting_pc, processor.registers.program_counter);
|
assert_eq!(starting_pc, processor.registers.program_counter);
|
||||||
assert_eq!(starting_sp - 2, processor.registers.stack_pointer);
|
assert_eq!(starting_sp - 2, processor.registers.stack_pointer);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue