* Fixed a bug in the JCC instruction.
* Changed the cycle count from u32 to u16.
* Added cycle counting to the block linker.
* Optimised the branch exit slightly.

git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@6681 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
skidau 2010-12-29 04:34:33 +00:00
parent fc1db5eaa0
commit 8ccdba7273
6 changed files with 146 additions and 74 deletions

View File

@ -490,6 +490,10 @@
RelativePath=".\Src\Jit\DSPJitUtil.cpp" RelativePath=".\Src\Jit\DSPJitUtil.cpp"
> >
</File> </File>
<File
RelativePath=".\Src\Jit\DSPJitUtil.h"
>
</File>
</Filter> </Filter>
<File <File
RelativePath=".\Src\assemble.cpp" RelativePath=".\Src\assemble.cpp"

View File

@ -37,7 +37,7 @@
SDSP g_dsp; SDSP g_dsp;
DSPBreakpoints dsp_breakpoints; DSPBreakpoints dsp_breakpoints;
DSPCoreState core_state = DSPCORE_STOP; DSPCoreState core_state = DSPCORE_STOP;
int cyclesLeft = 0; u16 cyclesLeft = 0;
DSPEmitter *jit = NULL; DSPEmitter *jit = NULL;
Common::Event step_event; Common::Event step_event;

View File

@ -267,7 +267,7 @@ struct SDSP
extern SDSP g_dsp; extern SDSP g_dsp;
extern DSPBreakpoints dsp_breakpoints; extern DSPBreakpoints dsp_breakpoints;
extern DSPEmitter *jit; extern DSPEmitter *jit;
extern int cyclesLeft; extern u16 cyclesLeft;
bool DSPCore_Init(const char *irom_filename, const char *coef_filename, bool DSPCore_Init(const char *irom_filename, const char *coef_filename,
bool bUsingJIT); bool bUsingJIT);

View File

@ -26,14 +26,42 @@
#include "ABI.h" #include "ABI.h"
#define MAX_BLOCK_SIZE 250 #define MAX_BLOCK_SIZE 250
#define DSP_IDLE_SKIP_CYCLES 1000 #define DSP_IDLE_SKIP_CYCLES 0x1000
using namespace Gen; using namespace Gen;
const u8 *stubEntryPoint; const u8 *stubEntryPoint;
u16 blocksCompiled; u16 blocksCompiled;
u16 unresolvedCalls; u16 unresolvedCalls;
int startAddr;
static bool checkExtendedExclude(UDSPInstruction inst)
{
const DSPOPCTemplate *tinst = GetOpTemplate(inst);
// Call extended
if (!tinst->extended)
return false;
if ((inst >> 12) != 0x3)
return false;
/*
if((inst & 0x00ff) == 0x00c0) {
fprintf(stderr,"blocking %04x\n", inst);
return true;
}
*/
return false;
}
static bool checkMainExclude(UDSPInstruction inst)
{
/*
if((inst & 0xfffc) == 0x1fcc)
return true;
*/
// if((inst & 0xfffc) == 0x1fcc)
// return true;
return false;
}
DSPEmitter::DSPEmitter() : storeIndex(-1), storeIndex2(-1) DSPEmitter::DSPEmitter() : storeIndex(-1), storeIndex2(-1)
{ {
@ -112,35 +140,6 @@ void DSPEmitter::checkExceptions(u32 retval)
SetJumpTarget(skipCheck); SetJumpTarget(skipCheck);
} }
static bool checkExtendedExclude(UDSPInstruction inst)
{
const DSPOPCTemplate *tinst = GetOpTemplate(inst);
// Call extended
if (!tinst->extended)
return false;
if ((inst >> 12) != 0x3)
return false;
/*
if((inst & 0x00ff) == 0x00c0) {
fprintf(stderr,"blocking %04x\n", inst);
return true;
}
*/
return false;
}
static bool checkMainExclude(UDSPInstruction inst)
{
/*
if((inst & 0xfffc) == 0x1fcc)
return true;
*/
// if((inst & 0xfffc) == 0x1fcc)
// return true;
return false;
}
void DSPEmitter::Default(UDSPInstruction inst) void DSPEmitter::Default(UDSPInstruction inst)
{ {
if (opTable[inst]->reads_pc) if (opTable[inst]->reads_pc)
@ -359,11 +358,11 @@ void DSPEmitter::Compile(int start_addr)
ABI_PopAllCalleeSavedRegsAndAdjustStack(); ABI_PopAllCalleeSavedRegsAndAdjustStack();
if (DSPAnalyzer::code_flags[start_addr] & DSPAnalyzer::CODE_IDLE_SKIP) if (DSPAnalyzer::code_flags[start_addr] & DSPAnalyzer::CODE_IDLE_SKIP)
{ {
MOV(32,R(EAX),Imm32(DSP_IDLE_SKIP_CYCLES)); MOV(16, R(EAX), Imm16(DSP_IDLE_SKIP_CYCLES));
} }
else else
{ {
MOV(32,R(EAX),Imm32(blockSize[start_addr])); MOV(16, R(EAX), Imm16(blockSize[start_addr]));
} }
RET(); RET();
@ -379,7 +378,7 @@ void DSPEmitter::Compile(int start_addr)
{ {
break; break;
} }
else else if (!opcode->jitFunc)
{ {
//look at g_dsp.pc if we actually branched //look at g_dsp.pc if we actually branched
#ifdef _M_IX86 // All32 #ifdef _M_IX86 // All32
@ -396,11 +395,11 @@ void DSPEmitter::Compile(int start_addr)
ABI_PopAllCalleeSavedRegsAndAdjustStack(); ABI_PopAllCalleeSavedRegsAndAdjustStack();
if (DSPAnalyzer::code_flags[start_addr] & DSPAnalyzer::CODE_IDLE_SKIP) if (DSPAnalyzer::code_flags[start_addr] & DSPAnalyzer::CODE_IDLE_SKIP)
{ {
MOV(32,R(EAX),Imm32(DSP_IDLE_SKIP_CYCLES)); MOV(16, R(EAX), Imm16(DSP_IDLE_SKIP_CYCLES));
} }
else else
{ {
MOV(32,R(EAX),Imm32(blockSize[start_addr])); MOV(16, R(EAX), Imm16(blockSize[start_addr]));
} }
RET(); RET();
@ -443,11 +442,11 @@ void DSPEmitter::Compile(int start_addr)
ABI_PopAllCalleeSavedRegsAndAdjustStack(); ABI_PopAllCalleeSavedRegsAndAdjustStack();
if (DSPAnalyzer::code_flags[start_addr] & DSPAnalyzer::CODE_IDLE_SKIP) if (DSPAnalyzer::code_flags[start_addr] & DSPAnalyzer::CODE_IDLE_SKIP)
{ {
MOV(32,R(EAX),Imm32(DSP_IDLE_SKIP_CYCLES)); MOV(16, R(EAX), Imm16(DSP_IDLE_SKIP_CYCLES));
} }
else else
{ {
MOV(32,R(EAX),Imm32(blockSize[start_addr])); MOV(16, R(EAX), Imm16(blockSize[start_addr]));
} }
RET(); RET();
} }
@ -473,13 +472,13 @@ void DSPEmitter::CompileDispatcher()
// Cache pointers into registers // Cache pointers into registers
#ifdef _M_IX86 #ifdef _M_IX86
MOV(32, R(ESI), M(&cyclesLeft)); MOV(16, R(ESI), M(&cyclesLeft));
MOV(32, R(EBX), ImmPtr(blocks)); MOV(32, R(EBX), ImmPtr(blocks));
#else #else
// Using R12 here since it is callee save register on both // Using R12 here since it is callee save register on both
// linux and windows 64. // linux and windows 64.
MOV(64, R(R12), ImmPtr(&cyclesLeft)); MOV(64, R(R12), ImmPtr(&cyclesLeft));
MOV(32, R(R12), MatR(R12)); MOV(16, R(R12), MatR(R12));
MOV(64, R(RBX), ImmPtr(blocks)); MOV(64, R(RBX), ImmPtr(blocks));
#endif #endif
@ -510,9 +509,9 @@ void DSPEmitter::CompileDispatcher()
// Decrement cyclesLeft // Decrement cyclesLeft
#ifdef _M_IX86 #ifdef _M_IX86
SUB(32, R(ESI), R(EAX)); SUB(16, R(ESI), R(EAX));
#else #else
SUB(32, R(R12), R(EAX)); SUB(16, R(R12), R(EAX));
#endif #endif
J_CC(CC_A, dispatcherLoop); J_CC(CC_A, dispatcherLoop);

View File

@ -249,12 +249,13 @@ public:
// CALL this to start the dispatcher // CALL this to start the dispatcher
const u8 *enterDispatcher; const u8 *enterDispatcher;
u16 compilePC; u16 compilePC;
u16 startAddr;
CompiledCode *blockLinks; CompiledCode *blockLinks;
u16 *blockSize;
private: private:
CompiledCode *blocks; CompiledCode *blocks;
const u8 *blockLinkEntry; const u8 *blockLinkEntry;
u16 *blockSize;
u16 compileSR; u16 compileSR;
// The index of the last stored ext value (compile time). // The index of the last stored ext value (compile time).

View File

@ -18,6 +18,7 @@
#include "../DSPMemoryMap.h" #include "../DSPMemoryMap.h"
#include "../DSPEmitter.h" #include "../DSPEmitter.h"
#include "../DSPStacks.h" #include "../DSPStacks.h"
#include "../DSPAnalyzer.h"
#include "DSPJitUtil.h" #include "DSPJitUtil.h"
#include "x64Emitter.h" #include "x64Emitter.h"
#include "ABI.h" #include "ABI.h"
@ -146,31 +147,66 @@ void ReJitConditional(const UDSPInstruction opc, DSPEmitter& emitter)
//} //}
} }
void WriteBranchExit(DSPEmitter& emitter)
{
// ABI_RestoreStack(0);
emitter.ABI_PopAllCalleeSavedRegsAndAdjustStack();
if (DSPAnalyzer::code_flags[emitter.startAddr] & DSPAnalyzer::CODE_IDLE_SKIP)
{
emitter.MOV(16, R(EAX), Imm16(0x1000));
}
else
{
emitter.MOV(16, R(EAX), Imm16(emitter.blockSize[emitter.startAddr]));
}
emitter.RET();
}
void r_jcc(const UDSPInstruction opc, DSPEmitter& emitter) void r_jcc(const UDSPInstruction opc, DSPEmitter& emitter)
{ {
u16 dest = dsp_imem_read(emitter.compilePC + 1); u16 dest = dsp_imem_read(emitter.compilePC + 1);
#ifdef _M_IX86 // All32 #ifdef _M_IX86 // All32
emitter.MOV(16, M(&(g_dsp.pc)), Imm16(dest)); emitter.MOV(16, M(&(g_dsp.pc)), Imm16(dest));
// Jump directly to the called block if it has already been compiled. // Jump directly to the called block if it has already been compiled. (Not working)
// TODO: Subtract cycles from cyclesLeft //if (emitter.blockLinks[dest])
if (emitter.blockLinks[dest]) //{
{ // // Check if we have enough cycles to execute the next block
emitter.JMPptr(M(&emitter.blockLinks[dest])); // emitter.MOV(16, R(ESI), M(&cyclesLeft));
} // emitter.CMP(16, R(ESI),Imm16(emitter.blockSize[emitter.startAddr] + emitter.blockSize[dest]));
// FixupBranch notEnoughCycles = emitter.J_CC(CC_BE);
// emitter.SUB(16, R(ESI), Imm16(emitter.blockSize[emitter.startAddr]));
// emitter.MOV(16, M(&cyclesLeft), R(ESI));
// emitter.JMPptr(M(&emitter.blockLinks[dest]));
// emitter.ClearCallFlag();
// emitter.SetJumpTarget(notEnoughCycles);
//}
#else #else
emitter.MOV(64, R(RAX), ImmPtr(&(g_dsp.pc))); emitter.MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
emitter.MOV(16, MatR(RAX), Imm16(dest)); emitter.MOV(16, MatR(RAX), Imm16(dest));
// Jump directly to the next block if it has already been compiled. // Jump directly to the next block if it has already been compiled. (Not working)
// TODO: Subtract cycles from cyclesLeft //if (emitter.blockLinks[dest])
if (emitter.blockLinks[dest]) //{
{ // // Check if we have enough cycles to execute the next block
emitter.MOV(64, R(RAX), ImmPtr((void *)(emitter.blockLinks[dest]))); // emitter.MOV(64, R(R12), ImmPtr(&cyclesLeft));
emitter.JMPptr(R(RAX)); // emitter.MOV(16, R(RAX), MatR(R12));
emitter.ClearCallFlag(); // emitter.CMP(16,R(RAX),Imm16(emitter.blockSize[emitter.startAddr] + emitter.blockSize[dest]));
} // FixupBranch notEnoughCycles = emitter.J_CC(CC_BE);
// emitter.SUB(16, R(RAX), Imm16(emitter.blockSize[emitter.startAddr]));
// emitter.MOV(16, MatR(R12), R(RAX));
// emitter.MOV(64, R(RAX), ImmPtr((void *)(emitter.blockLinks[dest])));
// emitter.JMPptr(R(RAX));
// emitter.ClearCallFlag();
// emitter.SetJumpTarget(notEnoughCycles);
//}
#endif #endif
WriteBranchExit(emitter);
} }
// Generic jmp implementation // Generic jmp implementation
// Jcc addressA // Jcc addressA
@ -178,19 +214,16 @@ void r_jcc(const UDSPInstruction opc, DSPEmitter& emitter)
// aaaa aaaa aaaa aaaa // aaaa aaaa aaaa aaaa
// Jump to addressA if condition cc has been met. Set program counter to // Jump to addressA if condition cc has been met. Set program counter to
// address represented by value that follows this "jmp" instruction. // address represented by value that follows this "jmp" instruction.
// NOTE: Cannot use Default(opc) here because of the need to write branch exit
void DSPEmitter::jcc(const UDSPInstruction opc) void DSPEmitter::jcc(const UDSPInstruction opc)
{ {
// Disabled as jcc has issues in games
Default(opc); return;
#if 0
#ifdef _M_IX86 // All32 #ifdef _M_IX86 // All32
MOV(16, M(&(g_dsp.pc)), Imm16(compilePC + 1)); MOV(16, M(&(g_dsp.pc)), Imm16(compilePC + 2));
#else #else
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc))); MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
MOV(16, MatR(RAX), Imm16(compilePC + 1)); MOV(16, MatR(RAX), Imm16(compilePC + 2));
#endif #endif
ReJitConditional<r_jcc>(opc, *this); ReJitConditional<r_jcc>(opc, *this);
#endif
} }
void r_jmprcc(const UDSPInstruction opc, DSPEmitter& emitter) void r_jmprcc(const UDSPInstruction opc, DSPEmitter& emitter)
@ -208,13 +241,21 @@ void r_jmprcc(const UDSPInstruction opc, DSPEmitter& emitter)
emitter.MOV(64, R(RAX), ImmPtr(&(g_dsp.pc))); emitter.MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
emitter.MOV(16, MatR(RAX), R(RSI)); emitter.MOV(16, MatR(RAX), R(RSI));
#endif #endif
WriteBranchExit(emitter);
} }
// Generic jmpr implementation // Generic jmpr implementation
// JMPcc $R // JMPcc $R
// 0001 0111 rrr0 cccc // 0001 0111 rrr0 cccc
// Jump to address; set program counter to a value from register $R. // Jump to address; set program counter to a value from register $R.
// NOTE: Cannot use Default(opc) here because of the need to write branch exit
void DSPEmitter::jmprcc(const UDSPInstruction opc) void DSPEmitter::jmprcc(const UDSPInstruction opc)
{ {
#ifdef _M_IX86 // All32
MOV(16, M(&g_dsp.pc), Imm16(compilePC + 1));
#else
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
MOV(16, MatR(RAX), Imm16(compilePC + 1));
#endif
ReJitConditional<r_jmprcc>(opc, *this); ReJitConditional<r_jmprcc>(opc, *this);
} }
@ -227,24 +268,43 @@ void r_call(const UDSPInstruction opc, DSPEmitter& emitter)
emitter.MOV(16, M(&(g_dsp.pc)), Imm16(dest)); emitter.MOV(16, M(&(g_dsp.pc)), Imm16(dest));
// Jump directly to the called block if it has already been compiled. // Jump directly to the called block if it has already been compiled.
// TODO: Subtract cycles from cyclesLeft
if (emitter.blockLinks[dest]) if (emitter.blockLinks[dest])
{ {
// Check if we have enough cycles to execute the next block
emitter.MOV(16, R(ESI), M(&cyclesLeft));
emitter.CMP(16, R(ESI), Imm16(emitter.blockSize[emitter.startAddr] + emitter.blockSize[dest]));
FixupBranch notEnoughCycles = emitter.J_CC(CC_BE);
emitter.SUB(16, R(ESI), Imm16(emitter.blockSize[emitter.startAddr]));
emitter.MOV(16, M(&cyclesLeft), R(ESI));
emitter.JMPptr(M(&emitter.blockLinks[dest])); emitter.JMPptr(M(&emitter.blockLinks[dest]));
emitter.ClearCallFlag();
emitter.SetJumpTarget(notEnoughCycles);
} }
#else #else
emitter.MOV(64, R(RAX), ImmPtr(&(g_dsp.pc))); emitter.MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
emitter.MOV(16, MatR(RAX), Imm16(dest)); emitter.MOV(16, MatR(RAX), Imm16(dest));
// Jump directly to the called block if it has already been compiled. // Jump directly to the called block if it has already been compiled.
// TODO: Subtract cycles from cyclesLeft
if (emitter.blockLinks[dest]) if (emitter.blockLinks[dest])
{ {
// Check if we have enough cycles to execute the next block
emitter.MOV(64, R(R12), ImmPtr(&cyclesLeft));
emitter.MOV(16, R(RAX), MatR(R12));
emitter.CMP(16,R(RAX), Imm16(emitter.blockSize[emitter.startAddr] + emitter.blockSize[dest]));
FixupBranch notEnoughCycles = emitter.J_CC(CC_BE);
emitter.SUB(16, R(RAX), Imm16(emitter.blockSize[emitter.startAddr]));
emitter.MOV(16, MatR(R12), R(RAX));
emitter.MOV(64, R(RAX), ImmPtr((void *)(emitter.blockLinks[dest]))); emitter.MOV(64, R(RAX), ImmPtr((void *)(emitter.blockLinks[dest])));
emitter.JMPptr(R(RAX)); emitter.JMPptr(R(RAX));
emitter.ClearCallFlag(); emitter.ClearCallFlag();
emitter.SetJumpTarget(notEnoughCycles);
} }
#endif #endif
WriteBranchExit(emitter);
} }
// Generic call implementation // Generic call implementation
// CALLcc addressA // CALLcc addressA
@ -253,13 +313,14 @@ void r_call(const UDSPInstruction opc, DSPEmitter& emitter)
// Call function if condition cc has been met. Push program counter of // Call function if condition cc has been met. Push program counter of
// instruction following "call" to $st0. Set program counter to address // instruction following "call" to $st0. Set program counter to address
// represented by value that follows this "call" instruction. // represented by value that follows this "call" instruction.
// NOTE: Cannot use Default(opc) here because of the need to write branch exit
void DSPEmitter::call(const UDSPInstruction opc) void DSPEmitter::call(const UDSPInstruction opc)
{ {
#ifdef _M_IX86 // All32 #ifdef _M_IX86 // All32
MOV(16, M(&(g_dsp.pc)), Imm16(compilePC + 1)); MOV(16, M(&(g_dsp.pc)), Imm16(compilePC + 2));
#else #else
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc))); MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
MOV(16, MatR(RAX), Imm16(compilePC + 1)); MOV(16, MatR(RAX), Imm16(compilePC + 2));
#endif #endif
ReJitConditional<r_call>(opc, *this); ReJitConditional<r_call>(opc, *this);
} }
@ -279,6 +340,7 @@ void r_callr(const UDSPInstruction opc, DSPEmitter& emitter)
emitter.MOV(64, R(RAX), ImmPtr(&(g_dsp.pc))); emitter.MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
emitter.MOV(16, MatR(RAX), R(RSI)); emitter.MOV(16, MatR(RAX), R(RSI));
#endif #endif
WriteBranchExit(emitter);
} }
// Generic callr implementation // Generic callr implementation
// CALLRcc $R // CALLRcc $R
@ -286,6 +348,7 @@ void r_callr(const UDSPInstruction opc, DSPEmitter& emitter)
// Call function if condition cc has been met. Push program counter of // Call function if condition cc has been met. Push program counter of
// instruction following "call" to call stack $st0. Set program counter to // instruction following "call" to call stack $st0. Set program counter to
// register $R. // register $R.
// NOTE: Cannot use Default(opc) here because of the need to write branch exit
void DSPEmitter::callr(const UDSPInstruction opc) void DSPEmitter::callr(const UDSPInstruction opc)
{ {
#ifdef _M_IX86 // All32 #ifdef _M_IX86 // All32
@ -306,11 +369,11 @@ void r_ifcc(const UDSPInstruction opc, DSPEmitter& emitter)
emitter.MOV(16, MatR(RAX), Imm16(emitter.compilePC + 1)); emitter.MOV(16, MatR(RAX), Imm16(emitter.compilePC + 1));
#endif #endif
} }
// Generic if implementation
// Generic jmpr implementation // IFcc
// JMPcc $R // 0000 0010 0111 cccc
// 0001 0111 rrr0 cccc // Execute following opcode if the condition has been met.
// Jump to address; set program counter to a value from register $R. // NOTE: Cannot use Default(opc) here because of the need to write branch exit
void DSPEmitter::ifcc(const UDSPInstruction opc) void DSPEmitter::ifcc(const UDSPInstruction opc)
{ {
#ifdef _M_IX86 // All32 #ifdef _M_IX86 // All32
@ -320,6 +383,7 @@ void DSPEmitter::ifcc(const UDSPInstruction opc)
MOV(16, MatR(RAX), Imm16((compilePC + 1) + opTable[compilePC + 1]->size)); MOV(16, MatR(RAX), Imm16((compilePC + 1) + opTable[compilePC + 1]->size));
#endif #endif
ReJitConditional<r_ifcc>(opc, *this); ReJitConditional<r_ifcc>(opc, *this);
WriteBranchExit(*this);
} }
void r_ret(const UDSPInstruction opc, DSPEmitter& emitter) void r_ret(const UDSPInstruction opc, DSPEmitter& emitter)
@ -331,6 +395,7 @@ void r_ret(const UDSPInstruction opc, DSPEmitter& emitter)
emitter.MOV(64, R(RAX), ImmPtr(&(g_dsp.pc))); emitter.MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
emitter.MOV(16, MatR(RAX), R(DX)); emitter.MOV(16, MatR(RAX), R(DX));
#endif #endif
WriteBranchExit(emitter);
} }
// Generic ret implementation // Generic ret implementation
@ -338,6 +403,7 @@ void r_ret(const UDSPInstruction opc, DSPEmitter& emitter)
// 0000 0010 1101 cccc // 0000 0010 1101 cccc
// Return from subroutine if condition cc has been met. Pops stored PC // Return from subroutine if condition cc has been met. Pops stored PC
// from call stack $st0 and sets $pc to this location. // from call stack $st0 and sets $pc to this location.
// NOTE: Cannot use Default(opc) here because of the need to write branch exit
void DSPEmitter::ret(const UDSPInstruction opc) void DSPEmitter::ret(const UDSPInstruction opc)
{ {
#ifdef _M_IX86 // All32 #ifdef _M_IX86 // All32
@ -563,6 +629,7 @@ void DSPEmitter::bloop(const UDSPInstruction opc)
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc))); MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
MOV(16, MatR(RAX), Imm16(loop_pc + opTable[loop_pc]->size)); MOV(16, MatR(RAX), Imm16(loop_pc + opTable[loop_pc]->size));
#endif #endif
WriteBranchExit(*this);
SetJumpTarget(exit); SetJumpTarget(exit);
} }
@ -607,5 +674,6 @@ void DSPEmitter::bloopi(const UDSPInstruction opc)
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc))); MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
MOV(16, MatR(RAX), Imm16(loop_pc + opTable[loop_pc]->size)); MOV(16, MatR(RAX), Imm16(loop_pc + opTable[loop_pc]->size));
#endif #endif
WriteBranchExit(*this);
} }
} }