automatic palettes work

This commit is contained in:
Alex Janka 2023-04-22 15:23:15 +10:00
parent 7fba9cca89
commit 27e0374181
6 changed files with 88 additions and 54 deletions

1
Cargo.lock generated
View file

@ -989,6 +989,7 @@ name = "gb-emu-lib"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"async-ringbuf", "async-ringbuf",
"bytemuck",
"futures", "futures",
"itertools", "itertools",
"once_cell", "once_cell",

View file

@ -15,3 +15,4 @@ once_cell = "1.17.1"
itertools = "0.10.5" itertools = "0.10.5"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_with = "2.3.1" serde_with = "2.3.1"
bytemuck = "1.13"

View file

@ -5,8 +5,8 @@ use self::{
cgb::CgbData, cgb::CgbData,
tile_window::TileWindow, tile_window::TileWindow,
types::{ types::{
GpuInterrupts, Lcdc, Oam, ObjPalette, ObjSize, Object, ObjectFlags, Palette, Stat, ColourInner, GpuInterrupts, Lcdc, Oam, ObjPalette, ObjSize, Object, ObjectFlags, Palette,
TiledataArea, TilemapArea, Vram, Stat, TiledataArea, TilemapArea, Vram,
}, },
}; };
use crate::{ use crate::{
@ -132,7 +132,7 @@ where
} else { } else {
None None
}; };
let buffer = vec![Colour::Error.into(); WIDTH * HEIGHT]; let buffer = vec![ColourInner::Error.rgb_bytes(None).into(); WIDTH * HEIGHT];
Self { Self {
buffer, buffer,
@ -315,7 +315,8 @@ where
*e = true; *e = true;
} }
for x in 0..WIDTH { for x in 0..WIDTH {
self.buffer[(scanline as usize * WIDTH) + x] = Colour::Error.into(); self.buffer[(scanline as usize * WIDTH) + x] =
ColourInner::Error.rgb_bytes(None).into();
} }
if self.lcdc.bg_window_enable { if self.lcdc.bg_window_enable {
self.render_scanline_bg(scanline); self.render_scanline_bg(scanline);
@ -328,7 +329,8 @@ where
} }
} else { } else {
for x in 0..WIDTH { for x in 0..WIDTH {
self.buffer[(scanline as usize * WIDTH) + x] = Colour::Error.into(); self.buffer[(scanline as usize * WIDTH) + x] =
ColourInner::Error.rgb_bytes(None).into();
} }
} }
if self.lcdc.obj_enable { if self.lcdc.obj_enable {
@ -449,7 +451,9 @@ where
if x_coord < WIDTH { if x_coord < WIDTH {
let buffer_index = (scanline as usize * WIDTH) + x_coord; let buffer_index = (scanline as usize * WIDTH) + x_coord;
if !object.flags.behind_bg_and_window || self.is_bg_zero[x_coord] { if !object.flags.behind_bg_and_window || self.is_bg_zero[x_coord] {
self.buffer[buffer_index] = colour.into(); let cgb_data = self.cgb_data.as_ref().map(|v| (v.palettes.obj, 0));
self.buffer[buffer_index] = colour.rgb_bytes(cgb_data).into();
} }
} }
} }
@ -495,7 +499,9 @@ where
let (colour, is_zero) = self.bg_palette.map_bits(lsb, msb); let (colour, is_zero) = self.bg_palette.map_bits(lsb, msb);
self.is_bg_zero[x] = is_zero; self.is_bg_zero[x] = is_zero;
self.buffer[(scanline as usize * WIDTH) + x] = colour.into(); let cgb_data = self.cgb_data.as_ref().map(|v| (v.palettes.bg, 0));
self.buffer[(scanline as usize * WIDTH) + x] = colour.rgb_bytes(cgb_data).into();
} }
} }

View file

@ -10,8 +10,8 @@ use super::{Colour, Gpu};
#[derive(Serialize, Deserialize, Clone, Copy)] #[derive(Serialize, Deserialize, Clone, Copy)]
pub(super) struct CgbData { pub(super) struct CgbData {
palettes: CgbPaletteRegisters, pub(super) palettes: CgbPaletteRegisters,
object_priority_mode: ObjectPriorityMode, pub(super) object_priority_mode: ObjectPriorityMode,
} }
impl Default for CgbData { impl Default for CgbData {
@ -24,24 +24,24 @@ impl Default for CgbData {
} }
#[derive(Serialize, Deserialize, Clone, Copy)] #[derive(Serialize, Deserialize, Clone, Copy)]
enum ObjectPriorityMode { pub(super) enum ObjectPriorityMode {
OamLocation = 0, OamLocation = 0,
Coordinate = 1, Coordinate = 1,
} }
#[derive(Serialize, Deserialize, Clone, Copy, Default)] #[derive(Serialize, Deserialize, Clone, Copy, Default)]
struct CgbPaletteRegisters { pub(super) struct CgbPaletteRegisters {
bg: CgbPalette, pub(super) bg: CgbPalette,
obj: CgbPalette, pub(super) obj: CgbPalette,
} }
#[serde_with::serde_as] #[serde_with::serde_as]
#[derive(Serialize, Deserialize, Clone, Copy)] #[derive(Serialize, Deserialize, Clone, Copy)]
struct CgbPalette { pub(super) struct CgbPalette {
auto_increment: bool, auto_increment: bool,
index: u8, index: u8,
#[serde_as(as = "[_; 0x40]")] #[serde_as(as = "[_; 0x40]")]
data: [u8; 0x40], pub(super) data: [u8; 0x40],
} }
impl Default for CgbPalette { impl Default for CgbPalette {

View file

@ -4,7 +4,10 @@ use crate::{
util::get_bit, util::get_bit,
}; };
use super::{types::Vram, Colour}; use super::{
types::{ColourInner, Vram},
Colour,
};
pub(super) struct TileWindow<ColourFormat, R> pub(super) struct TileWindow<ColourFormat, R>
where where
@ -22,7 +25,10 @@ where
{ {
pub(super) fn new(window: R) -> Self { pub(super) fn new(window: R) -> Self {
Self { Self {
sprite_buffer: vec![Colour::Error.into(); TILE_WINDOW_WIDTH * TILE_WINDOW_HEIGHT], sprite_buffer: vec![
ColourInner::Error.rgb_bytes(None).into();
TILE_WINDOW_WIDTH * TILE_WINDOW_HEIGHT
],
sprite_renderer: window, sprite_renderer: window,
} }
} }
@ -72,7 +78,7 @@ where
let colour = palette.map_bits(lsb, msb); let colour = palette.map_bits(lsb, msb);
self.sprite_buffer[real_px_x + (real_px_y * TILE_WINDOW_WIDTH)] = self.sprite_buffer[real_px_x + (real_px_y * TILE_WINDOW_WIDTH)] =
colour.0.into(); colour.0.rgb_bytes(None).into();
} }
} }
} }

View file

@ -1,3 +1,4 @@
use bytemuck::from_bytes;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
@ -5,6 +6,8 @@ use crate::{
util::{as_signed, get_bit}, util::{as_signed, get_bit},
}; };
use super::cgb::CgbPalette;
#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)] #[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)]
pub(crate) enum DrawMode { pub(crate) enum DrawMode {
HBlank, HBlank,
@ -89,75 +92,92 @@ impl Default for Lcdc {
} }
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub enum Colour { pub enum ColourInner {
White, Zero = 0,
LightGray, One = 1,
DarkGray, Two = 2,
Black, Three = 3,
Error, Error = 255,
} }
pub struct Colour(u8, u8, u8);
impl From<Colour> for u32 { impl From<Colour> for u32 {
fn from(value: Colour) -> Self { fn from(value: Colour) -> Self {
let rgb = value.rgb_bytes(); let (r, g, b) = (value.0 as u32, value.1 as u32, value.2 as u32);
let (r, g, b) = (rgb.0 as u32, rgb.1 as u32, rgb.2 as u32);
(r << 16) | (g << 8) | b (r << 16) | (g << 8) | b
} }
} }
impl From<Colour> for [u8; 4] { impl From<Colour> for [u8; 4] {
fn from(value: Colour) -> Self { fn from(value: Colour) -> Self {
let (r, g, b) = value.rgb_bytes(); let Colour(r, g, b) = value;
[r, g, b, 0xFF] [r, g, b, 0xFF]
} }
} }
impl Colour { fn rgb_from_bytes(bytes: u16) -> Colour {
fn rgb_bytes(&self) -> (u8, u8, u8) { let blue = (((bytes & (0b11111 << 10)) >> 10) << 3) as u8;
let green = (((bytes & (0b11111 << 5)) >> 5) << 3) as u8;
let red = ((bytes & 0b11111) << 3) as u8;
Colour(red, green, blue)
}
impl ColourInner {
pub(super) fn rgb_bytes(&self, cgb_data: Option<(CgbPalette, u8)>) -> Colour {
if let Some((cgb_palette, pallete_num)) = cgb_data {
if *self == ColourInner::Error {
return Colour(0xFF, 0, 0);
}
let offset: usize = (pallete_num as usize * 2 * 4) + (*self as usize * 2);
rgb_from_bytes(*from_bytes(&cgb_palette.data[offset..=offset + 1]))
} else {
match self { match self {
Colour::White => (0xFF, 0xFF, 0xFF), ColourInner::Zero => Colour(0xFF, 0xFF, 0xFF),
Colour::LightGray => (0xAA, 0xAA, 0xAA), ColourInner::One => Colour(0xAA, 0xAA, 0xAA),
Colour::DarkGray => (0x55, 0x55, 0x55), ColourInner::Two => Colour(0x55, 0x55, 0x55),
Colour::Black => (0x00, 0x00, 0x00), ColourInner::Three => Colour(0x00, 0x00, 0x00),
Colour::Error => (0xFF, 0x00, 0x00), ColourInner::Error => Colour(0xFF, 0x00, 0x00),
}
} }
} }
pub(super) fn from_bits(first: bool, second: bool) -> Colour { pub(super) fn from_bits(first: bool, second: bool) -> ColourInner {
match (first, second) { match (first, second) {
(true, true) => Colour::Black, (true, true) => ColourInner::Three,
(true, false) => Colour::LightGray, (true, false) => ColourInner::One,
(false, true) => Colour::DarkGray, (false, true) => ColourInner::Two,
(false, false) => Colour::White, (false, false) => ColourInner::Zero,
} }
} }
fn as_bits(&self) -> u8 { fn as_bits(&self) -> u8 {
match self { match self {
Colour::White => 0b00, ColourInner::Zero => 0b00,
Colour::LightGray => 0b01, ColourInner::One => 0b01,
Colour::DarkGray => 0b10, ColourInner::Two => 0b10,
Colour::Black => 0b11, ColourInner::Three => 0b11,
Colour::Error => 0b00, ColourInner::Error => 0b00,
} }
} }
} }
#[derive(Clone, Copy, Debug, Serialize, Deserialize)] #[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub(super) struct Palette { pub(super) struct Palette {
pub(super) zero: Colour, pub(super) zero: ColourInner,
pub(super) one: Colour, pub(super) one: ColourInner,
pub(super) two: Colour, pub(super) two: ColourInner,
pub(super) three: Colour, pub(super) three: ColourInner,
} }
impl Palette { impl Palette {
pub(super) fn from_byte(byte: u8) -> Palette { pub(super) fn from_byte(byte: u8) -> Palette {
Palette { Palette {
zero: Colour::from_bits(get_bit(byte, 0), get_bit(byte, 1)), zero: ColourInner::from_bits(get_bit(byte, 0), get_bit(byte, 1)),
one: Colour::from_bits(get_bit(byte, 2), get_bit(byte, 3)), one: ColourInner::from_bits(get_bit(byte, 2), get_bit(byte, 3)),
two: Colour::from_bits(get_bit(byte, 4), get_bit(byte, 5)), two: ColourInner::from_bits(get_bit(byte, 4), get_bit(byte, 5)),
three: Colour::from_bits(get_bit(byte, 6), get_bit(byte, 7)), three: ColourInner::from_bits(get_bit(byte, 6), get_bit(byte, 7)),
} }
} }
@ -168,7 +188,7 @@ impl Palette {
| (self.three.as_bits() << 6) | (self.three.as_bits() << 6)
} }
pub(super) fn map_bits(&self, lsb: bool, msb: bool) -> (Colour, bool) { pub(super) fn map_bits(&self, lsb: bool, msb: bool) -> (ColourInner, bool) {
match (lsb, msb) { match (lsb, msb) {
(true, true) => (self.three, false), (true, true) => (self.three, false),
(true, false) => (self.one, false), (true, false) => (self.one, false),