171 lines
5.4 KiB
Rust
171 lines
5.4 KiB
Rust
use crate::{device, ui};
|
|
|
|
const AI_DRAM_ADDR_REG: usize = 0;
|
|
const AI_LEN_REG: usize = 1;
|
|
// const AI_CONTROL_REG: usize = 2;
|
|
pub const AI_STATUS_REG: usize = 3;
|
|
const AI_DACRATE_REG: usize = 4;
|
|
// const AI_BITRATE_REG: usize = 5;
|
|
pub const AI_REGS_COUNT: usize = 6;
|
|
|
|
pub const AI_STATUS_BUSY: u32 = 0x40000000;
|
|
const AI_STATUS_FULL: u32 = 0x80000000;
|
|
|
|
#[derive(Clone, serde::Serialize, serde::Deserialize)]
|
|
pub struct Ai {
|
|
pub regs: [u32; AI_REGS_COUNT],
|
|
pub fifo: [AiDma; 2],
|
|
pub last_read: u64,
|
|
pub delayed_carry: bool,
|
|
pub freq: u64,
|
|
}
|
|
|
|
#[derive(Copy, Clone, serde::Serialize, serde::Deserialize)]
|
|
pub struct AiDma {
|
|
pub address: u64,
|
|
pub length: u64,
|
|
pub duration: u64,
|
|
}
|
|
|
|
fn get_remaining_dma_length(device: &device::Device) -> u64 {
|
|
if device.ai.fifo[0].duration == 0 {
|
|
return 0;
|
|
}
|
|
|
|
let next_ai_event = device::events::get_event(device, device::events::EVENT_TYPE_AI);
|
|
if next_ai_event.is_none() {
|
|
return 0;
|
|
}
|
|
|
|
let remaining_dma_duration =
|
|
next_ai_event.unwrap().count - device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG];
|
|
|
|
let dma_length = remaining_dma_duration * device.ai.fifo[0].length / device.ai.fifo[0].duration;
|
|
dma_length & !7
|
|
}
|
|
|
|
fn get_dma_duration(device: &device::Device) -> u64 {
|
|
let bytes_per_sample = 4; /* XXX: assume 16bit stereo - should depends on bitrate instead */
|
|
let length = (device.ai.regs[AI_LEN_REG] & !7) as u64;
|
|
|
|
(length * device.cpu.clock_rate) / (bytes_per_sample * device.ai.freq)
|
|
}
|
|
|
|
fn do_dma(device: &mut device::Device) {
|
|
device.ai.last_read = device.ai.fifo[0].length;
|
|
|
|
if device.ai.delayed_carry {
|
|
device.ai.fifo[0].address += 0x2000;
|
|
}
|
|
|
|
device.ai.delayed_carry =
|
|
((device.ai.fifo[0].address + device.ai.fifo[0].length) & 0x1FFF) == 0;
|
|
|
|
/* schedule end of dma event */
|
|
device::events::create_event(
|
|
device,
|
|
device::events::EVENT_TYPE_AI,
|
|
device.ai.fifo[0].duration,
|
|
);
|
|
device::mi::set_rcp_interrupt(device, device::mi::MI_INTR_AI);
|
|
}
|
|
|
|
fn fifo_push(device: &mut device::Device) {
|
|
let duration = get_dma_duration(device);
|
|
|
|
if (device.ai.regs[AI_STATUS_REG] & AI_STATUS_BUSY) != 0 {
|
|
device.ai.fifo[1].address =
|
|
device.ai.regs[AI_DRAM_ADDR_REG] as u64 & device::rdram::RDRAM_MASK as u64;
|
|
device.ai.fifo[1].length = (device.ai.regs[AI_LEN_REG] & !7) as u64;
|
|
device.ai.fifo[1].duration = duration;
|
|
device.ai.regs[AI_STATUS_REG] |= AI_STATUS_FULL;
|
|
} else {
|
|
device.ai.fifo[0].address =
|
|
device.ai.regs[AI_DRAM_ADDR_REG] as u64 & device::rdram::RDRAM_MASK as u64;
|
|
device.ai.fifo[0].length = (device.ai.regs[AI_LEN_REG] & !7) as u64;
|
|
device.ai.fifo[0].duration = duration;
|
|
device.ai.regs[AI_STATUS_REG] |= AI_STATUS_BUSY;
|
|
|
|
do_dma(device);
|
|
ui::audio::resume_game_audio(&mut device.ui);
|
|
}
|
|
}
|
|
|
|
fn fifo_pop(device: &mut device::Device) {
|
|
if device.ai.regs[AI_STATUS_REG] & AI_STATUS_FULL != 0 {
|
|
device.ai.fifo[0].address = device.ai.fifo[1].address;
|
|
device.ai.fifo[0].length = device.ai.fifo[1].length;
|
|
device.ai.fifo[0].duration = device.ai.fifo[1].duration;
|
|
device.ai.regs[AI_STATUS_REG] &= !AI_STATUS_FULL;
|
|
|
|
do_dma(device);
|
|
} else {
|
|
device.ai.regs[AI_STATUS_REG] &= !AI_STATUS_BUSY;
|
|
device.ai.delayed_carry = false;
|
|
ui::audio::pause_game_audio(&mut device.ui);
|
|
}
|
|
}
|
|
|
|
pub fn read_regs(
|
|
device: &mut device::Device,
|
|
address: u64,
|
|
_access_size: device::memory::AccessSize,
|
|
) -> u32 {
|
|
let reg = (address & 0xFFFF) >> 2;
|
|
let return_value = match reg as usize {
|
|
AI_LEN_REG => {
|
|
let value = get_remaining_dma_length(device);
|
|
if value < device.ai.last_read {
|
|
let diff = device.ai.fifo[0].length - device.ai.last_read;
|
|
|
|
ui::audio::play_audio(
|
|
device,
|
|
(device.ai.fifo[0].address + diff) as usize,
|
|
device.ai.last_read - value,
|
|
);
|
|
|
|
device.ai.last_read = value;
|
|
}
|
|
value as u32
|
|
}
|
|
_ => device.ai.regs[reg as usize],
|
|
};
|
|
device::cop0::add_cycles(device, 20);
|
|
return_value
|
|
}
|
|
|
|
pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u32) {
|
|
let reg = (address & 0xFFFF) >> 2;
|
|
match reg as usize {
|
|
AI_LEN_REG => {
|
|
device::memory::masked_write_32(&mut device.ai.regs[reg as usize], value, mask);
|
|
if device.ai.regs[reg as usize] != 0 {
|
|
fifo_push(device)
|
|
}
|
|
}
|
|
AI_STATUS_REG => device::mi::clear_rcp_interrupt(device, device::mi::MI_INTR_AI),
|
|
AI_DACRATE_REG => {
|
|
if device.ai.regs[reg as usize] != value & mask {
|
|
device.ai.freq = device.vi.clock / (1 + (value & mask)) as u64;
|
|
ui::audio::update_freq(device);
|
|
}
|
|
device::memory::masked_write_32(&mut device.ai.regs[reg as usize], value, mask)
|
|
}
|
|
_ => device::memory::masked_write_32(&mut device.ai.regs[reg as usize], value, mask),
|
|
}
|
|
}
|
|
|
|
pub fn dma_event(device: &mut device::Device) {
|
|
if device.ai.last_read != 0 {
|
|
let diff = device.ai.fifo[0].length - device.ai.last_read;
|
|
ui::audio::play_audio(
|
|
device,
|
|
(device.ai.fifo[0].address + diff) as usize,
|
|
device.ai.last_read,
|
|
);
|
|
device.ai.last_read = 0;
|
|
}
|
|
|
|
fifo_pop(device);
|
|
}
|