mirror of
https://github.com/n64dev/cen64.git
synced 2024-06-23 14:33:13 -04:00
633 lines
16 KiB
C
633 lines
16 KiB
C
//
|
|
// rsp/functions.c: RSP execution functions.
|
|
//
|
|
// 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.
|
|
//
|
|
|
|
#define RSP_BUILD_OP(op, func, flags) \
|
|
(RSP_##func)
|
|
|
|
#include "common.h"
|
|
#include "bus/controller.h"
|
|
#include "rsp/cp0.h"
|
|
#include "rsp/cpu.h"
|
|
#include "rsp/decoder.h"
|
|
#include "rsp/opcodes.h"
|
|
#include "rsp/opcodes_priv.h"
|
|
#include "rsp/pipeline.h"
|
|
#include "vr4300/interface.h"
|
|
|
|
// Mask to negate second operand if subtract operation.
|
|
#if defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__))
|
|
static inline uint32_t rsp_addsub_mask(uint32_t iw)
|
|
{
|
|
uint32_t mask;
|
|
__asm__("shr $2, %k[iwiw];"
|
|
"sbb %k[mask], %k[mask];"
|
|
: [mask] "=r" (mask), [iwiw] "+r" (iw) : : "cc");
|
|
return mask;
|
|
}
|
|
#else
|
|
cen64_align(static const uint32_t rsp_addsub_lut[4], 16) = {
|
|
0x0U, ~0x0U, ~0x0U, ~0x0U
|
|
};
|
|
static inline uint32_t rsp_addsub_mask(uint32_t iw)
|
|
{
|
|
return rsp_addsub_lut[iw & 0x2];
|
|
}
|
|
#endif
|
|
|
|
// Mask to denote which part of the vector to load/store.
|
|
cen64_align(static const uint16_t rsp_bdls_lut[2][4][4], CACHE_LINE_SIZE) = {
|
|
{
|
|
{0x00FF, 0x0000, 0x0000, 0x0000}, // B
|
|
{0xFFFF, 0x0000, 0x0000, 0x0000}, // S
|
|
{0xFFFF, 0xFFFF, 0x0000, 0x0000}, // L
|
|
{0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF}, // D
|
|
},
|
|
|
|
{
|
|
{0xFF00, 0x0000, 0x0000, 0x0000}, // B
|
|
{0xFFFF, 0x0000, 0x0000, 0x0000}, // S
|
|
{0xFFFF, 0xFFFF, 0x0000, 0x0000}, // L
|
|
{0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF}, // D
|
|
}
|
|
};
|
|
|
|
cen64_align(static const uint16_t rsp_qr_lut[16][8], CACHE_LINE_SIZE) = {
|
|
{0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF},
|
|
{0xFF00, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF},
|
|
{0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF},
|
|
{0x0000, 0xFF00, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF},
|
|
|
|
{0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF},
|
|
{0x0000, 0x0000, 0xFF00, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF},
|
|
{0x0000, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF},
|
|
{0x0000, 0x0000, 0x0000, 0xFF00, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF},
|
|
|
|
{0x0000, 0x0000, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF},
|
|
{0x0000, 0x0000, 0x0000, 0x0000, 0xFF00, 0xFFFF, 0xFFFF, 0xFFFF},
|
|
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF},
|
|
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFF00, 0xFFFF, 0xFFFF},
|
|
|
|
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFFFF, 0xFFFF},
|
|
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFF00, 0xFFFF},
|
|
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFFFF},
|
|
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFF00}
|
|
};
|
|
|
|
// Mask to select link address register for some branches.
|
|
static inline uint32_t rsp_branch_mask(uint32_t iw, unsigned index) {
|
|
iw = (uint32_t)( (int32_t)(iw << (31 - index)) >> 31 );
|
|
return ~iw; /* ones' complement must be done last on return */
|
|
}
|
|
|
|
// Mask to selectively sign-extend loaded values.
|
|
cen64_align(static const uint32_t rsp_load_sex_mask[8], 32) = {
|
|
~0U, ~0U, 0U, ~0U, // sex
|
|
0xFFU, 0xFFFFU, 0U, ~0U, // zex
|
|
};
|
|
|
|
// Function to sign-extend 6-bit values.
|
|
static inline unsigned sign_extend_6(int i) {
|
|
return (i << (32 - 7)) >> (32 - 7);
|
|
}
|
|
|
|
//
|
|
// ADDIU
|
|
// LUI
|
|
// SUBIU
|
|
//
|
|
void RSP_ADDIU_LUI_SUBIU(struct rsp *rsp,
|
|
uint32_t iw, uint32_t rs, uint32_t rt) {
|
|
struct rsp_exdf_latch *exdf_latch = &rsp->pipeline.exdf_latch;
|
|
unsigned immshift = iw >> 24 & 0x10;
|
|
unsigned dest;
|
|
|
|
dest = GET_RT(iw);
|
|
|
|
rt = (int16_t) iw;
|
|
rt = rs + (rt << immshift);
|
|
|
|
exdf_latch->result.result = rt;
|
|
exdf_latch->result.dest = dest;
|
|
}
|
|
|
|
//
|
|
// ADDU
|
|
// SUBU
|
|
//
|
|
void RSP_ADDU_SUBU(struct rsp *rsp,
|
|
uint32_t iw, uint32_t rs, uint32_t rt) {
|
|
struct rsp_exdf_latch *exdf_latch = &rsp->pipeline.exdf_latch;
|
|
uint32_t mask = rsp_addsub_mask(iw);
|
|
|
|
unsigned dest;
|
|
uint32_t rd;
|
|
|
|
dest = GET_RD(iw);
|
|
rt = (rt ^ mask) - mask;
|
|
rd = rs + rt;
|
|
|
|
exdf_latch->result.result = rd;
|
|
exdf_latch->result.dest = dest;
|
|
}
|
|
|
|
//
|
|
// AND
|
|
// OR
|
|
// XOR
|
|
//
|
|
void RSP_AND_OR_XOR(struct rsp *rsp,
|
|
uint32_t iw, uint32_t rs, uint32_t rt) {
|
|
struct rsp_exdf_latch *exdf_latch = &rsp->pipeline.exdf_latch;
|
|
|
|
unsigned dest;
|
|
uint32_t rd, rand, rxor;
|
|
|
|
dest = GET_RD(iw);
|
|
rand = rs & rt;
|
|
rxor = rs ^ rt;
|
|
rd = rand + rxor; // lea
|
|
if((iw & 1) == 0) // cmov
|
|
rd = rxor;
|
|
if((iw & 3) == 0) // cmov
|
|
rd = rand;
|
|
|
|
exdf_latch->result.result = rd;
|
|
exdf_latch->result.dest = dest;
|
|
}
|
|
|
|
//
|
|
// ANDI
|
|
// ORI
|
|
// XORI
|
|
//
|
|
void RSP_ANDI_ORI_XORI(struct rsp *rsp,
|
|
uint32_t iw, uint32_t rs, uint32_t rt) {
|
|
struct rsp_exdf_latch *exdf_latch = &rsp->pipeline.exdf_latch;
|
|
|
|
unsigned dest;
|
|
uint32_t rd, rand, rxor;
|
|
|
|
dest = GET_RT(iw);
|
|
rt = (uint16_t) iw;
|
|
rand = rs & rt;
|
|
rxor = rs ^ rt;
|
|
rd = rand + rxor; // lea
|
|
if((iw & 67108864) == 0) // cmov
|
|
rd = rxor;
|
|
if((iw & 201326592) == 0) // cmov
|
|
rd = rand;
|
|
|
|
exdf_latch->result.result = rd;
|
|
exdf_latch->result.dest = dest;
|
|
}
|
|
|
|
//
|
|
// BEQ
|
|
// BNE
|
|
//
|
|
// TODO: Adjust branch target for RSP IMEM range.
|
|
//
|
|
void RSP_BEQ_BNE(struct rsp *rsp,
|
|
uint32_t iw, uint32_t rs, uint32_t rt) {
|
|
struct rsp_ifrd_latch *ifrd_latch = &rsp->pipeline.ifrd_latch;
|
|
struct rsp_rdex_latch *rdex_latch = &rsp->pipeline.rdex_latch;
|
|
uint32_t offset = (uint32_t) ((int16_t) iw) << 2;
|
|
|
|
bool is_ne = iw >> 26 & 0x1;
|
|
bool cmp = rs == rt;
|
|
|
|
if (cmp == is_ne)
|
|
return;
|
|
|
|
ifrd_latch->pc = (rdex_latch->common.pc + offset + 4) & 0xFFC;
|
|
}
|
|
|
|
//
|
|
// BGEZ
|
|
// BLTZ
|
|
//
|
|
// TODO: Adjust branch target for RSP IMEM range.
|
|
//
|
|
void RSP_BGEZ_BLTZ(
|
|
struct rsp *rsp,
|
|
uint32_t iw, uint32_t rs, uint32_t unused(rt)) {
|
|
struct rsp_ifrd_latch *ifrd_latch = &rsp->pipeline.ifrd_latch;
|
|
struct rsp_rdex_latch *rdex_latch = &rsp->pipeline.rdex_latch;
|
|
uint32_t offset = (uint32_t) ((int16_t) iw) << 2;
|
|
|
|
bool is_ge = iw >> 16 & 0x1;
|
|
bool cmp = (int32_t) rs < 0;
|
|
|
|
if (cmp == is_ge)
|
|
return;
|
|
|
|
ifrd_latch->pc = (rdex_latch->common.pc + offset + 4) & 0xFFC;
|
|
}
|
|
|
|
//
|
|
// BGEZAL
|
|
// BLTZAL
|
|
//
|
|
// TODO: Adjust branch target for RSP IMEM range.
|
|
//
|
|
void RSP_BGEZAL_BLTZAL(
|
|
struct rsp *rsp, uint32_t iw, uint32_t rs, uint32_t unused(rt)) {
|
|
struct rsp_ifrd_latch *ifrd_latch = &rsp->pipeline.ifrd_latch;
|
|
struct rsp_rdex_latch *rdex_latch = &rsp->pipeline.rdex_latch;
|
|
struct rsp_exdf_latch *exdf_latch = &rsp->pipeline.exdf_latch;
|
|
uint32_t offset = (uint32_t) ((int16_t) iw) << 2;
|
|
|
|
bool is_ge = iw >> 16 & 0x1;
|
|
bool cmp = (int32_t) rs < 0;
|
|
|
|
exdf_latch->result.result = rdex_latch->common.pc + 8;
|
|
exdf_latch->result.dest = RSP_REGISTER_RA;
|
|
|
|
if (cmp == is_ge)
|
|
return;
|
|
|
|
ifrd_latch->pc = (rdex_latch->common.pc + offset + 4) & 0xFFC;
|
|
}
|
|
|
|
//
|
|
// BGTZ
|
|
// BLEZ
|
|
//
|
|
// TODO: Adjust branch target for RSP IMEM range.
|
|
//
|
|
void RSP_BGTZ_BLEZ(
|
|
struct rsp *rsp, uint32_t iw, uint32_t rs, uint32_t unused(rt)) {
|
|
struct rsp_ifrd_latch *ifrd_latch = &rsp->pipeline.ifrd_latch;
|
|
struct rsp_rdex_latch *rdex_latch = &rsp->pipeline.rdex_latch;
|
|
uint32_t offset = (uint32_t) ((int16_t) iw) << 2;
|
|
|
|
bool is_gt = iw >> 26 & 0x1;
|
|
bool cmp = (int32_t) rs <= 0;
|
|
|
|
if (cmp == is_gt)
|
|
return;
|
|
|
|
ifrd_latch->pc = (rdex_latch->common.pc + offset + 4) & 0xFFC;
|
|
}
|
|
|
|
//
|
|
// BREAK
|
|
//
|
|
void RSP_BREAK(struct rsp *rsp,
|
|
uint32_t iw, uint32_t rs, uint32_t rt) {
|
|
struct rsp_exdf_latch *exdf_latch = &rsp->pipeline.exdf_latch;
|
|
|
|
uint32_t result = rsp->regs[RSP_CP0_REGISTER_SP_STATUS] |
|
|
(SP_STATUS_HALT | SP_STATUS_BROKE);
|
|
|
|
if (rsp->regs[RSP_CP0_REGISTER_SP_STATUS] & SP_STATUS_INTR_BREAK)
|
|
signal_rcp_interrupt(rsp->bus->vr4300, MI_INTR_SP);
|
|
|
|
exdf_latch->result.dest = RSP_CP0_REGISTER_SP_STATUS;
|
|
exdf_latch->result.result = result;
|
|
}
|
|
|
|
//
|
|
// LB
|
|
// LBU
|
|
// LH
|
|
// LHU
|
|
// LW
|
|
// SB
|
|
// SH
|
|
// SW
|
|
//
|
|
cen64_hot void RSP_INT_MEM(struct rsp *rsp,
|
|
uint32_t iw, uint32_t rs, uint32_t rt) {
|
|
struct rsp_exdf_latch *exdf_latch = &rsp->pipeline.exdf_latch;
|
|
|
|
uint32_t address = rs + (int16_t) iw;
|
|
uint32_t sel_mask = (int32_t) (iw << 2) >> 31;
|
|
unsigned request_index = (iw >> 26 & 0x7);
|
|
uint32_t rdqm = rsp_load_sex_mask[request_index];
|
|
unsigned request_size = request_index & 0x3;
|
|
unsigned lshiftamt = (3 - request_size) << 3;
|
|
uint32_t wdqm = ~0U << lshiftamt;
|
|
|
|
exdf_latch->request.addr = address;
|
|
exdf_latch->request.packet.p_int.data = rt << lshiftamt;
|
|
exdf_latch->request.packet.p_int.rdqm = rdqm;
|
|
exdf_latch->request.type = RSP_MEM_REQUEST_INT_MEM;
|
|
exdf_latch->request.packet.p_int.rshift = lshiftamt;
|
|
exdf_latch->request.packet.p_int.wdqm = sel_mask & wdqm;
|
|
|
|
exdf_latch->result.dest = ~sel_mask & GET_RT(iw);
|
|
}
|
|
|
|
//
|
|
// INVALID
|
|
//
|
|
void RSP_INVALID(struct rsp *rsp,
|
|
uint32_t iw, uint32_t unused(rs), uint32_t unused(rt)) {
|
|
#ifndef NDEBUG
|
|
struct rsp_rdex_latch *rdex_latch = &rsp->pipeline.rdex_latch;
|
|
|
|
debug("Unimplemented instruction: %s [0x%.8X] @ 0x%.8X\n",
|
|
rsp_opcode_mnemonics[rdex_latch->opcode.id],
|
|
iw, rdex_latch->common.pc);
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// J
|
|
// JAL
|
|
//
|
|
void RSP_J_JAL(struct rsp *rsp,
|
|
uint32_t iw, uint32_t rs, uint32_t rt) {
|
|
struct rsp_ifrd_latch *ifrd_latch = &rsp->pipeline.ifrd_latch;
|
|
struct rsp_rdex_latch *rdex_latch = &rsp->pipeline.rdex_latch;
|
|
struct rsp_exdf_latch *exdf_latch = &rsp->pipeline.exdf_latch;
|
|
|
|
uint32_t target = iw << 2 & 0xFFC;
|
|
uint32_t mask = rsp_branch_mask(iw, 26); //is_jal
|
|
|
|
exdf_latch->result.result = (rdex_latch->common.pc + 8) & 0xFFC;
|
|
exdf_latch->result.dest = RSP_REGISTER_RA & ~mask;
|
|
|
|
ifrd_latch->pc = target;
|
|
}
|
|
|
|
//
|
|
// JALR
|
|
// JR
|
|
//
|
|
void RSP_JALR_JR(struct rsp *rsp,
|
|
uint32_t iw, uint32_t rs, uint32_t unused(rt)) {
|
|
struct rsp_ifrd_latch *ifrd_latch = &rsp->pipeline.ifrd_latch;
|
|
struct rsp_rdex_latch *rdex_latch = &rsp->pipeline.rdex_latch;
|
|
struct rsp_exdf_latch *exdf_latch = &rsp->pipeline.exdf_latch;
|
|
|
|
uint32_t mask = rsp_branch_mask(iw, 0); // is_jalr
|
|
unsigned rd = GET_RD(iw);
|
|
|
|
exdf_latch->result.result = (rdex_latch->common.pc + 8) & 0xFFC;
|
|
exdf_latch->result.dest = rd & ~mask;
|
|
|
|
ifrd_latch->pc = rs & 0xFFC;
|
|
}
|
|
|
|
//
|
|
// LBV
|
|
// LDV
|
|
// LLV
|
|
// LSV
|
|
// SBV
|
|
// SDV
|
|
// SLV
|
|
// SSV
|
|
//
|
|
void RSP_LBDLSV_SBDLSV(struct rsp *rsp,
|
|
uint32_t iw, uint32_t rs, uint32_t rt) {
|
|
struct rsp_exdf_latch *exdf_latch = &rsp->pipeline.exdf_latch;
|
|
unsigned shift_and_idx = iw >> 11 & 0x3;
|
|
unsigned op = iw >> 29 & 0x1;
|
|
unsigned dest = GET_VT(iw);
|
|
|
|
exdf_latch->request.addr = rs + (sign_extend_6(iw) << shift_and_idx);
|
|
|
|
__m128i vdqm = _mm_loadl_epi64((__m128i *) (rsp_bdls_lut[op][shift_and_idx]));
|
|
_mm_store_si128((__m128i *) exdf_latch->request.packet.p_vect.vdqm.e, vdqm);
|
|
|
|
exdf_latch->request.packet.p_vect.element = GET_EL(iw);
|
|
exdf_latch->request.type = RSP_MEM_REQUEST_VECTOR;
|
|
exdf_latch->request.packet.p_vect.vldst_func = op
|
|
? rsp_vstore_group1
|
|
: rsp_vload_group1;
|
|
|
|
exdf_latch->request.packet.p_vect.dest = dest;
|
|
}
|
|
|
|
//
|
|
// LPV
|
|
// LUV
|
|
// SPV
|
|
// SUV
|
|
//
|
|
void RSP_LFHPUV_SFHPUV(struct rsp *rsp,
|
|
uint32_t iw, uint32_t rs, uint32_t rt) {
|
|
struct rsp_exdf_latch *exdf_latch = &rsp->pipeline.exdf_latch;
|
|
unsigned dest = GET_VT(iw);
|
|
|
|
static const enum rsp_mem_request_type fhpu_type_lut[4] = {
|
|
RSP_MEM_REQUEST_PACK,
|
|
RSP_MEM_REQUEST_UPACK,
|
|
RSP_MEM_REQUEST_HALF,
|
|
RSP_MEM_REQUEST_FOURTH
|
|
};
|
|
|
|
exdf_latch->request.addr = rs + (sign_extend_6(iw) << 3);
|
|
|
|
// memcpy(&exdf_latch->request.vdqm.e,
|
|
// rsp_fhpu_lut[exdf_latch->request.addr & 0xF],
|
|
// sizeof(exdf_latch->request.vdqm.e));
|
|
|
|
exdf_latch->request.type = fhpu_type_lut[(iw >> 11 & 0x1F) - 6];
|
|
exdf_latch->request.packet.p_vect.vldst_func = (iw >> 29 & 0x1)
|
|
? rsp_vstore_group2
|
|
: rsp_vload_group2;
|
|
|
|
exdf_latch->request.packet.p_vect.dest = dest;
|
|
}
|
|
|
|
//
|
|
// LQV
|
|
// LRV
|
|
// SQV
|
|
// SRV
|
|
//
|
|
void RSP_LQRV_SQRV(struct rsp *rsp,
|
|
uint32_t iw, uint32_t rs, uint32_t rt) {
|
|
struct rsp_exdf_latch *exdf_latch = &rsp->pipeline.exdf_latch;
|
|
unsigned op = iw >> 29 & 0x1;
|
|
unsigned dest = GET_VT(iw);
|
|
|
|
exdf_latch->request.addr = rs + (sign_extend_6(iw) << 4);
|
|
|
|
memcpy(exdf_latch->request.packet.p_vect.vdqm.e,
|
|
rsp_qr_lut[exdf_latch->request.addr & 0xF],
|
|
sizeof(exdf_latch->request.packet.p_vect.vdqm.e));
|
|
|
|
exdf_latch->request.packet.p_vect.element = GET_EL(iw);
|
|
exdf_latch->request.type = (iw >> 11 & 0x1)
|
|
? RSP_MEM_REQUEST_REST
|
|
: RSP_MEM_REQUEST_QUAD;
|
|
|
|
exdf_latch->request.packet.p_vect.vldst_func = op
|
|
? rsp_vstore_group4
|
|
: rsp_vload_group4;
|
|
|
|
exdf_latch->request.packet.p_vect.dest = dest;
|
|
}
|
|
|
|
//
|
|
// LTV
|
|
// STV
|
|
//
|
|
void RSP_LTV_STV(struct rsp *rsp,
|
|
uint32_t iw, uint32_t rs, uint32_t rt) {
|
|
struct rsp_exdf_latch *exdf_latch = &rsp->pipeline.exdf_latch;
|
|
unsigned op = iw >> 29 & 0x1;
|
|
|
|
exdf_latch->request.addr = rs + (sign_extend_6(iw) << 4);
|
|
exdf_latch->request.type = RSP_MEM_REQUEST_TRANSPOSE;
|
|
|
|
exdf_latch->request.packet.p_transpose.vt = GET_VT(iw) & 0x18;
|
|
exdf_latch->request.packet.p_transpose.element = GET_EL(iw) >> 1;
|
|
|
|
exdf_latch->request.packet.p_transpose.transpose_func = op
|
|
? rsp_stv
|
|
: rsp_ltv;
|
|
}
|
|
|
|
//
|
|
// NOR
|
|
//
|
|
void RSP_NOR(struct rsp *rsp,
|
|
uint32_t iw, uint32_t rs, uint32_t rt) {
|
|
struct rsp_exdf_latch *exdf_latch = &rsp->pipeline.exdf_latch;
|
|
unsigned dest = GET_RD(iw);
|
|
|
|
exdf_latch->result.result = ~(rs | rt);
|
|
exdf_latch->result.dest = dest;
|
|
}
|
|
|
|
//
|
|
// SLL
|
|
// SLLV
|
|
//
|
|
void RSP_SLL_SLLV(struct rsp *rsp,
|
|
uint32_t iw, uint32_t rs, uint32_t rt) {
|
|
struct rsp_exdf_latch *exdf_latch = &rsp->pipeline.exdf_latch;
|
|
|
|
unsigned dest = GET_RD(iw);
|
|
unsigned sa = (rs & 0x1F) + (iw >> 6 & 0x1F);
|
|
|
|
exdf_latch->result.result = rt << sa;
|
|
exdf_latch->result.dest = dest;
|
|
}
|
|
|
|
//
|
|
// SLT
|
|
//
|
|
void RSP_SLT(struct rsp *rsp,
|
|
uint32_t iw, uint32_t rs, uint32_t rt) {
|
|
struct rsp_exdf_latch *exdf_latch = &rsp->pipeline.exdf_latch;
|
|
unsigned dest = GET_RD(iw);
|
|
|
|
exdf_latch->result.result = (int32_t) rs < (int32_t) rt;
|
|
exdf_latch->result.dest = dest;
|
|
}
|
|
|
|
//
|
|
// SLTI
|
|
//
|
|
void RSP_SLTI(struct rsp *rsp,
|
|
uint32_t iw, uint32_t rs, uint32_t rt) {
|
|
struct rsp_exdf_latch *exdf_latch = &rsp->pipeline.exdf_latch;
|
|
|
|
unsigned dest = GET_RT(iw);
|
|
int32_t imm = (int16_t) iw;
|
|
|
|
exdf_latch->result.result = (int32_t) rs < imm;
|
|
exdf_latch->result.dest = dest;
|
|
}
|
|
|
|
//
|
|
// SLTIU
|
|
//
|
|
void RSP_SLTIU(struct rsp *rsp,
|
|
uint32_t iw, uint32_t rs, uint32_t rt) {
|
|
struct rsp_exdf_latch *exdf_latch = &rsp->pipeline.exdf_latch;
|
|
|
|
unsigned dest = GET_RT(iw);
|
|
uint32_t imm = (int16_t) iw;
|
|
|
|
exdf_latch->result.result = rs < imm;
|
|
exdf_latch->result.dest = dest;
|
|
}
|
|
|
|
//
|
|
// SLTU
|
|
//
|
|
void RSP_SLTU(struct rsp *rsp,
|
|
uint32_t iw, uint32_t rs, uint32_t rt) {
|
|
struct rsp_exdf_latch *exdf_latch = &rsp->pipeline.exdf_latch;
|
|
unsigned dest = GET_RD(iw);
|
|
|
|
exdf_latch->result.result = rs < rt;
|
|
exdf_latch->result.dest = dest;
|
|
}
|
|
|
|
//
|
|
// SRA
|
|
//
|
|
void RSP_SRA(struct rsp *rsp,
|
|
uint32_t iw, uint32_t unused(rs), uint32_t rt) {
|
|
struct rsp_exdf_latch *exdf_latch = &rsp->pipeline.exdf_latch;
|
|
unsigned dest = GET_RD(iw);
|
|
unsigned sa = iw >> 6 & 0x1F;
|
|
|
|
exdf_latch->result.result = (int32_t) rt >> sa;
|
|
exdf_latch->result.dest = dest;
|
|
}
|
|
|
|
//
|
|
// SRAV
|
|
//
|
|
void RSP_SRAV(struct rsp *rsp,
|
|
uint32_t iw, uint32_t rs, uint32_t rt) {
|
|
struct rsp_exdf_latch *exdf_latch = &rsp->pipeline.exdf_latch;
|
|
unsigned dest = GET_RD(iw);
|
|
unsigned sa = rs & 0x1F;
|
|
|
|
exdf_latch->result.result = (int32_t) rt >> sa;
|
|
exdf_latch->result.dest = dest;
|
|
}
|
|
|
|
//
|
|
// SRL
|
|
//
|
|
void RSP_SRL(struct rsp *rsp,
|
|
uint32_t iw, uint32_t unused(rs), uint32_t rt) {
|
|
struct rsp_exdf_latch *exdf_latch = &rsp->pipeline.exdf_latch;
|
|
unsigned dest = GET_RD(iw);
|
|
unsigned sa = iw >> 6 & 0x1F;
|
|
|
|
exdf_latch->result.result = rt >> sa;
|
|
exdf_latch->result.dest = dest;
|
|
}
|
|
|
|
//
|
|
// SRLV
|
|
//
|
|
void RSP_SRLV(struct rsp *rsp,
|
|
uint32_t iw, uint32_t rs, uint32_t rt) {
|
|
struct rsp_exdf_latch *exdf_latch = &rsp->pipeline.exdf_latch;
|
|
unsigned dest = GET_RD(iw);
|
|
unsigned sa = rs & 0x1F;
|
|
|
|
exdf_latch->result.result = rt >> sa;
|
|
exdf_latch->result.dest = dest;
|
|
}
|
|
|
|
// Function lookup table.
|
|
cen64_align(const rsp_function
|
|
rsp_function_table[NUM_RSP_OPCODES], CACHE_LINE_SIZE) = {
|
|
#define X(op) op,
|
|
#include "rsp/opcodes.md"
|
|
#undef X
|
|
};
|
|
|