interrupt handling

This commit is contained in:
Alex Janka 2023-02-02 19:01:11 +11:00
parent 04a9748aa6
commit 6ae1d4a4b8

View file

@ -1,6 +1,6 @@
use std::{mem::transmute, time::Duration}; use std::{mem::transmute, time::Duration};
use crate::{verbose_println, Memory}; use crate::{processor::instructions::instructions::set, verbose_println, Memory};
mod instructions; mod instructions;
mod opcodes; mod opcodes;
@ -25,9 +25,10 @@ pub struct CPU {
pub last_instruction_addr: u16, pub last_instruction_addr: u16,
} }
// MHz // Hz
const CLOCK_SPEED: f64 = 4.194304 * 1000000.; const CLOCK_SPEED: f64 = 4.194304 * 1000000.;
const SPEEDUP: f64 = 1.; const SPEEDUP: f64 = 1.;
const FF04_SPEED: f32 = 16384.;
impl CPU { impl CPU {
pub fn exec_next(&mut self) { pub fn exec_next(&mut self) {
@ -48,9 +49,35 @@ impl CPU {
self.last_instruction_addr self.last_instruction_addr
); );
let cycles = self.run_opcode(opcode); let cycles = self.run_opcode(opcode);
spin_sleep::sleep(Duration::from_secs_f64( self.increment_timers(cycles);
(cycles * 4) as f64 / (CLOCK_SPEED * SPEEDUP),
)); 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 { fn next_opcode(&mut self) -> u8 {
@ -58,6 +85,56 @@ impl CPU {
self.reg.pc = self.reg.pc.wrapping_add(0x1); self.reg.pc = self.reg.pc.wrapping_add(0x1);
return opcode; 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)] #[derive(Clone, Copy)]