fix: allow for the ppu to modify interrupt flags
This commit is contained in:
parent
f45c2439c1
commit
f365633c1e
42
src/bus.rs
42
src/bus.rs
|
@ -1,7 +1,7 @@
|
||||||
use super::cartridge::Cartridge;
|
use super::cartridge::Cartridge;
|
||||||
use super::high_ram::HighRam;
|
use super::high_ram::HighRam;
|
||||||
use super::instruction::Cycles;
|
use super::instruction::Cycles;
|
||||||
use super::interrupt::Interrupt;
|
use super::interrupt::{Interrupt, InterruptFlag};
|
||||||
use super::ppu::Ppu;
|
use super::ppu::Ppu;
|
||||||
use super::serial::Serial;
|
use super::serial::Serial;
|
||||||
use super::sound::Sound;
|
use super::sound::Sound;
|
||||||
|
@ -118,7 +118,7 @@ impl Bus {
|
||||||
0xFF01 => self.serial.next,
|
0xFF01 => self.serial.next,
|
||||||
0xFF02 => self.serial.control.into(),
|
0xFF02 => self.serial.control.into(),
|
||||||
0xFF07 => self.timer.control.into(),
|
0xFF07 => self.timer.control.into(),
|
||||||
0xFF0F => self.interrupt.flag.into(),
|
0xFF0F => self.interrupt_flag().into(),
|
||||||
0xFF11 => self.sound.ch1.sound_duty.into(),
|
0xFF11 => self.sound.ch1.sound_duty.into(),
|
||||||
0xFF12 => self.sound.ch1.vol_envelope.into(),
|
0xFF12 => self.sound.ch1.vol_envelope.into(),
|
||||||
0xFF14 => self.sound.ch1.freq_hi.into(),
|
0xFF14 => self.sound.ch1.freq_hi.into(),
|
||||||
|
@ -203,7 +203,7 @@ impl Bus {
|
||||||
0xFF01 => self.serial.next = byte,
|
0xFF01 => self.serial.next = byte,
|
||||||
0xFF02 => self.serial.control = byte.into(),
|
0xFF02 => self.serial.control = byte.into(),
|
||||||
0xFF07 => self.timer.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(),
|
0xFF11 => self.sound.ch1.sound_duty = byte.into(),
|
||||||
0xFF12 => self.sound.ch1.vol_envelope = byte.into(),
|
0xFF12 => self.sound.ch1.vol_envelope = byte.into(),
|
||||||
0xFF13 => self.sound.ch1.freq_lo = byte.into(),
|
0xFF13 => self.sound.ch1.freq_lo = byte.into(),
|
||||||
|
@ -251,3 +251,39 @@ impl Bus {
|
||||||
self.write_byte(addr, (word & 0x00FF) as u8);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
28
src/ppu.rs
28
src/ppu.rs
|
@ -5,6 +5,7 @@ const GB_WIDTH: usize = 160;
|
||||||
const GB_HEIGHT: usize = 144;
|
const GB_HEIGHT: usize = 144;
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Ppu {
|
pub struct Ppu {
|
||||||
|
pub interrupt: Interrupt,
|
||||||
pub lcd_control: LCDControl,
|
pub lcd_control: LCDControl,
|
||||||
pub monochrome: Monochrome,
|
pub monochrome: Monochrome,
|
||||||
pub pos: ScreenPosition,
|
pub pos: ScreenPosition,
|
||||||
|
@ -60,6 +61,7 @@ impl Ppu {
|
||||||
self.pos.line_y += 1;
|
self.pos.line_y += 1;
|
||||||
|
|
||||||
let next_mode = if self.pos.line_y >= 144 {
|
let next_mode = if self.pos.line_y >= 144 {
|
||||||
|
self.interrupt.set_vblank(true);
|
||||||
Mode::VBlank
|
Mode::VBlank
|
||||||
} else {
|
} else {
|
||||||
Mode::OamScan
|
Mode::OamScan
|
||||||
|
@ -138,6 +140,7 @@ impl Ppu {
|
||||||
impl Default for Ppu {
|
impl Default for Ppu {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
interrupt: Interrupt::default(),
|
||||||
lcd_control: Default::default(),
|
lcd_control: Default::default(),
|
||||||
monochrome: Default::default(),
|
monochrome: Default::default(),
|
||||||
pos: 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! {
|
bitfield! {
|
||||||
pub struct LCDStatus(u8);
|
pub struct LCDStatus(u8);
|
||||||
impl Debug;
|
impl Debug;
|
||||||
|
|
Loading…
Reference in New Issue