use crate::common::{Clock, NesRegion, Reset, ResetKind};
use bitflags::bitflags;
use serde::{Deserialize, Serialize};
#[derive(Default, Serialize, Deserialize, Debug, Copy, Clone)]
#[must_use]
pub struct Mask {
pub emphasis: u16,
pub grayscale: u8,
pub rendering_enabled: bool,
pub prev_rendering_enabled: bool,
pub pending_rendering_update: bool,
pub show_left_bg: bool,
pub show_left_spr: bool,
pub show_bg: bool,
pub show_spr: bool,
pub bits: Bits,
pub region: NesRegion,
}
bitflags! {
#[derive(Default, Serialize, Deserialize, Debug, Copy, Clone)]
#[must_use]
pub struct Bits: u8 {
const GRAYSCALE = 0x01;
const SHOW_LEFT_BG = 0x02;
const SHOW_LEFT_SPR = 0x04;
const SHOW_BG = 0x08;
const SHOW_SPR = 0x10;
const EMPHASIZE_RED = 0x20;
const EMPHASIZE_GREEN = 0x40;
const EMPHASIZE_BLUE = 0x80;
}
}
impl Mask {
pub fn new(region: NesRegion) -> Self {
let mut mask = Self {
region,
..Default::default()
};
mask.write(0);
mask
}
#[inline]
pub fn write(&mut self, val: u8) {
self.bits = Bits::from_bits_truncate(val);
self.grayscale = if self.bits.contains(Bits::GRAYSCALE) {
0x30
} else {
0x3F
};
self.show_left_bg = self.bits.contains(Bits::SHOW_LEFT_BG);
self.show_left_spr = self.bits.contains(Bits::SHOW_LEFT_SPR);
self.show_bg = self.bits.contains(Bits::SHOW_BG);
self.show_spr = self.bits.contains(Bits::SHOW_SPR);
self.pending_rendering_update = self.rendering_enabled != (self.show_bg || self.show_spr);
self.update_emphasis();
}
pub fn update_emphasis(&mut self) {
self.emphasis = u16::from(
match self.region {
NesRegion::Auto | NesRegion::Ntsc => self.bits.intersection(
Bits::EMPHASIZE_RED | Bits::EMPHASIZE_GREEN | Bits::EMPHASIZE_BLUE,
),
NesRegion::Pal | NesRegion::Dendy => {
let mut emphasis = self.bits.intersection(Bits::EMPHASIZE_BLUE);
emphasis.set(
Bits::EMPHASIZE_GREEN,
self.bits.contains(Bits::EMPHASIZE_RED),
);
emphasis.set(
Bits::EMPHASIZE_RED,
self.bits.contains(Bits::EMPHASIZE_GREEN),
);
emphasis
}
}
.bits(),
) << 1;
}
#[inline]
pub fn set_region(&mut self, region: NesRegion) {
self.region = region;
self.update_emphasis();
}
}
impl Reset for Mask {
fn reset(&mut self, _kind: ResetKind) {
self.write(0);
}
}
impl Clock for Mask {
fn clock(&mut self) {
if self.pending_rendering_update {
self.pending_rendering_update = false;
self.prev_rendering_enabled = self.rendering_enabled;
self.rendering_enabled = self.show_bg || self.show_spr;
self.pending_rendering_update = self.prev_rendering_enabled != self.rendering_enabled;
}
}
}