This repository has been archived on 2026-08-27. You can view files and clone it, but cannot push or open issues or pull requests.
gopher64-netplay/src/device/si.rs
Logan McNaughton 2e4a23c315 Initial Commit
2023-10-04 14:33:17 -06:00

120 lines
3.7 KiB
Rust

use crate::device;
pub const SI_DRAM_ADDR_REG: u32 = 0;
pub const SI_PIF_ADDR_RD64B_REG: u32 = 1;
//pub const SI_R2_REG: u32 = 2;
//pub const SI_R3_REG: u32 = 3;
pub const SI_PIF_ADDR_WR64B_REG: u32 = 4;
//pub const SI_R5_REG: u32 = 5;
pub const SI_STATUS_REG: u32 = 6;
pub const SI_REGS_COUNT: u32 = 7;
pub const SI_STATUS_DMA_BUSY: u32 = 1 << 0;
pub const SI_STATUS_IO_BUSY: u32 = 1 << 1;
//pub const SI_STATUS_DMA_ERROR: u32 = 1 << 3;
pub const SI_STATUS_INTERRUPT: u32 = 1 << 12;
#[derive(PartialEq)]
pub enum DmaDir {
None,
Write,
Read,
}
pub struct Si {
pub regs: [u32; SI_REGS_COUNT as usize],
pub dma_dir: DmaDir,
}
pub fn read_regs(
device: &mut device::Device,
address: u64,
_access_size: device::memory::AccessSize,
) -> u32 {
return device.si.regs[((address & 0xFFFF) >> 2) as usize];
}
pub fn dma_read(device: &mut device::Device) {
device.si.dma_dir = DmaDir::Read;
let duration = device::pif::update_pif_ram(device);
device.si.regs[SI_STATUS_REG as usize] |= SI_STATUS_DMA_BUSY;
device::events::create_event(
device,
device::events::EventType::SI,
device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize] + duration,
dma_event,
)
}
pub fn dma_write(device: &mut device::Device) {
device.si.dma_dir = DmaDir::Write;
copy_pif_rdram(device);
device.si.regs[SI_STATUS_REG as usize] |= SI_STATUS_DMA_BUSY;
device::events::create_event(
device,
device::events::EventType::SI,
device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize] + 5200, //based on https://github.com/rasky/n64-systembench
dma_event,
)
}
pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u32) {
let reg = (address & 0xFFFF) >> 2;
match reg as u32 {
SI_STATUS_REG => {
device.si.regs[reg as usize] &= !SI_STATUS_INTERRUPT;
device::mi::clear_rcp_interrupt(device, device::mi::MI_INTR_SI)
}
SI_PIF_ADDR_RD64B_REG => dma_read(device),
SI_PIF_ADDR_WR64B_REG => dma_write(device),
_ => device::memory::masked_write_32(&mut device.si.regs[reg as usize], value, mask),
}
}
//rdram is in native endian format, and pif memory is in big endian format
pub fn copy_pif_rdram(device: &mut device::Device) {
let dram_addr = device.si.regs[SI_DRAM_ADDR_REG as usize] as usize & device::rdram::RDRAM_MASK;
if device.si.dma_dir == DmaDir::Write {
let mut i = 0;
while i < device::pif::PIF_RAM_SIZE {
let data = u32::from_ne_bytes(
device.rdram.mem[dram_addr + i..dram_addr + i + 4]
.try_into()
.unwrap(),
);
device.pif.ram[i..i + 4].copy_from_slice(&data.to_be_bytes());
i += 4;
}
} else if device.si.dma_dir == DmaDir::Read {
let mut i = 0;
while i < device::pif::PIF_RAM_SIZE {
let data = u32::from_be_bytes(device.pif.ram[i..i + 4].try_into().unwrap());
device.rdram.mem[dram_addr + i..dram_addr + i + 4].copy_from_slice(&data.to_ne_bytes());
i += 4;
}
} else {
panic!("si dma unknown")
}
}
pub fn dma_event(device: &mut device::Device) {
if device.si.dma_dir == DmaDir::Write {
device::pif::process_ram(device);
} else if device.si.dma_dir == DmaDir::Read {
device::si::copy_pif_rdram(device);
} else {
panic!("si dma unknown")
}
device.si.dma_dir = DmaDir::None;
device.si.regs[SI_STATUS_REG as usize] &= !(SI_STATUS_DMA_BUSY | SI_STATUS_IO_BUSY);
device.si.regs[SI_STATUS_REG as usize] |= SI_STATUS_INTERRUPT;
device::mi::set_rcp_interrupt(device, device::mi::MI_INTR_SI)
}