use crate::ppu::palette::color::EmphasisFlags;
use crate::ppu::palette::Color;
use devices6502;
#[derive(Debug)]
pub struct PaletteColorIndex(u8);
const PALETTE_RAM_SIZE: usize = 32;
#[derive(Default)]
pub struct Ram {
data: [u8; PALETTE_RAM_SIZE],
}
impl PaletteColorIndex {
#[inline]
pub fn new(palette_index: u8, color_index: u8) -> Self {
debug_assert!(color_index < 4);
debug_assert!(palette_index < 4);
let high = (palette_index & 0x03) << 2;
let low = color_index & 0x3;
Self(high | low)
}
#[inline]
pub fn palette_index(&self) -> u8 {
(self.0 & 0x0C) >> 2
}
#[inline]
pub fn color_index(&self) -> u8 {
self.0 & 0x03
}
pub fn destruct(&self) -> (u8, u8) {
(self.palette_index(), self.color_index())
}
}
impl From<PaletteColorIndex> for usize {
fn from(value: PaletteColorIndex) -> Self {
value.0 as Self
}
}
impl From<u8> for PaletteColorIndex {
fn from(value: u8) -> Self {
Self(value)
}
}
impl Ram {
pub fn new() -> Self {
Self::default()
}
pub fn read(&self, index: usize) -> u8 {
self.data[index % PALETTE_RAM_SIZE]
}
pub fn write(&mut self, index: usize, data: u8) {
self.data[index % PALETTE_RAM_SIZE] = data;
if (index % 4) == 0 {
let addr = index ^ 0x10;
self.data[addr % PALETTE_RAM_SIZE] = data;
}
}
#[inline]
pub fn read_background_color(&self, emphasis: EmphasisFlags, greyscale: bool) -> Color {
let v = if greyscale {
self.data[0] & 0x30
} else {
self.data[0]
};
Color::new(v, emphasis)
}
#[inline]
pub fn read_palette_color(
&self,
index: PaletteColorIndex,
is_sprite: bool,
emphasis: EmphasisFlags,
greyscale: bool,
) -> Color {
const BACKGROUND_PALETTES_START_INDEX: u8 = 1;
const SPRITE_PALETTES_START_INDEX: u8 = 17;
let addr = if index.color_index() == 0 {
0
} else {
let start_index = if is_sprite {
SPRITE_PALETTES_START_INDEX
} else {
BACKGROUND_PALETTES_START_INDEX
};
(index.palette_index() * 4) + (index.color_index() - 1) + start_index
} as usize;
let v = if greyscale {
self.data[addr] & 0x30
} else {
self.data[addr]
};
Color::new(v, emphasis)
}
#[inline]
pub fn read_background_palette_color(
&self,
index: PaletteColorIndex,
emphasis: EmphasisFlags,
greyscale: bool,
) -> Color {
self.read_palette_color(index, false, emphasis, greyscale)
}
#[inline]
pub fn read_sprite_palette_color(
&self,
index: PaletteColorIndex,
emphasis: EmphasisFlags,
greyscale: bool,
) -> Color {
self.read_palette_color(index, true, emphasis, greyscale)
}
}
impl devices6502::Device for Ram {
fn with_data(data: &[u8]) -> Self {
let mut new_ram = Ram::default();
new_ram.init_data(data);
new_ram
}
fn init_data(&mut self, data: &[u8]) {
self.data.copy_from_slice(data);
}
fn cache_current_read_data(&self, destination: &mut [u8]) {
destination.copy_from_slice(&self.data);
}
fn read(&self, addr: u16) -> u8 {
self.read(addr as usize)
}
fn write(&mut self, data: u8, addr: u16) {
self.write(addr as usize, data);
}
fn addr_space_size() -> u32 {
PALETTE_RAM_SIZE as u32
}
fn addr_bits_count() -> u8 {
PALETTE_RAM_SIZE.ilog2() as u8
}
fn addr_space_size_dyn(&self) -> u32 {
PALETTE_RAM_SIZE as u32
}
fn addr_bits_count_dyn(&self) -> u8 {
PALETTE_RAM_SIZE.ilog2() as u8
}
}
#[cfg(test)]
mod tests {
use super::*;
fn multiple_of_four(v: usize) -> bool {
(v % 4) == 0
}
#[test]
fn createpaletteram_writevalues_colorscorrect() {
let mut palette_ram = Ram::new();
let colors: Vec<Color> = (0u8..(PALETTE_RAM_SIZE as u8))
.map(|x| Color::new(x, EmphasisFlags::empty()))
.collect();
for (i, color) in colors.iter().enumerate() {
if !multiple_of_four(i) || (i < 16) {
palette_ram.write(i, color.color_index());
}
}
assert_eq!(
palette_ram.read_background_color(EmphasisFlags::empty(), false),
colors[0]
);
for bg_palette_index in 0..4 {
for color_index in 0..4 {
let original_color = if color_index == 0 {
colors[0]
} else {
colors[bg_palette_index * 4 + color_index]
};
let read_ram_color = palette_ram.read_background_palette_color(
PaletteColorIndex::new(bg_palette_index as u8, color_index as u8),
EmphasisFlags::empty(),
false,
);
assert_eq!(original_color, read_ram_color);
}
}
for sprite_palette_index in 0..4 {
for color_index in 0..4 {
let original_color = if color_index == 0 {
colors[0]
} else {
colors[sprite_palette_index * 4 + color_index + 16]
};
let read_ram_color = palette_ram.read_sprite_palette_color(
PaletteColorIndex::new(sprite_palette_index as u8, color_index as u8),
EmphasisFlags::empty(),
false,
);
assert_eq!(original_color, read_ram_color);
}
}
}
#[test]
fn createpaletteram_writemirroredvalues_colorscorrect() {
let mut palette_ram = Ram::new();
let mirrored_pairs: Vec<(usize, usize)> = (0..4).map(|i| (i * 4, i * 4 + 16)).collect();
for (i, (first, second)) in mirrored_pairs.iter().enumerate() {
palette_ram.write(*first, 1 + i as u8);
assert_eq!(palette_ram.read(*second), 1 + i as u8);
palette_ram.write(*second, 10 + i as u8);
assert_eq!(palette_ram.read(*first), 10 + i as u8);
}
}
}