Add support for enabling/disabling interrupts
parent
2db1e58568
commit
66c7e4287c
25
src/cpu.rs
25
src/cpu.rs
|
@ -15,6 +15,7 @@ pub struct Processor {
|
|||
pub registers: Registers,
|
||||
pub memory: Memory,
|
||||
pub num_cycles: u128,
|
||||
interrupt_enable_pending: bool,
|
||||
}
|
||||
|
||||
impl Processor {
|
||||
|
@ -32,9 +33,17 @@ impl Processor {
|
|||
let (instruction, bytes_read) =
|
||||
parse::next_instruction(&memory_view).expect("invalid instruction");
|
||||
|
||||
let interrupt_enable_pending_already = self.interrupt_enable_pending;
|
||||
self.run_instruction(instruction);
|
||||
|
||||
let (next_pc, _carry) = pc.overflowing_add(bytes_read);
|
||||
// EI does not work until after the following instruction, so we check after running an instruction if
|
||||
// this is true
|
||||
if interrupt_enable_pending_already && self.interrupt_enable_pending {
|
||||
self.interrupt_enable_pending = false;
|
||||
self.registers.enable_interrupts();
|
||||
}
|
||||
|
||||
let next_pc = pc.wrapping_add(bytes_read);
|
||||
self.registers.program_counter = next_pc;
|
||||
}
|
||||
|
||||
|
@ -51,6 +60,20 @@ impl Processor {
|
|||
self.num_cycles += u128::from(run_res.unwrap().0);
|
||||
}
|
||||
|
||||
pub fn enable_interrupts(&mut self) {
|
||||
self.interrupt_enable_pending = true;
|
||||
}
|
||||
|
||||
pub fn disable_interrupts(&mut self) {
|
||||
self.interrupt_enable_pending = false;
|
||||
self.registers.disable_interrupts();
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn interrupts_enabled(&mut self) -> bool {
|
||||
self.registers.interrupts_enabled()
|
||||
}
|
||||
|
||||
fn load_from_register_to_register_address(
|
||||
&mut self,
|
||||
register_with_dst_address: register::Combined,
|
||||
|
|
|
@ -8,4 +8,6 @@ pub enum MiscInstruction {
|
|||
ComplementARegister,
|
||||
DecimalAdjustAccumulator,
|
||||
Nop,
|
||||
EnableInterrupts,
|
||||
DisableInterrupts,
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@ impl OpcodeParser for Parser {
|
|||
0x27 => Ok(build_daa_data()),
|
||||
0x2F => Ok(build_complement_a_register_data()),
|
||||
0x00 => Ok(build_nop_data()),
|
||||
0xFB => Ok(build_enable_interrupts_data()),
|
||||
0xF3 => Ok(build_disable_interrupts_data()),
|
||||
_ => Err(super::Error::UnknownOpcode(opcode)),
|
||||
}
|
||||
}
|
||||
|
@ -40,8 +42,13 @@ fn build_daa_data() -> ParseOutput {
|
|||
}
|
||||
|
||||
fn build_nop_data() -> ParseOutput {
|
||||
(
|
||||
Instruction::Misc(MiscInstruction::Nop),
|
||||
1,
|
||||
)
|
||||
(Instruction::Misc(MiscInstruction::Nop), 1)
|
||||
}
|
||||
|
||||
fn build_enable_interrupts_data() -> ParseOutput {
|
||||
(Instruction::Misc(MiscInstruction::EnableInterrupts), 1)
|
||||
}
|
||||
|
||||
fn build_disable_interrupts_data() -> ParseOutput {
|
||||
(Instruction::Misc(MiscInstruction::DisableInterrupts), 1)
|
||||
}
|
||||
|
|
|
@ -8,9 +8,7 @@ use super::{Cycles, Run};
|
|||
impl Run for MiscInstruction {
|
||||
fn run_on(&self, processor: &mut Processor) -> Result<Cycles, Error> {
|
||||
match *self {
|
||||
MiscInstruction::Nop => {
|
||||
Ok(Cycles(4))
|
||||
}
|
||||
MiscInstruction::Nop => Ok(Cycles(4)),
|
||||
|
||||
MiscInstruction::SetCarryFlag => {
|
||||
set_flags_in_carry_bit_instruction(processor, 1);
|
||||
|
@ -66,6 +64,18 @@ impl Run for MiscInstruction {
|
|||
|
||||
Ok(Cycles(4))
|
||||
}
|
||||
|
||||
MiscInstruction::EnableInterrupts => {
|
||||
processor.enable_interrupts();
|
||||
|
||||
Ok(Cycles(4))
|
||||
},
|
||||
|
||||
MiscInstruction::DisableInterrupts => {
|
||||
processor.disable_interrupts();
|
||||
|
||||
Ok(Cycles(4))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,6 +66,7 @@ pub struct Registers {
|
|||
pub program_counter: u16,
|
||||
// the "F" register and the "flags" register are the same register
|
||||
flags: u8,
|
||||
interrupts_enabled: bool,
|
||||
}
|
||||
|
||||
impl Flag {
|
||||
|
@ -99,6 +100,7 @@ impl Default for Registers {
|
|||
e: 0,
|
||||
h: 0,
|
||||
l: 0,
|
||||
interrupts_enabled: false,
|
||||
stack_pointer: 0,
|
||||
program_counter: INITIAL_PROGRAM_COUNTER_VALUE,
|
||||
flags: 0,
|
||||
|
@ -264,6 +266,19 @@ impl Registers {
|
|||
Combined::HL => (&mut self.h, &mut self.l),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn enable_interrupts(&mut self) {
|
||||
self.interrupts_enabled = true;
|
||||
}
|
||||
|
||||
pub(super) fn disable_interrupts(&mut self) {
|
||||
self.interrupts_enabled = false;
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn interrupts_enabled(&self) -> bool {
|
||||
self.interrupts_enabled
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -171,3 +171,69 @@ fn test_nop_executes_successfully() {
|
|||
// uhhh it does nothing
|
||||
assert_eq!(processor.num_cycles, 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_enable_interrupts_enables_interrupts_on_the_following_instruction() {
|
||||
let mut processor = Processor::default();
|
||||
|
||||
[
|
||||
// Enable interrupts
|
||||
0xFB, // Nop
|
||||
0x00,
|
||||
]
|
||||
.iter()
|
||||
.copied()
|
||||
.enumerate()
|
||||
.for_each(|(idx, opcode)| {
|
||||
processor
|
||||
.memory
|
||||
.set(
|
||||
usize::from(processor.registers.program_counter) + idx,
|
||||
opcode,
|
||||
)
|
||||
.expect("could not program data");
|
||||
});
|
||||
|
||||
assert!(!processor.interrupts_enabled());
|
||||
|
||||
processor.run_next_instruction();
|
||||
assert!(!processor.interrupts_enabled());
|
||||
|
||||
processor.run_next_instruction();
|
||||
assert!(processor.interrupts_enabled());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_disable_interrupts_disables_interrupts_once_enabled() {
|
||||
let mut processor = Processor::default();
|
||||
|
||||
[
|
||||
// Enable interrupts
|
||||
0xFB, // Nop
|
||||
0x00,
|
||||
0xF3,
|
||||
]
|
||||
.iter()
|
||||
.copied()
|
||||
.enumerate()
|
||||
.for_each(|(idx, opcode)| {
|
||||
processor
|
||||
.memory
|
||||
.set(
|
||||
usize::from(processor.registers.program_counter) + idx,
|
||||
opcode,
|
||||
)
|
||||
.expect("could not program data");
|
||||
});
|
||||
|
||||
assert!(!processor.interrupts_enabled());
|
||||
|
||||
processor.run_next_instruction();
|
||||
assert!(!processor.interrupts_enabled());
|
||||
|
||||
processor.run_next_instruction();
|
||||
assert!(processor.interrupts_enabled());
|
||||
|
||||
processor.run_next_instruction();
|
||||
assert!(!processor.interrupts_enabled());
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue