diff --git a/src/components/ppu/bgpi.rs b/src/components/ppu/bgpi.rs new file mode 100644 index 0000000..88c036e --- /dev/null +++ b/src/components/ppu/bgpi.rs @@ -0,0 +1,27 @@ +pub struct BGPI { + pub address: u8, + pub auto_increment: bool +} + +impl BGPI { + pub fn new() -> Self { + Self { + address: 0, + auto_increment: false + } + } + + pub fn read(&self) -> u8 { + let a = if self.auto_increment { + 0x80 + } else { + 0x00 + }; + a | self.address + } + + pub fn write(&mut self, v: u8) { + self.auto_increment = v & 0x80 != 0x00; + self.address = v & 0x3F; + } +} diff --git a/src/components/ppu/mod.rs b/src/components/ppu/mod.rs index 642b2a5..3391f6d 100644 --- a/src/components/ppu/mod.rs +++ b/src/components/ppu/mod.rs @@ -1,2 +1,4 @@ pub mod ppu; -mod cc; \ No newline at end of file +mod cc; +mod bgpi; +mod structs; \ No newline at end of file diff --git a/src/components/ppu/ppu.rs b/src/components/ppu/ppu.rs index d36cd46..a61db2a 100644 --- a/src/components/ppu/ppu.rs +++ b/src/components/ppu/ppu.rs @@ -1,8 +1,9 @@ use crate::components::prelude::*; use crate::config::{Color, Config, Palette, PPUConfig}; use crate::Framebuffer; -use bitflags::bitflags; +use crate::components::ppu::bgpi::BGPI; use crate::components::ppu::cc::ColorCorrection; +use crate::components::ppu::structs::*; /// RGBA (4 bytes) per pixel pub const FRAMEBUFFER_SIZE: usize = 4 * SCREEN_W * SCREEN_H; @@ -41,99 +42,6 @@ pub struct PPU { pub framebuffer: Framebuffer, } -#[derive(PartialEq, Copy, Clone)] -enum Priority { - Color0, - Priority, - Normal, -} - -#[derive(PartialEq, Copy, Clone)] -enum PPUMode { - OAMScan = 2, - Draw = 3, - HBlank = 0, - VBlank = 1, -} - -bitflags! { - #[derive(PartialEq, Copy, Clone)] - pub struct Attributes: u8 { - const PRIORITY = 0b1000_0000; - const Y_FLIP = 0b0100_0000; - const X_FLIP = 0b0010_0000; - const PALETTE_NO_0 = 0b0001_0000; - const BANK = 0b0000_1000; - } -} - -bitflags! { - #[derive(PartialEq, Copy, Clone)] - pub struct LCDC: u8 { - // LCD & PPU enable: 0 = Off; 1 = On - const LCD_ENABLE = 0b1000_0000; - // Window tile map area: 0 = 9800–9BFF; 1 = 9C00–9FFF - const WINDOW_AREA = 0b0100_0000; - // Window enable: 0 = Off; 1 = On - const WINDOW_ENABLE = 0b0010_0000; - // BG & Window tile data area: 0 = 8800–97FF; 1 = 8000–8FFF - const TILE_DATA_AREA = 0b0001_0000; - // BG tile map area: 0 = 9800–9BFF; 1 = 9C00–9FFF - const BG_TILE_MAP_AREA = 0b0000_1000; - // OBJ size: 0 = 8×8; 1 = 8×16 - const OBJ_SIZE = 0b0000_0100; - // OBJ enable: 0 = Off; 1 = On - const OBJ_ENABLE = 0b0000_0010; - // BG & Window enable (GB) / priority (CGB): 0 = Off; 1 = On - const WINDOW_PRIORITY = 0b0000_0001; - } -} - -bitflags! { - #[derive(PartialEq, Copy, Clone)] - pub struct LCDS: u8 { - // LYC int select (Read/Write): If set, selects the LYC == LY condition for the STAT interrupt. - const LYC_SELECT = 0b0100_0000; - // Mode 2 int select (Read/Write): If set, selects the Mode 2 condition for the STAT interrupt. - const MODE_2_SELECT = 0b0010_0000; - // Mode 1 int select (Read/Write): If set, selects the Mode 1 condition for the STAT interrupt. - const MODE_1_SELECT = 0b0001_0000; - // Mode 0 int select (Read/Write): If set, selects the Mode 0 condition for the STAT interrupt. - const MODE_0_SELECT = 0b0000_1000; - // LYC == LY (Read-only): Set when LY contains the same value as LYC; it is constantly updated. - const LYC_EQUALS = 0b0000_0100; - // PPU mode (Read-only): Indicates the PPU’s current status. - } -} - -struct BGPI { - address: u8, - auto_increment: bool -} - -impl BGPI { - fn new() -> Self { - Self { - address: 0, - auto_increment: false - } - } - - fn read(&self) -> u8 { - let a = if self.auto_increment { - 0x80 - } else { - 0x00 - }; - a | self.address - } - - fn write(&mut self, v: u8) { - self.auto_increment = v & 0x80 != 0x00; - self.address = v & 0x3F; - } -} - impl PPU { pub fn new(config: Config, framebuffer: Framebuffer) -> Self { Self { @@ -296,7 +204,22 @@ impl PPU { CCMode::SGB => self.cc.sgb_color_lut[color as usize], }; - self.write_pixel(color[0], color[1], color[2], x, self.ly); + self.set_pixel(color[0], color[1], color[2], x, self.ly); + } + + fn set_pixel(&mut self, r: u8, g: u8, b: u8, x: usize, y: u8) { + pub const BYTES_PER_PIXEL: usize = 4; + pub const BYTES_PER_ROW: usize = BYTES_PER_PIXEL * SCREEN_W; + + let vertical_offset = y as usize * BYTES_PER_ROW; + let horizontal_offset = x * BYTES_PER_PIXEL; + let total_offset = vertical_offset + horizontal_offset; + + let mut framebuffer = self.framebuffer.write().unwrap(); + framebuffer[total_offset + 0] = r; + framebuffer[total_offset + 1] = g; + framebuffer[total_offset + 2] = b; + framebuffer[total_offset + 3] = 0xFF; } fn draw_bg(&mut self) { @@ -407,7 +330,7 @@ impl PPU { Self::grey_to_l(self.ppu_config.palette, self.bgp, color) }; - self.write_pixel(color.r(), color.g(), color.b(), x, self.ly); + self.set_pixel(color.r(), color.g(), color.b(), x, self.ly); } } } @@ -516,7 +439,7 @@ impl PPU { Self::grey_to_l(self.ppu_config.palette, self.obp0, color) }; - self.write_pixel(color.r(), color.g(), color.b(), px.wrapping_add(x) as usize, self.ly); + self.set_pixel(color.r(), color.g(), color.b(), px.wrapping_add(x) as usize, self.ly); } } } @@ -529,21 +452,6 @@ impl PPU { fn write_vram(&mut self, a: u16, v: u8, bank: usize) { self.vram[(bank * 0x2000) + a as usize - 0x8000] = v; } - - fn write_pixel(&mut self, r: u8, g: u8, b: u8, x: usize, y: u8) { - pub const BYTES_PER_PIXEL: usize = 4; - pub const BYTES_PER_ROW: usize = BYTES_PER_PIXEL * SCREEN_W; - - let vertical_offset = y as usize * BYTES_PER_ROW; - let horizontal_offset = x * BYTES_PER_PIXEL; - let total_offset = vertical_offset + horizontal_offset; - - let mut framebuffer = self.framebuffer.write().unwrap(); - framebuffer[total_offset + 0] = r; - framebuffer[total_offset + 1] = g; - framebuffer[total_offset + 2] = b; - framebuffer[total_offset + 3] = 0xFF; - } } impl Memory for PPU { @@ -625,7 +533,7 @@ impl Memory for PPU { for y in 0..SCREEN_H { for x in 0..SCREEN_W { - self.write_pixel(r, g, b, x, y as u8); + self.set_pixel(r, g, b, x, y as u8); } } }, diff --git a/src/components/ppu/structs.rs b/src/components/ppu/structs.rs new file mode 100644 index 0000000..cd02c9e --- /dev/null +++ b/src/components/ppu/structs.rs @@ -0,0 +1,66 @@ +use bitflags::bitflags; + +#[derive(PartialEq, Copy, Clone)] +pub enum Priority { + Color0, + Priority, + Normal, +} + +#[derive(PartialEq, Copy, Clone)] +pub enum PPUMode { + OAMScan = 2, + Draw = 3, + HBlank = 0, + VBlank = 1, +} + +bitflags! { + #[derive(PartialEq, Copy, Clone)] + pub struct Attributes: u8 { + const PRIORITY = 0b1000_0000; + const Y_FLIP = 0b0100_0000; + const X_FLIP = 0b0010_0000; + const PALETTE_NO_0 = 0b0001_0000; + const BANK = 0b0000_1000; + } +} + +bitflags! { + #[derive(PartialEq, Copy, Clone)] + pub struct LCDC: u8 { + // LCD & PPU enable: 0 = Off; 1 = On + const LCD_ENABLE = 0b1000_0000; + // Window tile map area: 0 = 9800–9BFF; 1 = 9C00–9FFF + const WINDOW_AREA = 0b0100_0000; + // Window enable: 0 = Off; 1 = On + const WINDOW_ENABLE = 0b0010_0000; + // BG & Window tile data area: 0 = 8800–97FF; 1 = 8000–8FFF + const TILE_DATA_AREA = 0b0001_0000; + // BG tile map area: 0 = 9800–9BFF; 1 = 9C00–9FFF + const BG_TILE_MAP_AREA = 0b0000_1000; + // OBJ size: 0 = 8×8; 1 = 8×16 + const OBJ_SIZE = 0b0000_0100; + // OBJ enable: 0 = Off; 1 = On + const OBJ_ENABLE = 0b0000_0010; + // BG & Window enable (GB) / priority (CGB): 0 = Off; 1 = On + const WINDOW_PRIORITY = 0b0000_0001; + } +} + +bitflags! { + #[derive(PartialEq, Copy, Clone)] + pub struct LCDS: u8 { + // LYC int select (Read/Write): If set, selects the LYC == LY condition for the STAT interrupt. + const LYC_SELECT = 0b0100_0000; + // Mode 2 int select (Read/Write): If set, selects the Mode 2 condition for the STAT interrupt. + const MODE_2_SELECT = 0b0010_0000; + // Mode 1 int select (Read/Write): If set, selects the Mode 1 condition for the STAT interrupt. + const MODE_1_SELECT = 0b0001_0000; + // Mode 0 int select (Read/Write): If set, selects the Mode 0 condition for the STAT interrupt. + const MODE_0_SELECT = 0b0000_1000; + // LYC == LY (Read-only): Set when LY contains the same value as LYC; it is constantly updated. + const LYC_EQUALS = 0b0000_0100; + // PPU mode (Read-only): Indicates the PPU’s current status. + } +} \ No newline at end of file