systemtest fixes (#1339)

This commit is contained in:
Logan McNaughton authored and GitHub committed 2026-11-03 17:50:44 +00:00
1 parent 36c3a95f15
commit 2855526561
13 files changed
+777 -1139

No files matched your search

+7 -8
View File
@@ -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],
+20 -8
View File
@@ -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)
+3 -3
View File
@@ -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,
);
}
}
+67 -71
View File
@@ -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
];
}
+378 -430
View File
File diff suppressed because it is too large. Load diff
+2 -2
View File
@@ -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,
+78 -90
View File
@@ -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) {
+7 -1
View File
@@ -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);
+193 -513
View File
@@ -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<F: FpuFloat>(
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<F: FpuFloat, R: FpuFloat>(
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<F: FpuFloat, const LONG: bool>(
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<F: FpuFloat, const COND: u32>(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::<f32>(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::<f32>(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::<f32>(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::<f32>(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::<f32, f32>(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::<f32, f32>(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::<f32, f32>(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::<f32, true>(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::<f32, true>(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::<f32, true>(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::<f32, true>(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::<f32, false>(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::<f32, false>(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::<f32, false>(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::<f32, false>(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::<f32, f64>(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::<f32, false>(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::<f32, true>(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::<f64>(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::<f64>(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::<f64>(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::<f64>(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::<f64, f64>(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::<f64, f64>(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::<f64, f64>(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::<f64, true>(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::<f64, true>(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::<f64, true>(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::<f64, true>(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::<f64, false>(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::<f64, false>(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::<f64, false>(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::<f64, false>(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::<f64, f32>(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::<f64, false>(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::<f64, true>(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)
}
+16 -9
View File
@@ -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<u64>, 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<u64>,
) -> 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<u64>,
) {
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,
+2 -2
View File
@@ -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)
}
}
+1 -1
View File
@@ -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;
+3 -1
View File
@@ -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] =