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/exceptions.rs
2026-04-15 13:39:49 +02:00

201 lines
6.9 KiB
Rust

use crate::device;
use crate::ui;
pub fn check_pending_interrupts(device: &mut device::Device) {
if (device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG as usize]
& (device::cop0::COP0_STATUS_IE
| device::cop0::COP0_STATUS_EXL
| device::cop0::COP0_STATUS_ERL))
!= device::cop0::COP0_STATUS_IE
{
// interrupts disabled globally, or error/exception is already set
return;
}
if device.mi.regs[device::mi::MI_INTR_REG as usize]
& device.mi.regs[device::mi::MI_INTR_MASK_REG as usize]
!= 0
{
device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] |= device::cop0::COP0_CAUSE_IP2;
device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] &=
!device::cop0::COP0_CAUSE_EXCCODE_MASK;
}
if (device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG as usize]
& device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize]
& device::cop0::COP0_CAUSE_IP_MASK)
== 0
{
// interrupt disabled, or no pending interrupts
return;
}
if device.cpu.cop0.is_event {
interrupt_exception(device);
} else {
device::events::create_event(device, device::events::EVENT_TYPE_INT, 0);
}
}
pub fn interrupt_exception(device: &mut device::Device) {
exception_general(device, 0x180);
}
pub fn floating_point_exception(device: &mut device::Device) {
device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] =
device::cop0::COP0_CAUSE_EXCCODE_FPE;
exception_general(device, 0x180)
}
pub fn trap_exception(device: &mut device::Device) {
device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] =
device::cop0::COP0_CAUSE_EXCCODE_TR;
exception_general(device, 0x180)
}
pub fn syscall_exception(device: &mut device::Device) {
device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] =
device::cop0::COP0_CAUSE_EXCCODE_SYS;
exception_general(device, 0x180)
}
pub fn break_exception(device: &mut device::Device) {
device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] =
device::cop0::COP0_CAUSE_EXCCODE_BP;
exception_general(device, 0x180)
}
pub fn reserved_exception(device: &mut device::Device, cop: u64) {
device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] =
device::cop0::COP0_CAUSE_EXCCODE_RI | cop;
exception_general(device, 0x180)
}
pub fn cop_unusable_exception(device: &mut device::Device, cop: u64) {
device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] =
device::cop0::COP0_CAUSE_EXCCODE_CPU | cop;
exception_general(device, 0x180)
}
pub fn tlb_miss_exception(
device: &mut device::Device,
address: u64,
access_type: device::memory::AccessType,
) {
if access_type == device::memory::AccessType::Read {
device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] =
device::cop0::COP0_CAUSE_EXCCODE_TLBL
} else {
device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] =
device::cop0::COP0_CAUSE_EXCCODE_TLBS
}
device.cpu.cop0.regs[device::cop0::COP0_BADVADDR_REG as usize] = address;
device::memory::masked_write_64(
&mut device.cpu.cop0.regs[device::cop0::COP0_CONTEXT_REG as usize],
address >> 9,
device::cop0::COP0_CONTEXT_BADVPN2_MASK,
);
device::memory::masked_write_64(
&mut device.cpu.cop0.regs[device::cop0::COP0_XCONTEXT_REG as usize],
address >> 9,
device::cop0::COP0_XCONTEXT_BADVPN2_MASK,
);
device::memory::masked_write_64(
&mut device.cpu.cop0.regs[device::cop0::COP0_XCONTEXT_REG as usize],
address >> 31,
device::cop0::COP0_XCONTEXT_REGION_MASK,
);
device::memory::masked_write_64(
&mut device.cpu.cop0.regs[device::cop0::COP0_ENTRYHI_REG as usize],
address,
0xFFFFE000,
);
let mut vector_offset = 0x180;
let mut valid = true;
for i in device.cpu.cop0.tlb_entries {
if address & !3 >= i.start_even && address & !3 <= i.end_even {
valid = i.v_even != 0;
if valid && access_type == device::memory::AccessType::Write && i.d_even == 0 {
device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] =
device::cop0::COP0_CAUSE_EXCCODE_MOD;
valid = false;
}
break;
}
if address & !3 >= i.start_odd && address & !3 <= i.end_odd {
valid = i.v_odd != 0;
if valid && access_type == device::memory::AccessType::Write && i.d_odd == 0 {
device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] =
device::cop0::COP0_CAUSE_EXCCODE_MOD;
valid = false;
}
break;
}
}
if device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG as usize] & device::cop0::COP0_STATUS_EXL
== 0
&& valid
{
vector_offset = 0;
}
exception_general(device, vector_offset)
}
pub fn reset_event(device: &mut device::Device) {
device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] &= !device::cop0::COP0_CAUSE_IP4;
device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG as usize] |= device::cop0::COP0_STATUS_ERL
| device::cop0::COP0_STATUS_SR
| device::cop0::COP0_STATUS_BEV;
device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG as usize] &= !device::cop0::COP0_STATUS_TS;
device.cpu.cop0.regs[device::cop0::COP0_ERROREPC_REG as usize] = device.cpu.pc;
device.cpu.pc = 0xBFC00000;
device.cpu.branch_state.state = device::cpu::State::Step;
device.rsp.regs2[device::rsp_interface::SP_PC_REG as usize] = 0;
device::pif::reset_pif(device, true);
ui::video::onscreen_message("Game reset", false);
}
fn exception_general(device: &mut device::Device, vector_offset: u32) {
if device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG as usize] & device::cop0::COP0_STATUS_EXL
== 0
{
device.cpu.cop0.regs[device::cop0::COP0_EPC_REG as usize] = device.cpu.pc;
if device::cpu::in_delay_slot(device) {
device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] |=
device::cop0::COP0_CAUSE_BD;
device.cpu.cop0.regs[device::cop0::COP0_EPC_REG as usize] -= 4;
} else {
device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] &=
!device::cop0::COP0_CAUSE_BD;
}
}
device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG as usize] |= device::cop0::COP0_STATUS_EXL;
if device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG as usize] & device::cop0::COP0_STATUS_BEV
== 0
{
device.cpu.pc = device::cpu_instructions::se32((0x80000000 + vector_offset) as i32);
} else {
device.cpu.pc = device::cpu_instructions::se32((0xBFC00200 + vector_offset) as i32);
}
device.cpu.branch_state.state = device::cpu::State::Exception;
device::cop0::add_cycles(device, 2);
}