diff --git a/src/cpu/interrupts.rs b/src/cpu/interrupts.rs index b042a17..3200187 100644 --- a/src/cpu/interrupts.rs +++ b/src/cpu/interrupts.rs @@ -16,6 +16,7 @@ const INTERRUPT_FLAG_ADDRESS: u16 = 0xFF0F; const INTERRUPT_ENABLE_ADDRESS: u16 = 0xFFFF; impl CPU { + /// Set the interrupt flag for the given interrupt pub fn set_interrupt_flag(&mut self, interrupt: InterruptTypes) { let interrupt_flag = self.memory.read_byte(INTERRUPT_FLAG_ADDRESS); self.memory.write_byte(INTERRUPT_FLAG_ADDRESS, interrupt_flag | (1 << interrupt as u8)); diff --git a/src/cpu/step.rs b/src/cpu/step.rs index 322b78b..c2a400a 100644 --- a/src/cpu/step.rs +++ b/src/cpu/step.rs @@ -495,6 +495,13 @@ impl CPU { FlagState::Unset => self.clear_zero_flag(), } + // Check whether we need to increase timer + let timer_modulo = self.get_timer_modulo(); + let remaining_cycles = self.cycles / timer_modulo; + if remaining_cycles + self.last_step_result.cycles as u64 >= timer_modulo { + self.increment_timer(); + } + // Update the last execution time self.last_execution_time = std::time::Instant::now(); self.cycles += self.last_step_result.cycles as u64; diff --git a/src/cpu/timer.rs b/src/cpu/timer.rs index e69de29..1142c84 100644 --- a/src/cpu/timer.rs +++ b/src/cpu/timer.rs @@ -0,0 +1,36 @@ +use super::{interrupts::InterruptTypes, CPU}; + +const TIMER_COUNTER_ADDRESS: u16 = 0xFF05; +const TIMER_MODULO_ADDRESS: u16 = 0xFF06; + +impl CPU { + pub fn increment_timer(&mut self) { + // Check whether FF07 is enabled [Pos 2] + if self.memory.read_byte(0xFF07) & 0b100 == 0 { + return; + } + let previous_val = self.memory.read_byte(TIMER_COUNTER_ADDRESS); + let (new_val, overflow) = previous_val.overflowing_add(1); + + if overflow { + log::debug!("Timer overflow - resetting to modulo & setting interrupt flag"); + self.memory.write_byte(TIMER_COUNTER_ADDRESS, self.memory.read_byte(TIMER_MODULO_ADDRESS)); + self.set_interrupt_flag(InterruptTypes::Timer); + } else { + self.memory.write_byte(TIMER_COUNTER_ADDRESS, new_val); + } + } + + /// Get the timer modulo based on the timer speed + pub fn get_timer_modulo(&mut self) -> u64 { + let timer_speed = self.memory.read_byte(0xFF07) & 0b11; + + match timer_speed { + 0b00 => 256, + 0b01 => 4, + 0b10 => 16, + 0b11 => 64, + _ => panic!("Invalid timer speed"), + } + } +} \ No newline at end of file diff --git a/src/memory/raw_memory_operations.rs b/src/memory/raw_memory_operations.rs index 888aa49..c9260fd 100644 --- a/src/memory/raw_memory_operations.rs +++ b/src/memory/raw_memory_operations.rs @@ -20,7 +20,10 @@ impl Memory { 0xFF50 => { log::debug!("Disabling boot rom"); self.boot_rom_enabled = false; - } + }, + // DIV register + // https://gbdev.io/pandocs/Timer_and_Divider_Registers.html#ff04--div-divider-register + 0xFF04 => self.memory[address as usize] = 0, // Prevents overwriting of the last 4 bits in FF00 which are mapped to controller input 0xFF00 => { let prev = self.read_byte(address);