feat(cpu): properly implement EI instruction

This commit is contained in:
Rekai Nyangadzayi Musuka 2021-04-05 00:52:12 -05:00
parent 4ace092257
commit a15a6a25b6
2 changed files with 57 additions and 15 deletions

View File

@ -4,13 +4,14 @@ use super::interrupt::{InterruptEnable, InterruptFlag};
use super::ppu::Ppu;
use bitfield::bitfield;
use std::fmt::{Display, Formatter, Result as FmtResult};
use std::ops::Add;
#[derive(Debug, Clone, Default)]
pub struct Cpu {
pub bus: Bus,
reg: Registers,
flags: Flags,
ime: bool,
ime: ImeState,
halted: Option<HaltState>,
state: State,
}
@ -41,12 +42,12 @@ impl Cpu {
})
}
pub fn ime(&self) -> bool {
pub fn ime(&self) -> ImeState {
self.ime
}
pub fn set_ime(&mut self, enabled: bool) {
self.ime = enabled;
pub fn set_ime(&mut self, state: ImeState) {
self.ime = state;
}
pub fn halt(&mut self, state: HaltState) {
@ -86,9 +87,9 @@ impl Cpu {
}
pub fn step(&mut self) -> Cycle {
if !self.bus.boot_enabled() {
self.log_state().unwrap();
}
// if !self.bus.boot_enabled() {
// self.log_state().unwrap();
// }
let cycles = match self.halted() {
Some(state) => {
@ -106,7 +107,9 @@ impl Cpu {
let instr = self.decode(opcode);
let cycles = self.execute(instr);
self.check_ime();
self.bus.step(cycles);
cycles
}
};
@ -151,7 +154,17 @@ impl Cpu {
&mut self.bus.ppu
}
pub fn handle_interrupts(&mut self) {
fn check_ime(&mut self) {
if let ImeState::EnablePending(count) = self.ime {
self.ime = if count < 2 {
self.ime.wait()
} else {
ImeState::Enabled
}
};
}
fn handle_interrupts(&mut self) {
let req = self.read_byte(0xFF0F);
let enabled = self.read_byte(0xFFFF);
@ -168,7 +181,14 @@ impl Cpu {
}
}
if self.ime() {
if let ImeState::Enabled = self.ime() {
println!(
"req & enabled: {:#010b} | Halted: {:?} | PC: {:#06X}",
req & enabled,
self.halted,
self.reg.pc,
);
let mut req: InterruptFlag = req.into();
let enabled: InterruptEnable = enabled.into();
@ -212,7 +232,7 @@ impl Cpu {
self.write_byte(0xFF0F, req.into());
// Disable all future interrupts
self.set_ime(false);
self.set_ime(ImeState::Disabled);
self.execute(Instruction::RST(register))
}
None => Cycle::new(0), // NO Interrupts were enabled and / or requested
@ -454,3 +474,25 @@ pub enum HaltState {
NonePending,
SomePending,
}
#[derive(Debug, Clone, Copy)]
pub enum ImeState {
Disabled,
Enabled,
EnablePending(u8),
}
impl Default for ImeState {
fn default() -> Self {
Self::Disabled
}
}
impl ImeState {
pub fn wait(self) -> Self {
match self {
Self::EnablePending(count) => Self::EnablePending(count + 1),
_ => panic!("IME is {:?}, however wait() was called", self),
}
}
}

View File

@ -1,4 +1,4 @@
use super::cpu::{Cpu, Flags, HaltState, Register, RegisterPair};
use super::cpu::{Cpu, Flags, HaltState, ImeState, Register, RegisterPair};
use std::{convert::TryFrom, fmt::Debug};
#[derive(Debug, Copy, Clone)]
@ -611,7 +611,7 @@ impl Instruction {
let req = cpu.read_byte(0xFF0F);
let enabled = cpu.read_byte(0xFFFF);
let halt_state = if cpu.ime() {
let halt_state = if let ImeState::Enabled = cpu.ime() {
ImeSet
} else if req & enabled != 0 {
SomePending
@ -957,7 +957,7 @@ impl Instruction {
// Same as RET, after which interrupts are enabled.
let addr = Self::pop(cpu);
cpu.set_register_pair(RegisterPair::PC, addr);
cpu.set_ime(true);
cpu.set_ime(ImeState::Enabled);
Cycle::new(16)
}
Instruction::JP(cond, target) => match target {
@ -1016,13 +1016,13 @@ impl Instruction {
},
Instruction::DI => {
// Disable IME
cpu.set_ime(false);
cpu.set_ime(ImeState::Disabled);
Cycle::new(4)
}
Instruction::EI => {
// Enable IME (After the next instruction)
// FIXME: IME is set after the next instruction, this currently is not represented in this emulator.
cpu.set_ime(true);
cpu.set_ime(ImeState::EnablePending(0));
Cycle::new(4)
}
Instruction::CALL(cond, nn) => {