use crate::{HEIGHT, WIDTH}; use super::{clear_bit, set_bit, set_or_clear_bit, CPU}; #[derive(PartialEq)] enum DrawMode { HBlank, VBlank, Mode2, Mode3, } pub struct GPU { pub buffer: Vec, mode: DrawMode, mode_clock: usize, scanline: usize, } impl Default for GPU { fn default() -> Self { Self { buffer: vec![0; WIDTH * HEIGHT], mode: DrawMode::Mode2, mode_clock: 0, scanline: 0, } } } impl CPU { pub fn advance_gpu_clock(&mut self, steps: u8) { let real_steps = (steps as usize) * 4; self.gpu.mode_clock += real_steps; match self.gpu.mode { DrawMode::HBlank => { // mode 0: hblank if self.gpu.mode_clock >= 204 { self.gpu.mode_clock = 0; self.gpu.scanline += 1; if self.gpu.scanline == 143 { self.enter_vblank(); } else { self.gpu.mode = DrawMode::Mode2; } } } DrawMode::VBlank => { // mode 1: vblank if self.gpu.mode_clock >= 456 { self.gpu.mode_clock = 0; self.gpu.scanline += 1; if self.gpu.scanline == 153 { self.exit_vblank(); } } } DrawMode::Mode2 => { // search oam for sprites on this line // we dont really have to emulate this if self.gpu.mode_clock >= 80 { self.gpu.mode_clock = 0; self.gpu.mode = DrawMode::Mode3; } } DrawMode::Mode3 => { // generate scanline if self.gpu.mode_clock >= 172 { self.gpu.mode_clock = 0; self.enter_hblank(); } } } self.set_lcd_status(); } fn enter_hblank(&mut self) { self.gpu.mode = DrawMode::HBlank; self.render_scanline(self.gpu.scanline); } fn enter_vblank(&mut self) { self.gpu.mode = DrawMode::VBlank; self.render_window(); self.memory.set(0xFF0F, set_bit(self.memory.get(0xFF0F), 0)); } fn exit_vblank(&mut self) { self.gpu.mode = DrawMode::Mode2; self.gpu.scanline = 0; self.memory .set(0xFF0F, clear_bit(self.memory.get(0xFF0F), 0)); } fn set_lcd_status(&mut self) { let mut stat = self.memory.get(0xFF41); stat = set_or_clear_bit(stat, 2, self.memory.get(0xFF44) == self.memory.get(0xFF45)); stat = set_or_clear_bit( stat, 1, (self.gpu.mode == DrawMode::Mode2) || (self.gpu.mode == DrawMode::Mode3), ); stat = set_or_clear_bit( stat, 0, (self.gpu.mode == DrawMode::VBlank) || (self.gpu.mode == DrawMode::Mode3), ); self.memory.set(0xFF41, stat); } fn render_scanline(&mut self, _scanline: usize) {} fn render_window(&mut self) { self.window .update_with_buffer(&self.gpu.buffer, WIDTH, HEIGHT) .unwrap(); } }