Implement RETI
parent
cd05183bb3
commit
a3f6025a6f
|
@ -65,10 +65,15 @@ impl Processor {
|
||||||
self.num_cycles += u128::from(run_res.unwrap().0);
|
self.num_cycles += u128::from(run_res.unwrap().0);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn enable_interrupts(&mut self) {
|
pub fn stage_interrupt_enable(&mut self) {
|
||||||
self.interrupt_enable_pending = true;
|
self.interrupt_enable_pending = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn enable_interrupts_now(&mut self) {
|
||||||
|
self.interrupt_enable_pending = false;
|
||||||
|
self.registers.enable_interrupts();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn disable_interrupts(&mut self) {
|
pub fn disable_interrupts(&mut self) {
|
||||||
self.interrupt_enable_pending = false;
|
self.interrupt_enable_pending = false;
|
||||||
self.registers.disable_interrupts();
|
self.registers.disable_interrupts();
|
||||||
|
|
|
@ -23,4 +23,6 @@ pub enum ControlFlowInstruction {
|
||||||
value: u8,
|
value: u8,
|
||||||
addr: u16,
|
addr: u16,
|
||||||
},
|
},
|
||||||
|
Return,
|
||||||
|
ReturnAndEnableInterrupts,
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,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(parse_unconditional_return)
|
||||||
.or_parse(parse_restart_instruction)
|
.or_parse(parse_restart_instruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,6 +59,17 @@ fn parse_restart_instruction(opcode: u8) -> ParseResult {
|
||||||
Ok((Instruction::ControlFlow(control_flow_instruction), 1))
|
Ok((Instruction::ControlFlow(control_flow_instruction), 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_unconditional_return(opcode: u8) -> ParseResult {
|
||||||
|
match opcode {
|
||||||
|
0xC9 => Ok((Instruction::ControlFlow(ControlFlowInstruction::Return), 1)),
|
||||||
|
0xD9 => Ok((
|
||||||
|
Instruction::ControlFlow(ControlFlowInstruction::ReturnAndEnableInterrupts),
|
||||||
|
1,
|
||||||
|
)),
|
||||||
|
_ => Err(parse::Error::UnknownOpcode(opcode)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,19 @@ impl Run for ControlFlowInstruction {
|
||||||
|
|
||||||
Ok(Cycles(16))
|
Ok(Cycles(16))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Self::Return => {
|
||||||
|
return_to_popped_addr(processor)?;
|
||||||
|
|
||||||
|
Ok(Cycles(16))
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::ReturnAndEnableInterrupts => {
|
||||||
|
processor.enable_interrupts_now();
|
||||||
|
return_to_popped_addr(processor)?;
|
||||||
|
|
||||||
|
Ok(Cycles(16))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,3 +93,7 @@ fn jump_to(processor: &mut Processor, to_addr: u16) {
|
||||||
.registers
|
.registers
|
||||||
.set_single_16bit_register(register::SingleSixteenBit::ProgramCounter, to_addr);
|
.set_single_16bit_register(register::SingleSixteenBit::ProgramCounter, to_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn return_to_popped_addr(processor: &mut Processor) -> Result<(), super::Error> {
|
||||||
|
processor.pop_from_stack_to_16bit_register(register::SingleSixteenBit::ProgramCounter.into())
|
||||||
|
}
|
||||||
|
|
|
@ -66,7 +66,7 @@ impl Run for MiscInstruction {
|
||||||
}
|
}
|
||||||
|
|
||||||
MiscInstruction::EnableInterrupts => {
|
MiscInstruction::EnableInterrupts => {
|
||||||
processor.enable_interrupts();
|
processor.stage_interrupt_enable();
|
||||||
|
|
||||||
Ok(Cycles(4))
|
Ok(Cycles(4))
|
||||||
}
|
}
|
||||||
|
|
|
@ -184,3 +184,82 @@ fn test_call_and_jump_does_nothing_if_condition_fails(opcode: u8, flag: Flag, ex
|
||||||
// run_instruction does not advance the program counter, so this will be the same. Normally it would be old_pc plus 3
|
// run_instruction does not advance the program counter, so this will be the same. Normally it would be old_pc plus 3
|
||||||
assert_eq!(old_pc, processor.registers.program_counter);
|
assert_eq!(old_pc, processor.registers.program_counter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test_case(0xC9; "RET")]
|
||||||
|
#[test_case(0xD9; "RETI")]
|
||||||
|
fn test_ret_moves_pc_to_address_on_stack(opcode: u8) {
|
||||||
|
let mut processor = Processor::default();
|
||||||
|
|
||||||
|
let starting_sp = processor.registers.stack_pointer;
|
||||||
|
processor
|
||||||
|
.memory
|
||||||
|
.set_both((
|
||||||
|
((starting_sp - 1).into(), 0x13),
|
||||||
|
((starting_sp - 2).into(), 0x37),
|
||||||
|
))
|
||||||
|
.expect("failed to write to stack");
|
||||||
|
|
||||||
|
processor.registers.stack_pointer -= 2;
|
||||||
|
|
||||||
|
let data = [opcode, 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_case(0xC9; "RET")]
|
||||||
|
#[test_case(0xD9; "RETI")]
|
||||||
|
fn test_ret_moves_sp_two_bytes(opcode: u8) {
|
||||||
|
let mut processor = Processor::default();
|
||||||
|
|
||||||
|
let starting_sp = processor.registers.stack_pointer;
|
||||||
|
processor
|
||||||
|
.memory
|
||||||
|
.set_both((
|
||||||
|
((starting_sp - 1).into(), 0x13),
|
||||||
|
((starting_sp - 2).into(), 0x37),
|
||||||
|
))
|
||||||
|
.expect("failed to write to stack");
|
||||||
|
|
||||||
|
processor.registers.stack_pointer -= 2;
|
||||||
|
|
||||||
|
let data = [opcode, 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!(starting_sp, processor.registers.stack_pointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_reti_enables_interrupts_immediately() {
|
||||||
|
let mut processor = Processor::default();
|
||||||
|
|
||||||
|
let starting_sp = processor.registers.stack_pointer;
|
||||||
|
processor
|
||||||
|
.memory
|
||||||
|
.set_both((
|
||||||
|
((starting_sp - 1).into(), 0x13),
|
||||||
|
((starting_sp - 2).into(), 0x37),
|
||||||
|
))
|
||||||
|
.expect("failed to write to stack");
|
||||||
|
|
||||||
|
processor.disable_interrupts();
|
||||||
|
|
||||||
|
processor.registers.stack_pointer -= 2;
|
||||||
|
|
||||||
|
let data = [0xD9, 0x06];
|
||||||
|
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
|
||||||
|
|
||||||
|
assert_eq!(extra_data, &[0x06]);
|
||||||
|
|
||||||
|
processor.run_instruction(ins);
|
||||||
|
|
||||||
|
assert!(processor.interrupts_enabled());
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue