From af35cfc712177ee050594fd3bdb64637ed582d40 Mon Sep 17 00:00:00 2001 From: Steins7 Date: Sun, 10 Mar 2024 22:47:12 +0100 Subject: [PATCH] Implement proper clock management For some reason, sleep causes some issues in release in the egl crate. Maybe some timing issue ? In debug, the program is way slower than it should be --- src/bus.rs | 12 +++++++++++- src/cpu.rs | 18 +++++++++++++----- src/main.rs | 34 ++++++++++++++++++++++++++++------ 3 files changed, 52 insertions(+), 12 deletions(-) diff --git a/src/bus.rs b/src/bus.rs index d8acc29..ce943a0 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -14,6 +14,7 @@ pub struct Bus { ppu: Ppu, apu: Ram<0x1F>,//TODO cartridge: Rc>, + tick_counter: u8, } impl fmt::Debug for Bus { @@ -119,11 +120,20 @@ impl Bus { ppu: Ppu::new(mapper.clone(), screen, pattern_table), apu: Ram::<0x1F>::new(), cartridge: mapper, + tick_counter: 0, } } pub fn tick(&mut self) { - self.ppu.tick(); + + if self.tick_counter >= 3 { + self.tick_counter = 0; + self.ppu.tick(); + } else { + self.tick_counter += 1; + } + + } } diff --git a/src/cpu.rs b/src/cpu.rs index 940b251..c9cff94 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -151,6 +151,7 @@ pub struct Cpu { s: u8, p: StatusReg, bus: Bus, + tick_counter: u8, } impl fmt::Debug for Cpu { @@ -189,16 +190,23 @@ impl Cpu { s: 0xFD, p: StatusReg::from_bits(0x34).unwrap(), bus, + tick_counter: 0, } } - pub fn tick(&mut self) { + pub fn tick(&mut self) { - self.execute(); - self.bus.tick(); - } + if self.tick_counter >= 11 { + self.tick_counter = 0; + self.execute(); + } else { + self.tick_counter += 1; + } - fn execute(&mut self) { + self.bus.tick(); + } + + fn execute(&mut self) { let opcode = self.read_pc_addr(); // if self.pc-1 > 0x36D0 && self.pc-1 < 0x36D8 { diff --git a/src/main.rs b/src/main.rs index 60cdb78..3c0740c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,6 +25,12 @@ use canvas::{ }; use utils::DisplayBuffer; +///Nanoseconds for 12 periods of the master clock (21.478 MHz). The frequency is divided by 12 since +///this is the CPU clock. All faster peripherals (i.e. the PPU, will be run whithout timing +///considerations whithing these 12 periods. This shouldn't affect the system's behavior while +///improving the clock's precision +const MASTER_CLOCK_PERIOD_NS: u128 = 559; + struct EmulatorState { pub screen: Rc>, pub screen_sprite: TextureSprite, @@ -95,15 +101,31 @@ impl Application for NesEmulator { fn tick(state: &mut EmulatorState, canvas: &mut Canvas) -> Result<(), &'static str> { - let frame_time = state.start.elapsed().as_secs_f32(); - state.start = Instant::now(); - canvas.clear(); - state.cpu.tick(); + //save frame start for frame time computation + let frame_start = state.start; - if state.screen.borrow_mut().is_ready { - state.screen.borrow_mut().is_ready = false; + //tick the system for 1 frame + while state.screen.borrow_mut().is_ready == false { + + let tick_period = state.start.elapsed().as_nanos(); + //this should never fail as id frame_period doesn't fit in a u64, then the substraction + //saturated and the resulting 0 now fits + let delta = MASTER_CLOCK_PERIOD_NS.saturating_sub(tick_period) as u64; + std::thread::sleep(std::time::Duration::from_nanos(delta)); + state.start = Instant::now(); + + for _ in 0..12 { + //TODO the cpu is ticked 12 times since MASTER_CLOCK_PERIOD_NS corresponds to 12 + //clock periods. Further division is done in the function + state.cpu.tick(); + } } + state.screen.borrow_mut().is_ready = false; + //draw frame + let frame_time = frame_start.elapsed().as_secs_f32(); + + canvas.clear(); canvas.draw(&mut state.screen_sprite); canvas.draw(&mut state.pattern_sprite);