use core::fmt;
use super::engine::DOTS_PER_SCANLINE;
pub const RESET_LOCKOUT_CPU_CYCLES: u64 = 29_658;
pub const BORDER_BLACK: u8 = 0x0E;
#[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 => "RP2C02",
Region::Pal => "RP2C07",
Region::Dendy => "UA6538",
}
}
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 dot_divider(self) -> u64 {
match self {
Region::Ntsc => 4,
Region::Pal | Region::Dendy => 5,
}
}
pub const fn geometry(self) -> Geometry {
let cpu = self.cpu_divider();
let dot = self.dot_divider();
let warmup_dots = RESET_LOCKOUT_CPU_CYCLES * cpu / dot;
let nmi_announce_dots = cpu.div_ceil(dot);
let (scanlines_per_frame, picture_height, post_render_lines, vblank_lines, odd_frame_skip) =
match self {
Region::Ntsc => (262, 240, 1, 20, true),
Region::Pal => (312, 239, 1, 70, false),
Region::Dendy => (312, 239, 51, 20, false),
};
Geometry {
scanlines_per_frame,
visible_scanlines: VISIBLE_SCANLINES,
picture_height,
post_render_lines,
vblank_scanline: VISIBLE_SCANLINES + post_render_lines,
vblank_lines,
pre_render_scanline: scanlines_per_frame - 1,
odd_frame_skip,
dots_per_frame: DOTS_PER_SCANLINE as u64 * scanlines_per_frame as u64,
warmup_dots,
nmi_announce_dots,
}
}
}
impl fmt::Display for Region {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Region::Ntsc => "NTSC",
Region::Pal => "PAL",
Region::Dendy => "Dendy",
})
}
}
const VISIBLE_SCANLINES: u16 = 240;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Geometry {
pub scanlines_per_frame: u16,
pub visible_scanlines: u16,
pub picture_height: u16,
pub post_render_lines: u16,
pub vblank_scanline: u16,
pub vblank_lines: u16,
pub pre_render_scanline: u16,
pub odd_frame_skip: bool,
pub dots_per_frame: u64,
pub warmup_dots: u64,
pub nmi_announce_dots: u64,
}
impl Geometry {
#[inline]
pub const fn top_border_lines(&self) -> u16 {
self.visible_scanlines - self.picture_height
}
}