diff --git a/src/device/cop0.rs b/src/device/cop0.rs index fc1e725c..cbd98a13 100644 --- a/src/device/cop0.rs +++ b/src/device/cop0.rs @@ -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, + ) } diff --git a/src/device/events.rs b/src/device/events.rs index b4769a44..7979b64e 100644 --- a/src/device/events.rs +++ b/src/device/events.rs @@ -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); diff --git a/src/device/exceptions.rs b/src/device/exceptions.rs index 62975076..534b3706 100644 --- a/src/device/exceptions.rs +++ b/src/device/exceptions.rs @@ -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;