Skip to content
Snippets Groups Projects
ppu.rs 54.1 KiB
Newer Older
use crate::{
    gb::{GameBoyConfig, GameBoyMode},
    warnln,
};

use core::fmt;
use std::{
    borrow::BorrowMut,
    cell::RefCell,
    fmt::{Display, Formatter},
#[cfg(feature = "wasm")]
use wasm_bindgen::prelude::*;

pub const VRAM_SIZE_DMG: usize = 8192;
pub const VRAM_SIZE_CGB: usize = 16384;
pub const VRAM_SIZE: usize = VRAM_SIZE_CGB;
pub const HRAM_SIZE: usize = 128;
pub const OAM_SIZE: usize = 260;
pub const PALETTE_SIZE: usize = 4;
pub const RGB_SIZE: usize = 3;
pub const RGBA_SIZE: usize = 4;
pub const TILE_WIDTH: usize = 8;
pub const TILE_HEIGHT: usize = 8;
pub const TILE_DOUBLE_HEIGHT: usize = 16;
pub const TILE_COUNT_DMG: usize = 384;
pub const TILE_COUNT_CGB: usize = 768;

/// The number of tiles that can be store in Game Boy's
/// VRAM memory according to specifications.
pub const TILE_COUNT: usize = TILE_COUNT_CGB;
/// The number of objects/sprites that can be handled at
/// the same time by the Game Boy.
pub const OBJ_COUNT: usize = 40;

/// The width of the Game Boy screen in pixels.
pub const DISPLAY_WIDTH: usize = 160;

/// The height of the Game Boy screen in pixels.
pub const DISPLAY_HEIGHT: usize = 144;
/// The size to be used by the buffer of colors
/// for the Game Boy screen the values there should
/// range from 0 to 3.
pub const COLOR_BUFFER_SIZE: usize = DISPLAY_WIDTH * DISPLAY_HEIGHT;

/// The size of the RGB frame buffer in bytes.
pub const FRAME_BUFFER_SIZE: usize = DISPLAY_WIDTH * DISPLAY_HEIGHT * RGB_SIZE;

/// The base colors to be used to populate the
/// custom palettes of the Game Boy.
pub const PALETTE_COLORS: Palette = [[255, 255, 255], [192, 192, 192], [96, 96, 96], [0, 0, 0]];

pub const DEFAULT_TILE_ATTR: TileData = TileData {
    palette: 0,
    vram_bank: 0,
    xflip: false,
    yflip: false,
    priority: false,
};

/// Defines the Game Boy pixel type as a buffer
/// with the size of RGB (3 bytes).
pub type Pixel = [u8; RGB_SIZE];

/// Defines a transparent Game Boy pixel type as a buffer
/// with the size of RGBA (4 bytes).
pub type PixelAlpha = [u8; RGBA_SIZE];

/// Defines a type that represents a color palette
/// within the Game Boy context.
pub type Palette = [Pixel; PALETTE_SIZE];

/// Defines a type that represents a color palette
/// with alpha within the Game Boy context.
pub type PaletteAlpha = [PixelAlpha; PALETTE_SIZE];

/// Represents a palette with the metadata that is
/// associated with it.
#[cfg_attr(feature = "wasm", wasm_bindgen)]
#[derive(Clone, PartialEq, Eq)]
pub struct PaletteInfo {
    name: String,
João Magalhães's avatar
João Magalhães committed
    colors: Palette,
}

impl PaletteInfo {
    pub fn new(name: &str, colors: Palette) -> Self {
        Self {
            name: String::from(name),
João Magalhães's avatar
João Magalhães committed
            colors,
        }
    }

    pub fn name(&self) -> &String {
        &self.name
    }

    pub fn colors(&self) -> &Palette {
        &self.colors
    }
}

/// Represents a tile within the Game Boy context,
/// should contain the pixel buffer of the tile.
/// The tiles are always 8x8 pixels in size.
#[cfg_attr(feature = "wasm", wasm_bindgen)]
João Magalhães's avatar
João Magalhães committed
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Tile {
    /// The buffer for the tile, should contain a byte
    /// per each pixel of the tile with values ranging
    /// from 0 to 3 (4 colors).
    buffer: [u8; 64],
#[cfg_attr(feature = "wasm", wasm_bindgen)]
impl Tile {
    pub fn get(&self, x: usize, y: usize) -> u8 {
        self.buffer[y * TILE_WIDTH + x]
    pub fn get_flipped(&self, x: usize, y: usize, xflip: bool, yflip: bool) -> u8 {
        let x: usize = if xflip { 7 - x } else { x };
        let y = if yflip { 7 - y } else { y };
        self.buffer[y * TILE_WIDTH + x]
    }

    pub fn set(&mut self, x: usize, y: usize, value: u8) {
        self.buffer[y * TILE_WIDTH + x] = value;
    }

    pub fn buffer(&self) -> Vec<u8> {
        self.buffer.to_vec()
    }
impl Tile {
    pub fn get_row(&self, y: usize) -> &[u8] {
        &self.buffer[y * TILE_WIDTH..(y + 1) * TILE_WIDTH]
    }
}

impl Tile {
    pub fn palette_buffer(&self, palette: Palette) -> Vec<u8> {
        self.buffer
            .iter()
            .flat_map(|p| palette[*p as usize])
            .collect()
    }
}

impl Display for Tile {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        let mut buffer = String::new();
        for y in 0..8 {
            for x in 0..8 {
                buffer.push_str(format!("{}", self.get(x, y)).as_str());
            }
            buffer.push('\n');
        write!(f, "{}", buffer)
#[cfg_attr(feature = "wasm", wasm_bindgen)]
João Magalhães's avatar
João Magalhães committed
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct ObjectData {
    x: i16,
    y: i16,
    tile: u8,
    palette_cgb: u8,
    tile_bank: u8,
    palette: u8,
    xflip: bool,
    yflip: bool,
impl ObjectData {
    pub fn new() -> Self {
        Self {
            x: 0,
            y: 0,
            tile: 0,
            palette_cgb: 0,
            tile_bank: 0,
            palette: 0,
            xflip: false,
            yflip: false,
            bg_over: false,
            index: 0,
        }
    }
}

Loading
Loading full blame...