Some checks failed
CI / rust (push) Has been cancelled
Full NES emulation: CPU, PPU, APU, 47 mappers, iNES/NES 2.0 parsing. GTK4 desktop client with HeaderBar, pixel-perfect Cairo rendering, drag-and-drop ROM loading, and keyboard shortcuts. 187 tests covering core emulation, mappers, and runtime.
84 lines
2.1 KiB
Rust
84 lines
2.1 KiB
Rust
use std::time::{Duration, Instant};
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
#[non_exhaustive]
|
|
pub enum VideoMode {
|
|
Ntsc,
|
|
Pal,
|
|
Dendy,
|
|
}
|
|
|
|
impl VideoMode {
|
|
pub fn from_ines_timing_mode(mode: u8) -> Self {
|
|
match mode {
|
|
1 => Self::Pal,
|
|
3 => Self::Dendy,
|
|
_ => Self::Ntsc,
|
|
}
|
|
}
|
|
|
|
pub fn cpu_hz(self) -> f64 {
|
|
match self {
|
|
Self::Ntsc => 1_789_773.0,
|
|
Self::Pal => 1_662_607.0,
|
|
Self::Dendy => 1_773_448.0,
|
|
}
|
|
}
|
|
|
|
pub fn frame_hz(self) -> f64 {
|
|
match self {
|
|
Self::Ntsc => 60.098_8,
|
|
Self::Pal => 50.007_0,
|
|
Self::Dendy => 50.0,
|
|
}
|
|
}
|
|
|
|
pub fn frame_duration(self) -> Duration {
|
|
Duration::from_secs_f64(1.0 / self.frame_hz())
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct FramePacer {
|
|
frame_duration: Duration,
|
|
next_deadline: Option<Instant>,
|
|
}
|
|
|
|
impl FramePacer {
|
|
pub fn new(mode: VideoMode) -> Self {
|
|
Self::with_frame_duration(mode.frame_duration())
|
|
}
|
|
|
|
pub fn with_frame_duration(frame_duration: Duration) -> Self {
|
|
Self {
|
|
frame_duration,
|
|
next_deadline: None,
|
|
}
|
|
}
|
|
|
|
pub fn reset(&mut self) {
|
|
self.next_deadline = None;
|
|
}
|
|
|
|
pub fn wait_next_frame(&mut self) {
|
|
let now = Instant::now();
|
|
match self.next_deadline {
|
|
None => {
|
|
self.next_deadline = Some(now + self.frame_duration);
|
|
}
|
|
Some(deadline) => {
|
|
if now < deadline {
|
|
std::thread::sleep(deadline - now);
|
|
self.next_deadline = Some(deadline + self.frame_duration);
|
|
} else {
|
|
let frame_ns = self.frame_duration.as_nanos();
|
|
let late_ns = now.duration_since(deadline).as_nanos();
|
|
let missed = (late_ns / frame_ns) + 1;
|
|
self.next_deadline =
|
|
Some(deadline + self.frame_duration.mul_f64(missed as f64 + 1.0));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|