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