/* * 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.h" #include #include #include #include #include #define GENDEBUG typedef int (* gen_f)(RSP_REGS & rsp); struct gen_t { UINT32 crc; void * lib; gen_f f; #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 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 char tmps[1024]; static char * delayed; static int has_cond; #define COND \ has_cond = 1, fprintf #define NOCOND() \ if (cont && OPI((pc+4)&0xfff).labeled == curvisit) { \ COND(fp, "cond = 1; \n"); \ } else \ has_cond = 0 static void D_JUMP_ABS(UINT32 addr) { int a = addr&0xfff; sprintf(tmps, "%s { /*if (rsp.inval_gen) { rsp.nextpc=0x%x; return 0; }*/ %s goto L%d; }", has_cond? "if (cond)":"", a, has_cond? "cond=0; ":"", OPI(a).label); delayed = tmps; } static void D_JUMP_REL(int pc, int offset) { D_JUMP_ABS(pc+4 + ((offset) << 2)); } static void D_JUMP() { sprintf(tmps, "%s { return 0; }", has_cond? "if (cond)":""); delayed = tmps; } static void D_JUMPL(int pc) { sprintf(tmps, "%s { \n" "%s" " int res;\n" " if (res = rsp_jump(rsp.nextpc)) return res; \n" " if (/*rsp.inval_gen || */sp_pc != 0x%x) return 0; \n" "}", has_cond? "if (cond)":"", has_cond?" cond=0; \n":"", (pc+8)&0xfff); delayed = tmps; has_cond = 1; } static void dogen(const char * s, UINT32 op, FILE * fp) { fprintf(fp, "#define op 0x%x\n%s\n#undef op\n", op, s); } #define GEN(s) dogen(s, op, fp) static void rsp_gen(int pc) { int i; const char * old_delayed; int oldbr, br; 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; igentable) { for (i=0; inbgen; 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++; opi->curgen = gen; #ifdef GENDEBUG strcpy(gen->name, lib); #endif gen->crc = crc; gen->lib = dlopen(lib, RTLD_NOW); if (gen->lib) { gen->f = (gen_f) dlsym(gen->lib, sym); assert(gen->f); fprintf(stderr, "reloaded %s\n", lib); return; } // else // printf("%s\n", dlerror()); sprintf(src, "z64/rspgen/%x-%x-%x.cpp", crc, pc, len); FILE * fp = fopen(src, "w"); fprintf(fp, "#include \"rsp.h\"\n" "\n" "extern \"C\" {\n" "int %s() {\n" "int cond=0;\n", sym); for (i=0; i %x\n", branches[i].start, branches[i].end-4); for (pc=branches[i].start; cont || delayed; pc = (pc+4)&0xfff) { UINT32 op = ROPCODE(pc); char s[128]; rsp_dasm_one(s, pc, op); if (cont && OPI(pc).labeled == curvisit) fprintf(fp, "L%d: ;\n", OPI(pc).label); //fprintf(fp, "/* %3x\t%s */\n", pc, s); fprintf(fp, "GENTRACE(\"%3x\t%s\\n\");\n", pc, s); oldbr = br; br = 0; old_delayed = delayed; delayed = 0; if (((pc+4)&0xfff)==branches[i].end) cont = 0; switch (op >> 26) { case 0x00: /* SPECIAL */ { switch (op & 0x3f) { case 0x08: /* JR */ if (!old_delayed) { br = 1|8|16; NOCOND(); D_JUMP(); } break; case 0x09: /* JALR */ if (!old_delayed) { br = 1; NOCOND(); D_JUMPL(pc); } break; case 0x0d: /* BREAK */ br = 2|8; //delayed = "return 1;"; has_cond = 0; break; } break; } case 0x01: /* REGIMM */ { switch (RTREG) { case 0x00: /* BLTZ */ if (!old_delayed) { COND(fp, " cond=(INT32)(_RSVAL(0x%x)) < 0;\n", op); D_JUMP_REL(pc, _SIMM16(op)); br = 4; } break; case 0x01: /* BGEZ */ if (!old_delayed) { COND(fp, " cond=(INT32)(_RSVAL(0x%x)) >= 0;\n", op); D_JUMP_REL(pc, _SIMM16(op)); br = 4; } break; case 0x11: /* BGEZAL */ br = 1; COND(fp, "cond=(INT32)(_RSVAL(0x%x)) >= 0;\n", op); D_JUMPL(pc); break; } break; } case 0x02: /* J */ if (!old_delayed) { NOCOND(); D_JUMP_ABS(_UIMM26(op) <<2); br = 4|8|16; } break; case 0x04: /* BEQ */ if (!old_delayed) { COND(fp, " cond=_RSVAL(0x%0x) == _RTVAL(0x%0x);\n", op, op); D_JUMP_REL(pc, _SIMM16(op)); br = 4; } break; case 0x05: /* BNE */ if (!old_delayed) { COND(fp, " cond=_RSVAL(0x%0x) != _RTVAL(0x%0x);\n", op, op); D_JUMP_REL(pc, _SIMM16(op)); br = 4; } break; case 0x06: /* BLEZ */ if (!old_delayed) { COND(fp, " cond=(INT32)_RSVAL(0x%0x) <= 0;\n", op); D_JUMP_REL(pc, _SIMM16(op)); br = 4; } break; case 0x07: /* BGTZ */ if (!old_delayed) { COND(fp, " cond=(INT32)_RSVAL(0x%0x) > 0;\n", op); D_JUMP_REL(pc, _SIMM16(op)); br = 4; } break; case 0x03: /* JAL */ if (!old_delayed) { br = 1; NOCOND(); D_JUMPL(pc); } break; } if (!(br&4) && (!old_delayed || !br)) { if (br && !(br&16)) { fprintf(fp, "sp_pc = 0x%x;\n", (pc + 4)&0xfff); } //fprintf(fp, "rsp_execute_one(0x%x);\n", op); switch (op >> 26) { case 0x00: /* SPECIAL */ { switch (op & 0x3f) { case 0x00: /* SLL */ if (RDREG) GEN("RDVAL = (UINT32)RTVAL << SHIFT;"); break; case 0x02: /* SRL */ if (RDREG) GEN("RDVAL = (UINT32)RTVAL >> SHIFT; "); break; case 0x03: /* SRA */ if (RDREG) GEN("RDVAL = (INT32)RTVAL >> SHIFT; "); break; case 0x04: /* SLLV */ if (RDREG) GEN("RDVAL = (UINT32)RTVAL << (RSVAL & 0x1f); "); break; case 0x06: /* SRLV */ if (RDREG) GEN("RDVAL = (UINT32)RTVAL >> (RSVAL & 0x1f); "); break; case 0x07: /* SRAV */ if (RDREG) GEN("RDVAL = (INT32)RTVAL >> (RSVAL & 0x1f); "); break; case 0x08: /* JR */ GEN("JUMP_PC(RSVAL); "); break; case 0x09: /* JALR */ GEN("JUMP_PC_L(RSVAL, RDREG); "); break; case 0x0d: /* BREAK */ { GEN( " \ *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(); \ } \ rsp.nextpc = ~0; \ return 1; \ "); break; } case 0x20: /* ADD */ if (RDREG) GEN("RDVAL = (INT32)(RSVAL + RTVAL); "); break; case 0x21: /* ADDU */ if (RDREG) GEN("RDVAL = (INT32)(RSVAL + RTVAL); "); break; case 0x22: /* SUB */ if (RDREG) GEN("RDVAL = (INT32)(RSVAL - RTVAL); "); break; case 0x23: /* SUBU */ if (RDREG) GEN("RDVAL = (INT32)(RSVAL - RTVAL); "); break; case 0x24: /* AND */ if (RDREG) GEN("RDVAL = RSVAL & RTVAL; "); break; case 0x25: /* OR */ if (RDREG) GEN("RDVAL = RSVAL | RTVAL; "); break; case 0x26: /* XOR */ if (RDREG) GEN("RDVAL = RSVAL ^ RTVAL; "); break; case 0x27: /* NOR */ if (RDREG) GEN("RDVAL = ~(RSVAL | RTVAL); "); break; case 0x2a: /* SLT */ if (RDREG) GEN("RDVAL = (INT32)RSVAL < (INT32)RTVAL; "); break; case 0x2b: /* SLTU */ if (RDREG) GEN("RDVAL = (UINT32)RSVAL < (UINT32)RTVAL; "); break; default: GEN("unimplemented_opcode(op); "); break; } break; } case 0x01: /* REGIMM */ { switch (RTREG) { case 0x00: /* BLTZ */ GEN("if ((INT32)(RSVAL) < 0) JUMP_REL(SIMM16); "); break; case 0x01: /* BGEZ */ GEN("if ((INT32)(RSVAL) >= 0) JUMP_REL(SIMM16); "); break; // VP according to the doc, link is performed even when condition fails case 0x11: /* BGEZAL */ GEN("LINK(31); if ((INT32)(RSVAL) >= 0) JUMP_REL(SIMM16); "); break; //case 0x11: /* BGEZAL */ if ((INT32)(RSVAL) >= 0) JUMP_REL_L(SIMM16, 31); break; default: GEN("unimplemented_opcode(op); "); break; } break; } case 0x02: /* J */ GEN("JUMP_ABS(UIMM26); "); break; case 0x03: /* JAL */ GEN("JUMP_ABS_L(UIMM26, 31); "); break; case 0x04: /* BEQ */ GEN("if (RSVAL == RTVAL) JUMP_REL(SIMM16); "); break; case 0x05: /* BNE */ GEN("if (RSVAL != RTVAL) JUMP_REL(SIMM16); "); break; case 0x06: /* BLEZ */ GEN("if ((INT32)RSVAL <= 0) JUMP_REL(SIMM16); "); break; case 0x07: /* BGTZ */ GEN("if ((INT32)RSVAL > 0) JUMP_REL(SIMM16); "); break; case 0x08: /* ADDI */ if (RTREG) GEN("RTVAL = (INT32)(RSVAL + SIMM16); "); break; case 0x09: /* ADDIU */ if (RTREG) GEN("RTVAL = (INT32)(RSVAL + SIMM16); "); break; case 0x0a: /* SLTI */ if (RTREG) GEN("RTVAL = (INT32)(RSVAL) < ((INT32)SIMM16); "); break; case 0x0b: /* SLTIU */ if (RTREG) GEN("RTVAL = (UINT32)(RSVAL) < (UINT32)((INT32)SIMM16); "); break; case 0x0c: /* ANDI */ if (RTREG) GEN("RTVAL = RSVAL & UIMM16; "); break; case 0x0d: /* ORI */ if (RTREG) GEN("RTVAL = RSVAL | UIMM16; "); break; case 0x0e: /* XORI */ if (RTREG) GEN("RTVAL = RSVAL ^ UIMM16; "); break; case 0x0f: /* LUI */ if (RTREG) GEN("RTVAL = UIMM16 << 16; "); break; case 0x10: /* COP0 */ { switch ((op >> 21) & 0x1f) { case 0x00: /* MFC0 */ if (RTREG) GEN("RTVAL = get_cop0_reg(rsp, RDREG); "); break; case 0x04: /* MTC0 */ { GEN("set_cop0_reg(rsp, RDREG, RTVAL); \n"); if (RDREG == 0x08/4) { fprintf(fp, "if (rsp.inval_gen) {\n" " rsp.inval_gen = 0;\n" " sp_pc = 0x%x; \n" " return 2; \n" "}\n" , (pc + 4)&0xfff); } break; } default: printf("unimplemented cop0 %x (%x)\n", (op >> 21) & 0x1f, op); break; } break; } case 0x12: /* COP2 */ { switch ((op >> 21) & 0x1f) { case 0x00: /* MFC2 */ { // 31 25 20 15 10 6 0 // --------------------------------------------------- // | 010010 | 00000 | TTTTT | DDDDD | IIII | 0000000 | // --------------------------------------------------- // if (RTREG) GEN("\ {int el = (op >> 7) & 0xf;\ UINT16 b1 = VREG_B(VS1REG, (el+0) & 0xf);\ UINT16 b2 = VREG_B(VS1REG, (el+1) & 0xf);\ RTVAL = (INT32)(INT16)((b1 << 8) | (b2));}\ "); break; } case 0x02: /* 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) GEN("RTVAL = rsp.flag[RDREG];"); break; } case 0x04: /* MTC2 */ { // 31 25 20 15 10 6 0 // --------------------------------------------------- // | 010010 | 00100 | TTTTT | DDDDD | IIII | 0000000 | // --------------------------------------------------- // GEN("\ {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 0x06: /* CTC2 */ { // 31 25 20 15 10 0 // ------------------------------------------------ // | 010010 | 00110 | TTTTT | DDDDD | 00000000000 | // ------------------------------------------------ // GEN("rsp.flag[RDREG] = RTVAL & 0xffff;"); break; } case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: case 0x18: case 0x19: case 0x1a: case 0x1b: case 0x1c: case 0x1d: case 0x1e: case 0x1f: { GEN("handle_vector_ops(rsp, op);"); break; } default: GEN("unimplemented_opcode(op); "); break; } break; } case 0x20: /* LB */ if (RTREG) GEN("RTVAL = (INT32)(INT8)READ8(RSVAL + SIMM16); "); break; case 0x21: /* LH */ if (RTREG) GEN("RTVAL = (INT32)(INT16)READ16(RSVAL + SIMM16); "); break; case 0x23: /* LW */ if (RTREG) GEN("RTVAL = READ32(RSVAL + SIMM16); "); break; case 0x24: /* LBU */ if (RTREG) GEN("RTVAL = (UINT8)READ8(RSVAL + SIMM16); "); break; case 0x25: /* LHU */ if (RTREG) GEN("RTVAL = (UINT16)READ16(RSVAL + SIMM16); "); break; case 0x28: /* SB */ GEN("WRITE8(RSVAL + SIMM16, RTVAL); "); break; case 0x29: /* SH */ GEN("WRITE16(RSVAL + SIMM16, RTVAL); "); break; case 0x2b: /* SW */ GEN("WRITE32(RSVAL + SIMM16, RTVAL); "); break; case 0x32: /* LWC2 */ GEN("handle_lwc2(rsp, op); "); break; case 0x3a: /* SWC2 */ GEN("handle_swc2(rsp, op); "); break; default: { GEN("unimplemented_opcode(op);"); break; } } // if (br) { // if (br & 2) // fprintf(fp, "return 1;\n"); // else // fprintf(fp, "return 0;\n"); // } } if (old_delayed) fprintf(fp, "%s\n", old_delayed); } if (!((/*br|*/oldbr)&8) && ((!oldbr && !(br&2)) || has_cond)) { fprintf(fp, "/* jumping back to %x */\ngoto L%d;\n", pc, OPI(pc).label); assert(OPI(pc).labeled == curvisit); } } fprintf(fp, "}}\n"); fclose(fp); pid_t pid = fork(); // SDL redirect these signals, but we need them untouched for waitpid call signal(17, 0); signal(11, 0); if (!pid) { // char s[128]; // atexit(0); // sprintf(s, "gcc -Iz64 -g -shared -O2 %s -o %s", src, lib); // system(s); // exit(0); //setsid(); //execl("/usr/bin/gcc", "/usr/bin/gcc", "-Iz64", "-shared", "-g", "-O3", "-fomit-frame-pointer", src, "-o", lib, "-finline-limit=10000", 0); //execl("/usr/bin/gcc", "/usr/bin/gcc", "-Iz64", "-shared", "-O3", src, "-o", lib, "-fomit-frame-pointer", "-ffast-math", "-funroll-loops", "-fforce-addr", "-finline-limit=10000", 0); //execl("/usr/bin/gcc", "/usr/bin/gcc", "-Iz64", "-shared", "-O3", src, "-o", lib, "-fomit-frame-pointer", "-ffast-math", "-funroll-loops", "-fforce-addr", "-finline-limit=10000", "-m3dnow", "-mmmx", "-msse", "-msse2", "-mfpmath=sse", 0); execl("/usr/bin/gcc", "/usr/bin/gcc", "-Iz64", "-shared", "-O6", src, "-o", lib, "-fomit-frame-pointer", "-ffast-math", "-funroll-loops", "-fforce-addr", "-finline-limit=10000", "-m3dnow", "-mmmx", "-msse", "-msse2", 0); printf("gnii ??\n"); exit(0); } waitpid(pid, 0, __WALL); gen->lib = dlopen(lib, RTLD_NOW); if (!gen->lib) printf("%s\n", dlerror()); assert(gen->lib); fprintf(stderr, "created and loaded %s\n", lib); gen->f = (gen_f) dlsym(gen->lib, sym); assert(gen->f); } 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; } int rsp_jump(int pc) { pc &= 0xfff; sp_pc = pc; rsp.nextpc = ~0; opinfo_t * opi = &OPI(pc); gen_t * gen = opi->curgen; if (!gen) rsp_gen(pc); gen = opi->curgen; GENTRACE("rsp_jump %x (%s)\n", pc, gen->name); int res = gen->f(rsp); GENTRACE("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; }