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/cop0.rs
Logan McNaughton 5da6a6c3ee
clippy fixes (#688)
* clippy fixes

* fix fmt
2026-03-24 11:09:16 +01:00

390 lines
14 KiB
Rust

use crate::{device, savestates};
pub const COP0_INDEX_REG: u32 = 0;
const COP0_RANDOM_REG: u32 = 1;
pub const COP0_ENTRYLO0_REG: u32 = 2;
pub const COP0_ENTRYLO1_REG: u32 = 3;
pub const COP0_CONTEXT_REG: u32 = 4;
pub const COP0_PAGEMASK_REG: u32 = 5;
const COP0_WIRED_REG: u32 = 6;
//const COP0_UNUSED_7: u32 = 7;
pub const COP0_BADVADDR_REG: u32 = 8;
pub const COP0_COUNT_REG: u32 = 9;
pub const COP0_ENTRYHI_REG: u32 = 10;
const COP0_COMPARE_REG: u32 = 11;
pub const COP0_STATUS_REG: u32 = 12;
pub const COP0_CAUSE_REG: u32 = 13;
pub const COP0_EPC_REG: u32 = 14;
const COP0_PREVID_REG: u32 = 15;
const COP0_CONFIG_REG: u32 = 16;
pub const COP0_LLADDR_REG: u32 = 17;
//const COP0_WATCHLO_REG: u32 = 18;
//const COP0_WATCHHI_REG: u32 = 19;
pub const COP0_XCONTEXT_REG: u32 = 20;
//const COP0_UNUSED_21: u32 = 21;
//const COP0_UNUSED_22: u32 = 22;
//const COP0_UNUSED_23: u32 = 23;
//const COP0_UNUSED_24: u32 = 24;
//const COP0_UNUSED_25: u32 = 25;
//const COP0_PARITYERR_REG: u32 = 26;
//const COP0_CACHEERR_REG: u32 = 27;
pub const COP0_TAGLO_REG: u32 = 28;
//const COP0_TAGHI_REG: u32 = 29;
pub const COP0_ERROREPC_REG: u32 = 30;
//const COP0_UNUSED_31: u32 = 31;
pub const COP0_REGS_COUNT: u32 = 32;
pub const COP0_STATUS_IE: u64 = 1 << 0;
pub const COP0_STATUS_EXL: u64 = 1 << 1;
pub const COP0_STATUS_ERL: u64 = 1 << 2;
pub const COP0_STATUS_SR: u64 = 1 << 20;
pub const COP0_STATUS_TS: u64 = 1 << 21;
pub const COP0_STATUS_BEV: u64 = 1 << 22;
pub const COP0_STATUS_FR: u64 = 1 << 26;
pub const COP0_STATUS_CU1: u64 = 1 << 29;
pub const COP0_STATUS_CU2: u64 = 1 << 30;
//const COP0_CAUSE_EXCCODE_INTR: u64 = 0 << 2;
pub const COP0_CAUSE_EXCCODE_MOD: u64 = 1 << 2;
pub const COP0_CAUSE_EXCCODE_TLBL: u64 = 2 << 2;
pub const COP0_CAUSE_EXCCODE_TLBS: u64 = 3 << 2;
//const COP0_CAUSE_EXCCODE_ADEL: u64 = 4 << 2;
//const COP0_CAUSE_EXCCODE_ADES: u64 = 5 << 2;
pub const COP0_CAUSE_EXCCODE_SYS: u64 = 8 << 2;
pub const COP0_CAUSE_EXCCODE_BP: u64 = 9 << 2;
pub const COP0_CAUSE_EXCCODE_RI: u64 = 10 << 2;
pub const COP0_CAUSE_EXCCODE_CPU: u64 = 11 << 2;
pub const COP0_CAUSE_EXCCODE_TR: u64 = 13 << 2;
pub const COP0_CAUSE_EXCCODE_FPE: u64 = 15 << 2;
pub const COP0_CAUSE_IP2: u64 = 1 << 10;
pub const COP0_CAUSE_IP4: u64 = 1 << 12;
pub const COP0_CAUSE_IP7: u64 = 1 << 15;
pub const COP0_CAUSE_BD: u64 = 1 << 31;
pub const COP0_CAUSE_CE1: u64 = 1 << 28;
pub const COP0_CAUSE_CE2: u64 = 1 << 29;
pub const COP0_CAUSE_IP_MASK: u64 = 0b00000000000000001111111100000000;
pub const COP0_CAUSE_EXCCODE_MASK: u64 = 0x1F << 2;
pub const COP0_CONTEXT_BADVPN2_MASK: u64 = 0b00000000011111111111111111110000;
pub const COP0_XCONTEXT_BADVPN2_MASK: u64 = 0b01111111111111111111111111110000;
pub const COP0_XCONTEXT_REGION_MASK: u64 = 0b110000000000000000000000000000000;
const COP0_INDEX_REG_MASK: u64 = 0b10000000000000000000000000111111;
//const COP0_RANDOM_REG_MASK: u64 = 0b00000000000000000000000000111111;
const COP0_ENTRYLO_REG_MASK: u64 =
0b0000000000000000000000000000000000111111111111111111111111111111;
const COP0_CONTEXT_REG_MASK: u64 =
0b1111111111111111111111111111111111111111100000000000000000000000;
const COP0_PAGEMASK_REG_MASK: u64 = 0b00000001111111111110000000000000;
const COP0_WIRED_REG_MASK: u64 = 0b00000000000000000000000000111111;
const COP0_ENTRYHI_REG_MASK: u64 =
0b1100000000000000000000001111111111111111111111111110000011111111;
const COP0_STATUS_REG_MASK: u64 = 0b11111111010101111111111111111111;
const COP0_CAUSE_REG_MASK: u64 = 0b00000000000000000000001100000000;
const COP0_CONFIG_REG_MASK: u64 = 0b00001111000000001000000000001111;
const COP0_LLADDR_REG_MASK: u64 = 0b11111111111111111111111111111111;
const COP0_WATCHLO_REG_MASK: u64 = 0b11111111111111111111111111111011;
const COP0_WATCHHI_REG_MASK: u64 = 0b00000000000000000000000000001111;
const COP0_XCONTEXT_REG_MASK: u64 =
0b1111111111111111111111111111111000000000000000000000000000000000;
const COP0_PARITYERR_REG_MASK: u64 = 0b00000000000000000000000011111111;
const COP0_TAGLO_REG_MASK: u64 = 0b00001111111111111111111111000000;
#[derive(serde::Serialize, serde::Deserialize)]
pub struct Cop0 {
pub reg_latch: u64,
pub regs: [u64; COP0_REGS_COUNT as usize],
pub reg_write_masks: [u64; COP0_REGS_COUNT as usize],
#[serde(skip, default = "savestates::default_instructions")]
pub instrs: [fn(&mut device::Device, u32); 32],
#[serde(skip, default = "savestates::default_instructions")]
pub instrs2: [fn(&mut device::Device, u32); 32],
pub tlb_lut_r: Vec<device::tlb::TlbLut>,
pub tlb_lut_w: Vec<device::tlb::TlbLut>,
pub tlb_entries: [device::tlb::TlbEntry; 32],
pub is_event: bool,
}
fn mfc0(device: &mut device::Device, opcode: u32) {
device.cpu.gpr[device::cpu_instructions::rt(opcode) as usize] = device::cpu_instructions::se32(
(get_control_registers(device, device::cpu_instructions::rd(opcode))) as u32 as i32,
)
}
fn dmfc0(device: &mut device::Device, opcode: u32) {
device.cpu.gpr[device::cpu_instructions::rt(opcode) as usize] =
get_control_registers(device, device::cpu_instructions::rd(opcode))
}
fn mtc0(device: &mut device::Device, opcode: u32) {
device::cop0::set_control_registers(
device,
device::cpu_instructions::rd(opcode),
device::cpu_instructions::se32(
device.cpu.gpr[device::cpu_instructions::rt(opcode) as usize] as u32 as i32,
),
)
}
fn dmtc0(device: &mut device::Device, opcode: u32) {
device::cop0::set_control_registers(
device,
device::cpu_instructions::rd(opcode),
device.cpu.gpr[device::cpu_instructions::rt(opcode) as usize],
)
}
fn tlbr(device: &mut device::Device, _opcode: u32) {
device::tlb::read(device, device.cpu.cop0.regs[COP0_INDEX_REG as usize])
}
fn tlbwi(device: &mut device::Device, _opcode: u32) {
device::tlb::write(device, device.cpu.cop0.regs[COP0_INDEX_REG as usize])
}
fn tlbwr(device: &mut device::Device, _opcode: u32) {
let random = set_random_register(device);
device::tlb::write(device, random)
}
fn tlbp(device: &mut device::Device, _opcode: u32) {
device::tlb::probe(device)
}
fn eret(device: &mut device::Device, _opcode: u32) {
if device.cpu.cop0.regs[COP0_STATUS_REG as usize] & COP0_STATUS_ERL != 0 {
device.cpu.pc = device.cpu.cop0.regs[COP0_ERROREPC_REG as usize];
device.cpu.cop0.regs[COP0_STATUS_REG as usize] &= !COP0_STATUS_ERL
} else {
device.cpu.pc = device.cpu.cop0.regs[COP0_EPC_REG as usize];
device.cpu.cop0.regs[COP0_STATUS_REG as usize] &= !COP0_STATUS_EXL
}
device.cpu.branch_state.state = device::cpu::State::Exception;
device.cpu.llbit = false;
device::exceptions::check_pending_interrupts(device)
}
fn execute_cp0(device: &mut device::Device, opcode: u32) {
device.cpu.cop0.instrs2[(opcode & 0x3F) as usize](device, opcode)
}
pub fn syscall(device: &mut device::Device, _opcode: u32) {
device::exceptions::syscall_exception(device)
}
pub fn break_(device: &mut device::Device, _opcode: u32) {
device::exceptions::break_exception(device)
}
pub fn reserved(device: &mut device::Device, _opcode: u32) {
device::exceptions::reserved_exception(device, 0);
}
pub fn emux_xdetect(_device: &mut device::Device, _opcode: u32) {}
fn get_control_registers(device: &device::Device, index: u32) -> u64 {
match index {
COP0_COUNT_REG => device.cpu.cop0.regs[index as usize] >> 1,
COP0_RANDOM_REG => set_random_register(device),
7 | 21 | 22 | 23 | 24 | 25 | 31 => device.cpu.cop0.reg_latch,
_ => device.cpu.cop0.regs[index as usize],
}
}
fn set_control_registers(device: &mut device::Device, index: u32, mut data: u64) {
device.cpu.cop0.reg_latch = data;
match index {
COP0_COUNT_REG => {
data &= 0xFFFFFFFF;
data <<= 1;
device::events::translate_events(
device,
device.cpu.cop0.regs[COP0_COUNT_REG as usize],
data,
);
device.cpu.cop0.regs[COP0_COUNT_REG as usize] = data;
return;
}
COP0_WIRED_REG => device.cpu.cop0.regs[COP0_RANDOM_REG as usize] = 31,
COP0_COMPARE_REG => {
data &= 0xFFFFFFFF;
let current_count = (device.cpu.cop0.regs[COP0_COUNT_REG as usize] >> 1) & 0xFFFFFFFF;
let mut compare_event_diff = (data as u32).wrapping_sub(current_count as u32);
if compare_event_diff == 0 {
compare_event_diff += u32::MAX;
}
device::events::create_event(
device,
device::events::EVENT_TYPE_COMPARE,
(compare_event_diff as u64) << 1,
);
device.cpu.cop0.regs[COP0_CAUSE_REG as usize] &= !COP0_CAUSE_IP7;
}
COP0_STATUS_REG
if data & COP0_STATUS_FR != device.cpu.cop0.regs[index as usize] & COP0_STATUS_FR =>
{
device::cop1::set_fgr_registers(device, data)
}
_ => {}
}
device::memory::masked_write_64(
&mut device.cpu.cop0.regs[index as usize],
data,
device.cpu.cop0.reg_write_masks[index as usize],
);
device::exceptions::check_pending_interrupts(device);
}
pub fn compare_event(device: &mut device::Device) {
device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] |= device::cop0::COP0_CAUSE_IP7;
device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] &=
!device::cop0::COP0_CAUSE_EXCCODE_MASK;
device::events::create_event_at(
device,
device::events::EVENT_TYPE_COMPARE,
device.cpu.next_event_count + (u32::MAX as u64),
);
device::exceptions::check_pending_interrupts(device);
}
fn set_random_register(device: &device::Device) -> u64 {
if device.cpu.cop0.regs[COP0_WIRED_REG as usize] > 31 {
(u64::MAX - device.cpu.cop0.regs[COP0_COUNT_REG as usize]) & 0x3F
} else {
(u64::MAX - device.cpu.cop0.regs[COP0_COUNT_REG as usize])
% (32 - device.cpu.cop0.regs[COP0_WIRED_REG as usize])
+ device.cpu.cop0.regs[COP0_WIRED_REG as usize]
}
}
pub fn add_cycles(device: &mut device::Device, cycles: u64) {
device.cpu.cop0.regs[COP0_COUNT_REG as usize] += cycles // COUNT_REG is shifted right 1 bit when read by MFC0
}
pub fn map_instructions(device: &mut device::Device) {
device.cpu.cop0.instrs = [
device::cop0::mfc0, // 0
device::cop0::dmfc0, // 1
device::cop0::reserved, // 2
device::cop0::reserved, // 3
device::cop0::mtc0, // 4
device::cop0::dmtc0, // 5
device::cop0::reserved, // 6
device::cop0::reserved, // 7
device::cop0::reserved, // 8
device::cop0::reserved, // 9
device::cop0::reserved, // 10
device::cop0::reserved, // 11
device::cop0::reserved, // 12
device::cop0::reserved, // 13
device::cop0::reserved, // 14
device::cop0::reserved, // 15
device::cop0::execute_cp0, // 16
device::cop0::reserved, // 17
device::cop0::reserved, // 18
device::cop0::reserved, // 19
device::cop0::emux_xdetect, // 20
device::cop0::reserved, // 21
device::cop0::reserved, // 22
device::cop0::reserved, // 23
device::cop0::reserved, // 24
device::cop0::reserved, // 25
device::cop0::reserved, // 26
device::cop0::reserved, // 27
device::cop0::reserved, // 28
device::cop0::reserved, // 29
device::cop0::reserved, // 30
device::cop0::reserved, // 31
];
device.cpu.cop0.instrs2 = [
device::cop0::reserved, // 0
device::cop0::tlbr, // 1
device::cop0::tlbwi, // 2
device::cop0::reserved, // 3
device::cop0::reserved, // 4
device::cop0::reserved, // 5
device::cop0::tlbwr, // 6
device::cop0::reserved, // 7
device::cop0::tlbp, // 8
device::cop0::reserved, // 9
device::cop0::reserved, // 10
device::cop0::reserved, // 11
device::cop0::reserved, // 12
device::cop0::reserved, // 13
device::cop0::reserved, // 14
device::cop0::reserved, // 15
device::cop0::reserved, // 16
device::cop0::reserved, // 17
device::cop0::reserved, // 18
device::cop0::reserved, // 19
device::cop0::reserved, // 20
device::cop0::reserved, // 21
device::cop0::reserved, // 22
device::cop0::reserved, // 23
device::cop0::eret, // 24
device::cop0::reserved, // 25
device::cop0::reserved, // 26
device::cop0::reserved, // 27
device::cop0::reserved, // 28
device::cop0::reserved, // 29
device::cop0::reserved, // 30
device::cop0::reserved, // 31
];
}
pub fn init(device: &mut device::Device) {
device.cpu.cop0.reg_write_masks = [
COP0_INDEX_REG_MASK,
0, // Random, read only
COP0_ENTRYLO_REG_MASK,
COP0_ENTRYLO_REG_MASK,
COP0_CONTEXT_REG_MASK,
COP0_PAGEMASK_REG_MASK,
COP0_WIRED_REG_MASK,
0, // 7
0, // BadVAddr, read only
u32::MAX as u64, // count
COP0_ENTRYHI_REG_MASK,
u32::MAX as u64, // compare
COP0_STATUS_REG_MASK,
COP0_CAUSE_REG_MASK,
u64::MAX, // EPC
0, // previd, read only
COP0_CONFIG_REG_MASK,
COP0_LLADDR_REG_MASK,
COP0_WATCHLO_REG_MASK,
COP0_WATCHHI_REG_MASK,
COP0_XCONTEXT_REG_MASK,
0, // 21
0, // 22
0, // 23
0, // 24
0, // 25
COP0_PARITYERR_REG_MASK,
0, // cache error
COP0_TAGLO_REG_MASK,
0, // taghi
u64::MAX, // ErrorPC
0, // 31
];
map_instructions(device);
// taken from VR4300 manual
device.cpu.cop0.regs[COP0_RANDOM_REG as usize] = 0b00000000000000000000000000011111;
device.cpu.cop0.regs[COP0_CONFIG_REG as usize] = 0b01110000000001101110010001100000;
device.cpu.cop0.regs[COP0_STATUS_REG as usize] = 0b00000000010000000000000000000100;
device.cpu.cop0.regs[COP0_PREVID_REG as usize] = 0b00000000000000000000101100100010;
device.cpu.cop0.regs[COP0_EPC_REG as usize] = 0b11111111111111111111111111111111;
device.cpu.cop0.regs[COP0_ERROREPC_REG as usize] = 0b11111111111111111111111111111111;
device.cpu.cop0.regs[COP0_BADVADDR_REG as usize] = 0xFFFFFFFF;
device.cpu.cop0.regs[COP0_CONTEXT_REG as usize] = 0x7FFFF0;
device::events::create_event(device, device::events::EVENT_TYPE_COMPARE, u32::MAX as u64)
}