interrupt handling
This commit is contained in:
parent
04a9748aa6
commit
6ae1d4a4b8
|
@ -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)]
|
||||
|
|
Loading…
Reference in a new issue