Initial commit: NES emulator with GTK4 desktop frontend
Some checks failed
CI / rust (push) Has been cancelled
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.
This commit is contained in:
83
src/runtime/timing.rs
Normal file
83
src/runtime/timing.rs
Normal file
@@ -0,0 +1,83 @@
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user