mirror of
https://github.com/n64dev/cen64.git
synced 2025-04-02 10:31:54 -04:00
Replaced all references to simulation with emulation Updated copyright year Updated .gitignore to reduce chances of random files being uploaded to the repo Added .gitattributes to normalize all text files, and to ignore binary files (which includes the logo and the NEC PDF)
1404 lines
33 KiB
C
1404 lines
33 KiB
C
//
|
|
// vr4300/functions.c: VR4300 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 VR4300_BUILD_OP(op, func, flags) \
|
|
(VR4300_##func)
|
|
|
|
#include "common.h"
|
|
#include "bus/controller.h"
|
|
#include "vr4300/cp0.h"
|
|
#include "vr4300/cp1.h"
|
|
#include "vr4300/cpu.h"
|
|
#include "vr4300/decoder.h"
|
|
#include "vr4300/opcodes.h"
|
|
#include "vr4300/opcodes_priv.h"
|
|
#include "vr4300/pipeline.h"
|
|
#include "vr4300/segment.h"
|
|
|
|
// Mask to negate second operand if subtract operation.
|
|
cen64_align(static const uint64_t vr4300_addsub_lut[4], 32) = {
|
|
0x0ULL, ~0x0ULL, ~0x0ULL, ~0x0ULL
|
|
};
|
|
|
|
// Mask to select outputs for bitwise operations.
|
|
cen64_align(static const uint64_t vr4300_bitwise_lut[4][2], 64) = {
|
|
{~0ULL, 0ULL}, // AND
|
|
{~0ULL, ~0ULL}, // OR
|
|
{ 0ULL, ~0ULL}, // XOR
|
|
{ 0ULL, 0ULL}, // -
|
|
};
|
|
|
|
// Mask to kill the instruction word if "likely" branch.
|
|
cen64_align(static const uint32_t vr4300_branch_lut[2], 8) = {
|
|
~0U, 0U
|
|
};
|
|
|
|
// Mask to selectively sign-extend compute values.
|
|
cen64_align(static const uint64_t vr4300_mult_sex_mask[2], 16) = {
|
|
~0ULL, ~0ULL >> 32
|
|
};
|
|
|
|
// Mask to selectively sign-extend loaded values.
|
|
cen64_align(static const uint64_t vr4300_load_sex_mask[2][4], CACHE_LINE_SIZE) = {
|
|
{~0ULL, ~0ULL, 0ULL, ~0ULL}, // sex
|
|
{0xFFULL, 0xFFFFULL, 0ULL, 0xFFFFFFFFULL}, // zex
|
|
};
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
//
|
|
// ADD
|
|
// SUB
|
|
//
|
|
int VR4300_ADD_SUB(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t rs, uint64_t rt) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
uint64_t mask = vr4300_addsub_lut[iw & 0x2];
|
|
|
|
unsigned dest;
|
|
uint64_t rd;
|
|
|
|
dest = GET_RD(iw);
|
|
rt = (rt ^ mask) - mask;
|
|
rd = rs + rt;
|
|
|
|
// TODO/FIXME: Uncomment this later...
|
|
//assert(((rd >> 31) == (rd >> 32)) && "Overflow exception.");
|
|
|
|
exdc_latch->result = (int32_t) rd;
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// ADDI
|
|
// SUBI
|
|
//
|
|
int VR4300_ADDI_SUBI(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t rs, uint64_t rt) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
uint64_t mask = 0; //vr4300_addsub_lut[iw & 0x1];
|
|
|
|
unsigned dest;
|
|
|
|
dest = GET_RT(iw);
|
|
rt = (int16_t) iw;
|
|
rt = (rt ^ mask) - mask;
|
|
rt = rs + rt;
|
|
|
|
// TODO/FIXME: Uncomment this later...
|
|
//assert(((rt >> 31) == (rt >> 32)) && "Overflow exception.");
|
|
|
|
exdc_latch->result = (int32_t) rt;
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// ADDIU
|
|
// LUI
|
|
// SUBIU
|
|
//
|
|
int VR4300_ADDIU_LUI_SUBIU(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t rs, uint64_t rt) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
unsigned immshift = iw >> 24 & 0x10;
|
|
unsigned dest;
|
|
|
|
dest = GET_RT(iw);
|
|
|
|
rt = (int16_t) iw;
|
|
rt = rs + (rt << immshift);
|
|
|
|
exdc_latch->result = (int32_t) rt;
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// ADDU
|
|
// SUBU
|
|
//
|
|
int VR4300_ADDU_SUBU(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t rs, uint64_t rt) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
uint64_t mask = vr4300_addsub_lut[iw & 0x2];
|
|
|
|
unsigned dest;
|
|
uint64_t rd;
|
|
|
|
dest = GET_RD(iw);
|
|
rt = (rt ^ mask) - mask;
|
|
rd = rs + rt;
|
|
|
|
exdc_latch->result = (int32_t) rd;
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// AND
|
|
// OR
|
|
// XOR
|
|
//
|
|
int VR4300_AND_OR_XOR(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t rs, uint64_t rt) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
uint64_t and_mask = vr4300_bitwise_lut[iw & 0x3][0];
|
|
uint64_t xor_mask = vr4300_bitwise_lut[iw & 0x3][1];
|
|
|
|
unsigned dest;
|
|
uint64_t rd;
|
|
|
|
dest = GET_RD(iw);
|
|
rd = ((rs & rt) & and_mask) | ((rs ^ rt) & xor_mask);
|
|
|
|
exdc_latch->result = rd;
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// ANDI
|
|
// ORI
|
|
// XORI
|
|
//
|
|
int VR4300_ANDI_ORI_XORI(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t rs, uint64_t rt) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
uint64_t and_mask = vr4300_bitwise_lut[iw >> 26 & 0x3][0];
|
|
uint64_t xor_mask = vr4300_bitwise_lut[iw >> 26 & 0x3][1];
|
|
|
|
unsigned dest;
|
|
|
|
dest = GET_RT(iw);
|
|
rt = (uint16_t) iw;
|
|
rt = ((rs & rt) & and_mask) | ((rs ^ rt) & xor_mask);
|
|
|
|
exdc_latch->result = rt;
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
#ifdef VR4300_BUSY_WAIT_DETECTION
|
|
//
|
|
// BEQ
|
|
// BEQL
|
|
// BNE
|
|
// BNEL
|
|
//
|
|
// If VR4300_BUSY_WAIT_DETECTOR is defined, this version of BEQ
|
|
// is used. Otherwise, the version below is used. The reason we
|
|
// have this BEQ is to detect libultra's busy wait loops:
|
|
//
|
|
// beq $0, $0, -4
|
|
// nop
|
|
//
|
|
// When we detect such loops in cached memory, the pipeline
|
|
// simulation mostly shuts down and goes into an accelerated mode
|
|
// where fewer checks are made (to boost performance).
|
|
//
|
|
// XXX: Ensure we're in cached memory AND we're followed by a NOP.
|
|
// If we don't do both of these things, we could get ourselves
|
|
// into trouble.
|
|
//
|
|
int VR4300_BEQ_BEQL_BNE_BNEL_BWDETECT(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t rs, uint64_t rt) {
|
|
struct vr4300_icrf_latch *icrf_latch = &vr4300->pipeline.icrf_latch;
|
|
struct vr4300_rfex_latch *rfex_latch = &vr4300->pipeline.rfex_latch;
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
uint32_t mask = vr4300_branch_lut[iw >> 30 & 0x1];
|
|
uint64_t offset = (uint64_t) ((int16_t) iw) << 2;
|
|
|
|
bool is_ne = iw >> 26 & 0x1;
|
|
bool cmp = rs == rt;
|
|
|
|
if (cmp == is_ne) {
|
|
rfex_latch->iw_mask = mask;
|
|
return 0;
|
|
}
|
|
|
|
icrf_latch->pc = rfex_latch->common.pc + (offset + 4);
|
|
|
|
if (icrf_latch->pc == rfex_latch->common.pc &&
|
|
GET_RS(iw) == 0 && GET_RT(iw) == 0) {
|
|
//debug("Enter busy wait @ %llu cycles\n", vr4300->cycles);
|
|
|
|
exdc_latch->dest = PIPELINE_CYCLE_TYPE;
|
|
exdc_latch->result = 5;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#else
|
|
//
|
|
// BEQ
|
|
// BEQL
|
|
// BNE
|
|
// BNEL
|
|
//
|
|
int VR4300_BEQ_BEQL_BNE_BNEL(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t rs, uint64_t rt) {
|
|
struct vr4300_icrf_latch *icrf_latch = &vr4300->pipeline.icrf_latch;
|
|
struct vr4300_rfex_latch *rfex_latch = &vr4300->pipeline.rfex_latch;
|
|
uint32_t mask = vr4300_branch_lut[iw >> 30 & 0x1];
|
|
uint64_t offset = (uint64_t) ((int16_t) iw) << 2;
|
|
|
|
bool is_ne = iw >> 26 & 0x1;
|
|
bool cmp = rs == rt;
|
|
|
|
if (cmp == is_ne) {
|
|
rfex_latch->iw_mask = mask;
|
|
return 0;
|
|
}
|
|
|
|
icrf_latch->pc = rfex_latch->common.pc + (offset + 4);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// BGEZ
|
|
// BGEZL
|
|
// BLTZ
|
|
// BLTZL
|
|
//
|
|
int VR4300_BGEZ_BGEZL_BLTZ_BLTZL(
|
|
struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t rs, uint64_t unused(rt)) {
|
|
struct vr4300_icrf_latch *icrf_latch = &vr4300->pipeline.icrf_latch;
|
|
struct vr4300_rfex_latch *rfex_latch = &vr4300->pipeline.rfex_latch;
|
|
uint32_t mask = vr4300_branch_lut[iw >> 17 & 0x1];
|
|
uint64_t offset = (uint64_t) ((int16_t) iw) << 2;
|
|
|
|
bool is_ge = iw >> 16 & 0x1;
|
|
bool cmp = (int64_t) rs < 0;
|
|
|
|
if (cmp == is_ge) {
|
|
rfex_latch->iw_mask = mask;
|
|
return 0;
|
|
}
|
|
|
|
icrf_latch->pc = rfex_latch->common.pc + (offset + 4);
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// BGEZAL
|
|
// BGEZALL
|
|
// BLTZAL
|
|
// BLTZALL
|
|
//
|
|
int VR4300_BGEZAL_BGEZALL_BLTZAL_BLTZALL(
|
|
struct vr4300 *vr4300, uint32_t iw, uint64_t rs, uint64_t unused(rt)) {
|
|
struct vr4300_icrf_latch *icrf_latch = &vr4300->pipeline.icrf_latch;
|
|
struct vr4300_rfex_latch *rfex_latch = &vr4300->pipeline.rfex_latch;
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
uint32_t mask = vr4300_branch_lut[iw >> 17 & 0x1];
|
|
uint64_t offset = (uint64_t) ((int16_t) iw) << 2;
|
|
|
|
bool is_ge = iw >> 16 & 0x1;
|
|
bool cmp = (int64_t) rs < 0;
|
|
|
|
exdc_latch->result = rfex_latch->common.pc + 8;
|
|
exdc_latch->dest = VR4300_REGISTER_RA;
|
|
|
|
if (cmp == is_ge) {
|
|
rfex_latch->iw_mask = mask;
|
|
return 0;
|
|
}
|
|
|
|
icrf_latch->pc = rfex_latch->common.pc + (offset + 4);
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// BGTZ
|
|
// BGTZL
|
|
// BLEZ
|
|
// BLEZL
|
|
//
|
|
int VR4300_BGTZ_BGTZL_BLEZ_BLEZL(
|
|
struct vr4300 *vr4300, uint32_t iw, uint64_t rs, uint64_t unused(rt)) {
|
|
struct vr4300_icrf_latch *icrf_latch = &vr4300->pipeline.icrf_latch;
|
|
struct vr4300_rfex_latch *rfex_latch = &vr4300->pipeline.rfex_latch;
|
|
uint32_t mask = vr4300_branch_lut[iw >> 30 & 0x1];
|
|
uint64_t offset = (uint64_t) ((int16_t) iw) << 2;
|
|
|
|
bool is_gt = iw >> 26 & 0x1;
|
|
bool cmp = (int64_t) rs <= 0;
|
|
|
|
if (cmp == is_gt) {
|
|
rfex_latch->iw_mask = mask;
|
|
return 0;
|
|
}
|
|
|
|
icrf_latch->pc = rfex_latch->common.pc + (offset + 4);
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// CACHE
|
|
//
|
|
cen64_cold static int vr4300_cacheop_unimplemented(
|
|
struct vr4300 *vr4300, uint64_t vaddr, uint32_t paddr) {
|
|
debug("Unimplemented CACHE operation!\n");
|
|
|
|
abort();
|
|
return 0;
|
|
}
|
|
|
|
cen64_cold static int vr4300_cacheop_ic_invalidate(
|
|
struct vr4300 *vr4300, uint64_t vaddr, uint32_t paddr) {
|
|
vr4300_icache_invalidate(&vr4300->icache, vaddr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
cen64_cold static int vr4300_cacheop_ic_set_taglo(
|
|
struct vr4300 *vr4300, uint64_t vaddr, uint32_t paddr) {
|
|
vr4300_icache_set_taglo(&vr4300->icache, vaddr,
|
|
vr4300->regs[VR4300_CP0_REGISTER_TAGLO]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
cen64_cold static int vr4300_cacheop_ic_invalidate_hit(
|
|
struct vr4300 *vr4300, uint64_t vaddr, uint32_t paddr) {
|
|
vr4300_icache_invalidate_hit(&vr4300->icache, vaddr, paddr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
cen64_cold static int vr4300_cacheop_dc_get_taglo(
|
|
struct vr4300 *vr4300, uint64_t vaddr, uint32_t paddr) {
|
|
vr4300->regs[VR4300_CP0_REGISTER_TAGLO] =
|
|
vr4300_dcache_get_taglo(&vr4300->dcache, vaddr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
cen64_cold static int vr4300_cacheop_dc_set_taglo(
|
|
struct vr4300 *vr4300, uint64_t vaddr, uint32_t paddr) {
|
|
vr4300_dcache_set_taglo(&vr4300->dcache, vaddr,
|
|
vr4300->regs[VR4300_CP0_REGISTER_TAGLO]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
cen64_cold static int vr4300_cacheop_dc_wb_invalidate(
|
|
struct vr4300 *vr4300, uint64_t vaddr, uint32_t paddr) {
|
|
struct vr4300_dcache_line *line;
|
|
|
|
uint32_t bus_address;
|
|
uint32_t data[4];
|
|
unsigned i;
|
|
|
|
if (!(line = vr4300_dcache_wb_invalidate(&vr4300->dcache, vaddr)))
|
|
return 0;
|
|
|
|
bus_address = vr4300_dcache_get_tag(line, vaddr);
|
|
memcpy(data, line->data, sizeof(data));
|
|
|
|
for (i = 0; i < 4; i++)
|
|
bus_write_word(vr4300, bus_address + i * 4,
|
|
data[i ^ (WORD_ADDR_XOR >> 2)], ~0);
|
|
|
|
return DCACHE_ACCESS_DELAY;
|
|
}
|
|
|
|
cen64_cold static int vr4300_cacheop_dc_create_dirty_ex(
|
|
struct vr4300 *vr4300, uint64_t vaddr, uint32_t paddr) {
|
|
struct vr4300_dcache_line *line;
|
|
|
|
uint32_t bus_address;
|
|
uint32_t data[4];
|
|
unsigned i;
|
|
|
|
int delay = 0;
|
|
|
|
if ((line = vr4300_dcache_should_flush_line(&vr4300->dcache, vaddr))) {
|
|
bus_address = vr4300_dcache_get_tag(line, vaddr);
|
|
memcpy(data, line->data, sizeof(data));
|
|
|
|
for (i = 0; i < 4; i++)
|
|
bus_write_word(vr4300, bus_address + i * 4,
|
|
data[i ^ (WORD_ADDR_XOR >> 2)], ~0);
|
|
|
|
delay = DCACHE_ACCESS_DELAY;
|
|
}
|
|
|
|
vr4300_dcache_create_dirty_exclusive(&vr4300->dcache, vaddr, paddr);
|
|
return delay;
|
|
}
|
|
|
|
cen64_cold static int vr4300_cacheop_dc_hit_invalidate(
|
|
struct vr4300 *vr4300, uint64_t vaddr, uint32_t paddr) {
|
|
struct vr4300_dcache_line *line;
|
|
|
|
if ((line = vr4300_dcache_probe(&vr4300->dcache, vaddr, paddr)))
|
|
vr4300_dcache_invalidate(line);
|
|
|
|
return 0;
|
|
}
|
|
|
|
cen64_cold static int vr4300_cacheop_dc_hit_wb_invalidate(
|
|
struct vr4300 *vr4300, uint64_t vaddr, uint32_t paddr) {
|
|
struct vr4300_dcache_line *line;
|
|
|
|
uint32_t bus_address;
|
|
uint32_t data[4];
|
|
unsigned i;
|
|
|
|
if (!(line = vr4300_dcache_probe(&vr4300->dcache, vaddr, paddr)))
|
|
return 0;
|
|
|
|
if (line->metadata & 0x2) {
|
|
bus_address = vr4300_dcache_get_tag(line, vaddr);
|
|
memcpy(data, line->data, sizeof(data));
|
|
|
|
for (i = 0; i < 4; i++)
|
|
bus_write_word(vr4300, bus_address + i * 4,
|
|
data[i ^ (WORD_ADDR_XOR >> 2)], ~0);
|
|
|
|
line->metadata &= ~0x1;
|
|
return DCACHE_ACCESS_DELAY;
|
|
}
|
|
|
|
line->metadata &= ~0x1;
|
|
return 0;
|
|
}
|
|
|
|
cen64_cold static int vr4300_cacheop_dc_hit_wb(
|
|
struct vr4300 *vr4300, uint64_t vaddr, uint32_t paddr) {
|
|
struct vr4300_dcache_line *line;
|
|
|
|
uint32_t bus_address;
|
|
uint32_t data[4];
|
|
unsigned i;
|
|
|
|
if (!(line = vr4300_dcache_probe(&vr4300->dcache, vaddr, paddr)))
|
|
return 0;
|
|
|
|
if (line->metadata & 0x2) {
|
|
bus_address = vr4300_dcache_get_tag(line, vaddr);
|
|
memcpy(data, line->data, sizeof(data));
|
|
|
|
for (i = 0; i < 4; i++)
|
|
bus_write_word(vr4300, bus_address + i * 4,
|
|
data[i ^ (WORD_ADDR_XOR >> 2)], ~0);
|
|
|
|
// TODO: Technically, it's clean now...
|
|
line->metadata &= ~0x2;
|
|
return DCACHE_ACCESS_DELAY;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int VR4300_CACHE(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t rs, uint64_t rt) {
|
|
|
|
cen64_align(static vr4300_cacheop_func_t cacheop_lut[32], CACHE_LINE_SIZE) = {
|
|
vr4300_cacheop_ic_invalidate, vr4300_cacheop_unimplemented,
|
|
vr4300_cacheop_ic_set_taglo, vr4300_cacheop_unimplemented,
|
|
vr4300_cacheop_ic_invalidate_hit, vr4300_cacheop_unimplemented,
|
|
vr4300_cacheop_unimplemented, vr4300_cacheop_unimplemented,
|
|
|
|
vr4300_cacheop_dc_wb_invalidate, vr4300_cacheop_dc_get_taglo,
|
|
vr4300_cacheop_dc_set_taglo, vr4300_cacheop_dc_create_dirty_ex,
|
|
vr4300_cacheop_dc_hit_invalidate, vr4300_cacheop_dc_hit_wb_invalidate,
|
|
vr4300_cacheop_dc_hit_wb, vr4300_cacheop_unimplemented,
|
|
|
|
vr4300_cacheop_unimplemented, vr4300_cacheop_unimplemented,
|
|
vr4300_cacheop_unimplemented, vr4300_cacheop_unimplemented,
|
|
vr4300_cacheop_unimplemented, vr4300_cacheop_unimplemented,
|
|
vr4300_cacheop_unimplemented, vr4300_cacheop_unimplemented,
|
|
|
|
vr4300_cacheop_unimplemented, vr4300_cacheop_unimplemented,
|
|
vr4300_cacheop_unimplemented, vr4300_cacheop_unimplemented,
|
|
vr4300_cacheop_unimplemented, vr4300_cacheop_unimplemented,
|
|
vr4300_cacheop_unimplemented, vr4300_cacheop_unimplemented
|
|
};
|
|
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
uint64_t vaddr = rs + (int16_t) iw;
|
|
|
|
uint32_t op_type = (iw >> 18 & 0x7);
|
|
unsigned op = (iw >> 13 & 0x18) | op_type;
|
|
|
|
exdc_latch->request.vaddr = vaddr;
|
|
exdc_latch->request.cacheop = cacheop_lut[op];
|
|
exdc_latch->request.type = op_type > 2
|
|
? VR4300_BUS_REQUEST_CACHE_WRITE
|
|
: VR4300_BUS_REQUEST_CACHE_IDX;
|
|
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// DADD
|
|
// DSUB
|
|
//
|
|
int VR4300_DADD_DSUB(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t rs, uint64_t rt) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
uint64_t mask = vr4300_addsub_lut[iw & 0x2];
|
|
|
|
unsigned dest;
|
|
uint64_t rd;
|
|
|
|
dest = GET_RD(iw);
|
|
rt = (rt ^ mask) - mask;
|
|
rd = rs + rt;
|
|
|
|
// TODO/FIXME: Uncomment this later...
|
|
//assert(((rd >> 63) == (rd >> 64)) && "Overflow exception.");
|
|
|
|
exdc_latch->result = (int64_t) rd;
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// DADDI
|
|
// DSUBI
|
|
//
|
|
int VR4300_DADDI_DSUBI(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t rs, uint64_t rt) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
uint64_t mask = 0; //vr4300_addsub_lut[iw & 0x1];
|
|
|
|
unsigned dest;
|
|
|
|
dest = GET_RT(iw);
|
|
rt = (int16_t) iw;
|
|
rt = (rt ^ mask) - mask;
|
|
rt = rs + rt;
|
|
|
|
// TODO/FIXME: Uncomment this later...
|
|
//assert(((rt >> 63) == (rt >> 64)) && "Overflow exception.");
|
|
|
|
exdc_latch->result = (int64_t) rt;
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// DADDIU
|
|
// DSUBIU
|
|
//
|
|
int VR4300_DADDIU_DSUBIU(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t rs, uint64_t rt) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
uint64_t mask = 0; //vr4300_addsub_lut[iw & 0x1];
|
|
|
|
unsigned dest;
|
|
|
|
dest = GET_RT(iw);
|
|
rt = (int16_t) iw;
|
|
rt = (rt ^ mask) - mask;
|
|
rt = rs + rt;
|
|
|
|
exdc_latch->result = (int64_t) rt;
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// DADDU
|
|
// DSUBU
|
|
//
|
|
int VR4300_DADDU_DSUBU(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t rs, uint64_t rt) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
uint64_t mask = vr4300_addsub_lut[iw & 0x2];
|
|
|
|
unsigned dest;
|
|
uint64_t rd;
|
|
|
|
dest = GET_RD(iw);
|
|
rt = (rt ^ mask) - mask;
|
|
rd = rs + rt;
|
|
|
|
exdc_latch->result = (int64_t) rd;
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// DIV
|
|
// DIVU
|
|
//
|
|
int VR4300_DIV_DIVU(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t rs, uint64_t rt) {
|
|
bool is_divu = iw & 0x1;
|
|
|
|
uint64_t sex_mask = vr4300_mult_sex_mask[is_divu];
|
|
int64_t rs_sex = (int32_t) rs & sex_mask;
|
|
int64_t rt_sex = (int32_t) rt & sex_mask;
|
|
|
|
if (likely(rt_sex != 0)) {
|
|
int32_t div = rs_sex / rt_sex;
|
|
int32_t mod = rs_sex % rt_sex;
|
|
|
|
// TODO: Delay the output a few cycles.
|
|
vr4300->regs[VR4300_REGISTER_LO] = div;
|
|
vr4300->regs[VR4300_REGISTER_HI] = mod;
|
|
}
|
|
|
|
return vr4300_do_mci(vr4300, 37);
|
|
}
|
|
|
|
//
|
|
// DDIV
|
|
//
|
|
int VR4300_DDIV(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t _rs, uint64_t _rt) {
|
|
if (likely(_rt != 0)) {
|
|
int64_t rs = _rs;
|
|
int64_t rt = _rt;
|
|
|
|
uint64_t div = rs / rt;
|
|
uint64_t mod = rs % rt;
|
|
|
|
// TODO: Delay the output a few cycles.
|
|
vr4300->regs[VR4300_REGISTER_LO] = div;
|
|
vr4300->regs[VR4300_REGISTER_HI] = mod;
|
|
}
|
|
|
|
return vr4300_do_mci(vr4300, 69);
|
|
}
|
|
|
|
//
|
|
// DDIVU
|
|
//
|
|
int VR4300_DDIVU(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t rs, uint64_t rt) {
|
|
if (likely(rt != 0)) {
|
|
uint64_t div = rs / rt;
|
|
uint64_t mod = rs % rt;
|
|
|
|
// TODO: Delay the output a few cycles.
|
|
vr4300->regs[VR4300_REGISTER_LO] = div;
|
|
vr4300->regs[VR4300_REGISTER_HI] = mod;
|
|
}
|
|
|
|
return vr4300_do_mci(vr4300, 69);
|
|
}
|
|
|
|
//
|
|
// DMULT
|
|
//
|
|
int VR4300_DMULT(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t rs, uint64_t rt) {
|
|
int64_t lo, hi;
|
|
|
|
#if defined(__GNUC__) && defined(__x86_64__)
|
|
__int128_t rsx = (int64_t) rs;
|
|
__int128_t rtx = (int64_t) rt;
|
|
__int128_t result;
|
|
|
|
result = rsx * rtx;
|
|
|
|
lo = result;
|
|
hi = result >> 64;
|
|
#else
|
|
int64_t hi_prod, mid_prods, lo_prod;
|
|
int64_t rshi = (int32_t) (rs >> 32);
|
|
int64_t rthi = (int32_t) (rt >> 32);
|
|
int64_t rslo = (uint32_t) rs;
|
|
int64_t rtlo = (uint32_t) rt;
|
|
|
|
mid_prods = (rshi * rtlo) + (rslo * rthi);
|
|
lo_prod = (rslo * rtlo);
|
|
hi_prod = (rshi * rthi);
|
|
|
|
mid_prods += lo_prod >> 32;
|
|
hi = hi_prod + (mid_prods >> 32);
|
|
lo = (uint32_t) lo_prod + (mid_prods << 32);
|
|
#endif
|
|
|
|
// TODO: Delay the output a few cycles.
|
|
vr4300->regs[VR4300_REGISTER_LO] = lo;
|
|
vr4300->regs[VR4300_REGISTER_HI] = hi;
|
|
return vr4300_do_mci(vr4300, 8);
|
|
}
|
|
|
|
//
|
|
// DMULTU
|
|
//
|
|
// TODO: Add a version that works on MSVC.
|
|
//
|
|
int VR4300_DMULTU(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t rs, uint64_t rt) {
|
|
uint64_t lo, hi;
|
|
|
|
#if defined(__GNUC__) && defined(__x86_64__)
|
|
__uint128_t rsx = rs;
|
|
__uint128_t rtx = rt;
|
|
__uint128_t result;
|
|
|
|
result = rsx * rtx;
|
|
|
|
lo = result;
|
|
hi = result >> 64;
|
|
#else
|
|
uint64_t hi_prod, mid_prods, lo_prod;
|
|
uint64_t rshi = (int32_t) (rs >> 32);
|
|
uint64_t rthi = (int32_t) (rt >> 32);
|
|
uint64_t rslo = (uint32_t) rs;
|
|
uint64_t rtlo = (uint32_t) rt;
|
|
|
|
mid_prods = (rshi * rtlo) + (rslo * rthi);
|
|
lo_prod = (rslo * rtlo);
|
|
hi_prod = (rshi * rthi);
|
|
|
|
mid_prods += lo_prod >> 32;
|
|
hi = hi_prod + (mid_prods >> 32);
|
|
lo = (uint32_t) lo_prod + (mid_prods << 32);
|
|
#endif
|
|
|
|
// TODO: Delay the output a few cycles.
|
|
vr4300->regs[VR4300_REGISTER_LO] = lo;
|
|
vr4300->regs[VR4300_REGISTER_HI] = hi;
|
|
return vr4300_do_mci(vr4300, 8);
|
|
}
|
|
|
|
//
|
|
// DSLL
|
|
// DSLL32
|
|
//
|
|
int VR4300_DSLL_DSLL32(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);
|
|
unsigned sa = (iw >> 6 & 0x1F) + ((iw & 0x4) << 3);
|
|
|
|
exdc_latch->result = rt << sa;
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// DSLLV
|
|
//
|
|
int VR4300_DSLLV(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);
|
|
unsigned sa = rs & 0x3F;
|
|
|
|
exdc_latch->result = rt << sa;
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// DSRA
|
|
// DSRA32
|
|
//
|
|
int VR4300_DSRA_DSRA32(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);
|
|
unsigned sa = (iw >> 6 & 0x1F) + ((iw & 0x4) << 3);
|
|
|
|
exdc_latch->result = (int64_t) rt >> sa;
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// DSRAV
|
|
//
|
|
int VR4300_DSRAV(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);
|
|
unsigned sa = rs & 0x3F;
|
|
|
|
exdc_latch->result = (int64_t) rt >> sa;
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// DSRL
|
|
// DSRL32
|
|
//
|
|
int VR4300_DSRL_DSRL32(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);
|
|
unsigned sa = (iw >> 6 & 0x1F) + ((iw & 0x4) << 3);
|
|
|
|
exdc_latch->result = rt >> sa;
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// DSRLV
|
|
//
|
|
int VR4300_DSRLV(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);
|
|
unsigned sa = rs & 0x3F;
|
|
|
|
exdc_latch->result = rt >> sa;
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// INV
|
|
//
|
|
int VR4300_INVALID(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t unused(rs), uint64_t unused(rt)) {
|
|
#ifndef NDEBUG
|
|
struct vr4300_rfex_latch *rfex_latch = &vr4300->pipeline.rfex_latch;
|
|
|
|
debug("Unimplemented instruction: %s [0x%.8X] @ 0x%.16llX\n",
|
|
vr4300_opcode_mnemonics[rfex_latch->opcode.id], iw, (long long unsigned)
|
|
rfex_latch->common.pc);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef VR4300_BUSY_WAIT_DETECTION
|
|
//
|
|
// J
|
|
//
|
|
// If VR4300_BUSY_WAIT_DETECTOR is defined, this version of J
|
|
// is used. Otherwise, the version below is used. The reason we
|
|
// have this J is to detect libultra's busy wait loops:
|
|
//
|
|
// busy_wait:
|
|
// j busy_wait
|
|
// nop
|
|
//
|
|
// When we detect such loops in cached memory, the pipeline
|
|
// simulation mostly shuts down and goes into an accelerated mode
|
|
// where fewer checks are made (to boost performance).
|
|
//
|
|
// XXX: Ensure we're in cached memory AND we're followed by a NOP.
|
|
// If we don't do both of these things, we could get ourselves
|
|
// into trouble.
|
|
//
|
|
int VR4300_J_JAL_BWDETECT(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t rs, uint64_t rt) {
|
|
struct vr4300_icrf_latch *icrf_latch = &vr4300->pipeline.icrf_latch;
|
|
struct vr4300_rfex_latch *rfex_latch = &vr4300->pipeline.rfex_latch;
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
|
|
bool is_jal = iw >> 26 & 0x1;
|
|
uint32_t target = iw << 2 & 0x0FFFFFFF;
|
|
uint32_t mask = vr4300_branch_lut[is_jal];
|
|
|
|
exdc_latch->result = rfex_latch->common.pc + 8;
|
|
exdc_latch->dest = VR4300_REGISTER_RA & ~mask;
|
|
|
|
icrf_latch->pc = (rfex_latch->common.pc & ~0x0FFFFFFFULL) | target;
|
|
|
|
if (icrf_latch->pc == rfex_latch->common.pc) {
|
|
//debug("Enter busy wait @ %llu cycles\n", vr4300->cycles);
|
|
|
|
exdc_latch->dest = PIPELINE_CYCLE_TYPE;
|
|
exdc_latch->result = 5;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#else
|
|
//
|
|
// J
|
|
// JAL
|
|
//
|
|
int VR4300_J_JAL(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t rs, uint64_t rt) {
|
|
struct vr4300_icrf_latch *icrf_latch = &vr4300->pipeline.icrf_latch;
|
|
struct vr4300_rfex_latch *rfex_latch = &vr4300->pipeline.rfex_latch;
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
|
|
bool is_jal = iw >> 26 & 0x1;
|
|
uint32_t target = iw << 2 & 0x0FFFFFFF;
|
|
uint32_t mask = vr4300_branch_lut[is_jal];
|
|
|
|
exdc_latch->result = rfex_latch->common.pc + 8;
|
|
exdc_latch->dest = VR4300_REGISTER_RA & ~mask;
|
|
|
|
icrf_latch->pc = (rfex_latch->common.pc & ~0x0FFFFFFFULL) | target;
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// JALR
|
|
// JR
|
|
//
|
|
int VR4300_JALR_JR(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t rs, uint64_t unused(rt)) {
|
|
struct vr4300_icrf_latch *icrf_latch = &vr4300->pipeline.icrf_latch;
|
|
struct vr4300_rfex_latch *rfex_latch = &vr4300->pipeline.rfex_latch;
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
|
|
bool is_jalr = iw & 0x1;
|
|
uint32_t mask = vr4300_branch_lut[is_jalr];
|
|
uint32_t rd = GET_RD(iw);
|
|
|
|
exdc_latch->result = rfex_latch->common.pc + 8;
|
|
exdc_latch->dest = rd & ~mask;
|
|
|
|
icrf_latch->pc = rs;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// LD
|
|
// SD
|
|
//
|
|
// TODO/FIXME: Check for unaligned addresses.
|
|
//
|
|
int VR4300_LD_SD(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t rs, uint64_t rt) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
uint64_t sel_mask = (int64_t) (int32_t) (iw << 2) >> 32;
|
|
|
|
exdc_latch->request.vaddr = rs + (int16_t) iw;
|
|
exdc_latch->request.data = ~sel_mask | (sel_mask & rt);
|
|
exdc_latch->request.wdqm = sel_mask;
|
|
exdc_latch->request.postshift = 0;
|
|
exdc_latch->request.access_type = VR4300_ACCESS_DWORD;
|
|
exdc_latch->request.type = 1 - sel_mask;
|
|
exdc_latch->request.size = 8;
|
|
|
|
exdc_latch->dest = ~sel_mask & GET_RT(iw);
|
|
exdc_latch->result = 0;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// LB
|
|
// LBU
|
|
// LH
|
|
// LHU
|
|
// LW
|
|
// LWU
|
|
// SB
|
|
// SH
|
|
// SW
|
|
//
|
|
// TODO/FIXME: Check for unaligned addresses.
|
|
//
|
|
cen64_hot int VR4300_LOAD_STORE(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t rs, uint64_t rt) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
uint64_t sel_mask = (int64_t) (int32_t) (iw << 2) >> 32;
|
|
|
|
uint64_t address = rs + (int16_t) iw;
|
|
unsigned request_size = (iw >> 26 & 0x3);
|
|
uint64_t dqm = vr4300_load_sex_mask[iw >> 28 & 0x1][request_size] & ~sel_mask;
|
|
unsigned lshiftamt = (3 - request_size) << 3;
|
|
unsigned rshiftamt = (address & 0x3) << 3;
|
|
|
|
exdc_latch->request.vaddr = address & ~(sel_mask & 0x3);
|
|
exdc_latch->request.data = dqm | (sel_mask & ((rt << lshiftamt) >> rshiftamt));
|
|
exdc_latch->request.wdqm = ((uint32_t) sel_mask << lshiftamt) >> rshiftamt;
|
|
exdc_latch->request.postshift = 0;
|
|
exdc_latch->request.access_type = VR4300_ACCESS_WORD;
|
|
exdc_latch->request.type = 1 - sel_mask;
|
|
exdc_latch->request.size = request_size + 1;
|
|
|
|
exdc_latch->dest = ~sel_mask & GET_RT(iw);
|
|
exdc_latch->result = 0;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// LDL
|
|
// LDR
|
|
//
|
|
int VR4300_LDL_LDR(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t rs, uint64_t rt) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
|
|
uint64_t address = rs + (int16_t) iw;
|
|
unsigned offset = address & 0x7;
|
|
unsigned dest = GET_RT(iw);
|
|
uint64_t dqm;
|
|
int size;
|
|
|
|
// LDR
|
|
if (iw >> 26 & 0x1) {
|
|
address ^= offset;
|
|
size = offset + 1;
|
|
dqm = ~0ULL >> ((8 - size) << 3);
|
|
}
|
|
|
|
// LDL
|
|
else {
|
|
size = 8;
|
|
dqm = ~0ULL << (offset << 3);
|
|
}
|
|
|
|
exdc_latch->request.vaddr = address;
|
|
exdc_latch->request.data = dqm;
|
|
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 = size;
|
|
|
|
exdc_latch->dest = dest;
|
|
exdc_latch->result = rt & ~dqm;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// LWL
|
|
// LWR
|
|
//
|
|
int VR4300_LWL_LWR(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t rs, uint64_t rt) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
|
|
uint64_t address = rs + (int16_t) iw;
|
|
unsigned offset = address & 0x3;
|
|
unsigned dest = GET_RT(iw);
|
|
uint64_t dqm;
|
|
int size;
|
|
|
|
// LWR
|
|
if (iw >> 28 & 0x1) {
|
|
cen64_align(static const uint64_t forceset[], 32) =
|
|
{0ULL, 0ULL, 0ULL, ~0ULL};
|
|
|
|
size = offset + 1;
|
|
dqm = ~0U >> ((4 - size) << 3);
|
|
address ^= offset;
|
|
|
|
//
|
|
// TODO/FIXME: Assume 32-bit mode.
|
|
//
|
|
dqm |= forceset[offset];
|
|
}
|
|
|
|
// LWL
|
|
else {
|
|
dqm = ~0ULL << (offset << 3);
|
|
size = 4;
|
|
}
|
|
|
|
exdc_latch->request.vaddr = address;
|
|
exdc_latch->request.data = dqm;
|
|
exdc_latch->request.wdqm = 0ULL;
|
|
exdc_latch->request.postshift = 0;
|
|
exdc_latch->request.access_type = VR4300_ACCESS_WORD;
|
|
exdc_latch->request.type = VR4300_BUS_REQUEST_READ;
|
|
exdc_latch->request.size = size;
|
|
|
|
exdc_latch->dest = dest;
|
|
exdc_latch->result = rt & ~dqm;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// MFHI
|
|
// MFLO
|
|
//
|
|
int VR4300_MFHI_MFLO(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);
|
|
bool is_mflo = iw >> 1 & 0x1;
|
|
|
|
// TODO: Read these here, or...? Registers are probably tied into EX logic...
|
|
exdc_latch->result = vr4300->regs[VR4300_REGISTER_HI + is_mflo];
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// MTHI
|
|
// MTLO
|
|
//
|
|
int VR4300_MTHI_MTLO(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t rs, uint64_t rt) {
|
|
|
|
bool is_mtlo = iw >> 1 & 0x1;
|
|
|
|
// TODO: Write these here, or...? Registers are probably tied into EX logic...
|
|
vr4300->regs[VR4300_REGISTER_HI + is_mtlo] = rs;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// MULT
|
|
// MULTU
|
|
//
|
|
int VR4300_MULT_MULTU(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t rs, uint64_t rt) {
|
|
bool is_multu = iw & 0x1;
|
|
|
|
uint64_t sex_mask = vr4300_mult_sex_mask[is_multu];
|
|
uint64_t rs_sex = (int32_t) rs & sex_mask;
|
|
uint64_t rt_sex = (int32_t) rt & sex_mask;
|
|
uint64_t result = rs_sex * rt_sex;
|
|
|
|
// TODO: Delay the output a few cycles.
|
|
vr4300->regs[VR4300_REGISTER_LO] = (int32_t) result;
|
|
vr4300->regs[VR4300_REGISTER_HI] = (int32_t) (result >> 32);
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// NOR
|
|
//
|
|
int VR4300_NOR(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);
|
|
|
|
exdc_latch->result = ~(rs | rt);
|
|
exdc_latch->dest = dest;
|
|
return vr4300_do_mci(vr4300, 57);
|
|
}
|
|
|
|
//
|
|
// SLL
|
|
// SLLV
|
|
//
|
|
int VR4300_SLL_SLLV(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);
|
|
unsigned sa = (rs & 0x1F) + (iw >> 6 & 0x1F);
|
|
|
|
exdc_latch->result = (int32_t) (rt << sa);
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// SLT
|
|
//
|
|
int VR4300_SLT(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);
|
|
|
|
exdc_latch->result = (int64_t) rs < (int64_t) rt;
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// SLTI
|
|
//
|
|
int VR4300_SLTI(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);
|
|
int64_t imm = (int16_t) iw;
|
|
|
|
exdc_latch->result = (int64_t) rs < imm;
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// SLTIU
|
|
//
|
|
int VR4300_SLTIU(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);
|
|
uint64_t imm = (int16_t) iw;
|
|
|
|
exdc_latch->result = rs < imm;
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// SLTU
|
|
//
|
|
int VR4300_SLTU(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);
|
|
|
|
exdc_latch->result = rs < rt;
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// SRA
|
|
//
|
|
int VR4300_SRA(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t unused(rs), uint64_t rt) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
unsigned dest = GET_RD(iw);
|
|
unsigned sa = iw >> 6 & 0x1F;
|
|
|
|
exdc_latch->result = (int32_t) rt >> sa;
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// SRAV
|
|
//
|
|
int VR4300_SRAV(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);
|
|
unsigned sa = rs & 0x1F;
|
|
|
|
exdc_latch->result = (int32_t) rt >> sa;
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// SRL
|
|
//
|
|
int VR4300_SRL(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t unused(rs), uint64_t rt) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
unsigned dest = GET_RD(iw);
|
|
unsigned sa = iw >> 6 & 0x1F;
|
|
|
|
exdc_latch->result = (int32_t) ((uint32_t) rt >> sa);
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// SRLV
|
|
//
|
|
int VR4300_SRLV(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);
|
|
unsigned sa = rs & 0x1F;
|
|
|
|
exdc_latch->result = (int32_t) ((uint32_t) rt >> sa);
|
|
exdc_latch->dest = dest;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// SDL
|
|
// SDR
|
|
//
|
|
int VR4300_SDL_SDR(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t rs, uint64_t rt) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
|
|
uint64_t address = rs + (int16_t) iw;
|
|
unsigned offset = address & 0x7;
|
|
uint64_t mask = ~0ULL;
|
|
|
|
unsigned shiftamt;
|
|
uint64_t data;
|
|
uint64_t dqm;
|
|
|
|
// SDR
|
|
if (iw >> 26 & 0x1) {
|
|
shiftamt = (7 - offset) << 3;
|
|
data = rt << shiftamt;
|
|
dqm = mask << shiftamt;
|
|
}
|
|
|
|
// SDL
|
|
else {
|
|
shiftamt = offset << 3;
|
|
data = rt >> shiftamt;
|
|
dqm = mask >> shiftamt;
|
|
}
|
|
|
|
exdc_latch->request.vaddr = address & ~0x3ULL;
|
|
exdc_latch->request.data = data;
|
|
exdc_latch->request.wdqm = dqm;
|
|
exdc_latch->request.access_type = VR4300_ACCESS_DWORD;
|
|
exdc_latch->request.type = VR4300_BUS_REQUEST_WRITE;
|
|
exdc_latch->request.size = 8;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// SWL
|
|
// SWR
|
|
//
|
|
int VR4300_SWL_SWR(struct vr4300 *vr4300,
|
|
uint32_t iw, uint64_t rs, uint64_t rt) {
|
|
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
|
|
|
|
uint64_t address = rs + (int16_t) iw;
|
|
unsigned offset = address & 0x3;
|
|
uint32_t mask = ~0U;
|
|
|
|
unsigned shiftamt;
|
|
uint64_t data;
|
|
uint64_t dqm;
|
|
|
|
// SWR
|
|
if (iw >> 28 & 0x1) {
|
|
shiftamt = (3 - offset) << 3;
|
|
data = rt << shiftamt;
|
|
dqm = mask << shiftamt;
|
|
}
|
|
|
|
// SWL
|
|
else {
|
|
shiftamt = offset << 3;
|
|
data = rt >> shiftamt;
|
|
dqm = mask >> shiftamt;
|
|
}
|
|
|
|
exdc_latch->request.vaddr = address & ~0x3ULL;
|
|
exdc_latch->request.data = data;
|
|
exdc_latch->request.wdqm = dqm;
|
|
exdc_latch->request.access_type = VR4300_ACCESS_WORD;
|
|
exdc_latch->request.type = VR4300_BUS_REQUEST_WRITE;
|
|
exdc_latch->request.size = 4;
|
|
return 0;
|
|
}
|
|
|
|
// Function lookup table.
|
|
cen64_align(const vr4300_function
|
|
vr4300_function_table[NUM_VR4300_OPCODES], CACHE_LINE_SIZE) = {
|
|
#define X(op) op,
|
|
#include "vr4300/opcodes.md"
|
|
#undef X
|
|
};
|
|
|