From 558f9e7c726825813b0e3e208b0f7ca21b08de49 Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Thu, 18 Mar 2021 21:06:01 -0500 Subject: [PATCH] feat: implement cpu interrupts --- src/bus.rs | 6 ++-- src/cpu.rs | 84 ++++++++++++++++++++++++++++++++++++++++------ src/instruction.rs | 2 +- src/interrupt.rs | 83 +++------------------------------------------ src/main.rs | 9 +++-- 5 files changed, 86 insertions(+), 98 deletions(-) diff --git a/src/bus.rs b/src/bus.rs index 33b8206..f0ed21f 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -59,9 +59,9 @@ impl Bus { } pub fn step(&mut self, cycles: Cycles) { - let _ = self.timer.step(cycles); - let _ = self.sound.step(cycles); - let _ = self.ppu.step(cycles); + self.timer.step(cycles); + self.sound.step(cycles); + self.ppu.step(cycles); } } diff --git a/src/cpu.rs b/src/cpu.rs index 3b65951..738bb48 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -1,11 +1,9 @@ use super::bus::Bus; -use super::instruction::{Cycles, Instruction}; +use super::instruction::{Cycles, Instruction, JumpCondition}; +use super::interrupt::{InterruptEnable, InterruptFlag}; use super::ppu::Ppu; use bitfield::bitfield; -use std::{ - fmt::{Display, Formatter, Result as FmtResult}, - io::Write, -}; +use std::fmt::{Display, Formatter, Result as FmtResult}; #[derive(Debug, Clone, Default)] pub struct Cpu { @@ -86,16 +84,18 @@ impl Cpu { let cycles = self.execute(instr); - if self.bus.read_byte(0xFF02) == 0x81 { - let c = self.bus.read_byte(0xFF01) as char; - self.bus.write_byte(0xFF02, 0x00); + // if self.bus.read_byte(0xFF02) == 0x81 { + // let c = self.bus.read_byte(0xFF01) as char; + // self.bus.write_byte(0xFF02, 0x00); - print!("{}", c); - std::io::stdout().flush().unwrap(); - } + // print!("{}", c); + // std::io::stdout().flush().unwrap(); + // } self.bus.step(cycles); + self.handle_interrupts(); + cycles } } @@ -133,6 +133,68 @@ impl Cpu { pub fn get_ppu(&mut self) -> &mut Ppu { &mut self.bus.ppu } + + pub fn handle_interrupts(&mut self) { + if self.ime() { + use JumpCondition::Always; + + let mut req: InterruptFlag = self.read_byte(0xFF0F).into(); + let mut enabled: InterruptEnable = self.read_byte(0xFFFF).into(); + + let vector = if req.vblank() && enabled.vblank() { + // Handle VBlank Interrupt + req.set_vblank(false); + enabled.set_vblank(false); + + // INT 40h + Some(0x0040) + } else if req.lcd_stat() && enabled.lcd_stat() { + // Handle LCD STAT Interrupt + req.set_lcd_stat(false); + enabled.set_lcd_stat(false); + + // INT 48h + Some(0x0048) + } else if req.timer() && enabled.timer() { + // Handle Timer Interrupt + req.set_timer(false); + enabled.set_timer(false); + + // INT 50h + Some(0x0050) + } else if req.serial() && enabled.serial() { + // Handle Serial Interrupt + req.set_serial(false); + enabled.set_serial(false); + + // INT 58h + Some(0x0058) + } else if req.joypad() && enabled.joypad() { + // Handle Joypad Interrupt + req.set_joypad(false); + enabled.set_joypad(false); + + // INT 60h + Some(0x0060) + } else { + None + }; + + let _ = match vector { + Some(register) => { + // Write the Changes to 0xFF0F and 0xFFFF registers + self.write_byte(0xFF0F, req.into()); + self.write_byte(0xFFFF, enabled.into()); + + // Disable all future interrupts + self.set_ime(false); + + self.execute(Instruction::CALL(Always, register)) + } + None => Cycles::new(0), // NO Interrupts were enabled and / or requested + }; + } + } } #[derive(Debug, Copy, Clone)] diff --git a/src/instruction.rs b/src/instruction.rs index c878eb6..6c395aa 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -1017,7 +1017,7 @@ impl Instruction { Cycles::new(4) } Instruction::CALL(cond, nn) => { - // CALL cc[y], nn | Store nn on the stack, then store nn in the program counter if cond is met + // CALL cc[y], nn | Store pc on the stack, then store nn in the program counter if cond is met // CALL nn | Store nn on the stack, then store nn in the program counter let flags: &Flags = cpu.flags(); let pc = cpu.register_pair(RegisterPair::PC); diff --git a/src/interrupt.rs b/src/interrupt.rs index 0f5afff..26cf5d8 100644 --- a/src/interrupt.rs +++ b/src/interrupt.rs @@ -9,84 +9,11 @@ pub struct Interrupt { bitfield! { pub struct InterruptEnable(u8); impl Debug; - _vblank, _set_vblank: 0; - _lcd_stat, _set_lcd_stat: 1; - _timer, _set_timer: 2; - _serial, _set_serial: 3; - _joypad, _set_joypad: 4; -} - -// TODO: Is this the correct behaviour? (I think not) -impl InterruptEnable { - pub fn vblank(&self) -> bool { - self._vblank() - } - - pub fn lcd_stat(&self) -> bool { - self._lcd_stat() - } - - pub fn timer(&self) -> bool { - self._timer() - } - - pub fn serial(&self) -> bool { - self._serial() - } - - pub fn joypad(&self) -> bool { - self._joypad() - } - - pub fn set_vblank(&self, flag: &mut InterruptFlag, value: bool) { - let prev = self._vblank(); - - if !prev && value { - flag.set_vblank(true); - } - - flag.set_vblank(value) - } - - pub fn set_lcd_stat(&self, flag: &mut InterruptFlag, value: bool) { - let prev = self._lcd_stat(); - - if !prev && value { - flag.set_lcd_stat(true); - } - - flag.set_lcd_stat(value) - } - - pub fn set_timer(&self, flag: &mut InterruptFlag, value: bool) { - let prev = self._timer(); - - if !prev && value { - flag.set_timer(true); - } - - flag.set_timer(value) - } - - pub fn set_serial(&self, flag: &mut InterruptFlag, value: bool) { - let prev = self._serial(); - - if !prev && value { - flag.set_serial(true); - } - - flag.set_serial(value) - } - - pub fn set_joypad(&self, flag: &mut InterruptFlag, value: bool) { - let prev = self._joypad(); - - if !prev && value { - flag.set_joypad(true); - } - - flag.set_joypad(value) - } + pub vblank, set_vblank: 0; + pub lcd_stat, set_lcd_stat: 1; + pub timer, set_timer: 2; + pub serial, set_serial: 3; + pub joypad, set_joypad: 4; } impl Copy for InterruptEnable {} diff --git a/src/main.rs b/src/main.rs index 7a63d97..7fc8085 100644 --- a/src/main.rs +++ b/src/main.rs @@ -30,11 +30,6 @@ fn main() -> Result<()> { event_loop.run(move |event, _, control_flow| { if let Event::RedrawRequested(_) = event { - let ppu = game_boy.get_ppu(); - let frame = pixels.get_frame(); - - ppu.copy_to_gui(frame); - if pixels .render() .map_err(|e| anyhow!("pixels.render() failed: {}", e)) @@ -57,6 +52,10 @@ fn main() -> Result<()> { // Emulation let _cycles = game_boy.step(); + + let ppu = game_boy.get_ppu(); + let frame = pixels.get_frame(); + ppu.copy_to_gui(frame); window.request_redraw(); } });