From 10848d28eb83bb41b9c0595723eefa8f1c16908a Mon Sep 17 00:00:00 2001 From: Franco Colmenarez Date: Thu, 25 Nov 2021 21:13:55 -0500 Subject: [PATCH] Refactor interrupts --- Cargo.lock | 47 ------------------------- src/bus.rs | 24 ++++++------- src/cpu.rs | 48 ++++--------------------- src/emulator.rs | 37 ++++--------------- src/interrupts.rs | 90 +++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/ppu.rs | 39 ++++++-------------- src/timer.rs | 42 ++++++++++------------ 8 files changed, 143 insertions(+), 185 deletions(-) create mode 100644 src/interrupts.rs diff --git a/Cargo.lock b/Cargo.lock index db4e1e3..0dba666 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1268,12 +1268,6 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb20dcc30536a1508e75d47dd0e399bb2fe7354dcf35cda9127f2bf1ed92e30e" -[[package]] -name = "ppv-lite86" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" - [[package]] name = "proc-macro-crate" version = "0.1.5" @@ -1317,46 +1311,6 @@ dependencies = [ "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]] name = "range-alloc" version = "0.1.2" @@ -1422,7 +1376,6 @@ dependencies = [ "env_logger", "log", "pixels", - "rand", "winit", "winit_input_helper", ] diff --git a/src/bus.rs b/src/bus.rs index efdca2d..ece0e1b 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -7,10 +7,14 @@ use crate::ppu::{ PPU, DMA_ADDRESS, }; -use crate::cpu::{Interrupt}; use crate::timer::{Timer}; use crate::joypad::{Joypad, JOYPAD_ADDRESS}; use crate::sound::{Sound}; +use crate::interrupts::{ + Interrupts, + INTERRUPT_ENABLE_ADDRESS, + INTERRUPT_FLAG_ADDRESS, +}; pub const BANK_ZERO: RangeInclusive = 0x0000..=0x3FFF; pub const BANK_SWITCHABLE: RangeInclusive = 0x4000..=0x7FFF; @@ -23,9 +27,6 @@ pub const SPRITE_ATTRIBUTE_TABLE: RangeInclusive = 0xFE00..=0xFE9F; pub const NOT_USABLE: RangeInclusive = 0xFEA0..=0xFEFF; pub const IO_REGISTERS: RangeInclusive = 0xFF00..=0xFF7F; pub const HIGH_RAM: RangeInclusive = 0xFF80..=0xFFFE; -pub const INTERRUPT_ENABLE_REGISTER: RangeInclusive = 0xFFFF..=0xFFFF; -pub const INTERRUPT_ENABLE_ADDRESS: u16 = 0xFFFF; -pub const INTERRUPT_FLAG_ADDRESS: u16 = 0xFF0F; pub struct Bus { data: [u8; 0x10000], @@ -34,6 +35,7 @@ pub struct Bus { pub joypad: Joypad, pub timer: Timer, pub sound: Sound, + pub interrupts: Interrupts, } impl Bus { @@ -58,6 +60,7 @@ impl Bus { joypad: Joypad::new(), timer: Timer::new(), sound: Sound::new(), + interrupts: Interrupts::new(), }; // Hardware registers after the bootrom @@ -90,7 +93,7 @@ impl Bus { if BANK_ZERO.contains(&address) || BANK_SWITCHABLE.contains(&address) || EXTERNAL_RAM.contains(&address) { return self.rom.read(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) { return self.ppu.read_vram(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) { 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) { self.data[address as usize] = data; // 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) { let bytes = data.to_le_bytes(); self.write(address, bytes[0]); 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)); - } } diff --git a/src/cpu.rs b/src/cpu.rs index 5a8fab3..7f468b5 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -8,7 +8,12 @@ use crate::utils::{ sub_half_carry, 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)] 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 } -#[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 { a: u8, f: u8, @@ -914,7 +880,7 @@ impl CPU { pub fn handle_interrupt(&mut self, bus: &mut Bus, interrupt: Interrupt) { // println!("Interrupt: {:?}", interrupt); - bus.set_interrupt_flag(interrupt, false); + bus.interrupts.set(interrupt, false); self.ime = false; self.registers.decrement(Register::PC, 3); self.exec(Opcode::CALL(OpcodeParameter::U16(interrupt.get_vector())), bus); diff --git a/src/emulator.rs b/src/emulator.rs index 7de5907..fd12dd3 100644 --- a/src/emulator.rs +++ b/src/emulator.rs @@ -2,7 +2,8 @@ use winit_input_helper::WinitInputHelper; 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::joypad::{Button}; #[cfg(not(test))] @@ -99,7 +100,7 @@ impl Emulator { self.bus.joypad.release(Button::Select); } 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 { self.cpu.run(&mut self.bus); let cycles = self.cpu.get_last_op_cycles().to_t(); - self.bus.ppu.do_cycles(cycles, frame_buffer); - if self.bus.ppu.get_interrupt(Interrupt::VBlank) { - 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); - } + self.bus.ppu.do_cycles(&mut self.bus.interrupts, cycles, frame_buffer); + self.bus.timer.do_cycles(&mut self.bus.interrupts, cycles); // 1 CPU cycle = 238.42ns // 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 { self.cpu.run(&mut self.bus); let cycles = self.cpu.get_last_op_cycles().to_t(); - self.bus.ppu.do_cycles(cycles, &mut frame); - if self.bus.ppu.get_interrupt(Interrupt::VBlank) { - 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); - } + self.bus.ppu.do_cycles(&mut self.bus.interrupts, cycles, &mut frame); + self.bus.timer.do_cycles(&mut self.bus.interrupts, cycles); // exit = self.cpu.get_exec_calls_count() >= 1258895; // log 1 exit = self.cpu.get_exec_calls_count() >= 161502; // log 2 diff --git a/src/interrupts.rs b/src/interrupts.rs new file mode 100644 index 0000000..d00b037 --- /dev/null +++ b/src/interrupts.rs @@ -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) + } +} diff --git a/src/lib.rs b/src/lib.rs index 2a07d9a..665aede 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,6 +5,7 @@ pub mod timer; pub mod sound; pub mod rom; pub mod bus; +pub mod interrupts; pub mod joypad; pub mod emulator; pub mod render; diff --git a/src/ppu.rs b/src/ppu.rs index 91b63b9..4c2c60b 100644 --- a/src/ppu.rs +++ b/src/ppu.rs @@ -4,7 +4,8 @@ use crate::utils::{ set_bit, }; 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_HEIGHT: u32 = 144; @@ -210,8 +211,6 @@ impl Sprite { pub struct PPU { state: bool, - vblank_request: bool, - lcdstat_request: bool, background_priority: bool, window_enable: bool, lcd_enable: bool, @@ -239,8 +238,6 @@ impl PPU { pub fn new() -> Self { Self { state: false, - vblank_request: false, - lcdstat_request: false, background_priority: false, window_enable: 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 { address >= 0xFF40 && address <= 0xFF4B } @@ -345,7 +326,7 @@ impl PPU { 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 { self.increment_cycles(cycles); return; @@ -355,7 +336,7 @@ impl PPU { if self.cycles.0 <= 80 && !self.get_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::SearchingOAM)) { // Mode 2 OAM scan self.set_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::SearchingOAM), true); - self.stat_interrupt(); + self.stat_interrupt(interrupts); self.oam_search(); } else if self.cycles.0 > 80 && self.cycles.0 <= 80 + 172 { // 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)) { // 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.stat_interrupt(); + self.stat_interrupt(interrupts); } } else if self.lcd_y >= 144 && !self.get_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank)) { // Mode 1 Vertical blank self.set_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank), true); - self.set_interrupt(Interrupt::VBlank, true); - self.stat_interrupt(); + interrupts.request(Interrupt::VBlank); + self.stat_interrupt(interrupts); } self.increment_cycles(cycles); @@ -393,11 +374,11 @@ impl PPU { self.window_y_counter = 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 lyc_compare = self.lcd_y == self.get_register(LCD_Y_COMPARE_ADDRESS); self.set_lcd_status(LCDStatus::LYCFlag, lyc_compare); @@ -417,7 +398,7 @@ impl PPU { self.get_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank)) ); if self.state && !prev_state { - self.set_interrupt(Interrupt::LCDSTAT, self.state); + interrupts.request(Interrupt::LCDSTAT); } } diff --git a/src/timer.rs b/src/timer.rs index f7c69b9..8079607 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -1,4 +1,5 @@ use crate::cpu::{Cycles}; +use crate::interrupts::{Interrupt, Interrupts}; use crate::utils::{ BitIndex, get_bit, @@ -13,7 +14,6 @@ pub struct Timer { divider: u16, prev_result: bool, is_enabled: bool, - interrupt: bool, control: u8, io_registers: [u8; 4], } @@ -25,7 +25,6 @@ impl Timer { divider: 0, control: 0, prev_result: false, - interrupt: false, is_enabled: false, io_registers: [0; 4], } @@ -62,29 +61,21 @@ impl Timer { 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 { 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.control = self.get_register(TIMER_CONTROL_ADDRESS); let mut count = 0; while count < cycles.0 { - self.cycle(); + self.cycle(interrupts); count += 1; } } - fn cycle(&mut self) { + fn cycle(&mut self, interrupts: &mut Interrupts) { self.divider = self.divider.wrapping_add(1); 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); if tima == 0 { self.set_register(TIMER_COUNTER_ADDRESS, self.get_register(TIMER_MODULO_ADDRESS)); - self.interrupt = true; + interrupts.request(Interrupt::Timer); } else { self.set_register(TIMER_COUNTER_ADDRESS, tima); } @@ -125,50 +116,53 @@ mod tests { #[test] fn test_tima_increment() { let mut timer = Timer::new(); + let mut interrupts = Interrupts::new(); timer.set_register(TIMER_CONTROL_ADDRESS, 0b101); timer.set_register(TIMER_COUNTER_ADDRESS, 0); timer.set_div(0b10111); - timer.do_cycles(Cycles(1)); + timer.do_cycles(&mut interrupts, Cycles(1)); assert_eq!(timer.div(), 0b11000); assert_eq!(timer.prev_result(), true); 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.prev_result(), true); assert_eq!(timer.get_register(TIMER_COUNTER_ADDRESS), 0); - assert_eq!(timer.get_interrupt(), false); - timer.do_cycles(Cycles(1)); + assert_eq!(interrupts.get(Interrupt::Timer), false); + timer.do_cycles(&mut interrupts, Cycles(1)); assert_eq!(timer.div(), 0b100000); assert_eq!(timer.get_register(TIMER_COUNTER_ADDRESS), 1); assert_eq!(timer.prev_result(), false); - assert_eq!(timer.get_interrupt(), false); + assert_eq!(interrupts.get(Interrupt::Timer), false); } #[test] fn test_tima_overflow() { let mut timer = Timer::new(); + let mut interrupts = Interrupts::new(); timer.set_register(TIMER_CONTROL_ADDRESS, 0b101); timer.set_register(TIMER_COUNTER_ADDRESS, 0xFF); timer.set_div(0b10111); - timer.do_cycles(Cycles(9)); + timer.do_cycles(&mut interrupts, Cycles(9)); assert_eq!(timer.div(), 0b100000); assert_eq!(timer.get_register(TIMER_COUNTER_ADDRESS), 0x00); - assert_eq!(timer.get_interrupt(), true); + assert_eq!(interrupts.get(Interrupt::Timer), true); } #[test] fn test_timer_enable() { let mut timer = Timer::new(); + let mut interrupts = Interrupts::new(); timer.set_register(TIMER_CONTROL_ADDRESS, 0b101); timer.set_register(TIMER_COUNTER_ADDRESS, 0); timer.set_div(0b11000); - timer.do_cycles(Cycles(1)); + timer.do_cycles(&mut interrupts, Cycles(1)); assert_eq!(timer.div(), 0b11001); assert_eq!(timer.get_register(TIMER_COUNTER_ADDRESS), 0); 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.get_register(TIMER_COUNTER_ADDRESS), 1); }