diff --git a/src/main.rs b/src/main.rs index 651a617..ca553e0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,7 +11,7 @@ use std::{ sync::RwLock, }; -use crate::processor::Registers; +use crate::processor::{gpu::GPU, Registers}; #[macro_export] macro_rules! verbose_println { @@ -257,14 +257,14 @@ fn main() { *v = args.verbose; } - let buffer: Vec = vec![0; WIDTH * HEIGHT]; + // let buffer: Vec = ; - let mut window = Window::new("Gameboy", WIDTH, HEIGHT, WindowOptions::default()) - .unwrap_or_else(|e| { + let window = + Window::new("Gameboy", WIDTH, HEIGHT, WindowOptions::default()).unwrap_or_else(|e| { panic!("{}", e); }); - window.update_with_buffer(&buffer, WIDTH, HEIGHT).unwrap(); + window.topmost(true); let rom: ROM = fs::read(args.rom).expect("Could not load ROM"); let bootrom: ROM = fs::read(args.bootrom).expect("Could not load BootROM"); @@ -278,10 +278,8 @@ fn main() { reg, last_instruction: 0x0, last_instruction_addr: 0x0, - vblank_timer: 0., - vblank_remaining: 0, window, - buffer, + gpu: GPU::default(), }; cpu_ram_init(&mut cpu); let mut cycle_num = 0; diff --git a/src/processor/gpu.rs b/src/processor/gpu.rs new file mode 100644 index 0000000..9438b7b --- /dev/null +++ b/src/processor/gpu.rs @@ -0,0 +1,112 @@ +use crate::{HEIGHT, WIDTH}; + +use super::{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.gpu.mode = DrawMode::Mode2; + self.gpu.scanline = 0; + } + } + } + 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 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(); + } +} diff --git a/src/processor/mod.rs b/src/processor/mod.rs index 96d8856..cddc1c6 100644 --- a/src/processor/mod.rs +++ b/src/processor/mod.rs @@ -4,6 +4,9 @@ use minifb::Window; use crate::{processor::instructions::instructions::set, verbose_println, Memory}; +use self::gpu::GPU; + +pub mod gpu; mod instructions; mod opcodes; @@ -25,16 +28,14 @@ pub struct CPU { pub reg: Registers, pub last_instruction: u8, pub last_instruction_addr: u16, - pub vblank_timer: f64, - pub vblank_remaining: u8, pub window: Window, - pub buffer: Vec, + pub gpu: GPU, } // Hz const CLOCK_SPEED: f64 = 4.194304 * 1000000.; const SPEEDUP: f64 = 1.; -const FF04_SPEED: f32 = 16384.; +const FF04_SPEED: f64 = 16384.; impl CPU { pub fn exec_next(&mut self) { @@ -49,11 +50,11 @@ impl CPU { } } - if self.vblank_timer >= (1. / 59.7) { - self.vblank_timer = 0.; - self.vblank_remaining = 20; - self.memory.set(0xFF0F, set_bit(self.memory.get(0xFF0F), 0)); - } + // if self.vblank_timer >= (1. / 59.7) { + // self.vblank_timer = 0.; + // self.vblank_remaining = 20; + // self.memory.set(0xFF0F, set_bit(self.memory.get(0xFF0F), 0)); + // } verbose_println!( "exec {:#4X} from pc: {:#X}", @@ -69,22 +70,23 @@ impl CPU { fn increment_timers(&mut self, cycles: u8) { let secs = (cycles * 4) as f64 / CLOCK_SPEED; + self.advance_gpu_clock(cycles); - self.vblank_timer += secs; - if self.vblank_remaining > 0 { - self.vblank_remaining = self.vblank_remaining.saturating_sub(cycles); - if self.vblank_remaining == 0 { - self.memory - .set(0xFF0F, clear_bit(self.memory.get(0xFF0F), 0)); - self.draw(); - } - } + // self.vblank_timer += secs; + // if self.vblank_remaining > 0 { + // self.vblank_remaining = self.vblank_remaining.saturating_sub(cycles); + // if self.vblank_remaining == 0 { + // self.memory + // .set(0xFF0F, clear_bit(self.memory.get(0xFF0F), 0)); + // self.draw(); + // } + // } self.memory.set( 0xFF04, self.memory .get(0xFF04) - .wrapping_add((FF04_SPEED * 16384.) as u8), + .wrapping_add((FF04_SPEED * secs) as u8), ); let (timer_enabled, timer_rate) = self.timer_scale(); if timer_enabled { @@ -102,8 +104,6 @@ impl CPU { spin_sleep::sleep(Duration::from_secs_f64(secs / SPEEDUP)); } - fn draw(&mut self) {} - fn next_opcode(&mut self) -> u8 { let opcode = self.memory.get(self.reg.pc); self.reg.pc = self.reg.pc.wrapping_add(0x1); @@ -269,6 +269,14 @@ fn get_bit(byte: u8, flag: u8) -> bool { return got > 0x0; } +fn set_or_clear_bit(byte: u8, flag: u8, condition: bool) -> u8 { + if condition { + set_bit(byte, flag) + } else { + clear_bit(byte, flag) + } +} + fn set_bit(byte: u8, flag: u8) -> u8 { byte | (1 << flag) }