diff --git a/src/emu.rs b/src/emu.rs index 93439af..7e80b76 100644 --- a/src/emu.rs +++ b/src/emu.rs @@ -1,4 +1,5 @@ -use super::periph::{Display, Keypad, ManagedTimer as Timer, TimerManager}; +use super::periph::{Display, Keypad}; +use super::timer::{Timer, Type as TimerType}; use std::fs::File; use std::io::{self, Read}; use std::path::Path; @@ -357,7 +358,7 @@ impl Chip8 { fn copy_delay_timer_val(&mut self, x: u8) { // set Vx to be the value of the delay timer - self.v[x as usize] = TimerManager::get(Timer::Delay); + self.v[x as usize] = Timer::get(TimerType::Delay); } fn loop_until_key_vx(&mut self, x: u8) { @@ -372,12 +373,12 @@ impl Chip8 { fn set_delay_to_vx(&mut self, x: u8) { // set delay timer to be value of Vx - TimerManager::set(self.v[x as usize], Timer::Delay); + Timer::set(self.v[x as usize], TimerType::Delay); } fn set_sound_to_vx(&mut self, x: u8) { // set sound timer to be value of Vx - TimerManager::set(self.v[x as usize], Timer::Sound); + Timer::set(self.v[x as usize], TimerType::Sound); } fn add_vx_to_i(&mut self, x: u8) { diff --git a/src/lib.rs b/src/lib.rs index 2f0d221..c1efc33 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,2 +1,3 @@ pub mod emu; pub mod periph; +pub mod timer; diff --git a/src/main.rs b/src/main.rs index 7b6496a..2cd8bcb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use chip8::{emu::Chip8, periph::TimerManager}; +use chip8::{emu::Chip8, timer::Timer}; use pixels::{wgpu::Surface, Pixels, SurfaceTexture}; use std::path::Path; use std::time::{Duration, Instant}; @@ -23,7 +23,7 @@ fn main() { let rom_path = Path::new(&path); chip8.load_rom(rom_path).expect("Unable to load ROM"); - TimerManager::start(); + Timer::start_thread(); let mut start = Instant::now(); let frametime = Duration::from_nanos(1e+9 as u64 / OPCODES_PER_SECOND); diff --git a/src/periph.rs b/src/periph.rs index 2cf8729..420da2d 100644 --- a/src/periph.rs +++ b/src/periph.rs @@ -1,14 +1,3 @@ -use lazy_static::lazy_static; -use rodio::{source::SineWave, Sink}; -use std::sync::Mutex; -use std::thread; -use std::time::{Duration, Instant}; - -lazy_static! { - static ref SOUND_TIMER: Mutex = Mutex::new(Timer::default().with_beep()); - static ref DELAY_TIMER: Mutex = Mutex::new(Timer::default()); -} - #[derive(Copy, Clone)] pub struct Display { pub buf: [u8; Self::WIDTH as usize * Self::HEIGHT as usize], @@ -65,112 +54,6 @@ impl Default for Display { } } -pub struct TimerManager; - -pub enum ManagedTimer { - Sound, - Delay, -} - -impl TimerManager { - pub fn start() { - thread::spawn(|| { - let duration = Duration::from_nanos(1e+9 as u64 / 60); - let mut start = Instant::now(); - - loop { - if Instant::now().duration_since(start) > duration { - SOUND_TIMER.lock().unwrap().tick(); - DELAY_TIMER.lock().unwrap().tick(); - start = Instant::now(); - } - } - }); - } - - pub fn get(timer: ManagedTimer) -> u8 { - match timer { - ManagedTimer::Delay => DELAY_TIMER.lock().unwrap().get(), - ManagedTimer::Sound => SOUND_TIMER.lock().unwrap().get(), - } - } - - pub fn set(secs: u8, timer: ManagedTimer) { - match timer { - ManagedTimer::Delay => DELAY_TIMER.lock().unwrap().set(secs), - ManagedTimer::Sound => SOUND_TIMER.lock().unwrap().set(secs), - } - } -} - -#[derive(Debug, Copy, Clone)] -pub struct Timer { - remaining: u8, - enabled: bool, - beep_enabled: bool, -} - -impl Default for Timer { - fn default() -> Self { - Timer { - remaining: 0, - enabled: false, - beep_enabled: false, - } - } -} - -impl Timer { - pub fn with_beep(mut self) -> Self { - self.beep_enabled = true; - self - } - - pub fn set(&mut self, secs: u8) { - self.remaining = secs; - self.enabled = true; - } - - pub fn get(&self) -> u8 { - self.remaining - } - - pub fn tick(&mut self) { - if self.enabled { - if self.remaining == 0 { - self.enabled = false; - - if self.beep_enabled { - Self::beep(); - } - } else { - self.remaining -= 1; - } - } - } - - fn beep() { - let maybe_device = rodio::default_output_device(); - let beep = SineWave::new(440); - - // TODO: Fix Pop when sound ends? - if let Some(device) = maybe_device { - std::thread::spawn(move || { - let sink = Sink::new(&device); - let duration = Duration::from_millis(100); - let start = Instant::now(); - sink.append(beep); - - loop { - if Instant::now().duration_since(start) > duration { - break; - } - } - }); - } - } -} - #[derive(Debug, Copy, Clone)] pub struct Keypad { keys: [bool; 16], diff --git a/src/timer.rs b/src/timer.rs new file mode 100644 index 0000000..aff2dab --- /dev/null +++ b/src/timer.rs @@ -0,0 +1,90 @@ +use rodio::{source::SineWave, Sink}; +use std::sync::atomic::{AtomicU8, Ordering}; +use std::thread; +use std::time::{Duration, Instant}; + +static SOUND_TIMER: AtomicU8 = AtomicU8::new(0); +static DELAY_TIMER: AtomicU8 = AtomicU8::new(0); + +pub enum Type { + Delay, + Sound, +} + +pub struct Timer; + +impl Timer { + pub fn start_thread() { + thread::spawn(|| { + let duration = Duration::from_nanos(1e+9 as u64 / 60); + let mut start = Instant::now(); + let mut has_beeped = false; + loop { + if Instant::now().duration_since(start) > duration { + Timer::tick(&mut has_beeped); + start = Instant::now(); + } + } + }); + } + + fn tick(has_beeped: &mut bool) { + // Sound Timer + let remaining = SOUND_TIMER.load(Ordering::Relaxed); + match (remaining, *has_beeped) { + (0, false) => { + Self::beep(); + *has_beeped = true; + } + (0, true) => {} + _ => { + if *has_beeped { + *has_beeped = false; + } + + SOUND_TIMER.store(remaining - 1, Ordering::Release) + } + } + + // Delay Timer + let remaining = DELAY_TIMER.load(Ordering::Relaxed); + if remaining != 0 { + DELAY_TIMER.store(remaining - 1, Ordering::Release); + } + } + + pub fn get(timer: Type) -> u8 { + match timer { + Type::Delay => DELAY_TIMER.load(Ordering::Acquire), + Type::Sound => SOUND_TIMER.load(Ordering::Acquire), + } + } + + pub fn set(secs: u8, timer: Type) { + match timer { + Type::Delay => DELAY_TIMER.store(secs, Ordering::Relaxed), + Type::Sound => SOUND_TIMER.store(secs, Ordering::Relaxed), + } + } + + fn beep() { + let maybe_device = rodio::default_output_device(); + let beep = SineWave::new(440); + + // TODO: Fix Pop when sound ends? + if let Some(device) = maybe_device { + std::thread::spawn(move || { + let sink = Sink::new(&device); + let duration = Duration::from_millis(100); + let start = Instant::now(); + sink.append(beep); + + loop { + if Instant::now().duration_since(start) > duration { + break; + } + } + }); + } + } +}