/************************************************************************** * * * Copyright (C) 2000-2001, Eclipse Productions * * * * Offical Source code of the Apollo Project. DO NOT DISTRIBUTE! Any * * unauthorized distribution of these files in any way, shape, or form * * is a direct infringement on the copyrights entitled to Eclipse * * Productions and you will be prosecuted to the fullest extent of * * the law. Have a nice day... ^_^ * * * *************************************************************************/ /************************************************************************** * * Revision History: * Date: Comment: * ----------------------------------------------------------------------- * ??-??-?? Initial Version (Andrew) * 06-02-00 Revised MULT and SRAV (Andrew) * 06-19-00 Rewrote some of the emulate code * 11-01-01 Rewrote parts of the file... going to keep up on history now * 11-11-01 Removed COP1 Unusable when it shouldn't be checked. * Added LL/SC for Gauntlet Legends * **************************************************************************/ /************************************************************************** * * Notes: * * -MULT and SRAV use 64bit mult/shifts when it is not necessary. This * causes a slowdown in emulation, but not innacurate emulation. * **************************************************************************/ #include #include #include // For status window #include #include #include #include #include #include #include #include "WinMain.h" #include "EmuMain.h" #include "CpuMain.h" #include "resource.h" #pragma warning( disable : 4244 ) #define CPU_ERROR(x,y) Debug(0,"%s %08X",x,y) #define BREAKPOINT 0xFFFFFFF #define FAST_INFINITE_LOOP #define ENABLE_RDRAM_HACK #ifdef FAST_INFINITE_LOOP #define FAST_INFINITE_LOOP_JUMP extern u32 VsyncTime,instructions; if (target==pc-4 && pr32(pc)==0) { InterruptTime = 0; }; #define FAST_INFINITE_LOOP_BRANCH extern u32 VsyncTime,instructions; if (target==pc-4 && pr32(pc)==0 && sop.rt==sop.rs) { InterruptTime = 0; }; #else #define FAST_INFINITE_LOOP_JUMP // #define FAST_INFINITE_LOOP_BRANCH #endif extern u32 VsyncTime; extern u32 VsyncInterrupt; u64 CpuRegs[32]; u32 MmuRegs[32]; R4K_FPU_union FpuRegs; u32 FpuControl[32]; s64 cpuHi; s64 cpuLo; u32 instructions = 0; u32 SystemType; u32 pc; char InterruptNeeded=0; s_sop sop; extern void (*r4300i[0x40])(); extern void (*special[0x40])(); extern void (*regimm[0x20])(); extern void (*MmuSpecial[0x40])(); extern void (*MmuNormal[0x20])(); extern void (*FpuCommands[0x40])(); extern DWORD fsize; // Needed for some rom hacks int inDelay=0; // In a delay slot void OpcodeLookup (DWORD, char *); // Used for Debugging, Defined in R4KDebugger //void DynaReset (); // Reset the dynarec... void doInstr(void) { inDelay = 1; // if (pc < 0xA0000000) // if (TLBLUT[pc>>12] > 0xFFFFFFF0) // __asm int 3; opcode = vr32(pc); *(DWORD*)&sop = opcode; r4300i[sop.op](); inDelay = 0; } unsigned long GenerateCRC (unsigned char *data, int size); void DisassembleRange (u32 Start, u32 End); void OpcodeLookup (DWORD addy, char *out); void ClearEventList (); void InitRegisters () { /* CpuRegs[0]=0x0000000000000000; CpuRegs[6]=0xFFFFFFFFA4001F0C; CpuRegs[7]=0xFFFFFFFFA4001F08; CpuRegs[8]=0x00000000000000C0; CpuRegs[9]=0x0000000000000000; CpuRegs[10]=0x0000000000000040; CpuRegs[11]=0xFFFFFFFFA4000040; CpuRegs[16]=0x0000000000000000; CpuRegs[17]=0x0000000000000000; CpuRegs[18]=0x0000000000000000; CpuRegs[19]=0x0000000000000000; CpuRegs[21]=0x0000000000000000; CpuRegs[26]=0x0000000000000000; CpuRegs[27]=0x0000000000000000; CpuRegs[28]=0x0000000000000000; CpuRegs[29]=0xFFFFFFFFA4001FF0; CpuRegs[30]=0x0000000000000000; */ *(DWORD *)&idmem[0x1004] = 0x8DA807FC; /* CpuRegs[5]=0x000000005493FB9A; CpuRegs[14]=0xFFFFFFFFC2C20384; */ *(DWORD *)&idmem[0x1000] = 0x3C0DBFC0; *(DWORD *)&idmem[0x1008] = 0x25AD07C0; *(DWORD *)&idmem[0x100C] = 0x31080080; *(DWORD *)&idmem[0x1010] = 0x5500FFFC; *(DWORD *)&idmem[0x1014] = 0x3C0DBFC0; *(DWORD *)&idmem[0x1018] = 0x8DA80024; *(DWORD *)&idmem[0x101C] = 0x3C0BB000; /* CpuRegs[1]=0x0000000000000000; CpuRegs[2]=0xFFFFFFFFF58B0FBF; CpuRegs[3]=0xFFFFFFFFF58B0FBF; CpuRegs[4]=0x0000000000000FBF; CpuRegs[12]=0xFFFFFFFF9651F81E; CpuRegs[13]=0x000000002D42AAC5; CpuRegs[15]=0x0000000056584D60; CpuRegs[22]=0x0000000000000091; CpuRegs[25]=0xFFFFFFFFCDCE565F; CpuRegs[20]=0x0000000000000001; CpuRegs[23]=0x0000000000000000; CpuRegs[24]=0x0000000000000003;*/ CpuRegs[0x00] = 0; CpuRegs[0x01] = 0; CpuRegs[0x02] = 0xffffffffd1731be9; CpuRegs[0x03] = 0xffffffffd1731be9; CpuRegs[0x04] = 0x01be9; CpuRegs[0x05] = 0xfffffffff45231e5; CpuRegs[0x06] = 0xffffffffa4001f0c; CpuRegs[0x07] = 0xffffffffa4001f08; CpuRegs[0x08] = 0x070; CpuRegs[0x09] = 0; CpuRegs[0x0a] = 0x040; CpuRegs[0x0b] = 0xffffffffa4000040; CpuRegs[0x0c] = 0xffffffffd1330bc3; CpuRegs[0x0d] = 0xffffffffd1330bc3; CpuRegs[0x0e] = 0x025613a26; CpuRegs[0x0f] = 0x02ea04317; CpuRegs[0x10] = 0; CpuRegs[0x11] = 0; CpuRegs[0x12] = 0; CpuRegs[0x13] = 0; CpuRegs[0x14] = (u64)SystemType; CpuRegs[0x15] = 0; CpuRegs[0x17] = 0x06; CpuRegs[0x18] = 0; CpuRegs[0x19] = 0xffffffffd73f2993; CpuRegs[0x1a] = 0; CpuRegs[0x1b] = 0; CpuRegs[0x1c] = 0; CpuRegs[0x1d] = 0xffffffffa4001ff0; CpuRegs[0x1e] = 0; CpuRegs[0x1f] = 0xffffffffa4001550; } void ResetCPU (void) { char Buffer[255]; DWORD bootcode; #ifdef ENABLE_RDRAM_HACK u32 hack; #endif void InitMMU(); memcpy(idmem, RomMemory, 0x1000); memset(&CpuRegs, 0, sizeof(CpuRegs)); memset(&MmuRegs, 0, sizeof(MmuRegs)); memset(&FpuRegs, 0, sizeof(FpuRegs)); memset(&FpuControl, 0, sizeof(FpuControl)); bootcode = GenerateCRC (idmem+0x40, 0x1000-0x40); InitMMU(); cpuHi = cpuLo = 0; pc = 0xA4000040; //pc = 0xBFC00000; sprintf (Buffer, "Now playing: %s", rInfo.InternalName); SendMessage(hwndStatus, SB_SETTEXT, 0, (LPARAM)(LPSTR) Buffer); ClearEventList (); // Clears the Interrupt Processing... TLBLUT.ResetTLB(); // Clear TLB Table //DynaReset (); switch (RomHeader.Country_Code) { // NTSC case 0x45: case 0x4A: SystemType = 1; break; // PAL case 0x44: case 0x50: case 0x55: case 0x59: case 0x58: SystemType = 0; break; default: SystemType = 1; break; } Debug (0, "CC: %X", RomHeader.Country_Code); #ifdef ENABLE_RDRAM_HACK hack = 0x318; #endif switch (bootcode) { case 0xB9C47DC8: // CIC-NUS-6102 /* CIC-NUS #2 */ CpuRegs[22] = (u64)0x000000000000003F; Debug (0, "CIC-NUS-6102 Detected..."); break; case 0xEDCE5AD9: // CIC-NUS-6103 CpuRegs[22] = (u64)0x0000000000000078; Debug (0, "CIC-NUS-6103 Detected..."); break; case 0xB53C588A: // CIC-NUS-6105 CpuRegs[22] = (u64)0x0000000000000091; #ifdef ENABLE_RDRAM_HACK hack = 0x3F0; #endif Debug (0, "CIC-NUS-6105 Detected..."); break; case 0x57BB4128: // Yoshi Story??? case 0x6D8ED9C: // F-Zero X // CIC-NUS-6106 CpuRegs[22] = (u64)0x0000000000000085; Debug (0, "CIC-NUS-6106 Detected..."); break; default: Debug (1, "Unknown Bootcode... Please e-mail Azimer with the following Information: \r\nCRC: %X, RomTitle: %s", bootcode, rInfo.InternalName); cpuIsReset = true; cpuIsDone = true; } InitRegisters (); //DisassembleRange (0xA4000040, 0xA4001000); // Disassemble the Bootcode #ifdef ENABLE_RDRAM_HACK while ((pc > 0xA4000000)) { //instructions += incrementer; InterruptTime -= incrementer; ((u32*)&sop)[0] = vr32(pc); pc+=4; r4300i[sop.op](); } if (RegSettings.isPakInstalled == true) { pw32 (hack , 0x00800000); // Memory Size Hack } else { pw32 (hack , 0x00400000); // Memory Size Hack } u64 crc = (u64)RomHeader.CRC1 << 32 | RomHeader.CRC2; Debug (0, "CRC: %08X%08X CC: %02X", RomHeader.CRC1, RomHeader.CRC2, RomHeader.Country_Code); pw32 (0xA02FE1C0, 0xAD170014); // DK64 Hack... pw32 (0xA02FB1F4, 0xAD090010); // Banjo Tooie Hack pw32 (0xA5000508, 0x05080508); // F-Zero X Hack for N64DD - Thanks F|RES and LaC! // Golden Eye 007 (USA) (Need to add JAP and EURO versions...) if ((crc == 0xdcbc50d109fd1aa3) && (RomHeader.Country_Code == 0x45)) { //TLBLUT.mapmem (0x7f000000,0x10034b30,0x1000000); // TODO: I need to fix this so it works again... } #endif // void InitCoreSync (); // InitCoreSync (); } void r4300i_HandleErrorRegister(void) { char buffer[255]; Debug (0, "CpuRegs[0] != 0, PC = %08X", pc-4); CpuRegs[0] = 0; OpcodeLookup(pc-4, buffer); Debug (0, buffer); } #define MAXPCSIZE 0x7F u32 lastPC[MAXPCSIZE+1]; u32 lastPCPtr = 0; void Emulate(void) { if (RegSettings.dynamicEnabled == true) { //RegSettings.dynamicEnabled = false; void R4KDyna_Execute (void); R4KDyna_Execute (); // Just run once so we can figure stuff out... return; Debug (1, "The dynarec isn't enabled... Going interpretive..."); //extern void r4300iCompiler_Execute(void); //r4300iCompiler_Execute (); return; } while (!cpuIsReset) { InterruptTime -= RegSettings.VSyncHack; if ((InterruptTime <= 0)) CheckInterrupts(); extern u8 *rdram; if (TLBLUT[pc>>12] > 0xFFFFFFF0) { QuickTLBExcept (); } //lastPC[lastPCPtr++] = pc; //lastPCPtr &= MAXPCSIZE; ((u32*)&sop)[0] = ((u32 *)returnMemPointer(TLBLUT[pc>>12]+(pc & 0xfff)))[0]; pc+=4; r4300i[sop.op](); if (CpuRegs[0] != 0) { r4300i_HandleErrorRegister(); } } } void opCOP0(void) { if (sop.rs == 16) MmuSpecial[sop.func](); else MmuNormal[sop.rs](); } void opCOP2(void) { bool CopUnuseableException(u32 addr, u32 copx); CopUnuseableException(pc-4,2); } void opNI(void) { Debug(0,"%08X: NI called. op%02X func%02X rs%02X",pc-4,sop.op,sop.func,sop.rs); Debug(0,"%08X: NI called. %08X",pc-4,*(u32 *)&sop); CPU_ERROR("NI Called",pc); cpuIsDone = cpuIsReset = true; } void opSPECIAL ( void ) { special[sop.func](); } void opREGIMM( void ) { regimm[sop.rt](); } void opADD(void) { // Last Checked 01-14-01 CpuRegs[sop.rd] = (s64)((s32)CpuRegs[sop.rt] + (s32)CpuRegs[sop.rs]); // TODO: Integer Overflow... } void opADDI(void) { // Last Checked 01-14-01 CpuRegs[sop.rt] = (s64)((s32)CpuRegs[sop.rs] + (s32)((s16)opcode)); // TODO: Integer Overflow... } void opADDIU(void) { // Last Checked 01-14-01 CpuRegs[sop.rt] = (s64)((s32)CpuRegs[sop.rs] + (s32)((s16)opcode)); } void opADDU(void) { // Last Checked 01-14-01 CpuRegs[sop.rd] = (s64)((s32)CpuRegs[sop.rt] + (s32)CpuRegs[sop.rs]); } void opAND(void) { // Last Checked 01-14-01 CpuRegs[sop.rd] = CpuRegs[sop.rt] & CpuRegs[sop.rs]; } void opANDI(void) { // Last Checked 01-14-01 CpuRegs[sop.rt] = CpuRegs[sop.rs] & (u64)((u16)opcode); } void Test64bit (u64 x, u64 y) { // *** Temporary Testing *** static u32 last64bit = 0; char buffer[100]; if (last64bit == pc) return; if ( ( (u32)x == (u32)y ) != ( (u64)x == (u64)y ) ) { //Debug (0, "Need 64bit at %08X", pc); last64bit = pc; //OpcodeLookup(pc-4, buffer); //Debug (0, "%08x: %s", pc-4, buffer); //OpcodeLookup(pc, buffer); //Debug (0, "%08x: %s", pc, buffer); } } void opBEQ(void) { // Last Checked 01-14-01 Test64bit (CpuRegs[sop.rt], CpuRegs[sop.rs]); if (CpuRegs[sop.rt] == CpuRegs[sop.rs]) { u32 target = ((s16)opcode); target = pc + (target << 2); FAST_INFINITE_LOOP_BRANCH doInstr(); pc = target; } } void opBEQL(void) { // Last Checked 01-14-01 Test64bit (CpuRegs[sop.rt], CpuRegs[sop.rs]); if (CpuRegs[sop.rt] == CpuRegs[sop.rs]) { u32 target = ((s16)opcode); target = pc + (target << 2); FAST_INFINITE_LOOP_BRANCH doInstr(); pc = target; } else { pc +=4; } } void opBGEZ(void) { // Last Checked 01-14-01 Test64bit (0, CpuRegs[sop.rs]); if (((s64)CpuRegs[sop.rs]) >= 0) { u32 target = ((s16)opcode); target = pc + (target << 2); FAST_INFINITE_LOOP_BRANCH doInstr(); pc = target; } } void opBGEZAL(void) { // Last Checked 01-14-01 Test64bit (0, CpuRegs[sop.rs]); CpuRegs[31] = pc+4; if (((s64)CpuRegs[sop.rs]) >= 0) { u32 target = ((s16)opcode); target = pc + (target << 2); doInstr(); CpuRegs[31] = pc+4; pc = target; } } void opBGEZALL(void) { // Last Checked 01-14-01 Test64bit (0, CpuRegs[sop.rs]); CpuRegs[31] = pc+4; if (((s64)CpuRegs[sop.rs]) >= 0) { u32 target = ((s16)opcode); target = pc + (target << 2); doInstr(); CpuRegs[31] = pc+4; pc = target; }else{ pc+=4; } } void opBGEZL(void) { // Last Checked 01-14-01 Test64bit (0, CpuRegs[sop.rs]); if (((s64)CpuRegs[sop.rs]) >= 0) { u32 target = ((s16)opcode); target = pc + (target << 2); doInstr(); pc = target; }else{ pc+=4; } } void opBGTZ(void) { // Last Checked 01-14-01 Test64bit (0, CpuRegs[sop.rs]); if (((s64)CpuRegs[sop.rs]) > 0) { u32 target = ((s16)opcode); target = pc + (target << 2); FAST_INFINITE_LOOP_BRANCH doInstr(); pc = target; } } void opBGTZL(void) { Test64bit (0, CpuRegs[sop.rs]); if (((s64)CpuRegs[sop.rs]) > 0) { u32 target = ((s16)opcode); target = pc + (target << 2); doInstr(); pc = target; }else{ pc+=4; } } void opBLEZ(void) { // Last Checked 01-14-01 Test64bit (0, CpuRegs[sop.rs]); if (((s64)CpuRegs[sop.rs]) <= 0) { u32 target = ((s16)opcode); target = pc + (target << 2); FAST_INFINITE_LOOP_BRANCH doInstr(); pc = target; } } void opBLEZL(void) { // Last Checked 01-14-01 Test64bit (0, CpuRegs[sop.rs]); if (((s64)CpuRegs[sop.rs]) <= 0) { u32 target = ((s16)opcode); target = pc + (target << 2); doInstr(); pc = target; }else{ pc+=4; } } void opBLTZ(void) { // Last Checked 01-14-01 Test64bit (0, CpuRegs[sop.rs]); if (((s64)CpuRegs[sop.rs]) < 0) { u32 target = ((s16)opcode); target = pc + (target << 2); FAST_INFINITE_LOOP_BRANCH doInstr(); pc = target; } } void opBLTZAL(void) { // Last Checked 01-14-01 Test64bit (0, CpuRegs[sop.rs]); CpuRegs[31] = pc+4; if (((s64)CpuRegs[sop.rs]) < 0) { u32 target = ((s16)opcode); target = pc + (target << 2); doInstr(); pc = target; } } void opBLTZALL(void) { // Last Checked 01-14-01 Test64bit (0, CpuRegs[sop.rs]); CpuRegs[31] = pc+4; if (((s64)CpuRegs[sop.rs]) < 0) { u32 target = ((s16)opcode); target = pc + (target << 2); doInstr(); pc = target; }else{ pc+=4; } } void opBLTZL(void) { // Last Checked 01-14-01 Test64bit (0, CpuRegs[sop.rs]); if (((s64)CpuRegs[sop.rs]) < 0) { u32 target = ((s16)opcode); target = pc + (target << 2); doInstr(); pc = target; }else{ pc+=4; } } void opBNE(void) { // Last Checked 12-28-00 Test64bit (CpuRegs[sop.rt], CpuRegs[sop.rs]); if (CpuRegs[sop.rt] != CpuRegs[sop.rs]) { u32 target = ((s16)opcode); target = pc + (target << 2); FAST_INFINITE_LOOP_BRANCH doInstr(); pc = target; } } // speed hack, like in Banjo Game // 802483F4: BNEL v0,r0, 802483F4, where r0 = 0 // 802483f*: ADDIU v0,v0, FFFFFFFC v0=v0-4 // //----------------------------------------------------------------- //| BNEL | Branch on Not Equal Likley | //|-----------|---------------------------------------------------| //|010101 (21)| rs | rt | offset | //------6----------5---------5-------------------16---------------- // // //----------------------------------------------------------------- //| ADDIU | ADD Immediate Unsigned word | //|-----------|---------------------------------------------------| //|001001 (9) | rs | rt | immediate | //------6----------5---------5-------------------16---------------- // /* #define FAST_INFINITE_LOOP_JUMP extern u32 VsyncTime,instructions; if (target==pc-4 && pr32(pc)==0) instructions = VsyncTime-1; #define FAST_INFINITE_LOOP_BRANCH extern u32 VsyncTime,instructions; if (target==pc-4 && pr32(pc)==0 && sop.rt==sop.rs) instructions = VsyncTime-1; */ //800CDBC0: BNEL V0, V1, 0x800CDBC0 //800CDBC4: ADDIU V0, V0, 0x0004 void opBNEL(void) { // Last Checked 01-14-01 Test64bit (CpuRegs[sop.rt], CpuRegs[sop.rs]); if (CpuRegs[sop.rt] != CpuRegs[sop.rs]) { u32 target = ((s16)opcode); target = pc + (target << 2); doInstr(); pc = target; } else{ pc+=4; } } void opBREAK(void) { //Debug(0,"BREAK opcode at %X, A2:%X, VO:%X\n",pc,CpuRegs[6],CpuRegs[2]); //CPU_ERROR("Break Called",pc); //void BreakException (); //BreakException (); } void opCACHE(void) { } // TODO: Do I need to do anything with this instruction? 12-28-00 void opDADD(void) { CpuRegs[sop.rd] = CpuRegs[sop.rt] + CpuRegs[sop.rs]; } void opDADDI(void) { CpuRegs[sop.rt] = CpuRegs[sop.rs] + (s64)((s16)opcode); // Need OverFlow } void opDADDIU(void) { CpuRegs[sop.rt] = CpuRegs[sop.rs] + (s64)((s16)opcode); } void opDADDU(void) { CpuRegs[sop.rd] = CpuRegs[sop.rt] + CpuRegs[sop.rs]; } void opDDIV(void) { if (CpuRegs[sop.rt] == 0) { Debug (0, "DDIV by Zero... this should cause an exception: %08X", pc-4); cpuLo = cpuHi = 0; } else { cpuLo = (s64)CpuRegs[sop.rs] / (s64)CpuRegs[sop.rt]; cpuHi = (s64)CpuRegs[sop.rs] % (s64)CpuRegs[sop.rt]; } } void opDDIVU(void) { if (CpuRegs[sop.rt] == 0) { Debug (0, "DDIVU by Zero... this should cause an exception: %08X", pc-4); cpuLo = cpuHi = 0; } else { cpuLo = (u64)CpuRegs[sop.rs] / (u64)CpuRegs[sop.rt]; cpuHi = (u64)CpuRegs[sop.rs] % (u64)CpuRegs[sop.rt]; } } void opDIV(void) { if (CpuRegs[sop.rt] == 0) { Debug (0, "DIV by Zero... this should cause an exception: %08X", pc-4); cpuLo = cpuHi = 0; } else { cpuLo = ((s32)CpuRegs[sop.rs]) / ((s32)CpuRegs[sop.rt]); cpuHi = ((s32)CpuRegs[sop.rs]) % ((s32)CpuRegs[sop.rt]); } } void opDIVU(void) { if (CpuRegs[sop.rt] == 0) { Debug (0, "DIVU by Zero... this should cause an exception: %08X", pc-4); cpuLo = cpuHi = 0; } else { cpuLo = ((u32)CpuRegs[sop.rs]) / ((u32)CpuRegs[sop.rt]); cpuHi = ((u32)CpuRegs[sop.rs]) % ((u32)CpuRegs[sop.rt]); } } void opDMULT(void) { unsigned __int64 hh,hl,lh,ll,b; __int64 t1,t2; t1 = CpuRegs[sop.rs]; t2 = CpuRegs[sop.rt]; hh = ((__int64)(t1 >> 32) & 0x0ffffffff) * ((__int64)(t2 >> 32) & 0x0ffffffff); hl = (__int64)(t1 & 0x0ffffffff) * ((__int64)(t2 >> 32) & 0x0ffffffff); lh = ((__int64)(t1 >> 32) & 0x0ffffffff) * (__int64)(t2 & 0x0ffffffff); ll = ((__int64)(t1 & 0x0ffffffff) * (__int64)(t2 & 0x0ffffffff)); cpuLo = ((hl + lh) << 32) + ll; b=(((hl + lh) + (ll >> 32)) & 0x0100000000)>>32; cpuHi = (unsigned __int64)hh + ((signed __int64)(unsigned __int32)(hl >> 32) + (signed __int64)(unsigned __int32)(lh >> 32) + b); } void opDMULTU(void) { unsigned __int64 hh,hl,lh,ll,b; __int64 t1,t2; int sgn = 0; t1 = CpuRegs[sop.rs]; t2 = CpuRegs[sop.rt]; if (t1 < 0) {sgn ^= 1; t1 = -t1;} if (t2 < 0) {sgn ^= 1; t2 = -t2;} hh = ((__int64)(t1 >> 32) & 0x0ffffffff) * ((__int64)(t2 >> 32) & 0x0ffffffff); hl = (__int64)(t1 & 0x0ffffffff) * ((__int64)(t2 >> 32) & 0x0ffffffff); lh = ((__int64)(t1 >> 32) & 0x0ffffffff) * (__int64)(t2 & 0x0ffffffff); ll = ((__int64)(t1 & 0x0ffffffff) * (__int64)(t2 & 0x0ffffffff)); cpuLo = ((hl + lh) << 32) + ll; b=(((hl + lh) + (ll >> 32)) & 0x0100000000)>>32; cpuHi = (unsigned __int64)hh + ((signed __int64)(unsigned __int32)(hl >> 32) + (signed __int64)(unsigned __int32)(lh >> 32) + b); b = (cpuLo >= 0) ? 1 : 0; if (sgn != 0) { cpuLo = -cpuLo; cpuHi = -cpuHi + b; } } //*/ void opDSLL(void) { CpuRegs[sop.rd] = CpuRegs[sop.rt] << sop.sa; } void opDSLLV(void) { CpuRegs[sop.rd] = CpuRegs[sop.rt] << (CpuRegs[sop.rs] & 0x3f); } void opDSLL32(void) { CpuRegs[sop.rd] = CpuRegs[sop.rt] << (sop.sa+32); } void opDSRA(void) { CpuRegs[sop.rd] = ((s64)CpuRegs[sop.rt]) >> sop.sa; } void opDSRAV(void) { CpuRegs[sop.rd] = ((s64)CpuRegs[sop.rt]) >> (CpuRegs[sop.rs] & 0x3f); } void opDSRA32(void) { CpuRegs[sop.rd] = ((s64)CpuRegs[sop.rt]) >> (sop.sa+32); } void opDSRL(void) { CpuRegs[sop.rd] = CpuRegs[sop.rt] >> sop.sa; } void opDSRLV(void) { CpuRegs[sop.rd] = CpuRegs[sop.rt] >> (CpuRegs[sop.rs] & 0x3f); } void opDSRL32(void) { CpuRegs[sop.rd] = CpuRegs[sop.rt] >> (sop.sa+32); } void opDSUB(void) { CpuRegs[sop.rd] = CpuRegs[sop.rs] - CpuRegs[sop.rt]; } void opDSUBU(void) { CpuRegs[sop.rd] = CpuRegs[sop.rs] - CpuRegs[sop.rt]; } void opJ(void) { // Last Checked 01-14-01 u32 target = (pc & 0xf0000000) + ((opcode << 2) & 0x0fffffff); FAST_INFINITE_LOOP_JUMP doInstr(); pc = target; } void opJAL(void) { // Last Checked 01-14-01 u32 target = (pc & 0xf0000000) + ((opcode << 2) & 0x0fffffff); CpuRegs[31] = pc+4; doInstr(); /* if (0x8009F1D8 == target) // Zelda Hack return;*/ pc = target; // void RecompilerMain (); // RecompilerMain (); } void opJALR(void) { // Last Checked 01-14-01 u32 target = CpuRegs[sop.rs]; CpuRegs[sop.rd] = pc+4; doInstr(); pc = target; } void opJR(void) { // Last Checked 01-14-01 u32 target = CpuRegs[sop.rs]; doInstr(); pc = target; } void opLB(void) { CpuRegs[sop.rt] = (s8)(vr8(CpuRegs[sop.rs] + (s16)opcode)); } void opLBU(void) { // Last Checked 01-14-01 CpuRegs[sop.rt] = (u8)(vr8(CpuRegs[sop.rs] + (s16)opcode)); } void opLD(void) { CpuRegs[sop.rt] = (s64)(vr64(CpuRegs[sop.rs] + (s16)opcode)); } void opLDC1(void) { // 10-30-01 - COP1 Unusable isn't shouldn't happen bool CopUnuseableException(u32 addr, u32 copx); if (!(MmuRegs[12] & 0x20000000)) { pc-=4; CopUnuseableException(pc,1); return; } #ifdef USE_OLD_FPU FpuRegs.l[sop.rt/2] = vr64(CpuRegs[sop.rs] + (s16)opcode); #else *(unsigned __int64 *)FPRDoubleLocation[sop.rt] = vr64(CpuRegs[sop.rs] + (s16)opcode); #endif } void opLDC2(void) { bool CopUnuseableException(u32 addr, u32 copx); CopUnuseableException(pc-4,2); } void opLDL(void) { // Fixed: 10-29-01 (addr&3 not addr&7 and addr&0xffffffffc) u32 addr = CpuRegs[sop.rs] + (s16)opcode; u64 lwTmp = vr64(addr&0xfffffff8); // Was v32 switch (addr&7) { case 0: break; case 1: lwTmp = (lwTmp << 8 ) | ((u64)CpuRegs[sop.rt]&0x00000000000000ff); break; case 2: lwTmp = (lwTmp << 16) | ((u64)CpuRegs[sop.rt]&0x000000000000ffff); break; case 3: lwTmp = (lwTmp << 24) | ((u64)CpuRegs[sop.rt]&0x0000000000ffffff); break; case 4: lwTmp = (lwTmp << 32) | ((u64)CpuRegs[sop.rt]&0x00000000ffffffff); break; case 5: lwTmp = (lwTmp << 40) | ((u64)CpuRegs[sop.rt]&0x000000ffffffffff); break; case 6: lwTmp = (lwTmp << 48) | ((u64)CpuRegs[sop.rt]&0x0000ffffffffffff); break; default: lwTmp = (lwTmp << 56) | ((u64)CpuRegs[sop.rt]&0x00ffffffffffffff); break; } CpuRegs[sop.rt] = (s64)lwTmp; } void opLDR(void) { // Fixed: 10-29-01 (addr&3 not addr&7 and addr&0xffffffffc) u32 addr = CpuRegs[sop.rs] + (s32)(s16)opcode; u64 lwTmp = vr64(addr&0xfffffff8); // vr32 switch (addr&7) { case 7: break; case 6: lwTmp = (lwTmp >> 8 ) | ((u64)CpuRegs[sop.rt]&0xff00000000000000); break; case 5: lwTmp = (lwTmp >> 16) | ((u64)CpuRegs[sop.rt]&0xffff000000000000); break; case 4: lwTmp = (lwTmp >> 24) | ((u64)CpuRegs[sop.rt]&0xffffff0000000000); break; case 3: lwTmp = (lwTmp >> 32) | ((u64)CpuRegs[sop.rt]&0xffffffff00000000); break; case 2: lwTmp = (lwTmp >> 40) | ((u64)CpuRegs[sop.rt]&0xffffffffff000000); break; case 1: lwTmp = (lwTmp >> 48) | ((u64)CpuRegs[sop.rt]&0xffffffffffff0000); break; default: lwTmp = (lwTmp >> 56) | ((u64)CpuRegs[sop.rt]&0xffffffffffffff00); break; } CpuRegs[sop.rt] = (s64)lwTmp; } void opLH(void) { // Last Checked 01-14-01 CpuRegs[sop.rt] = (s64)(s16)(vr16(CpuRegs[sop.rs] + (s32)(s16)opcode)); // TODO: Address Error Exception } void opLHU(void) { CpuRegs[sop.rt] = (u64)(u16)(vr16(CpuRegs[sop.rs] + (s32)(s16)opcode)); } extern int LLBit; u32 LastLL; void opLL(void) { // This opcode is needed for Guantlet Legends u32 addr = VirtualToPhysical((CpuRegs[sop.rs] + (s32)(s16)opcode), 0); addr &= 0x1fffffff; //Debug (0, "LL Called: %08X -> %08X",pc-4, (CpuRegs[sop.rs] + (s32)(s16)opcode)); CpuRegs[sop.rt] = (s64)*(s32*)(valloc+addr); LastLL = MmuRegs[17] = addr; LLBit = 1; // TODO: Otherstuff } void opLLD(void) { // I don't believe this is implemented on the r4300 CPU_ERROR("LLD Called",pc); } void opLUI(void) { // Last Checked 01-14-01 CpuRegs[sop.rt] = (s64)(s32)((s16)opcode << 16); } bool romwrite = false; u32 nextread = 0; void opLW(void) { // Last Checked 01-14-01 /*u32 addr = (CpuRegs[sop.rs] + (s32)(s16)opcode); if ((addr >= 0xB0000000) && (addr < 0xBFFFFFFF)) { if (romwrite == true) { Debug (0, "%08X: %08X", addr, CpuRegs[sop.rt]); //CpuRegs [sop.rt] = nextread; romwrite = false; } }*/ CpuRegs[sop.rt] = (s64)(s32)(vr32(CpuRegs[sop.rs] + (s32)(s16)opcode)); /*extern u32 romsize; /* if ((addr & 0x00FFFFFF) > 0x2fb1f0) { if ((addr & 0x00FFFFFF) < 0x2fb5f0) { Debug (0, "Reading Reg: %08X - %08X", addr, CpuRegs[sop.rt]); } } extern u32 romsize; if ((addr > 0xB0000000+romsize)) { Debug(0, "Reading Reg: %08X - %08X", addr, CpuRegs[sop.rt]); } // TODO: Address Error Exception...*/ } void opLWC1(void) { // 10-30-01 - COP1 Unusable isn't shouldn't happen bool CopUnuseableException(u32 addr, u32 copx); if (!(MmuRegs[12] & 0x20000000)) { pc-=4; CopUnuseableException(pc,1); return; } #ifdef USE_OLD_FPU FpuRegs.w[sop.rt] = vr32(CpuRegs[sop.rs] + (s16)opcode); #else *(DWORD *)FPRFloatLocation[sop.rt] = vr32(CpuRegs[sop.rs] + (s16)opcode); #endif } void opLWC2(void) { bool CopUnuseableException(u32 addr, u32 copx); CopUnuseableException(pc-4,2); } void opLWL(void) { // Last Checked 12-28-00 u32 addr = CpuRegs[sop.rs] + (s16)opcode; u32 lwTmp = vr32(addr&0xfffffffc); switch (addr&3) { case 0: break; case 1: lwTmp = (lwTmp << 8 ) | ((u32)CpuRegs[sop.rt]&0x000000ff); break; case 2: lwTmp = (lwTmp << 16) | ((u32)CpuRegs[sop.rt]&0x0000ffff); break; default: lwTmp = (lwTmp << 24) | ((u32)CpuRegs[sop.rt]&0x00ffffff); break; } CpuRegs[sop.rt] = (s64)(s32)lwTmp; } void opLWR(void) { // Last Checked 12-28-00 u32 addr = CpuRegs[sop.rs] + (s16)opcode; u32 lwTmp = vr32(addr&0xfffffffc); switch (addr&3) { case 3: break; case 2: lwTmp = (lwTmp >> 8 ) | ((u32)CpuRegs[sop.rt]&0xff000000); break; case 1: lwTmp = (lwTmp >> 16) | ((u32)CpuRegs[sop.rt]&0xffff0000); break; default: lwTmp = (lwTmp >> 24) | ((u32)CpuRegs[sop.rt]&0xffffff00); break; } CpuRegs[sop.rt] = (s64)(s32)lwTmp; } void opLWU(void) { CpuRegs[sop.rt] = (u64)(u32)(vr32(CpuRegs[sop.rs] + (s32)(s16)opcode));//unrisky } void opMFHI(void) { // Last Checked 01-14-01 CpuRegs[sop.rd] = cpuHi; } void opMFLO(void) { // Last Checked 01-14-01 CpuRegs[sop.rd] = cpuLo; } void opMTHI(void) { // Last Checked 01-14-01 cpuHi = CpuRegs[sop.rs]; } void opMTLO(void) { // Last Checked 01-14-01 cpuLo = CpuRegs[sop.rs]; } void opMULT(void) { // Last Checked 01-14-01 //cpuLo = Int32x32To64((u32)CpuRegs[sop.rs],(u32)CpuRegs[sop.rt]); //cpuHi = (s32)(((s32*)&cpuLo)[1]); //cpuLo = (s32)(((s32*)&cpuLo)[0]); cpuLo = (s64)(s32)CpuRegs[sop.rs] * (s64)(s32)CpuRegs[sop.rt]; cpuHi = (s32)(cpuLo >> 32); cpuLo = (s32)cpuLo; } void opMULTU(void) { // Last Checked 01-14-01 /* cpuLo = UInt32x32To64((u32)CpuRegs[sop.rs],(u32)CpuRegs[sop.rt]); cpuHi = (s32)(((s32*)&cpuLo)[1]); cpuLo = (s32)(((s32*)&cpuLo)[0]); */ //u64 result; cpuLo = (u64)(u32)CpuRegs[sop.rs] * (u64)(u32)CpuRegs[sop.rt]; cpuHi = (s32)(cpuLo >> 32); cpuLo = (s32)cpuLo; } void opNOR(void) { CpuRegs[sop.rd] = ~(CpuRegs[sop.rs] | CpuRegs[sop.rt]); } void opOR(void) { // Last Checked 01-14-01 CpuRegs[sop.rd] = CpuRegs[sop.rs] | CpuRegs[sop.rt]; } void opORI(void) { // Last Checked 01-14-01 CpuRegs[sop.rt] = CpuRegs[sop.rs] | (u64)((u16)opcode); } void opSB(void) { // Last Checked 01-14-01 vw8(CpuRegs[sop.rs] + (s16)opcode,(u8)CpuRegs[sop.rt]); // TODO: Address Error Exception } void opSC(void) { // Required by Guantlet Legends //Debug (0, "SC Called: %08X <- %08X",pc-4, (CpuRegs[sop.rs] + (s32)(s16)opcode)); if (LastLL != MmuRegs[17]) LLBit = 0; if (LLBit == 1) { vw32 (CpuRegs[sop.rs] + (s16)opcode, (u32)CpuRegs[sop.rt]); } CpuRegs[sop.rt] = LLBit; } void opSCD(void) { CPU_ERROR("SCD Called",pc); } void opSD(void) { vw64(CpuRegs[sop.rs] + (s16)opcode,CpuRegs[sop.rt]); } void opSDC1(void) { // 10-30-01 - COP1 Unusable isn't shouldn't happen bool CopUnuseableException(u32 addr, u32 copx); if (!(MmuRegs[12] & 0x20000000)) { pc-=4; CopUnuseableException(pc,1); return; } #ifdef USE_OLD_FPU vw64(CpuRegs[sop.rs] + (s16)opcode,FpuRegs.l[sop.rt/2]); #else vw64(CpuRegs[sop.rs] + (s16)opcode, *(__int64 *)FPRDoubleLocation[sop.rt]); #endif } void opSDC2(void) { bool CopUnuseableException(u32 addr, u32 copx); CopUnuseableException(pc-4,2); } void opSDL(void) { // Fixed: 10-29-01 (addr&3 not addr&7 and addr&0xffffffffc) u32 addr = CpuRegs[sop.rs] + (s16)opcode; u64 lwTmp = vr64(addr&0xfffffff8); switch (addr&7) { case 0: lwTmp = CpuRegs[sop.rt]; break; case 1: lwTmp = ((u64)CpuRegs[sop.rt] >> 8) | (lwTmp&0xff00000000000000); break; case 2: lwTmp = ((u64)CpuRegs[sop.rt] >> 16) | (lwTmp&0xffff000000000000); break; case 3: lwTmp = ((u64)CpuRegs[sop.rt] >> 24) | (lwTmp&0xffffff0000000000); break; case 4: lwTmp = ((u64)CpuRegs[sop.rt] >> 32) | (lwTmp&0xffffffff00000000); break; case 5: lwTmp = ((u64)CpuRegs[sop.rt] >> 40) | (lwTmp&0xffffffffff000000); break; case 6: lwTmp = ((u64)CpuRegs[sop.rt] >> 48) | (lwTmp&0xffffffffffff0000); break; default: lwTmp = ((u64)CpuRegs[sop.rt] >> 56) | (lwTmp&0xffffffffffffff00); break; } vw64(addr&0xfffffff8,lwTmp); } void opSDR(void) { // Fixed: 10-29-01 (addr&3 not addr&7 and addr&0xffffffffc) u32 addr = CpuRegs[sop.rs] + (s16)opcode; u64 lwTmp = vr64(addr&0xfffffff8); switch (addr&7) { case 7: lwTmp = CpuRegs[sop.rt]; break; case 6: lwTmp = ((u64)CpuRegs[sop.rt] << 8) | (lwTmp&0x00000000000000ff); break; case 5: lwTmp = ((u64)CpuRegs[sop.rt] << 16) | (lwTmp&0x000000000000ffff); break; case 4: lwTmp = ((u64)CpuRegs[sop.rt] << 24) | (lwTmp&0x0000000000ffffff); break; case 3: lwTmp = ((u64)CpuRegs[sop.rt] << 32) | (lwTmp&0x00000000ffffffff); break; case 2: lwTmp = ((u64)CpuRegs[sop.rt] << 40) | (lwTmp&0x000000ffffffffff); break; case 1: lwTmp = ((u64)CpuRegs[sop.rt] << 48) | (lwTmp&0x0000ffffffffffff); break; default: lwTmp = ((u64)CpuRegs[sop.rt] << 56) | (lwTmp&0x00ffffffffffffff); break; } vw64(addr&0xfffffff8,lwTmp); } void opSH(void) { // Last Checked 01-14-01 // u32 addy = (CpuRegs[sop.rs] + (s16)opcode); // if (pc == 0x80001844) { // static FILE *dfile = fopen ("c:\\debug.txt", "wt"); // fprintf (dfile, "Value: %08X\n", CpuRegs[sop.rs]); // } // if (CpuRegs[sop.rs] == 0x4005) // Debug (0, "PC = %08X", pc); // if (addy & 1) { // void AddressErrorException (bool store); // AddressErrorException (true); // } else { vw16(CpuRegs[sop.rs] + (s16)opcode,(u16)CpuRegs[sop.rt]); // } } void opSLL(void) { // Last Checked 01-14-01 if (opcode==0) return; CpuRegs[sop.rd] = (s64)(s32)(CpuRegs[sop.rt] << sop.sa); } void opSLLV(void) { // Last Checked 01-14-01 CpuRegs[sop.rd] = (s64)(s32)(CpuRegs[sop.rt] << (CpuRegs[sop.rs] & 0x1f)); } void opSLT(void) { // Last Checked 01-14-01 if (((s64)CpuRegs[sop.rs]) < ((s64)CpuRegs[sop.rt])) { CpuRegs[sop.rd] = 1; } else { CpuRegs[sop.rd] = 0; } } void opSLTI(void) { // Last Checked 01-14-01 s64 test = (s16)opcode; //if (sop.rt == 0) return; // TODO: Commented this out 10-29-01 if (((s64)CpuRegs[sop.rs]) < (s64)test) { CpuRegs[sop.rt] = 1; } else { CpuRegs[sop.rt] = 0; } } void opSLTIU(void) { s64 test = ((s16)opcode); if ((u64)CpuRegs[sop.rs] < (u64)test) { CpuRegs[sop.rt] = 1; } else { CpuRegs[sop.rt] = 0; } } void opSLTU(void) { // Last Checked 01-14-01 if ((u64)CpuRegs[sop.rs] < (u64)CpuRegs[sop.rt]) { CpuRegs[sop.rd] = 1; } else { CpuRegs[sop.rd] = 0; } } void opSRA(void) { CpuRegs[sop.rd] = (s64)(s32)(((s32)CpuRegs[sop.rt]) >> sop.sa); } void opSRAV(void) { CpuRegs[sop.rd] = (s64)(s32)(((s32)CpuRegs[sop.rt]) >> (CpuRegs[sop.rs] & 0x1f)); } void opSRL(void) { // Last Checked 01-14-01 CpuRegs[sop.rd] = (s64)(s32)(((u32)CpuRegs[sop.rt]) >> sop.sa); } void opSRLV(void) { // Last Checked 01-14-01 CpuRegs[sop.rd] = (s64)(s32)(((u32)CpuRegs[sop.rt]) >> (CpuRegs[sop.rs] & 0x1f)); } void opSUB(void) { // Last Checked 01-14-01 CpuRegs[sop.rd] = (s64)(s32)((s32)CpuRegs[sop.rs] - (s32)CpuRegs[sop.rt]); } void opSUBU(void) { // Last Checked 01-14-01 CpuRegs[sop.rd] = (s64)(s32)(((s32)CpuRegs[sop.rs]) - ((s32)CpuRegs[sop.rt])); } void opSW(void) { // Last Checked 01-14-01 /*u32 addr = CpuRegs[sop.rs] + (s16)opcode; if ((addr >= 0xB0000000) && (addr < 0xBFFFFFFF)) { romwrite = true; //nextread = (u32)CpuRegs[sop.rt]; Debug (0, "Romwrite to : %08X", addr); }// else {*/ vw32(CpuRegs[sop.rs] + (s16)opcode,(u32)CpuRegs[sop.rt]); //} } void opSWC1(void) { // 10-30-01 - COP1 Unusable isn't shouldn't happen bool CopUnuseableException(u32 addr, u32 copx); if (!(MmuRegs[12] & 0x20000000)) { pc-=4; CopUnuseableException(pc,1); return; } #ifdef USE_OLD_FPU vw32(CpuRegs[sop.rs] + (s16)opcode,FpuRegs.w[sop.rt]); #else vw32(CpuRegs[sop.rs] + (s16)opcode, *(DWORD *)FPRFloatLocation[sop.rt]); #endif } void opSWC2(void) { bool CopUnuseableException(u32 addr, u32 copx); CopUnuseableException(pc-4,2); } void opSWL(void) { // Last Checked 12-28-00 u32 addr = CpuRegs[sop.rs] + (s16)opcode; u32 lwTmp = vr32(addr&0xfffffffc); switch (addr&3) { case 0: lwTmp = CpuRegs[sop.rt]; break; case 1: lwTmp = ((u32)CpuRegs[sop.rt] >> 8 ) | (lwTmp&0xff000000); break; case 2: lwTmp = ((u32)CpuRegs[sop.rt] >> 16) | (lwTmp&0xffff0000); break; default: lwTmp = ((u32)CpuRegs[sop.rt] >> 24) | (lwTmp&0xffffff00); break; } vw32(addr&0xfffffffc,lwTmp); } void opSWR(void) { // Last Checked 12-28-00 u32 addr = CpuRegs[sop.rs] + (s16)opcode; u32 lwTmp = vr32(addr&0xfffffffc); switch (addr&3) { case 3: lwTmp = CpuRegs[sop.rt]; break; case 2: lwTmp = ((u32)CpuRegs[sop.rt] << 8) | (lwTmp&0x000000ff); break; case 1: lwTmp = ((u32)CpuRegs[sop.rt] << 16) | (lwTmp&0x0000ffff); break; default: lwTmp = ((u32)CpuRegs[sop.rt] << 24) | (lwTmp&0x00ffffff); break; } vw32(addr&0xfffffffc,lwTmp); } void opSYNC(void) { } void opSYSCALL(void) { //CPU_ERROR("Syscall Called",pc); void SyscallException (); SyscallException (); } void opTEQ(void){ CPU_ERROR("TEQ Called",pc); } void opTEQI(void){ CPU_ERROR("TEQI Called",pc); } void opTGE(void) { CPU_ERROR("TGE Called",pc); } void opTGEI(void){ CPU_ERROR("TGEI Called",pc); } void opTGEIU(void){ CPU_ERROR("TGEIU Called",pc); } void opTGEU(void) { CPU_ERROR("TGEU Called",pc); } void opTLT(void){ CPU_ERROR("TLT Called",pc); } void opTLTI(void) { CPU_ERROR("TLTI Called",pc); } void opTLTIU(void) { CPU_ERROR("TLTIU Called",pc); } void opTLTU(void) { CPU_ERROR("TLTU Called",pc); } void opTNE(void) { CPU_ERROR("TNE Called",pc); } void opTNEI(void) { CPU_ERROR("TNEI Called",pc); } void opXOR(void) { // Last Checked 01-14-01 CpuRegs[sop.rd] = CpuRegs[sop.rs] ^ CpuRegs[sop.rt]; } void opXORI(void) { // Last Checked 01-14-01 CpuRegs[sop.rt] = CpuRegs[sop.rs] ^ (u64)((u16)opcode); } void opCOP1(void); //void RecOp (void); void (*r4300i[0x40])() = { opSPECIAL, opREGIMM, opJ, opJAL, opBEQ, opBNE, opBLEZ, opBGTZ, //00-07 opADDI, opADDIU, opSLTI, opSLTIU, opANDI, opORI, opXORI, opLUI, //08-0F opCOP0, opCOP1, opCOP2, opNI, opBEQL, opBNEL, opBLEZL,opBGTZL,//10-17 opDADDI, opDADDIU, opLDL, opLDR, opNI, opNI, opNI, opNI, //18-1F opLB, opLH, opLWL, opLW, opLBU, opLHU, opLWR, opLWU, //20-27 opSB, opSH, opSWL, opSW, opSDL, opSDR, opSWR, opCACHE,//28-2F opLL, opLWC1, opLWC2, opNI, opLLD, opLDC1, opLDC2, opLD, //30-37 opSC, opSWC1, opSWC2, opNI, opSCD, opSDC1, opSDC2, opSD //38-3F }; void (*special[0x40])() = { opSLL, opNI, opSRL, opSRA, opSLLV, opNI, opSRLV, opSRAV, // 00-07 opJR, opJALR, opNI, opNI, opSYSCALL, opBREAK, opNI, opSYNC, // 08-0F opMFHI, opMTHI, opMFLO, opMTLO, opDSLLV, opNI, opDSRLV, opDSRAV, // 10-17 opMULT, opMULTU, opDIV, opDIVU, opDMULT, opDMULTU, opDDIV, opDDIVU, // 18-1F opADD, opADDU, opSUB, opSUBU, opAND, opOR, opXOR, opNOR, // 20-27 opNI, opNI, opSLT, opSLTU, opDADD, opDADDU, opDSUB, opDSUBU, // 28-2F opTGE, opTGEU, opTLT, opTLTU, opTEQ, opNI, opTNE, opNI, // 30-37 opDSLL, opNI, opDSRL, opDSRA, opDSLL32, opNI, opDSRL32, opDSRA32 // 38-3F }; void (*regimm[0x20])() = { opBLTZ, opBGEZ, opBLTZL, opBGEZL, opNI, opNI, opNI, opNI, opTGEI, opTGEIU, opTLTI, opTLTIU, opTEQI, opNI, opTNEI, opNI, opBLTZAL, opBGEZAL, opBLTZALL, opBGEZALL, opNI, opNI, opNI, opNI, opNI, opNI, opNI, opNI, opNI, opNI, opNI, opNI, };