diff --git a/src/device.rs b/src/device.rs index 13cfad4..47dda62 100644 --- a/src/device.rs +++ b/src/device.rs @@ -196,6 +196,7 @@ impl Device { regs: [0; cop0::COP0_REGS_COUNT as usize], reg_write_masks: [0; cop0::COP0_REGS_COUNT as usize], reg_latch: 0, + pending_compare_interrupt: false, instrs: [cop0::reserved; 32], instrs2: [cop0::reserved; 32], tlb_lut_w: vec![ diff --git a/src/device/ai.rs b/src/device/ai.rs index bb62d70..db98341 100644 --- a/src/device/ai.rs +++ b/src/device/ai.rs @@ -68,7 +68,7 @@ fn do_dma(device: &mut device::Device) { device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize] + device.ai.fifo[0].duration, dma_event, ); - device::mi::schedule_rcp_interrupt(device, device::mi::MI_INTR_AI); + device::mi::set_rcp_interrupt(device, device::mi::MI_INTR_AI); } fn fifo_push(device: &mut device::Device) { diff --git a/src/device/cop0.rs b/src/device/cop0.rs index 0a4e7bb..c4c34c6 100644 --- a/src/device/cop0.rs +++ b/src/device/cop0.rs @@ -55,14 +55,14 @@ 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; -const COP0_CAUSE_IP7: u64 = 1 << 15; +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_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; @@ -97,6 +97,7 @@ pub struct Cop0 { pub tlb_lut_r: Vec, pub tlb_lut_w: Vec, pub tlb_entries: [device::tlb::TlbEntry; 32], + pub pending_compare_interrupt: bool, } fn mfc0(device: &mut device::Device, opcode: u32) { @@ -214,6 +215,7 @@ fn set_control_registers(device: &mut device::Device, index: u32, mut data: u64) compare_event, ); device.cpu.cop0.regs[COP0_CAUSE_REG as usize] &= !COP0_CAUSE_IP7; + device.cpu.cop0.pending_compare_interrupt = false; } COP0_STATUS_REG => { if data & COP0_STATUS_FR != device.cpu.cop0.regs[index as usize] & COP0_STATUS_FR { @@ -231,9 +233,7 @@ fn set_control_registers(device: &mut device::Device, index: u32, mut data: u64) } 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.cpu.cop0.pending_compare_interrupt = true; device::events::create_event( device, device::events::EventType::Compare, diff --git a/src/device/events.rs b/src/device/events.rs index ee7c283..70d4f13 100644 --- a/src/device/events.rs +++ b/src/device/events.rs @@ -8,7 +8,7 @@ pub enum EventType { PI, DP, SP, - InterruptCheck, + Interrupt, SPDma, Compare, Vru, diff --git a/src/device/exceptions.rs b/src/device/exceptions.rs index 4558b71..5ddc51e 100644 --- a/src/device/exceptions.rs +++ b/src/device/exceptions.rs @@ -1,15 +1,6 @@ use crate::device; pub fn check_pending_interrupts(device: &mut device::Device) { - 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.regs[device::cop0::COP0_STATUS_REG as usize] & (device::cop0::COP0_STATUS_IE | device::cop0::COP0_STATUS_EXL @@ -20,6 +11,34 @@ pub fn check_pending_interrupts(device: &mut device::Device) { 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; + } else if device.cpu.cop0.pending_compare_interrupt { + device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] = device::cop0::COP0_CAUSE_IP7; + device.cpu.cop0.pending_compare_interrupt = false; + } + + 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; + } + + device::events::create_event( + device, + device::events::EventType::Interrupt, + device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize], + interrupt_exception, + ); +} + +pub fn interrupt_exception(device: &mut device::Device) { exception_general(device, 0x180); } diff --git a/src/device/mi.rs b/src/device/mi.rs index 595dd12..c48b491 100644 --- a/src/device/mi.rs +++ b/src/device/mi.rs @@ -2,8 +2,8 @@ use crate::device; const MI_INIT_MODE_REG: u32 = 0; const MI_VERSION_REG: u32 = 1; -const MI_INTR_REG: u32 = 2; -const MI_INTR_MASK_REG: u32 = 3; +pub const MI_INTR_REG: u32 = 2; +pub const MI_INTR_MASK_REG: u32 = 3; pub const MI_REGS_COUNT: u32 = 4; /* read */ @@ -64,11 +64,7 @@ pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u _ => device::memory::masked_write_32(&mut device.mi.regs[reg as usize], value, mask), } - if device.mi.regs[MI_INTR_REG as usize] & device.mi.regs[MI_INTR_MASK_REG as usize] != 0 { - device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] &= - !device::cop0::COP0_CAUSE_EXCCODE_MASK; - device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] |= device::cop0::COP0_CAUSE_IP2; - } else { + if device.mi.regs[MI_INTR_REG as usize] & device.mi.regs[MI_INTR_MASK_REG as usize] == 0 { device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] &= !device::cop0::COP0_CAUSE_IP2; } @@ -149,37 +145,13 @@ pub fn clear_rcp_interrupt(device: &mut device::Device, interrupt: u32) { device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] &= !device::cop0::COP0_CAUSE_IP2; } - device::exceptions::check_pending_interrupts(device) } pub fn set_rcp_interrupt(device: &mut device::Device, interrupt: u32) { device.mi.regs[MI_INTR_REG as usize] |= interrupt; - - if device.mi.regs[MI_INTR_REG as usize] & device.mi.regs[MI_INTR_MASK_REG as usize] != 0 { - device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] &= - !device::cop0::COP0_CAUSE_EXCCODE_MASK; - device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] |= device::cop0::COP0_CAUSE_IP2; - } device::exceptions::check_pending_interrupts(device) } -pub fn schedule_rcp_interrupt(device: &mut device::Device, interrupt: u32) { - device.mi.regs[MI_INTR_REG as usize] |= interrupt; - - if device.mi.regs[MI_INTR_REG as usize] & device.mi.regs[MI_INTR_MASK_REG as usize] != 0 { - device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] &= - !device::cop0::COP0_CAUSE_EXCCODE_MASK; - device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] |= device::cop0::COP0_CAUSE_IP2; - } - - device::events::create_event( - device, - device::events::EventType::InterruptCheck, - device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize], - device::exceptions::check_pending_interrupts, - ) -} - pub fn init(device: &mut device::Device) { device.mi.regs[MI_VERSION_REG as usize] = 0x02020102 } diff --git a/src/device/rsp_interface.rs b/src/device/rsp_interface.rs index 097db3f..60d1ce8 100644 --- a/src/device/rsp_interface.rs +++ b/src/device/rsp_interface.rs @@ -361,7 +361,7 @@ fn update_sp_status(device: &mut device::Device, w: u32) { } /* 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); + device::mi::set_rcp_interrupt(device, device::mi::MI_INTR_SP); } /* clear / set single step */