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/pi.rs
2025-01-27 22:50:49 +01:00

197 lines
7.3 KiB
Rust

use crate::device;
const PI_DRAM_ADDR_REG: u32 = 0;
const PI_CART_ADDR_REG: u32 = 1;
const PI_RD_LEN_REG: u32 = 2;
const PI_WR_LEN_REG: u32 = 3;
pub const PI_STATUS_REG: u32 = 4;
const PI_BSD_DOM1_LAT_REG: u32 = 5;
const PI_BSD_DOM1_PWD_REG: u32 = 6;
const PI_BSD_DOM1_PGS_REG: u32 = 7;
const PI_BSD_DOM1_RLS_REG: u32 = 8;
const PI_BSD_DOM2_LAT_REG: u32 = 9;
const PI_BSD_DOM2_PWD_REG: u32 = 10;
const PI_BSD_DOM2_PGS_REG: u32 = 11;
const PI_BSD_DOM2_RLS_REG: u32 = 12;
//const UNKNOWN_REG: u32 = 13; //LibDragon
pub const PI_REGS_COUNT: u32 = 14;
/* PI_STATUS - read */
const PI_STATUS_DMA_BUSY: u32 = 1 << 0;
pub const PI_STATUS_IO_BUSY: u32 = 1 << 1;
//const PI_STATUS_ERROR: u32 = 1 << 2;
const PI_STATUS_INTERRUPT: u32 = 1 << 3;
/* PI_STATUS - write */
const PI_STATUS_RESET: u32 = 1 << 0;
const PI_STATUS_CLR_INTR: u32 = 1 << 1;
pub struct Pi {
pub regs: [u32; PI_REGS_COUNT as usize],
}
struct PiHandler {
read: fn(&mut device::Device, u32, u32, u32) -> u64,
write: fn(&mut device::Device, u32, u32, u32) -> u64,
}
pub fn read_regs(
device: &mut device::Device,
address: u64,
_access_size: device::memory::AccessSize,
) -> u32 {
let reg = (address & 0xFFFF) >> 2;
match reg as u32 {
PI_WR_LEN_REG | PI_RD_LEN_REG => 0x7F,
PI_CART_ADDR_REG => device.pi.regs[reg as usize] & 0xFFFFFFFE,
PI_DRAM_ADDR_REG => device.pi.regs[reg as usize] & 0xFFFFFE,
_ => device.pi.regs[reg as usize],
}
}
fn dma_read(device: &mut device::Device) {
let handler = get_handler(device.pi.regs[PI_CART_ADDR_REG as usize]);
let cart_addr = device.pi.regs[PI_CART_ADDR_REG as usize] & !1;
let dram_addr = device.pi.regs[PI_DRAM_ADDR_REG as usize] & 0xFFFFFE;
let mut length = (device.pi.regs[PI_RD_LEN_REG as usize] & 0xFFFFFF) + 1;
/* PI seems to treat the first 128 bytes differently, see https://n64brew.dev/wiki/Peripheral_Interface#Unaligned_DMA_transfer */
if length >= 0x7f && (length & 1) != 0 {
length += 1;
}
let cycles = (handler.read)(device, cart_addr, dram_addr, length);
device::events::create_event(
device,
device::events::EventType::PI,
device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize] + cycles,
dma_event,
);
/* Update PI_DRAM_ADDR_REG and PI_CART_ADDR_REG */
device.pi.regs[PI_DRAM_ADDR_REG as usize] =
(device.pi.regs[PI_DRAM_ADDR_REG as usize] + length + 7) & !7;
device.pi.regs[PI_CART_ADDR_REG as usize] =
(device.pi.regs[PI_CART_ADDR_REG as usize] + length + 1) & !1;
device.pi.regs[PI_STATUS_REG as usize] |= PI_STATUS_DMA_BUSY
}
fn dma_write(device: &mut device::Device) {
let handler = get_handler(device.pi.regs[PI_CART_ADDR_REG as usize]);
let cart_addr = device.pi.regs[PI_CART_ADDR_REG as usize] & !1;
let dram_addr = device.pi.regs[PI_DRAM_ADDR_REG as usize] & 0xFFFFFE;
let mut length = (device.pi.regs[PI_WR_LEN_REG as usize] & 0xFFFFFF) + 1;
/* PI seems to treat the first 128 bytes differently, see https://n64brew.dev/wiki/Peripheral_Interface#Unaligned_DMA_transfer */
if length >= 0x7f && (length & 1) != 0 {
length += 1;
}
if length <= 0x80 {
length -= dram_addr & 0x7;
}
let cycles = (handler.write)(device, cart_addr, dram_addr, length);
device::events::create_event(
device,
device::events::EventType::PI,
device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize] + cycles,
dma_event,
);
/* Update PI_DRAM_ADDR_REG and PI_CART_ADDR_REG */
device.pi.regs[PI_DRAM_ADDR_REG as usize] =
(device.pi.regs[PI_DRAM_ADDR_REG as usize] + length + 7) & !7;
device.pi.regs[PI_CART_ADDR_REG as usize] =
(device.pi.regs[PI_CART_ADDR_REG as usize] + length + 1) & !1;
device.pi.regs[PI_STATUS_REG as usize] |= PI_STATUS_DMA_BUSY
}
fn get_handler(address: u32) -> PiHandler {
let mut handler = PiHandler {
read: device::cart_rom::dma_read,
write: device::cart_rom::dma_write,
};
if address >= device::memory::MM_CART_ROM as u32 {
handler.read = device::cart_rom::dma_read;
handler.write = device::cart_rom::dma_write;
} else if address >= device::memory::MM_DOM2_ADDR2 as u32 {
handler.read = device::sram::dma_read;
handler.write = device::sram::dma_write;
} else {
panic!("unknown pi handler")
}
handler
}
pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u32) {
let reg = (address & 0xFFFF) >> 2;
match reg as u32 {
PI_RD_LEN_REG => {
device::memory::masked_write_32(&mut device.pi.regs[reg as usize], value, mask);
device::pi::dma_read(device)
}
PI_WR_LEN_REG => {
device::memory::masked_write_32(&mut device.pi.regs[reg as usize], value, mask);
device::pi::dma_write(device)
}
PI_STATUS_REG => {
if value & mask & PI_STATUS_CLR_INTR != 0 {
device.pi.regs[reg as usize] &= !PI_STATUS_INTERRUPT;
device::mi::clear_rcp_interrupt(device, device::mi::MI_INTR_PI);
}
if value & mask & PI_STATUS_RESET != 0 {
device.pi.regs[PI_STATUS_REG as usize] = 0
}
}
PI_BSD_DOM1_LAT_REG | PI_BSD_DOM1_PWD_REG | PI_BSD_DOM2_LAT_REG | PI_BSD_DOM2_PWD_REG => {
device::memory::masked_write_32(&mut device.pi.regs[reg as usize], value & 0xFF, mask)
}
PI_BSD_DOM1_PGS_REG | PI_BSD_DOM2_PGS_REG => {
device::memory::masked_write_32(&mut device.pi.regs[reg as usize], value & 0xF, mask)
}
PI_BSD_DOM1_RLS_REG | PI_BSD_DOM2_RLS_REG => {
device::memory::masked_write_32(&mut device.pi.regs[reg as usize], value & 0x3, mask)
}
_ => device::memory::masked_write_32(&mut device.pi.regs[reg as usize], value, mask),
}
}
pub fn calculate_cycles(device: &device::Device, domain: i32, length: u32) -> u64 {
let mut cycles: f64 = 0.0;
let (page_size, latency, pulse_width, release, pages);
let page_size_base: f64 = 2.0;
if domain == 1 {
latency = (device.pi.regs[PI_BSD_DOM1_LAT_REG as usize] + 1) as f64;
pulse_width = (device.pi.regs[PI_BSD_DOM1_PWD_REG as usize] + 1) as f64;
release = (device.pi.regs[PI_BSD_DOM1_RLS_REG as usize] + 1) as f64;
page_size = page_size_base.powf((device.pi.regs[PI_BSD_DOM1_PGS_REG as usize] + 2) as f64);
} else if domain == 2 {
latency = (device.pi.regs[PI_BSD_DOM2_LAT_REG as usize] + 1) as f64;
pulse_width = (device.pi.regs[PI_BSD_DOM2_PWD_REG as usize] + 1) as f64;
release = (device.pi.regs[PI_BSD_DOM2_RLS_REG as usize] + 1) as f64;
page_size = page_size_base.powf((device.pi.regs[PI_BSD_DOM2_PGS_REG as usize] + 2) as f64);
} else {
panic!("unknown pi dma")
}
pages = (length as f64 / page_size).ceil();
cycles += (14.0 + latency) * pages;
cycles += (pulse_width + release) * (length as f64 / 2.0);
cycles += 5.0 * pages;
(cycles * 1.5) as u64 // Converting RCP clock speed to CPU clock speed
}
pub fn dma_event(device: &mut device::Device) {
device.pi.regs[PI_STATUS_REG as usize] &= !(PI_STATUS_DMA_BUSY | PI_STATUS_IO_BUSY);
device.pi.regs[PI_STATUS_REG as usize] |= PI_STATUS_INTERRUPT;
device::mi::set_rcp_interrupt(device, device::mi::MI_INTR_PI)
}