use core::fmt::Debug;
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Status {
Negative = 7,
Overflow = 6,
AlwaysSet = 5,
Break = 4,
Decimal = 3,
InterruptDisable = 2,
Zero = 1,
Carry = 0,
}
impl Status {
pub fn mask(&self) -> u8 {
1 << (*self as u8)
}
}
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Register {
A = 0,
X = 1,
Y = 2,
P = 3,
S = 4,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct RegisterFile {
pc: u16,
r: [u8; 5],
}
impl RegisterFile {
pub fn new() -> Self {
let r: [u8; 5] = [0xAA, 0xCC, 0xD2, 0, 0x01];
Self { pc: 0xFF55, r }
}
pub fn reset(&mut self) {
*self.reg_mut(Register::P) = 0;
self.set_flag(Status::InterruptDisable);
self.set_flag(Status::AlwaysSet);
}
#[inline]
pub fn reg_mut(&mut self, reg: Register) -> &mut u8 {
&mut self.r[(reg as u8) as usize]
}
#[inline]
pub fn reg(&self, reg: Register) -> u8 {
self.r[(reg as u8) as usize]
}
#[inline]
pub fn a(&self) -> u8 {
self.reg(Register::A)
}
#[inline]
pub fn x(&self) -> u8 {
self.reg(Register::X)
}
#[inline]
pub fn y(&self) -> u8 {
self.reg(Register::Y)
}
#[inline]
pub fn sp(&self) -> u8 {
self.reg(Register::S)
}
#[inline]
pub fn pc(&self) -> u16 {
self.pc
}
#[inline]
pub fn a_mut(&mut self) -> &mut u8 {
&mut self.r[(Register::A as u8) as usize]
}
#[inline]
pub fn x_mut(&mut self) -> &mut u8 {
&mut self.r[(Register::X as u8) as usize]
}
#[inline]
pub fn y_mut(&mut self) -> &mut u8 {
&mut self.r[(Register::Y as u8) as usize]
}
#[inline]
pub fn sp_mut(&mut self) -> &mut u8 {
&mut self.r[(Register::S as u8) as usize]
}
#[inline]
pub fn set_pc(&mut self, pc: u16) {
self.pc = pc
}
#[inline]
pub fn adjust_pc_by(&mut self, offset: i8) {
self.pc = self.pc.wrapping_add(offset as i16 as u16);
}
#[inline]
pub fn flag_set(&self, flag: Status) -> bool {
(self.reg(Register::P) & flag.mask()) != 0
}
#[inline]
pub fn set_flag_from_cond(&mut self, flag: Status, cond: bool) {
let p = self.reg_mut(Register::P);
*p = (*p & !flag.mask()) | (cond as u8) << (flag as u8);
}
#[inline]
pub fn set_flag(&mut self, flag: Status) {
self.set_flag_from_cond(flag, true)
}
#[inline]
pub fn clear_flag(&mut self, flag: Status) {
self.set_flag_from_cond(flag, false)
}
}
impl Default for RegisterFile {
fn default() -> Self {
RegisterFile::new()
}
}
impl Debug for RegisterFile {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut flags = ['n', 'v', 'x', 'b', 'd', 'i', 'z', 'c'];
if self.flag_set(Status::Negative) {
flags[7 - (Status::Negative as u8 as usize)] = 'N';
}
if self.flag_set(Status::Overflow) {
flags[7 - (Status::Overflow as u8 as usize)] = 'V';
}
if self.flag_set(Status::AlwaysSet) {
flags[7 - (Status::AlwaysSet as u8 as usize)] = 'X';
}
if self.flag_set(Status::Break) {
flags[7 - (Status::Break as u8 as usize)] = 'B';
}
if self.flag_set(Status::Decimal) {
flags[7 - (Status::Decimal as u8 as usize)] = 'D';
}
if self.flag_set(Status::InterruptDisable) {
flags[7 - (Status::InterruptDisable as u8 as usize)] = 'I';
}
if self.flag_set(Status::Zero) {
flags[7 - (Status::Zero as u8 as usize)] = 'Z';
}
if self.flag_set(Status::Carry) {
flags[7 - (Status::Carry as u8 as usize)] = 'C';
}
f.debug_struct("RegisterFile")
.field("PC", &self.pc)
.field("A", &self.a())
.field("X", &self.x())
.field("Y", &self.y())
.field("SP", &self.sp())
.field("P", &flags)
.finish()
}
}