diff options
author | Determinant <[email protected]> | 2017-12-09 15:30:31 +0800 |
---|---|---|
committer | Determinant <[email protected]> | 2017-12-09 15:30:31 +0800 |
commit | bc6440d96f3226d39a4a56dcc55dc7809f7fbc37 (patch) | |
tree | 45526a847f9dcc6b1bcd56b18dd0b4bb64cc4103 /src | |
parent | 1f664ad8d6a820a73bba52bc78b6e2fbb1b12345 (diff) |
clock by audio
Diffstat (limited to 'src')
-rw-r--r-- | src/apu.rs | 15 | ||||
-rw-r--r-- | src/bin.rs | 136 | ||||
-rw-r--r-- | src/memory.rs | 16 | ||||
-rw-r--r-- | src/mos6502.rs | 9 | ||||
-rw-r--r-- | src/ppu.rs | 4 |
5 files changed, 120 insertions, 60 deletions
@@ -3,7 +3,6 @@ use mos6502; pub trait Speaker { fn queue(&mut self, sample: u16); - fn push(&mut self); } const CPU_SAMPLE_FREQ: u32 = 240; @@ -72,7 +71,7 @@ const TND_TABLE: [u16; 203] = [ 0xbbfe, 0xbc84, 0xbd09, 0xbd8d, 0xbe11 ]; -struct Sampler { +pub struct Sampler { freq2: u32, q0: u32, r0: u32, @@ -82,7 +81,7 @@ struct Sampler { } impl Sampler { - fn new(freq1: u32, freq2: u32) -> Self { + pub fn new(freq1: u32, freq2: u32) -> Self { let q0 = freq1 / freq2; let r0 = freq1 - q0 * freq2; Sampler { @@ -95,7 +94,7 @@ impl Sampler { } } - fn tick(&mut self) -> (bool, bool) { + pub fn tick(&mut self) -> (bool, bool) { let (q, r) = self.ddl; if self.cnt == q { let nr = r + self.r0; @@ -415,16 +414,10 @@ impl<'a> APU<'a> { let mut irq = false; if let (true, _) = self.cpu_sampler.tick() { irq = self.tick_frame_counter(); - //print!("+"); } - if let (true, sec) = self.audio_sampler.tick() { + if let (true, _) = self.audio_sampler.tick() { let sample = self.output(); self.spkr.queue(sample); - //print!("."); - if sec { - self.spkr.push(); - //println!("ok"); - } } self.tick_timer(); self.cycle_even = !self.cycle_even; @@ -1,6 +1,7 @@ extern crate core; use std::fs::File; +use std::sync::Mutex; use std::io::Read; use std::cell::RefCell; use std::intrinsics::transmute; @@ -14,7 +15,7 @@ use sdl2::rect::Rect; use sdl2::pixels::PixelFormatEnum; use sdl2::event::Event; use sdl2::keyboard::Keycode; -use sdl2::audio::{AudioSpecDesired}; +use sdl2::audio::{AudioCallback, AudioSpecDesired}; mod memory; #[macro_use] mod mos6502; @@ -91,8 +92,6 @@ struct SDLWindow<'a> { events: sdl2::EventPump, frame_buffer: [u8; FB_SIZE], texture: sdl2::render::Texture, - timer: Instant, - duration_per_frame: Duration, p1_button_state: u8, p1_ctl: &'a stdctl::Joystick, p1_keymap: [u8; 256], @@ -130,10 +129,8 @@ impl<'a> SDLWindow<'a> { frame_buffer: [0; FB_SIZE], texture: texture_creator.create_texture_streaming( PixelFormatEnum::RGB24, WIN_WIDTH, WIN_HEIGHT).unwrap(), - timer: Instant::now(), - duration_per_frame: Duration::from_millis(1000 / 60), p1_button_state: 0, - p1_ctl, p1_keymap: [stdctl::NULL; 256] + p1_ctl, p1_keymap: [stdctl::NULL; 256], }; { let keymap = &mut res.p1_keymap; @@ -178,7 +175,7 @@ impl<'a> SDLWindow<'a> { } } -#[inline] +#[inline(always)] fn get_rgb(color: u8) -> (u8, u8, u8) { let c = RGB_COLORS[color as usize]; ((c >> 16) as u8, ((c >> 8) & 0xff) as u8, (c & 0xff) as u8) @@ -209,44 +206,107 @@ impl<'a> ppu::Screen for SDLWindow<'a> { fn render(&mut self) { self.texture.update(None, &self.frame_buffer, FB_PITCH).unwrap(); + //canvas.set_draw_color(Color::RGB(128, 128, 128)); + } + + fn frame(&mut self) { self.canvas.clear(); self.canvas.copy(&self.texture, None, Some(Rect::new(0, 0, WIN_WIDTH, WIN_HEIGHT))).unwrap(); self.canvas.present(); if self.poll() {std::process::exit(0);} - let e = self.timer.elapsed(); - if self.duration_per_frame > e { - let diff = self.duration_per_frame - e; - sleep(diff); - //println!("{} faster", diff.subsec_nanos() as f64 / 1e6); - } else { - //println!("{} slower", (e - self.duration_per_frame).subsec_nanos() as f64 / 1e6); + } +} + +struct CircularBuffer { + buffer: [i16; 65536], + head: usize, + tail: usize +} + +impl CircularBuffer { + fn new() -> Self { + CircularBuffer { + buffer: [0; 65536], + head: 0, + tail: 1 + } + } + + fn enque(&mut self, sample: i16) { + self.buffer[self.tail] = sample; + self.tail += 1; + if self.tail == self.buffer.len() { + self.tail = 0 + } + } + + fn deque(&mut self) -> i16 { + let res = self.buffer[self.head]; + { + let mut h = self.head + 1; + if h == self.buffer.len() { + h = 0 + } + if h != self.tail { + self.head = h + } } - self.timer = Instant::now(); - //canvas.set_draw_color(Color::RGB(128, 128, 128)); + res } } struct SDLAudio<'a> { - device: &'a sdl2::audio::AudioQueue<i16>, + timer: Instant, + duration_per_frame: Duration, + lagged: Duration, + cnt: u16, + buffer: &'a Mutex<&'a mut CircularBuffer>, } impl<'a> SDLAudio<'a> { - fn new(device: &'a sdl2::audio::AudioQueue<i16>) -> Self { - let t = SDLAudio { - device - }; - t.device.resume(); - t + fn new(lbuff: &'a Mutex<&'a mut CircularBuffer>) -> Self { + SDLAudio { + timer: Instant::now(), + duration_per_frame: Duration::from_millis(10), + lagged: Duration::new(0, 0), + cnt: 0, + buffer: lbuff, + } } } -impl<'a> apu::Speaker for SDLAudio<'a> { - fn queue(&mut self, sample: u16) { - self.device.queue(&[sample.wrapping_sub(32768) as i16]); +struct SDLAudioPlayback<'a>(&'a Mutex<&'a mut CircularBuffer>); + +impl<'a> AudioCallback for SDLAudioPlayback<'a> { + type Channel = i16; + fn callback(&mut self, out: &mut[i16]) { + let mut b = self.0.lock().unwrap(); + for x in out.iter_mut() { + *x = b.deque() + } } +} - fn push(&mut self) { +impl<'a> apu::Speaker for SDLAudio<'a> { + fn queue(&mut self, sample: u16) { + let mut b = self.buffer.lock().unwrap(); + b.enque(sample.wrapping_sub(32768) as i16); + self.cnt += 1; + if self.cnt == apu::AUDIO_SAMPLE_FREQ as u16 / 100 { + let e = self.timer.elapsed(); + if self.duration_per_frame > e { + let mut diff = self.duration_per_frame - e; + let delta = std::cmp::min(diff, self.lagged); + diff -= delta; + self.lagged -= delta; + sleep(diff); + } else { + self.lagged += e - self.duration_per_frame + } + self.cnt = 0; + self.timer = Instant::now(); + } } } @@ -335,37 +395,33 @@ fn main() { } */ + /* audio */ let sdl_context = sdl2::init().unwrap(); let audio_subsystem = sdl_context.audio().unwrap(); + let mut buff = CircularBuffer::new(); + let lbuff = Mutex::new(&mut buff); + let mut spkr = SDLAudio::new(&lbuff); let desired_spec = AudioSpecDesired { freq: Some(apu::AUDIO_SAMPLE_FREQ as i32), channels: Some(1), samples: Some(4096) }; - let device = audio_subsystem.open_queue::<i16, _>(None, &desired_spec).unwrap(); + let device = audio_subsystem.open_playback(None, &desired_spec, |_| { + SDLAudioPlayback(&lbuff) + }).unwrap(); + device.resume(); let p1ctl = stdctl::Joystick::new(); let cart = SimpleCart::new(chr_rom, prg_rom, sram, mirror); let mut win = SDLWindow::new(&sdl_context, &p1ctl); - let mut spkr = SDLAudio::new(&device); let mut m: Box<mapper::Mapper> = match mapper_id { 0 | 2 => Box::new(mapper::Mapper2::new(cart)), 1 => Box::new(mapper::Mapper1::new(cart)), _ => panic!("unsupported mapper {}", mapper_id) }; - let dur_sec = Duration::from_millis(1000); - let mut tim_sec = Instant::now(); - let mut f = || { - let e = tim_sec.elapsed(); - if e < dur_sec { - let diff = dur_sec - e; - sleep(diff); - } - tim_sec = Instant::now(); - }; let mapper = RefCell::new(&mut (*m) as &mut mapper::Mapper); - let mut cpu = CPU::new(CPUMemory::new(&mapper, Some(&p1ctl), None), &mut f); + let mut cpu = CPU::new(CPUMemory::new(&mapper, Some(&p1ctl), None)/*, &mut f*/); let mut ppu = PPU::new(PPUMemory::new(&mapper), &mut win); let mut apu = APU::new(&mut spkr); let cpu_ptr = &mut cpu as *mut CPU; diff --git a/src/memory.rs b/src/memory.rs index ecafe86..db7b200 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,7 +1,7 @@ #![allow(dead_code)] use ppu::PPU; -use apu::APU; -use mos6502::CPU; +use apu::{APU, Sampler}; +use mos6502::{CPU, CPU_FREQ}; use cartridge::MirrorType; use mapper::Mapper; use controller::Controller; @@ -16,14 +16,17 @@ pub trait VMem { pub struct CPUBus<'a> { cpu: *mut CPU<'a>, ppu: *mut PPU<'a>, - apu: *mut APU<'a> + apu: *mut APU<'a>, + ppu_sampler: RefCell<Sampler>, } impl<'a> CPUBus<'a> { pub fn new() -> Self { CPUBus {ppu: null_mut(), cpu: null_mut(), - apu: null_mut()} + apu: null_mut(), + ppu_sampler: RefCell::new(Sampler::new(CPU_FREQ, 60)), + } } pub fn attach(&mut self, cpu: *mut CPU<'a>, @@ -48,6 +51,9 @@ impl<'a> CPUBus<'a> { if apu.tick() { cpu.trigger_irq() } + if let (true, _) = self.ppu_sampler.borrow_mut().tick() { + ppu.scr.frame() + } cpu.tick(); } } @@ -74,6 +80,7 @@ impl<'a> CPUMemory<'a> { &self.bus } + #[inline(always)] pub fn read_without_tick(&self, addr: u16) -> u8 { let cpu = self.bus.get_cpu(); let ppu = self.bus.get_ppu(); @@ -101,6 +108,7 @@ impl<'a> CPUMemory<'a> { } } + #[inline(always)] pub fn write_without_tick(&mut self, addr: u16, data: u8) { let cpu = self.bus.get_cpu(); let ppu = self.bus.get_ppu(); diff --git a/src/mos6502.rs b/src/mos6502.rs index 122a549..d7bcb04 100644 --- a/src/mos6502.rs +++ b/src/mos6502.rs @@ -605,7 +605,7 @@ pub struct CPU<'a> { pub elapsed: u32, int: Option<IntType>, pub mem: CPUMemory<'a>, - sec_callback: &'a mut FnMut(), + //sec_callback: &'a mut FnMut(), } macro_rules! make_int { @@ -639,7 +639,8 @@ impl<'a> CPU<'a> { #[inline(always)] pub fn get_over(&self) -> u8 { (self.status >> 6) & 1 } #[inline(always)] pub fn get_neg(&self) -> u8 { (self.status >> 7) & 1 } - pub fn new(mem: CPUMemory<'a>, sec_callback: &'a mut FnMut()) -> Self { + pub fn new(mem: CPUMemory<'a>/*, + sec_callback: &'a mut FnMut()*/) -> Self { let pc = 0; /* nes power up state */ let a = 0; @@ -654,7 +655,7 @@ impl<'a> CPU<'a> { opr: 0, ea: 0, imm_val: 0, int: None, acc: false, - mem, elapsed: 0, sec_callback} + mem, elapsed: 0/*, sec_callback*/} } pub fn powerup(&mut self) { @@ -693,11 +694,13 @@ impl<'a> CPU<'a> { pub fn tick(&mut self) { self.cycle -= 1; + /* self.elapsed += 1; if self.elapsed == CPU_FREQ { self.elapsed = 0; (self.sec_callback)(); } + */ } pub fn reset(&mut self) { @@ -6,6 +6,7 @@ use core::intrinsics::transmute; pub trait Screen { fn put(&mut self, x: u8, y: u8, color: u8); fn render(&mut self); + fn frame(&mut self); } #[repr(C, packed)] @@ -52,7 +53,7 @@ pub struct PPU<'a> { early_read: bool, /* IO */ mem: PPUMemory<'a>, - scr: &'a mut Screen, + pub scr: &'a mut Screen, } impl<'a> PPU<'a> { @@ -199,7 +200,6 @@ impl<'a> PPU<'a> { const FLAG_OVERFLOW: u8 = 1 << 5; const FLAG_SPRITE_ZERO: u8 = 1 << 6; const FLAG_VBLANK: u8 = 1 << 7; - #[inline(always)] fn fetch_nametable_byte(&mut self) { self.bg_nt = self.mem.read_nametable(self.v & 0x0fff); |