mupen64plus-oldsvn/z64/rsp_recomp.cpp

572 lines
17 KiB
C++

/*
* z64
*
* This program is free software; you can redistribute it and/
* or modify it under the terms of the GNU General Public Li-
* cence as published by the Free Software Foundation; either
* version 2 of the Licence, or any later version.
*
* This program is distributed in the hope that it will be use-
* ful, but WITHOUT ANY WARRANTY; without even the implied war-
* ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public Licence for more details.
*
* You should have received a copy of the GNU General Public
* Licence along with this program; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
* USA.
*
**/
#include "rsp_recomp.h"
#include <assert.h>
#define GENDEBUG
struct gen_t {
UINT32 crc;
int lbc;
rsp_bc_t * bc;
#ifdef GENDEBUG
char name[32];
#endif
};
struct opinfo_t {
int visit, labeled;
int label;
int nbgen;
int szgen;
gen_t * gentable;
gen_t * curgen;
};
struct branch_t {
int start, end;
};
static int curvisit;
static opinfo_t opinfo[0x1000/4];
static int jumps[0x1000];
static int nb_branches;
static branch_t branches[256];
static int nb_labels;
static int labels[256];
#define OPI(pc) opinfo[(pc)>>2]
/*inline*/ void SETLABEL(int pc) {
//printf("%x\n", pc);
//pc &= 0xfff;
assert(pc >= 0 && pc < 0x1000);
if (OPI(pc).labeled != curvisit) {
labels[nb_labels] = pc;
OPI(pc).label = nb_labels++;
assert(nb_labels < sizeof(labels)/sizeof(labels[0]));
OPI(pc).labeled = curvisit;
}
}
#define ABS(addr) (((addr) << 2) & 0xfff)
#define REL(offset) ((pc + ((offset) << 2)) & 0xfff)
static UINT32 prep_gen(int pc, UINT32 crc, int & len)
{
UINT32 op;
int br = 0;
branches[nb_branches].start = pc;
while ( !br )
{
if (OPI(pc).visit == curvisit) {
SETLABEL((pc)&0xfff);
SETLABEL((pc+4)&0xfff);
break;
}
OPI(pc).visit = curvisit;
op = ROPCODE(pc);
crc = ((crc<<1)|(crc>>31))^op^pc;
pc = (pc+4)&0xfff;
len++;
switch (op >> 26)
{
case 0x00: /* SPECIAL */
{
switch (op & 0x3f)
{
case 0x08: /* JR */
br = 1;
break;
case 0x09: /* JALR */
//br = 1;
break;
case 0x0d: /* BREAK */
br = 1;
break;
}
break;
}
case 0x01: /* REGIMM */
{
switch (RTREG)
{
case 0x00: /* BLTZ */
case 0x01: /* BGEZ */
SETLABEL(REL(SIMM16));
break;
case 0x11: /* BGEZAL */
//br = 1;
break;
}
break;
}
case 0x02: /* J */
SETLABEL(ABS(UIMM26));
br = 1;
break;
case 0x04: /* BEQ */
case 0x05: /* BNE */
case 0x06: /* BLEZ */
case 0x07: /* BGTZ */
SETLABEL(REL(SIMM16));
break;
case 0x03: /* JAL */
//SETLABEL(ABS(UIMM26));
//br = 1;
break;
}
}
branches[nb_branches++].end = pc;
assert(nb_branches < sizeof(branches)/sizeof(branches[0]));
return crc;
}
static void rsp_gen(int pc)
{
int i;
curvisit++;
if (!curvisit) {
// we looped, reset all visit counters
for (i=0; i<0x1000/4; i++) {
opinfo[i].visit = 0;
opinfo[i].labeled = 0;
}
curvisit++;
}
nb_branches = 0;
nb_labels = 0;
int len = 0;
UINT32 crc = prep_gen(pc, 0, len);
for (i=0; i<nb_labels; i++) {
if (OPI(labels[i]).visit != curvisit)
crc = prep_gen(labels[i], crc, len);
}
opinfo_t * opi = &OPI(pc);
if (opi->gentable) {
for (i=0; i<opi->nbgen; i++)
if (opi->gentable[i].crc == crc) {
opi->curgen = opi->gentable + i;
return;
}
}
if (opi->nbgen >= opi->szgen) {
if (opi->szgen)
opi->szgen *= 2;
else
opi->szgen = 4;
opi->gentable = (gen_t *) realloc(opi->gentable, sizeof(gen_t)*(opi->szgen));
}
gen_t * gen;
gen = opi->gentable + opi->nbgen++;
gen->crc = crc;
opi->curgen = gen;
// convert to bytecode
int lbc = 0;
static rsp_bc_t bc[0x1000*2+10];
for (i=0; i<nb_branches; i++) {
int pc;
int loopc;
int cont = 1;
rsp_opinfo_t delayed;
delayed.op = 0;
for (pc = branches[i].start; cont || delayed.op; pc = (pc+4)&0xfff) {
UINT32 op = ROPCODE(pc);
// int realpc = pc;
// char s[128];
// rsp_dasm_one(s, realpc, op);
// printf("%d %3x\t%s\n", lbc, realpc, s);
rsp_opinfo_t info;
rsp_get_opinfo(op, &info);
if ((info.flags & RSP_OPINFO_JUMP) && !cont)
info.flags = 0;
else {
int nop = 0;
switch (info.op2) {
case RSP_SLL:
case RSP_SRL:
case RSP_SRA:
if (RDREG == RTREG && SHIFT == 0) nop = 1;
break;
}
if (cont)
jumps[pc] = lbc;
if (!nop) {
bc[lbc].op = op;
bc[lbc].op2 = info.op2;
bc[lbc].flags = info.flags | ((pc&0xffc)<<5-2) | (!cont? (1<<15):0);
lbc++;
}
loopc = (pc+4)&0xfff;
}
if (delayed.op) {
int addop;
const UINT32 op = delayed.op;
switch (delayed.op2) {
case RSP_BLTZ:
case RSP_BGEZ:
case RSP_BEQ:
case RSP_BNE:
case RSP_BLEZ:
case RSP_BGTZ:
addop = RSP_CONDJUMPLOCAL;
bc[lbc].flags = (pc + (SIMM16<<2))&0xfff; // address to be resolved later
break;
case RSP_J:
addop = RSP_JUMPLOCAL;
bc[lbc].flags = (UIMM26<<2)&0xfff; // address to be resolved later
break;
case RSP_BGEZAL:
addop = RSP_CONDJUMP;
break;
case RSP_JAL:
case RSP_JR:
case RSP_JALR:
addop = RSP_JUMP;
break;
}
bc[lbc].op = delayed.op;
bc[lbc].op2 = addop;
lbc++;
}
if (info.flags & RSP_OPINFO_JUMP) {
delayed = info;
} else
delayed.op = 0;
if (((pc + 4)&0xfff) == branches[i].end)
cont = 0;
}
if (bc[lbc-1].op2 != RSP_JUMP &&
bc[lbc-1].op2 != RSP_JUMPLOCAL &&
bc[lbc-1].op2 != RSP_BREAK &&
bc[lbc-1].op2 != RSP_STOP) {
bc[lbc].op = 0;
bc[lbc].op2 = RSP_LOOP;
bc[lbc].flags = loopc; // address to be resolved later
lbc++;
}
}
// resolve local jumps
for (i=0; i<lbc; i++) {
// printf("%d %x\n", i, bc[i].op2);
// if (bc[i].op2 < RSP_CONTROL_OFFS) {
// int realpc = (bc[i].flags>>3)&0xffc;
// char s[128];
// rsp_dasm_one(s, realpc, bc[i].op);
// printf("%3x\t%s\n", realpc, s);
// }
switch (bc[i].op2) {
case RSP_JUMPLOCAL:
case RSP_CONDJUMPLOCAL:
case RSP_LOOP:
{
// int pc;
// for (pc = 0; pc<lbc; pc++)
// if (bc[pc].op2 < RSP_CONTROL_OFFS &&
// !(bc[pc].flags & (1<<15)) &&
// ((bc[pc].flags>>5)<<2) == bc[i].flags)
// break;
// assert(pc < lbc);
// bc[i].flags = pc<<5;
bc[i].flags = jumps[bc[i].flags]<<5;
break;
}
}
}
gen->lbc = lbc;
gen->bc = (rsp_bc_t *) malloc(sizeof(rsp_bc_t)*lbc);
memcpy(gen->bc, bc, sizeof(rsp_bc_t)*lbc);
}
void rsp_invalidate(int begin, int len)
{
//printf("invalidate %x %x\n", begin, len);
begin = 0; len = 0x1000;
assert(begin+len<=0x1000);
while (len > 0) {
OPI(begin).curgen = 0;
begin += 4;
len -= 4;
}
rsp.inval_gen = 1;
}
inline void rsp_execute_one(RSP_REGS & rsp, const UINT32 op)
{
switch (op >> 26)
{
case 0x12: /* COP2 */
{
handle_vector_ops(op);
break;
}
case 0x32: /* LWC2 */ handle_lwc2(op); break;
case 0x3a: /* SWC2 */ handle_swc2(op); break;
default:
{
unimplemented_opcode(op);
break;
}
}
}
static int cond;
static int run(RSP_REGS & rsp, gen_t * gen)
{
int pc = 0;
cond = 0;
for ( ; ; ) {
const rsp_bc_t & bc = gen->bc[pc];
const UINT32 op = bc.op;
const int op2 = bc.op2;
// if (op2 < RSP_CONTROL_OFFS) {
// int realpc = (bc.flags>>3)&0xffc;
// char s[128];
// rsp_dasm_one(s, realpc, op);
// fprintf(stderr, "%3x\t%s\n", realpc, s);
// }
pc++;
switch (op2) {
case RSP_LOOP:
pc = bc.flags>>5;
break;
case RSP_JUMPLOCAL:
case RSP_CONDJUMPLOCAL:
if (cond) {
pc = bc.flags>>5;
cond = 0;
}
break;
case RSP_JUMP:
case RSP_CONDJUMP:
if (cond) {
return 0;
}
break;
#define _LINK(l) rsp.r[l] = ((bc.flags >>3)+8)&0xffc
#define _JUMP_PC(a) { cond=1; rsp.nextpc = ((a) & 0xfff); }
#define _JUMP_PC_L(a, l) { _LINK(l); _JUMP_PC(a); }
#define _JUMP_REL(a) _JUMP_PC(((bc.flags >>3)+4+(a<<2))&0xffc)
#define _JUMP_REL_L(a, l) _JUMP_PC_L(((bc.flags >>3)+4+(a<<2))&0xffc, l)
case RSP_SLL: if (RDREG) RDVAL = (UINT32)RTVAL << SHIFT; break;
case RSP_SRL: if (RDREG) RDVAL = (UINT32)RTVAL >> SHIFT; break;
case RSP_SRA: if (RDREG) RDVAL = (INT32)RTVAL >> SHIFT; break;
case RSP_SLLV: if (RDREG) RDVAL = (UINT32)RTVAL << (RSVAL & 0x1f); break;
case RSP_SRLV: if (RDREG) RDVAL = (UINT32)RTVAL >> (RSVAL & 0x1f); break;
case RSP_SRAV: if (RDREG) RDVAL = (INT32)RTVAL >> (RSVAL & 0x1f); break;
case RSP_JR: _JUMP_PC(RSVAL); break;
case RSP_JALR: _JUMP_PC_L(RSVAL, RDREG); break;
case RSP_BREAK:
{
*z64_rspinfo.SP_STATUS_REG |= (SP_STATUS_HALT | SP_STATUS_BROKE );
if ((*z64_rspinfo.SP_STATUS_REG & SP_STATUS_INTR_BREAK) != 0 ) {
*z64_rspinfo.MI_INTR_REG |= 1;
z64_rspinfo.CheckInterrupts();
}
return 1;
}
case RSP_ADD: if (RDREG) RDVAL = (INT32)(RSVAL + RTVAL); break;
case RSP_ADDU: if (RDREG) RDVAL = (INT32)(RSVAL + RTVAL); break;
case RSP_SUB: if (RDREG) RDVAL = (INT32)(RSVAL - RTVAL); break;
case RSP_SUBU: if (RDREG) RDVAL = (INT32)(RSVAL - RTVAL); break;
case RSP_AND: if (RDREG) RDVAL = RSVAL & RTVAL; break;
case RSP_OR: if (RDREG) RDVAL = RSVAL | RTVAL; break;
case RSP_XOR: if (RDREG) RDVAL = RSVAL ^ RTVAL; break;
case RSP_NOR: if (RDREG) RDVAL = ~(RSVAL | RTVAL); break;
case RSP_SLT: if (RDREG) RDVAL = (INT32)RSVAL < (INT32)RTVAL; break;
case RSP_SLTU: if (RDREG) RDVAL = (UINT32)RSVAL < (UINT32)RTVAL; break;
case RSP_BLTZ: if ((INT32)(RSVAL) < 0) cond = 1; break;
case RSP_BGEZ: if ((INT32)(RSVAL) >= 0) cond = 1; break;
case RSP_BGEZAL: _LINK(31); if ((INT32)(RSVAL) >= 0) _JUMP_REL(SIMM16); break;
case RSP_J: cond = 1; break;
case RSP_JAL: _JUMP_PC_L(UIMM26<<2, 31); break;
case RSP_BEQ: if (RSVAL == RTVAL) cond = 1; break;
case RSP_BNE: if (RSVAL != RTVAL) cond = 1; break;
case RSP_BLEZ: if ((INT32)RSVAL <= 0) cond = 1; break;
case RSP_BGTZ: if ((INT32)RSVAL > 0) cond = 1; break;
case RSP_ADDI: if (RTREG) RTVAL = (INT32)(RSVAL + SIMM16); break;
case RSP_ADDIU: if (RTREG) RTVAL = (INT32)(RSVAL + SIMM16); break;
case RSP_SLTI: if (RTREG) RTVAL = (INT32)(RSVAL) < ((INT32)SIMM16); break;
case RSP_SLTIU: if (RTREG) RTVAL = (UINT32)(RSVAL) < (UINT32)((INT32)SIMM16); break;
case RSP_ANDI: if (RTREG) RTVAL = RSVAL & UIMM16; break;
case RSP_ORI: if (RTREG) RTVAL = RSVAL | UIMM16; break;
case RSP_XORI: if (RTREG) RTVAL = RSVAL ^ UIMM16; break;
case RSP_LUI: if (RTREG) RTVAL = UIMM16 << 16; break;
case RSP_COP0:
{
switch ((op >> 21) & 0x1f)
{
case 0x00: /* MFC0 */
if (RTREG)
RTVAL = get_cop0_reg(RDREG);
break;
case 0x04: /* MTC0 */
set_cop0_reg(RDREG, RTVAL);
if (rsp.inval_gen) {
rsp.inval_gen = 0;
sp_pc = ((bc.flags >>3) + 4)&0xffc;
return 2;
}
break;
default:
printf("unimplemented cop0 %x (%x)\n", (op >> 21) & 0x1f, op);
break;
}
break;
}
case RSP_MFC2:
{
// 31 25 20 15 10 6 0
// ---------------------------------------------------
// | 010010 | 00000 | TTTTT | DDDDD | IIII | 0000000 |
// ---------------------------------------------------
//
int el = (op >> 7) & 0xf;
UINT16 b1 = VREG_B(VS1REG, (el+0) & 0xf);
UINT16 b2 = VREG_B(VS1REG, (el+1) & 0xf);
if (RTREG) RTVAL = (INT32)(INT16)((b1 << 8) | (b2));
break;
}
case RSP_CFC2:
{
// 31 25 20 15 10 0
// ------------------------------------------------
// | 010010 | 00010 | TTTTT | DDDDD | 00000000000 |
// ------------------------------------------------
//
// VP to sign extend or to not sign extend ?
//if (RTREG) RTVAL = (INT16)rsp.flag[RDREG];
if (RTREG) RTVAL = rsp.flag[RDREG];
break;
}
case RSP_MTC2:
{
// 31 25 20 15 10 6 0
// ---------------------------------------------------
// | 010010 | 00100 | TTTTT | DDDDD | IIII | 0000000 |
// ---------------------------------------------------
//
int el = (op >> 7) & 0xf;
VREG_B(VS1REG, (el+0) & 0xf) = (RTVAL >> 8) & 0xff;
VREG_B(VS1REG, (el+1) & 0xf) = (RTVAL >> 0) & 0xff;
break;
}
case RSP_CTC2:
{
// 31 25 20 15 10 0
// ------------------------------------------------
// | 010010 | 00110 | TTTTT | DDDDD | 00000000000 |
// ------------------------------------------------
//
rsp.flag[RDREG] = RTVAL & 0xffff;
break;
}
case RSP_LB: if (RTREG) RTVAL = (INT32)(INT8)READ8(RSVAL + SIMM16); break;
case RSP_LH: if (RTREG) RTVAL = (INT32)(INT16)READ16(RSVAL + SIMM16); break;
case RSP_LW: if (RTREG) RTVAL = READ32(RSVAL + SIMM16); break;
case RSP_LBU: if (RTREG) RTVAL = (UINT8)READ8(RSVAL + SIMM16); break;
case RSP_LHU: if (RTREG) RTVAL = (UINT16)READ16(RSVAL + SIMM16); break;
case RSP_SB: WRITE8(RSVAL + SIMM16, RTVAL); break;
case RSP_SH: WRITE16(RSVAL + SIMM16, RTVAL); break;
case RSP_SW: WRITE32(RSVAL + SIMM16, RTVAL); break;
default:
switch (op >> 26)
{
case 0x12: /* COP2 */
handle_vector_ops(op);
break;
case 0x32: /* LWC2 */
handle_lwc2(op);
break;
case 0x3a: /* SWC2 */
handle_swc2(op);
break;
}
}
}
}
int rsp_gen_cache_hit;
int rsp_gen_cache_miss;
int rsp_jump(int pc)
{
pc &= 0xfff;
sp_pc = pc;
rsp.nextpc = ~0;
opinfo_t * opi = &OPI(pc);
gen_t * gen = opi->curgen;
rsp_gen_cache_hit++;
if (!gen) {
rsp_gen_cache_miss++;
rsp_gen(pc);
}
gen = opi->curgen;
//fprintf(stderr, "rsp_jump %x (%s)\n", pc, gen->name);
int res = run(rsp, gen);
//fprintf(stderr, "r31 %x from %x nextpc %x pc %x res %d (%s)\n", rsp.r[31], pc, rsp.nextpc, sp_pc, res, gen->name);
if (rsp.nextpc != ~0)
{
sp_pc = (rsp.nextpc & 0xfff);
rsp.nextpc = ~0;
}
else
{
//sp_pc = ((sp_pc+4)&0xfff);
}
return res;
}