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"
dependencies = [
"async-ringbuf",
"bytemuck",
"futures",
"itertools",
"once_cell",

View file

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

View file

@ -5,8 +5,8 @@ use self::{
cgb::CgbData,
tile_window::TileWindow,
types::{
GpuInterrupts, Lcdc, Oam, ObjPalette, ObjSize, Object, ObjectFlags, Palette, Stat,
TiledataArea, TilemapArea, Vram,
ColourInner, GpuInterrupts, Lcdc, Oam, ObjPalette, ObjSize, Object, ObjectFlags, Palette,
Stat, TiledataArea, TilemapArea, Vram,
},
};
use crate::{
@ -132,7 +132,7 @@ where
} else {
None
};
let buffer = vec![Colour::Error.into(); WIDTH * HEIGHT];
let buffer = vec![ColourInner::Error.rgb_bytes(None).into(); WIDTH * HEIGHT];
Self {
buffer,
@ -315,7 +315,8 @@ where
*e = true;
}
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 {
self.render_scanline_bg(scanline);
@ -328,7 +329,8 @@ where
}
} else {
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 {
@ -449,7 +451,9 @@ where
if x_coord < WIDTH {
let buffer_index = (scanline as usize * WIDTH) + 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);
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)]
pub(super) struct CgbData {
palettes: CgbPaletteRegisters,
object_priority_mode: ObjectPriorityMode,
pub(super) palettes: CgbPaletteRegisters,
pub(super) object_priority_mode: ObjectPriorityMode,
}
impl Default for CgbData {
@ -24,24 +24,24 @@ impl Default for CgbData {
}
#[derive(Serialize, Deserialize, Clone, Copy)]
enum ObjectPriorityMode {
pub(super) enum ObjectPriorityMode {
OamLocation = 0,
Coordinate = 1,
}
#[derive(Serialize, Deserialize, Clone, Copy, Default)]
struct CgbPaletteRegisters {
bg: CgbPalette,
obj: CgbPalette,
pub(super) struct CgbPaletteRegisters {
pub(super) bg: CgbPalette,
pub(super) obj: CgbPalette,
}
#[serde_with::serde_as]
#[derive(Serialize, Deserialize, Clone, Copy)]
struct CgbPalette {
pub(super) struct CgbPalette {
auto_increment: bool,
index: u8,
#[serde_as(as = "[_; 0x40]")]
data: [u8; 0x40],
pub(super) data: [u8; 0x40],
}
impl Default for CgbPalette {

View file

@ -4,7 +4,10 @@ use crate::{
util::get_bit,
};
use super::{types::Vram, Colour};
use super::{
types::{ColourInner, Vram},
Colour,
};
pub(super) struct TileWindow<ColourFormat, R>
where
@ -22,7 +25,10 @@ where
{
pub(super) fn new(window: R) -> 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,
}
}
@ -72,7 +78,7 @@ where
let colour = palette.map_bits(lsb, msb);
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 crate::{
@ -5,6 +6,8 @@ use crate::{
util::{as_signed, get_bit},
};
use super::cgb::CgbPalette;
#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)]
pub(crate) enum DrawMode {
HBlank,
@ -89,75 +92,92 @@ impl Default for Lcdc {
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub enum Colour {
White,
LightGray,
DarkGray,
Black,
Error,
pub enum ColourInner {
Zero = 0,
One = 1,
Two = 2,
Three = 3,
Error = 255,
}
pub struct Colour(u8, u8, u8);
impl From<Colour> for u32 {
fn from(value: Colour) -> Self {
let rgb = value.rgb_bytes();
let (r, g, b) = (rgb.0 as u32, rgb.1 as u32, rgb.2 as u32);
let (r, g, b) = (value.0 as u32, value.1 as u32, value.2 as u32);
(r << 16) | (g << 8) | b
}
}
impl From<Colour> for [u8; 4] {
fn from(value: Colour) -> Self {
let (r, g, b) = value.rgb_bytes();
let Colour(r, g, b) = value;
[r, g, b, 0xFF]
}
}
impl Colour {
fn rgb_bytes(&self) -> (u8, u8, u8) {
fn rgb_from_bytes(bytes: u16) -> Colour {
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 {
Colour::White => (0xFF, 0xFF, 0xFF),
Colour::LightGray => (0xAA, 0xAA, 0xAA),
Colour::DarkGray => (0x55, 0x55, 0x55),
Colour::Black => (0x00, 0x00, 0x00),
Colour::Error => (0xFF, 0x00, 0x00),
ColourInner::Zero => Colour(0xFF, 0xFF, 0xFF),
ColourInner::One => Colour(0xAA, 0xAA, 0xAA),
ColourInner::Two => Colour(0x55, 0x55, 0x55),
ColourInner::Three => Colour(0x00, 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) {
(true, true) => Colour::Black,
(true, false) => Colour::LightGray,
(false, true) => Colour::DarkGray,
(false, false) => Colour::White,
(true, true) => ColourInner::Three,
(true, false) => ColourInner::One,
(false, true) => ColourInner::Two,
(false, false) => ColourInner::Zero,
}
}
fn as_bits(&self) -> u8 {
match self {
Colour::White => 0b00,
Colour::LightGray => 0b01,
Colour::DarkGray => 0b10,
Colour::Black => 0b11,
Colour::Error => 0b00,
ColourInner::Zero => 0b00,
ColourInner::One => 0b01,
ColourInner::Two => 0b10,
ColourInner::Three => 0b11,
ColourInner::Error => 0b00,
}
}
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub(super) struct Palette {
pub(super) zero: Colour,
pub(super) one: Colour,
pub(super) two: Colour,
pub(super) three: Colour,
pub(super) zero: ColourInner,
pub(super) one: ColourInner,
pub(super) two: ColourInner,
pub(super) three: ColourInner,
}
impl Palette {
pub(super) fn from_byte(byte: u8) -> Palette {
Palette {
zero: Colour::from_bits(get_bit(byte, 0), get_bit(byte, 1)),
one: Colour::from_bits(get_bit(byte, 2), get_bit(byte, 3)),
two: Colour::from_bits(get_bit(byte, 4), get_bit(byte, 5)),
three: Colour::from_bits(get_bit(byte, 6), get_bit(byte, 7)),
zero: ColourInner::from_bits(get_bit(byte, 0), get_bit(byte, 1)),
one: ColourInner::from_bits(get_bit(byte, 2), get_bit(byte, 3)),
two: ColourInner::from_bits(get_bit(byte, 4), get_bit(byte, 5)),
three: ColourInner::from_bits(get_bit(byte, 6), get_bit(byte, 7)),
}
}
@ -168,7 +188,7 @@ impl Palette {
| (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) {
(true, true) => (self.three, false),
(true, false) => (self.one, false),