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 super::ppu::Ppu;
use bitfield::bitfield; use bitfield::bitfield;
use std::fmt::{Display, Formatter, Result as FmtResult}; use std::fmt::{Display, Formatter, Result as FmtResult};
use std::ops::Add;
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct Cpu { pub struct Cpu {
pub bus: Bus, pub bus: Bus,
reg: Registers, reg: Registers,
flags: Flags, flags: Flags,
ime: bool, ime: ImeState,
halted: Option<HaltState>, halted: Option<HaltState>,
state: State, state: State,
} }
@ -41,12 +42,12 @@ impl Cpu {
}) })
} }
pub fn ime(&self) -> bool { pub fn ime(&self) -> ImeState {
self.ime self.ime
} }
pub fn set_ime(&mut self, enabled: bool) { pub fn set_ime(&mut self, state: ImeState) {
self.ime = enabled; self.ime = state;
} }
pub fn halt(&mut self, state: HaltState) { pub fn halt(&mut self, state: HaltState) {
@ -86,9 +87,9 @@ impl Cpu {
} }
pub fn step(&mut self) -> Cycle { pub fn step(&mut self) -> Cycle {
if !self.bus.boot_enabled() { // if !self.bus.boot_enabled() {
self.log_state().unwrap(); // self.log_state().unwrap();
} // }
let cycles = match self.halted() { let cycles = match self.halted() {
Some(state) => { Some(state) => {
@ -106,7 +107,9 @@ impl Cpu {
let instr = self.decode(opcode); let instr = self.decode(opcode);
let cycles = self.execute(instr); let cycles = self.execute(instr);
self.check_ime();
self.bus.step(cycles); self.bus.step(cycles);
cycles cycles
} }
}; };
@ -151,7 +154,17 @@ impl Cpu {
&mut self.bus.ppu &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 req = self.read_byte(0xFF0F);
let enabled = self.read_byte(0xFFFF); 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 mut req: InterruptFlag = req.into();
let enabled: InterruptEnable = enabled.into(); let enabled: InterruptEnable = enabled.into();
@ -212,7 +232,7 @@ impl Cpu {
self.write_byte(0xFF0F, req.into()); self.write_byte(0xFF0F, req.into());
// Disable all future interrupts // Disable all future interrupts
self.set_ime(false); self.set_ime(ImeState::Disabled);
self.execute(Instruction::RST(register)) self.execute(Instruction::RST(register))
} }
None => Cycle::new(0), // NO Interrupts were enabled and / or requested None => Cycle::new(0), // NO Interrupts were enabled and / or requested
@ -454,3 +474,25 @@ pub enum HaltState {
NonePending, NonePending,
SomePending, 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}; use std::{convert::TryFrom, fmt::Debug};
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
@ -611,7 +611,7 @@ impl Instruction {
let req = cpu.read_byte(0xFF0F); let req = cpu.read_byte(0xFF0F);
let enabled = cpu.read_byte(0xFFFF); let enabled = cpu.read_byte(0xFFFF);
let halt_state = if cpu.ime() { let halt_state = if let ImeState::Enabled = cpu.ime() {
ImeSet ImeSet
} else if req & enabled != 0 { } else if req & enabled != 0 {
SomePending SomePending
@ -957,7 +957,7 @@ impl Instruction {
// Same as RET, after which interrupts are enabled. // Same as RET, after which interrupts are enabled.
let addr = Self::pop(cpu); let addr = Self::pop(cpu);
cpu.set_register_pair(RegisterPair::PC, addr); cpu.set_register_pair(RegisterPair::PC, addr);
cpu.set_ime(true); cpu.set_ime(ImeState::Enabled);
Cycle::new(16) Cycle::new(16)
} }
Instruction::JP(cond, target) => match target { Instruction::JP(cond, target) => match target {
@ -1016,13 +1016,13 @@ impl Instruction {
}, },
Instruction::DI => { Instruction::DI => {
// Disable IME // Disable IME
cpu.set_ime(false); cpu.set_ime(ImeState::Disabled);
Cycle::new(4) Cycle::new(4)
} }
Instruction::EI => { Instruction::EI => {
// Enable IME (After the next instruction) // Enable IME (After the next instruction)
// FIXME: IME is set after the next instruction, this currently is not represented in this emulator. // 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) Cycle::new(4)
} }
Instruction::CALL(cond, nn) => { Instruction::CALL(cond, nn) => {