From 2c93357907407c70213eb71bf3532413da9b67b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Magalh=C3=A3es?= <joamag@gmail.com> Date: Thu, 30 Jun 2022 01:13:19 +0100 Subject: [PATCH] feat: initial GPU clock support --- src/cpu.rs | 25 ++++++++++++++-- src/gb.rs | 10 +++---- src/mmu.rs | 4 +-- src/ppu.rs | 83 ++++++++++++++++++++++++++++++++++++++++++++++++------ 4 files changed, 103 insertions(+), 19 deletions(-) diff --git a/src/cpu.rs b/src/cpu.rs index 55fb7f15..bbdb384b 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -1,4 +1,4 @@ -use crate::mmu::Mmu; +use crate::{mmu::Mmu, ppu::Ppu}; pub const PREFIX: u8 = 0xcb; @@ -525,7 +525,7 @@ impl Cpu { instruction = &INSTRUCTIONS[opcode as usize]; } - let (instruction_fn, instruction_size, instruction_str) = instruction; + let (instruction_fn, instruction_time, instruction_str) = instruction; println!( "{}\t(0x{:02x})\t${:04x} {}", @@ -533,7 +533,11 @@ impl Cpu { ); instruction_fn(self); - self.ticks = self.ticks.wrapping_add(*instruction_size as u32); + self.ticks = self.ticks.wrapping_add(*instruction_time as u32); + + // calls the clock in the PPU to update its own + // execution lifecycle by one set of ticks + self.ppu().clock(*instruction_time); } #[inline(always)] @@ -541,11 +545,26 @@ impl Cpu { &mut self.mmu } + #[inline(always)] + pub fn ppu(&mut self) -> &mut Ppu { + self.mmu().ppu() + } + + #[inline(always)] + pub fn ticks(&self) -> u32 { + self.ticks + } + #[inline(always)] pub fn pc(&self) -> u16 { self.pc } + #[inline(always)] + pub fn sp(&self) -> u16 { + self.sp + } + #[inline(always)] fn af(&self) -> u16 { (self.a as u16) << 8 | self.f() as u16 diff --git a/src/gb.rs b/src/gb.rs index 6d6739c0..aa723ef2 100644 --- a/src/gb.rs +++ b/src/gb.rs @@ -16,16 +16,16 @@ impl GameBoy { self.cpu.clock() } - pub fn cpu(&self) -> &Cpu { - &self.cpu + pub fn cpu(&mut self) -> &mut Cpu { + &mut self.cpu } - pub fn mmu(&mut self) -> &Mmu { + pub fn mmu(&mut self) -> &mut Mmu { self.cpu.mmu() } - pub fn ppu(&mut self) -> &Ppu { - self.mmu().ppu() + pub fn ppu(&mut self) -> &mut Ppu { + self.cpu.ppu() } pub fn load_boot(&mut self, path: &str) { diff --git a/src/mmu.rs b/src/mmu.rs index 758f9537..ce015cb4 100644 --- a/src/mmu.rs +++ b/src/mmu.rs @@ -26,8 +26,8 @@ impl Mmu { } } - pub fn ppu(&self) -> &Ppu { - &self.ppu + pub fn ppu(&mut self) -> &mut Ppu { + &mut self.ppu } pub fn read(&mut self, addr: u16) -> u8 { diff --git a/src/ppu.rs b/src/ppu.rs index adcb4322..cc3c4cea 100644 --- a/src/ppu.rs +++ b/src/ppu.rs @@ -1,24 +1,63 @@ +use crate::cpu::Cpu; + pub const VRAM_SIZE: usize = 8192; pub const HRAM_SIZE: usize = 128; pub const PALETTE_SIZE: usize = 4; pub const RGBA_SIZE: usize = 4; +pub const SCREEN_WIDTH: usize = 160; +pub const SCREEN_HEIGHT: usize = 154; +/// Represents the Game Boy PPU (Pixel Processing Unit) and controls +/// all of the logic behind the graphics processing and presentation. +/// Should store both the VRAM and HRAM together with the internal +/// graphic related registers. +/// Outputs the screen as a monochromatic 8 bit frame buffer. +/// +/// # Basic usage +/// ```rust +/// let ppu = Ppu::new(); +/// ppu.tick(); +/// ``` pub struct Ppu { + /// The 8 bit based monochromatic frame buffer with the + /// processed set of pixels ready to be displayed on screen. + pub frame_buffer: [u8; SCREEN_WIDTH * SCREEN_HEIGHT], + /// Video dedicated memory (VRAM) where both the tiles and + /// the sprites are going to be stored. pub vram: [u8; VRAM_SIZE], pub hram: [u8; HRAM_SIZE], - pub palette: [[u8; RGBA_SIZE]; PALETTE_SIZE], - pub scy: u8, - pub scx: u8, - pub line: u8, - pub switch_bg: bool, - pub bg_map: bool, - pub bg_tile: bool, - pub switch_lcd: bool, + palette: [[u8; RGBA_SIZE]; PALETTE_SIZE], + /// The scroll Y register that controls the Y offset + /// of the background. + scy: u8, + /// The scroll X register that controls the X offset + /// of the background. + scx: u8, + /// The current scan line in processing, should + /// range between 0 (0x00) and 153 (0x99), representing + /// the 154 lines plus 10 extra v-blank lines. + line: u8, + switch_bg: bool, + bg_map: bool, + bg_tile: bool, + switch_lcd: bool, + mode: PpuMode, + /// Internal clock counter used to control the time in ticks + /// spent in each of the PPU modes. + mode_clock: u16, +} + +pub enum PpuMode { + OamRead, + VramRead, + Hblank, + VBlank, } impl Ppu { pub fn new() -> Ppu { Ppu { + frame_buffer: [0u8; SCREEN_WIDTH * SCREEN_HEIGHT], vram: [0u8; VRAM_SIZE], hram: [0u8; HRAM_SIZE], palette: [[0u8; RGBA_SIZE]; PALETTE_SIZE], @@ -29,10 +68,32 @@ impl Ppu { bg_map: false, bg_tile: false, switch_lcd: false, + mode: PpuMode::OamRead, + mode_clock: 0, } } - pub fn clock() {} + pub fn clock(&mut self, ticks: u8) { + self.mode_clock += ticks as u16; + match self.mode { + PpuMode::OamRead => { + if self.mode_clock >= 204 { + self.mode_clock = 0; + self.mode = PpuMode::VramRead; + } + } + PpuMode::VramRead => { + if self.mode_clock >= 172 { + self.render_line(); + + self.mode_clock = 0; + self.mode = PpuMode::Hblank; + } + } + PpuMode::Hblank => todo!(), + PpuMode::VBlank => todo!(), + } + } pub fn read(&mut self, addr: u16) -> u8 { match addr & 0x00ff { @@ -74,4 +135,8 @@ impl Ppu { addr => panic!("Writing in unknown PPU location 0x{:04x}", addr), } } + + fn render_line(&self) { + //@todo implement the rendering of a line + } } -- GitLab