mirror of
https://github.com/gopher64/gopher64.git
synced 2026-11-15 10:29:38 +01:00
systemtest fixes (#1339)
This commit is contained in:
1 parent
36c3a95f15
commit
2855526561
13 files changed
+777
-1139
No files matched your search
+7
-8
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
File diff suppressed because it is too large.
Load diff
+2
-2
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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] =
|
||||
|
||||
Reference in new issue
Block a user