375 lines
14 KiB
Rust
375 lines
14 KiB
Rust
use crate::device;
|
|
|
|
pub const COP0_INDEX_REG: u32 = 0;
|
|
pub 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;
|
|
pub const COP0_WIRED_REG: u32 = 6;
|
|
//pub 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;
|
|
pub 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;
|
|
pub const COP0_PREVID_REG: u32 = 15;
|
|
pub const COP0_CONFIG_REG: u32 = 16;
|
|
pub const COP0_LLADDR_REG: u32 = 17;
|
|
//pub const COP0_WATCHLO_REG: u32 = 18;
|
|
//pub const COP0_WATCHHI_REG: u32 = 19;
|
|
pub const COP0_XCONTEXT_REG: u32 = 20;
|
|
//pub const COP0_UNUSED_21: u32 = 21;
|
|
//pub const COP0_UNUSED_22: u32 = 22;
|
|
//pub const COP0_UNUSED_23: u32 = 23;
|
|
//pub const COP0_UNUSED_24: u32 = 24;
|
|
//pub const COP0_UNUSED_25: u32 = 25;
|
|
//pub const COP0_PARITYERR_REG: u32 = 26;
|
|
//pub const COP0_CACHEERR_REG: u32 = 27;
|
|
pub const COP0_TAGLO_REG: u32 = 28;
|
|
//pub const COP0_TAGHI_REG: u32 = 29;
|
|
pub const COP0_ERROREPC_REG: u32 = 30;
|
|
//pub 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_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;
|
|
|
|
//pub 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;
|
|
//pub const COP0_CAUSE_EXCCODE_ADEL: u64 = 4 << 2;
|
|
//pub 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_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;
|
|
|
|
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],
|
|
pub instrs: [fn(&mut device::Device, u32); 32],
|
|
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 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,
|
|
)
|
|
}
|
|
|
|
pub 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))
|
|
}
|
|
|
|
pub 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,
|
|
),
|
|
)
|
|
}
|
|
|
|
pub 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],
|
|
)
|
|
}
|
|
|
|
pub fn tlbr(device: &mut device::Device, _opcode: u32) {
|
|
device::tlb::read(device, device.cpu.cop0.regs[COP0_INDEX_REG as usize])
|
|
}
|
|
|
|
pub fn tlbwi(device: &mut device::Device, _opcode: u32) {
|
|
device::tlb::write(device, device.cpu.cop0.regs[COP0_INDEX_REG as usize])
|
|
}
|
|
|
|
pub fn tlbwr(device: &mut device::Device, _opcode: u32) {
|
|
let random = set_random_register(device);
|
|
device::tlb::write(device, random)
|
|
}
|
|
|
|
pub fn tlbp(device: &mut device::Device, _opcode: u32) {
|
|
device::tlb::probe(device)
|
|
}
|
|
|
|
pub 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)
|
|
}
|
|
|
|
pub 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 get_control_registers(device: &mut device::Device, index: u32) -> u64 {
|
|
match index {
|
|
COP0_COUNT_REG => return device.cpu.cop0.regs[index as usize] >> 1,
|
|
COP0_RANDOM_REG => return set_random_register(device),
|
|
7 | 21 | 22 | 23 | 24 | 25 | 31 => return device.cpu.cop0.reg_latch,
|
|
_ => return device.cpu.cop0.regs[index as usize],
|
|
}
|
|
}
|
|
|
|
pub 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 <<= 1;
|
|
device::events::translate_events(
|
|
device,
|
|
device.cpu.cop0.regs[COP0_COUNT_REG as usize],
|
|
data,
|
|
)
|
|
}
|
|
COP0_WIRED_REG => device.cpu.cop0.regs[COP0_RANDOM_REG as usize] = 31,
|
|
COP0_COMPARE_REG => {
|
|
let current_count = (device.cpu.cop0.regs[COP0_COUNT_REG as usize] >> 1) & 0xFFFFFFFF;
|
|
let mut event_time =
|
|
(device.cpu.cop0.regs[COP0_COUNT_REG as usize] & 0xFFFFFFFF00000000) | (data << 1);
|
|
if current_count >= data {
|
|
event_time += !0 as u32 as u64
|
|
}
|
|
let compare_event: &mut device::events::Event =
|
|
device::events::get_event(device, device::events::EventType::Compare).unwrap();
|
|
compare_event.count = event_time; // reschedule the next compare interrupt event
|
|
device::events::set_next_event(device);
|
|
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[COP0_CAUSE_REG as usize] &= !COP0_CAUSE_EXCCODE_MASK;
|
|
device.cpu.cop0.regs[COP0_CAUSE_REG as usize] |= COP0_CAUSE_IP7;
|
|
|
|
device::events::create_event(
|
|
device,
|
|
device::events::EventType::Compare,
|
|
device.cpu.next_event_count + (!0 as u32 as u64),
|
|
compare_event,
|
|
);
|
|
device::exceptions::check_pending_interrupts(device);
|
|
}
|
|
|
|
pub fn set_random_register(device: &mut device::Device) -> u64 {
|
|
if device.cpu.cop0.regs[COP0_WIRED_REG as usize] > 31 {
|
|
return (u64::MAX - device.cpu.cop0.regs[COP0_COUNT_REG as usize]) & 0x3F;
|
|
} else {
|
|
return (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 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
|
|
!0 as u32 as u64, // count
|
|
COP0_ENTRYHI_REG_MASK,
|
|
!0 as u32 as u64, // compare
|
|
COP0_STATUS_REG_MASK,
|
|
COP0_CAUSE_REG_MASK,
|
|
!0, // 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
|
|
!0, // ErrorPC
|
|
0, // 31
|
|
];
|
|
|
|
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::reserved, // 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
|
|
];
|
|
// 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::EventType::Compare,
|
|
!0 as u32 as u64,
|
|
compare_event,
|
|
)
|
|
}
|