From 6ae1d4a4b8c9bfaa1078b2f09c4bd97bf62e0cce Mon Sep 17 00:00:00 2001 From: Alex Janka Date: Thu, 2 Feb 2023 19:01:11 +1100 Subject: [PATCH] interrupt handling --- src/processor/mod.rs | 87 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 82 insertions(+), 5 deletions(-) diff --git a/src/processor/mod.rs b/src/processor/mod.rs index cf1188f..8c8885f 100644 --- a/src/processor/mod.rs +++ b/src/processor/mod.rs @@ -1,6 +1,6 @@ use std::{mem::transmute, time::Duration}; -use crate::{verbose_println, Memory}; +use crate::{processor::instructions::instructions::set, verbose_println, Memory}; mod instructions; mod opcodes; @@ -25,9 +25,10 @@ pub struct CPU { pub last_instruction_addr: u16, } -// MHz +// Hz const CLOCK_SPEED: f64 = 4.194304 * 1000000.; const SPEEDUP: f64 = 1.; +const FF04_SPEED: f32 = 16384.; impl CPU { pub fn exec_next(&mut self) { @@ -48,9 +49,35 @@ impl CPU { self.last_instruction_addr ); let cycles = self.run_opcode(opcode); - spin_sleep::sleep(Duration::from_secs_f64( - (cycles * 4) as f64 / (CLOCK_SPEED * SPEEDUP), - )); + self.increment_timers(cycles); + + let interrupt_cycles = self.handle_interrupts(); + self.increment_timers(interrupt_cycles); + } + + fn increment_timers(&mut self, cycles: u8) { + let secs = (cycles * 4) as f64 / (CLOCK_SPEED * SPEEDUP); + + self.memory.set( + 0xFF04, + self.memory + .get(0xFF04) + .wrapping_add((FF04_SPEED * 16384.) as u8), + ); + let (timer_enabled, timer_rate) = self.timer_scale(); + if timer_enabled { + let (val, wrap) = self + .memory + .get(0xFF05) + .overflowing_add((secs * timer_rate as f64) as u8); + if wrap { + self.memory.set(0xFF05, self.memory.get(0xFF06)); + self.memory.set(0xFF0F, set(self.memory.get(0xFF0F), 2)); + } else { + self.memory.set(0xFF05, val); + } + } + spin_sleep::sleep(Duration::from_secs_f64(secs)); } fn next_opcode(&mut self) -> u8 { @@ -58,6 +85,56 @@ impl CPU { self.reg.pc = self.reg.pc.wrapping_add(0x1); return opcode; } + + fn handle_interrupts(&mut self) -> u8 { + if self.memory.ime { + let req_and_enabled = self.memory.get(0xFF0F) & self.memory.get(0xFFFF); + // all interrupts should last 5 cycles? + if get_bit(req_and_enabled, 0) { + // vblank + self.service_interrupt(0x40); + 5 + } else if get_bit(req_and_enabled, 1) { + // lcd stat + self.service_interrupt(0x48); + 5 + } else if get_bit(req_and_enabled, 2) { + // timer + self.service_interrupt(0x50); + 5 + } else if get_bit(req_and_enabled, 3) { + // serial + self.service_interrupt(0x58); + 5 + } else if get_bit(req_and_enabled, 4) { + // joypad + self.service_interrupt(0x60); + 5 + } else { + 0 + } + } else { + 0 + } + } + + fn service_interrupt(&mut self, addr: u16) { + self.push(self.reg.pc); + self.reg.pc = addr; + self.memory.ime = false; + } + + fn timer_scale(&self) -> (bool, usize) { + let timer_control = self.memory.get(0xFF07); + let timer_enable = get_bit(timer_control, 2); + let timer_rate = match (get_bit(timer_control, 1), get_bit(timer_control, 0)) { + (true, true) => 256, + (true, false) => 64, + (false, true) => 16, + (false, false) => 1024, + }; + (timer_enable, timer_rate) + } } #[derive(Clone, Copy)]