use crate::core::error::Result;
use crate::core::state::{Sink, Source};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum Region {
#[default]
Ntsc,
Pal,
Dendy,
}
impl Region {
pub const NAMES: &'static [&'static str] = &["ntsc", "pal", "dendy"];
pub fn from_name(name: &str) -> Option<Region> {
match name {
"ntsc" => Some(Region::Ntsc),
"pal" => Some(Region::Pal),
"dendy" => Some(Region::Dendy),
_ => None,
}
}
pub const fn name(self) -> &'static str {
match self {
Region::Ntsc => "ntsc",
Region::Pal => "pal",
Region::Dendy => "dendy",
}
}
pub const fn part_number(self) -> &'static str {
match self {
Region::Ntsc => "RP2A03",
Region::Pal => "RP2A07",
Region::Dendy => "UA6527P",
}
}
pub const fn master_clock(self) -> (u64, u64) {
match self {
Region::Ntsc => (236_250_000, 11),
Region::Pal | Region::Dendy => (53_203_425, 2),
}
}
pub const fn cpu_divider(self) -> u64 {
match self {
Region::Ntsc => 12,
Region::Pal => 16,
Region::Dendy => 15,
}
}
pub const fn four_step(self) -> [u32; 6] {
match self {
Region::Ntsc | Region::Dendy => [7457, 14913, 22371, 29828, 29829, 29830],
Region::Pal => [8313, 16627, 24939, 33252, 33253, 33254],
}
}
pub const fn five_step(self) -> [u32; 6] {
match self {
Region::Ntsc | Region::Dendy => [7457, 14913, 22371, 29829, 37281, 37282],
Region::Pal => [8313, 16627, 24939, 33253, 41565, 41566],
}
}
}
impl core::fmt::Display for Region {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(match self {
Region::Ntsc => "NTSC",
Region::Pal => "PAL",
Region::Dendy => "Dendy",
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum Mode {
#[default]
FourStep,
FiveStep,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct FrameEvent {
pub quarter: bool,
pub half: bool,
}
impl FrameEvent {
pub const NONE: FrameEvent = FrameEvent {
quarter: false,
half: false,
};
pub const QUARTER: FrameEvent = FrameEvent {
quarter: true,
half: false,
};
pub const BOTH: FrameEvent = FrameEvent {
quarter: true,
half: true,
};
}
#[derive(Debug, Clone)]
pub struct FrameCounter {
region: Region,
mode: Mode,
inhibit: bool,
irq: bool,
irq_set_at: u64,
cycle: u32,
reset_delay: u8,
}
impl FrameCounter {
pub const fn new(region: Region) -> FrameCounter {
FrameCounter {
region,
mode: Mode::FourStep,
inhibit: false,
irq: false,
irq_set_at: u64::MAX,
cycle: 0,
reset_delay: 0,
}
}
#[inline]
pub const fn tv_region(&self) -> Region {
self.region
}
#[inline]
pub const fn mode(&self) -> Mode {
self.mode
}
#[inline]
pub const fn irq(&self) -> bool {
self.irq
}
#[inline]
pub const fn cycle(&self) -> u32 {
self.cycle
}
#[inline]
pub const fn reset_pending(&self) -> bool {
self.reset_delay > 0
}
pub fn read_irq(&mut self, now: u64, peek: bool) -> bool {
let was = self.irq;
if !peek && self.irq_set_at != now {
self.irq = false;
}
was
}
pub fn clear_irq(&mut self) {
self.irq = false;
}
pub fn write(&mut self, value: u8, on_put_cycle: bool) {
self.mode = if value & 0x80 != 0 {
Mode::FiveStep
} else {
Mode::FourStep
};
self.inhibit = value & 0x40 != 0;
if self.inhibit {
self.irq = false;
}
self.reset_delay = if on_put_cycle { 3 } else { 4 };
}
pub fn tick(&mut self, now: u64) -> FrameEvent {
if self.reset_delay > 0 {
self.reset_delay -= 1;
if self.reset_delay == 0 {
self.cycle = 0;
return match self.mode {
Mode::FiveStep => FrameEvent::BOTH,
Mode::FourStep => FrameEvent::NONE,
};
}
}
self.cycle += 1;
match self.mode {
Mode::FourStep => {
let t = self.region.four_step();
if self.cycle == t[0] {
FrameEvent::QUARTER
} else if self.cycle == t[1] {
FrameEvent::BOTH
} else if self.cycle == t[2] {
FrameEvent::QUARTER
} else if self.cycle == t[3] {
self.set_irq(now);
FrameEvent::NONE
} else if self.cycle == t[4] {
self.set_irq(now);
FrameEvent::BOTH
} else if self.cycle == t[5] {
self.set_irq(now);
self.cycle = 0;
FrameEvent::NONE
} else {
FrameEvent::NONE
}
}
Mode::FiveStep => {
let t = self.region.five_step();
if self.cycle == t[0] {
FrameEvent::QUARTER
} else if self.cycle == t[1] {
FrameEvent::BOTH
} else if self.cycle == t[2] {
FrameEvent::QUARTER
} else if self.cycle == t[4] {
FrameEvent::BOTH
} else if self.cycle == t[5] {
self.cycle = 0;
FrameEvent::NONE
} else {
FrameEvent::NONE
}
}
}
}
fn set_irq(&mut self, now: u64) {
if !self.inhibit {
self.irq = true;
self.irq_set_at = now;
}
}
pub fn reset_cold(&mut self) {
*self = FrameCounter::new(self.region);
}
pub fn reset_warm(&mut self) {
self.irq = false;
self.irq_set_at = u64::MAX;
self.cycle = 0;
self.reset_delay = 0;
}
pub fn save(&self, w: &mut dyn Sink) -> Result<()> {
w.write_u8(match self.mode {
Mode::FourStep => 0,
Mode::FiveStep => 1,
})?;
w.write_bool(self.inhibit)?;
w.write_bool(self.irq)?;
w.write_u64(self.irq_set_at)?;
w.write_u32(self.cycle)?;
w.write_u8(self.reset_delay)
}
pub fn load<'a>(&mut self, r: &mut dyn Source<'a>) -> Result<()> {
self.mode = if r.read_u8()? == 0 {
Mode::FourStep
} else {
Mode::FiveStep
};
self.inhibit = r.read_bool()?;
self.irq = r.read_bool()?;
self.irq_set_at = r.read_u64()?;
self.cycle = r.read_u32()?;
self.reset_delay = r.read_u8()?;
Ok(())
}
}