mirror of
https://github.com/simple64/simple64.git
synced 2026-11-13 02:18:42 +01:00
394 lines
11 KiB
C++
394 lines
11 KiB
C++
#include "../state.hpp"
|
|
|
|
#ifdef TRACE_COP2
|
|
#include <stdio.h>
|
|
#define TRACE_LS(op) printf(#op " v%u, %u, %d(r%u)\n", rt, e, offset, base)
|
|
#else
|
|
#define TRACE_LS(op) ((void)0)
|
|
#endif
|
|
|
|
extern "C"
|
|
{
|
|
// Using mostly Ares' implementation as a base here
|
|
|
|
static inline uint8_t byteFromHalfWords(const uint16_t *arr, unsigned i)
|
|
{
|
|
return (i & 1) ?
|
|
(uint8_t)(arr[i >> 1] & 0xff) :
|
|
(uint8_t)(arr[i >> 1] >> 8);
|
|
}
|
|
|
|
static inline void writeByteToHalfWords(uint16_t *arr, unsigned i, uint8_t b)
|
|
{
|
|
const unsigned n = i >> 1;
|
|
if (i & 1)
|
|
arr[n] = (arr[n] & 0xff00) | (uint16_t)b;
|
|
else
|
|
arr[n] = (arr[n] & 0xff) | ((uint16_t)b << 8);
|
|
}
|
|
|
|
// Load 8-bit
|
|
void RSP_LBV(RSP::CPUState *rsp, unsigned rt, unsigned e, int offset, unsigned base)
|
|
{
|
|
TRACE_LS(LBV);
|
|
unsigned addr = (rsp->sr[base] + offset * 1) & 0xfff;
|
|
reinterpret_cast<uint8_t *>(rsp->cp2.regs[rt].e)[MES(e)] = READ_MEM_U8(rsp->dmem, addr);
|
|
}
|
|
|
|
// Store 8-bit
|
|
void RSP_SBV(RSP::CPUState *rsp, unsigned rt, unsigned e, int offset, unsigned base)
|
|
{
|
|
TRACE_LS(SBV);
|
|
unsigned addr = (rsp->sr[base] + offset * 1) & 0xfff;
|
|
uint8_t v = reinterpret_cast<uint8_t *>(rsp->cp2.regs[rt].e)[MES(e)];
|
|
|
|
#ifdef INTENSE_DEBUG
|
|
fprintf(stderr, "SBV: 0x%x (0x%x)\n", addr, v);
|
|
#endif
|
|
|
|
WRITE_MEM_U8(rsp->dmem, addr, v);
|
|
}
|
|
|
|
// Load 16-bit
|
|
void RSP_LSV(RSP::CPUState *rsp, unsigned rt, unsigned e, int offset, unsigned base)
|
|
{
|
|
TRACE_LS(LSV);
|
|
unsigned addr = rsp->sr[base] + offset * 2;
|
|
const unsigned end = (e > 14) ? 16 : (e + 2);
|
|
for (unsigned i = e; i < end; i++)
|
|
writeByteToHalfWords(rsp->cp2.regs[rt].e, i & 0xf, READ_MEM_U8(rsp->dmem, addr++ & 0xfff));
|
|
}
|
|
|
|
// Store 16-bit
|
|
void RSP_SSV(RSP::CPUState *rsp, unsigned rt, unsigned e, int offset, unsigned base)
|
|
{
|
|
TRACE_LS(SSV);
|
|
unsigned addr = (rsp->sr[base] + offset * 2) & 0xfff;
|
|
uint8_t v0 = reinterpret_cast<uint8_t *>(rsp->cp2.regs[rt].e)[MES(e)];
|
|
uint8_t v1 = reinterpret_cast<uint8_t *>(rsp->cp2.regs[rt].e)[MES((e + 1) & 0xf)];
|
|
|
|
#ifdef INTENSE_DEBUG
|
|
fprintf(stderr, "SSV: 0x%x (0x%x, 0x%x)\n", addr, v0, v1);
|
|
#endif
|
|
|
|
WRITE_MEM_U8(rsp->dmem, addr, v0);
|
|
WRITE_MEM_U8(rsp->dmem, (addr + 1) & 0xfff, v1);
|
|
}
|
|
|
|
// Load 32-bit
|
|
void RSP_LLV(RSP::CPUState *rsp, unsigned rt, unsigned e, int offset, unsigned base)
|
|
{
|
|
TRACE_LS(LLV);
|
|
unsigned addr = rsp->sr[base] + offset * 4;
|
|
const unsigned end = (e > 12) ? 16 : (e + 4);
|
|
for (unsigned i = e; i < end; i++)
|
|
writeByteToHalfWords(rsp->cp2.regs[rt].e, i & 0xf, READ_MEM_U8(rsp->dmem, addr++ & 0xfff));
|
|
}
|
|
|
|
// Store 32-bit
|
|
void RSP_SLV(RSP::CPUState *rsp, unsigned rt, unsigned e, int offset, unsigned base)
|
|
{
|
|
TRACE_LS(SLV);
|
|
unsigned addr = (rsp->sr[base] + offset * 4) & 0xfff;
|
|
|
|
#ifdef INTENSE_DEBUG
|
|
fprintf(stderr, "SLV 0x%x, e = %u\n", addr, e);
|
|
#endif
|
|
|
|
for (unsigned i = e; i < e + 4; i++)
|
|
WRITE_MEM_U8(rsp->dmem, addr++, byteFromHalfWords(rsp->cp2.regs[rt].e, i & 0xf));
|
|
}
|
|
|
|
// Load 64-bit
|
|
void RSP_LDV(RSP::CPUState *rsp, unsigned rt, unsigned e, int offset, unsigned base)
|
|
{
|
|
TRACE_LS(LDV);
|
|
unsigned addr = rsp->sr[base] + offset * 8;
|
|
const unsigned end = (e > 8) ? 16 : (e + 8);
|
|
for (unsigned i = e; i < end; i++)
|
|
writeByteToHalfWords(rsp->cp2.regs[rt].e, i & 0xf, READ_MEM_U8(rsp->dmem, addr++ & 0xfff));
|
|
}
|
|
|
|
// Store 64-bit
|
|
void RSP_SDV(RSP::CPUState *rsp, unsigned rt, unsigned e, int offset, unsigned base)
|
|
{
|
|
TRACE_LS(SDV);
|
|
unsigned addr = (rsp->sr[base] + offset * 8) & 0xfff;
|
|
|
|
#ifdef INTENSE_DEBUG
|
|
fprintf(stderr, "SDV 0x%x, e = %u\n", addr, e);
|
|
#endif
|
|
|
|
// Handle illegal scenario.
|
|
if ((e > 8) || (e & 1) || (addr & 1))
|
|
{
|
|
for (unsigned i = 0; i < 8; i++)
|
|
{
|
|
WRITE_MEM_U8(rsp->dmem, (addr + i) & 0xfff,
|
|
reinterpret_cast<const uint8_t *>(rsp->cp2.regs[rt].e)[MES((e + i) & 0xf)]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
e >>= 1;
|
|
for (unsigned i = 0; i < 4; i++)
|
|
{
|
|
WRITE_MEM_U16(rsp->dmem, (addr + 2 * i) & 0xfff, rsp->cp2.regs[rt].e[e + i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Load 8x8-bit into high bits.
|
|
void RSP_LPV(RSP::CPUState *rsp, unsigned rt, unsigned e, int offset, unsigned base)
|
|
{
|
|
TRACE_LS(LPV);
|
|
unsigned addr = (rsp->sr[base] + offset * 8) & 0xfff;
|
|
const unsigned index = (addr & 7) - e;
|
|
addr &= ~7;
|
|
|
|
auto *reg = rsp->cp2.regs[rt].e;
|
|
for (unsigned i = 0; i < 8; i++)
|
|
reg[i] = READ_MEM_U8(rsp->dmem, (addr + (i + index & 0xf)) & 0xfff) << 8;
|
|
}
|
|
|
|
void RSP_SPV(RSP::CPUState *rsp, unsigned rt, unsigned e, int offset, unsigned base)
|
|
{
|
|
TRACE_LS(SPV);
|
|
unsigned addr = (rsp->sr[base] + offset * 8) & 0xfff;
|
|
auto *reg = rsp->cp2.regs[rt].e;
|
|
|
|
for (unsigned i = e; i < e + 8; i++) {
|
|
const unsigned shift = ((i & 0xf) < 8) ? 8 : 7;
|
|
WRITE_MEM_U8(rsp->dmem, addr++ & 0xfff, int16_t(reg[i & 7]) >> shift);
|
|
}
|
|
}
|
|
|
|
// Load 8x8-bit into high bits, but shift by 7 instead of 8.
|
|
// Was probably used for certain fixed point algorithms to get more headroom without
|
|
// saturation, but weird nonetheless.
|
|
void RSP_LUV(RSP::CPUState *rsp, unsigned rt, unsigned e, int offset, unsigned base)
|
|
{
|
|
TRACE_LS(LUV);
|
|
unsigned addr = (rsp->sr[base] + offset * 8) & 0xfff;
|
|
const unsigned index = (addr & 7) - e;
|
|
addr &= ~7;
|
|
|
|
auto *reg = rsp->cp2.regs[rt].e;
|
|
for (unsigned i = 0; i < 8; i++)
|
|
reg[i] = READ_MEM_U8(rsp->dmem, (addr + (i + index & 0xf)) & 0xfff) << 7;
|
|
}
|
|
|
|
void RSP_SUV(RSP::CPUState *rsp, unsigned rt, unsigned e, int offset, unsigned base)
|
|
{
|
|
TRACE_LS(SUV);
|
|
unsigned addr = (rsp->sr[base] + offset * 8) & 0xfff;
|
|
auto *reg = rsp->cp2.regs[rt].e;
|
|
|
|
for (unsigned i = e; i < e + 8; i++) {
|
|
const unsigned shift = ((i & 0xf) < 8) ? 7 : 8;
|
|
WRITE_MEM_U8(rsp->dmem, addr++ & 0xfff, int16_t(reg[i & 7]) >> shift);
|
|
}
|
|
}
|
|
|
|
// Load 8x8-bits into high bits, but shift by 7 instead of 8.
|
|
// Seems to differ from LUV in that it loads every other byte instead of packed bytes.
|
|
void RSP_LHV(RSP::CPUState *rsp, unsigned rt, unsigned e, int offset, unsigned base)
|
|
{
|
|
TRACE_LS(LHV);
|
|
unsigned addr = rsp->sr[base] + offset * 16;
|
|
const unsigned index = (addr & 7) - e;
|
|
addr &= ~7;
|
|
|
|
auto *reg = rsp->cp2.regs[rt].e;
|
|
for (unsigned i = 0; i < 8; i++)
|
|
reg[i] = (uint16_t)READ_MEM_U8(rsp->dmem, (addr + (index + i * 2 & 0xf)) & 0xfff) << 7;
|
|
}
|
|
|
|
void RSP_SHV(RSP::CPUState *rsp, unsigned rt, unsigned e, int offset, unsigned base)
|
|
{
|
|
TRACE_LS(SHV);
|
|
unsigned addr = (rsp->sr[base] + offset * 16) & 0xfff;
|
|
const unsigned index = addr & 7;
|
|
addr &= ~7;
|
|
|
|
const auto *reg = rsp->cp2.regs[rt].e;
|
|
for (unsigned i = 0; i < 8; i++)
|
|
{
|
|
const unsigned b = e + (i << 1);
|
|
const uint8_t byte = byteFromHalfWords(reg, b & 0xf) << 1 | byteFromHalfWords(reg, b + 1 & 0xf) >> 7;
|
|
WRITE_MEM_U8(rsp->dmem, addr + (index + i * 2 & 0xf), byte);
|
|
}
|
|
}
|
|
|
|
void RSP_LFV(RSP::CPUState *rsp, unsigned rt, unsigned e, int offset, unsigned base)
|
|
{
|
|
TRACE_LS(LFV);
|
|
uint16_t temp[8];
|
|
|
|
unsigned addr = rsp->sr[base] + offset * 16;
|
|
const unsigned index = (addr & 7) - e;
|
|
const unsigned end = (e > 8) ? 16 : (e + 8);
|
|
addr &= ~7;
|
|
|
|
for (unsigned i = 0; i < 4; i++)
|
|
{
|
|
temp[i] = (uint16_t)READ_MEM_U8(rsp->dmem, (addr + (index + i * 4 & 0xf)) & 0xfff) << 7;
|
|
temp[i+4] = (uint16_t)READ_MEM_U8(rsp->dmem, (addr + (index + i * 4 + 8 & 0xf)) & 0xfff) << 7;
|
|
}
|
|
|
|
for (unsigned i = e; i < end; i++)
|
|
writeByteToHalfWords(rsp->cp2.regs[rt].e, i, byteFromHalfWords(temp, i));
|
|
}
|
|
|
|
#define RSP_SFV_CASE(a,b,c,d) \
|
|
WRITE_MEM_U8(rsp->dmem, addr + base, int16_t(reg[a]) >> 7); \
|
|
WRITE_MEM_U8(rsp->dmem, addr + 4 + base, int16_t(reg[b]) >> 7); \
|
|
WRITE_MEM_U8(rsp->dmem, addr + (8 + base & 0xf), int16_t(reg[c]) >> 7); \
|
|
WRITE_MEM_U8(rsp->dmem, addr + (12 + base & 0xf), int16_t(reg[d]) >> 7);
|
|
|
|
void RSP_SFV(RSP::CPUState *rsp, unsigned rt, unsigned e, int offset, unsigned base)
|
|
{
|
|
TRACE_LS(SFV);
|
|
unsigned addr = (rsp->sr[base] + offset * 16) & 0xfff;
|
|
base = addr & 7;
|
|
addr &= ~7;
|
|
|
|
auto *reg = rsp->cp2.regs[rt].e;
|
|
switch (e)
|
|
{
|
|
case 0:
|
|
case 15:
|
|
RSP_SFV_CASE(0,1,2,3)
|
|
break;
|
|
case 1:
|
|
RSP_SFV_CASE(6,7,4,5)
|
|
break;
|
|
case 4:
|
|
RSP_SFV_CASE(1,2,3,0)
|
|
break;
|
|
case 5:
|
|
RSP_SFV_CASE(7,4,5,6)
|
|
break;
|
|
case 8:
|
|
RSP_SFV_CASE(4,5,6,7)
|
|
break;
|
|
case 11:
|
|
RSP_SFV_CASE(3,0,1,2)
|
|
break;
|
|
case 12:
|
|
RSP_SFV_CASE(5,6,7,4)
|
|
break;
|
|
default:
|
|
WRITE_MEM_U8(rsp->dmem, addr + base, 0);
|
|
WRITE_MEM_U8(rsp->dmem, addr + 4 + base, 0);
|
|
WRITE_MEM_U8(rsp->dmem, addr + (8 + base & 0xf), 0);
|
|
WRITE_MEM_U8(rsp->dmem, addr + (12 + base & 0xf), 0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void RSP_LWV(RSP::CPUState *rsp, unsigned rt, unsigned e, int offset, unsigned base)
|
|
{
|
|
TRACE_LS(LWV);
|
|
unsigned addr = rsp->sr[base] + offset * 16;
|
|
for (unsigned i = 16 - e; i < 16 + e; i++)
|
|
{
|
|
writeByteToHalfWords(rsp->cp2.regs[rt].e, i & 0xf, READ_MEM_U8(rsp->dmem, addr & 0xfff));
|
|
addr += 4;
|
|
}
|
|
}
|
|
|
|
void RSP_SWV(RSP::CPUState *rsp, unsigned rt, unsigned e, int offset, unsigned base)
|
|
{
|
|
TRACE_LS(SWV);
|
|
|
|
unsigned addr = (rsp->sr[base] + offset * 16) & 0xfff;
|
|
base = addr & 7;
|
|
addr &= ~7;
|
|
|
|
for (unsigned i = e; i < e + 16; i++)
|
|
WRITE_MEM_U8(rsp->dmem, addr + (base++ & 0xf), byteFromHalfWords(rsp->cp2.regs[rt].e, i & 0xf));
|
|
}
|
|
|
|
void RSP_LQV(RSP::CPUState *rsp, unsigned rt, unsigned e, int offset, unsigned base)
|
|
{
|
|
TRACE_LS(LQV);
|
|
unsigned addr = rsp->sr[base] + offset * 16;
|
|
unsigned end = 16 + e - (addr & 0xf);
|
|
if (end > 16) end = 16;
|
|
|
|
for (unsigned i = e; i < end; i++)
|
|
writeByteToHalfWords(rsp->cp2.regs[rt].e, i & 0xf, READ_MEM_U8(rsp->dmem, addr++ & 0xfff));
|
|
}
|
|
|
|
void RSP_SQV(RSP::CPUState *rsp, unsigned rt, unsigned e, int offset, unsigned base)
|
|
{
|
|
TRACE_LS(SQV);
|
|
unsigned addr = (rsp->sr[base] + offset * 16) & 0xfff;
|
|
|
|
const unsigned end = e + (16 - (addr & 15));
|
|
for (unsigned i = e; i < end; i++)
|
|
WRITE_MEM_U8(rsp->dmem, addr++, byteFromHalfWords(rsp->cp2.regs[rt].e, i & 15));
|
|
}
|
|
|
|
void RSP_LRV(RSP::CPUState *rsp, unsigned rt, unsigned e, int offset, unsigned base)
|
|
{
|
|
TRACE_LS(LRV);
|
|
unsigned addr = rsp->sr[base] + offset * 16;
|
|
const unsigned start = 16 - ((addr & 0xf) - e);
|
|
addr &= ~0xf;
|
|
|
|
for (unsigned i = start; i < 16; i++)
|
|
writeByteToHalfWords(rsp->cp2.regs[rt].e, i & 0xf, READ_MEM_U8(rsp->dmem, addr++ & 0xfff));
|
|
}
|
|
|
|
void RSP_SRV(RSP::CPUState *rsp, unsigned rt, unsigned e, int offset, unsigned base)
|
|
{
|
|
TRACE_LS(SRV);
|
|
unsigned addr = (rsp->sr[base] + offset * 16) & 0xfff;
|
|
const unsigned end = e + (addr & 0xf);
|
|
base = 16 - (addr & 0xf);
|
|
addr &= ~0xf;
|
|
|
|
for (unsigned i = e; i < end; i++)
|
|
WRITE_MEM_U8(rsp->dmem, addr++, byteFromHalfWords(rsp->cp2.regs[rt].e, i + base & 0xf));
|
|
}
|
|
|
|
void RSP_LTV(RSP::CPUState *rsp, unsigned rt, unsigned e, int offset, unsigned base)
|
|
{
|
|
TRACE_LS(LTV);
|
|
unsigned addr = rsp->sr[base] + offset * 16;
|
|
const unsigned start = addr & ~7;
|
|
const unsigned vt0 = rt & ~7;
|
|
addr = start + ((e + (addr & 8)) & 0xf);
|
|
unsigned j = e >> 1;
|
|
|
|
for (unsigned i = 0; i < 16; j++)
|
|
{
|
|
j &= 7;
|
|
writeByteToHalfWords(rsp->cp2.regs[vt0+j].e, i++, READ_MEM_U8(rsp->dmem, addr++ & 0xfff));
|
|
if (addr == start + 16) addr = start;
|
|
writeByteToHalfWords(rsp->cp2.regs[vt0+j].e, i++, READ_MEM_U8(rsp->dmem, addr++ & 0xfff));
|
|
if (addr == start + 16) addr = start;
|
|
}
|
|
}
|
|
|
|
void RSP_STV(RSP::CPUState *rsp, unsigned rt, unsigned e, int offset, unsigned base)
|
|
{
|
|
TRACE_LS(STV);
|
|
e &= ~1;
|
|
rt &= ~7;
|
|
|
|
unsigned addr = (rsp->sr[base] + offset * 16) & 0xfff;
|
|
unsigned element = 16 - e;
|
|
base = (addr & 7) - e;
|
|
addr &= ~7;
|
|
|
|
for (unsigned i = rt; i < rt + 8; i++ )
|
|
{
|
|
WRITE_MEM_U8(rsp->dmem, addr + (base++ & 0xf), byteFromHalfWords(rsp->cp2.regs[i].e, element++ & 0xf));
|
|
WRITE_MEM_U8(rsp->dmem, addr + (base++ & 0xf), byteFromHalfWords(rsp->cp2.regs[i].e, element++ & 0xf));
|
|
}
|
|
}
|
|
}
|