fix: allow for the ppu to modify interrupt flags

This commit is contained in:
Rekai Nyangadzayi Musuka 2021-03-20 19:53:56 -05:00
parent f45c2439c1
commit f365633c1e
2 changed files with 67 additions and 3 deletions

View File

@ -1,7 +1,7 @@
use super::cartridge::Cartridge;
use super::high_ram::HighRam;
use super::instruction::Cycles;
use super::interrupt::Interrupt;
use super::interrupt::{Interrupt, InterruptFlag};
use super::ppu::Ppu;
use super::serial::Serial;
use super::sound::Sound;
@ -118,7 +118,7 @@ impl Bus {
0xFF01 => self.serial.next,
0xFF02 => self.serial.control.into(),
0xFF07 => self.timer.control.into(),
0xFF0F => self.interrupt.flag.into(),
0xFF0F => self.interrupt_flag().into(),
0xFF11 => self.sound.ch1.sound_duty.into(),
0xFF12 => self.sound.ch1.vol_envelope.into(),
0xFF14 => self.sound.ch1.freq_hi.into(),
@ -203,7 +203,7 @@ impl Bus {
0xFF01 => self.serial.next = byte,
0xFF02 => self.serial.control = byte.into(),
0xFF07 => self.timer.control = byte.into(),
0xFF0F => self.interrupt.flag = byte.into(),
0xFF0F => self.set_interrupt_flag(byte),
0xFF11 => self.sound.ch1.sound_duty = byte.into(),
0xFF12 => self.sound.ch1.vol_envelope = byte.into(),
0xFF13 => self.sound.ch1.freq_lo = byte.into(),
@ -251,3 +251,39 @@ impl Bus {
self.write_byte(addr, (word & 0x00FF) as u8);
}
}
impl Bus {
fn interrupt_flag(&self) -> InterruptFlag {
// Read the current Interrupt status from the PPU
let ppu_vblank = self.ppu.interrupt.vblank();
let ppu_lcd_stat = self.ppu.interrupt.lcd_stat();
// We actually don't care about what the InterruptFlag currently says
// about vblank and lcd_stat, because the PPU has the more accurate
// knowledge about what state these interrupt flags are in.
// In order to have the PPU be the source of truth
// (and accounting for the fact that we aren't able to update)
// the interrupt flag register 0xFF0F in the method, we can
// mask over those two interruptss
// Copy the Interrupt Flag register 0xFF0F
let mut flag = self.interrupt.flag;
flag.set_vblank(ppu_vblank);
flag.set_lcd_stat(ppu_lcd_stat);
flag
}
fn set_interrupt_flag(&mut self, byte: u8) {
// Update the Interrupt register 0xFF0F
self.interrupt.flag = byte.into();
let vblank = self.interrupt.flag.vblank();
let lcd_stat = self.interrupt.flag.lcd_stat();
// Update the PPU's internal tracking of the interrupt register 0xFF0F
self.ppu.interrupt.set_vblank(vblank);
self.ppu.interrupt.set_lcd_stat(lcd_stat);
}
}

View File

@ -5,6 +5,7 @@ const GB_WIDTH: usize = 160;
const GB_HEIGHT: usize = 144;
#[derive(Debug, Clone)]
pub struct Ppu {
pub interrupt: Interrupt,
pub lcd_control: LCDControl,
pub monochrome: Monochrome,
pub pos: ScreenPosition,
@ -60,6 +61,7 @@ impl Ppu {
self.pos.line_y += 1;
let next_mode = if self.pos.line_y >= 144 {
self.interrupt.set_vblank(true);
Mode::VBlank
} else {
Mode::OamScan
@ -138,6 +140,7 @@ impl Ppu {
impl Default for Ppu {
fn default() -> Self {
Self {
interrupt: Interrupt::default(),
lcd_control: Default::default(),
monochrome: Default::default(),
pos: Default::default(),
@ -149,6 +152,31 @@ impl Default for Ppu {
}
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Interrupt {
_vblank: bool,
_lcd_stat: bool,
}
impl Interrupt {
pub fn vblank(&self) -> bool {
self._vblank
}
pub fn set_vblank(&mut self, enabled: bool) {
self._vblank = enabled;
}
pub fn lcd_stat(&self) -> bool {
self._lcd_stat
}
pub fn set_lcd_stat(&mut self, enabled: bool) {
self._lcd_stat = enabled;
}
}
bitfield! {
pub struct LCDStatus(u8);
impl Debug;