diff --git a/src/processor/memory.rs b/src/processor/memory.rs index 7a50f81..89e72ce 100644 --- a/src/processor/memory.rs +++ b/src/processor/memory.rs @@ -226,9 +226,9 @@ impl Cpu { self.memory.apu.div_apu_tick(); } - self.memory - .interrupts - .set_interrupt(Interrupt::Timer, timer_return.timer_interrupt); + if timer_return.timer_interrupt { + self.memory.interrupts.set_interrupt(Interrupt::Timer, true); + } self.memory.apu.tick(steps); diff --git a/src/processor/memory/interrupts.rs b/src/processor/memory/interrupts.rs index cc7e531..9931f8c 100644 --- a/src/processor/memory/interrupts.rs +++ b/src/processor/memory/interrupts.rs @@ -1,6 +1,6 @@ use crate::util::get_bit; -#[derive(Default)] +#[derive(Default, Debug)] struct InterruptRegister { vblank: bool, lcd_stat: bool, @@ -31,6 +31,7 @@ fn bool_to_shifted(input: bool, shift: u8) -> u8 { (if input { 1 } else { 0 }) << shift } +#[derive(Debug)] pub enum Interrupt { Vblank, LcdStat, @@ -57,7 +58,7 @@ impl Interrupts { } pub(super) fn get_flag_register(&self) -> u8 { - self.flag_register.as_register() + 0b11100000 | self.flag_register.as_register() } pub(super) fn set_flag_register(&mut self, data: u8) { @@ -94,4 +95,12 @@ impl Interrupts { None } } + + pub fn is_interrupt_queued(&self) -> bool { + (self.enable_register.vblank && self.flag_register.vblank) + || (self.enable_register.lcd_stat && self.flag_register.lcd_stat) + || (self.enable_register.timer && self.flag_register.timer) + || (self.enable_register.serial && self.flag_register.serial) + || (self.enable_register.joypad && self.flag_register.joypad) + } } diff --git a/src/processor/mod.rs b/src/processor/mod.rs index 7010177..2b4de46 100644 --- a/src/processor/mod.rs +++ b/src/processor/mod.rs @@ -26,6 +26,7 @@ pub struct Cpu { pub last_instruction: u8, last_instruction_addr: u16, halted: bool, + should_halt_bug: bool, gamepad_handler: Gilrs, cycle_start: Instant, } @@ -41,6 +42,7 @@ impl Cpu { last_instruction: 0x0, last_instruction_addr: 0x0, halted: false, + should_halt_bug: false, gamepad_handler, cycle_start: Instant::now(), } @@ -56,10 +58,6 @@ impl Cpu { return; } - self.last_instruction_addr = self.reg.pc; - let opcode = self.next_opcode(); - self.last_instruction = opcode; - if self.memory.ime_scheduled > 0 { self.memory.ime_scheduled = self.memory.ime_scheduled.saturating_sub(1); if self.memory.ime_scheduled == 0 { @@ -67,13 +65,20 @@ impl Cpu { } } + self.last_instruction_addr = self.reg.pc; + let opcode = self.next_opcode(); + if self.should_halt_bug { + self.reg.pc = self.reg.pc.wrapping_sub(0x1); + self.should_halt_bug = false; + } + self.last_instruction = opcode; + verbose_println!( "exec {:#4X} from pc: {:#X}", opcode, self.last_instruction_addr ); - let cycles = self.run_opcode(opcode); - self.increment_timers(cycles); + self.run_and_increment_timers(opcode); } fn next_opcode(&mut self) -> u8 { @@ -82,8 +87,22 @@ impl Cpu { opcode } + fn run_and_increment_timers(&mut self, opcode: u8) { + let cycles = self.run_opcode(opcode); + self.increment_timers(cycles); + } + + fn halt(&mut self) { + if !self.memory.ime && self.memory.interrupts.is_interrupt_queued() { + // halt bug + self.should_halt_bug = true; + } else { + self.halted = true; + } + } + fn handle_interrupts(&mut self) -> u8 { - if self.memory.ime || self.halted { + if self.memory.ime { if let Some(interrupt) = self.memory.interrupts.get_next_interrupt() { let interrupt_addr = match interrupt { Interrupt::Vblank => 0x40, @@ -98,6 +117,9 @@ impl Cpu { 0 } } else { + if self.halted && self.memory.interrupts.is_interrupt_queued() { + self.halted = false; + } 0 } } diff --git a/src/processor/opcodes.rs b/src/processor/opcodes.rs index 3bc45de..4676661 100644 --- a/src/processor/opcodes.rs +++ b/src/processor/opcodes.rs @@ -554,7 +554,7 @@ impl Cpu { 2 } 0x76 => { - self.halted = true; + self.halt(); 1 } 0x77 => {