From 2855526561e21ff81f94fad86f362f78dff8be7a Mon Sep 17 00:00:00 2001 From: Logan McNaughton <848146+loganmc10@users.noreply.github.com> Date: Tue, 3 Nov 2026 17:50:44 +0000 Subject: [PATCH] systemtest fixes (#1339) --- src/device.rs | 15 +- src/device/cache.rs | 28 +- src/device/cart/sc64.rs | 6 +- src/device/cop0.rs | 138 +++-- src/device/cop1.rs | 808 ++++++++++++++---------------- src/device/cpu.rs | 4 +- src/device/cpu_instructions.rs | 168 +++---- src/device/exceptions.rs | 8 +- src/device/fpu_instructions.rs | 706 +++++++------------------- src/device/memory.rs | 25 +- src/device/rdp.rs | 4 +- src/device/rsp_interface.rs | 2 +- src/device/rsp_su_instructions.rs | 4 +- 13 files changed, 777 insertions(+), 1139 deletions(-) diff --git a/src/device.rs b/src/device.rs index 3635048b..21f76c4c 100644 --- a/src/device.rs +++ b/src/device.rs @@ -363,14 +363,13 @@ impl Device { cop1: cop1::Cop1 { fcr0: 0, fcr31: 0, - fgr32: [[0; 4]; 32], - fgr64: [[0; 8]; 32], - instrs: [cop1::reserved; 32], - b_instrs: [cop1::reserved; 4], - s_instrs: [cop1::reserved; 64], - d_instrs: [cop1::reserved; 64], - w_instrs: [cop1::reserved; 64], - l_instrs: [cop1::reserved; 64], + fgr: [0; 32], + instrs: [cop1::unimplemented; 32], + b_instrs: [cop1::unimplemented; 32], + s_instrs: [cop1::unimplemented; 64], + d_instrs: [cop1::unimplemented; 64], + w_instrs: [cop1::unimplemented; 64], + l_instrs: [cop1::unimplemented; 64], }, cop2: cop2::Cop2 { instrs: [cop2::reserved; 32], diff --git a/src/device/cache.rs b/src/device/cache.rs index abeadc57..68890302 100644 --- a/src/device/cache.rs +++ b/src/device/cache.rs @@ -61,8 +61,16 @@ pub fn icache_fill(device: &mut device::Device, line_index: usize, phys_address: } } -pub fn icache_fetch(device: &mut device::Device, phys_address: u64) { - let line_index = ((phys_address >> 5) & 0x1FF) as usize; +pub fn icache_line(virt_address: u64) -> usize { + ((virt_address >> 5) & 0x1FF) as usize +} + +pub fn dcache_line(virt_address: u64) -> usize { + ((virt_address >> 4) & 0x1FF) as usize +} + +pub fn icache_fetch(device: &mut device::Device, virt_address: u64, phys_address: u64) { + let line_index = icache_line(virt_address); if !icache_hit(device, line_index, phys_address) { icache_fill(device, line_index, phys_address) } @@ -81,8 +89,6 @@ pub fn dcache_hit(device: &device::Device, line_index: usize, phys_address: u64) pub fn dcache_writeback(device: &mut device::Device, line_index: usize) { device::cop0::add_cycles(device, device::rdram::rdram_calculate_cycles(16)); - device.memory.dcache[line_index].dirty = false; - let cache_address = ((device.memory.dcache[line_index].tag | (device.memory.dcache[line_index].index) as u32) & 0x1ffffffc) as u64; @@ -118,8 +124,8 @@ fn dcache_fill(device: &mut device::Device, line_index: usize, phys_address: u64 } } -pub fn dcache_read(device: &mut device::Device, phys_address: u64) -> u32 { - let line_index = ((phys_address >> 4) & 0x1FF) as usize; +pub fn dcache_read(device: &mut device::Device, virt_address: u64, phys_address: u64) -> u32 { + let line_index = dcache_line(virt_address); if !dcache_hit(device, line_index, phys_address) { if device.memory.dcache[line_index].valid && device.memory.dcache[line_index].dirty { dcache_writeback(device, line_index) @@ -131,8 +137,14 @@ pub fn dcache_read(device: &mut device::Device, phys_address: u64) -> u32 { device.memory.dcache[line_index].words[((phys_address >> 2) & 3) as usize] } -pub fn dcache_write(device: &mut device::Device, phys_address: u64, value: u32, mask: u32) { - let line_index = ((phys_address >> 4) & 0x1FF) as usize; +pub fn dcache_write( + device: &mut device::Device, + virt_address: u64, + phys_address: u64, + value: u32, + mask: u32, +) { + let line_index = dcache_line(virt_address); if !dcache_hit(device, line_index, phys_address) { if device.memory.dcache[line_index].valid && device.memory.dcache[line_index].dirty { dcache_writeback(device, line_index) diff --git a/src/device/cart/sc64.rs b/src/device/cart/sc64.rs index adff272d..f044b667 100644 --- a/src/device/cart/sc64.rs +++ b/src/device/cart/sc64.rs @@ -172,7 +172,7 @@ pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u address + i as u64, data, 0xFFFFFFFF, - false, + None, ); i += 4; } @@ -190,7 +190,7 @@ pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u device, address + i as u64, device::memory::AccessSize::Word, - false, + None, ) .to_be_bytes(); device @@ -332,7 +332,7 @@ pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u device, writeback_sectors_address + (i * 4) as u64, device::memory::AccessSize::Word, - false, + None, ); } } diff --git a/src/device/cop0.rs b/src/device/cop0.rs index 887a5fbf..749a6c19 100644 --- a/src/device/cop0.rs +++ b/src/device/cop0.rs @@ -55,6 +55,7 @@ pub const COP0_CAUSE_EXCCODE_SYS: u64 = 8 << 2; pub const COP0_CAUSE_EXCCODE_BP: u64 = 9 << 2; pub const COP0_CAUSE_EXCCODE_RI: u64 = 10 << 2; pub const COP0_CAUSE_EXCCODE_CPU: u64 = 11 << 2; +pub const COP0_CAUSE_EXCCODE_OV: u64 = 12 << 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; @@ -90,7 +91,7 @@ const COP0_WATCHHI_REG_MASK: u64 = 0b00000000000000000000000000001111; const COP0_XCONTEXT_REG_MASK: u64 = 0b1111111111111111111111111111111000000000000000000000000000000000; const COP0_PARITYERR_REG_MASK: u64 = 0b00000000000000000000000011111111; -const COP0_TAGLO_REG_MASK: u64 = 0b00001111111111111111111111000000; +const COP0_TAGLO_REG_MASK: u64 = 0xFFFFFFFF; #[derive(Clone, serde::Serialize, serde::Deserialize)] pub struct Cop0 { @@ -128,9 +129,8 @@ fn mtc0(device: &mut device::Device, opcode: u32) { device::cop0::set_control_registers( device, device::cpu_instructions::rd(opcode), - device::cpu_instructions::se32( - device.cpu.gpr[device::cpu_instructions::rt(opcode) as usize] as u32 as i32, - ), + // MTC0 transfers the full 64-bit GPR, same as DMTC0 + device.cpu.gpr[device::cpu_instructions::rt(opcode) as usize], ) } @@ -192,7 +192,8 @@ pub fn reserved(device: &mut device::Device, _opcode: u32) { device::exceptions::reserved_exception(device, 0); } -fn emux(_device: &mut device::Device, _opcode: u32) {} +// Used for EMUX and the COP0 encodings that hardware ignores +fn noop(_device: &mut device::Device, _opcode: u32) {} fn get_control_registers(device: &device::Device, index: u32) -> u64 { match index as usize { @@ -222,11 +223,6 @@ fn set_control_registers(device: &mut device::Device, index: u32, mut data: u64) schedule_compare_event(device, data); device.cpu.cop0.regs[COP0_CAUSE_REG] &= !COP0_CAUSE_IP7; } - COP0_STATUS_REG - if data & COP0_STATUS_FR != device.cpu.cop0.regs[index as usize] & COP0_STATUS_FR => - { - device::cop1::set_fgr_registers(device, data) - } _ => {} } device::memory::masked_write_64( @@ -279,13 +275,13 @@ pub fn map_instructions(device: &mut device::Device) { device.cpu.cop0.instrs = [ device::cop0::mfc0, // 0 device::cop0::dmfc0, // 1 - device::cop0::reserved, // 2 + device::cop0::noop, // 2 device::cop0::reserved, // 3 device::cop0::mtc0, // 4 device::cop0::dmtc0, // 5 - device::cop0::reserved, // 6 + device::cop0::noop, // 6 device::cop0::reserved, // 7 - device::cop0::reserved, // 8 + device::cop0::noop, // 8 device::cop0::reserved, // 9 device::cop0::reserved, // 10 device::cop0::reserved, // 11 @@ -312,70 +308,70 @@ pub fn map_instructions(device: &mut device::Device) { ]; device.cpu.cop0.instrs2 = [ - device::cop0::reserved, // 0 + device::cop0::noop, // 0 device::cop0::tlbr, // 1 device::cop0::tlbwi, // 2 - device::cop0::reserved, // 3 - device::cop0::reserved, // 4 - device::cop0::reserved, // 5 + device::cop0::noop, // 3 + device::cop0::noop, // 4 + device::cop0::noop, // 5 device::cop0::tlbwr, // 6 - device::cop0::reserved, // 7 + device::cop0::noop, // 7 device::cop0::tlbp, // 8 - device::cop0::reserved, // 9 - device::cop0::reserved, // 10 - device::cop0::reserved, // 11 - device::cop0::reserved, // 12 - device::cop0::reserved, // 13 - device::cop0::reserved, // 14 - device::cop0::reserved, // 15 + device::cop0::noop, // 9 + device::cop0::noop, // 10 + device::cop0::noop, // 11 + device::cop0::noop, // 12 + device::cop0::noop, // 13 + device::cop0::noop, // 14 + device::cop0::noop, // 15 device::cop0::reserved, // 16 - device::cop0::reserved, // 17 - device::cop0::reserved, // 18 - device::cop0::reserved, // 19 - device::cop0::reserved, // 20 - device::cop0::reserved, // 21 - device::cop0::reserved, // 22 - device::cop0::reserved, // 23 + device::cop0::noop, // 17 + device::cop0::noop, // 18 + device::cop0::noop, // 19 + device::cop0::noop, // 20 + device::cop0::noop, // 21 + device::cop0::noop, // 22 + device::cop0::noop, // 23 device::cop0::eret, // 24 - device::cop0::reserved, // 25 - device::cop0::reserved, // 26 - device::cop0::reserved, // 27 - device::cop0::reserved, // 28 - device::cop0::reserved, // 29 - device::cop0::reserved, // 30 - device::cop0::reserved, // 31 - device::cop0::emux, // 32 - device::cop0::emux, // 33 - device::cop0::emux, // 34 - device::cop0::emux, // 35 - device::cop0::emux, // 36 - device::cop0::emux, // 37 - device::cop0::emux, // 38 - device::cop0::emux, // 39 - device::cop0::emux, // 40 - device::cop0::emux, // 41 - device::cop0::emux, // 42 - device::cop0::emux, // 43 - device::cop0::emux, // 44 - device::cop0::emux, // 45 - device::cop0::emux, // 46 - device::cop0::emux, // 47 - device::cop0::emux, // 48 - device::cop0::emux, // 49 - device::cop0::emux, // 50 - device::cop0::emux, // 51 - device::cop0::emux, // 52 - device::cop0::emux, // 53 - device::cop0::emux, // 54 - device::cop0::emux, // 55 - device::cop0::emux, // 56 - device::cop0::emux, // 57 - device::cop0::emux, // 58 - device::cop0::emux, // 59 - device::cop0::emux, // 60 - device::cop0::emux, // 61 - device::cop0::emux, // 62 - device::cop0::emux, // 63 + device::cop0::noop, // 25 + device::cop0::noop, // 26 + device::cop0::noop, // 27 + device::cop0::noop, // 28 + device::cop0::noop, // 29 + device::cop0::noop, // 30 + device::cop0::noop, // 31 + device::cop0::noop, // 32 + device::cop0::noop, // 33 + device::cop0::noop, // 34 + device::cop0::noop, // 35 + device::cop0::noop, // 36 + device::cop0::noop, // 37 + device::cop0::noop, // 38 + device::cop0::noop, // 39 + device::cop0::noop, // 40 + device::cop0::noop, // 41 + device::cop0::noop, // 42 + device::cop0::noop, // 43 + device::cop0::noop, // 44 + device::cop0::noop, // 45 + device::cop0::noop, // 46 + device::cop0::noop, // 47 + device::cop0::noop, // 48 + device::cop0::noop, // 49 + device::cop0::noop, // 50 + device::cop0::noop, // 51 + device::cop0::noop, // 52 + device::cop0::noop, // 53 + device::cop0::noop, // 54 + device::cop0::noop, // 55 + device::cop0::noop, // 56 + device::cop0::noop, // 57 + device::cop0::noop, // 58 + device::cop0::noop, // 59 + device::cop0::noop, // 60 + device::cop0::noop, // 61 + device::cop0::noop, // 62 + device::cop0::noop, // 63 ]; } diff --git a/src/device/cop1.rs b/src/device/cop1.rs index 5c5d8c27..1fe4cbb2 100644 --- a/src/device/cop1.rs +++ b/src/device/cop1.rs @@ -1,20 +1,5 @@ use crate::{device, savestates}; -//const FCR31_FLAG_INEXACT_BIT: u32 = 1 << 2; -//const FCR31_FLAG_UNDERFLOW_BIT: u32 = 1 << 3; -//const FCR31_FLAG_OVERFLOW_BIT: u32 = 1 << 4; -//const FCR31_FLAG_DIVBYZERO_BIT: u32 = 1 << 5; -//const FCR31_FLAG_INVALID_BIT: u32 = 1 << 6; -//const FCR31_ENABLE_INEXACT_BIT: u32 = 1 << 7; -//const FCR31_ENABLE_UNDERFLOW_BIT: u32 = 1 << 8; -//const FCR31_ENABLE_OVERFLOW_BIT: u32 = 1 << 9; -//const FCR31_ENABLE_DIVBYZERO_BIT: u32 = 1 << 10; -//const FCR31_ENABLE_INVALID_BIT: u32 = 1 << 11; -//const FCR31_CAUSE_INEXACT_BIT: u32 = 1 << 12; -//const FCR31_CAUSE_UNDERFLOW_BIT: u32 = 1 << 13; -//const FCR31_CAUSE_OVERFLOW_BIT: u32 = 1 << 14; -//const FCR31_CAUSE_DIVBYZERO_BIT: u32 = 1 << 15; -//const FCR31_CAUSE_INVALID_BIT: u32 = 1 << 16; const FCR31_CAUSE_UNIMP_BIT: u32 = 1 << 17; pub const FCR31_CMP_BIT: u32 = 1 << 23; const FCR31_FS_BIT: u32 = 1 << 24; @@ -27,12 +12,12 @@ const FCR31_WRITE_MASK: u32 = 0b00000001100000111111111111111111; pub struct Cop1 { pub fcr0: u32, pub fcr31: u32, - pub fgr32: [[u8; 4]; 32], - pub fgr64: [[u8; 8]; 32], + // The FPU always has 32 64-bit registers, Status.FR only changes how they are indexed + pub fgr: [u64; 32], #[serde(skip, default = "savestates::default_instructions")] pub instrs: [fn(&mut device::Device, u32); 32], #[serde(skip, default = "savestates::default_instructions")] - pub b_instrs: [fn(&mut device::Device, u32); 4], + pub b_instrs: [fn(&mut device::Device, u32); 32], #[serde(skip, default = "savestates::default_instructions")] pub s_instrs: [fn(&mut device::Device, u32); 64], #[serde(skip, default = "savestates::default_instructions")] @@ -43,8 +28,12 @@ pub struct Cop1 { pub l_instrs: [fn(&mut device::Device, u32); 64], } +fn cu1_enabled(device: &device::Device) -> bool { + device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG] & device::cop0::COP0_STATUS_CU1 != 0 +} + pub fn lwc1(device: &mut device::Device, opcode: u32) { - if device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG] & device::cop0::COP0_STATUS_CU1 == 0 { + if !cu1_enabled(device) { return unusable(device, opcode); } @@ -63,18 +52,12 @@ pub fn lwc1(device: &mut device::Device, opcode: u32) { phys_address, device::memory::AccessSize::Word, cached, - ) - .to_ne_bytes(); - set_fpr_single( - device, - device::fpu_instructions::ft(opcode) as usize, - f32::from_ne_bytes(value), - false, - ) + ); + set_fpr_word(device, device::fpu_instructions::ft(opcode), value) } pub fn ldc1(device: &mut device::Device, opcode: u32) { - if device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG] & device::cop0::COP0_STATUS_CU1 == 0 { + if !cu1_enabled(device) { return unusable(device, opcode); } @@ -89,29 +72,27 @@ pub fn ldc1(device: &mut device::Device, opcode: u32) { return; } - let mut w = [2; 32]; - w[0] = device::memory::data_read( + let hi = device::memory::data_read( device, phys_address, device::memory::AccessSize::Dword, cached, ); - w[1] = device::memory::data_read( + let lo = device::memory::data_read( device, phys_address + 4, device::memory::AccessSize::Dword, cached, ); - let value = ((w[0] as u64) << 32) | (w[1]) as u64; - set_fpr_double( + set_fpr_dword( device, - device::fpu_instructions::ft(opcode) as usize, - f64::from_ne_bytes(value.to_ne_bytes()), + device::fpu_instructions::ft(opcode), + ((hi as u64) << 32) | lo as u64, ) } pub fn swc1(device: &mut device::Device, opcode: u32) { - if device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG] & device::cop0::COP0_STATUS_CU1 == 0 { + if !cu1_enabled(device) { return unusable(device, opcode); } @@ -125,18 +106,12 @@ pub fn swc1(device: &mut device::Device, opcode: u32) { if err { return; } - let value = get_fpr_single(device, device::fpu_instructions::ft(opcode) as usize).to_ne_bytes(); - device::memory::data_write( - device, - phys_address, - u32::from_ne_bytes(value), - 0xFFFFFFFF, - cached, - ); + let value = get_fpr_word(device, device::fpu_instructions::ft(opcode)); + device::memory::data_write(device, phys_address, value, 0xFFFFFFFF, cached); } pub fn sdc1(device: &mut device::Device, opcode: u32) { - if device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG] & device::cop0::COP0_STATUS_CU1 == 0 { + if !cu1_enabled(device) { return unusable(device, opcode); } @@ -151,46 +126,38 @@ pub fn sdc1(device: &mut device::Device, opcode: u32) { return; } - let value = get_fpr_double(device, device::fpu_instructions::ft(opcode) as usize).to_ne_bytes(); - + let value = get_fpr_dword(device, device::fpu_instructions::ft(opcode)); device::memory::data_write( device, phys_address, - u32::from_ne_bytes(value[4..8].try_into().unwrap()), + (value >> 32) as u32, 0xFFFFFFFF, cached, ); - device::memory::data_write( - device, - phys_address + 4, - u32::from_ne_bytes(value[0..4].try_into().unwrap()), - 0xFFFFFFFF, - cached, - ) + device::memory::data_write(device, phys_address + 4, value as u32, 0xFFFFFFFF, cached) } fn mfc1(device: &mut device::Device, opcode: u32) { - if device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG] & device::cop0::COP0_STATUS_CU1 == 0 { + if !cu1_enabled(device) { return unusable(device, opcode); } - let value = get_fpr_single(device, device::fpu_instructions::fs(opcode) as usize); + let value = get_fpr_word(device, device::fpu_instructions::fs(opcode)); device.cpu.gpr[device::cpu_instructions::rt(opcode) as usize] = - device::cpu_instructions::se32(u32::from_ne_bytes(value.to_ne_bytes()) as i32); + device::cpu_instructions::se32(value as i32); } fn dmfc1(device: &mut device::Device, opcode: u32) { - if device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG] & device::cop0::COP0_STATUS_CU1 == 0 { + if !cu1_enabled(device) { return unusable(device, opcode); } - let value = get_fpr_double(device, device::fpu_instructions::fs(opcode) as usize); device.cpu.gpr[device::cpu_instructions::rt(opcode) as usize] = - u64::from_ne_bytes(value.to_ne_bytes()); + get_fpr_dword(device, device::fpu_instructions::fs(opcode)); } fn cfc1(device: &mut device::Device, opcode: u32) { - if device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG] & device::cop0::COP0_STATUS_CU1 == 0 { + if !cu1_enabled(device) { return unusable(device, opcode); } @@ -199,45 +166,26 @@ fn cfc1(device: &mut device::Device, opcode: u32) { ) } -fn dcfc1(device: &mut device::Device, opcode: u32) { - if device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG] & device::cop0::COP0_STATUS_CU1 == 0 { - return unusable(device, opcode); - } - - device.cpu.cop1.fcr31 &= !FCR31_CAUSE_MASK; - device.cpu.cop1.fcr31 |= FCR31_CAUSE_UNIMP_BIT; - device::exceptions::floating_point_exception(device) -} - fn mtc1(device: &mut device::Device, opcode: u32) { - if device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG] & device::cop0::COP0_STATUS_CU1 == 0 { + if !cu1_enabled(device) { return unusable(device, opcode); } - let value = f32::from_ne_bytes( - (device.cpu.gpr[device::cpu_instructions::rt(opcode) as usize] as u32).to_ne_bytes(), - ); - set_fpr_single( - device, - device::fpu_instructions::fs(opcode) as usize, - value, - false, - ) + let value = device.cpu.gpr[device::cpu_instructions::rt(opcode) as usize] as u32; + set_fpr_word(device, device::fpu_instructions::fs(opcode), value) } fn dmtc1(device: &mut device::Device, opcode: u32) { - if device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG] & device::cop0::COP0_STATUS_CU1 == 0 { + if !cu1_enabled(device) { return unusable(device, opcode); } - let value = f64::from_ne_bytes( - device.cpu.gpr[device::cpu_instructions::rt(opcode) as usize].to_ne_bytes(), - ); - set_fpr_double(device, device::fpu_instructions::fs(opcode) as usize, value) + let value = device.cpu.gpr[device::cpu_instructions::rt(opcode) as usize]; + set_fpr_dword(device, device::fpu_instructions::fs(opcode), value) } fn ctc1(device: &mut device::Device, opcode: u32) { - if device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG] & device::cop0::COP0_STATUS_CU1 == 0 { + if !cu1_enabled(device) { return unusable(device, opcode); } @@ -248,26 +196,16 @@ fn ctc1(device: &mut device::Device, opcode: u32) { ) } -fn dctc1(device: &mut device::Device, opcode: u32) { - if device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG] & device::cop0::COP0_STATUS_CU1 == 0 { - return unusable(device, opcode); - } - - device.cpu.cop1.fcr31 &= !FCR31_CAUSE_MASK; - device.cpu.cop1.fcr31 |= FCR31_CAUSE_UNIMP_BIT; - device::exceptions::floating_point_exception(device) -} - fn execute_cop1_b(device: &mut device::Device, opcode: u32) { - if device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG] & device::cop0::COP0_STATUS_CU1 == 0 { + if !cu1_enabled(device) { return unusable(device, opcode); } - device.cpu.cop1.b_instrs[((opcode >> 16) & 0x3) as usize](device, opcode) + device.cpu.cop1.b_instrs[((opcode >> 16) & 0x1F) as usize](device, opcode) } fn execute_cop1_s(device: &mut device::Device, opcode: u32) { - if device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG] & device::cop0::COP0_STATUS_CU1 == 0 { + if !cu1_enabled(device) { return unusable(device, opcode); } @@ -275,7 +213,7 @@ fn execute_cop1_s(device: &mut device::Device, opcode: u32) { } fn execute_cop1_d(device: &mut device::Device, opcode: u32) { - if device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG] & device::cop0::COP0_STATUS_CU1 == 0 { + if !cu1_enabled(device) { return unusable(device, opcode); } @@ -283,7 +221,7 @@ fn execute_cop1_d(device: &mut device::Device, opcode: u32) { } fn execute_cop1_l(device: &mut device::Device, opcode: u32) { - if device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG] & device::cop0::COP0_STATUS_CU1 == 0 { + if !cu1_enabled(device) { return unusable(device, opcode); } @@ -291,7 +229,7 @@ fn execute_cop1_l(device: &mut device::Device, opcode: u32) { } fn execute_cop1_w(device: &mut device::Device, opcode: u32) { - if device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG] & device::cop0::COP0_STATUS_CU1 == 0 { + if !cu1_enabled(device) { return unusable(device, opcode); } @@ -302,12 +240,15 @@ fn unusable(device: &mut device::Device, _opcode: u32) { device::exceptions::cop_unusable_exception(device, device::cop0::COP0_CAUSE_CE1) } -pub fn reserved(device: &mut device::Device, opcode: u32) { - if device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG] & device::cop0::COP0_STATUS_CU1 == 0 { +// Reserved COP1 encodings (and DCFC1/DCTC1) raise an Unimplemented Operation exception +pub fn unimplemented(device: &mut device::Device, opcode: u32) { + if !cu1_enabled(device) { return unusable(device, opcode); } - device::exceptions::reserved_exception(device, device::cop0::COP0_CAUSE_CE1); + device.cpu.cop1.fcr31 &= !FCR31_CAUSE_MASK; + device.cpu.cop1.fcr31 |= FCR31_CAUSE_UNIMP_BIT; + device::exceptions::floating_point_exception(device) } fn get_control_registers_fpu(device: &device::Device, index: u32) -> u32 { @@ -387,47 +328,48 @@ pub fn set_host_flush_mode(device: &device::Device) { } } -pub fn set_fpr_single(device: &mut device::Device, index: usize, value: f32, clear_high: bool) { - let bytes = value.to_ne_bytes(); +// In half mode (Status.FR = 0) arithmetic operations ignore the lowest bit of fs, while ft and +// fd use the full index +#[inline(always)] +pub fn fs_index(device: &device::Device, index: u32) -> usize { if device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG] & device::cop0::COP0_STATUS_FR == 0 { - device.cpu.cop1.fgr32[index] = bytes; + (index & !1) as usize } else { - let bytes_lo = bytes; - let bytes_hi: [u8; 4] = if clear_high { - [0, 0, 0, 0] - } else { - device.cpu.cop1.fgr64[index][4..8].try_into().unwrap() - }; - device.cpu.cop1.fgr64[index] = [bytes_lo, bytes_hi].concat().try_into().unwrap(); + index as usize } } -pub fn get_fpr_single(device: &device::Device, index: usize) -> f32 { +// 32 bit transfers (MTC1, MFC1, LWC1, SWC1): in half mode odd indices access the upper half of +// the even register +pub fn get_fpr_word(device: &device::Device, index: u32) -> u32 { if device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG] & device::cop0::COP0_STATUS_FR == 0 { - f32::from_ne_bytes(device.cpu.cop1.fgr32[index]) + (device.cpu.cop1.fgr[(index & !1) as usize] >> ((index & 1) * 32)) as u32 } else { - f32::from_ne_bytes(device.cpu.cop1.fgr64[index][0..4].try_into().unwrap()) + device.cpu.cop1.fgr[index as usize] as u32 } } -pub fn set_fpr_double(device: &mut device::Device, index: usize, value: f64) { - let bytes = value.to_ne_bytes(); - if device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG] & device::cop0::COP0_STATUS_FR == 0 { - device.cpu.cop1.fgr32[index & !1] = bytes[0..4].try_into().unwrap(); - device.cpu.cop1.fgr32[(index & !1) + 1] = bytes[4..8].try_into().unwrap(); +pub fn set_fpr_word(device: &mut device::Device, index: u32, value: u32) { + let (register, shift) = if device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG] + & device::cop0::COP0_STATUS_FR + == 0 + { + ((index & !1) as usize, (index & 1) * 32) } else { - device.cpu.cop1.fgr64[index] = bytes; - } + (index as usize, 0) + }; + let fgr = &mut device.cpu.cop1.fgr[register]; + *fgr = (*fgr & !(0xFFFFFFFF << shift)) | ((value as u64) << shift); } -pub fn get_fpr_double(device: &device::Device, index: usize) -> f64 { - if device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG] & device::cop0::COP0_STATUS_FR == 0 { - let bytes_lo = device.cpu.cop1.fgr32[index & !1]; - let bytes_hi = device.cpu.cop1.fgr32[(index & !1) + 1]; - f64::from_ne_bytes([bytes_lo, bytes_hi].concat().try_into().unwrap()) - } else { - f64::from_ne_bytes(device.cpu.cop1.fgr64[index]) - } +// 64 bit transfers (DMTC1, DMFC1, LDC1, SDC1) +pub fn get_fpr_dword(device: &device::Device, index: u32) -> u64 { + device.cpu.cop1.fgr[fs_index(device, index)] +} + +pub fn set_fpr_dword(device: &mut device::Device, index: u32, value: u64) { + let index = fs_index(device, index); + device.cpu.cop1.fgr[index] = value } pub fn map_instructions(device: &mut device::Device) { @@ -436,337 +378,343 @@ pub fn map_instructions(device: &mut device::Device) { device::fpu_instructions::bc1t, // 1 device::fpu_instructions::bc1fl, // 2 device::fpu_instructions::bc1tl, // 3 + device::cop1::unimplemented, // 4 + device::cop1::unimplemented, // 5 + device::cop1::unimplemented, // 6 + device::cop1::unimplemented, // 7 + device::cop1::unimplemented, // 8 + device::cop1::unimplemented, // 9 + device::cop1::unimplemented, // 10 + device::cop1::unimplemented, // 11 + device::cop1::unimplemented, // 12 + device::cop1::unimplemented, // 13 + device::cop1::unimplemented, // 14 + device::cop1::unimplemented, // 15 + device::cop1::unimplemented, // 16 + device::cop1::unimplemented, // 17 + device::cop1::unimplemented, // 18 + device::cop1::unimplemented, // 19 + device::cop1::unimplemented, // 20 + device::cop1::unimplemented, // 21 + device::cop1::unimplemented, // 22 + device::cop1::unimplemented, // 23 + device::cop1::unimplemented, // 24 + device::cop1::unimplemented, // 25 + device::cop1::unimplemented, // 26 + device::cop1::unimplemented, // 27 + device::cop1::unimplemented, // 28 + device::cop1::unimplemented, // 29 + device::cop1::unimplemented, // 30 + device::cop1::unimplemented, // 31 ]; device.cpu.cop1.s_instrs = [ - device::fpu_instructions::add_s, // 0 - device::fpu_instructions::sub_s, // 1 - device::fpu_instructions::mul_s, // 2 - device::fpu_instructions::div_s, // 3 - device::fpu_instructions::sqrt_s, // 4 - device::fpu_instructions::abs_s, // 5 - device::fpu_instructions::mov_s, // 6 - device::fpu_instructions::neg_s, // 7 - device::fpu_instructions::round_l_s, // 8 - device::fpu_instructions::trunc_l_s, // 9 - device::fpu_instructions::ceil_l_s, // 10 - device::fpu_instructions::floor_l_s, // 11 - device::fpu_instructions::round_w_s, // 12 - device::fpu_instructions::trunc_w_s, // 13 - device::fpu_instructions::ceil_w_s, // 14 - device::fpu_instructions::floor_w_s, // 15 - device::cop1::reserved, // 16 - device::cop1::reserved, // 17 - device::cop1::reserved, // 18 - device::cop1::reserved, // 19 - device::cop1::reserved, // 20 - device::cop1::reserved, // 21 - device::cop1::reserved, // 22 - device::cop1::reserved, // 23 - device::cop1::reserved, // 24 - device::cop1::reserved, // 25 - device::cop1::reserved, // 26 - device::cop1::reserved, // 27 - device::cop1::reserved, // 28 - device::cop1::reserved, // 29 - device::cop1::reserved, // 30 - device::cop1::reserved, // 31 - device::cop1::reserved, // 32 - device::fpu_instructions::cvt_d_s, // 33 - device::cop1::reserved, // 34 - device::cop1::reserved, // 35 - device::fpu_instructions::cvt_w_s, // 36 - device::fpu_instructions::cvt_l_s, // 37 - device::cop1::reserved, // 38 - device::cop1::reserved, // 38 - device::cop1::reserved, // 40 - device::cop1::reserved, // 41 - device::cop1::reserved, // 42 - device::cop1::reserved, // 43 - device::cop1::reserved, // 44 - device::cop1::reserved, // 45 - device::cop1::reserved, // 46 - device::cop1::reserved, // 47 - device::fpu_instructions::c_f_s, // 48 - device::fpu_instructions::c_un_s, // 49 - device::fpu_instructions::c_eq_s, // 50 - device::fpu_instructions::c_ueq_s, // 51 - device::fpu_instructions::c_olt_s, // 52 - device::fpu_instructions::c_ult_s, // 53 - device::fpu_instructions::c_ole_s, // 54 - device::fpu_instructions::c_ule_s, // 55 - device::fpu_instructions::c_sf_s, // 56 - device::fpu_instructions::c_ngle_s, // 57 - device::fpu_instructions::c_seq_s, // 58 - device::fpu_instructions::c_ngl_s, // 59 - device::fpu_instructions::c_lt_s, // 60 - device::fpu_instructions::c_nge_s, // 61 - device::fpu_instructions::c_le_s, // 62 - device::fpu_instructions::c_ngt_s, // 63 + device::fpu_instructions::add_s, // 0 + device::fpu_instructions::sub_s, // 1 + device::fpu_instructions::mul_s, // 2 + device::fpu_instructions::div_s, // 3 + device::fpu_instructions::sqrt_s, // 4 + device::fpu_instructions::abs_s, // 5 + device::fpu_instructions::mov_s, // 6 + device::fpu_instructions::neg_s, // 7 + device::fpu_instructions::round_l_s, // 8 + device::fpu_instructions::trunc_l_s, // 9 + device::fpu_instructions::ceil_l_s, // 10 + device::fpu_instructions::floor_l_s, // 11 + device::fpu_instructions::round_w_s, // 12 + device::fpu_instructions::trunc_w_s, // 13 + device::fpu_instructions::ceil_w_s, // 14 + device::fpu_instructions::floor_w_s, // 15 + device::cop1::unimplemented, // 16 + device::cop1::unimplemented, // 17 + device::cop1::unimplemented, // 18 + device::cop1::unimplemented, // 19 + device::cop1::unimplemented, // 20 + device::cop1::unimplemented, // 21 + device::cop1::unimplemented, // 22 + device::cop1::unimplemented, // 23 + device::cop1::unimplemented, // 24 + device::cop1::unimplemented, // 25 + device::cop1::unimplemented, // 26 + device::cop1::unimplemented, // 27 + device::cop1::unimplemented, // 28 + device::cop1::unimplemented, // 29 + device::cop1::unimplemented, // 30 + device::cop1::unimplemented, // 31 + device::cop1::unimplemented, // 32 + device::fpu_instructions::cvt_d_s, // 33 + device::cop1::unimplemented, // 34 + device::cop1::unimplemented, // 35 + device::fpu_instructions::cvt_w_s, // 36 + device::fpu_instructions::cvt_l_s, // 37 + device::cop1::unimplemented, // 38 + device::cop1::unimplemented, // 38 + device::cop1::unimplemented, // 40 + device::cop1::unimplemented, // 41 + device::cop1::unimplemented, // 42 + device::cop1::unimplemented, // 43 + device::cop1::unimplemented, // 44 + device::cop1::unimplemented, // 45 + device::cop1::unimplemented, // 46 + device::cop1::unimplemented, // 47 + device::fpu_instructions::c_cond::, // 48 + device::fpu_instructions::c_cond::, // 49 + device::fpu_instructions::c_cond::, // 50 + device::fpu_instructions::c_cond::, // 51 + device::fpu_instructions::c_cond::, // 52 + device::fpu_instructions::c_cond::, // 53 + device::fpu_instructions::c_cond::, // 54 + device::fpu_instructions::c_cond::, // 55 + device::fpu_instructions::c_cond::, // 56 + device::fpu_instructions::c_cond::, // 57 + device::fpu_instructions::c_cond::, // 58 + device::fpu_instructions::c_cond::, // 59 + device::fpu_instructions::c_cond::, // 60 + device::fpu_instructions::c_cond::, // 61 + device::fpu_instructions::c_cond::, // 62 + device::fpu_instructions::c_cond::, // 63 ]; device.cpu.cop1.d_instrs = [ - device::fpu_instructions::add_d, // 0 - device::fpu_instructions::sub_d, // 1 - device::fpu_instructions::mul_d, // 2 - device::fpu_instructions::div_d, // 3 - device::fpu_instructions::sqrt_d, // 4 - device::fpu_instructions::abs_d, // 5 - device::fpu_instructions::mov_d, // 6 - device::fpu_instructions::neg_d, // 7 - device::fpu_instructions::round_l_d, // 8 - device::fpu_instructions::trunc_l_d, // 9 - device::fpu_instructions::ceil_l_d, // 10 - device::fpu_instructions::floor_l_d, // 11 - device::fpu_instructions::round_w_d, // 12 - device::fpu_instructions::trunc_w_d, // 13 - device::fpu_instructions::ceil_w_d, // 14 - device::fpu_instructions::floor_w_d, // 15 - device::cop1::reserved, // 16 - device::cop1::reserved, // 17 - device::cop1::reserved, // 18 - device::cop1::reserved, // 19 - device::cop1::reserved, // 20 - device::cop1::reserved, // 21 - device::cop1::reserved, // 22 - device::cop1::reserved, // 23 - device::cop1::reserved, // 24 - device::cop1::reserved, // 25 - device::cop1::reserved, // 26 - device::cop1::reserved, // 27 - device::cop1::reserved, // 28 - device::cop1::reserved, // 29 - device::cop1::reserved, // 30 - device::cop1::reserved, // 31 - device::fpu_instructions::cvt_s_d, // 32 - device::cop1::reserved, // 33 - device::cop1::reserved, // 34 - device::cop1::reserved, // 35 - device::fpu_instructions::cvt_w_d, // 36 - device::fpu_instructions::cvt_l_d, // 37 - device::cop1::reserved, // 38 - device::cop1::reserved, // 38 - device::cop1::reserved, // 40 - device::cop1::reserved, // 41 - device::cop1::reserved, // 42 - device::cop1::reserved, // 43 - device::cop1::reserved, // 44 - device::cop1::reserved, // 45 - device::cop1::reserved, // 46 - device::cop1::reserved, // 47 - device::fpu_instructions::c_f_d, // 48 - device::fpu_instructions::c_un_d, // 49 - device::fpu_instructions::c_eq_d, // 50 - device::fpu_instructions::c_ueq_d, // 51 - device::fpu_instructions::c_olt_d, // 52 - device::fpu_instructions::c_ult_d, // 53 - device::fpu_instructions::c_ole_d, // 54 - device::fpu_instructions::c_ule_d, // 55 - device::fpu_instructions::c_sf_d, // 56 - device::fpu_instructions::c_ngle_d, // 57 - device::fpu_instructions::c_seq_d, // 58 - device::fpu_instructions::c_ngl_d, // 59 - device::fpu_instructions::c_lt_d, // 60 - device::fpu_instructions::c_nge_d, // 61 - device::fpu_instructions::c_le_d, // 62 - device::fpu_instructions::c_ngt_d, // 63 + device::fpu_instructions::add_d, // 0 + device::fpu_instructions::sub_d, // 1 + device::fpu_instructions::mul_d, // 2 + device::fpu_instructions::div_d, // 3 + device::fpu_instructions::sqrt_d, // 4 + device::fpu_instructions::abs_d, // 5 + device::fpu_instructions::mov_d, // 6 + device::fpu_instructions::neg_d, // 7 + device::fpu_instructions::round_l_d, // 8 + device::fpu_instructions::trunc_l_d, // 9 + device::fpu_instructions::ceil_l_d, // 10 + device::fpu_instructions::floor_l_d, // 11 + device::fpu_instructions::round_w_d, // 12 + device::fpu_instructions::trunc_w_d, // 13 + device::fpu_instructions::ceil_w_d, // 14 + device::fpu_instructions::floor_w_d, // 15 + device::cop1::unimplemented, // 16 + device::cop1::unimplemented, // 17 + device::cop1::unimplemented, // 18 + device::cop1::unimplemented, // 19 + device::cop1::unimplemented, // 20 + device::cop1::unimplemented, // 21 + device::cop1::unimplemented, // 22 + device::cop1::unimplemented, // 23 + device::cop1::unimplemented, // 24 + device::cop1::unimplemented, // 25 + device::cop1::unimplemented, // 26 + device::cop1::unimplemented, // 27 + device::cop1::unimplemented, // 28 + device::cop1::unimplemented, // 29 + device::cop1::unimplemented, // 30 + device::cop1::unimplemented, // 31 + device::fpu_instructions::cvt_s_d, // 32 + device::cop1::unimplemented, // 33 + device::cop1::unimplemented, // 34 + device::cop1::unimplemented, // 35 + device::fpu_instructions::cvt_w_d, // 36 + device::fpu_instructions::cvt_l_d, // 37 + device::cop1::unimplemented, // 38 + device::cop1::unimplemented, // 38 + device::cop1::unimplemented, // 40 + device::cop1::unimplemented, // 41 + device::cop1::unimplemented, // 42 + device::cop1::unimplemented, // 43 + device::cop1::unimplemented, // 44 + device::cop1::unimplemented, // 45 + device::cop1::unimplemented, // 46 + device::cop1::unimplemented, // 47 + device::fpu_instructions::c_cond::, // 48 + device::fpu_instructions::c_cond::, // 49 + device::fpu_instructions::c_cond::, // 50 + device::fpu_instructions::c_cond::, // 51 + device::fpu_instructions::c_cond::, // 52 + device::fpu_instructions::c_cond::, // 53 + device::fpu_instructions::c_cond::, // 54 + device::fpu_instructions::c_cond::, // 55 + device::fpu_instructions::c_cond::, // 56 + device::fpu_instructions::c_cond::, // 57 + device::fpu_instructions::c_cond::, // 58 + device::fpu_instructions::c_cond::, // 59 + device::fpu_instructions::c_cond::, // 60 + device::fpu_instructions::c_cond::, // 61 + device::fpu_instructions::c_cond::, // 62 + device::fpu_instructions::c_cond::, // 63 ]; device.cpu.cop1.l_instrs = [ - device::cop1::reserved, // 0 - device::cop1::reserved, // 1 - device::cop1::reserved, // 2 - device::cop1::reserved, // 3 - device::cop1::reserved, // 4 - device::cop1::reserved, // 5 - device::cop1::reserved, // 6 - device::cop1::reserved, // 7 - device::cop1::reserved, // 8 - device::cop1::reserved, // 9 - device::cop1::reserved, // 10 - device::cop1::reserved, // 11 - device::cop1::reserved, // 12 - device::cop1::reserved, // 13 - device::cop1::reserved, // 14 - device::cop1::reserved, // 15 - device::cop1::reserved, // 16 - device::cop1::reserved, // 17 - device::cop1::reserved, // 18 - device::cop1::reserved, // 19 - device::cop1::reserved, // 20 - device::cop1::reserved, // 21 - device::cop1::reserved, // 22 - device::cop1::reserved, // 23 - device::cop1::reserved, // 24 - device::cop1::reserved, // 25 - device::cop1::reserved, // 26 - device::cop1::reserved, // 27 - device::cop1::reserved, // 28 - device::cop1::reserved, // 29 - device::cop1::reserved, // 30 - device::cop1::reserved, // 31 + device::cop1::unimplemented, // 0 + device::cop1::unimplemented, // 1 + device::cop1::unimplemented, // 2 + device::cop1::unimplemented, // 3 + device::cop1::unimplemented, // 4 + device::cop1::unimplemented, // 5 + device::cop1::unimplemented, // 6 + device::cop1::unimplemented, // 7 + device::cop1::unimplemented, // 8 + device::cop1::unimplemented, // 9 + device::cop1::unimplemented, // 10 + device::cop1::unimplemented, // 11 + device::cop1::unimplemented, // 12 + device::cop1::unimplemented, // 13 + device::cop1::unimplemented, // 14 + device::cop1::unimplemented, // 15 + device::cop1::unimplemented, // 16 + device::cop1::unimplemented, // 17 + device::cop1::unimplemented, // 18 + device::cop1::unimplemented, // 19 + device::cop1::unimplemented, // 20 + device::cop1::unimplemented, // 21 + device::cop1::unimplemented, // 22 + device::cop1::unimplemented, // 23 + device::cop1::unimplemented, // 24 + device::cop1::unimplemented, // 25 + device::cop1::unimplemented, // 26 + device::cop1::unimplemented, // 27 + device::cop1::unimplemented, // 28 + device::cop1::unimplemented, // 29 + device::cop1::unimplemented, // 30 + device::cop1::unimplemented, // 31 device::fpu_instructions::cvt_s_l, // 32 device::fpu_instructions::cvt_d_l, // 33 - device::cop1::reserved, // 34 - device::cop1::reserved, // 35 - device::cop1::reserved, // 36 - device::cop1::reserved, // 37 - device::cop1::reserved, // 38 - device::cop1::reserved, // 39 - device::cop1::reserved, // 40 - device::cop1::reserved, // 41 - device::cop1::reserved, // 42 - device::cop1::reserved, // 43 - device::cop1::reserved, // 44 - device::cop1::reserved, // 45 - device::cop1::reserved, // 46 - device::cop1::reserved, // 47 - device::cop1::reserved, // 48 - device::cop1::reserved, // 49 - device::cop1::reserved, // 50 - device::cop1::reserved, // 51 - device::cop1::reserved, // 52 - device::cop1::reserved, // 53 - device::cop1::reserved, // 54 - device::cop1::reserved, // 55 - device::cop1::reserved, // 56 - device::cop1::reserved, // 57 - device::cop1::reserved, // 58 - device::cop1::reserved, // 59 - device::cop1::reserved, // 60 - device::cop1::reserved, // 61 - device::cop1::reserved, // 62 - device::cop1::reserved, // 63 + device::cop1::unimplemented, // 34 + device::cop1::unimplemented, // 35 + device::cop1::unimplemented, // 36 + device::cop1::unimplemented, // 37 + device::cop1::unimplemented, // 38 + device::cop1::unimplemented, // 39 + device::cop1::unimplemented, // 40 + device::cop1::unimplemented, // 41 + device::cop1::unimplemented, // 42 + device::cop1::unimplemented, // 43 + device::cop1::unimplemented, // 44 + device::cop1::unimplemented, // 45 + device::cop1::unimplemented, // 46 + device::cop1::unimplemented, // 47 + device::cop1::unimplemented, // 48 + device::cop1::unimplemented, // 49 + device::cop1::unimplemented, // 50 + device::cop1::unimplemented, // 51 + device::cop1::unimplemented, // 52 + device::cop1::unimplemented, // 53 + device::cop1::unimplemented, // 54 + device::cop1::unimplemented, // 55 + device::cop1::unimplemented, // 56 + device::cop1::unimplemented, // 57 + device::cop1::unimplemented, // 58 + device::cop1::unimplemented, // 59 + device::cop1::unimplemented, // 60 + device::cop1::unimplemented, // 61 + device::cop1::unimplemented, // 62 + device::cop1::unimplemented, // 63 ]; device.cpu.cop1.w_instrs = [ - device::cop1::reserved, // 0 - device::cop1::reserved, // 1 - device::cop1::reserved, // 2 - device::cop1::reserved, // 3 - device::cop1::reserved, // 4 - device::cop1::reserved, // 5 - device::cop1::reserved, // 6 - device::cop1::reserved, // 7 - device::cop1::reserved, // 8 - device::cop1::reserved, // 9 - device::cop1::reserved, // 10 - device::cop1::reserved, // 11 - device::cop1::reserved, // 12 - device::cop1::reserved, // 13 - device::cop1::reserved, // 14 - device::cop1::reserved, // 15 - device::cop1::reserved, // 16 - device::cop1::reserved, // 17 - device::cop1::reserved, // 18 - device::cop1::reserved, // 19 - device::cop1::reserved, // 20 - device::cop1::reserved, // 21 - device::cop1::reserved, // 22 - device::cop1::reserved, // 23 - device::cop1::reserved, // 24 - device::cop1::reserved, // 25 - device::cop1::reserved, // 26 - device::cop1::reserved, // 27 - device::cop1::reserved, // 28 - device::cop1::reserved, // 29 - device::cop1::reserved, // 30 - device::cop1::reserved, // 31 + device::cop1::unimplemented, // 0 + device::cop1::unimplemented, // 1 + device::cop1::unimplemented, // 2 + device::cop1::unimplemented, // 3 + device::cop1::unimplemented, // 4 + device::cop1::unimplemented, // 5 + device::cop1::unimplemented, // 6 + device::cop1::unimplemented, // 7 + device::cop1::unimplemented, // 8 + device::cop1::unimplemented, // 9 + device::cop1::unimplemented, // 10 + device::cop1::unimplemented, // 11 + device::cop1::unimplemented, // 12 + device::cop1::unimplemented, // 13 + device::cop1::unimplemented, // 14 + device::cop1::unimplemented, // 15 + device::cop1::unimplemented, // 16 + device::cop1::unimplemented, // 17 + device::cop1::unimplemented, // 18 + device::cop1::unimplemented, // 19 + device::cop1::unimplemented, // 20 + device::cop1::unimplemented, // 21 + device::cop1::unimplemented, // 22 + device::cop1::unimplemented, // 23 + device::cop1::unimplemented, // 24 + device::cop1::unimplemented, // 25 + device::cop1::unimplemented, // 26 + device::cop1::unimplemented, // 27 + device::cop1::unimplemented, // 28 + device::cop1::unimplemented, // 29 + device::cop1::unimplemented, // 30 + device::cop1::unimplemented, // 31 device::fpu_instructions::cvt_s_w, // 32 device::fpu_instructions::cvt_d_w, // 33 - device::cop1::reserved, // 34 - device::cop1::reserved, // 35 - device::cop1::reserved, // 36 - device::cop1::reserved, // 37 - device::cop1::reserved, // 38 - device::cop1::reserved, // 39 - device::cop1::reserved, // 40 - device::cop1::reserved, // 41 - device::cop1::reserved, // 42 - device::cop1::reserved, // 43 - device::cop1::reserved, // 44 - device::cop1::reserved, // 45 - device::cop1::reserved, // 46 - device::cop1::reserved, // 47 - device::cop1::reserved, // 48 - device::cop1::reserved, // 49 - device::cop1::reserved, // 50 - device::cop1::reserved, // 51 - device::cop1::reserved, // 52 - device::cop1::reserved, // 53 - device::cop1::reserved, // 54 - device::cop1::reserved, // 55 - device::cop1::reserved, // 56 - device::cop1::reserved, // 57 - device::cop1::reserved, // 58 - device::cop1::reserved, // 59 - device::cop1::reserved, // 60 - device::cop1::reserved, // 61 - device::cop1::reserved, // 62 - device::cop1::reserved, // 63 + device::cop1::unimplemented, // 34 + device::cop1::unimplemented, // 35 + device::cop1::unimplemented, // 36 + device::cop1::unimplemented, // 37 + device::cop1::unimplemented, // 38 + device::cop1::unimplemented, // 39 + device::cop1::unimplemented, // 40 + device::cop1::unimplemented, // 41 + device::cop1::unimplemented, // 42 + device::cop1::unimplemented, // 43 + device::cop1::unimplemented, // 44 + device::cop1::unimplemented, // 45 + device::cop1::unimplemented, // 46 + device::cop1::unimplemented, // 47 + device::cop1::unimplemented, // 48 + device::cop1::unimplemented, // 49 + device::cop1::unimplemented, // 50 + device::cop1::unimplemented, // 51 + device::cop1::unimplemented, // 52 + device::cop1::unimplemented, // 53 + device::cop1::unimplemented, // 54 + device::cop1::unimplemented, // 55 + device::cop1::unimplemented, // 56 + device::cop1::unimplemented, // 57 + device::cop1::unimplemented, // 58 + device::cop1::unimplemented, // 59 + device::cop1::unimplemented, // 60 + device::cop1::unimplemented, // 61 + device::cop1::unimplemented, // 62 + device::cop1::unimplemented, // 63 ]; device.cpu.cop1.instrs = [ device::cop1::mfc1, // 0 device::cop1::dmfc1, // 1 device::cop1::cfc1, // 2 - device::cop1::dcfc1, // 3 + device::cop1::unimplemented, // 3 device::cop1::mtc1, // 4 device::cop1::dmtc1, // 5 device::cop1::ctc1, // 6 - device::cop1::dctc1, // 7 + device::cop1::unimplemented, // 7 device::cop1::execute_cop1_b, // 8 - device::cop1::reserved, // 9 - device::cop1::reserved, // 10 - device::cop1::reserved, // 11 - device::cop1::reserved, // 12 - device::cop1::reserved, // 13 - device::cop1::reserved, // 14 - device::cop1::reserved, // 15 + device::cop1::unimplemented, // 9 + device::cop1::unimplemented, // 10 + device::cop1::unimplemented, // 11 + device::cop1::unimplemented, // 12 + device::cop1::unimplemented, // 13 + device::cop1::unimplemented, // 14 + device::cop1::unimplemented, // 15 device::cop1::execute_cop1_s, // 16 device::cop1::execute_cop1_d, // 17 - device::cop1::reserved, // 18 - device::cop1::reserved, // 19 + device::cop1::unimplemented, // 18 + device::cop1::unimplemented, // 19 device::cop1::execute_cop1_w, // 20 device::cop1::execute_cop1_l, // 21 - device::cop1::reserved, // 22 - device::cop1::reserved, // 23 - device::cop1::reserved, // 24 - device::cop1::reserved, // 25 - device::cop1::reserved, // 26 - device::cop1::reserved, // 27 - device::cop1::reserved, // 28 - device::cop1::reserved, // 29 - device::cop1::reserved, // 30 - device::cop1::reserved, // 31 + device::cop1::unimplemented, // 22 + device::cop1::unimplemented, // 23 + device::cop1::unimplemented, // 24 + device::cop1::unimplemented, // 25 + device::cop1::unimplemented, // 26 + device::cop1::unimplemented, // 27 + device::cop1::unimplemented, // 28 + device::cop1::unimplemented, // 29 + device::cop1::unimplemented, // 30 + device::cop1::unimplemented, // 31 ] } pub fn init(device: &mut device::Device) { - set_fgr_registers(device, 0); device.cpu.cop1.fcr0 = 0b101000000000; map_instructions(device); set_host_flush_mode(device); } - -pub fn set_fgr_registers(device: &mut device::Device, status_reg: u64) { - // this method doesn't account for undocumented behaviour (accessing odd numbered registers in half mode) - if (status_reg & device::cop0::COP0_STATUS_FR) == 0 { - let mut i = 0; - while i < 32 { - let bytes = device.cpu.cop1.fgr64[i]; - device.cpu.cop1.fgr32[i] = bytes[0..4].try_into().unwrap(); - device.cpu.cop1.fgr32[i + 1] = bytes[4..8].try_into().unwrap(); - i += 2; - } - } else { - let mut i = 0; - while i < 32 { - let bytes_lo = device.cpu.cop1.fgr32[i]; - let bytes_hi = device.cpu.cop1.fgr32[i + 1]; - device.cpu.cop1.fgr64[i] = [bytes_lo, bytes_hi].concat().try_into().unwrap(); - i += 2; - } - } -} diff --git a/src/device/cpu.rs b/src/device/cpu.rs index d163e0a0..07aedb0b 100644 --- a/src/device/cpu.rs +++ b/src/device/cpu.rs @@ -272,8 +272,8 @@ pub fn run(device: &mut device::Device) { continue; // TLB exception } - if cached { - device::cache::icache_fetch(device, device.cpu.pc_phys) + if let Some(virt_address) = cached { + device::cache::icache_fetch(device, virt_address, device.cpu.pc_phys) } else { let opcode = device.memory.memory_map_read[(device.cpu.pc_phys >> 16) as usize]( device, diff --git a/src/device/cpu_instructions.rs b/src/device/cpu_instructions.rs index a8c8e4c3..2be942e3 100644 --- a/src/device/cpu_instructions.rs +++ b/src/device/cpu_instructions.rs @@ -139,8 +139,10 @@ pub fn bgtz(device: &mut device::Device, opcode: u32) { } pub fn addi(device: &mut device::Device, opcode: u32) { - device.cpu.gpr[rt(opcode) as usize] = - se32(device.cpu.gpr[rs(opcode) as usize] as i32).wrapping_add(se16(imm(opcode) as i16)) + match (device.cpu.gpr[rs(opcode) as usize] as i32).checked_add(imm(opcode) as i16 as i32) { + Some(result) => device.cpu.gpr[rt(opcode) as usize] = se32(result), + None => device::exceptions::overflow_exception(device), + } } pub fn addiu(device: &mut device::Device, opcode: u32) { @@ -215,8 +217,10 @@ pub fn bgtzl(device: &mut device::Device, opcode: u32) { } pub fn daddi(device: &mut device::Device, opcode: u32) { - device.cpu.gpr[rt(opcode) as usize] = - device.cpu.gpr[rs(opcode) as usize].wrapping_add(se16(imm(opcode) as i16)) + match (device.cpu.gpr[rs(opcode) as usize] as i64).checked_add(imm(opcode) as i16 as i64) { + Some(result) => device.cpu.gpr[rt(opcode) as usize] = result as u64, + None => device::exceptions::overflow_exception(device), + } } pub fn daddiu(device: &mut device::Device, opcode: u32) { @@ -624,43 +628,27 @@ pub fn swr(device: &mut device::Device, opcode: u32) { } pub fn cache(device: &mut device::Device, opcode: u32) { - let (phys_address, _, err) = device::memory::translate_address( - device, - device.cpu.gpr[rs(opcode) as usize].wrapping_add(se16(imm(opcode) as i16)), - device::memory::AccessType::Read, - ); + let virt_address = device.cpu.gpr[rs(opcode) as usize].wrapping_add(se16(imm(opcode) as i16)); + let (phys_address, _, err) = + device::memory::translate_address(device, virt_address, device::memory::AccessType::Read); if err { return; } - let dcache_line = ((phys_address >> 4) & 0x1FF) as usize; - let icache_line = ((phys_address >> 5) & 0x1FF) as usize; + let ptag = (phys_address & !0xFFF) as u32; + let dcache_line = device::cache::dcache_line(virt_address); + let icache_line = device::cache::icache_line(virt_address); match rt(opcode) { 0x00 => { - //icache index invalidate - device.memory.icache[icache_line].valid = false + //icache index invalidate (the ptag is replaced with the one of the address) + device.memory.icache[icache_line].valid = false; + device.memory.icache[icache_line].tag = ptag } 0x04 => { //icache load tag - let mut valid = 0; - if device.memory.icache[icache_line].valid { - valid = 1 - } - device::memory::masked_write_64( - &mut device.cpu.cop0.regs[device::cop0::COP0_TAGLO_REG], - valid << 7, - 0x80, - ); - device::memory::masked_write_64( - &mut device.cpu.cop0.regs[device::cop0::COP0_TAGLO_REG], - 0, - 0x40, - ); - device::memory::masked_write_64( - &mut device.cpu.cop0.regs[device::cop0::COP0_TAGLO_REG], - (device.memory.icache[icache_line].tag >> 4) as u64, - 0xFFFFF00, - ) + let valid = device.memory.icache[icache_line].valid as u64; + device.cpu.cop0.regs[device::cop0::COP0_TAGLO_REG] = + (valid << 7) | ((device.memory.icache[icache_line].tag >> 4) as u64 & 0xFFFFF00) } 0x08 => { //icache store tag @@ -685,64 +673,51 @@ pub fn cache(device: &mut device::Device, opcode: u32) { device::cache::icache_writeback(device, icache_line) } } + // The dcache invalidate operations only clear the valid flag, the dirty flag is kept 0x01 => { - //dcache index write back invalidate - if device.memory.dcache[dcache_line].dirty && device.memory.dcache[dcache_line].valid { - device::cache::dcache_writeback(device, dcache_line) + //dcache index write back invalidate (a valid line also takes the ptag of the address) + if device.memory.dcache[dcache_line].valid { + if device.memory.dcache[dcache_line].dirty { + device::cache::dcache_writeback(device, dcache_line) + } + device.memory.dcache[dcache_line].valid = false; + device.memory.dcache[dcache_line].tag = ptag } - device.memory.dcache[dcache_line].valid = false } 0x05 => { - //dcache index load tag - let mut valid = 0; - let mut dirty = 0; - if device.memory.dcache[dcache_line].valid { - valid = 1 - } - if device.memory.dcache[dcache_line].dirty { - dirty = 1 - } - device::memory::masked_write_64( - &mut device.cpu.cop0.regs[device::cop0::COP0_TAGLO_REG], - valid << 7, - 0x80, - ); - device::memory::masked_write_64( - &mut device.cpu.cop0.regs[device::cop0::COP0_TAGLO_REG], - dirty << 6, - 0x40, - ); - device::memory::masked_write_64( - &mut device.cpu.cop0.regs[device::cop0::COP0_TAGLO_REG], - (device.memory.dcache[dcache_line].tag >> 4) as u64, - 0xFFFFF00, - ) + //dcache index load tag (a valid line always reports the dirty state) + let state = if device.memory.dcache[dcache_line].valid { + 0xC0 + } else { + 0 + }; + device.cpu.cop0.regs[device::cop0::COP0_TAGLO_REG] = + state | ((device.memory.dcache[dcache_line].tag >> 4) as u64 & 0xFFFFF00) } 0x09 => { - //dcache index store tag + //dcache index store tag (the dirty flag can't be changed) device.memory.dcache[dcache_line].valid = (device.cpu.cop0.regs[device::cop0::COP0_TAGLO_REG] & 0x80) >> 7 != 0; - device.memory.dcache[dcache_line].dirty = - (device.cpu.cop0.regs[device::cop0::COP0_TAGLO_REG] & 0x40) >> 6 != 0; device.memory.dcache[dcache_line].tag = ((device.cpu.cop0.regs[device::cop0::COP0_TAGLO_REG] & 0xFFFFF00) << 4) as u32 } 0x0D => { - //dcache create dirty exclusive - if !device::cache::dcache_hit(device, dcache_line, phys_address) - && device.memory.dcache[dcache_line].dirty - { - device::cache::dcache_writeback(device, dcache_line) + //dcache create dirty exclusive (doesn't set the dirty flag) + if !device::cache::dcache_hit(device, dcache_line, phys_address) { + if device.memory.dcache[dcache_line].valid + && device.memory.dcache[dcache_line].dirty + { + device::cache::dcache_writeback(device, dcache_line) + } + device.memory.dcache[dcache_line].dirty = false; + device.memory.dcache[dcache_line].tag = ptag; + device.memory.dcache[dcache_line].valid = true } - device.memory.dcache[dcache_line].tag = (phys_address & !0xFFF) as u32; - device.memory.dcache[dcache_line].valid = true; - device.memory.dcache[dcache_line].dirty = true } 0x11 => { //dcache hit invalidate if device::cache::dcache_hit(device, dcache_line, phys_address) { - device.memory.dcache[dcache_line].valid = false; - device.memory.dcache[dcache_line].dirty = false + device.memory.dcache[dcache_line].valid = false } } 0x15 => { @@ -759,7 +734,8 @@ pub fn cache(device: &mut device::Device, opcode: u32) { if device::cache::dcache_hit(device, dcache_line, phys_address) && device.memory.dcache[dcache_line].dirty { - device::cache::dcache_writeback(device, dcache_line) + device::cache::dcache_writeback(device, dcache_line); + device.memory.dcache[dcache_line].dirty = false } } _ => { @@ -854,7 +830,6 @@ pub fn sc(device: &mut device::Device, opcode: u32) { return; } - device.cpu.llbit = false; device::memory::data_write( device, phys_address, @@ -879,7 +854,6 @@ pub fn scd(device: &mut device::Device, opcode: u32) { return; } - device.cpu.llbit = false; device::memory::data_write( device, phys_address, @@ -1033,8 +1007,10 @@ pub fn dsrav(device: &mut device::Device, opcode: u32) { } pub fn mult(device: &mut device::Device, opcode: u32) { - let result = ((device.cpu.gpr[rs(opcode) as usize]) as i32 as i64) - .wrapping_mul(device.cpu.gpr[rt(opcode) as usize] as i32 as i64); + // VR4300 quirk: rs is used as a full 64-bit value and rt as a sign-extended + // 35-bit value; the low 64 bits of the product are split into HI/LO. + let result = (device.cpu.gpr[rs(opcode) as usize] as i64) + .wrapping_mul(((device.cpu.gpr[rt(opcode) as usize] as i64) << 29) >> 29); device.cpu.lo = se32((result) as i32); device.cpu.hi = se32((result >> 32) as i32); @@ -1137,10 +1113,12 @@ pub fn ddivu(device: &mut device::Device, opcode: u32) { } pub fn add(device: &mut device::Device, opcode: u32) { - device.cpu.gpr[rd(opcode) as usize] = se32( - (device.cpu.gpr[rs(opcode) as usize] as u32) - .wrapping_add(device.cpu.gpr[rt(opcode) as usize] as u32) as i32, - ) + match (device.cpu.gpr[rs(opcode) as usize] as i32) + .checked_add(device.cpu.gpr[rt(opcode) as usize] as i32) + { + Some(result) => device.cpu.gpr[rd(opcode) as usize] = se32(result), + None => device::exceptions::overflow_exception(device), + } } pub fn addu(device: &mut device::Device, opcode: u32) { @@ -1151,10 +1129,12 @@ pub fn addu(device: &mut device::Device, opcode: u32) { } pub fn sub(device: &mut device::Device, opcode: u32) { - device.cpu.gpr[rd(opcode) as usize] = se32( - (device.cpu.gpr[rs(opcode) as usize] as u32) - .wrapping_sub(device.cpu.gpr[rt(opcode) as usize] as u32) as i32, - ) + match (device.cpu.gpr[rs(opcode) as usize] as i32) + .checked_sub(device.cpu.gpr[rt(opcode) as usize] as i32) + { + Some(result) => device.cpu.gpr[rd(opcode) as usize] = se32(result), + None => device::exceptions::overflow_exception(device), + } } pub fn subu(device: &mut device::Device, opcode: u32) { @@ -1196,8 +1176,12 @@ pub fn sltu(device: &mut device::Device, opcode: u32) { } pub fn dadd(device: &mut device::Device, opcode: u32) { - device.cpu.gpr[rd(opcode) as usize] = - device.cpu.gpr[rs(opcode) as usize].wrapping_add(device.cpu.gpr[rt(opcode) as usize]) + match (device.cpu.gpr[rs(opcode) as usize] as i64) + .checked_add(device.cpu.gpr[rt(opcode) as usize] as i64) + { + Some(result) => device.cpu.gpr[rd(opcode) as usize] = result as u64, + None => device::exceptions::overflow_exception(device), + } } pub fn daddu(device: &mut device::Device, opcode: u32) { @@ -1206,8 +1190,12 @@ pub fn daddu(device: &mut device::Device, opcode: u32) { } pub fn dsub(device: &mut device::Device, opcode: u32) { - device.cpu.gpr[rd(opcode) as usize] = - device.cpu.gpr[rs(opcode) as usize].wrapping_sub(device.cpu.gpr[rt(opcode) as usize]) + match (device.cpu.gpr[rs(opcode) as usize] as i64) + .checked_sub(device.cpu.gpr[rt(opcode) as usize] as i64) + { + Some(result) => device.cpu.gpr[rd(opcode) as usize] = result as u64, + None => device::exceptions::overflow_exception(device), + } } pub fn dsubu(device: &mut device::Device, opcode: u32) { diff --git a/src/device/exceptions.rs b/src/device/exceptions.rs index 5a91c18c..5ae2b6f2 100644 --- a/src/device/exceptions.rs +++ b/src/device/exceptions.rs @@ -39,10 +39,16 @@ fn set_exception_cause(device: &mut device::Device, cause: u64) { } pub fn interrupt_exception(device: &mut device::Device) { - device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG] &= !device::cop0::COP0_CAUSE_EXCCODE_MASK; + set_exception_cause(device, 0); exception_general(device, 0x180); } +pub fn overflow_exception(device: &mut device::Device) { + set_exception_cause(device, device::cop0::COP0_CAUSE_EXCCODE_OV); + + exception_general(device, 0x180) +} + pub fn floating_point_exception(device: &mut device::Device) { set_exception_cause(device, device::cop0::COP0_CAUSE_EXCCODE_FPE); diff --git a/src/device/fpu_instructions.rs b/src/device/fpu_instructions.rs index 0b4824f3..f1f736ec 100644 --- a/src/device/fpu_instructions.rs +++ b/src/device/fpu_instructions.rs @@ -1,4 +1,5 @@ use crate::device; +use crate::device::cop1::FCR31_CMP_BIT; pub fn fs(opcode: u32) -> u32 { (opcode >> 11) & 0x1F @@ -12,8 +13,144 @@ fn fd(opcode: u32) -> u32 { (opcode >> 6) & 0x1F } +pub trait FpuFloat: Copy + PartialEq + PartialOrd { + fn is_nan(self) -> bool; + fn round_ties_even(self) -> Self; + fn trunc(self) -> Self; + fn ceil(self) -> Self; + fn floor(self) -> Self; + fn to_i64(self) -> i64; + fn get_fs(device: &device::Device, index: u32) -> Self; + fn get_ft(device: &device::Device, index: u32) -> Self; + fn set_fd(device: &mut device::Device, index: u32, value: Self); +} + +macro_rules! impl_fpu_float { + ($t:ty, $bits:ty) => { + impl FpuFloat for $t { + fn is_nan(self) -> bool { + <$t>::is_nan(self) + } + // lowered to roundeven/frintn, which don't depend on the host rounding mode + fn round_ties_even(self) -> Self { + <$t>::round_ties_even(self) + } + fn trunc(self) -> Self { + <$t>::trunc(self) + } + fn ceil(self) -> Self { + <$t>::ceil(self) + } + fn floor(self) -> Self { + <$t>::floor(self) + } + fn to_i64(self) -> i64 { + self as i64 + } + fn get_fs(device: &device::Device, index: u32) -> Self { + <$t>::from_bits(device.cpu.cop1.fgr[device::cop1::fs_index(device, index)] as $bits) + } + fn get_ft(device: &device::Device, index: u32) -> Self { + <$t>::from_bits(device.cpu.cop1.fgr[index as usize] as $bits) + } + // 32 bit results clear the upper half of the register + fn set_fd(device: &mut device::Device, index: u32, value: Self) { + device.cpu.cop1.fgr[index as usize] = value.to_bits() as u64 + } + } + }; +} + +impl_fpu_float!(f32, u32); +impl_fpu_float!(f64, u64); + +fn get_fs_s32(device: &device::Device, index: u32) -> i32 { + device.cpu.cop1.fgr[device::cop1::fs_index(device, index)] as i32 +} + +fn get_fs_s64(device: &device::Device, index: u32) -> i64 { + device.cpu.cop1.fgr[device::cop1::fs_index(device, index)] as i64 +} + +fn set_fd_s32(device: &mut device::Device, index: u32, value: i32) { + device.cpu.cop1.fgr[index as usize] = value as u32 as u64 +} + +fn set_fd_s64(device: &mut device::Device, index: u32, value: i64) { + device.cpu.cop1.fgr[index as usize] = value as u64 +} + +#[inline(always)] +fn binary_op( + device: &mut device::Device, + opcode: u32, + cycles: u64, + op: impl FnOnce(F, F) -> F, +) { + let a = F::get_fs(device, fs(opcode)); + let b = F::get_ft(device, ft(opcode)); + F::set_fd(device, fd(opcode), op(a, b)); + device::cop0::add_cycles(device, cycles) +} + +#[inline(always)] +fn unary_op( + device: &mut device::Device, + opcode: u32, + cycles: u64, + op: impl FnOnce(F) -> R, +) { + let a = F::get_fs(device, fs(opcode)); + R::set_fd(device, fd(opcode), op(a)); + device::cop0::add_cycles(device, cycles) +} + +const ROUND_NEAREST: u32 = 0; +const ROUND_TRUNC: u32 = 1; +const ROUND_CEIL: u32 = 2; +const ROUND_FLOOR: u32 = 3; + +// Conversion to a 32 bit (W) or 64 bit (L) integer +#[inline(always)] +fn float_to_int( + device: &mut device::Device, + opcode: u32, + mode: u32, +) { + let value = F::get_fs(device, fs(opcode)); + let result = match mode { + ROUND_NEAREST => value.round_ties_even(), + ROUND_TRUNC => value.trunc(), + ROUND_CEIL => value.ceil(), + _ => value.floor(), + }; + if LONG { + set_fd_s64(device, fd(opcode), result.to_i64()) + } else { + set_fd_s32(device, fd(opcode), result.to_i64() as i32) + } + device::cop0::add_cycles(device, 4) +} + +/// C.cond.fmt, COND is the 4 bit condition from the opcode: +/// bit 0: true if unordered, bit 1: true if equal, bit 2: true if less than +pub fn c_cond(device: &mut device::Device, opcode: u32) { + let a = F::get_fs(device, fs(opcode)); + let b = F::get_ft(device, ft(opcode)); + let condition = if a.is_nan() || b.is_nan() { + COND & 1 != 0 + } else { + (COND & 2 != 0 && a == b) || (COND & 4 != 0 && a < b) + }; + if condition { + device.cpu.cop1.fcr31 |= FCR31_CMP_BIT + } else { + device.cpu.cop1.fcr31 &= !FCR31_CMP_BIT + } +} + pub fn bc1f(device: &mut device::Device, opcode: u32) { - if device.cpu.cop1.fcr31 & device::cop1::FCR31_CMP_BIT == 0 { + if device.cpu.cop1.fcr31 & FCR31_CMP_BIT == 0 { device::cpu_instructions::check_relative_idle_loop(device, opcode); device.cpu.branch_state.state = device::cpu::State::Take; device.cpu.branch_state.pc = device.cpu.pc.wrapping_add( @@ -25,7 +162,7 @@ pub fn bc1f(device: &mut device::Device, opcode: u32) { } pub fn bc1t(device: &mut device::Device, opcode: u32) { - if device.cpu.cop1.fcr31 & device::cop1::FCR31_CMP_BIT != 0 { + if device.cpu.cop1.fcr31 & FCR31_CMP_BIT != 0 { device::cpu_instructions::check_relative_idle_loop(device, opcode); device.cpu.branch_state.state = device::cpu::State::Take; device.cpu.branch_state.pc = device.cpu.pc.wrapping_add( @@ -37,7 +174,7 @@ pub fn bc1t(device: &mut device::Device, opcode: u32) { } pub fn bc1fl(device: &mut device::Device, opcode: u32) { - if device.cpu.cop1.fcr31 & device::cop1::FCR31_CMP_BIT == 0 { + if device.cpu.cop1.fcr31 & FCR31_CMP_BIT == 0 { device::cpu_instructions::check_relative_idle_loop(device, opcode); device.cpu.branch_state.state = device::cpu::State::Take; device.cpu.branch_state.pc = device.cpu.pc.wrapping_add( @@ -49,7 +186,7 @@ pub fn bc1fl(device: &mut device::Device, opcode: u32) { } pub fn bc1tl(device: &mut device::Device, opcode: u32) { - if device.cpu.cop1.fcr31 & device::cop1::FCR31_CMP_BIT != 0 { + if device.cpu.cop1.fcr31 & FCR31_CMP_BIT != 0 { device::cpu_instructions::check_relative_idle_loop(device, opcode); device.cpu.branch_state.state = device::cpu::State::Take; device.cpu.branch_state.pc = device.cpu.pc.wrapping_add( @@ -61,640 +198,183 @@ pub fn bc1tl(device: &mut device::Device, opcode: u32) { } pub fn add_s(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); - let ft = device::cop1::get_fpr_single(device, ft(opcode) as usize); - device::cop1::set_fpr_single(device, fd(opcode) as usize, fs + ft, true); - device::cop0::add_cycles(device, 2) + binary_op::(device, opcode, 2, |a, b| a + b) } pub fn sub_s(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); - let ft = device::cop1::get_fpr_single(device, ft(opcode) as usize); - device::cop1::set_fpr_single(device, fd(opcode) as usize, fs - ft, true); - device::cop0::add_cycles(device, 2) + binary_op::(device, opcode, 2, |a, b| a - b) } pub fn mul_s(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); - let ft = device::cop1::get_fpr_single(device, ft(opcode) as usize); - device::cop1::set_fpr_single(device, fd(opcode) as usize, fs * ft, true); - device::cop0::add_cycles(device, 4) + binary_op::(device, opcode, 4, |a, b| a * b) } pub fn div_s(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); - let ft = device::cop1::get_fpr_single(device, ft(opcode) as usize); - device::cop1::set_fpr_single(device, fd(opcode) as usize, fs / ft, true); - device::cop0::add_cycles(device, 28) + binary_op::(device, opcode, 28, |a, b| a / b) } pub fn sqrt_s(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); - device::cop1::set_fpr_single(device, fd(opcode) as usize, fs.sqrt(), true); - device::cop0::add_cycles(device, 28) + unary_op::(device, opcode, 28, |a| a.sqrt()) } pub fn abs_s(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); - device::cop1::set_fpr_single(device, fd(opcode) as usize, fs.abs(), true); - device::cop0::add_cycles(device, 2) + unary_op::(device, opcode, 2, |a| a.abs()) } +// MOV copies the whole register and doesn't touch FCR31 pub fn mov_s(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); - device::cop1::set_fpr_double(device, fd(opcode) as usize, fs); + device.cpu.cop1.fgr[fd(opcode) as usize] = + device.cpu.cop1.fgr[device::cop1::fs_index(device, fs(opcode))] } pub fn neg_s(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); - device::cop1::set_fpr_single(device, fd(opcode) as usize, -fs, true); + unary_op::(device, opcode, 0, |a| -a) } pub fn round_l_s(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); - let value = f64::from_ne_bytes((fs.round_ties_even() as i64).to_ne_bytes()); - device::cop1::set_fpr_double(device, fd(opcode) as usize, value); - device::cop0::add_cycles(device, 4) + float_to_int::(device, opcode, ROUND_NEAREST) } pub fn trunc_l_s(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); - let value = f64::from_ne_bytes((fs.trunc() as i64).to_ne_bytes()); - device::cop1::set_fpr_double(device, fd(opcode) as usize, value); - device::cop0::add_cycles(device, 4) + float_to_int::(device, opcode, ROUND_TRUNC) } pub fn ceil_l_s(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); - let value = f64::from_ne_bytes((fs.ceil() as i64).to_ne_bytes()); - device::cop1::set_fpr_double(device, fd(opcode) as usize, value); - device::cop0::add_cycles(device, 4) + float_to_int::(device, opcode, ROUND_CEIL) } pub fn floor_l_s(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); - let value = f64::from_ne_bytes((fs.floor() as i64).to_ne_bytes()); - device::cop1::set_fpr_double(device, fd(opcode) as usize, value); - device::cop0::add_cycles(device, 4) + float_to_int::(device, opcode, ROUND_FLOOR) } pub fn round_w_s(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); - let value = f32::from_ne_bytes((fs.round_ties_even() as i32).to_ne_bytes()); - device::cop1::set_fpr_single(device, fd(opcode) as usize, value, true); - device::cop0::add_cycles(device, 4) + float_to_int::(device, opcode, ROUND_NEAREST) } pub fn trunc_w_s(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); - let value = f32::from_ne_bytes((fs.trunc() as i32).to_ne_bytes()); - device::cop1::set_fpr_single(device, fd(opcode) as usize, value, true); - device::cop0::add_cycles(device, 4) + float_to_int::(device, opcode, ROUND_TRUNC) } pub fn ceil_w_s(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); - let value = f32::from_ne_bytes((fs.ceil() as i32).to_ne_bytes()); - device::cop1::set_fpr_single(device, fd(opcode) as usize, value, true); - device::cop0::add_cycles(device, 4) + float_to_int::(device, opcode, ROUND_CEIL) } pub fn floor_w_s(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); - let value = f32::from_ne_bytes((fs.floor() as i32).to_ne_bytes()); - device::cop1::set_fpr_single(device, fd(opcode) as usize, value, true); - device::cop0::add_cycles(device, 4) + float_to_int::(device, opcode, ROUND_FLOOR) } pub fn cvt_d_s(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); - device::cop1::set_fpr_double(device, fd(opcode) as usize, fs as f64); - device::cop0::add_cycles(device, 4) + unary_op::(device, opcode, 4, |a| a as f64) } pub fn cvt_w_s(device: &mut device::Device, opcode: u32) { - match device.cpu.cop1.fcr31 & 3 { - 0 => round_w_s(device, opcode), - 1 => trunc_w_s(device, opcode), - 2 => ceil_w_s(device, opcode), - 3 => floor_w_s(device, opcode), - _ => panic!("unknown cvt_w_s"), - } + let mode = device.cpu.cop1.fcr31 & 3; + float_to_int::(device, opcode, mode) } pub fn cvt_l_s(device: &mut device::Device, opcode: u32) { - match device.cpu.cop1.fcr31 & 3 { - 0 => round_l_s(device, opcode), - 1 => trunc_l_s(device, opcode), - 2 => ceil_l_s(device, opcode), - 3 => floor_l_s(device, opcode), - _ => panic!("unknown cvt_l_s"), - } -} - -pub fn c_f_s(device: &mut device::Device, _opcode: u32) { - device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT -} - -pub fn c_un_s(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); - let ft = device::cop1::get_fpr_single(device, ft(opcode) as usize); - if fs.is_nan() || ft.is_nan() { - device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; - } else { - device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; - } -} - -pub fn c_eq_s(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); - let ft = device::cop1::get_fpr_single(device, ft(opcode) as usize); - if fs.is_nan() || ft.is_nan() { - device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; - return; - } - if fs == ft { - device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; - } else { - device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; - } -} - -pub fn c_ueq_s(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); - let ft = device::cop1::get_fpr_single(device, ft(opcode) as usize); - if fs.is_nan() || ft.is_nan() { - device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; - return; - } - if fs == ft { - device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; - } else { - device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; - } -} - -pub fn c_olt_s(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); - let ft = device::cop1::get_fpr_single(device, ft(opcode) as usize); - if fs.is_nan() || ft.is_nan() { - device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; - return; - } - if fs < ft { - device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; - } else { - device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; - } -} - -pub fn c_ult_s(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); - let ft = device::cop1::get_fpr_single(device, ft(opcode) as usize); - if fs.is_nan() || ft.is_nan() { - device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; - return; - } - if fs < ft { - device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; - } else { - device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; - } -} - -pub fn c_ole_s(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); - let ft = device::cop1::get_fpr_single(device, ft(opcode) as usize); - if fs.is_nan() || ft.is_nan() { - device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; - return; - } - if fs <= ft { - device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; - } else { - device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; - } -} - -pub fn c_ule_s(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); - let ft = device::cop1::get_fpr_single(device, ft(opcode) as usize); - if fs.is_nan() || ft.is_nan() { - device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; - return; - } - if fs <= ft { - device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; - } else { - device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; - } -} - -pub fn c_sf_s(device: &mut device::Device, _opcode: u32) { - device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; -} - -pub fn c_ngle_s(device: &mut device::Device, _opcode: u32) { - device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; -} - -pub fn c_seq_s(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); - let ft = device::cop1::get_fpr_single(device, ft(opcode) as usize); - - if fs == ft { - device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; - } else { - device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; - } -} - -pub fn c_ngl_s(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); - let ft = device::cop1::get_fpr_single(device, ft(opcode) as usize); - - if fs == ft { - device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; - } else { - device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; - } -} - -pub fn c_lt_s(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); - let ft = device::cop1::get_fpr_single(device, ft(opcode) as usize); - - if fs < ft { - device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; - } else { - device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; - } -} - -pub fn c_nge_s(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); - let ft = device::cop1::get_fpr_single(device, ft(opcode) as usize); - - if fs < ft { - device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; - } else { - device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; - } -} - -pub fn c_le_s(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); - let ft = device::cop1::get_fpr_single(device, ft(opcode) as usize); - - if fs <= ft { - device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; - } else { - device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; - } -} - -pub fn c_ngt_s(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); - let ft = device::cop1::get_fpr_single(device, ft(opcode) as usize); - - if fs <= ft { - device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; - } else { - device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; - } + let mode = device.cpu.cop1.fcr31 & 3; + float_to_int::(device, opcode, mode) } pub fn add_d(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); - let ft = device::cop1::get_fpr_double(device, ft(opcode) as usize); - device::cop1::set_fpr_double(device, fd(opcode) as usize, fs + ft); - device::cop0::add_cycles(device, 2) + binary_op::(device, opcode, 2, |a, b| a + b) } pub fn sub_d(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); - let ft = device::cop1::get_fpr_double(device, ft(opcode) as usize); - device::cop1::set_fpr_double(device, fd(opcode) as usize, fs - ft); - device::cop0::add_cycles(device, 2) + binary_op::(device, opcode, 2, |a, b| a - b) } pub fn mul_d(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); - let ft = device::cop1::get_fpr_double(device, ft(opcode) as usize); - device::cop1::set_fpr_double(device, fd(opcode) as usize, fs * ft); - device::cop0::add_cycles(device, 7) + binary_op::(device, opcode, 7, |a, b| a * b) } pub fn div_d(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); - let ft = device::cop1::get_fpr_double(device, ft(opcode) as usize); - device::cop1::set_fpr_double(device, fd(opcode) as usize, fs / ft); - device::cop0::add_cycles(device, 57) + binary_op::(device, opcode, 57, |a, b| a / b) } pub fn sqrt_d(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); - device::cop1::set_fpr_double(device, fd(opcode) as usize, fs.sqrt()); - device::cop0::add_cycles(device, 57) + unary_op::(device, opcode, 57, |a| a.sqrt()) } pub fn abs_d(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); - device::cop1::set_fpr_double(device, fd(opcode) as usize, fs.abs()); - device::cop0::add_cycles(device, 2) + unary_op::(device, opcode, 2, |a| a.abs()) } pub fn mov_d(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); - device::cop1::set_fpr_double(device, fd(opcode) as usize, fs); + mov_s(device, opcode) } pub fn neg_d(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); - device::cop1::set_fpr_double(device, fd(opcode) as usize, -fs); + unary_op::(device, opcode, 0, |a| -a) } pub fn round_l_d(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); - let value = f64::from_ne_bytes((fs.round_ties_even() as i64).to_ne_bytes()); - device::cop1::set_fpr_double(device, fd(opcode) as usize, value); - device::cop0::add_cycles(device, 4) + float_to_int::(device, opcode, ROUND_NEAREST) } pub fn trunc_l_d(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); - let value = f64::from_ne_bytes((fs.trunc() as i64).to_ne_bytes()); - device::cop1::set_fpr_double(device, fd(opcode) as usize, value); - device::cop0::add_cycles(device, 4) + float_to_int::(device, opcode, ROUND_TRUNC) } pub fn ceil_l_d(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); - let value = f64::from_ne_bytes((fs.ceil() as i64).to_ne_bytes()); - device::cop1::set_fpr_double(device, fd(opcode) as usize, value); - device::cop0::add_cycles(device, 4) + float_to_int::(device, opcode, ROUND_CEIL) } pub fn floor_l_d(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); - let value = f64::from_ne_bytes((fs.floor() as i64).to_ne_bytes()); - device::cop1::set_fpr_double(device, fd(opcode) as usize, value); - device::cop0::add_cycles(device, 4) + float_to_int::(device, opcode, ROUND_FLOOR) } pub fn round_w_d(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); - let value = f32::from_ne_bytes((fs.round_ties_even() as i32).to_ne_bytes()); - device::cop1::set_fpr_single(device, fd(opcode) as usize, value, true); - device::cop0::add_cycles(device, 4) + float_to_int::(device, opcode, ROUND_NEAREST) } pub fn trunc_w_d(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); - let value = f32::from_ne_bytes((fs.trunc() as i32).to_ne_bytes()); - device::cop1::set_fpr_single(device, fd(opcode) as usize, value, true); - device::cop0::add_cycles(device, 4) + float_to_int::(device, opcode, ROUND_TRUNC) } pub fn ceil_w_d(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); - let value = f32::from_ne_bytes((fs.ceil() as i32).to_ne_bytes()); - device::cop1::set_fpr_single(device, fd(opcode) as usize, value, true); - device::cop0::add_cycles(device, 4) + float_to_int::(device, opcode, ROUND_CEIL) } pub fn floor_w_d(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); - let value = f32::from_ne_bytes((fs.floor() as i32).to_ne_bytes()); - device::cop1::set_fpr_single(device, fd(opcode) as usize, value, true); - device::cop0::add_cycles(device, 4) + float_to_int::(device, opcode, ROUND_FLOOR) } pub fn cvt_s_d(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); - device::cop1::set_fpr_single(device, fd(opcode) as usize, fs as f32, true); - device::cop0::add_cycles(device, 4) + unary_op::(device, opcode, 4, |a| a as f32) } pub fn cvt_w_d(device: &mut device::Device, opcode: u32) { - match device.cpu.cop1.fcr31 & 3 { - 0 => round_w_d(device, opcode), - 1 => trunc_w_d(device, opcode), - 2 => ceil_w_d(device, opcode), - 3 => floor_w_d(device, opcode), - _ => panic!("unknown cvt_w_d"), - } + let mode = device.cpu.cop1.fcr31 & 3; + float_to_int::(device, opcode, mode) } pub fn cvt_l_d(device: &mut device::Device, opcode: u32) { - match device.cpu.cop1.fcr31 & 3 { - 0 => round_l_d(device, opcode), - 1 => trunc_l_d(device, opcode), - 2 => ceil_l_d(device, opcode), - 3 => floor_l_d(device, opcode), - _ => panic!("unknown cvt_l_d"), - } -} - -pub fn c_f_d(device: &mut device::Device, _opcode: u32) { - device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; -} - -pub fn c_un_d(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); - let ft = device::cop1::get_fpr_double(device, ft(opcode) as usize); - if fs.is_nan() || ft.is_nan() { - device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; - } else { - device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; - } -} - -pub fn c_eq_d(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); - let ft = device::cop1::get_fpr_double(device, ft(opcode) as usize); - if fs.is_nan() || ft.is_nan() { - device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; - return; - } - if fs == ft { - device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; - } else { - device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; - } -} - -pub fn c_ueq_d(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); - let ft = device::cop1::get_fpr_double(device, ft(opcode) as usize); - if fs.is_nan() || ft.is_nan() { - device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; - return; - } - if fs == ft { - device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; - } else { - device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; - } -} - -pub fn c_olt_d(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); - let ft = device::cop1::get_fpr_double(device, ft(opcode) as usize); - if fs.is_nan() || ft.is_nan() { - device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; - return; - } - if fs < ft { - device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; - } else { - device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; - } -} - -pub fn c_ult_d(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); - let ft = device::cop1::get_fpr_double(device, ft(opcode) as usize); - if fs.is_nan() || ft.is_nan() { - device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; - return; - } - if fs < ft { - device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; - } else { - device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; - } -} - -pub fn c_ole_d(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); - let ft = device::cop1::get_fpr_double(device, ft(opcode) as usize); - if fs.is_nan() || ft.is_nan() { - device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; - return; - } - if fs <= ft { - device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; - } else { - device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; - } -} - -pub fn c_ule_d(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); - let ft = device::cop1::get_fpr_double(device, ft(opcode) as usize); - if fs.is_nan() || ft.is_nan() { - device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; - return; - } - if fs <= ft { - device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; - } else { - device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; - } -} - -pub fn c_sf_d(device: &mut device::Device, _opcode: u32) { - device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; -} - -pub fn c_ngle_d(device: &mut device::Device, _opcode: u32) { - device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; -} - -pub fn c_seq_d(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); - let ft = device::cop1::get_fpr_double(device, ft(opcode) as usize); - - if fs == ft { - device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; - } else { - device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; - } -} - -pub fn c_ngl_d(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); - let ft = device::cop1::get_fpr_double(device, ft(opcode) as usize); - - if fs == ft { - device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; - } else { - device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; - } -} - -pub fn c_lt_d(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); - let ft = device::cop1::get_fpr_double(device, ft(opcode) as usize); - - if fs < ft { - device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; - } else { - device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; - } -} - -pub fn c_nge_d(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); - let ft = device::cop1::get_fpr_double(device, ft(opcode) as usize); - - if fs < ft { - device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; - } else { - device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; - } -} - -pub fn c_le_d(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); - let ft = device::cop1::get_fpr_double(device, ft(opcode) as usize); - - if fs <= ft { - device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; - } else { - device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; - } -} - -pub fn c_ngt_d(device: &mut device::Device, opcode: u32) { - let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); - let ft = device::cop1::get_fpr_double(device, ft(opcode) as usize); - - if fs <= ft { - device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; - } else { - device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; - } + let mode = device.cpu.cop1.fcr31 & 3; + float_to_int::(device, opcode, mode) } pub fn cvt_s_l(device: &mut device::Device, opcode: u32) { - let fs = - i64::from_ne_bytes(device::cop1::get_fpr_double(device, fs(opcode) as usize).to_ne_bytes()); - device::cop1::set_fpr_single(device, fd(opcode) as usize, fs as f32, true); + let value = get_fs_s64(device, fs(opcode)); + f32::set_fd(device, fd(opcode), value as f32); device::cop0::add_cycles(device, 4) } pub fn cvt_d_l(device: &mut device::Device, opcode: u32) { - let fs = - i64::from_ne_bytes(device::cop1::get_fpr_double(device, fs(opcode) as usize).to_ne_bytes()); - device::cop1::set_fpr_double(device, fd(opcode) as usize, fs as f64); + let value = get_fs_s64(device, fs(opcode)); + f64::set_fd(device, fd(opcode), value as f64); device::cop0::add_cycles(device, 4) } pub fn cvt_s_w(device: &mut device::Device, opcode: u32) { - let fs = - i32::from_ne_bytes(device::cop1::get_fpr_single(device, fs(opcode) as usize).to_ne_bytes()); - device::cop1::set_fpr_single(device, fd(opcode) as usize, fs as f32, true); + let value = get_fs_s32(device, fs(opcode)); + f32::set_fd(device, fd(opcode), value as f32); device::cop0::add_cycles(device, 4) } pub fn cvt_d_w(device: &mut device::Device, opcode: u32) { - let fs = i32::from_ne_bytes( - device::cop1::get_fpr_double(device, fs(opcode) as usize).to_ne_bytes()[0..4] - .try_into() - .unwrap(), - ); - device::cop1::set_fpr_double(device, fd(opcode) as usize, fs as f64); + let value = get_fs_s32(device, fs(opcode)); + f64::set_fd(device, fd(opcode), value as f64); device::cop0::add_cycles(device, 4) } diff --git a/src/device/memory.rs b/src/device/memory.rs index 7355a597..4921af20 100644 --- a/src/device/memory.rs +++ b/src/device/memory.rs @@ -60,30 +60,37 @@ pub fn masked_write_64(dst: &mut u64, value: u64, mask: u64) { *dst = (*dst & !mask) | (value & mask); } +#[inline(always)] pub fn translate_address( device: &mut device::Device, mut address: u64, access_type: AccessType, -) -> (u64, bool, bool) { +) -> (u64, Option, bool) { if (address & 0xc0000000) != 0x80000000 { if (address & 0xFFFFFFFF00000000) == 0x9000000000000000 { // this is a hack to support 64-bit addresses used by some homebrew address |= 0xA0000000; } else { - return device::tlb::get_physical_address(device, address, access_type); + let (phys_address, cached, err) = + device::tlb::get_physical_address(device, address, access_type); + return (phys_address, cached.then_some(address), err); } } - (address & 0x1FFFFFFF, address & 0x20000000 == 0, false) + ( + address & 0x1FFFFFFF, + (address & 0x20000000 == 0).then_some(address), + false, + ) } pub fn data_read( device: &mut device::Device, phys_address: u64, access_size: AccessSize, - cached: bool, + cached: Option, ) -> u32 { - if cached { - device::cache::dcache_read(device, phys_address) + if let Some(virt_address) = cached { + device::cache::dcache_read(device, virt_address, phys_address) } else { device.memory.memory_map_read[(phys_address >> 16) as usize]( device, @@ -98,10 +105,10 @@ pub fn data_write( phys_address: u64, value: u32, mask: u32, - cached: bool, + cached: Option, ) { - if cached { - device::cache::dcache_write(device, phys_address, value, mask) + if let Some(virt_address) = cached { + device::cache::dcache_write(device, virt_address, phys_address, value, mask) } else { device.memory.memory_map_write[(phys_address >> 16) as usize]( device, diff --git a/src/device/rdp.rs b/src/device/rdp.rs index ed0afa0e..bb062e55 100644 --- a/src/device/rdp.rs +++ b/src/device/rdp.rs @@ -123,11 +123,11 @@ fn run_rdp(device: &mut device::Device) { }; let now = device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG] + rsp_cycles; let timer = ui::video::process_rdp_list(now); - device.rdp.regs_dpc[DPC_STATUS_REG] |= - DPC_STATUS_START_GCLK | DPC_STATUS_PIPE_BUSY | DPC_STATUS_CMD_BUSY; + device.rdp.regs_dpc[DPC_STATUS_REG] |= DPC_STATUS_START_GCLK | DPC_STATUS_PIPE_BUSY; device.rdp.regs_dpc[DPC_PIPEBUSY_REG] = 0xFFFFFF; if timer != 0 { + device.rdp.regs_dpc[DPC_STATUS_REG] |= DPC_STATUS_CMD_BUSY; device::events::create_event(device, device::events::EVENT_TYPE_DP, rsp_cycles + timer) } } diff --git a/src/device/rsp_interface.rs b/src/device/rsp_interface.rs index a426ebe3..985de792 100644 --- a/src/device/rsp_interface.rs +++ b/src/device/rsp_interface.rs @@ -8,7 +8,7 @@ const SP_WR_LEN_REG: usize = 3; pub const SP_STATUS_REG: usize = 4; const SP_DMA_FULL_REG: usize = 5; const SP_DMA_BUSY_REG: usize = 6; -const SP_SEMAPHORE_REG: usize = 7; +pub const SP_SEMAPHORE_REG: usize = 7; pub const SP_REGS_COUNT: usize = 8; pub const SP_PC_REG: usize = 0; diff --git a/src/device/rsp_su_instructions.rs b/src/device/rsp_su_instructions.rs index fbac08c7..5634821f 100644 --- a/src/device/rsp_su_instructions.rs +++ b/src/device/rsp_su_instructions.rs @@ -566,9 +566,11 @@ pub fn mfc0(device: &mut device::Device, opcode: u32) { // This read ended the RSP execution. Re-execute the mfc0 when the // RSP resumes instead of acting on a stale value. Delay slots are left alone - // as the branch state can't be rewound. + // as the branch state can't be rewound. Semaphore reads are not re-executed: + // the read itself sets the semaphore, so the value isn't stale. if !was_sync_point && device.rsp.cpu.sync_point + && rd(opcode) != device::rsp_interface::SP_SEMAPHORE_REG as u32 && device.rsp.cpu.branch_state.state == device::cpu::State::Step { device.rsp.regs2[device::rsp_interface::SP_PC_REG] =