feat(audio): implement nominal Direct Sound playback and FIFO DMA
This commit is contained in:
parent
894f524cda
commit
5888c4d292
21 changed files with 933 additions and 129 deletions
|
|
@ -1,5 +1,7 @@
|
|||
use std::{cell::Cell, error::Error, fmt};
|
||||
|
||||
#[cfg(test)]
|
||||
mod audio_step_tests;
|
||||
mod fetch;
|
||||
#[cfg(test)]
|
||||
mod fetch_tests;
|
||||
|
|
@ -18,6 +20,7 @@ pub(crate) use fetch::InstructionFetch;
|
|||
use iwram_bus::IwramBus;
|
||||
|
||||
use crate::{
|
||||
audio::{Audio, StereoLevel},
|
||||
cpu::InstructionSet,
|
||||
display::{DisplayPosition, CYCLES_PER_LINE, HBLANK_START, VISIBLE_LINES},
|
||||
dma::{DmaError, DMA_BASE, DMA_END},
|
||||
|
|
@ -97,7 +100,7 @@ struct CpuAccess {
|
|||
/// Successful DMA accesses drive local lanes even before the first CPU instruction.
|
||||
/// Channel data remains separate; general and sub-instruction CPU bus handoff is unmodeled.
|
||||
/// Instruction buffering and supported bus history consume shared fetch samples.
|
||||
/// Timer data accesses use staged machine-step time; other per-access device timing remains unmodeled.
|
||||
/// Timer and sound accesses use staged machine-step time; other per-access device timing remains unmodeled.
|
||||
pub struct Memory {
|
||||
external_ram: Vec<u8>,
|
||||
internal_ram: Vec<u8>,
|
||||
|
|
@ -162,6 +165,17 @@ impl Memory {
|
|||
Ok(memory)
|
||||
}
|
||||
|
||||
/// Current committed Direct Sound mixer level. This is not a host sample stream.
|
||||
pub fn audio_level(&self) -> StereoLevel {
|
||||
self.io.audio.level()
|
||||
}
|
||||
|
||||
fn audio_state(&self) -> Audio {
|
||||
self.timer_step
|
||||
.get()
|
||||
.map_or(self.io.audio, |step| step.audio)
|
||||
}
|
||||
|
||||
/// Total elapsed emulated clock cycles, wrapping at u64::MAX. Reads/writes
|
||||
/// alone consume no cycles. STOP discards externally supplied clock advances.
|
||||
pub fn cycles(&self) -> u64 {
|
||||
|
|
@ -727,7 +741,7 @@ impl Memory {
|
|||
bytes: [u8; N],
|
||||
) -> Result<(), MemoryError> {
|
||||
let index = self.write_index::<N>(address)?;
|
||||
Self::validate_io_values(address, &bytes)?;
|
||||
Self::validate_io_values(&mut self.audio_state(), address, &bytes)?;
|
||||
self.record_access(
|
||||
address,
|
||||
match N {
|
||||
|
|
@ -753,6 +767,15 @@ impl Memory {
|
|||
self.iwram_bus.access(address, width, value);
|
||||
}
|
||||
if address >> 24 == 0x04 {
|
||||
if Audio::mapped(address) {
|
||||
if let Some(mut step) = self.timer_step.get() {
|
||||
step.audio.write(address, &bytes);
|
||||
self.timer_step.set(Some(step));
|
||||
} else {
|
||||
self.io.audio.write(address, &bytes);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
if address & !3 == KEYINPUT {
|
||||
self.io.write_keypad(address, &bytes);
|
||||
return Ok(());
|
||||
|
|
@ -843,11 +866,17 @@ impl Memory {
|
|||
}
|
||||
}
|
||||
|
||||
fn validate_io_values(address: u32, bytes: &[u8]) -> Result<(), MemoryError> {
|
||||
fn validate_io_values(
|
||||
audio: &mut Audio,
|
||||
address: u32,
|
||||
bytes: &[u8],
|
||||
) -> Result<(), MemoryError> {
|
||||
if address >> 24 == 0x04 {
|
||||
for (offset, &value) in bytes.iter().enumerate() {
|
||||
let address = address + offset as u32;
|
||||
if let Some(operation) = Io::unsupported_write(address, value) {
|
||||
if let Some(operation) = Io::unsupported_write(address, value)
|
||||
.or_else(|| audio.unsupported(address, value))
|
||||
{
|
||||
return Err(MemoryError::UnsupportedIo {
|
||||
address,
|
||||
value,
|
||||
|
|
@ -855,6 +884,9 @@ impl Memory {
|
|||
});
|
||||
}
|
||||
}
|
||||
if Audio::mapped(address) {
|
||||
audio.write(address, bytes);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -863,9 +895,10 @@ impl Memory {
|
|||
/// leave a partial block store. This is not a model of hardware data aborts.
|
||||
/// Mapped I/O reads currently have no side effects; writes cannot fail after validation.
|
||||
pub(crate) fn write_words(&mut self, writes: &[(u32, u32)]) -> Result<(), MemoryError> {
|
||||
let mut audio = self.audio_state();
|
||||
for &(address, value) in writes {
|
||||
self.write_index::<4>(address)?;
|
||||
Self::validate_io_values(address, &value.to_le_bytes())?;
|
||||
Self::validate_io_values(&mut audio, address, &value.to_le_bytes())?;
|
||||
}
|
||||
for &(address, value) in writes {
|
||||
// The map cannot change between validation and these writes.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue