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 7fc2412c9c
move state to new thread (#990)
* move state to new thread

* more

* more
2026-05-30 12:26:38 +00:00

133 lines
4.2 KiB
Rust

use crate::device;
use crate::ui;
use rand::Rng;
const SI_DRAM_ADDR_REG: usize = 0;
const SI_PIF_ADDR_RD64B_REG: usize = 1;
//const SI_R2_REG: usize = 2;
//const SI_R3_REG: usize = 3;
const SI_PIF_ADDR_WR64B_REG: usize = 4;
//const SI_R5_REG: usize = 5;
pub const SI_STATUS_REG: usize = 6;
pub const SI_REGS_COUNT: usize = 7;
pub const SI_STATUS_DMA_BUSY: u32 = 1 << 0;
pub const SI_STATUS_IO_BUSY: u32 = 1 << 1;
//const SI_STATUS_DMA_ERROR: u32 = 1 << 3;
const SI_STATUS_INTERRUPT: u32 = 1 << 12;
#[derive(Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum DmaDir {
None,
Write,
Read,
}
#[derive(Clone, serde::Serialize, serde::Deserialize)]
pub struct Si {
pub regs: [u32; SI_REGS_COUNT],
pub dma_dir: DmaDir,
}
pub fn read_regs(
device: &mut device::Device,
address: u64,
_access_size: device::memory::AccessSize,
) -> u32 {
device::cop0::add_cycles(device, 20);
device.si.regs[((address & 0xFFFF) >> 2) as usize]
}
fn randomize_interrupt_time(rng: &mut rand::rngs::Xoshiro256PlusPlus) -> u64 {
rng.next_u64() % 0x100
}
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] |= SI_STATUS_DMA_BUSY;
let length = duration + randomize_interrupt_time(&mut device.rng);
device::events::create_event(device, device::events::EVENT_TYPE_SI, length)
}
fn dma_write(device: &mut device::Device) {
device.si.dma_dir = DmaDir::Write;
copy_pif_rdram(device);
device.si.regs[SI_STATUS_REG] |= SI_STATUS_DMA_BUSY;
let length = 6000 + randomize_interrupt_time(&mut device.rng); //based on https://github.com/rasky/n64-systembench
device::events::create_event(device, device::events::EVENT_TYPE_SI, length)
}
pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u32) {
let reg = (address & 0xFFFF) >> 2;
match reg as usize {
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
fn copy_pif_rdram(device: &mut device::Device) {
let dram_addr = device.si.regs[SI_DRAM_ADDR_REG] 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
.get(dram_addr + i..dram_addr + i + 4)
.unwrap_or(&[0; 4])
.try_into()
.unwrap_or_default(),
);
device.pif.ram[i..i + 4].copy_from_slice(&data.to_be_bytes());
i += 4;
}
} else if device.si.dma_dir == DmaDir::Read {
// check RDP before SI writes to RDRAM
ui::video::check_framebuffers(dram_addr as u32, device::pif::PIF_RAM_SIZE as u32);
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
.get_mut(dram_addr + i..dram_addr + i + 4)
.unwrap_or(&mut [0; 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] &= !(SI_STATUS_DMA_BUSY | SI_STATUS_IO_BUSY);
device.si.regs[SI_STATUS_REG] |= SI_STATUS_INTERRUPT;
device::mi::set_rcp_interrupt(device, device::mi::MI_INTR_SI)
}