mirror of
https://github.com/n64dev/cen64.git
synced 2024-06-20 21:17:58 -04:00
1454 lines
29 KiB
C
1454 lines
29 KiB
C
//
|
|
// vr4300/cp1.c: VR4300 floating point unit coprocessor.
|
|
//
|
|
// CEN64: Cycle-Accurate Nintendo 64 Emulator.
|
|
// Copyright (C) 2015, Tyler J. Stachecki.
|
|
//
|
|
// This file is subject to the terms and conditions defined in
|
|
// 'LICENSE', which is part of this source code package.
|
|
//
|
|
|
|
#include "common.h"
|
|
#include "fpu/fpu.h"
|
|
#include "vr4300/cp1.h"
|
|
#include "vr4300/cpu.h"
|
|
#include "vr4300/decoder.h"
|
|
#include "vr4300/fault.h"
|
|
|
|
//
|
|
// Raises a MCI interlock for a set number of cycles.
|
|
//
|
|
static inline int vr4300_do_mci(struct vr4300 *vr4300, unsigned cycles) {
|
|
vr4300->pipeline.cycles_to_stall = cycles - 1;
|
|
vr4300->regs[PIPELINE_CYCLE_TYPE] = 3;
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// ABS.fmt
|
|
//
|
|
int VR4300_CP1_ABS(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t fs, uint64_t ft) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
|
|
enum vr4300_fmt fmt = GET_FMT(iw);
|
|
unsigned dest = GET_FD(iw);
|
|
|
|
uint32_t fs32, fd32;
|
|
uint64_t result;
|
|
|
|
switch (fmt) {
|
|
case VR4300_FMT_S:
|
|
fs32 = fs;
|
|
|
|
fpu_abs_32(&fs32, &fd32);
|
|
result = fd32;
|
|
break;
|
|
|
|
case VR4300_FMT_D:
|
|
fpu_abs_64(&fs, &result);
|
|
break;
|
|
|
|
default:
|
|
VR4300_INV(vr4300);
|
|
return 1;
|
|
}
|
|
|
|
exdc_latch->result = result;
|
|
exdc_latch->dest = dest;
|
|
return vr4300_do_mci(vr4300, 3);
|
|
}
|
|
|
|
//
|
|
// ADD.fmt
|
|
//
|
|
int VR4300_CP1_ADD(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t fs, uint64_t ft) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
|
|
enum vr4300_fmt fmt = GET_FMT(iw);
|
|
unsigned dest = GET_FD(iw);
|
|
|
|
uint32_t fs32, ft32, fd32;
|
|
uint64_t result;
|
|
|
|
switch (fmt) {
|
|
case VR4300_FMT_S:
|
|
fs32 = fs;
|
|
ft32 = ft;
|
|
|
|
fpu_add_32(&fs32, &ft32, &fd32);
|
|
result = fd32;
|
|
break;
|
|
|
|
case VR4300_FMT_D:
|
|
fpu_add_64(&fs, &ft, &result);
|
|
break;
|
|
|
|
default:
|
|
VR4300_INV(vr4300);
|
|
return 1;
|
|
}
|
|
|
|
exdc_latch->result = result;
|
|
exdc_latch->dest = dest;
|
|
return vr4300_do_mci(vr4300, 3);
|
|
}
|
|
|
|
//
|
|
// BC1F
|
|
// BC1FL
|
|
// BC1T
|
|
// BC1TL
|
|
//
|
|
int VR4300_BC1(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t fs, uint64_t ft) {
|
|
struct vr4300_icrf_latch *icrf_latch = &vr4300->pipeline.icrf_latch;
|
|
struct vr4300_rfex_latch *rfex_latch = &vr4300->pipeline.rfex_latch;
|
|
unsigned opcode = (iw >> 16) & 0x3;
|
|
|
|
uint64_t offset = (uint64_t) ((int16_t) iw) << 2;
|
|
uint64_t taken_pc = rfex_latch->common.pc + (offset + 4);
|
|
uint32_t cond = vr4300->regs[VR4300_CP1_FCR31];
|
|
|
|
// XXX: The VR4300 manual says that the results of a FPU
|
|
// FCR writes aren't ready on the next cycle, but it seems
|
|
// that this might actually not be a limitiation of the real
|
|
// hardware?
|
|
if (vr4300->pipeline.dcwb_latch.dest == VR4300_CP1_FCR31)
|
|
cond = vr4300->pipeline.dcwb_latch.result;
|
|
|
|
switch (opcode) {
|
|
case 0x0: // BC1F
|
|
if (!(cond >> 23 & 0x1))
|
|
icrf_latch->pc = taken_pc;
|
|
break;
|
|
|
|
case 0x1: // BC1T
|
|
if (cond >> 23 & 0x1)
|
|
icrf_latch->pc = taken_pc;
|
|
break;
|
|
|
|
case 0x2: // BC1FL
|
|
if (!(cond >> 23 & 0x1))
|
|
icrf_latch->pc = taken_pc;
|
|
else
|
|
rfex_latch->iw_mask = 0;
|
|
break;
|
|
|
|
case 0x3: // BC1TL
|
|
if (cond >> 23 & 0x1)
|
|
icrf_latch->pc = taken_pc;
|
|
else
|
|
rfex_latch->iw_mask = 0;
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// C.eq.fmt
|
|
// C.seq.fmt
|
|
//
|
|
int VR4300_CP1_C_EQ_C_SEQ(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t fs, uint64_t ft) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
|
|
enum vr4300_fmt fmt = GET_FMT(iw);
|
|
unsigned dest = VR4300_CP1_FCR31;
|
|
uint64_t result = vr4300->regs[dest];
|
|
|
|
uint32_t fs32, ft32;
|
|
uint8_t flag;
|
|
|
|
switch (fmt) {
|
|
case VR4300_FMT_S:
|
|
fs32 = fs;
|
|
ft32 = ft;
|
|
|
|
result &= ~(1 << 23);
|
|
flag = fpu_cmp_eq_32(&fs32, &ft32);
|
|
break;
|
|
|
|
case VR4300_FMT_D:
|
|
result &= ~(1 << 23);
|
|
flag = fpu_cmp_eq_64(&fs, &ft);
|
|
break;
|
|
|
|
default:
|
|
VR4300_INV(vr4300);
|
|
return 1;
|
|
}
|
|
|
|
exdc_latch->result = result | (flag << 23);
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// C.f.fmt
|
|
// C.sf.fmt
|
|
//
|
|
int VR4300_CP1_C_F_C_SF(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t fs, uint64_t ft) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
|
|
enum vr4300_fmt fmt = GET_FMT(iw);
|
|
unsigned dest = VR4300_CP1_FCR31;
|
|
uint64_t result = vr4300->regs[dest];
|
|
|
|
uint32_t fs32, ft32;
|
|
uint8_t flag;
|
|
|
|
switch (fmt) {
|
|
case VR4300_FMT_S:
|
|
fs32 = fs;
|
|
ft32 = ft;
|
|
|
|
result &= ~(1 << 23);
|
|
flag = fpu_cmp_f_32(&fs32, &ft32);
|
|
break;
|
|
|
|
case VR4300_FMT_D:
|
|
result &= ~(1 << 23);
|
|
flag = fpu_cmp_f_64(&fs, &ft);
|
|
break;
|
|
|
|
default:
|
|
VR4300_INV(vr4300);
|
|
return 1;
|
|
}
|
|
|
|
exdc_latch->result = result | (flag << 23);
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// C.ole.fmt
|
|
// C.le.fmt
|
|
//
|
|
int VR4300_CP1_C_OLE_C_LE(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t fs, uint64_t ft) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
|
|
enum vr4300_fmt fmt = GET_FMT(iw);
|
|
unsigned dest = VR4300_CP1_FCR31;
|
|
uint64_t result = vr4300->regs[dest];
|
|
|
|
uint32_t fs32, ft32;
|
|
uint8_t flag;
|
|
|
|
switch (fmt) {
|
|
case VR4300_FMT_S:
|
|
fs32 = fs;
|
|
ft32 = ft;
|
|
|
|
result &= ~(1 << 23);
|
|
flag = fpu_cmp_ole_32(&fs32, &ft32);
|
|
break;
|
|
|
|
case VR4300_FMT_D:
|
|
result &= ~(1 << 23);
|
|
flag = fpu_cmp_ole_64(&fs, &ft);
|
|
break;
|
|
|
|
default:
|
|
VR4300_INV(vr4300);
|
|
return 1;
|
|
}
|
|
|
|
exdc_latch->result = result | (flag << 23);
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// C.olt.fmt
|
|
// C.lt.fmt
|
|
//
|
|
int VR4300_CP1_C_OLT_C_LT(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t fs, uint64_t ft) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
|
|
enum vr4300_fmt fmt = GET_FMT(iw);
|
|
unsigned dest = VR4300_CP1_FCR31;
|
|
uint64_t result = vr4300->regs[dest];
|
|
|
|
uint32_t fs32, ft32;
|
|
uint8_t flag;
|
|
|
|
switch (fmt) {
|
|
case VR4300_FMT_S:
|
|
fs32 = fs;
|
|
ft32 = ft;
|
|
|
|
result &= ~(1 << 23);
|
|
flag = fpu_cmp_olt_32(&fs32, &ft32);
|
|
break;
|
|
|
|
case VR4300_FMT_D:
|
|
result &= ~(1 << 23);
|
|
flag = fpu_cmp_olt_64(&fs, &ft);
|
|
break;
|
|
|
|
default:
|
|
VR4300_INV(vr4300);
|
|
return 1;
|
|
}
|
|
|
|
exdc_latch->result = result | (flag << 23);
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// C.ueq.fmt
|
|
// C.ngl.fmt
|
|
//
|
|
int VR4300_CP1_C_UEQ_C_NGL(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t fs, uint64_t ft) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
|
|
enum vr4300_fmt fmt = GET_FMT(iw);
|
|
unsigned dest = VR4300_CP1_FCR31;
|
|
uint64_t result = vr4300->regs[dest];
|
|
|
|
uint32_t fs32, ft32;
|
|
uint8_t flag;
|
|
|
|
switch (fmt) {
|
|
case VR4300_FMT_S:
|
|
fs32 = fs;
|
|
ft32 = ft;
|
|
|
|
result &= ~(1 << 23);
|
|
flag = fpu_cmp_ueq_32(&fs32, &ft32);
|
|
break;
|
|
|
|
case VR4300_FMT_D:
|
|
result &= ~(1 << 23);
|
|
flag = fpu_cmp_ueq_64(&fs, &ft);
|
|
break;
|
|
|
|
default:
|
|
VR4300_INV(vr4300);
|
|
return 1;
|
|
}
|
|
|
|
exdc_latch->result = result | (flag << 23);
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// C.ule.fmt
|
|
// C.ngt.fmt
|
|
//
|
|
int VR4300_CP1_C_ULE_C_NGT(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t fs, uint64_t ft) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
|
|
enum vr4300_fmt fmt = GET_FMT(iw);
|
|
unsigned dest = VR4300_CP1_FCR31;
|
|
uint64_t result = vr4300->regs[dest];
|
|
|
|
uint32_t fs32, ft32;
|
|
uint8_t flag;
|
|
|
|
switch (fmt) {
|
|
case VR4300_FMT_S:
|
|
fs32 = fs;
|
|
ft32 = ft;
|
|
|
|
result &= ~(1 << 23);
|
|
flag = fpu_cmp_ule_32(&fs32, &ft32);
|
|
break;
|
|
|
|
case VR4300_FMT_D:
|
|
result &= ~(1 << 23);
|
|
flag = fpu_cmp_ule_64(&fs, &ft);
|
|
break;
|
|
|
|
default:
|
|
VR4300_INV(vr4300);
|
|
return 1;
|
|
}
|
|
|
|
exdc_latch->result = result | (flag << 23);
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// C.ult.fmt
|
|
// C.nge.fmt
|
|
//
|
|
int VR4300_CP1_C_ULT_C_NGE(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t fs, uint64_t ft) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
|
|
enum vr4300_fmt fmt = GET_FMT(iw);
|
|
unsigned dest = VR4300_CP1_FCR31;
|
|
uint64_t result = vr4300->regs[dest];
|
|
|
|
uint32_t fs32, ft32;
|
|
uint8_t flag;
|
|
|
|
switch (fmt) {
|
|
case VR4300_FMT_S:
|
|
fs32 = fs;
|
|
ft32 = ft;
|
|
|
|
result &= ~(1 << 23);
|
|
flag = fpu_cmp_ult_32(&fs32, &ft32);
|
|
break;
|
|
|
|
case VR4300_FMT_D:
|
|
result &= ~(1 << 23);
|
|
flag = fpu_cmp_ult_64(&fs, &ft);
|
|
break;
|
|
|
|
default:
|
|
VR4300_INV(vr4300);
|
|
return 1;
|
|
}
|
|
|
|
exdc_latch->result = result | (flag << 23);
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// C.un.fmt
|
|
// C.ngle.fmt
|
|
//
|
|
int VR4300_CP1_C_UN_C_NGLE(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t fs, uint64_t ft) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
|
|
enum vr4300_fmt fmt = GET_FMT(iw);
|
|
unsigned dest = VR4300_CP1_FCR31;
|
|
uint64_t result = vr4300->regs[dest];
|
|
|
|
uint32_t fs32, ft32;
|
|
uint8_t flag;
|
|
|
|
switch (fmt) {
|
|
case VR4300_FMT_S:
|
|
fs32 = fs;
|
|
ft32 = ft;
|
|
|
|
result &= ~(1 << 23);
|
|
flag = fpu_cmp_un_32(&fs32, &ft32);
|
|
break;
|
|
|
|
case VR4300_FMT_D:
|
|
result &= ~(1 << 23);
|
|
flag = fpu_cmp_un_64(&fs, &ft);
|
|
break;
|
|
|
|
default:
|
|
VR4300_INV(vr4300);
|
|
return 1;
|
|
}
|
|
|
|
exdc_latch->result = result | (flag << 23);
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// CEIL.l.fmt
|
|
//
|
|
int VR4300_CP1_CEIL_L(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t fs, uint64_t ft) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
|
|
enum vr4300_fmt fmt = GET_FMT(iw);
|
|
unsigned dest = GET_FD(iw);
|
|
|
|
uint32_t fs32;
|
|
uint64_t result;
|
|
|
|
#ifndef CEN64_ARCH_HAS_CEIL
|
|
fpu_state_t saved_state = fpu_get_state();
|
|
fpu_set_state((saved_state & ~FPU_ROUND_MASK) | FPU_ROUND_POSINF);
|
|
|
|
switch (fmt) {
|
|
case VR4300_FMT_S:
|
|
fs32 = fs;
|
|
|
|
fpu_cvt_i64_f32(&fs32, &result);
|
|
break;
|
|
|
|
case VR4300_FMT_D:
|
|
fpu_cvt_i64_f64(&fs, &result);
|
|
break;
|
|
|
|
default:
|
|
VR4300_INV(vr4300);
|
|
return 1;
|
|
}
|
|
|
|
fpu_set_state((saved_state & FPU_ROUND_MASK) |
|
|
(fpu_get_state() & ~FPU_ROUND_MASK));
|
|
#else
|
|
switch (fmt) {
|
|
case VR4300_FMT_S:
|
|
fs32 = fs;
|
|
|
|
fpu_ceil_i64_f32(&fs32, &result);
|
|
break;
|
|
|
|
case VR4300_FMT_D:
|
|
fpu_ceil_i64_f64(&fs, &result);
|
|
break;
|
|
|
|
default:
|
|
VR4300_INV(vr4300);
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
exdc_latch->result = result;
|
|
exdc_latch->dest = dest;
|
|
return vr4300_do_mci(vr4300, 5);
|
|
}
|
|
|
|
//
|
|
// CEIL.w.fmt
|
|
//
|
|
int VR4300_CP1_CEIL_W(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t fs, uint64_t ft) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
|
|
enum vr4300_fmt fmt = GET_FMT(iw);
|
|
unsigned dest = GET_FD(iw);
|
|
|
|
uint32_t fs32;
|
|
uint32_t result;
|
|
|
|
#ifndef CEN64_ARCH_HAS_CEIL
|
|
fpu_state_t saved_state = fpu_get_state();
|
|
fpu_set_state((saved_state & ~FPU_ROUND_MASK) | FPU_ROUND_POSINF);
|
|
|
|
switch (fmt) {
|
|
case VR4300_FMT_S:
|
|
fs32 = fs;
|
|
|
|
fpu_cvt_i32_f32(&fs32, &result);
|
|
break;
|
|
|
|
case VR4300_FMT_D:
|
|
fpu_cvt_i32_f64(&fs, &result);
|
|
break;
|
|
|
|
default:
|
|
VR4300_INV(vr4300);
|
|
return 1;
|
|
}
|
|
|
|
fpu_set_state((saved_state & FPU_ROUND_MASK) |
|
|
(fpu_get_state() & ~FPU_ROUND_MASK));
|
|
#else
|
|
switch (fmt) {
|
|
case VR4300_FMT_S:
|
|
fs32 = fs;
|
|
|
|
fpu_ceil_i32_f32(&fs32, &result);
|
|
break;
|
|
|
|
case VR4300_FMT_D:
|
|
fpu_ceil_i32_f64(&fs, &result);
|
|
break;
|
|
|
|
default:
|
|
VR4300_INV(vr4300);
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
exdc_latch->result = result;
|
|
exdc_latch->dest = dest;
|
|
return vr4300_do_mci(vr4300, 5);
|
|
}
|
|
|
|
//
|
|
// CFC1
|
|
//
|
|
int VR4300_CFC1(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t rs, uint64_t rt) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
|
|
unsigned dest = GET_RT(iw);
|
|
unsigned src = GET_RD(iw);
|
|
uint64_t result;
|
|
|
|
switch (src) {
|
|
case 0: src = VR4300_CP1_FCR0; break;
|
|
case 31: src = VR4300_CP1_FCR31; break;
|
|
|
|
default:
|
|
src = 0;
|
|
|
|
assert(0 && "CFC1: Read reserved FCR.");
|
|
break;
|
|
}
|
|
|
|
result = vr4300->regs[src];
|
|
|
|
// XXX: The VR4300 manual says that the results of a FPU
|
|
// FCR writes aren't ready on the next cycle, but it seems
|
|
// that this might actually not be a limitiation of the real
|
|
// hardware?
|
|
if (vr4300->pipeline.dcwb_latch.dest == VR4300_CP1_FCR31)
|
|
result = vr4300->pipeline.dcwb_latch.result;
|
|
|
|
// Undefined while the next instruction
|
|
// executes, so we can cheat and use the RF.
|
|
exdc_latch->result = (int32_t) result;
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// CTC1
|
|
//
|
|
// XXX: Raise exception on cause/enable.
|
|
// XXX: In such cases, ensure write occurs.
|
|
//
|
|
int VR4300_CTC1(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t rs, uint64_t rt) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
unsigned dest = GET_RD(iw);
|
|
|
|
if (dest == 31)
|
|
dest = VR4300_CP1_FCR31;
|
|
|
|
else {
|
|
assert(0 && "CTC1: Write to fixed/reserved FCR.");
|
|
|
|
dest = 0;
|
|
rt = 0;
|
|
}
|
|
|
|
// Undefined while the next instruction
|
|
// executes, so we can cheat and use WB.
|
|
exdc_latch->result = rt;
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// CVT.d.fmt
|
|
//
|
|
int VR4300_CP1_CVT_D(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t fs, uint64_t ft) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
enum vr4300_fmt fmt = GET_FMT(iw);
|
|
unsigned dest = GET_FD(iw);
|
|
|
|
uint32_t fs32;
|
|
uint64_t result;
|
|
|
|
switch (fmt) {
|
|
case VR4300_FMT_S:
|
|
fs32 = fs;
|
|
|
|
fpu_cvt_f64_f32(&fs32, &result);
|
|
break;
|
|
|
|
case VR4300_FMT_W:
|
|
fs32 = fs;
|
|
|
|
fpu_cvt_f64_i32(&fs32, &result);
|
|
break;
|
|
|
|
case VR4300_FMT_L:
|
|
fpu_cvt_f64_i64(&fs, &result);
|
|
break;
|
|
|
|
default:
|
|
VR4300_INV(vr4300);
|
|
return 1;
|
|
}
|
|
|
|
exdc_latch->result = result;
|
|
exdc_latch->dest = dest;
|
|
return fmt != VR4300_FMT_S
|
|
? vr4300_do_mci(vr4300, 5)
|
|
: 0;
|
|
}
|
|
|
|
//
|
|
// CVT.l.fmt
|
|
//
|
|
int VR4300_CP1_CVT_L(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t fs, uint64_t ft) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
enum vr4300_fmt fmt = GET_FMT(iw);
|
|
unsigned dest = GET_FD(iw);
|
|
|
|
uint32_t fs32;
|
|
uint64_t result;
|
|
|
|
switch (fmt) {
|
|
case VR4300_FMT_S:
|
|
fs32 = fs;
|
|
|
|
fpu_cvt_i64_f32(&fs32, &result);
|
|
break;
|
|
|
|
case VR4300_FMT_D:
|
|
fpu_cvt_i64_f64(&fs, &result);
|
|
break;
|
|
|
|
default:
|
|
VR4300_INV(vr4300);
|
|
return 1;
|
|
}
|
|
|
|
exdc_latch->result = result;
|
|
exdc_latch->dest = dest;
|
|
return vr4300_do_mci(vr4300, 5);
|
|
}
|
|
|
|
//
|
|
// CVT.s.fmt
|
|
//
|
|
int VR4300_CP1_CVT_S(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t fs, uint64_t ft) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
enum vr4300_fmt fmt = GET_FMT(iw);
|
|
unsigned dest = GET_FD(iw);
|
|
|
|
uint32_t fs32;
|
|
uint32_t result;
|
|
|
|
switch (fmt) {
|
|
case VR4300_FMT_D:
|
|
fpu_cvt_f32_f64(&fs, &result);
|
|
break;
|
|
|
|
case VR4300_FMT_W:
|
|
fs32 = fs;
|
|
|
|
fpu_cvt_f32_i32(&fs32, &result);
|
|
break;
|
|
|
|
case VR4300_FMT_L:
|
|
fpu_cvt_f32_i64(&fs, &result);
|
|
break;
|
|
|
|
default:
|
|
VR4300_INV(vr4300);
|
|
return 1;
|
|
}
|
|
|
|
exdc_latch->result = result;
|
|
exdc_latch->dest = dest;
|
|
return vr4300_do_mci(vr4300,
|
|
fmt == VR4300_FMT_D ? 2 : 5);
|
|
}
|
|
|
|
//
|
|
// CVT.w.fmt
|
|
//
|
|
int VR4300_CP1_CVT_W(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t fs, uint64_t ft) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
enum vr4300_fmt fmt = GET_FMT(iw);
|
|
unsigned dest = GET_FD(iw);
|
|
|
|
uint32_t fs32;
|
|
uint32_t result;
|
|
|
|
switch (fmt) {
|
|
case VR4300_FMT_S:
|
|
fs32 = fs;
|
|
|
|
fpu_cvt_i32_f32(&fs32, &result);
|
|
break;
|
|
|
|
case VR4300_FMT_D:
|
|
fpu_cvt_i32_f64(&fs, &result);
|
|
break;
|
|
|
|
default:
|
|
VR4300_INV(vr4300);
|
|
return 1;
|
|
}
|
|
|
|
exdc_latch->result = result;
|
|
exdc_latch->dest = dest;
|
|
return vr4300_do_mci(vr4300, 5);
|
|
}
|
|
|
|
//
|
|
// DIV.fmt
|
|
//
|
|
int VR4300_CP1_DIV(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t fs, uint64_t ft) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
enum vr4300_fmt fmt = GET_FMT(iw);
|
|
unsigned dest = GET_FD(iw);
|
|
|
|
uint32_t fs32, ft32, fd32;
|
|
uint64_t result;
|
|
|
|
switch (fmt) {
|
|
case VR4300_FMT_S:
|
|
fs32 = fs;
|
|
ft32 = ft;
|
|
|
|
fpu_div_32(&fs32, &ft32, &fd32);
|
|
result = fd32;
|
|
break;
|
|
|
|
case VR4300_FMT_D:
|
|
fpu_div_64(&fs, &ft, &result);
|
|
break;
|
|
|
|
default:
|
|
VR4300_INV(vr4300);
|
|
return 1;
|
|
}
|
|
|
|
exdc_latch->result = result;
|
|
exdc_latch->dest = dest;
|
|
return vr4300_do_mci(vr4300,
|
|
fmt == VR4300_FMT_D ? 58 : 29);
|
|
}
|
|
|
|
//
|
|
// DMFC1
|
|
//
|
|
int VR4300_DMFC1(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t fs, uint64_t unused(rt)) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
unsigned dest = GET_RT(iw);
|
|
|
|
exdc_latch->result = fs;
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// DMTC1
|
|
//
|
|
int VR4300_DMTC1(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t fs, uint64_t rt) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
unsigned dest = GET_FS(iw);
|
|
|
|
exdc_latch->result = rt;
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// FLOOR.l.fmt
|
|
//
|
|
int VR4300_CP1_FLOOR_L(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t fs, uint64_t ft) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
enum vr4300_fmt fmt = GET_FMT(iw);
|
|
unsigned dest = GET_FD(iw);
|
|
|
|
uint32_t fs32;
|
|
uint64_t result;
|
|
|
|
#ifndef CEN64_ARCH_HAS_FLOOR
|
|
fpu_state_t saved_state = fpu_get_state();
|
|
fpu_set_state((saved_state & ~FPU_ROUND_MASK) | FPU_ROUND_NEGINF);
|
|
|
|
switch (fmt) {
|
|
case VR4300_FMT_S:
|
|
fs32 = fs;
|
|
|
|
fpu_cvt_i64_f32(&fs32, &result);
|
|
break;
|
|
|
|
case VR4300_FMT_D:
|
|
fpu_cvt_i64_f64(&fs, &result);
|
|
break;
|
|
|
|
default:
|
|
VR4300_INV(vr4300);
|
|
return 1;
|
|
}
|
|
|
|
fpu_set_state((saved_state & FPU_ROUND_MASK) |
|
|
(fpu_get_state() & ~FPU_ROUND_MASK));
|
|
#else
|
|
switch (fmt) {
|
|
case VR4300_FMT_S:
|
|
fs32 = fs;
|
|
|
|
fpu_floor_i64_f32(&fs32, &result);
|
|
break;
|
|
|
|
case VR4300_FMT_D:
|
|
fpu_floor_i64_f64(&fs, &result);
|
|
break;
|
|
|
|
default:
|
|
VR4300_INV(vr4300);
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
exdc_latch->result = result;
|
|
exdc_latch->dest = dest;
|
|
return vr4300_do_mci(vr4300, 5);
|
|
}
|
|
|
|
//
|
|
// FLOOR.w.fmt
|
|
//
|
|
int VR4300_CP1_FLOOR_W(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t fs, uint64_t ft) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
enum vr4300_fmt fmt = GET_FMT(iw);
|
|
unsigned dest = GET_FD(iw);
|
|
|
|
uint32_t fs32;
|
|
uint32_t result;
|
|
|
|
#ifndef CEN64_ARCH_HAS_FLOOR
|
|
fpu_state_t saved_state = fpu_get_state();
|
|
fpu_set_state((saved_state & ~FPU_ROUND_MASK) | FPU_ROUND_NEGINF);
|
|
|
|
switch (fmt) {
|
|
case VR4300_FMT_S:
|
|
fs32 = fs;
|
|
|
|
fpu_cvt_i32_f32(&fs32, &result);
|
|
break;
|
|
|
|
case VR4300_FMT_D:
|
|
fpu_cvt_i32_f64(&fs, &result);
|
|
break;
|
|
|
|
default:
|
|
VR4300_INV(vr4300);
|
|
return 1;
|
|
}
|
|
|
|
fpu_set_state((saved_state & FPU_ROUND_MASK) |
|
|
(fpu_get_state() & ~FPU_ROUND_MASK));
|
|
#else
|
|
switch (fmt) {
|
|
case VR4300_FMT_S:
|
|
fs32 = fs;
|
|
|
|
fpu_floor_i32_f32(&fs32, &result);
|
|
break;
|
|
|
|
case VR4300_FMT_D:
|
|
fpu_floor_i32_f64(&fs, &result);
|
|
break;
|
|
|
|
default:
|
|
VR4300_INV(vr4300);
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
exdc_latch->result = result;
|
|
exdc_latch->dest = dest;
|
|
return vr4300_do_mci(vr4300, 5);
|
|
}
|
|
|
|
//
|
|
// LDC1
|
|
//
|
|
// TODO/FIXME: Check for unaligned addresses.
|
|
//
|
|
int VR4300_LDC1(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t rs, uint64_t rt) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
unsigned dest = GET_FT(iw);
|
|
|
|
exdc_latch->request.vaddr = rs + (int16_t) iw;
|
|
exdc_latch->request.data = ~0ULL;
|
|
exdc_latch->request.wdqm = 0ULL;
|
|
exdc_latch->request.postshift = 0;
|
|
exdc_latch->request.access_type = VR4300_ACCESS_DWORD;
|
|
exdc_latch->request.type = VR4300_BUS_REQUEST_READ;
|
|
exdc_latch->request.size = 8;
|
|
|
|
exdc_latch->dest = dest;
|
|
exdc_latch->result = 0;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// LWC1
|
|
//
|
|
// TODO/FIXME: Check for unaligned addresses.
|
|
//
|
|
int VR4300_LWC1(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t rs, uint64_t ft) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
uint32_t status = vr4300->regs[VR4300_CP0_REGISTER_STATUS];
|
|
uint64_t address = (rs + (int16_t) iw);
|
|
unsigned dest = GET_FT(iw);
|
|
|
|
uint64_t result = 0;
|
|
unsigned postshift = 0;
|
|
|
|
if (!(status & 0x04000000)) {
|
|
result = dest & 0x1
|
|
? ft & 0x00000000FFFFFFFFULL
|
|
: ft & 0xFFFFFFFF00000000ULL;
|
|
|
|
postshift = (dest & 0x1) << 5;
|
|
dest &= ~0x1;
|
|
}
|
|
|
|
exdc_latch->request.vaddr = address;
|
|
exdc_latch->request.data = ~0U;
|
|
exdc_latch->request.wdqm = 0ULL;
|
|
exdc_latch->request.postshift = postshift;
|
|
exdc_latch->request.access_type = VR4300_ACCESS_WORD;
|
|
exdc_latch->request.type = VR4300_BUS_REQUEST_READ;
|
|
exdc_latch->request.size = 4;
|
|
|
|
exdc_latch->result = result;
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// MUL.fmt
|
|
//
|
|
int VR4300_CP1_MUL(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t fs, uint64_t ft) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
enum vr4300_fmt fmt = GET_FMT(iw);
|
|
unsigned dest = GET_FD(iw);
|
|
|
|
uint32_t fs32, ft32, fd32;
|
|
uint64_t result;
|
|
|
|
switch (fmt) {
|
|
case VR4300_FMT_S:
|
|
fs32 = fs;
|
|
ft32 = ft;
|
|
|
|
fpu_mul_32(&fs32, &ft32, &fd32);
|
|
result = fd32;
|
|
break;
|
|
|
|
case VR4300_FMT_D:
|
|
fpu_mul_64(&fs, &ft, &result);
|
|
break;
|
|
|
|
default:
|
|
VR4300_INV(vr4300);
|
|
return 1;
|
|
}
|
|
|
|
exdc_latch->result = result;
|
|
exdc_latch->dest = dest;
|
|
return vr4300_do_mci(vr4300,
|
|
fmt == VR4300_FMT_D ? 8 : 5);
|
|
}
|
|
|
|
//
|
|
// MFC1
|
|
//
|
|
int VR4300_MFC1(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t fs, uint64_t unused(rt)) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
uint32_t status = vr4300->regs[VR4300_CP0_REGISTER_STATUS];
|
|
unsigned dest = GET_RT(iw);
|
|
uint64_t result;
|
|
|
|
if (status & 0x04000000)
|
|
result = (int32_t) fs;
|
|
|
|
else {
|
|
result = (GET_FS(iw) & 0x1)
|
|
? (int32_t) (fs >> 32)
|
|
: (int32_t) (fs);
|
|
}
|
|
|
|
exdc_latch->result = result;
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// MOV.fmt
|
|
//
|
|
int VR4300_CP1_MOV(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t fs, uint64_t ft) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
unsigned dest = GET_FD(iw);
|
|
|
|
exdc_latch->result = fs;
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// MTC1
|
|
//
|
|
int VR4300_MTC1(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t fs, uint64_t rt) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
uint32_t status = vr4300->regs[VR4300_CP0_REGISTER_STATUS];
|
|
uint64_t result = (int32_t) rt;
|
|
unsigned dest = GET_FS(iw);
|
|
|
|
if (!(status & 0x04000000)) {
|
|
result = (dest & 0x1)
|
|
? ((uint32_t) fs) | (rt << 32)
|
|
: (fs & ~0xFFFFFFFFULL) | ((uint32_t) rt);
|
|
|
|
dest &= ~0x1;
|
|
}
|
|
|
|
exdc_latch->result = result;
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// NEG.fmt
|
|
//
|
|
int VR4300_CP1_NEG(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t fs, uint64_t ft) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
enum vr4300_fmt fmt = GET_FMT(iw);
|
|
unsigned dest = GET_FD(iw);
|
|
|
|
uint32_t fs32, fd32;
|
|
uint64_t result;
|
|
|
|
switch (fmt) {
|
|
case VR4300_FMT_S:
|
|
fs32 = fs;
|
|
|
|
fpu_neg_32(&fs32, &fd32);
|
|
result = fd32;
|
|
break;
|
|
|
|
case VR4300_FMT_D:
|
|
fpu_neg_64(&fs, &result);
|
|
break;
|
|
|
|
default:
|
|
VR4300_INV(vr4300);
|
|
return 1;
|
|
}
|
|
|
|
exdc_latch->result = result;
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// ROUND.l.fmt
|
|
//
|
|
int VR4300_CP1_ROUND_L(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t fs, uint64_t ft) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
enum vr4300_fmt fmt = GET_FMT(iw);
|
|
unsigned dest = GET_FD(iw);
|
|
|
|
uint32_t fs32;
|
|
uint64_t result;
|
|
|
|
#ifndef CEN64_ARCH_HAS_ROUND
|
|
fpu_state_t saved_state = fpu_get_state();
|
|
fpu_set_state((saved_state & ~FPU_ROUND_MASK) | FPU_ROUND_NEAREST);
|
|
|
|
switch (fmt) {
|
|
case VR4300_FMT_S:
|
|
fs32 = fs;
|
|
|
|
fpu_cvt_i64_f32(&fs32, &result);
|
|
break;
|
|
|
|
case VR4300_FMT_D:
|
|
fpu_cvt_i64_f64(&fs, &result);
|
|
break;
|
|
|
|
default:
|
|
VR4300_INV(vr4300);
|
|
return 1;
|
|
}
|
|
|
|
fpu_set_state((saved_state & FPU_ROUND_MASK) |
|
|
(fpu_get_state() & ~FPU_ROUND_MASK));
|
|
#else
|
|
switch (fmt) {
|
|
case VR4300_FMT_S:
|
|
fs32 = fs;
|
|
|
|
fpu_round_i64_f32(&fs32, &result);
|
|
break;
|
|
|
|
case VR4300_FMT_D:
|
|
fpu_round_i64_f64(&fs, &result);
|
|
break;
|
|
|
|
default:
|
|
VR4300_INV(vr4300);
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
exdc_latch->result = result;
|
|
exdc_latch->dest = dest;
|
|
return vr4300_do_mci(vr4300, 5);
|
|
}
|
|
|
|
//
|
|
// ROUND.w.fmt
|
|
//
|
|
int VR4300_CP1_ROUND_W(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t fs, uint64_t ft) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
enum vr4300_fmt fmt = GET_FMT(iw);
|
|
unsigned dest = GET_FD(iw);
|
|
|
|
uint32_t fs32;
|
|
uint32_t result;
|
|
|
|
#ifndef CEN64_ARCH_HAS_ROUND
|
|
fpu_state_t saved_state = fpu_get_state();
|
|
fpu_set_state((saved_state & ~FPU_ROUND_MASK) | FPU_ROUND_NEAREST);
|
|
|
|
switch (fmt) {
|
|
case VR4300_FMT_S:
|
|
fs32 = fs;
|
|
|
|
fpu_cvt_i32_f32(&fs32, &result);
|
|
break;
|
|
|
|
case VR4300_FMT_D:
|
|
fpu_cvt_i32_f64(&fs, &result);
|
|
break;
|
|
|
|
default:
|
|
VR4300_INV(vr4300);
|
|
return 1;
|
|
|
|
}
|
|
|
|
fpu_set_state((saved_state & FPU_ROUND_MASK) |
|
|
(fpu_get_state() & ~FPU_ROUND_MASK));
|
|
#else
|
|
switch (fmt) {
|
|
case VR4300_FMT_S:
|
|
fs32 = fs;
|
|
|
|
fpu_round_i32_f32(&fs32, &result);
|
|
break;
|
|
|
|
case VR4300_FMT_D:
|
|
fpu_round_i32_f64(&fs, &result);
|
|
break;
|
|
|
|
default:
|
|
VR4300_INV(vr4300);
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
exdc_latch->result = result;
|
|
exdc_latch->dest = dest;
|
|
return vr4300_do_mci(vr4300, 5);
|
|
}
|
|
|
|
//
|
|
// SDC1
|
|
//
|
|
// TODO/FIXME: Check for unaligned addresses.
|
|
//
|
|
int VR4300_SDC1(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t rs, uint64_t ft) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
|
|
exdc_latch->request.vaddr = rs + (int16_t) iw;
|
|
exdc_latch->request.data = ft;
|
|
exdc_latch->request.wdqm = ~0ULL;
|
|
exdc_latch->request.access_type = VR4300_ACCESS_DWORD;
|
|
exdc_latch->request.type = VR4300_BUS_REQUEST_WRITE;
|
|
exdc_latch->request.size = 8;
|
|
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// SQRT.fmt
|
|
//
|
|
int VR4300_CP1_SQRT(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t fs, uint64_t ft) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
enum vr4300_fmt fmt = GET_FMT(iw);
|
|
unsigned dest = GET_FD(iw);
|
|
|
|
uint32_t fs32, fd32;
|
|
uint64_t result;
|
|
|
|
switch (fmt) {
|
|
case VR4300_FMT_S:
|
|
fs32 = fs;
|
|
|
|
fpu_sqrt_32(&fs32, &fd32);
|
|
result = fd32;
|
|
break;
|
|
|
|
case VR4300_FMT_D:
|
|
fpu_sqrt_64(&fs, &result);
|
|
break;
|
|
|
|
default:
|
|
VR4300_INV(vr4300);
|
|
return 1;
|
|
}
|
|
|
|
exdc_latch->result = result;
|
|
exdc_latch->dest = dest;
|
|
return vr4300_do_mci(vr4300,
|
|
fmt == VR4300_FMT_D ? 58 : 29);
|
|
}
|
|
|
|
//
|
|
// SUB.fmt
|
|
//
|
|
int VR4300_CP1_SUB(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t fs, uint64_t ft) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
enum vr4300_fmt fmt = GET_FMT(iw);
|
|
unsigned dest = GET_FD(iw);
|
|
|
|
uint32_t fs32, ft32, fd32;
|
|
uint64_t result;
|
|
|
|
switch (fmt) {
|
|
case VR4300_FMT_S:
|
|
fs32 = fs;
|
|
ft32 = ft;
|
|
|
|
fpu_sub_32(&fs32, &ft32, &fd32);
|
|
result = fd32;
|
|
break;
|
|
|
|
case VR4300_FMT_D:
|
|
fpu_sub_64(&fs, &ft, &result);
|
|
break;
|
|
|
|
default:
|
|
VR4300_INV(vr4300);
|
|
return 1;
|
|
}
|
|
|
|
exdc_latch->result = result;
|
|
exdc_latch->dest = dest;
|
|
return vr4300_do_mci(vr4300, 3);
|
|
}
|
|
|
|
//
|
|
// SWC1
|
|
//
|
|
// TODO/FIXME: Check for unaligned addresses.
|
|
//
|
|
int VR4300_SWC1(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t rs, uint64_t ft) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
uint32_t status = vr4300->regs[VR4300_CP0_REGISTER_STATUS];
|
|
unsigned ft_reg = GET_FT(iw);
|
|
|
|
if (!(status & 0x04000000))
|
|
ft >>= ((ft_reg & 0x1) << 5);
|
|
|
|
exdc_latch->request.vaddr = rs + (int16_t) iw;
|
|
exdc_latch->request.data = ft;
|
|
exdc_latch->request.wdqm = ~0U;
|
|
exdc_latch->request.access_type = VR4300_ACCESS_WORD;
|
|
exdc_latch->request.type = VR4300_BUS_REQUEST_WRITE;
|
|
exdc_latch->request.size = 4;
|
|
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// TRUNC.l.fmt
|
|
//
|
|
int VR4300_CP1_TRUNC_L(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t fs, uint64_t ft) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
enum vr4300_fmt fmt = GET_FMT(iw);
|
|
unsigned dest = GET_FD(iw);
|
|
|
|
uint32_t fs32;
|
|
uint64_t result;
|
|
|
|
switch (fmt) {
|
|
case VR4300_FMT_S:
|
|
fs32 = fs;
|
|
|
|
fpu_trunc_i64_f32(&fs32, &result);
|
|
break;
|
|
|
|
case VR4300_FMT_D:
|
|
fpu_trunc_i64_f64(&fs, &result);
|
|
break;
|
|
|
|
default:
|
|
VR4300_INV(vr4300);
|
|
return 1;
|
|
}
|
|
|
|
exdc_latch->result = result;
|
|
exdc_latch->dest = dest;
|
|
return vr4300_do_mci(vr4300, 5);
|
|
}
|
|
|
|
//
|
|
// TRUNC.w.fmt
|
|
//
|
|
int VR4300_CP1_TRUNC_W(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t fs, uint64_t ft) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
enum vr4300_fmt fmt = GET_FMT(iw);
|
|
unsigned dest = GET_FD(iw);
|
|
|
|
uint32_t fs32;
|
|
uint32_t result;
|
|
|
|
switch (fmt) {
|
|
case VR4300_FMT_S:
|
|
fs32 = fs;
|
|
|
|
fpu_trunc_i32_f32(&fs32, &result);
|
|
break;
|
|
|
|
case VR4300_FMT_D:
|
|
fpu_trunc_i32_f64(&fs, &result);
|
|
break;
|
|
|
|
default:
|
|
VR4300_INV(vr4300);
|
|
return 1;
|
|
}
|
|
|
|
exdc_latch->result = result;
|
|
exdc_latch->dest = dest;
|
|
return vr4300_do_mci(vr4300, 5);
|
|
}
|
|
|
|
// Initializes the coprocessor.
|
|
void vr4300_cp1_init(struct vr4300 *vr4300) {
|
|
fpu_set_state(FPU_ROUND_NEAREST | FPU_MASK_EXCPS);
|
|
vr4300->regs[VR4300_CP1_FCR0] = 0xa00; // hardcoded fpu version of both 0xb22 and 0xb10 N64s
|
|
}
|