#[cfg(not(target_arch = "wasm32"))]
use serde_derive::{Deserialize, Serialize};
#[cfg_attr(not(target_arch = "wasm32"), derive(Deserialize, Serialize))]
pub struct Registers {
pub high_tile_byte: u8,
pub low_tile_byte: u8,
pub nametable_byte: u8,
pub palette: u8,
pub tile: u64,
pub v: u16, pub t: u16, pub x: u8, pub w: u8,
pub nametable_address: u16,
pub vram_address_increment: u16,
pub sprite_pattern_table_address: u16,
pub background_pattern_table_address: u16,
pub sprite_size: (u8, u8),
pub is_master: bool,
pub nmi_enabled: bool,
pub greyscale_enabled: bool,
pub show_left_background: bool,
pub show_left_sprites: bool,
pub show_background: bool,
pub show_sprites: bool,
pub emphasize_red: bool,
pub emphasize_green: bool,
pub emphasize_blue: bool,
pub rendering_enabled: bool,
pub sprite_overflow: bool,
pub sprite_0_hit: bool,
pub v_blank_started: bool,
pub oam_addr: u8,
pub scroll_x: u8,
pub scroll_y: u8,
pub bus_address: u16,
pub buffer: u8,
pub last_written_byte: u8,
}
const NAMETABLE_ADDRESSES: [u16; 4] = [0x2000, 0x2400, 0x2800, 0x2C00];
impl Registers {
pub fn new() -> Registers {
Registers {
high_tile_byte: 0,
low_tile_byte: 0,
nametable_byte: 0,
palette: 0,
tile: 0,
v: 0,
t: 0,
x: 0,
w: 0,
nametable_address: 0,
vram_address_increment: 0,
sprite_pattern_table_address: 0,
background_pattern_table_address: 0,
sprite_size: (8, 8),
is_master: false,
nmi_enabled: false,
greyscale_enabled: false,
show_left_background: false,
show_left_sprites: false,
show_background: false,
show_sprites: false,
emphasize_red: false,
emphasize_green: false,
emphasize_blue: false,
rendering_enabled: false,
sprite_overflow: false,
sprite_0_hit: false,
v_blank_started: false,
oam_addr: 0,
scroll_x: 0,
scroll_y: 0,
bus_address: 0,
buffer: 0,
last_written_byte: 0,
}
}
pub fn increment_scroll_x(&mut self) {
if (self.v & 0x001F) == 31 {
self.v &= !0x001F;
self.v ^= 0x0400;
} else {
self.v += 1;
}
}
pub fn increment_scroll_y(&mut self) {
if (self.v & 0x7000) != 0x7000 {
self.v += 0x1000;
} else {
self.v &= !0x7000;
let mut y = (self.v & 0x03E0) >> 5;
if y == 29 {
y = 0;
self.v ^= 0x0800;
} else if y == 31 {
y = 0;
} else {
y += 1;
}
self.v = (self.v & !0x03E0) | (y << 5);
}
}
pub fn copy_scroll_x(&mut self) {
self.v = (self.v & 0xFBE0) | (self.t & 0x041F)
}
pub fn copy_scroll_y(&mut self) {
self.v = (self.v & 0x841F) | (self.t & 0x7BE0)
}
pub fn read_ppu_status(&mut self) -> u8 {
let ret = (self.last_written_byte & 0x1F)
| if self.sprite_overflow { 0x20 } else { 0 }
| if self.sprite_0_hit { 0x40 } else { 0 }
| if self.v_blank_started { 0x80 } else { 0 };
self.v_blank_started = false;
self.w = 0;
ret
}
pub fn write_ppu_ctrl(&mut self, val: u8) {
self.nametable_address = NAMETABLE_ADDRESSES[(val & 0x3) as usize];
self.vram_address_increment = if val & 0x04 != 0 { 32 } else { 1 };
self.sprite_pattern_table_address = if val & 0x08 != 0 { 0x1000 } else { 0x0000 };
self.background_pattern_table_address = if val & 0x10 != 0 { 0x1000 } else { 0x0000 };
self.sprite_size = if val & 0x20 != 0 { (8, 16) } else { (8, 8) };
self.is_master = val & 0x40 != 0;
self.nmi_enabled = val & 0x80 != 0;
self.t = (self.t & !0x0C00) | ((u16::from(val) & 0x03) << 10);
}
pub fn write_ppu_mask(&mut self, val: u8) {
self.greyscale_enabled = val & 0x01 != 0;
self.show_left_background = val & 0x02 != 0;
self.show_left_sprites = val & 0x04 != 0;
self.show_background = val & 0x08 != 0;
self.show_sprites = val & 0x10 != 0;
self.emphasize_red = val & 0x20 != 0;
self.emphasize_green = val & 0x40 != 0;
self.emphasize_blue = val & 0x80 != 0;
self.rendering_enabled = self.show_background || self.show_sprites;
}
pub fn write_ppu_scroll(&mut self, val: u8) {
if self.w == 0 {
self.t = (self.t & !0x001F) | (u16::from(val) >> 3);
self.x = val & 0x07;
self.w = 1;
} else {
self.t = (self.t & !0x73E0)
| ((u16::from(val) & 0x07) << 12)
| ((u16::from(val) & 0xF8) << 2);
self.w = 0;
}
}
pub fn write_ppu_addr(&mut self, val: u8) {
if self.w == 0 {
self.t = (self.t & !0x7F00) | ((u16::from(val) & 0x3F) << 8);
self.w = 1;
} else {
self.t = (self.t & !0x00FF) | u16::from(val);
self.v = self.t;
self.bus_address = self.t & 0x3FFF;
self.w = 0;
}
}
}
impl Default for Registers {
fn default() -> Self {
Registers::new()
}
}