182 lines
6.4 KiB
Rust
182 lines
6.4 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]
|
|
& (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] & device.mi.regs[device::mi::MI_INTR_MASK_REG] != 0 {
|
|
device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG] |= device::cop0::COP0_CAUSE_IP2;
|
|
}
|
|
|
|
if (device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG]
|
|
& device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG]
|
|
& 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) {
|
|
device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG] &= !device::cop0::COP0_CAUSE_EXCCODE_MASK;
|
|
exception_general(device, 0x180);
|
|
}
|
|
|
|
pub fn floating_point_exception(device: &mut device::Device) {
|
|
device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG] = 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] = 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] = 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] = 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] = 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] = 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] = device::cop0::COP0_CAUSE_EXCCODE_TLBL
|
|
} else {
|
|
device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG] = device::cop0::COP0_CAUSE_EXCCODE_TLBS
|
|
}
|
|
|
|
device.cpu.cop0.regs[device::cop0::COP0_BADVADDR_REG] = address;
|
|
device::memory::masked_write_64(
|
|
&mut device.cpu.cop0.regs[device::cop0::COP0_CONTEXT_REG],
|
|
address >> 9,
|
|
device::cop0::COP0_CONTEXT_BADVPN2_MASK,
|
|
);
|
|
device::memory::masked_write_64(
|
|
&mut device.cpu.cop0.regs[device::cop0::COP0_XCONTEXT_REG],
|
|
address >> 9,
|
|
device::cop0::COP0_XCONTEXT_BADVPN2_MASK,
|
|
);
|
|
device::memory::masked_write_64(
|
|
&mut device.cpu.cop0.regs[device::cop0::COP0_XCONTEXT_REG],
|
|
address >> 31,
|
|
device::cop0::COP0_XCONTEXT_REGION_MASK,
|
|
);
|
|
device::memory::masked_write_64(
|
|
&mut device.cpu.cop0.regs[device::cop0::COP0_ENTRYHI_REG],
|
|
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] =
|
|
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] =
|
|
device::cop0::COP0_CAUSE_EXCCODE_MOD;
|
|
valid = false;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG] & 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] &= !device::cop0::COP0_CAUSE_IP4;
|
|
|
|
device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG] |= device::cop0::COP0_STATUS_ERL
|
|
| device::cop0::COP0_STATUS_SR
|
|
| device::cop0::COP0_STATUS_BEV;
|
|
device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG] &= !device::cop0::COP0_STATUS_TS;
|
|
|
|
device.cpu.cop0.regs[device::cop0::COP0_ERROREPC_REG] = 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] = 0;
|
|
|
|
device::pif::reset_pif(device, true);
|
|
|
|
ui::video::onscreen_message("Game reset", ui::video::MESSAGE_LENGTH_MESSAGE_SHORT);
|
|
}
|
|
|
|
fn exception_general(device: &mut device::Device, vector_offset: u32) {
|
|
if device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG] & device::cop0::COP0_STATUS_EXL == 0 {
|
|
device.cpu.cop0.regs[device::cop0::COP0_EPC_REG] = device.cpu.pc;
|
|
if device::cpu::in_delay_slot(device) {
|
|
device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG] |= device::cop0::COP0_CAUSE_BD;
|
|
device.cpu.cop0.regs[device::cop0::COP0_EPC_REG] -= 4;
|
|
} else {
|
|
device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG] &= !device::cop0::COP0_CAUSE_BD;
|
|
}
|
|
}
|
|
|
|
device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG] |= device::cop0::COP0_STATUS_EXL;
|
|
|
|
if device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG] & 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);
|
|
}
|