/* Pcsx2 - Pc Ps2 Emulator * Copyright (C) 2002-2005 Pcsx2 Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ // recompiler reworked to add dynamic linking Jan06 // and added reg caching, const propagation, block analysis Jun06 // zerofrog(@gmail.com) #if 1 #ifdef __MSCW32__ #pragma warning(disable:4244) #pragma warning(disable:4761) #endif extern "C" { #include #include #include #include #include #if defined(__WIN32__) #include #endif #include "PS2Etypes.h" #include "System.h" #include "zlib.h" #include "Memory.h" #include "Misc.h" #include "Vif.h" #include "VU.h" #include "R3000A.h" #include "PsxMem.h" #include "ix86/ix86.h" #include "iCore.h" #include "ir3000A.h" #include "PsxCounters.h" extern u32 psxNextCounter, psxNextsCounter; u32 g_psxMaxRecMem = 0; extern char *disRNameGPR[]; extern char* disR3000Fasm(u32 code, u32 pc); } #define PSX_NUMBLOCKS (1<<12) #define MAPBASE 0x48000000 #define RECMEM_SIZE (8*1024*1024) // R3000A statics uptr *psxRecLUT; int psxreclog = 0; static u32 s_BranchCount = 0; static char *recMem; // the recompiled blocks will be here static BASEBLOCK *recRAM; // and the ptr to the blocks here static BASEBLOCK *recROM; // and here static BASEBLOCK *recROM1; // also here static BASEBLOCKEX *recBlocks = NULL; static char *recPtr; u32 psxpc; // recompiler psxpc int psxbranch; // set for branch static EEINST* s_pInstCache = NULL; static u32 s_nInstCacheSize = 0; u32 g_psxNextBranchCycle = 0; static BASEBLOCK* s_pCurBlock = NULL; static BASEBLOCKEX* s_pCurBlockEx = NULL; static BASEBLOCK* s_pDispatchBlock = NULL; static u32 s_nEndBlock = 0; // what psxpc the current block ends static u32 s_nNextBlock = 0; // next free block in recBlocks static u32 s_ConstGPRreg; static u32 s_saveConstGPRreg = 0, s_saveHasConstReg = 0, s_saveFlushedConstReg = 0; static EEINST* s_psaveInstInfo = NULL; u32 s_psxBlockCycles = 0; // cycles of current block recompiling static u32 s_savenBlockCycles = 0; static void iPsxBranchTest(u32 newpc, u32 cpuBranch); void psxRecompileNextInstruction(int delayslot); static void recRecompile(u32 startpc); extern void (*rpsxBSC[64])(); extern void (*rpsxBSC_co[64])(); void rpsxpropBSC(EEINST* prev, EEINST* pinst); #ifdef _DEBUG extern "C" u32 psxdump = 0; #else #define psxdump 0 #endif #define PSX_GETBLOCK(x) PC_GETBLOCK_(x, psxRecLUT) #define PSXREC_CLEARM(mem) { \ if ((mem) < g_psxMaxRecMem && psxRecLUT[(mem) >> 16]) { \ BASEBLOCK* p = PSX_GETBLOCK(mem); \ if( *(u32*)p ) psxRecClearMem(p); \ } \ } \ BASEBLOCKEX* PSX_GETBLOCKEX(BASEBLOCK* p) { // BASEBLOCKEX* pex = *(BASEBLOCKEX**)(p+1); // if( pex >= recBlocks && pex < recBlocks+PSX_NUMBLOCKS ) // return pex; // otherwise, use the sorted list return GetBaseBlockEx(p->startpc, 1); } //////////////////////////////////////////////////// static void iDumpBlock( int startpc, char * ptr ) { FILE *f; char filename[ 256 ]; u32 i, j; EEINST* pcur; u8 used[34]; int numused, count; SysPrintf( "dump1 %x:%x, %x\n", startpc, psxpc, psxRegs.cycle ); #ifdef __WIN32__ CreateDirectory("dumps", NULL); sprintf( filename, "dumps\\psxdump%.8X.txt", startpc); #else mkdir("dumps", 0755); sprintf( filename, "dumps/psxdump%.8X.txt", startpc); #endif fflush( stdout ); f = fopen( filename, "w" ); assert( f != NULL ); for ( i = startpc; i < s_nEndBlock; i += 4 ) { fprintf( f, "%s\n", disR3000Fasm( *(u32*)PSXM( i ), i ) ); } // write the instruction info fprintf(f, "\n\nlive0 - %x, lastuse - %x used - %x\n", EEINST_LIVE0, EEINST_LASTUSE, EEINST_USED); memset(used, 0, sizeof(used)); numused = 0; for(i = 0; i < ARRAYSIZE(s_pInstCache->regs); ++i) { if( s_pInstCache->regs[i] & EEINST_USED ) { used[i] = 1; numused++; } } fprintf(f, " "); for(i = 0; i < ARRAYSIZE(s_pInstCache->regs); ++i) { if( used[i] ) fprintf(f, "%2d ", i); } fprintf(f, "\n"); fprintf(f, " "); for(i = 0; i < ARRAYSIZE(s_pInstCache->regs); ++i) { if( used[i] ) fprintf(f, "%s ", disRNameGPR[i]); } fprintf(f, "\n"); pcur = s_pInstCache+1; for( i = 0; i < (s_nEndBlock-startpc)/4; ++i, ++pcur) { fprintf(f, "%2d: %2.2x ", i+1, pcur->info); count = 1; for(j = 0; j < ARRAYSIZE(s_pInstCache->regs); j++) { if( used[j] ) { fprintf(f, "%2.2x%s", pcur->regs[j], ((count%8)&&count>26) { case 32: case 33: case 34: case 35: case 36: case 37: case 38: return ((tempcode>>21)&0x1f)==((tempcode>>16)&0x1f); // rs==rt } return 0; } u8 _psxIsLoadStore(u32 tempcode) { switch(tempcode>>26) { case 32: case 33: case 34: case 35: case 36: case 37: case 38: // 4 byte stores case 40: case 41: case 42: case 43: case 46: return 1; } return 0; } void _psxFlushAllUnused() { int i; for(i = 0; i < 34; ++i) { if( psxpc < s_nEndBlock ) { if( (g_pCurInstInfo[1].regs[i]&EEINST_USED) ) continue; } else if( (g_pCurInstInfo[0].regs[i]&EEINST_USED) ) continue; if( i < 32 && PSX_IS_CONST1(i) ) _psxFlushConstReg(i); else { _deleteX86reg(X86TYPE_PSX, i, 1); } } } int _psxFlushUnusedConstReg() { int i; for(i = 1; i < 32; ++i) { if( (g_psxHasConstReg & (1< 0 ); // make sure right GPR was saved assert( g_psxHasConstReg == s_saveHasConstReg || (g_psxHasConstReg ^ s_saveHasConstReg) == (1< 0) { pblock = PSX_GETBLOCK(psxRegs.pc); if ( !pblock->pFnptr || pblock->startpc != psxRegs.pc ) { recRecompile(psxRegs.pc); } assert( pblock->pFnptr != 0 ); #ifdef _DEBUG fnptr = (u8*)pblock->pFnptr; __asm { // save data mov oldesi, esi mov s_uSaveESP, esp sub s_uSaveESP, 8 push ebp call fnptr // jump into function // restore data pop ebp mov esi, oldesi } #else pfn = ((R3000AFNPTR)pblock->pFnptr); // use call instead of pfn() __asm call pfn #endif } } extern u32 g_psxNextBranchCycle; u32 g_psxlastpc = 0; static u32 g_temp; // jumped to when invalid psxpc address __declspec(naked,noreturn) void psxDispatcher() { // EDX contains the current psxpc to jump to, stack contains the jump addr to modify __asm push edx // calc PSX_GETBLOCK s_pDispatchBlock = PSX_GETBLOCK(psxRegs.pc); __asm { mov eax, s_pDispatchBlock // check if startpc == psxRegs.pc mov ecx, psxRegs.pc //and ecx, 0x5fffffff // remove higher bits cmp ecx, dword ptr [eax+BLOCKTYPE_STARTPC] je CheckPtr // recompile push psxRegs.pc // psxpc call recRecompile add esp, 4 // pop old param mov eax, s_pDispatchBlock CheckPtr: mov eax, dword ptr [eax] } #ifdef _DEBUG __asm mov g_temp, eax assert( g_temp ); #endif // __asm { // test eax, 0x40000000 // BLOCKTYPE_NEEDCLEAR // jz Done // // move new psxpc // and eax, 0x0fffffff // mov ecx, psxRegs.pc // mov dword ptr [eax+1], ecx // } __asm { and eax, 0x0fffffff mov edx, eax pop ecx // x86Ptr to mod sub edx, ecx sub edx, 4 mov dword ptr [ecx], edx jmp eax } } __declspec(naked,noreturn) void psxDispatcherClear() { // EDX contains the current psxpc __asm mov psxRegs.pc, edx __asm push edx // calc PSX_GETBLOCK s_pDispatchBlock = PSX_GETBLOCK(psxRegs.pc); if( s_pDispatchBlock->startpc == psxRegs.pc ) { assert( s_pDispatchBlock->pFnptr != 0 ); // already modded the code, jump to the new place __asm { pop edx add esp, 4 // ignore stack mov eax, s_pDispatchBlock mov eax, dword ptr [eax] and eax, 0x0fffffff jmp eax } } __asm { call recRecompile add esp, 4 // pop old param mov eax, s_pDispatchBlock mov eax, dword ptr [eax] pop ecx // old fnptr and eax, 0x0fffffff mov byte ptr [ecx], 0xe9 // jmp32 mov edx, eax sub edx, ecx sub edx, 5 mov dword ptr [ecx+1], edx jmp eax } } // called when jumping to variable psxpc address __declspec(naked,noreturn) void psxDispatcherReg() { __asm { //s_pDispatchBlock = PSX_GETBLOCK(psxRegs.pc); mov edx, psxRegs.pc mov ecx, edx } __asm { shr edx, 14 and edx, 0xfffffffc add edx, psxRecLUT mov edx, dword ptr [edx] mov eax, ecx and eax, 0xfffc // edx += 2*eax shl eax, 1 add edx, eax // check if startpc == psxRegs.pc mov eax, ecx //and eax, 0x5fffffff // remove higher bits cmp eax, dword ptr [edx+BLOCKTYPE_STARTPC] jne recomp mov eax, dword ptr [edx] } #ifdef _DEBUG __asm mov g_temp, eax assert( g_temp ); #endif __asm { and eax, 0x0fffffff jmp eax // fnptr recomp: sub esp, 8 mov dword ptr [esp+4], edx mov dword ptr [esp], ecx call recRecompile mov edx, dword ptr [esp+4] add esp, 8 mov eax, dword ptr [edx] and eax, 0x0fffffff jmp eax // fnptr } } static void recClear(u32 Addr, u32 Size) { u32 i; for(i = 0; i < Size; ++i, Addr+=4) { PSXREC_CLEARM(Addr); } } #define EE_MIN_BLOCK_BYTES 15 void rpsxMemConstClear(u32 mem) { // NOTE! This assumes recLUT never changes its mapping if( !psxRecLUT[mem>>16] ) return; CMP32ItoM((u32)PSX_GETBLOCK(mem), 0); j8Ptr[6] = JE8(0); PUSH32I((u32)PSX_GETBLOCK(mem)); CALLFunc((u32)psxRecClearMem); ADD32ItoR(ESP, 4); x86SetJ8(j8Ptr[6]); } void psxRecClearMem(BASEBLOCK* p) { BASEBLOCKEX* pexblock; BASEBLOCK* pstart; int lastdelay; // necessary since recompiler doesn't call femms/emms if (cpucaps.has3DNOWInstructionExtensions) __asm femms else __asm emms assert( p != NULL ); if( p->uType & BLOCKTYPE_DELAYSLOT ) { psxRecClearMem(p-1); if( p->pFnptr == 0 ) return; } assert( p->pFnptr != 0 ); assert( p->startpc ); x86Ptr = (s8*)p->pFnptr; // there is a small problem: mem can be ored with 0xa<<28 or 0x8<<28, and don't know which MOV32ItoR(EDX, p->startpc); PUSH32I((u32)x86Ptr); // will be replaced by JMP32 JMP32((u32)psxDispatcherClear - ( (u32)x86Ptr + 5 )); assert( x86Ptr == (s8*)p->pFnptr + EE_MIN_BLOCK_BYTES ); pstart = PSX_GETBLOCK(p->startpc); pexblock = PSX_GETBLOCKEX(pstart); assert( pexblock->startpc == pstart->startpc ); // if( pexblock->pOldFnptr ) { // // have to mod oldfnptr too // x86Ptr = pexblock->pOldFnptr; // // MOV32ItoR(EDX, p->startpc); // JMP32((u32)DispatcherClear - ( (u32)x86Ptr + 5 )); // } // else // pexblock->pOldFnptr = (u8*)p->pFnptr; // don't delete if last is delay lastdelay = pexblock->size; if( pstart[pexblock->size-1].uType & BLOCKTYPE_DELAYSLOT ) { assert( pstart[pexblock->size-1].pFnptr != pstart->pFnptr ); if( pstart[pexblock->size-1].pFnptr != 0 ) { pstart[pexblock->size-1].uType = 0; --lastdelay; } } memset(pstart, 0, lastdelay*sizeof(BASEBLOCK)); RemoveBaseBlockEx(pexblock, 1); pexblock->size = 0; pexblock->startpc = 0; } void psxSetBranchReg(u32 reg) { psxbranch = 1; if( reg != 0xffffffff ) { _allocX86reg(ESI, X86TYPE_PCWRITEBACK, 0, MODE_WRITE); _psxMoveGPRtoR(ESI, reg); psxRecompileNextInstruction(1); if( x86regs[ESI].inuse ) { assert( x86regs[ESI].type == X86TYPE_PCWRITEBACK ); MOV32RtoM((int)&psxRegs.pc, ESI); x86regs[ESI].inuse = 0; } else { MOV32MtoR(EAX, (u32)&g_recWriteback); MOV32RtoM((int)&psxRegs.pc, EAX); } } _psxFlushCall(FLUSH_EVERYTHING); iPsxBranchTest(0xffffffff, 1); JMP32((u32)psxDispatcherReg - ( (u32)x86Ptr + 5 )); } void psxSetBranchImm( u32 imm ) { u32* ptr; psxbranch = 1; assert( imm ); // end the current block MOV32ItoM( (u32)&psxRegs.pc, imm ); _psxFlushCall(FLUSH_EVERYTHING); iPsxBranchTest(imm, imm <= psxpc); MOV32ItoR(EDX, 0); ptr = (u32*)(x86Ptr-4); *ptr = (u32)JMP32((u32)psxDispatcher - ( (u32)x86Ptr + 5 )); } #define USE_FAST_BRANCHES 0 static void iPsxBranchTest(u32 newpc, u32 cpuBranch) { if( !USE_FAST_BRANCHES || cpuBranch ) { MOV32MtoR(ECX, (int)&psxRegs.cycle); ADD32ItoR(ECX, s_psxBlockCycles*17/16); // greater mult factor causes nfsmw to crash MOV32RtoM((int)&psxRegs.cycle, ECX); // update cycles } else { ADD32ItoM((int)&psxRegs.cycle, s_psxBlockCycles*17/16); return; } SUB32MtoR(ECX, (int)&g_psxNextBranchCycle); // check if should branch j8Ptr[0] = JS8( 0 ); CALLFunc((int)psxBranchTest); CMP32ItoM((int)&EEsCycle, 0); j8Ptr[2] = JG8(0); RET2(); x86SetJ8( j8Ptr[2] ); if( newpc != 0xffffffff ) { CMP32ItoM((int)&psxRegs.pc, newpc); JNE32((u32)psxDispatcherReg - ( (u32)x86Ptr + 6 )); } x86SetJ8( j8Ptr[0] ); } static int *s_pCode; static void checkcodefn() { int pctemp; __asm mov pctemp, eax SysPrintf("iop code changed! %x\n", pctemp); } void rpsxSYSCALL() { MOV32ItoM( (u32)&psxRegs.code, psxRegs.code ); MOV32ItoM((uptr)&psxRegs.pc, psxpc - 4); _psxFlushCall(FLUSH_NODESTROY); PUSH32I(psxbranch == 1 ? 1 : 0); PUSH32I(0x20); CALLFunc ((int)psxException); ADD32ItoR(ESP, 8); CMP32ItoM((int)&psxRegs.pc, psxpc-4); j8Ptr[0] = JE8(0); ADD32ItoM((u32)&psxRegs.cycle, s_psxBlockCycles); JMP32((u32)psxDispatcherReg - ( (u32)x86Ptr + 5 )); x86SetJ8(j8Ptr[0]); //if (!psxbranch) psxbranch = 2; } u32 psxRecompileCodeSafe(u32 temppc) { BASEBLOCK* pblock = PSX_GETBLOCK(temppc); if( pblock->pFnptr != 0 && pblock->startpc != s_pCurBlock->startpc ) { if( psxpc == pblock->startpc ) return 0; } return 1; } void psxRecompileNextInstruction(int delayslot) { static u8 s_bFlushReg = 1; BASEBLOCK* pblock = PSX_GETBLOCK(psxpc); // need *ppblock != s_pCurBlock because of branches if( pblock->pFnptr != 0 && pblock->startpc != s_pCurBlock->startpc ) { if( !delayslot && psxpc == pblock->startpc ) { // code already in place, so jump to it and exit recomp assert( PSX_GETBLOCKEX(pblock)->startpc == pblock->startpc ); _psxFlushCall(FLUSH_EVERYTHING); MOV32ItoM((u32)&psxRegs.pc, psxpc); // if( pexblock->pOldFnptr ) { // // code already in place, so jump to it and exit recomp // JMP32((u32)pexblock->pOldFnptr - ((u32)x86Ptr + 5)); // branch = 3; // return; // } JMP32((u32)pblock->pFnptr - ((u32)x86Ptr + 5)); psxbranch = 3; return; } else { if( !(delayslot && pblock->startpc == psxpc) ) { s8* oldX86 = x86Ptr; //__Log("clear block %x\n", pblock->startpc); psxRecClearMem(pblock); x86Ptr = oldX86; if( delayslot ) SysPrintf("delay slot %x\n", psxpc); } } } if( delayslot ) pblock->uType = BLOCKTYPE_DELAYSLOT; s_pCode = (int *)PSXM( psxpc ); assert(s_pCode); psxRegs.code = *(int *)s_pCode; s_psxBlockCycles++; psxpc += 4; //#ifdef _DEBUG // CMP32ItoM((u32)s_pCode, psxRegs.code); // j8Ptr[0] = JE8(0); // MOV32ItoR(EAX, psxpc); // CALLFunc((u32)checkcodefn); // x86SetJ8( j8Ptr[ 0 ] ); //#endif g_pCurInstInfo++; // peephole optimizations if( g_pCurInstInfo->info & EEINSTINFO_COREC ) { assert(0); // recBSC_co[cpuRegs.code>>26](); // psxpc += 4; // s_psxBlockCycles++; // g_pCurInstInfo++; } else { assert( !(g_pCurInstInfo->info & EEINSTINFO_NOREC) ); rpsxBSC[ psxRegs.code >> 26 ](); } if( !delayslot ) { if( s_bFlushReg ) { //_psxFlushUnusedConstReg(); } else s_bFlushReg = 1; } else s_bFlushReg = 1; _clearNeededX86regs(); } static void recExecute() { for (;;) execute(); } static void recExecuteBlock() { execute(); } extern "C" void iDumpPsxRegisters(u32 startpc, u32 temp) { int i; char* pstr = temp ? "t" : ""; __Log("%spsxreg: %x %x\n", pstr, startpc, psxRegs.interrupt); for(i = 0; i < 34; i+=2) __Log("%spsx%d: %x %x\n", pstr, i, psxRegs.GPR.r[i], psxRegs.GPR.r[i+1]); __Log("%scycle: %x %x %x %x; counters %x %x\n", pstr, psxRegs.cycle, g_psxNextBranchCycle, EEsCycle, IOPoCycle, (u32)psxNextsCounter, (u32)psxNextCounter); for(i = 0; i < 6; ++i) __Log("%scounter%d: %x %x %x\n", pstr, i, psxCounters[i].count, psxCounters[i].rate, psxCounters[i].sCycleT); for(i = 0; i < 32; ++i) { __Log("int%d: %x %x\n", i, psxRegs.sCycle[i], psxRegs.eCycle[i]); } } void iDumpPsxRegisters(u32 startpc); static void printfn() { static int lastrec = 0; static int curcount = 0; const int skip = 0; if( (psxdump&2) ) {//&& lastrec != g_psxlastpc ) { curcount++; if( curcount > skip ) { iDumpPsxRegisters(g_psxlastpc, 1); curcount = 0; } lastrec = g_psxlastpc; } } u32 s_recblocks[] = {0}; static void recRecompile(u32 startpc) { u32 i; u32 branchTo; u32 willbranch3 = 0; u32* ptr; #ifdef _DEBUG //psxdump |= 4; if( psxdump & 4 ) iDumpPsxRegisters(startpc, 0); #endif assert( startpc ); // if recPtr reached the mem limit reset whole mem if (((uptr)recPtr - (uptr)recMem) >= (RECMEM_SIZE - 0x10000)) recReset(); s_pCurBlock = PSX_GETBLOCK(startpc); if( s_pCurBlock->pFnptr ) { // clear if already taken assert( s_pCurBlock->startpc < startpc ); psxRecClearMem(s_pCurBlock); } if( s_pCurBlock->startpc == startpc ) { s_pCurBlockEx = PSX_GETBLOCKEX(s_pCurBlock); assert( s_pCurBlockEx->startpc == startpc ); } else { s_pCurBlockEx = NULL; for(i = 0; i < PSX_NUMBLOCKS; ++i) { if( recBlocks[(i+s_nNextBlock)%PSX_NUMBLOCKS].size == 0 ) { s_pCurBlockEx = recBlocks+(i+s_nNextBlock)%PSX_NUMBLOCKS; s_nNextBlock = (i+s_nNextBlock+1)%PSX_NUMBLOCKS; break; } } if( s_pCurBlockEx == NULL ) { //SysPrintf("ee reset (blocks)\n"); recReset(); s_nNextBlock = 0; s_pCurBlockEx = recBlocks; } s_pCurBlockEx->startpc = startpc; } x86SetPtr( recPtr ); x86Align(16); recPtr = x86Ptr; psxbranch = 0; s_pCurBlock->startpc = startpc; s_pCurBlock->pFnptr = (u32)x86Ptr; s_psxBlockCycles = 0; // reset recomp state variables psxpc = startpc; s_saveConstGPRreg = 0; g_psxHasConstReg = g_psxFlushedConstReg = 1; _initX86regs(); #ifdef _DEBUG // for debugging purposes MOV32ItoM((u32)&g_psxlastpc, psxpc); CALLFunc((u32)printfn); #endif // go until the next branch i = startpc; s_nEndBlock = 0xffffffff; while(1) { BASEBLOCK* pblock = PSX_GETBLOCK(i); if( pblock->pFnptr != 0 && pblock->startpc != s_pCurBlock->startpc ) { if( i == pblock->startpc ) { // branch = 3 willbranch3 = 1; s_nEndBlock = i; break; } } psxRegs.code = *(int *)PSXM(i); switch(psxRegs.code >> 26) { case 0: // special if( _Funct_ == 8 || _Funct_ == 9 ) { // JR, JALR s_nEndBlock = i + 8; goto StartRecomp; } break; case 1: // regimm if( _Rt_ == 0 || _Rt_ == 1 || _Rt_ == 16 || _Rt_ == 17 ) { branchTo = _Imm_ * 4 + i + 4; if( branchTo > startpc && branchTo < i ) s_nEndBlock = branchTo; else s_nEndBlock = i+8; goto StartRecomp; } break; case 2: // J case 3: // JAL s_nEndBlock = i + 8; goto StartRecomp; // branches case 4: case 5: case 6: case 7: branchTo = _Imm_ * 4 + i + 4; if( branchTo > startpc && branchTo < i ) s_nEndBlock = branchTo; else s_nEndBlock = i+8; goto StartRecomp; } i += 4; } StartRecomp: // rec info // { EEINST* pcur; if( s_nInstCacheSize < (s_nEndBlock-startpc)/4+1 ) { free(s_pInstCache); s_nInstCacheSize = (s_nEndBlock-startpc)/4+10; s_pInstCache = (EEINST*)malloc(sizeof(EEINST)*s_nInstCacheSize); assert( s_pInstCache != NULL ); } pcur = s_pInstCache + (s_nEndBlock-startpc)/4; _recClearInst(pcur); pcur->info = 0; for(i = s_nEndBlock; i > startpc; i -= 4 ) { psxRegs.code = *(int *)PSXM(i-4); pcur[-1] = pcur[0]; rpsxpropBSC(pcur-1, pcur); pcur--; } } // peephole optimizations // // { // g_pCurInstInfo = s_pInstCache; // // for(i = startpc; i < s_nEndBlock-4; i += 4) { // g_pCurInstInfo++; // if( psxRecompileCodeSafe(i) ) { // u32 curcode = *(u32*)PSXM(i); // u32 nextcode = *(u32*)PSXM(i+4); // if( _psxIsLoadStore(curcode) && _psxIsLoadStore(nextcode) && (curcode>>26) == (nextcode>>26) && rpsxBSC_co[curcode>>26] != NULL ) { // // // rs has to be the same, and cannot be just written // if( ((curcode >> 21) & 0x1F) == ((nextcode >> 21) & 0x1F) && !_psxLoadWritesRs(curcode) ) { // // // good enough // g_pCurInstInfo[0].info |= EEINSTINFO_COREC; // g_pCurInstInfo[0].numpeeps = 1; // g_pCurInstInfo[1].info |= EEINSTINFO_NOREC; // g_pCurInstInfo++; // i += 4; // continue; // } // } // } // } // } #ifdef _DEBUG // dump code for(i = 0; i < ARRAYSIZE(s_recblocks); ++i) { if( startpc == s_recblocks[i] ) { iDumpBlock(startpc, recPtr); } } if( (psxdump & 1) ) iDumpBlock(startpc, recPtr); #endif g_pCurInstInfo = s_pInstCache; while (!psxbranch && psxpc < s_nEndBlock) { psxRecompileNextInstruction(0); } assert( (psxpc-startpc)>>2 <= 0xffff ); s_pCurBlockEx->size = (psxpc-startpc)>>2; for(i = 1; i < (u32)s_pCurBlockEx->size-1; ++i) { s_pCurBlock[i].pFnptr = s_pCurBlock->pFnptr; s_pCurBlock[i].startpc = s_pCurBlock->startpc; } // don't overwrite if delay slot if( i < (u32)s_pCurBlockEx->size && !(s_pCurBlock[i].uType & BLOCKTYPE_DELAYSLOT) ) { s_pCurBlock[i].pFnptr = s_pCurBlock->pFnptr; s_pCurBlock[i].startpc = s_pCurBlock->startpc; } // set the block ptr AddBaseBlockEx(s_pCurBlockEx, 1); if( !(psxpc&0x10000000) ) g_psxMaxRecMem = max( (psxpc&~0xa0000000), g_psxMaxRecMem ); if( psxbranch == 2 ) { _psxFlushCall(FLUSH_EVERYTHING); iPsxBranchTest(0xffffffff, 1); JMP32((u32)psxDispatcherReg - ( (u32)x86Ptr + 5 )); } else { assert( psxbranch != 3 ); if( psxbranch ) assert( !willbranch3 ); else ADD32ItoM((int)&psxRegs.cycle, s_psxBlockCycles*17/16); if( willbranch3 ) { BASEBLOCK* pblock = PSX_GETBLOCK(s_nEndBlock); assert( psxpc == s_nEndBlock ); _psxFlushCall(FLUSH_EVERYTHING); MOV32ItoM((u32)&psxRegs.pc, psxpc); JMP32((u32)pblock->pFnptr - ((u32)x86Ptr + 5)); psxbranch = 3; } else if( !psxbranch ) { // didn't branch, but had to stop MOV32ItoM( (u32)&psxRegs.pc, psxpc ); _psxFlushCall(FLUSH_EVERYTHING); ptr = JMP32(0); //JMP32((u32)psxDispatcherReg - ( (u32)x86Ptr + 5 )); } } assert( x86Ptr >= (s8*)s_pCurBlock->pFnptr + EE_MIN_BLOCK_BYTES ); assert( x86Ptr < recMem+RECMEM_SIZE ); recPtr = x86Ptr; assert( (g_psxHasConstReg&g_psxFlushedConstReg) == g_psxHasConstReg ); if( !psxbranch ) { BASEBLOCK* pcurblock = s_pCurBlock; u32 nEndBlock = s_nEndBlock; s_pCurBlock = PSX_GETBLOCK(psxpc); assert( ptr != NULL ); if( s_pCurBlock->startpc != psxpc ) recRecompile(psxpc); // could have reset if( pcurblock->startpc == startpc ) { assert( pcurblock->pFnptr ); assert( s_pCurBlock->startpc == nEndBlock ); *ptr = s_pCurBlock->pFnptr - ( (u32)ptr + 4 ); } else { recRecompile(startpc); assert( pcurblock->pFnptr != 0 ); } } } R3000Acpu psxRec = { recInit, recReset, recExecute, recExecuteBlock, recClear, recShutdown }; #endif