diff --git a/src/cartridge.rs b/src/cartridge.rs index 8456d9e..b83b054 100644 --- a/src/cartridge.rs +++ b/src/cartridge.rs @@ -109,7 +109,8 @@ impl Cartridge { fn write_byte_none(&mut self, addr: u16, val: u8) { match addr { 0xA000 ... 0xBFFF => { - self.ram[addr as usize - 0xA000] = val; + // self.ram[addr as usize - 0xA000] = val; + println!("No MBC, ignoring RAM write {:02X} to {:04X}", val, addr); } _ => println!("No MBC, unable to write {:02X} to {:04X}", val, addr), } diff --git a/src/display.rs b/src/display.rs index 178359a..e98057d 100644 --- a/src/display.rs +++ b/src/display.rs @@ -28,17 +28,23 @@ const CTRL_BG_SPRITE_SIZE: u8 = 1 << 2; const CTRL_BG_SPRITE_ENABLE: u8 = 1 << 1; const CTRL_BG_DISPLAY: u8 = 1 << 0; +// Status flags +const STAT_LYC_LC_COINCIDENCE_INT: u8 = 1 << 6; +const STAT_MODE_OAM_INT: u8 = 1 << 5; +const STAT_MODE_VBLANK_INT: u8 = 1 << 4; +const STAT_MODE_HBLANK_INT: u8 = 1 << 3; + #[derive(Debug)] enum DisplayMode { - Scanline, - Readmode, + ReadOAMMemory, + ReadFullMemory, HBlank, VBlank, } impl Default for DisplayMode { fn default() -> DisplayMode { - DisplayMode::Scanline + DisplayMode::HBlank } } @@ -67,7 +73,6 @@ pub struct Display { pub event_pump: sdl2::EventPump, vblank_fired: bool, - stat_fired: bool, vblank_interrupt: bool, stat_interrupt: bool, } @@ -102,7 +107,6 @@ impl Display { event_pump: event_pump, vblank_fired: false, - stat_fired: false, vblank_interrupt: false, stat_interrupt: false, } @@ -179,52 +183,76 @@ impl Display { // Do we want to have a time delta here? pub fn tick(&mut self, ticks: u16) { - self.current_ticks += ticks; self.status &= 0xFC; + if self.control & CTRL_LCD_DISPLAY_ENABLE == 0 { + // Display is disabled + self.current_ticks = 0; + self.current_mode = DisplayMode::VBlank; + self.curline = 0; + return; + } + + self.current_ticks += ticks; match self.current_mode { - DisplayMode::Scanline => { + DisplayMode::ReadOAMMemory => { // Mode 2, Reading OAM memory, RAM may be accessed. if self.current_ticks > TICKS_END_SCANLINE { - self.vblank_fired = false; - self.stat_fired = false; self.current_ticks = 0; - self.current_mode = DisplayMode::Readmode; + self.current_mode = DisplayMode::ReadFullMemory; } - self.status |= 3; + self.status |= 2; }, - DisplayMode::Readmode => { + DisplayMode::ReadFullMemory => { // Mode 3, reading OAM, VMEM and palette data. + // Nothing may be accessed. if self.current_ticks > TICKS_END_READMODE { self.current_ticks = 0; self.current_mode = DisplayMode::HBlank; self.renderscan(); + if self.status & STAT_MODE_HBLANK_INT > 0 { + self.stat_interrupt = true; + } } - self.status |= 2; + self.status |= 3; }, - DisplayMode::HBlank => { + DisplayMode::HBlank => { // Mode 0, H-Blank, Memory (RAM, OAM) may be accessed. if self.current_ticks > TICKS_END_HBLANK { self.current_ticks = 0; self.curline += 1; + // render scan? if self.curline == 143 { - self.current_mode = DisplayMode::VBlank; - self.vblank_interrupt = true; - self.vblank_fired = true; // We don't need this, do we? + self.current_mode = DisplayMode::VBlank; // To Mode 1 + if self.status & STAT_MODE_VBLANK_INT > 0 { + self.stat_interrupt = true; + } + + // render frame. + self.renderer.present(); + self.renderer.set_draw_color(sdl2::pixels::Color::RGB(0, 0, 0)); + self.renderer.clear(); } else { - self.current_mode = DisplayMode::Scanline; + self.current_mode = DisplayMode::ReadOAMMemory; // Mode 2 again + if self.status & STAT_MODE_OAM_INT > 0 { + self.stat_interrupt = true; + } } } self.status &= 0xFC; self.status |= 0; }, - DisplayMode::VBlank => { + DisplayMode::VBlank => { // Mode 1, V-Blank (or display disabled), Memory (RAM, OAM) + // may be accessed if self.current_ticks > TICKS_END_VBLANK { self.current_ticks = 0; self.curline += 1; + if self.curline == 144 { + self.vblank_interrupt = true; + } if self.curline > 153 { - self.renderer.present(); - self.renderer.set_draw_color(sdl2::pixels::Color::RGB(0, 0, 0)); - self.renderer.clear(); - self.current_mode = DisplayMode::Scanline; + self.current_mode = DisplayMode::ReadOAMMemory; // Mode 2, scanline. self.curline = 0; + if self.status & STAT_MODE_OAM_INT > 0 { + self.stat_interrupt = true; + } } } self.status |= 1; @@ -234,8 +262,9 @@ impl Display { // Update the status register if self.curline == self.lyc { self.status |= 1 << 2; - self.stat_fired = true; - self.stat_interrupt = true; + if self.status & STAT_LYC_LC_COINCIDENCE_INT > 0 { + self.stat_interrupt = true; + } } else { self.status &= !(1 << 2); }