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/rsp_interface.rs
Logan McNaughton 4ead0218d9
remove pub keyword where it isn't needed (#161)
* remove pub keyword where it isn't needed

* more

* Update main.rs

* more
2025-01-18 19:58:23 +01:00

461 lines
15 KiB
Rust

use crate::device;
const SP_MEM_ADDR_REG: u32 = 0;
const SP_DRAM_ADDR_REG: u32 = 1;
const SP_RD_LEN_REG: u32 = 2;
const SP_WR_LEN_REG: u32 = 3;
pub const SP_STATUS_REG: u32 = 4;
const SP_DMA_FULL_REG: u32 = 5;
const SP_DMA_BUSY_REG: u32 = 6;
const SP_SEMAPHORE_REG: u32 = 7;
pub const SP_REGS_COUNT: u32 = 8;
pub const SP_PC_REG: u32 = 0;
//const SP_IBIST_REG: u32 = 1;
pub const SP_REGS2_COUNT: u32 = 2;
/* SP_STATUS - read */
pub const SP_STATUS_HALT: u32 = 1 << 0;
const SP_STATUS_BROKE: u32 = 1 << 1;
const SP_STATUS_DMA_BUSY: u32 = 1 << 2;
const SP_STATUS_DMA_FULL: u32 = 1 << 3;
//const SP_STATUS_IO_FULL: u32 = 1 << 4;
const SP_STATUS_SSTEP: u32 = 1 << 5;
const SP_STATUS_INTR_BREAK: u32 = 1 << 6;
const SP_STATUS_SIG0: u32 = 1 << 7;
const SP_STATUS_SIG1: u32 = 1 << 8;
const SP_STATUS_SIG2: u32 = 1 << 9;
const SP_STATUS_SIG3: u32 = 1 << 10;
const SP_STATUS_SIG4: u32 = 1 << 11;
const SP_STATUS_SIG5: u32 = 1 << 12;
const SP_STATUS_SIG6: u32 = 1 << 13;
const SP_STATUS_SIG7: u32 = 1 << 14;
/* SP_STATUS - write */
const SP_CLR_HALT: u32 = 1 << 0;
pub const SP_SET_HALT: u32 = 1 << 1;
const SP_CLR_BROKE: u32 = 1 << 2;
const SP_CLR_INTR: u32 = 1 << 3;
const SP_SET_INTR: u32 = 1 << 4;
const SP_CLR_SSTEP: u32 = 1 << 5;
const SP_SET_SSTEP: u32 = 1 << 6;
const SP_CLR_INTR_BREAK: u32 = 1 << 7;
const SP_SET_INTR_BREAK: u32 = 1 << 8;
const SP_CLR_SIG0: u32 = 1 << 9;
const SP_SET_SIG0: u32 = 1 << 10;
const SP_CLR_SIG1: u32 = 1 << 11;
const SP_SET_SIG1: u32 = 1 << 12;
const SP_CLR_SIG2: u32 = 1 << 13;
const SP_SET_SIG2: u32 = 1 << 14;
const SP_CLR_SIG3: u32 = 1 << 15;
const SP_SET_SIG3: u32 = 1 << 16;
const SP_CLR_SIG4: u32 = 1 << 17;
const SP_SET_SIG4: u32 = 1 << 18;
const SP_CLR_SIG5: u32 = 1 << 19;
const SP_SET_SIG5: u32 = 1 << 20;
const SP_CLR_SIG6: u32 = 1 << 21;
const SP_SET_SIG6: u32 = 1 << 22;
const SP_CLR_SIG7: u32 = 1 << 23;
const SP_SET_SIG7: u32 = 1 << 24;
const RSP_MEM_MASK: usize = 0x1FFF;
#[derive(PartialEq, Copy, Clone)]
pub enum DmaDir {
None,
Write,
Read,
}
#[derive(Copy, Clone)]
pub struct RspDma {
pub dir: DmaDir,
pub length: u32,
pub memaddr: u32,
pub dramaddr: u32,
}
pub struct Rsp {
pub cpu: device::rsp_cpu::Cpu,
pub regs: [u32; SP_REGS_COUNT as usize],
pub regs2: [u32; SP_REGS2_COUNT as usize],
pub mem: [u8; 0x2000],
pub fifo: [RspDma; 2],
}
pub fn read_mem_fast(
device: &mut device::Device,
address: u64,
_access_size: device::memory::AccessSize,
) -> u32 {
let masked_address = address as usize & RSP_MEM_MASK;
u32::from_be_bytes(
device.rsp.mem[masked_address..masked_address + 4]
.try_into()
.unwrap(),
)
}
pub fn read_mem(
device: &mut device::Device,
address: u64,
_access_size: device::memory::AccessSize,
) -> u32 {
let masked_address = address as usize & RSP_MEM_MASK;
u32::from_be_bytes(
device.rsp.mem[masked_address..masked_address + 4]
.try_into()
.unwrap(),
)
}
pub fn write_mem(device: &mut device::Device, address: u64, value: u32, _mask: u32) {
let masked_address = address as usize & RSP_MEM_MASK;
let mut data = u32::from_be_bytes(
device.rsp.mem[masked_address..masked_address + 4]
.try_into()
.unwrap(),
);
device::memory::masked_write_32(&mut data, value, 0xFFFFFFFF);
device.rsp.mem[masked_address..masked_address + 4].copy_from_slice(&data.to_be_bytes());
if masked_address & 0x1000 != 0 {
// imem being updated
device.rsp.cpu.instructions[(masked_address & 0xFFF) / 4].func =
device::rsp_cpu::decode_opcode(device, data);
device.rsp.cpu.instructions[(masked_address & 0xFFF) / 4].opcode = data;
}
// SH/SB are broken: They overwrite the whole 32 bit, filling everything that isn't written with zeroes
}
fn do_dma(device: &mut device::Device, dma: RspDma) {
let l = dma.length;
let length = ((l & 0xfff) | 7) + 1;
let count = ((l >> 12) & 0xff) + 1;
let skip = (l >> 20) & 0xff8;
let mut mem_addr = dma.memaddr & 0xff8;
let mut dram_addr = dma.dramaddr & 0xfffff8;
let offset = dma.memaddr & 0x1000;
if dma.dir == DmaDir::Read {
let mut j = 0;
while j < count {
let mut i = 0;
while i < length {
let data = u32::from_be_bytes(
device.rsp.mem[(offset + (mem_addr & 0xFFF)) as usize
..(offset + (mem_addr & 0xFFF)) as usize + 4]
.try_into()
.unwrap(),
);
device.rdram.mem[dram_addr as usize..dram_addr as usize + 4]
.copy_from_slice(&data.to_ne_bytes());
mem_addr += 4;
dram_addr += 4;
i += 4;
}
dram_addr += skip;
j += 1;
}
} else {
let mut j = 0;
while j < count {
let mut i = 0;
while i < length {
let mut data = 0;
if dram_addr < device.rdram.size {
data = u32::from_ne_bytes(
device.rdram.mem[dram_addr as usize..dram_addr as usize + 4]
.try_into()
.unwrap(),
);
}
if offset != 0 {
// imem being updated
device.rsp.cpu.instructions[((mem_addr & 0xFFF) / 4) as usize].func =
device::rsp_cpu::decode_opcode(device, data);
device.rsp.cpu.instructions[((mem_addr & 0xFFF) / 4) as usize].opcode = data;
}
device.rsp.mem[(offset + (mem_addr & 0xFFF)) as usize
..(offset + (mem_addr & 0xFFF)) as usize + 4]
.copy_from_slice(&data.to_be_bytes());
mem_addr += 4;
dram_addr += 4;
i += 4;
}
dram_addr += skip;
j += 1;
}
}
device.rsp.regs[SP_MEM_ADDR_REG as usize] = (mem_addr & 0xfff) + (dma.memaddr & 0x1000);
device.rsp.regs[SP_DRAM_ADDR_REG as usize] = dram_addr;
device.rsp.regs[SP_RD_LEN_REG as usize] = 0xff8;
device.rsp.regs[SP_WR_LEN_REG as usize] = 0xff8;
device::events::create_event(
device,
device::events::EventType::SPDma,
device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize]
+ device::rdram::rdram_calculate_cycles((count * length) as u64)
+ 9,
device::rsp_interface::fifo_pop,
);
}
fn fifo_push(device: &mut device::Device, dir: DmaDir) {
if device.rsp.regs[SP_DMA_FULL_REG as usize] != 0 {
panic!("RSP DMA already full")
}
if device.rsp.regs[SP_DMA_BUSY_REG as usize] != 0 {
device.rsp.fifo[1].dir = dir;
if dir == DmaDir::Read {
device.rsp.fifo[1].length = device.rsp.regs[SP_WR_LEN_REG as usize]
} else {
device.rsp.fifo[1].length = device.rsp.regs[SP_RD_LEN_REG as usize]
}
device.rsp.fifo[1].memaddr = device.rsp.regs[SP_MEM_ADDR_REG as usize];
device.rsp.fifo[1].dramaddr = device.rsp.regs[SP_DRAM_ADDR_REG as usize];
device.rsp.regs[SP_DMA_FULL_REG as usize] = 1;
device.rsp.regs[SP_STATUS_REG as usize] |= SP_STATUS_DMA_FULL
} else {
device.rsp.fifo[0].dir = dir;
if dir == DmaDir::Read {
device.rsp.fifo[0].length = device.rsp.regs[SP_WR_LEN_REG as usize]
} else {
device.rsp.fifo[0].length = device.rsp.regs[SP_RD_LEN_REG as usize]
}
device.rsp.fifo[0].memaddr = device.rsp.regs[SP_MEM_ADDR_REG as usize];
device.rsp.fifo[0].dramaddr = device.rsp.regs[SP_DRAM_ADDR_REG as usize];
device.rsp.regs[SP_DMA_BUSY_REG as usize] = 1;
device.rsp.regs[SP_STATUS_REG as usize] |= SP_STATUS_DMA_BUSY;
do_dma(device, device.rsp.fifo[0])
}
}
fn fifo_pop(device: &mut device::Device) {
if device.rsp.regs[SP_DMA_FULL_REG as usize] != 0 {
device.rsp.fifo[0].dir = device.rsp.fifo[1].dir;
device.rsp.fifo[0].length = device.rsp.fifo[1].length;
device.rsp.fifo[0].memaddr = device.rsp.fifo[1].memaddr;
device.rsp.fifo[0].dramaddr = device.rsp.fifo[1].dramaddr;
device.rsp.regs[SP_DMA_FULL_REG as usize] = 0;
device.rsp.regs[SP_STATUS_REG as usize] &= !SP_STATUS_DMA_FULL;
do_dma(device, device.rsp.fifo[0])
} else {
device.rsp.regs[SP_DMA_BUSY_REG as usize] = 0;
device.rsp.regs[SP_STATUS_REG as usize] &= !SP_STATUS_DMA_BUSY
}
}
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 {
SP_SEMAPHORE_REG => {
let value = device.rsp.regs[reg as usize];
device.rsp.regs[reg as usize] = 1;
value
}
_ => device.rsp.regs[reg as usize],
}
}
pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u32) {
let reg = (address & 0xFFFF) >> 2;
match reg as u32 {
SP_STATUS_REG => update_sp_status(device, value),
SP_RD_LEN_REG => {
device::memory::masked_write_32(&mut device.rsp.regs[reg as usize], value, mask);
fifo_push(device, DmaDir::Write)
}
SP_WR_LEN_REG => {
device::memory::masked_write_32(&mut device.rsp.regs[reg as usize], value, mask);
fifo_push(device, DmaDir::Read)
}
SP_SEMAPHORE_REG => {
device::memory::masked_write_32(&mut device.rsp.regs[reg as usize], 0, mask)
}
_ => {
device::memory::masked_write_32(&mut device.rsp.regs[reg as usize], value, mask);
}
}
}
pub fn read_regs2(
device: &mut device::Device,
address: u64,
_access_size: device::memory::AccessSize,
) -> u32 {
device.rsp.regs2[((address & 0xFFFF) >> 2) as usize]
}
pub fn write_regs2(device: &mut device::Device, address: u64, value: u32, mask: u32) {
let reg = (address & 0xFFFF) >> 2;
match reg as u32 {
SP_PC_REG => {
device::memory::masked_write_32(
&mut device.rsp.regs2[reg as usize],
value & 0xFFC,
mask,
);
}
_ => device::memory::masked_write_32(&mut device.rsp.regs2[reg as usize], value, mask),
}
}
fn update_sp_status(device: &mut device::Device, w: u32) {
let was_halted = device.rsp.regs[SP_STATUS_REG as usize] & SP_STATUS_HALT != 0;
/* clear / set halt */
if w & SP_CLR_HALT != 0 && w & SP_SET_HALT == 0 {
device.rsp.regs[SP_STATUS_REG as usize] &= !SP_STATUS_HALT
}
if w & SP_SET_HALT != 0 && w & SP_CLR_HALT == 0 {
device::events::remove_event(device, device::events::EventType::SP);
device.rsp.regs[SP_STATUS_REG as usize] |= SP_STATUS_HALT
}
/* clear broke */
if w & SP_CLR_BROKE != 0 {
device.rsp.regs[SP_STATUS_REG as usize] &= !SP_STATUS_BROKE
}
/* clear SP interrupt */
if (w & SP_CLR_INTR) != 0 && (w & SP_SET_INTR) == 0 {
device::mi::clear_rcp_interrupt(device, device::mi::MI_INTR_SP)
}
/* set SP interrupt */
if (w & SP_SET_INTR) != 0 && (w & SP_CLR_INTR) == 0 {
device::mi::schedule_rcp_interrupt(device, device::mi::MI_INTR_SP);
}
/* clear / set single step */
if w & SP_CLR_SSTEP != 0 && w & SP_SET_SSTEP == 0 {
device.rsp.regs[SP_STATUS_REG as usize] &= !SP_STATUS_SSTEP
}
if w & SP_SET_SSTEP != 0 && w & SP_CLR_SSTEP == 0 {
device.rsp.regs[SP_STATUS_REG as usize] |= SP_STATUS_SSTEP
}
/* clear / set interrupt on break */
if w & SP_CLR_INTR_BREAK != 0 && w & SP_SET_INTR_BREAK == 0 {
device.rsp.regs[SP_STATUS_REG as usize] &= !SP_STATUS_INTR_BREAK
}
if w & SP_SET_INTR_BREAK != 0 && w & SP_CLR_INTR_BREAK == 0 {
device.rsp.regs[SP_STATUS_REG as usize] |= SP_STATUS_INTR_BREAK
}
/* clear / set signal 0 */
if w & SP_CLR_SIG0 != 0 && w & SP_SET_SIG0 == 0 {
device.rsp.regs[SP_STATUS_REG as usize] &= !SP_STATUS_SIG0
}
if w & SP_SET_SIG0 != 0 && w & SP_CLR_SIG0 == 0 {
device.rsp.regs[SP_STATUS_REG as usize] |= SP_STATUS_SIG0
}
/* clear / set signal 1 */
if w & SP_CLR_SIG1 != 0 && w & SP_SET_SIG1 == 0 {
device.rsp.regs[SP_STATUS_REG as usize] &= !SP_STATUS_SIG1
}
if w & SP_SET_SIG1 != 0 && w & SP_CLR_SIG1 == 0 {
device.rsp.regs[SP_STATUS_REG as usize] |= SP_STATUS_SIG1
}
/* clear / set signal 2 */
if w & SP_CLR_SIG2 != 0 && w & SP_SET_SIG2 == 0 {
device.rsp.regs[SP_STATUS_REG as usize] &= !SP_STATUS_SIG2
}
if w & SP_SET_SIG2 != 0 && w & SP_CLR_SIG2 == 0 {
device.rsp.regs[SP_STATUS_REG as usize] |= SP_STATUS_SIG2
}
/* clear / set signal 3 */
if w & SP_CLR_SIG3 != 0 && w & SP_SET_SIG3 == 0 {
device.rsp.regs[SP_STATUS_REG as usize] &= !SP_STATUS_SIG3
}
if w & SP_SET_SIG3 != 0 && w & SP_CLR_SIG3 == 0 {
device.rsp.regs[SP_STATUS_REG as usize] |= SP_STATUS_SIG3
}
/* clear / set signal 4 */
if w & SP_CLR_SIG4 != 0 && w & SP_SET_SIG4 == 0 {
device.rsp.regs[SP_STATUS_REG as usize] &= !SP_STATUS_SIG4
}
if w & SP_SET_SIG4 != 0 && w & SP_CLR_SIG4 == 0 {
device.rsp.regs[SP_STATUS_REG as usize] |= SP_STATUS_SIG4
}
/* clear / set signal 5 */
if w & SP_CLR_SIG5 != 0 && w & SP_SET_SIG5 == 0 {
device.rsp.regs[SP_STATUS_REG as usize] &= !SP_STATUS_SIG5
}
if w & SP_SET_SIG5 != 0 && w & SP_CLR_SIG5 == 0 {
device.rsp.regs[SP_STATUS_REG as usize] |= SP_STATUS_SIG5
}
/* clear / set signal 6 */
if w & SP_CLR_SIG6 != 0 && w & SP_SET_SIG6 == 0 {
device.rsp.regs[SP_STATUS_REG as usize] &= !SP_STATUS_SIG6
}
if w & SP_SET_SIG6 != 0 && w & SP_CLR_SIG6 == 0 {
device.rsp.regs[SP_STATUS_REG as usize] |= SP_STATUS_SIG6
}
/* clear / set signal 7 */
if w & SP_CLR_SIG7 != 0 && w & SP_SET_SIG7 == 0 {
device.rsp.regs[SP_STATUS_REG as usize] &= !SP_STATUS_SIG7
}
if w & SP_SET_SIG7 != 0 && w & SP_CLR_SIG7 == 0 {
device.rsp.regs[SP_STATUS_REG as usize] |= SP_STATUS_SIG7
}
if device.rsp.regs[SP_STATUS_REG as usize] & SP_STATUS_HALT == 0 && was_halted {
device.rsp.cpu.broken = false;
device.rsp.cpu.halted = false;
device.rsp.cpu.sync_point = false;
do_task(device);
}
}
fn do_task(device: &mut device::Device) {
let timer = device::rsp_cpu::run(device);
device::events::create_event(
device,
device::events::EventType::SP,
device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize] + timer,
rsp_event,
)
}
fn rsp_event(device: &mut device::Device) {
if device.rsp.cpu.broken {
device.rsp.regs[SP_STATUS_REG as usize] |= SP_STATUS_HALT | SP_STATUS_BROKE;
if device.rsp.regs[SP_STATUS_REG as usize] & SP_STATUS_INTR_BREAK != 0 {
device::mi::set_rcp_interrupt(device, device::mi::MI_INTR_SP)
}
return;
}
if device.rsp.cpu.halted {
device.rsp.regs[SP_STATUS_REG as usize] |= SP_STATUS_HALT;
return;
}
device.rsp.cpu.sync_point = false;
do_task(device)
}
pub fn init(device: &mut device::Device) {
device.rsp.regs[SP_STATUS_REG as usize] = 1;
device::rsp_cpu::init(device);
}