use crate::common::{Clock, Reset, ResetKind};
use serde::{Deserialize, Serialize};
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[must_use]
pub enum Revision {
A,
#[default]
BC,
Acc,
}
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
#[must_use]
pub struct Mmc3 {
pub revision: Revision,
pub bank_select: u8,
pub bank_values: [u8; 8],
pub irq_latch: u8,
pub irq_counter: u8,
pub irq_enabled: bool,
pub irq_pending: bool,
pub irq_reload: bool,
pub master_clock: u32,
pub a12_low_clock: u32,
}
impl Mmc3 {
pub const fn set_revision(&mut self, revision: Revision) {
self.revision = revision;
}
pub const fn write_bank_select(&mut self, val: u8) {
self.bank_select = val;
}
pub const fn write_bank_data(&mut self, val: u8) {
self.bank_values[(self.bank_select & 0x07) as usize] = val;
}
pub const fn write_irq_latch(&mut self, val: u8) {
self.irq_latch = val;
}
pub const fn write_irq_reload(&mut self) {
self.irq_reload = true;
}
pub const fn write_irq_disable(&mut self) {
self.irq_enabled = false;
self.irq_pending = false;
}
pub const fn write_irq_enable(&mut self) {
self.irq_enabled = true;
}
const fn is_a12_rising_edge(&mut self, addr: u16) -> bool {
if addr & 0x1000 > 0 {
let is_rising_edge =
self.a12_low_clock > 0 && self.master_clock.wrapping_sub(self.a12_low_clock) >= 4;
self.a12_low_clock = 0;
return is_rising_edge;
} else if self.a12_low_clock == 0 {
self.a12_low_clock = self.master_clock;
}
false
}
pub const fn clock_irq(&mut self, addr: u16) {
if self.is_a12_rising_edge(addr) {
let counter = self.irq_counter;
if self.irq_counter == 0 || self.irq_reload {
self.irq_counter = self.irq_latch;
} else {
self.irq_counter -= 1;
}
if matches!(self.revision, Revision::A) {
if (counter > 0 || self.irq_reload) && self.irq_counter == 0 && self.irq_enabled {
self.irq_pending = true;
}
} else if self.irq_counter == 0 && self.irq_enabled {
self.irq_pending = true;
}
self.irq_reload = false;
}
}
#[must_use]
pub const fn irq_pending(&self) -> bool {
self.irq_pending
}
}
impl Reset for Mmc3 {
fn reset(&mut self, _kind: ResetKind) {
let revision = self.revision;
*self = Self::default();
self.revision = revision;
}
}
impl Clock for Mmc3 {
fn clock(&mut self) {
self.master_clock = self.master_clock.wrapping_add(1);
}
}