fix some interrupt issues (#1330)

* fix some interrupt issues

* more
This commit is contained in:
Logan McNaughton authored and GitHub committed 2026-11-01 19:05:59 +00:00
1 parent 1937be0e7f
commit 85ad29d0c6
3 files changed
+41 -28

No files matched your search

+24 -15
View File
@@ -12,6 +12,7 @@ pub const COP0_BADVADDR_REG: usize = 8;
pub const COP0_COUNT_REG: usize = 9;
pub const COP0_ENTRYHI_REG: usize = 10;
const COP0_COMPARE_REG: usize = 11;
const COMPARE_WRAP_PERIOD: u64 = 1 << 32;
pub const COP0_STATUS_REG: usize = 12;
pub const COP0_CAUSE_REG: usize = 13;
pub const COP0_EPC_REG: usize = 14;
@@ -191,7 +192,7 @@ pub fn emux_xdetect(_device: &mut device::Device, _opcode: u32) {}
fn get_control_registers(device: &device::Device, index: u32) -> u64 {
match index as usize {
COP0_COUNT_REG => device.cpu.cop0.regs[index as usize] >> 1,
COP0_COUNT_REG => (device.cpu.cop0.regs[index as usize] >> 1) & 0xFFFFFFFF,
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],
@@ -206,23 +207,14 @@ fn set_control_registers(device: &mut device::Device, index: u32, mut data: u64)
data <<= 1;
device::events::translate_events(device, device.cpu.cop0.regs[COP0_COUNT_REG], data);
device.cpu.cop0.regs[COP0_COUNT_REG] = data;
let compare = device.cpu.cop0.regs[COP0_COMPARE_REG];
schedule_compare_event(device, compare);
return;
}
COP0_WIRED_REG => device.cpu.cop0.regs[COP0_RANDOM_REG] = 31,
COP0_COMPARE_REG => {
data &= 0xFFFFFFFF;
let current_count = (device.cpu.cop0.regs[COP0_COUNT_REG] >> 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,
);
schedule_compare_event(device, data);
device.cpu.cop0.regs[COP0_CAUSE_REG] &= !COP0_CAUSE_IP7;
}
COP0_STATUS_REG
@@ -240,13 +232,26 @@ fn set_control_registers(device: &mut device::Device, index: u32, mut data: u64)
device::exceptions::check_pending_interrupts(device);
}
fn schedule_compare_event(device: &mut device::Device, compare: u64) {
let current_count = device.cpu.cop0.regs[COP0_COUNT_REG] >> 1;
let mut diff = (compare as u32).wrapping_sub(current_count as u32) as u64;
if diff == 0 {
diff = COMPARE_WRAP_PERIOD;
}
device::events::create_event_at(
device,
device::events::EVENT_TYPE_COMPARE,
(current_count + diff) << 1,
);
}
pub fn compare_event(device: &mut device::Device) {
device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG] |= device::cop0::COP0_CAUSE_IP7;
device::events::create_event_at(
device,
device::events::EVENT_TYPE_COMPARE,
device.cpu.next_event_count + (u32::MAX as u64),
device.cpu.next_event_count + (COMPARE_WRAP_PERIOD << 1),
);
device::exceptions::check_pending_interrupts(device);
}
@@ -385,5 +390,9 @@ pub fn init(device: &mut device::Device) {
device.cpu.cop0.regs[COP0_BADVADDR_REG] = 0xFFFFFFFF;
device.cpu.cop0.regs[COP0_CONTEXT_REG] = 0x7FFFF0;
device::events::create_event(device, device::events::EVENT_TYPE_COMPARE, u32::MAX as u64)
device::events::create_event(
device,
device::events::EVENT_TYPE_COMPARE,
COMPARE_WRAP_PERIOD << 1,
)
}
+1 -1
View File
@@ -95,7 +95,7 @@ fn set_next_event(device: &mut device::Device) {
pub fn translate_events(device: &mut device::Device, old_count: u64, new_count: u64) {
for i in device.cpu.events.iter_mut() {
if i.enabled {
i.count = i.count - old_count + new_count;
i.count = i.count.wrapping_sub(old_count).wrapping_add(new_count);
}
}
set_next_event(device);
+16 -12
View File
@@ -32,43 +32,49 @@ pub fn check_pending_interrupts(device: &mut device::Device) {
}
}
fn set_exception_cause(device: &mut device::Device, cause: u64) {
let regs = &mut device.cpu.cop0.regs;
regs[device::cop0::COP0_CAUSE_REG] =
(regs[device::cop0::COP0_CAUSE_REG] & device::cop0::COP0_CAUSE_IP_MASK) | cause;
}
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;
set_exception_cause(device, 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;
set_exception_cause(device, 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;
set_exception_cause(device, 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;
set_exception_cause(device, 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;
set_exception_cause(device, 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;
set_exception_cause(device, device::cop0::COP0_CAUSE_EXCCODE_CPU | cop);
exception_general(device, 0x180)
}
@@ -79,9 +85,9 @@ pub fn tlb_miss_exception(
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
set_exception_cause(device, device::cop0::COP0_CAUSE_EXCCODE_TLBL);
} else {
device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG] = device::cop0::COP0_CAUSE_EXCCODE_TLBS
set_exception_cause(device, device::cop0::COP0_CAUSE_EXCCODE_TLBS);
}
device.cpu.cop0.regs[device::cop0::COP0_BADVADDR_REG] = address;
@@ -112,8 +118,7 @@ pub fn tlb_miss_exception(
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;
set_exception_cause(device, device::cop0::COP0_CAUSE_EXCCODE_MOD);
valid = false;
}
break;
@@ -121,8 +126,7 @@ pub fn tlb_miss_exception(
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;
set_exception_cause(device, device::cop0::COP0_CAUSE_EXCCODE_MOD);
valid = false;
}
break;