Compare commits
5 Commits
192bdffd64
...
08bd33b4d4
Author | SHA1 | Date |
---|---|---|
Rekai Nyangadzayi Musuka | 08bd33b4d4 | |
Rekai Nyangadzayi Musuka | 2b5926e533 | |
Rekai Nyangadzayi Musuka | 1e27875dc3 | |
Rekai Nyangadzayi Musuka | 1566d60fd4 | |
Rekai Nyangadzayi Musuka | ab99e24f64 |
10
src/bus.rs
10
src/bus.rs
|
@ -66,12 +66,16 @@ impl Bus {
|
||||||
self.cartridge.as_ref()?.title()
|
self.cartridge.as_ref()?.title()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn pass_audio_src(&mut self, sender: AudioSender) {
|
pub(crate) fn pass_audio_src(&mut self, sender: AudioSender<f32>) {
|
||||||
self.snd.set_audio_src(sender)
|
self.snd.set_audio_src(sender)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn is_mpsc_still_full(&mut self) -> bool {
|
pub(crate) fn is_full(&self) -> bool {
|
||||||
self.snd.is_mpsc_still_full()
|
self.snd.is_full()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn flush_samples(&mut self) {
|
||||||
|
self.snd.flush_samples()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn clock(&mut self) {
|
pub(crate) fn clock(&mut self) {
|
||||||
|
|
27
src/cpu.rs
27
src/cpu.rs
|
@ -45,7 +45,7 @@ impl Cpu {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_audio_src(&mut self, sender: AudioSender) {
|
pub fn set_audio_src(&mut self, sender: AudioSender<f32>) {
|
||||||
self.bus.pass_audio_src(sender)
|
self.bus.pass_audio_src(sender)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,13 +96,6 @@ impl Cpu {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn step(&mut self) -> Cycle {
|
pub fn step(&mut self) -> Cycle {
|
||||||
// if !self.bus.boot_enabled() {
|
|
||||||
// let out = std::io::stdout();
|
|
||||||
// let handle = out.lock();
|
|
||||||
|
|
||||||
// self.log_state(handle).unwrap();
|
|
||||||
// }
|
|
||||||
|
|
||||||
let cycles = match self.halted() {
|
let cycles = match self.halted() {
|
||||||
Some(state) => {
|
Some(state) => {
|
||||||
use HaltState::*;
|
use HaltState::*;
|
||||||
|
@ -117,6 +110,10 @@ impl Cpu {
|
||||||
self.inc_pc();
|
self.inc_pc();
|
||||||
|
|
||||||
let instr = self.decode(opcode);
|
let instr = self.decode(opcode);
|
||||||
|
|
||||||
|
// let out = std::io::stdout();
|
||||||
|
// let _ = self._debug_log(out.lock(), &instr);
|
||||||
|
|
||||||
let cycles = self.execute(instr);
|
let cycles = self.execute(instr);
|
||||||
|
|
||||||
self.check_ime();
|
self.check_ime();
|
||||||
|
@ -125,15 +122,17 @@ impl Cpu {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// FIXME: Regression, this is not a proper fix,
|
// TODO: With how we currently handle audio
|
||||||
// the FIFO behaves weirdly using this code
|
// this --while being correct-- incurs a performance penalty
|
||||||
|
// as our emu is audio-bound.
|
||||||
|
let mut elapsed = 0x00;
|
||||||
let pending: u32 = cycles.into();
|
let pending: u32 = cycles.into();
|
||||||
let mut offset = 0;
|
while elapsed < pending {
|
||||||
for _ in 0..(pending + offset) {
|
if !self.bus.is_full() {
|
||||||
if !self.bus.is_mpsc_still_full() {
|
|
||||||
self.bus.clock();
|
self.bus.clock();
|
||||||
|
elapsed += 1;
|
||||||
} else {
|
} else {
|
||||||
offset += 1;
|
self.bus.flush_samples();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
15
src/ppu.rs
15
src/ppu.rs
|
@ -369,14 +369,15 @@ impl Ppu {
|
||||||
}
|
}
|
||||||
SendToFifoTwo => {
|
SendToFifoTwo => {
|
||||||
let palette = &self.monochrome.bg_palette;
|
let palette = &self.monochrome.bg_palette;
|
||||||
self.fetch.send_to_fifo(&mut self.fifo, palette);
|
|
||||||
self.fetch.x_pos += 1;
|
|
||||||
|
|
||||||
|
if let Ok(()) = self.fetch.send_to_fifo(&mut self.fifo, palette) {
|
||||||
|
self.fetch.x_pos += 1;
|
||||||
self.fetch.back.next(TileNumber);
|
self.fetch.back.next(TileNumber);
|
||||||
self.fetch.back.tile = Default::default();
|
self.fetch.back.tile = Default::default();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if self.fifo.is_enabled() {
|
if self.fifo.is_enabled() {
|
||||||
use RenderPriority::*;
|
use RenderPriority::*;
|
||||||
|
@ -720,7 +721,11 @@ impl PixelFetcher {
|
||||||
tile_data_addr + (offset * 2)
|
tile_data_addr + (offset * 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_to_fifo(&self, fifo: &mut FifoRenderer, palette: &BackgroundPalette) {
|
fn send_to_fifo(&self, fifo: &mut FifoRenderer, palette: &BackgroundPalette) -> Result<(), ()> {
|
||||||
|
if !fifo.back.is_empty() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
let (high, low) = self
|
let (high, low) = self
|
||||||
.back
|
.back
|
||||||
.tile
|
.tile
|
||||||
|
@ -729,14 +734,14 @@ impl PixelFetcher {
|
||||||
|
|
||||||
let tbpp = Pixels::from_bytes(high, low);
|
let tbpp = Pixels::from_bytes(high, low);
|
||||||
|
|
||||||
if fifo.back.is_empty() {
|
|
||||||
for x in 0..Pixels::PIXEL_COUNT {
|
for x in 0..Pixels::PIXEL_COUNT {
|
||||||
let shade = palette.shade(tbpp.shade_id(x));
|
let shade = palette.shade(tbpp.shade_id(x));
|
||||||
|
|
||||||
let fifo_info = BackgroundFifoInfo { shade };
|
let fifo_info = BackgroundFifoInfo { shade };
|
||||||
fifo.back.push_back(fifo_info);
|
fifo.back.push_back(fifo_info);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_obj_addr(attr: &ObjectAttribute, pos: &ScreenPosition, size: ObjectSize) -> u16 {
|
fn get_obj_addr(attr: &ObjectAttribute, pos: &ScreenPosition, size: ObjectSize) -> u16 {
|
||||||
|
|
102
src/sound.rs
102
src/sound.rs
|
@ -1,3 +1,5 @@
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
use bitfield::bitfield;
|
use bitfield::bitfield;
|
||||||
use crossbeam_channel::{Receiver, Sender, TrySendError};
|
use crossbeam_channel::{Receiver, Sender, TrySendError};
|
||||||
use rodio::Source;
|
use rodio::Source;
|
||||||
|
@ -7,8 +9,8 @@ use crate::emu::SM83_CLOCK_SPEED;
|
||||||
const WAVE_PATTERN_RAM_LEN: usize = 0x10;
|
const WAVE_PATTERN_RAM_LEN: usize = 0x10;
|
||||||
|
|
||||||
const SAMPLE_RATE: u32 = 48000; // Hz
|
const SAMPLE_RATE: u32 = 48000; // Hz
|
||||||
const AUDIO_BUFFER_LEN: usize = 1024;
|
const AUDIO_BUFFER_LEN: usize = 512;
|
||||||
const CHANNEL_COUNT: u16 = 2;
|
const CHANNEL_COUNT: usize = 2;
|
||||||
const SAMPLE_INCREMENT: u64 = SAMPLE_RATE as u64;
|
const SAMPLE_INCREMENT: u64 = SAMPLE_RATE as u64;
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone)]
|
#[derive(Default, Debug, Clone)]
|
||||||
|
@ -27,10 +29,10 @@ pub(crate) struct Sound {
|
||||||
frame_seq_state: FrameSequencerState,
|
frame_seq_state: FrameSequencerState,
|
||||||
div_prev: Option<u8>,
|
div_prev: Option<u8>,
|
||||||
|
|
||||||
sender: Option<AudioSender>,
|
sender: Option<AudioSender<f32>>,
|
||||||
sample_counter: u64,
|
sample_counter: u64,
|
||||||
|
|
||||||
is_mpsc_full: bool,
|
buffer: AudioBuffer<(f32, f32)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sound {
|
impl Sound {
|
||||||
|
@ -70,7 +72,7 @@ impl Sound {
|
||||||
|
|
||||||
self.div_prev = Some(bit_5);
|
self.div_prev = Some(bit_5);
|
||||||
|
|
||||||
if self.sample_counter >= SM83_CLOCK_SPEED {
|
if self.sender.is_some() && self.sample_counter >= SM83_CLOCK_SPEED {
|
||||||
self.sample_counter %= SM83_CLOCK_SPEED;
|
self.sample_counter %= SM83_CLOCK_SPEED;
|
||||||
// Sample the APU
|
// Sample the APU
|
||||||
|
|
||||||
|
@ -93,27 +95,33 @@ impl Sound {
|
||||||
let left = (ch1_left + ch2_left + ch3_left + ch4_left) / 4.0;
|
let left = (ch1_left + ch2_left + ch3_left + ch4_left) / 4.0;
|
||||||
let right = (ch1_right + ch2_right + ch3_right + ch4_right) / 4.0;
|
let right = (ch1_right + ch2_right + ch3_right + ch4_right) / 4.0;
|
||||||
|
|
||||||
if let Some(s) = self.sender.as_ref() {
|
self.buffer.push_back((left, right));
|
||||||
let _ = s.send_samples(left, right).map_err(|e| match e {
|
|
||||||
TrySendError::Full(_) => self.is_mpsc_full = true,
|
|
||||||
TrySendError::Disconnected(_) => {
|
|
||||||
panic!("Audio Sample MPSC Channel Disconnected")
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn set_audio_src(&mut self, sender: AudioSender) {
|
pub(crate) fn set_audio_src(&mut self, sender: AudioSender<f32>) {
|
||||||
self.sender = Some(sender);
|
self.sender = Some(sender);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn is_mpsc_still_full(&mut self) -> bool {
|
pub(crate) fn is_full(&self) -> bool {
|
||||||
if let Some(sender) = self.sender.as_ref() {
|
self.buffer.len() >= AUDIO_BUFFER_LEN * CHANNEL_COUNT
|
||||||
self.is_mpsc_full = sender.is_full();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.is_mpsc_full
|
pub(crate) fn flush_samples(&mut self) {
|
||||||
|
if let Some(sender) = self.sender.as_ref() {
|
||||||
|
while self.buffer.len() >= CHANNEL_COUNT {
|
||||||
|
match self.buffer.pop_front() {
|
||||||
|
Some((left, right)) => {
|
||||||
|
sender
|
||||||
|
.send_samples(left, right)
|
||||||
|
.expect("Successfully sent samples across threads");
|
||||||
|
}
|
||||||
|
None => unreachable!(
|
||||||
|
"While loop ensures that there are at least two items in AudioBuffer"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clock_length(freq_hi: &FrequencyHigh, length_timer: &mut u16, enabled: &mut bool) {
|
fn clock_length(freq_hi: &FrequencyHigh, length_timer: &mut u16, enabled: &mut bool) {
|
||||||
|
@ -1175,35 +1183,34 @@ impl From<ChannelControl> for u8 {
|
||||||
pub struct AudioMPSC;
|
pub struct AudioMPSC;
|
||||||
|
|
||||||
impl AudioMPSC {
|
impl AudioMPSC {
|
||||||
pub fn new() -> (AudioSender, AudioReceiver) {
|
pub fn new() -> (AudioSender<f32>, AudioReceiver<f32>) {
|
||||||
let (send, recv) = crossbeam_channel::bounded(AUDIO_BUFFER_LEN * 2);
|
// TODO: Can we provide an upper limit for this?
|
||||||
|
// The larger this channel is, the more lag there is between the Audio and
|
||||||
|
// Emulator
|
||||||
|
let (send, recv) = crossbeam_channel::unbounded();
|
||||||
|
|
||||||
(AudioSender { inner: send }, AudioReceiver { inner: recv })
|
(AudioSender { inner: send }, AudioReceiver { inner: recv })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct AudioSender {
|
pub struct AudioSender<T> {
|
||||||
inner: Sender<f32>,
|
inner: Sender<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AudioSender {
|
impl<T> AudioSender<T> {
|
||||||
fn send_samples(&self, left: f32, right: f32) -> Result<(), TrySendError<f32>> {
|
fn send_samples(&self, left: T, right: T) -> Result<(), TrySendError<T>> {
|
||||||
self.inner.try_send(left).and(self.inner.try_send(right))?;
|
self.inner.try_send(left).and(self.inner.try_send(right))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_full(&self) -> bool {
|
|
||||||
self.inner.is_full()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AudioReceiver {
|
pub struct AudioReceiver<T> {
|
||||||
inner: Receiver<f32>,
|
inner: Receiver<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Iterator for AudioReceiver {
|
impl<T> Iterator for AudioReceiver<T> {
|
||||||
type Item = f32;
|
type Item = T;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
// TODO: Should this never return none?
|
// TODO: Should this never return none?
|
||||||
|
@ -1211,7 +1218,7 @@ impl Iterator for AudioReceiver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Source for AudioReceiver {
|
impl<T: rodio::Sample> Source for AudioReceiver<T> {
|
||||||
fn current_frame_len(&self) -> Option<usize> {
|
fn current_frame_len(&self) -> Option<usize> {
|
||||||
// A frame changes when the samples rate or
|
// A frame changes when the samples rate or
|
||||||
// number of channels change. This will never happen, so
|
// number of channels change. This will never happen, so
|
||||||
|
@ -1221,7 +1228,7 @@ impl Source for AudioReceiver {
|
||||||
|
|
||||||
fn channels(&self) -> u16 {
|
fn channels(&self) -> u16 {
|
||||||
// The Gameboy supports two channels
|
// The Gameboy supports two channels
|
||||||
CHANNEL_COUNT
|
CHANNEL_COUNT as u16
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sample_rate(&self) -> u32 {
|
fn sample_rate(&self) -> u32 {
|
||||||
|
@ -1233,3 +1240,30 @@ impl Source for AudioReceiver {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct AudioBuffer<T> {
|
||||||
|
inner: VecDeque<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Default for AudioBuffer<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
inner: VecDeque::with_capacity(AUDIO_BUFFER_LEN * CHANNEL_COUNT),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> AudioBuffer<T> {
|
||||||
|
pub fn push_back(&mut self, value: T) {
|
||||||
|
self.inner.push_back(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop_front(&mut self) -> Option<T> {
|
||||||
|
self.inner.pop_front()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.inner.len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue