1
// Copyright 2013 Dolphin Emulator Project
2
// Licensed under GPLv2
3
// Refer to the license.txt file included.
7
#include "DSPEmitter.h"
8
#include "DSPMemoryMap.h"
11
#include "DSPInterpreter.h"
12
#include "DSPAnalyzer.h"
14
#define MAX_BLOCK_SIZE 250
15
#define DSP_IDLE_SKIP_CYCLES 0x1000
19
DSPEmitter::DSPEmitter() : gpr(*this), storeIndex(-1), storeIndex2(-1)
21
m_compiledCode = NULL;
23
AllocCodeSpace(COMPILED_CODE_SIZE);
25
blocks = new DSPCompiledCode[MAX_BLOCKS];
26
blockLinks = new Block[MAX_BLOCKS];
27
blockSize = new u16[MAX_BLOCKS];
30
compileSR |= SR_INT_ENABLE;
31
compileSR |= SR_EXT_INT_ENABLE;
34
stubEntryPoint = CompileStub();
36
//clear all of the block references
37
for(int i = 0x0000; i < MAX_BLOCKS; i++)
39
blocks[i] = (DSPCompiledCode)stubEntryPoint;
45
DSPEmitter::~DSPEmitter()
53
void DSPEmitter::ClearIRAM()
55
for(int i = 0x0000; i < 0x1000; i++)
57
blocks[i] = (DSPCompiledCode)stubEntryPoint;
60
unresolvedJumps[i].clear();
62
g_dsp.reset_dspjit_codespace = true;
65
void DSPEmitter::ClearIRAMandDSPJITCodespaceReset()
69
stubEntryPoint = CompileStub();
71
for(int i = 0x0000; i < 0x10000; i++)
73
blocks[i] = (DSPCompiledCode)stubEntryPoint;
76
unresolvedJumps[i].clear();
78
g_dsp.reset_dspjit_codespace = false;
82
// Must go out of block if exception is detected
83
void DSPEmitter::checkExceptions(u32 retval)
85
// Check for interrupts and exceptions
86
TEST(8, M(&g_dsp.exceptions), Imm8(0xff));
87
FixupBranch skipCheck = J_CC(CC_Z, true);
89
MOV(16, M(&(g_dsp.pc)), Imm16(compilePC));
91
DSPJitRegCache c(gpr);
93
ABI_CallFunction((void *)&DSPCore_CheckExceptions);
94
MOV(32, R(EAX), Imm32(retval));
95
JMP(returnDispatcher, true);
97
gpr.flushRegs(c,false);
99
SetJumpTarget(skipCheck);
102
bool DSPEmitter::FlagsNeeded()
104
if (!(DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_START_OF_INST) ||
105
(DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_UPDATE_SR))
111
void DSPEmitter::Default(UDSPInstruction inst)
113
if (opTable[inst]->reads_pc)
115
// Increment PC - we shouldn't need to do this for every instruction. only for branches and end of block.
116
// Fallbacks to interpreter need this for fetching immediate values
118
MOV(16, M(&(g_dsp.pc)), Imm16(compilePC + 1));
121
// Fall back to interpreter
123
_assert_msg_(DSPLLE, opTable[inst]->intFunc, "No function for %04x",inst);
124
ABI_CallFunctionC16((void*)opTable[inst]->intFunc, inst);
128
void DSPEmitter::EmitInstruction(UDSPInstruction inst)
130
const DSPOPCTemplate *tinst = GetOpTemplate(inst);
131
bool ext_is_jit = false;
136
if ((inst >> 12) == 0x3)
138
if (! extOpTable[inst & 0x7F]->jitFunc)
140
// Fall back to interpreter
142
ABI_CallFunctionC16((void*)extOpTable[inst & 0x7F]->intFunc, inst);
144
INFO_LOG(DSPLLE, "Instruction not JITed(ext part): %04x\n", inst);
149
(this->*extOpTable[inst & 0x7F]->jitFunc)(inst);
155
if (!extOpTable[inst & 0xFF]->jitFunc)
157
// Fall back to interpreter
159
ABI_CallFunctionC16((void*)extOpTable[inst & 0xFF]->intFunc, inst);
161
INFO_LOG(DSPLLE, "Instruction not JITed(ext part): %04x\n", inst);
166
(this->*extOpTable[inst & 0xFF]->jitFunc)(inst);
173
if (!opTable[inst]->jitFunc)
176
INFO_LOG(DSPLLE, "Instruction not JITed(main part): %04x\n", inst);
180
(this->*opTable[inst]->jitFunc)(inst);
188
//need to call the online cleanup function because
189
//the writeBackLog gets populated at runtime
191
ABI_CallFunction((void*)::applyWriteBackLog);
201
void DSPEmitter::Compile(u16 start_addr)
203
// Remember the current block address for later
204
startAddr = start_addr;
205
unresolvedJumps[start_addr].clear();
207
const u8 *entryPoint = AlignCode16();
210
// Check for other exceptions
211
if (dsp_SR_is_flag_set(SR_INT_ENABLE))
214
if (g_dsp.exceptions == 0)
220
blockLinkEntry = GetCodePtr();
222
compilePC = start_addr;
223
bool fixup_pc = false;
224
blockSize[start_addr] = 0;
226
while (compilePC < start_addr + MAX_BLOCK_SIZE)
228
if (DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_CHECK_INT)
229
checkExceptions(blockSize[start_addr]);
231
UDSPInstruction inst = dsp_imem_read(compilePC);
232
const DSPOPCTemplate *opcode = GetOpTemplate(inst);
234
EmitInstruction(inst);
236
blockSize[start_addr]++;
237
compilePC += opcode->size;
239
// If the block was trying to link into itself, remove the link
240
unresolvedJumps[start_addr].remove(compilePC);
244
// Handle loop condition, only if current instruction was flagged as a loop destination
246
if (DSPAnalyzer::code_flags[compilePC-1] & DSPAnalyzer::CODE_LOOP_END)
248
MOVZX(32, 16, EAX, M(&(g_dsp.r.st[2])));
249
CMP(32, R(EAX), Imm32(0));
250
FixupBranch rLoopAddressExit = J_CC(CC_LE, true);
252
MOVZX(32, 16, EAX, M(&g_dsp.r.st[3]));
253
CMP(32, R(EAX), Imm32(0));
254
FixupBranch rLoopCounterExit = J_CC(CC_LE, true);
258
//branch insns update the g_dsp.pc
259
MOV(16, M(&(g_dsp.pc)), Imm16(compilePC));
262
// These functions branch and therefore only need to be called in the
263
// end of each block and in this order
264
DSPJitRegCache c(gpr);
267
if (!DSPHost_OnThread() && DSPAnalyzer::code_flags[start_addr] & DSPAnalyzer::CODE_IDLE_SKIP)
269
MOV(16, R(EAX), Imm16(DSP_IDLE_SKIP_CYCLES));
273
MOV(16, R(EAX), Imm16(blockSize[start_addr]));
275
JMP(returnDispatcher, true);
277
gpr.flushRegs(c,false);
279
SetJumpTarget(rLoopAddressExit);
280
SetJumpTarget(rLoopCounterExit);
285
//don't update g_dsp.pc -- the branch insn already did
287
if (opcode->uncond_branch)
291
else if (!opcode->jitFunc)
293
//look at g_dsp.pc if we actually branched
294
MOV(16, R(AX), M(&g_dsp.pc));
295
CMP(16, R(AX), Imm16(compilePC));
296
FixupBranch rNoBranch = J_CC(CC_Z, true);
298
DSPJitRegCache c(gpr);
299
//don't update g_dsp.pc -- the branch insn already did
301
if (!DSPHost_OnThread() && DSPAnalyzer::code_flags[start_addr] & DSPAnalyzer::CODE_IDLE_SKIP)
303
MOV(16, R(EAX), Imm16(DSP_IDLE_SKIP_CYCLES));
307
MOV(16, R(EAX), Imm16(blockSize[start_addr]));
309
JMP(returnDispatcher, true);
311
gpr.flushRegs(c,false);
313
SetJumpTarget(rNoBranch);
317
// End the block if we're before an idle skip address
318
if (DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_IDLE_SKIP)
326
MOV(16, M(&(g_dsp.pc)), Imm16(compilePC));
329
blocks[start_addr] = (DSPCompiledCode)entryPoint;
331
// Mark this block as a linkable destination if it does not contain
332
// any unresolved CALL's
333
if (unresolvedJumps[start_addr].empty())
335
blockLinks[start_addr] = blockLinkEntry;
337
for(u16 i = 0x0000; i < 0xffff; ++i)
339
if (!unresolvedJumps[i].empty())
341
// Check if there were any blocks waiting for this block to be linkable
342
size_t size = unresolvedJumps[i].size();
343
unresolvedJumps[i].remove(start_addr);
344
if (unresolvedJumps[i].size() < size)
346
// Mark the block to be recompiled again
347
blocks[i] = (DSPCompiledCode)stubEntryPoint;
355
if (blockSize[start_addr] == 0)
357
// just a safeguard, should never happen anymore.
358
// if it does we might get stuck over in RunForCycles.
359
ERROR_LOG(DSPLLE, "Block at 0x%04x has zero size", start_addr);
360
blockSize[start_addr] = 1;
364
if (!DSPHost_OnThread() && DSPAnalyzer::code_flags[start_addr] & DSPAnalyzer::CODE_IDLE_SKIP)
366
MOV(16, R(EAX), Imm16(DSP_IDLE_SKIP_CYCLES));
370
MOV(16, R(EAX), Imm16(blockSize[start_addr]));
372
JMP(returnDispatcher, true);
375
const u8 *DSPEmitter::CompileStub()
377
const u8 *entryPoint = AlignCode16();
378
ABI_CallFunction((void *)&CompileCurrent);
379
XOR(32, R(EAX), R(EAX)); // Return 0 cycles executed
380
JMP(returnDispatcher);
384
void DSPEmitter::CompileDispatcher()
386
enterDispatcher = AlignCode16();
387
ABI_PushAllCalleeSavedRegsAndAdjustStack();
389
const u8 *dispatcherLoop = GetCodePtr();
391
FixupBranch exceptionExit;
392
if (DSPHost_OnThread())
394
CMP(8, M(const_cast<bool*>(&g_dsp.external_interrupt_waiting)), Imm8(0));
395
exceptionExit = J_CC(CC_NE);
398
// Check for DSP halt
399
TEST(8, M(&g_dsp.cr), Imm8(CR_HALT));
400
FixupBranch _halt = J_CC(CC_NE);
403
// Execute block. Cycles executed returned in EAX.
405
MOVZX(32, 16, ECX, M(&g_dsp.pc));
406
MOV(32, R(EBX), ImmPtr(blocks));
407
JMPptr(MComplex(EBX, ECX, SCALE_4, 0));
409
MOVZX(64, 16, ECX, M(&g_dsp.pc));//for clarity, use 64 here.
410
MOV(64, R(RBX), ImmPtr(blocks));
411
JMPptr(MComplex(RBX, RCX, SCALE_8, 0));
414
returnDispatcher = GetCodePtr();
416
// Decrement cyclesLeft
417
SUB(16, M(&cyclesLeft), R(EAX));
419
J_CC(CC_A, dispatcherLoop);
421
// DSP gave up the remaining cycles.
422
SetJumpTarget(_halt);
423
if (DSPHost_OnThread())
425
SetJumpTarget(exceptionExit);
427
//MOV(32, M(&cyclesLeft), Imm32(0));
428
ABI_PopAllCalleeSavedRegsAndAdjustStack();