aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDeterminant <ted.sybil@gmail.com>2017-12-09 15:30:31 +0800
committerDeterminant <ted.sybil@gmail.com>2017-12-09 15:30:31 +0800
commitbc6440d96f3226d39a4a56dcc55dc7809f7fbc37 (patch)
tree45526a847f9dcc6b1bcd56b18dd0b4bb64cc4103
parent1f664ad8d6a820a73bba52bc78b6e2fbb1b12345 (diff)
clock by audio
-rw-r--r--src/apu.rs15
-rw-r--r--src/bin.rs136
-rw-r--r--src/memory.rs16
-rw-r--r--src/mos6502.rs9
-rw-r--r--src/ppu.rs4
5 files changed, 120 insertions, 60 deletions
diff --git a/src/apu.rs b/src/apu.rs
index 3ad780c..d27fca7 100644
--- a/src/apu.rs
+++ b/src/apu.rs
@@ -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;
diff --git a/src/bin.rs b/src/bin.rs
index b10f21c..c3a922f 100644
--- a/src/bin.rs
+++ b/src/bin.rs
@@ -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) {
diff --git a/src/ppu.rs b/src/ppu.rs
index 945100a..87ac6de 100644
--- a/src/ppu.rs
+++ b/src/ppu.rs
@@ -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);