From 9982f86974fcb57f442dbabba71837ec71fafaf1 Mon Sep 17 00:00:00 2001 From: Alessio Cosenza Date: Fri, 16 Feb 2024 18:23:33 +0100 Subject: [PATCH] Lcd: Basic support for `BG2` --- emu/src/cpu/hardware/lcd.rs | 56 ++++++++++++++++++-- emu/src/cpu/hardware/lcd/layers.rs | 10 +++- emu/src/cpu/hardware/lcd/layers/layer_0.rs | 12 ++++- emu/src/cpu/hardware/lcd/layers/layer_1.rs | 12 ++++- emu/src/cpu/hardware/lcd/layers/layer_2.rs | 40 ++++++++++++-- emu/src/cpu/hardware/lcd/layers/layer_3.rs | 12 ++++- emu/src/cpu/hardware/lcd/layers/layer_obj.rs | 10 +++- emu/src/cpu/hardware/lcd/registers.rs | 8 +++ 8 files changed, 143 insertions(+), 17 deletions(-) diff --git a/emu/src/cpu/hardware/lcd.rs b/emu/src/cpu/hardware/lcd.rs index b044cf3..be0efa0 100644 --- a/emu/src/cpu/hardware/lcd.rs +++ b/emu/src/cpu/hardware/lcd.rs @@ -109,7 +109,7 @@ impl Default for Lcd { should_draw: false, layer_0: Layer0, layer_1: Layer1, - layer_2: Layer2, + layer_2: Layer2::default(), layer_3: Layer3, layer_obj: LayerObj::default(), } @@ -167,10 +167,27 @@ impl Lcd { let pixel_y = self.registers.vcount; let pixel_x = self.pixel_index; - self.buffer[pixel_y as usize][pixel_x as usize] = self - .layer_obj - .render(pixel_x as usize, pixel_y as usize) - .unwrap_or_else(|| Color::from_rgb(31, 31, 31)); + // We get the enabled layers (depending on BG mode and registers), we call render on them + // we filter out the `None` and we sort by priority. + let mut layers_with_pixel = self + .get_enabled_layers() + .into_iter() + .filter_map(|layer| { + layer.render( + pixel_x as usize, + pixel_y as usize, + &self.memory, + &self.registers, + ) + }) + .collect::>(); + + layers_with_pixel.sort_unstable_by_key(|pixel| pixel.priority); + + let first_pixel = layers_with_pixel.first(); + + self.buffer[pixel_y as usize][pixel_x as usize] = + first_pixel.map_or_else(|| Color::from_rgb(31, 31, 31), |info| info.color); } log(format!( @@ -209,4 +226,33 @@ impl Lcd { output } + + fn get_enabled_layers(&self) -> Vec<&dyn Layer> { + let mut result: Vec<&dyn Layer> = Vec::new(); + + let current_mode = self.registers.get_bg_mode(); + + if matches!(current_mode, 0 | 1) && self.registers.get_bg0_enabled() { + result.push(&self.layer_0); + } + + if matches!(current_mode, 0 | 1) && self.registers.get_bg1_enabled() { + result.push(&self.layer_1) + } + + // BG2 is available in every mode + if self.registers.get_bg2_enabled() { + result.push(&self.layer_2) + } + + if matches!(current_mode, 0 | 2) && self.registers.get_bg3_enabled() { + result.push(&self.layer_3) + } + + if self.registers.get_obj_enabled() { + result.push(&self.layer_obj); + } + + result + } } diff --git a/emu/src/cpu/hardware/lcd/layers.rs b/emu/src/cpu/hardware/lcd/layers.rs index 519808c..6359341 100644 --- a/emu/src/cpu/hardware/lcd/layers.rs +++ b/emu/src/cpu/hardware/lcd/layers.rs @@ -1,4 +1,4 @@ -use super::Color; +use super::{memory::Memory, registers::Registers, PixelInfo}; pub mod layer_0; pub mod layer_1; @@ -7,5 +7,11 @@ pub mod layer_3; pub mod layer_obj; pub trait Layer { - fn render(&self, x: usize, y: usize) -> Option; + fn render( + &self, + x: usize, + y: usize, + memory: &Memory, + registers: &Registers, + ) -> Option; } diff --git a/emu/src/cpu/hardware/lcd/layers/layer_0.rs b/emu/src/cpu/hardware/lcd/layers/layer_0.rs index 017f99d..e0507b9 100644 --- a/emu/src/cpu/hardware/lcd/layers/layer_0.rs +++ b/emu/src/cpu/hardware/lcd/layers/layer_0.rs @@ -1,3 +1,7 @@ +use crate::cpu::hardware::lcd::memory::Memory; +use crate::cpu::hardware::lcd::registers::Registers; +use crate::cpu::hardware::lcd::PixelInfo; + use super::Layer; use serde::Deserialize; use serde::Serialize; @@ -7,7 +11,13 @@ pub struct Layer0; impl Layer for Layer0 { #[allow(unused_variables)] - fn render(&self, x: usize, y: usize) -> Option { + fn render( + &self, + x: usize, + y: usize, + memory: &Memory, + registers: &Registers, + ) -> Option { // TODO: To implement None } diff --git a/emu/src/cpu/hardware/lcd/layers/layer_1.rs b/emu/src/cpu/hardware/lcd/layers/layer_1.rs index a186775..56eb0a6 100644 --- a/emu/src/cpu/hardware/lcd/layers/layer_1.rs +++ b/emu/src/cpu/hardware/lcd/layers/layer_1.rs @@ -1,3 +1,7 @@ +use crate::cpu::hardware::lcd::memory::Memory; +use crate::cpu::hardware::lcd::registers::Registers; +use crate::cpu::hardware::lcd::PixelInfo; + use super::Layer; use serde::Deserialize; use serde::Serialize; @@ -7,7 +11,13 @@ pub struct Layer1; impl Layer for Layer1 { #[allow(unused_variables)] - fn render(&self, x: usize, y: usize) -> Option { + fn render( + &self, + x: usize, + y: usize, + memory: &Memory, + registers: &Registers, + ) -> Option { // TODO: To implement None } diff --git a/emu/src/cpu/hardware/lcd/layers/layer_2.rs b/emu/src/cpu/hardware/lcd/layers/layer_2.rs index da514af..fc28aac 100644 --- a/emu/src/cpu/hardware/lcd/layers/layer_2.rs +++ b/emu/src/cpu/hardware/lcd/layers/layer_2.rs @@ -1,14 +1,44 @@ use super::Layer; +use crate::cpu::hardware::lcd::memory::Memory; +use crate::cpu::hardware::lcd::registers::Registers; +use crate::cpu::hardware::lcd::{Color, PixelInfo, LCD_WIDTH}; use serde::Deserialize; use serde::Serialize; +use serde_with::serde_as; -#[derive(Default, Serialize, Deserialize)] -pub struct Layer2; +#[serde_as] +#[derive(Serialize, Deserialize)] +pub struct Layer2 { + #[serde_as(as = "[_; 240]")] + bg_pixels_scanline: [Option; LCD_WIDTH], +} + +impl Default for Layer2 { + fn default() -> Self { + Self { + bg_pixels_scanline: [None; LCD_WIDTH], + } + } +} impl Layer for Layer2 { #[allow(unused_variables)] - fn render(&self, x: usize, y: usize) -> Option { - // TODO: To implement - None + fn render( + &self, + x: usize, + y: usize, + memory: &Memory, + registers: &Registers, + ) -> Option { + let idx: usize = y * LCD_WIDTH + x; + + let color_idx = memory.video_ram[idx] as usize; + let low_nibble = memory.bg_palette_ram[color_idx * 2] as u16; + let high_nibble = memory.bg_palette_ram[color_idx * 2 + 1] as u16; + + Some(PixelInfo { + color: Color::from_palette_color((high_nibble << 8) | low_nibble), + priority: 0, + }) } } diff --git a/emu/src/cpu/hardware/lcd/layers/layer_3.rs b/emu/src/cpu/hardware/lcd/layers/layer_3.rs index 5a11ff0..e6dbf34 100644 --- a/emu/src/cpu/hardware/lcd/layers/layer_3.rs +++ b/emu/src/cpu/hardware/lcd/layers/layer_3.rs @@ -1,3 +1,7 @@ +use crate::cpu::hardware::lcd::memory::Memory; +use crate::cpu::hardware::lcd::registers::Registers; +use crate::cpu::hardware::lcd::PixelInfo; + use super::Layer; use serde::Deserialize; use serde::Serialize; @@ -7,7 +11,13 @@ pub struct Layer3; impl Layer for Layer3 { #[allow(unused_variables)] - fn render(&self, x: usize, y: usize) -> Option { + fn render( + &self, + x: usize, + y: usize, + memory: &Memory, + registers: &Registers, + ) -> Option { // TODO: To implement None } diff --git a/emu/src/cpu/hardware/lcd/layers/layer_obj.rs b/emu/src/cpu/hardware/lcd/layers/layer_obj.rs index 83d739a..b68145c 100644 --- a/emu/src/cpu/hardware/lcd/layers/layer_obj.rs +++ b/emu/src/cpu/hardware/lcd/layers/layer_obj.rs @@ -37,8 +37,14 @@ impl Default for LayerObj { impl Layer for LayerObj { #[allow(unused_variables)] - fn render(&self, x: usize, y: usize) -> Option { - self.sprite_pixels_scanline[x].map(|info| info.color) + fn render( + &self, + x: usize, + y: usize, + memory: &Memory, + registers: &Registers, + ) -> Option { + self.sprite_pixels_scanline[x] } } diff --git a/emu/src/cpu/hardware/lcd/registers.rs b/emu/src/cpu/hardware/lcd/registers.rs index f07ef88..603f583 100644 --- a/emu/src/cpu/hardware/lcd/registers.rs +++ b/emu/src/cpu/hardware/lcd/registers.rs @@ -85,6 +85,14 @@ pub struct Registers { } impl Registers { + pub(super) fn get_bg0_enabled(&self) -> bool { + self.dispcnt.get_bit(8) + } + + pub(super) fn get_bg1_enabled(&self) -> bool { + self.dispcnt.get_bit(9) + } + pub(super) fn get_bg2_enabled(&self) -> bool { self.dispcnt.get_bit(10) }