Skip to content

Commit

Permalink
Lcd: Basic support for BG2
Browse files Browse the repository at this point in the history
  • Loading branch information
AlessioC31 committed Feb 16, 2024
1 parent 140560b commit 3a71f12
Show file tree
Hide file tree
Showing 8 changed files with 143 additions and 17 deletions.
56 changes: 51 additions & 5 deletions emu/src/cpu/hardware/lcd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
}
Expand Down Expand Up @@ -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::<Vec<PixelInfo>>();

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!(
Expand Down Expand Up @@ -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
}
}
10 changes: 8 additions & 2 deletions emu/src/cpu/hardware/lcd/layers.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::Color;
use super::{memory::Memory, registers::Registers, PixelInfo};

pub mod layer_0;
pub mod layer_1;
Expand All @@ -7,5 +7,11 @@ pub mod layer_3;
pub mod layer_obj;

pub trait Layer {
fn render(&self, x: usize, y: usize) -> Option<Color>;
fn render(
&self,
x: usize,
y: usize,
memory: &Memory,
registers: &Registers,
) -> Option<PixelInfo>;
}
12 changes: 11 additions & 1 deletion emu/src/cpu/hardware/lcd/layers/layer_0.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -7,7 +11,13 @@ pub struct Layer0;

impl Layer for Layer0 {
#[allow(unused_variables)]
fn render(&self, x: usize, y: usize) -> Option<crate::cpu::hardware::lcd::Color> {
fn render(
&self,
x: usize,
y: usize,
memory: &Memory,
registers: &Registers,
) -> Option<PixelInfo> {
// TODO: To implement
None
}
Expand Down
12 changes: 11 additions & 1 deletion emu/src/cpu/hardware/lcd/layers/layer_1.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -7,7 +11,13 @@ pub struct Layer1;

impl Layer for Layer1 {
#[allow(unused_variables)]
fn render(&self, x: usize, y: usize) -> Option<crate::cpu::hardware::lcd::Color> {
fn render(
&self,
x: usize,
y: usize,
memory: &Memory,
registers: &Registers,
) -> Option<PixelInfo> {
// TODO: To implement
None
}
Expand Down
40 changes: 35 additions & 5 deletions emu/src/cpu/hardware/lcd/layers/layer_2.rs
Original file line number Diff line number Diff line change
@@ -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<PixelInfo>; 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<crate::cpu::hardware::lcd::Color> {
// TODO: To implement
None
fn render(
&self,
x: usize,
y: usize,
memory: &Memory,
registers: &Registers,
) -> Option<PixelInfo> {
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,
})
}
}
12 changes: 11 additions & 1 deletion emu/src/cpu/hardware/lcd/layers/layer_3.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -7,7 +11,13 @@ pub struct Layer3;

impl Layer for Layer3 {
#[allow(unused_variables)]
fn render(&self, x: usize, y: usize) -> Option<crate::cpu::hardware::lcd::Color> {
fn render(
&self,
x: usize,
y: usize,
memory: &Memory,
registers: &Registers,
) -> Option<PixelInfo> {
// TODO: To implement
None
}
Expand Down
10 changes: 8 additions & 2 deletions emu/src/cpu/hardware/lcd/layers/layer_obj.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,14 @@ impl Default for LayerObj {

impl Layer for LayerObj {
#[allow(unused_variables)]
fn render(&self, x: usize, y: usize) -> Option<Color> {
self.sprite_pixels_scanline[x].map(|info| info.color)
fn render(
&self,
x: usize,
y: usize,
memory: &Memory,
registers: &Registers,
) -> Option<PixelInfo> {
self.sprite_pixels_scanline[x]
}
}

Expand Down
8 changes: 8 additions & 0 deletions emu/src/cpu/hardware/lcd/registers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down

0 comments on commit 3a71f12

Please sign in to comment.