Refactor interrupts

This commit is contained in:
Franco Colmenarez 2021-11-25 21:13:55 -05:00
parent aa88a6498e
commit 10848d28eb
8 changed files with 143 additions and 185 deletions

47
Cargo.lock generated
View File

@ -1268,12 +1268,6 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb20dcc30536a1508e75d47dd0e399bb2fe7354dcf35cda9127f2bf1ed92e30e" checksum = "bb20dcc30536a1508e75d47dd0e399bb2fe7354dcf35cda9127f2bf1ed92e30e"
[[package]]
name = "ppv-lite86"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba"
[[package]] [[package]]
name = "proc-macro-crate" name = "proc-macro-crate"
version = "0.1.5" version = "0.1.5"
@ -1317,46 +1311,6 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "rand"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
"rand_hc",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
dependencies = [
"getrandom",
]
[[package]]
name = "rand_hc"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
dependencies = [
"rand_core",
]
[[package]] [[package]]
name = "range-alloc" name = "range-alloc"
version = "0.1.2" version = "0.1.2"
@ -1422,7 +1376,6 @@ dependencies = [
"env_logger", "env_logger",
"log", "log",
"pixels", "pixels",
"rand",
"winit", "winit",
"winit_input_helper", "winit_input_helper",
] ]

View File

@ -7,10 +7,14 @@ use crate::ppu::{
PPU, PPU,
DMA_ADDRESS, DMA_ADDRESS,
}; };
use crate::cpu::{Interrupt};
use crate::timer::{Timer}; use crate::timer::{Timer};
use crate::joypad::{Joypad, JOYPAD_ADDRESS}; use crate::joypad::{Joypad, JOYPAD_ADDRESS};
use crate::sound::{Sound}; use crate::sound::{Sound};
use crate::interrupts::{
Interrupts,
INTERRUPT_ENABLE_ADDRESS,
INTERRUPT_FLAG_ADDRESS,
};
pub const BANK_ZERO: RangeInclusive<u16> = 0x0000..=0x3FFF; pub const BANK_ZERO: RangeInclusive<u16> = 0x0000..=0x3FFF;
pub const BANK_SWITCHABLE: RangeInclusive<u16> = 0x4000..=0x7FFF; pub const BANK_SWITCHABLE: RangeInclusive<u16> = 0x4000..=0x7FFF;
@ -23,9 +27,6 @@ pub const SPRITE_ATTRIBUTE_TABLE: RangeInclusive<u16> = 0xFE00..=0xFE9F;
pub const NOT_USABLE: RangeInclusive<u16> = 0xFEA0..=0xFEFF; pub const NOT_USABLE: RangeInclusive<u16> = 0xFEA0..=0xFEFF;
pub const IO_REGISTERS: RangeInclusive<u16> = 0xFF00..=0xFF7F; pub const IO_REGISTERS: RangeInclusive<u16> = 0xFF00..=0xFF7F;
pub const HIGH_RAM: RangeInclusive<u16> = 0xFF80..=0xFFFE; pub const HIGH_RAM: RangeInclusive<u16> = 0xFF80..=0xFFFE;
pub const INTERRUPT_ENABLE_REGISTER: RangeInclusive<u16> = 0xFFFF..=0xFFFF;
pub const INTERRUPT_ENABLE_ADDRESS: u16 = 0xFFFF;
pub const INTERRUPT_FLAG_ADDRESS: u16 = 0xFF0F;
pub struct Bus { pub struct Bus {
data: [u8; 0x10000], data: [u8; 0x10000],
@ -34,6 +35,7 @@ pub struct Bus {
pub joypad: Joypad, pub joypad: Joypad,
pub timer: Timer, pub timer: Timer,
pub sound: Sound, pub sound: Sound,
pub interrupts: Interrupts,
} }
impl Bus { impl Bus {
@ -58,6 +60,7 @@ impl Bus {
joypad: Joypad::new(), joypad: Joypad::new(),
timer: Timer::new(), timer: Timer::new(),
sound: Sound::new(), sound: Sound::new(),
interrupts: Interrupts::new(),
}; };
// Hardware registers after the bootrom // Hardware registers after the bootrom
@ -90,7 +93,7 @@ impl Bus {
if BANK_ZERO.contains(&address) || BANK_SWITCHABLE.contains(&address) || EXTERNAL_RAM.contains(&address) { if BANK_ZERO.contains(&address) || BANK_SWITCHABLE.contains(&address) || EXTERNAL_RAM.contains(&address) {
return self.rom.read(address); return self.rom.read(address);
} else if address == INTERRUPT_ENABLE_ADDRESS || address == INTERRUPT_FLAG_ADDRESS { } else if address == INTERRUPT_ENABLE_ADDRESS || address == INTERRUPT_FLAG_ADDRESS {
return 0b11100000 | self.data[address as usize]; return self.interrupts.read(address);
} else if VIDEO_RAM.contains(&address) { } else if VIDEO_RAM.contains(&address) {
return self.ppu.read_vram(address); return self.ppu.read_vram(address);
} else if SPRITE_ATTRIBUTE_TABLE.contains(&address) { } else if SPRITE_ATTRIBUTE_TABLE.contains(&address) {
@ -118,6 +121,8 @@ impl Bus {
if BANK_ZERO.contains(&address) || BANK_SWITCHABLE.contains(&address) || EXTERNAL_RAM.contains(&address) { if BANK_ZERO.contains(&address) || BANK_SWITCHABLE.contains(&address) || EXTERNAL_RAM.contains(&address) {
self.rom.write(address, data); self.rom.write(address, data);
} else if address == INTERRUPT_ENABLE_ADDRESS || address == INTERRUPT_FLAG_ADDRESS {
self.interrupts.write(address, data);
} else if WORK_RAM_1.contains(&address) || WORK_RAM_2.contains(&address) { } else if WORK_RAM_1.contains(&address) || WORK_RAM_2.contains(&address) {
self.data[address as usize] = data; self.data[address as usize] = data;
// Copy to the ECHO RAM // Copy to the ECHO RAM
@ -156,18 +161,9 @@ impl Bus {
} }
} }
pub fn force_write(&mut self, address: u16, data: u8) {
self.data[address as usize] = data;
}
pub fn write_16bit(&mut self, address: u16, data: u16) { pub fn write_16bit(&mut self, address: u16, data: u16) {
let bytes = data.to_le_bytes(); let bytes = data.to_le_bytes();
self.write(address, bytes[0]); self.write(address, bytes[0]);
self.write(address.wrapping_add(1), bytes[1]); self.write(address.wrapping_add(1), bytes[1]);
} }
pub fn set_interrupt_flag(&mut self, interrupt: Interrupt, val: bool) {
let byte = self.read(INTERRUPT_FLAG_ADDRESS);
self.write(INTERRUPT_FLAG_ADDRESS, interrupt.set(byte, val));
}
} }

View File

@ -8,7 +8,12 @@ use crate::utils::{
sub_half_carry, sub_half_carry,
add_half_carry_16bit, add_half_carry_16bit,
}; };
use crate::bus::{Bus, INTERRUPT_ENABLE_ADDRESS, INTERRUPT_FLAG_ADDRESS}; use crate::bus::{Bus};
use crate::interrupts::{
Interrupt,
INTERRUPT_ENABLE_ADDRESS,
INTERRUPT_FLAG_ADDRESS,
};
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub enum Register { pub enum Register {
@ -53,45 +58,6 @@ pub enum FlagRegister {
Carry, // Set if a carry was ocurrend from the last math operation or if register A is the smaller value when executing the CP instruction Carry, // Set if a carry was ocurrend from the last math operation or if register A is the smaller value when executing the CP instruction
} }
#[derive(Debug, Copy, Clone)]
pub enum Interrupt {
VBlank,
LCDSTAT,
Timer,
Serial,
Joypad,
}
impl Interrupt {
fn get_bit_index(&self) -> BitIndex {
match self {
Interrupt::VBlank => BitIndex::I0,
Interrupt::LCDSTAT => BitIndex::I1,
Interrupt::Timer => BitIndex::I2,
Interrupt::Serial => BitIndex::I3,
Interrupt::Joypad => BitIndex::I4,
}
}
pub fn get(&self, byte: u8) -> bool {
get_bit(byte, self.get_bit_index())
}
pub fn set(&self, byte: u8, val: bool) -> u8 {
set_bit(byte, val, self.get_bit_index())
}
pub fn get_vector(&self) -> u16 {
match self {
Interrupt::VBlank => 0x40,
Interrupt::LCDSTAT => 0x48,
Interrupt::Timer => 0x50,
Interrupt::Serial => 0x58,
Interrupt::Joypad => 0x60,
}
}
}
pub struct Registers { pub struct Registers {
a: u8, a: u8,
f: u8, f: u8,
@ -914,7 +880,7 @@ impl CPU {
pub fn handle_interrupt(&mut self, bus: &mut Bus, interrupt: Interrupt) { pub fn handle_interrupt(&mut self, bus: &mut Bus, interrupt: Interrupt) {
// println!("Interrupt: {:?}", interrupt); // println!("Interrupt: {:?}", interrupt);
bus.set_interrupt_flag(interrupt, false); bus.interrupts.set(interrupt, false);
self.ime = false; self.ime = false;
self.registers.decrement(Register::PC, 3); self.registers.decrement(Register::PC, 3);
self.exec(Opcode::CALL(OpcodeParameter::U16(interrupt.get_vector())), bus); self.exec(Opcode::CALL(OpcodeParameter::U16(interrupt.get_vector())), bus);

View File

@ -2,7 +2,8 @@
use winit_input_helper::WinitInputHelper; use winit_input_helper::WinitInputHelper;
use winit::event::{VirtualKeyCode}; use winit::event::{VirtualKeyCode};
use crate::cpu::{CPU, Cycles, Interrupt}; use crate::cpu::{CPU, Cycles};
use crate::interrupts::Interrupt;
use crate::bus::Bus; use crate::bus::Bus;
use crate::joypad::{Button}; use crate::joypad::{Button};
#[cfg(not(test))] #[cfg(not(test))]
@ -99,7 +100,7 @@ impl Emulator {
self.bus.joypad.release(Button::Select); self.bus.joypad.release(Button::Select);
} }
if change { if change {
self.bus.set_interrupt_flag(Interrupt::Joypad, true); self.bus.interrupts.request(Interrupt::Joypad);
} }
} }
@ -108,20 +109,8 @@ impl Emulator {
while self.cpu.get_cycles().to_t().0 <= cpu_cycles.0 { while self.cpu.get_cycles().to_t().0 <= cpu_cycles.0 {
self.cpu.run(&mut self.bus); self.cpu.run(&mut self.bus);
let cycles = self.cpu.get_last_op_cycles().to_t(); let cycles = self.cpu.get_last_op_cycles().to_t();
self.bus.ppu.do_cycles(cycles, frame_buffer); self.bus.ppu.do_cycles(&mut self.bus.interrupts, cycles, frame_buffer);
if self.bus.ppu.get_interrupt(Interrupt::VBlank) { self.bus.timer.do_cycles(&mut self.bus.interrupts, cycles);
self.bus.set_interrupt_flag(Interrupt::VBlank, true);
self.bus.ppu.set_interrupt(Interrupt::VBlank, false);
}
if self.bus.ppu.get_interrupt(Interrupt::LCDSTAT) {
self.bus.set_interrupt_flag(Interrupt::LCDSTAT, true);
self.bus.ppu.set_interrupt(Interrupt::LCDSTAT, false);
}
self.bus.timer.do_cycles(cycles);
if self.bus.timer.get_interrupt() {
self.bus.set_interrupt_flag(Interrupt::Timer, true);
self.bus.timer.set_interrupt(false);
}
// 1 CPU cycle = 238.42ns // 1 CPU cycle = 238.42ns
// thread::sleep(time::Duration::from_nanos((self.cpu.get_last_op_cycles().0 * 238).try_into().unwrap())); // thread::sleep(time::Duration::from_nanos((self.cpu.get_last_op_cycles().0 * 238).try_into().unwrap()));
@ -135,20 +124,8 @@ impl Emulator {
while !exit { while !exit {
self.cpu.run(&mut self.bus); self.cpu.run(&mut self.bus);
let cycles = self.cpu.get_last_op_cycles().to_t(); let cycles = self.cpu.get_last_op_cycles().to_t();
self.bus.ppu.do_cycles(cycles, &mut frame); self.bus.ppu.do_cycles(&mut self.bus.interrupts, cycles, &mut frame);
if self.bus.ppu.get_interrupt(Interrupt::VBlank) { self.bus.timer.do_cycles(&mut self.bus.interrupts, cycles);
self.bus.set_interrupt_flag(Interrupt::VBlank, true);
self.bus.ppu.set_interrupt(Interrupt::VBlank, false);
}
if self.bus.ppu.get_interrupt(Interrupt::LCDSTAT) {
self.bus.set_interrupt_flag(Interrupt::LCDSTAT, true);
self.bus.ppu.set_interrupt(Interrupt::LCDSTAT, false);
}
self.bus.timer.do_cycles(cycles);
if self.bus.timer.get_interrupt() {
self.bus.set_interrupt_flag(Interrupt::Timer, true);
self.bus.timer.set_interrupt(false);
}
// exit = self.cpu.get_exec_calls_count() >= 1258895; // log 1 // exit = self.cpu.get_exec_calls_count() >= 1258895; // log 1
exit = self.cpu.get_exec_calls_count() >= 161502; // log 2 exit = self.cpu.get_exec_calls_count() >= 161502; // log 2

90
src/interrupts.rs Normal file
View File

@ -0,0 +1,90 @@
use crate::utils::{
BitIndex,
get_bit,
set_bit,
};
pub const INTERRUPT_ENABLE_ADDRESS: u16 = 0xFFFF;
pub const INTERRUPT_FLAG_ADDRESS: u16 = 0xFF0F;
#[derive(Debug, Copy, Clone)]
pub enum Interrupt {
VBlank,
LCDSTAT,
Timer,
Serial,
Joypad,
}
impl Interrupt {
fn get_bit_index(&self) -> BitIndex {
match self {
Interrupt::VBlank => BitIndex::I0,
Interrupt::LCDSTAT => BitIndex::I1,
Interrupt::Timer => BitIndex::I2,
Interrupt::Serial => BitIndex::I3,
Interrupt::Joypad => BitIndex::I4,
}
}
pub fn get(&self, byte: u8) -> bool {
get_bit(byte, self.get_bit_index())
}
pub fn set(&self, byte: u8, val: bool) -> u8 {
set_bit(byte, val, self.get_bit_index())
}
pub fn get_vector(&self) -> u16 {
match self {
Interrupt::VBlank => 0x40,
Interrupt::LCDSTAT => 0x48,
Interrupt::Timer => 0x50,
Interrupt::Serial => 0x58,
Interrupt::Joypad => 0x60,
}
}
}
pub struct Interrupts {
interrupt_enable: u8,
interrupt_flag: u8,
}
impl Interrupts {
pub fn new() -> Self {
Self {
interrupt_enable: 0,
interrupt_flag: 0,
}
}
pub fn read(&self, address: u16) -> u8 {
let byte = match address {
INTERRUPT_ENABLE_ADDRESS => self.interrupt_enable,
INTERRUPT_FLAG_ADDRESS => self.interrupt_flag,
_ => unreachable!(),
};
0b11100000 | byte
}
pub fn write(&mut self, address: u16, data: u8) {
match address {
INTERRUPT_ENABLE_ADDRESS => self.interrupt_enable = data,
INTERRUPT_FLAG_ADDRESS => self.interrupt_flag = data,
_ => unreachable!(),
};
}
pub fn set(&mut self, interrupt: Interrupt, val: bool) {
self.interrupt_flag = interrupt.set(self.interrupt_flag, val);
}
pub fn get(&self, interrupt: Interrupt) -> bool {
interrupt.get(self.interrupt_flag)
}
pub fn request(&mut self, interrupt: Interrupt) {
self.set(interrupt, true)
}
}

View File

@ -5,6 +5,7 @@ pub mod timer;
pub mod sound; pub mod sound;
pub mod rom; pub mod rom;
pub mod bus; pub mod bus;
pub mod interrupts;
pub mod joypad; pub mod joypad;
pub mod emulator; pub mod emulator;
pub mod render; pub mod render;

View File

@ -4,7 +4,8 @@ use crate::utils::{
set_bit, set_bit,
}; };
use crate::bus::{SPRITE_ATTRIBUTE_TABLE}; use crate::bus::{SPRITE_ATTRIBUTE_TABLE};
use crate::cpu::{Cycles, Interrupt}; use crate::cpu::{Cycles};
use crate::interrupts::{Interrupts, Interrupt};
pub const LCD_WIDTH: u32 = 160; pub const LCD_WIDTH: u32 = 160;
pub const LCD_HEIGHT: u32 = 144; pub const LCD_HEIGHT: u32 = 144;
@ -210,8 +211,6 @@ impl Sprite {
pub struct PPU { pub struct PPU {
state: bool, state: bool,
vblank_request: bool,
lcdstat_request: bool,
background_priority: bool, background_priority: bool,
window_enable: bool, window_enable: bool,
lcd_enable: bool, lcd_enable: bool,
@ -239,8 +238,6 @@ impl PPU {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
state: false, state: false,
vblank_request: false,
lcdstat_request: false,
background_priority: false, background_priority: false,
window_enable: false, window_enable: false,
window_drawn: false, window_drawn: false,
@ -265,22 +262,6 @@ impl PPU {
} }
} }
pub fn set_interrupt(&mut self, interrupt: Interrupt, val: bool) {
match interrupt {
Interrupt::VBlank => self.vblank_request = val,
Interrupt::LCDSTAT => self.lcdstat_request = val,
_ => unreachable!(),
};
}
pub fn get_interrupt(&self, interrupt: Interrupt) -> bool {
match interrupt {
Interrupt::VBlank => self.vblank_request,
Interrupt::LCDSTAT => self.lcdstat_request,
_ => unreachable!(),
}
}
pub fn is_io_register(address: u16) -> bool { pub fn is_io_register(address: u16) -> bool {
address >= 0xFF40 && address <= 0xFF4B address >= 0xFF40 && address <= 0xFF4B
} }
@ -345,7 +326,7 @@ impl PPU {
self.cycles.0 += cycles.0; self.cycles.0 += cycles.0;
} }
pub fn do_cycles(&mut self, cycles: Cycles, frame_buffer: &mut [u8]) { pub fn do_cycles(&mut self, interrupts: &mut Interrupts, cycles: Cycles, frame_buffer: &mut [u8]) {
if !self.lcd_enable { if !self.lcd_enable {
self.increment_cycles(cycles); self.increment_cycles(cycles);
return; return;
@ -355,7 +336,7 @@ impl PPU {
if self.cycles.0 <= 80 && !self.get_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::SearchingOAM)) { if self.cycles.0 <= 80 && !self.get_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::SearchingOAM)) {
// Mode 2 OAM scan // Mode 2 OAM scan
self.set_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::SearchingOAM), true); self.set_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::SearchingOAM), true);
self.stat_interrupt(); self.stat_interrupt(interrupts);
self.oam_search(); self.oam_search();
} else if self.cycles.0 > 80 && self.cycles.0 <= 80 + 172 { } else if self.cycles.0 > 80 && self.cycles.0 <= 80 + 172 {
// Mode 3 drawing pixel line. This could also last 289 cycles // Mode 3 drawing pixel line. This could also last 289 cycles
@ -367,13 +348,13 @@ impl PPU {
} else if self.cycles.0 > 80 + 172 && self.cycles.0 <= 80 + 172 + 204 && !self.get_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::HBlank)) { } else if self.cycles.0 > 80 + 172 && self.cycles.0 <= 80 + 172 + 204 && !self.get_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::HBlank)) {
// Mode 0 Horizontal blank. This could last 87 or 204 cycles depending on the mode 3 // Mode 0 Horizontal blank. This could last 87 or 204 cycles depending on the mode 3
self.set_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::HBlank), true); self.set_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::HBlank), true);
self.stat_interrupt(); self.stat_interrupt(interrupts);
} }
} else if self.lcd_y >= 144 && !self.get_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank)) { } else if self.lcd_y >= 144 && !self.get_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank)) {
// Mode 1 Vertical blank // Mode 1 Vertical blank
self.set_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank), true); self.set_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank), true);
self.set_interrupt(Interrupt::VBlank, true); interrupts.request(Interrupt::VBlank);
self.stat_interrupt(); self.stat_interrupt(interrupts);
} }
self.increment_cycles(cycles); self.increment_cycles(cycles);
@ -393,11 +374,11 @@ impl PPU {
self.window_y_counter = 0; self.window_y_counter = 0;
self.lcd_y = 0; self.lcd_y = 0;
} }
self.stat_interrupt(); self.stat_interrupt(interrupts);
} }
} }
fn stat_interrupt(&mut self) { fn stat_interrupt(&mut self, interrupts: &mut Interrupts) {
let prev_state = self.state; let prev_state = self.state;
let lyc_compare = self.lcd_y == self.get_register(LCD_Y_COMPARE_ADDRESS); let lyc_compare = self.lcd_y == self.get_register(LCD_Y_COMPARE_ADDRESS);
self.set_lcd_status(LCDStatus::LYCFlag, lyc_compare); self.set_lcd_status(LCDStatus::LYCFlag, lyc_compare);
@ -417,7 +398,7 @@ impl PPU {
self.get_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank)) self.get_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank))
); );
if self.state && !prev_state { if self.state && !prev_state {
self.set_interrupt(Interrupt::LCDSTAT, self.state); interrupts.request(Interrupt::LCDSTAT);
} }
} }

View File

@ -1,4 +1,5 @@
use crate::cpu::{Cycles}; use crate::cpu::{Cycles};
use crate::interrupts::{Interrupt, Interrupts};
use crate::utils::{ use crate::utils::{
BitIndex, BitIndex,
get_bit, get_bit,
@ -13,7 +14,6 @@ pub struct Timer {
divider: u16, divider: u16,
prev_result: bool, prev_result: bool,
is_enabled: bool, is_enabled: bool,
interrupt: bool,
control: u8, control: u8,
io_registers: [u8; 4], io_registers: [u8; 4],
} }
@ -25,7 +25,6 @@ impl Timer {
divider: 0, divider: 0,
control: 0, control: 0,
prev_result: false, prev_result: false,
interrupt: false,
is_enabled: false, is_enabled: false,
io_registers: [0; 4], io_registers: [0; 4],
} }
@ -62,29 +61,21 @@ impl Timer {
self.io_registers[(address - 0xFF04) as usize] = data; self.io_registers[(address - 0xFF04) as usize] = data;
} }
pub fn get_interrupt(&self) -> bool {
self.interrupt
}
pub fn set_interrupt(&mut self, val: bool) {
self.interrupt = val
}
pub fn read_divider(&self) -> u8 { pub fn read_divider(&self) -> u8 {
self.divider.to_be_bytes()[0] self.divider.to_be_bytes()[0]
} }
pub fn do_cycles(&mut self, cycles: Cycles) { pub fn do_cycles(&mut self, interrupts: &mut Interrupts, cycles: Cycles) {
self.is_enabled = self.is_timer_enabled(); self.is_enabled = self.is_timer_enabled();
self.control = self.get_register(TIMER_CONTROL_ADDRESS); self.control = self.get_register(TIMER_CONTROL_ADDRESS);
let mut count = 0; let mut count = 0;
while count < cycles.0 { while count < cycles.0 {
self.cycle(); self.cycle(interrupts);
count += 1; count += 1;
} }
} }
fn cycle(&mut self) { fn cycle(&mut self, interrupts: &mut Interrupts) {
self.divider = self.divider.wrapping_add(1); self.divider = self.divider.wrapping_add(1);
let result = self.is_enabled && self.get_tima_rate(); let result = self.is_enabled && self.get_tima_rate();
@ -93,7 +84,7 @@ impl Timer {
let tima = self.get_register(TIMER_COUNTER_ADDRESS).wrapping_add(1); let tima = self.get_register(TIMER_COUNTER_ADDRESS).wrapping_add(1);
if tima == 0 { if tima == 0 {
self.set_register(TIMER_COUNTER_ADDRESS, self.get_register(TIMER_MODULO_ADDRESS)); self.set_register(TIMER_COUNTER_ADDRESS, self.get_register(TIMER_MODULO_ADDRESS));
self.interrupt = true; interrupts.request(Interrupt::Timer);
} else { } else {
self.set_register(TIMER_COUNTER_ADDRESS, tima); self.set_register(TIMER_COUNTER_ADDRESS, tima);
} }
@ -125,50 +116,53 @@ mod tests {
#[test] #[test]
fn test_tima_increment() { fn test_tima_increment() {
let mut timer = Timer::new(); let mut timer = Timer::new();
let mut interrupts = Interrupts::new();
timer.set_register(TIMER_CONTROL_ADDRESS, 0b101); timer.set_register(TIMER_CONTROL_ADDRESS, 0b101);
timer.set_register(TIMER_COUNTER_ADDRESS, 0); timer.set_register(TIMER_COUNTER_ADDRESS, 0);
timer.set_div(0b10111); timer.set_div(0b10111);
timer.do_cycles(Cycles(1)); timer.do_cycles(&mut interrupts, Cycles(1));
assert_eq!(timer.div(), 0b11000); assert_eq!(timer.div(), 0b11000);
assert_eq!(timer.prev_result(), true); assert_eq!(timer.prev_result(), true);
assert_eq!(timer.get_register(TIMER_COUNTER_ADDRESS), 0); assert_eq!(timer.get_register(TIMER_COUNTER_ADDRESS), 0);
assert_eq!(timer.get_interrupt(), false); assert_eq!(interrupts.get(Interrupt::Timer), false);
timer.do_cycles(Cycles(7)); timer.do_cycles(&mut interrupts, Cycles(7));
assert_eq!(timer.div(), 0b11111); assert_eq!(timer.div(), 0b11111);
assert_eq!(timer.prev_result(), true); assert_eq!(timer.prev_result(), true);
assert_eq!(timer.get_register(TIMER_COUNTER_ADDRESS), 0); assert_eq!(timer.get_register(TIMER_COUNTER_ADDRESS), 0);
assert_eq!(timer.get_interrupt(), false); assert_eq!(interrupts.get(Interrupt::Timer), false);
timer.do_cycles(Cycles(1)); timer.do_cycles(&mut interrupts, Cycles(1));
assert_eq!(timer.div(), 0b100000); assert_eq!(timer.div(), 0b100000);
assert_eq!(timer.get_register(TIMER_COUNTER_ADDRESS), 1); assert_eq!(timer.get_register(TIMER_COUNTER_ADDRESS), 1);
assert_eq!(timer.prev_result(), false); assert_eq!(timer.prev_result(), false);
assert_eq!(timer.get_interrupt(), false); assert_eq!(interrupts.get(Interrupt::Timer), false);
} }
#[test] #[test]
fn test_tima_overflow() { fn test_tima_overflow() {
let mut timer = Timer::new(); let mut timer = Timer::new();
let mut interrupts = Interrupts::new();
timer.set_register(TIMER_CONTROL_ADDRESS, 0b101); timer.set_register(TIMER_CONTROL_ADDRESS, 0b101);
timer.set_register(TIMER_COUNTER_ADDRESS, 0xFF); timer.set_register(TIMER_COUNTER_ADDRESS, 0xFF);
timer.set_div(0b10111); timer.set_div(0b10111);
timer.do_cycles(Cycles(9)); timer.do_cycles(&mut interrupts, Cycles(9));
assert_eq!(timer.div(), 0b100000); assert_eq!(timer.div(), 0b100000);
assert_eq!(timer.get_register(TIMER_COUNTER_ADDRESS), 0x00); assert_eq!(timer.get_register(TIMER_COUNTER_ADDRESS), 0x00);
assert_eq!(timer.get_interrupt(), true); assert_eq!(interrupts.get(Interrupt::Timer), true);
} }
#[test] #[test]
fn test_timer_enable() { fn test_timer_enable() {
let mut timer = Timer::new(); let mut timer = Timer::new();
let mut interrupts = Interrupts::new();
timer.set_register(TIMER_CONTROL_ADDRESS, 0b101); timer.set_register(TIMER_CONTROL_ADDRESS, 0b101);
timer.set_register(TIMER_COUNTER_ADDRESS, 0); timer.set_register(TIMER_COUNTER_ADDRESS, 0);
timer.set_div(0b11000); timer.set_div(0b11000);
timer.do_cycles(Cycles(1)); timer.do_cycles(&mut interrupts, Cycles(1));
assert_eq!(timer.div(), 0b11001); assert_eq!(timer.div(), 0b11001);
assert_eq!(timer.get_register(TIMER_COUNTER_ADDRESS), 0); assert_eq!(timer.get_register(TIMER_COUNTER_ADDRESS), 0);
timer.set_register(TIMER_CONTROL_ADDRESS, 0b001); timer.set_register(TIMER_CONTROL_ADDRESS, 0b001);
timer.do_cycles(Cycles(1)); timer.do_cycles(&mut interrupts, Cycles(1));
assert_eq!(timer.div(), 0b11010); assert_eq!(timer.div(), 0b11010);
assert_eq!(timer.get_register(TIMER_COUNTER_ADDRESS), 1); assert_eq!(timer.get_register(TIMER_COUNTER_ADDRESS), 1);
} }