diff --git a/snes-core/src/ppu/interface.rs b/snes-core/src/ppu/interface.rs index 60e6dbe..d7920b2 100644 --- a/snes-core/src/ppu/interface.rs +++ b/snes-core/src/ppu/interface.rs @@ -12,7 +12,7 @@ pub struct PPU { impl PPU { pub fn new() -> Self { Self { - framebuffer: vec![0; FRAMEBUFFER_SIZE], + framebuffer: vec![0xFF; FRAMEBUFFER_SIZE], registers: PPURegisters::new(), was_vblank_nmi_set: false, is_irq_set: false, @@ -26,6 +26,9 @@ impl PPU { } pub fn dot_cycle(&mut self) { + if !self.registers.is_vblanking() && !self.registers.is_hblanking() { + self.put_pixel(self.compute_pixel()) + } self.increment_hv_count(); } @@ -51,6 +54,29 @@ impl PPU { pub fn framebuffer(&self) -> &[u8] { &self.framebuffer } + + fn compute_pixel(&self) -> (u8, u8, u8) { + if self.registers.is_hblanking() || self.registers.is_hblanking() { + return (0x00, 0x00, 0x00) + } + (0xFF, 0x00, 0xFF) + } + + fn put_pixel(&mut self, pixel: (u8, u8, u8)) { + let fb_index = self.get_pixel_index(); + self.framebuffer[fb_index] = pixel.0; + self.framebuffer[fb_index + 1] = pixel.1; + self.framebuffer[fb_index + 2] = pixel.2; + } + + fn get_pixel_index(&self) -> usize { + let h_count = self.registers.h_count as usize; + let v_count = self.registers.v_count as usize; + return ( + (h_count - 22) + + ((v_count - 1) * MAX_TV_WIDTH) + ) * 4 + } } impl Default for PPU { @@ -81,4 +107,38 @@ mod ppu_general_test { assert_eq!(ppu.registers.h_count, 0); assert_eq!(ppu.registers.v_count, 0); } -} \ No newline at end of file + + #[test] + fn test_get_current_pixel_index() { + let mut ppu = PPU::new(); + ppu.registers.v_count = 1; + ppu.registers.h_count = 22; + assert_eq!(ppu.get_pixel_index(), 0); + ppu.registers.v_count = 1; + ppu.registers.h_count = 23; + assert_eq!(ppu.get_pixel_index(), 4); + ppu.registers.v_count = 1; + ppu.registers.h_count = 24; + assert_eq!(ppu.get_pixel_index(), 8); + ppu.registers.v_count = 2; + ppu.registers.h_count = 22; + assert_eq!(ppu.get_pixel_index(), 2048); + ppu.registers.v_count = 2; + ppu.registers.h_count = 23; + assert_eq!(ppu.get_pixel_index(), 2052); + ppu.registers.v_count = 2; + ppu.registers.h_count = 24; + assert_eq!(ppu.get_pixel_index(), 2056); + } + + #[test] + fn test_put_pixel() { + let mut ppu = PPU::new(); + ppu.registers.v_count = 2; + ppu.registers.h_count = 22; + ppu.put_pixel((11, 22, 33)); + assert_eq!(ppu.framebuffer[2048], 11); + assert_eq!(ppu.framebuffer[2049], 22); + assert_eq!(ppu.framebuffer[2050], 33); + } +} diff --git a/snes-core/src/ppu/registers.rs b/snes-core/src/ppu/registers.rs index 17d3c0b..52aab98 100644 --- a/snes-core/src/ppu/registers.rs +++ b/snes-core/src/ppu/registers.rs @@ -375,10 +375,26 @@ impl PPURegisters { } pub fn is_vblanking(&self) -> bool { - if self.v_count >= 1 && self.v_count <= 224 { - return false - } - true + !(self.v_count >= 1 && self.v_count <= 224) + } + + pub fn is_hblanking(&self) -> bool { + !(self.h_count >= 22 && self.h_count <= 277) + } + + pub fn get_current_res(&self) -> (u16, u16) { + let w = if self.is_true_high_res_mode_enabled() {512} else {256}; + let h = if self.is_interlace_mode_enabled() {448} else {224}; + (w, h) + } + + pub fn is_interlace_mode_enabled(&self) -> bool { + self._read(SETINI) & 0x01 == 0b1 + } + + pub fn is_true_high_res_mode_enabled(&self) -> bool { + let current_bg_mode = self._read(BGMODE); + current_bg_mode == 5 || current_bg_mode == 6 } } @@ -590,4 +606,70 @@ mod ppu_registers_test { registers.v_count = 50; assert!(!registers.is_vblanking()); } + + #[test] + fn test_is_hblanking() { + let mut registers = PPURegisters::new(); + registers.h_count = 0; + assert!(registers.is_hblanking()); + registers.h_count = 20; + assert!(registers.is_hblanking()); + registers.h_count = 21; + assert!(registers.is_hblanking()); + registers.h_count = 278; + assert!(registers.is_hblanking()); + registers.h_count = 300; + assert!(registers.is_hblanking()); + registers.h_count = 22; + assert!(!registers.is_hblanking()); + registers.h_count = 100; + assert!(!registers.is_hblanking()); + registers.h_count = 277; + assert!(!registers.is_hblanking()); + } + + #[test] + fn test_is_interlace_mode_enabled() { + let mut registers = PPURegisters::new(); + registers._write(SETINI, 0x01); + assert!(registers.is_interlace_mode_enabled()); + registers._write(SETINI, 0x00); + assert!(!registers.is_interlace_mode_enabled()); + } + + #[test] + fn test_is_true_high_res_mode_enabled() { + let mut registers = PPURegisters::new(); + registers._write(BGMODE, 1); + assert!(!registers.is_true_high_res_mode_enabled()); + registers._write(BGMODE, 2); + assert!(!registers.is_true_high_res_mode_enabled()); + registers._write(BGMODE, 3); + assert!(!registers.is_true_high_res_mode_enabled()); + registers._write(BGMODE, 4); + assert!(!registers.is_true_high_res_mode_enabled()); + registers._write(BGMODE, 5); + assert!(registers.is_true_high_res_mode_enabled()); + registers._write(BGMODE, 6); + assert!(registers.is_true_high_res_mode_enabled()); + registers._write(BGMODE, 7); + assert!(!registers.is_true_high_res_mode_enabled()); + } + + #[test] + fn test_get_current_res() { + let mut registers = PPURegisters::new(); + registers._write(BGMODE, 1); + registers._write(SETINI, 0x00); + assert_eq!(registers.get_current_res(), (256, 224)); + registers._write(BGMODE, 1); + registers._write(SETINI, 0x01); + assert_eq!(registers.get_current_res(), (256, 448)); + registers._write(BGMODE, 5); + registers._write(SETINI, 0x00); + assert_eq!(registers.get_current_res(), (512, 224)); + registers._write(BGMODE, 6); + registers._write(SETINI, 0x01); + assert_eq!(registers.get_current_res(), (512, 448)); + } }