~registry/dolphin-emu/triforce

« back to all changes in this revision

Viewing changes to Source/Core/Core/Src/DSP/DSPEmitter.cpp

  • Committer: Sérgio Benjamim
  • Date: 2015-02-13 05:54:40 UTC
  • Revision ID: sergio_br2@yahoo.com.br-20150213055440-ey2rt3sjpy27km78
Dolphin Triforce branch from code.google, commit b957980 (4.0-315).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2013 Dolphin Emulator Project
 
2
// Licensed under GPLv2
 
3
// Refer to the license.txt file included.
 
4
 
 
5
#include <cstring>
 
6
 
 
7
#include "DSPEmitter.h"
 
8
#include "DSPMemoryMap.h"
 
9
#include "DSPCore.h"
 
10
#include "DSPHost.h"
 
11
#include "DSPInterpreter.h"
 
12
#include "DSPAnalyzer.h"
 
13
 
 
14
#define MAX_BLOCK_SIZE 250
 
15
#define DSP_IDLE_SKIP_CYCLES 0x1000
 
16
 
 
17
using namespace Gen;
 
18
 
 
19
DSPEmitter::DSPEmitter() : gpr(*this), storeIndex(-1), storeIndex2(-1)
 
20
{
 
21
        m_compiledCode = NULL;
 
22
 
 
23
        AllocCodeSpace(COMPILED_CODE_SIZE);
 
24
 
 
25
        blocks = new DSPCompiledCode[MAX_BLOCKS];
 
26
        blockLinks = new Block[MAX_BLOCKS];
 
27
        blockSize = new u16[MAX_BLOCKS];
 
28
        
 
29
        compileSR = 0;
 
30
        compileSR |= SR_INT_ENABLE;
 
31
        compileSR |= SR_EXT_INT_ENABLE;
 
32
 
 
33
        CompileDispatcher();
 
34
        stubEntryPoint = CompileStub();
 
35
 
 
36
        //clear all of the block references
 
37
        for(int i = 0x0000; i < MAX_BLOCKS; i++)
 
38
        {
 
39
                blocks[i] = (DSPCompiledCode)stubEntryPoint;
 
40
                blockLinks[i] = 0;
 
41
                blockSize[i] = 0;
 
42
        }
 
43
}
 
44
 
 
45
DSPEmitter::~DSPEmitter() 
 
46
{
 
47
        delete[] blocks;
 
48
        delete[] blockLinks;
 
49
        delete[] blockSize;
 
50
        FreeCodeSpace();
 
51
}
 
52
 
 
53
void DSPEmitter::ClearIRAM()
 
54
{
 
55
        for(int i = 0x0000; i < 0x1000; i++)
 
56
        {
 
57
                blocks[i] = (DSPCompiledCode)stubEntryPoint;
 
58
                blockLinks[i] = 0;
 
59
                blockSize[i] = 0;
 
60
                unresolvedJumps[i].clear();
 
61
        }
 
62
        g_dsp.reset_dspjit_codespace = true;
 
63
}
 
64
 
 
65
void DSPEmitter::ClearIRAMandDSPJITCodespaceReset() 
 
66
{
 
67
        ClearCodeSpace();
 
68
        CompileDispatcher();
 
69
        stubEntryPoint = CompileStub();
 
70
 
 
71
        for(int i = 0x0000; i < 0x10000; i++)
 
72
        {
 
73
                blocks[i] = (DSPCompiledCode)stubEntryPoint;
 
74
                blockLinks[i] = 0;
 
75
                blockSize[i] = 0;
 
76
                unresolvedJumps[i].clear();
 
77
        }
 
78
        g_dsp.reset_dspjit_codespace = false;
 
79
}
 
80
 
 
81
 
 
82
// Must go out of block if exception is detected
 
83
void DSPEmitter::checkExceptions(u32 retval)
 
84
{
 
85
        // Check for interrupts and exceptions
 
86
        TEST(8, M(&g_dsp.exceptions), Imm8(0xff));
 
87
        FixupBranch skipCheck = J_CC(CC_Z, true);
 
88
 
 
89
        MOV(16, M(&(g_dsp.pc)), Imm16(compilePC));
 
90
 
 
91
        DSPJitRegCache c(gpr);
 
92
        gpr.saveRegs();
 
93
        ABI_CallFunction((void *)&DSPCore_CheckExceptions);
 
94
        MOV(32, R(EAX), Imm32(retval));
 
95
        JMP(returnDispatcher, true);
 
96
        gpr.loadRegs(false);
 
97
        gpr.flushRegs(c,false);
 
98
 
 
99
        SetJumpTarget(skipCheck);
 
100
}
 
101
 
 
102
bool DSPEmitter::FlagsNeeded()
 
103
{
 
104
        if (!(DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_START_OF_INST) || 
 
105
                (DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_UPDATE_SR))     
 
106
                return true; 
 
107
        else
 
108
                return false;
 
109
}
 
110
 
 
111
void DSPEmitter::Default(UDSPInstruction inst)
 
112
{
 
113
        if (opTable[inst]->reads_pc)
 
114
        {
 
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
 
117
 
 
118
                MOV(16, M(&(g_dsp.pc)), Imm16(compilePC + 1));
 
119
        }
 
120
 
 
121
        // Fall back to interpreter
 
122
        gpr.pushRegs();
 
123
        _assert_msg_(DSPLLE, opTable[inst]->intFunc, "No function for %04x",inst);
 
124
        ABI_CallFunctionC16((void*)opTable[inst]->intFunc, inst);
 
125
        gpr.popRegs();
 
126
}
 
127
 
 
128
void DSPEmitter::EmitInstruction(UDSPInstruction inst)
 
129
{
 
130
        const DSPOPCTemplate *tinst = GetOpTemplate(inst);
 
131
        bool ext_is_jit = false;
 
132
 
 
133
        // Call extended
 
134
        if (tinst->extended)
 
135
        {
 
136
                if ((inst >> 12) == 0x3)
 
137
                {
 
138
                        if (! extOpTable[inst & 0x7F]->jitFunc)
 
139
                        {
 
140
                                // Fall back to interpreter
 
141
                                gpr.pushRegs();
 
142
                                ABI_CallFunctionC16((void*)extOpTable[inst & 0x7F]->intFunc, inst);
 
143
                                gpr.popRegs();
 
144
                                INFO_LOG(DSPLLE, "Instruction not JITed(ext part): %04x\n", inst);
 
145
                                ext_is_jit = false;
 
146
                        }
 
147
                        else
 
148
                        {
 
149
                                (this->*extOpTable[inst & 0x7F]->jitFunc)(inst);
 
150
                                ext_is_jit = true;
 
151
                        }
 
152
                }
 
153
                else
 
154
                {
 
155
                        if (!extOpTable[inst & 0xFF]->jitFunc)
 
156
                        {
 
157
                                // Fall back to interpreter
 
158
                                gpr.pushRegs();
 
159
                                ABI_CallFunctionC16((void*)extOpTable[inst & 0xFF]->intFunc, inst);
 
160
                                gpr.popRegs();
 
161
                                INFO_LOG(DSPLLE, "Instruction not JITed(ext part): %04x\n", inst);
 
162
                                ext_is_jit = false;
 
163
                        }
 
164
                        else
 
165
                        {
 
166
                                (this->*extOpTable[inst & 0xFF]->jitFunc)(inst);
 
167
                                ext_is_jit = true;
 
168
                        }
 
169
                }
 
170
        }
 
171
        
 
172
        // Main instruction
 
173
        if (!opTable[inst]->jitFunc)
 
174
        {
 
175
                Default(inst);
 
176
                INFO_LOG(DSPLLE, "Instruction not JITed(main part): %04x\n", inst);
 
177
        }
 
178
        else
 
179
        {
 
180
                (this->*opTable[inst]->jitFunc)(inst);
 
181
        }
 
182
 
 
183
        // Backlog
 
184
        if (tinst->extended)
 
185
        {
 
186
                if (!ext_is_jit)
 
187
                {
 
188
                        //need to call the online cleanup function because
 
189
                        //the writeBackLog gets populated at runtime
 
190
                        gpr.pushRegs();
 
191
                        ABI_CallFunction((void*)::applyWriteBackLog);
 
192
                        gpr.popRegs();
 
193
                }
 
194
                else
 
195
                {
 
196
                        popExtValueToReg();
 
197
                }
 
198
        }
 
199
}
 
200
 
 
201
void DSPEmitter::Compile(u16 start_addr)
 
202
{
 
203
        // Remember the current block address for later
 
204
        startAddr = start_addr;
 
205
        unresolvedJumps[start_addr].clear();
 
206
 
 
207
        const u8 *entryPoint = AlignCode16();
 
208
 
 
209
        /*
 
210
        // Check for other exceptions
 
211
        if (dsp_SR_is_flag_set(SR_INT_ENABLE))
 
212
                return;
 
213
 
 
214
        if (g_dsp.exceptions == 0)
 
215
                return;
 
216
        */
 
217
 
 
218
        gpr.loadRegs();
 
219
 
 
220
        blockLinkEntry = GetCodePtr();
 
221
 
 
222
        compilePC = start_addr;
 
223
        bool fixup_pc = false;
 
224
        blockSize[start_addr] = 0;
 
225
 
 
226
        while (compilePC < start_addr + MAX_BLOCK_SIZE)
 
227
        {
 
228
                if (DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_CHECK_INT)
 
229
                        checkExceptions(blockSize[start_addr]);
 
230
 
 
231
                UDSPInstruction inst = dsp_imem_read(compilePC);
 
232
                const DSPOPCTemplate *opcode = GetOpTemplate(inst);
 
233
 
 
234
                EmitInstruction(inst);
 
235
 
 
236
                blockSize[start_addr]++;
 
237
                compilePC += opcode->size;
 
238
 
 
239
                // If the block was trying to link into itself, remove the link
 
240
                unresolvedJumps[start_addr].remove(compilePC);
 
241
 
 
242
                fixup_pc = true;
 
243
 
 
244
                // Handle loop condition, only if current instruction was flagged as a loop destination
 
245
                // by the analyzer.
 
246
                if (DSPAnalyzer::code_flags[compilePC-1] & DSPAnalyzer::CODE_LOOP_END)
 
247
                {
 
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);
 
251
 
 
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);
 
255
 
 
256
                        if (!opcode->branch)
 
257
                        {
 
258
                                //branch insns update the g_dsp.pc
 
259
                                MOV(16, M(&(g_dsp.pc)), Imm16(compilePC));
 
260
                        }
 
261
 
 
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);
 
265
                        HandleLoop();
 
266
                        gpr.saveRegs();
 
267
                        if (!DSPHost_OnThread() && DSPAnalyzer::code_flags[start_addr] & DSPAnalyzer::CODE_IDLE_SKIP)
 
268
                        {
 
269
                                MOV(16, R(EAX), Imm16(DSP_IDLE_SKIP_CYCLES));
 
270
                        }
 
271
                        else
 
272
                        {
 
273
                                MOV(16, R(EAX), Imm16(blockSize[start_addr]));
 
274
                        }
 
275
                        JMP(returnDispatcher, true);
 
276
                        gpr.loadRegs(false);
 
277
                        gpr.flushRegs(c,false);
 
278
 
 
279
                        SetJumpTarget(rLoopAddressExit);
 
280
                        SetJumpTarget(rLoopCounterExit);
 
281
                }
 
282
 
 
283
                if (opcode->branch)
 
284
                {
 
285
                        //don't update g_dsp.pc -- the branch insn already did
 
286
                        fixup_pc = false;
 
287
                        if (opcode->uncond_branch)
 
288
                        {
 
289
                                break;
 
290
                        }
 
291
                        else if (!opcode->jitFunc)
 
292
                        {
 
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);
 
297
 
 
298
                                DSPJitRegCache c(gpr);
 
299
                                //don't update g_dsp.pc -- the branch insn already did
 
300
                                gpr.saveRegs();
 
301
                                if (!DSPHost_OnThread() && DSPAnalyzer::code_flags[start_addr] & DSPAnalyzer::CODE_IDLE_SKIP)
 
302
                                {
 
303
                                        MOV(16, R(EAX), Imm16(DSP_IDLE_SKIP_CYCLES));
 
304
                                }
 
305
                                else
 
306
                                {
 
307
                                        MOV(16, R(EAX), Imm16(blockSize[start_addr]));
 
308
                                }
 
309
                                JMP(returnDispatcher, true);
 
310
                                gpr.loadRegs(false);
 
311
                                gpr.flushRegs(c,false);
 
312
 
 
313
                                SetJumpTarget(rNoBranch);
 
314
                        }
 
315
                }
 
316
 
 
317
                // End the block if we're before an idle skip address
 
318
                if (DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_IDLE_SKIP)
 
319
                {
 
320
                        break;
 
321
                }
 
322
        }
 
323
 
 
324
        if (fixup_pc)
 
325
        {
 
326
                MOV(16, M(&(g_dsp.pc)), Imm16(compilePC));
 
327
        }
 
328
 
 
329
        blocks[start_addr] = (DSPCompiledCode)entryPoint;
 
330
 
 
331
        // Mark this block as a linkable destination if it does not contain
 
332
        // any unresolved CALL's
 
333
        if (unresolvedJumps[start_addr].empty())
 
334
        {
 
335
                blockLinks[start_addr] = blockLinkEntry;
 
336
 
 
337
                for(u16 i = 0x0000; i < 0xffff; ++i)
 
338
                {
 
339
                        if (!unresolvedJumps[i].empty())
 
340
                        {
 
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)
 
345
                                {
 
346
                                        // Mark the block to be recompiled again
 
347
                                        blocks[i] = (DSPCompiledCode)stubEntryPoint;
 
348
                                        blockLinks[i] = 0;
 
349
                                        blockSize[i] = 0;
 
350
                                }
 
351
                        }
 
352
                }
 
353
        }
 
354
 
 
355
        if (blockSize[start_addr] == 0) 
 
356
        {
 
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;
 
361
        }
 
362
 
 
363
        gpr.saveRegs();
 
364
        if (!DSPHost_OnThread() && DSPAnalyzer::code_flags[start_addr] & DSPAnalyzer::CODE_IDLE_SKIP)
 
365
        {
 
366
                MOV(16, R(EAX), Imm16(DSP_IDLE_SKIP_CYCLES));
 
367
        }
 
368
        else
 
369
        {
 
370
                MOV(16, R(EAX), Imm16(blockSize[start_addr]));
 
371
        }
 
372
        JMP(returnDispatcher, true);
 
373
}
 
374
 
 
375
const u8 *DSPEmitter::CompileStub()
 
376
{
 
377
        const u8 *entryPoint = AlignCode16();
 
378
        ABI_CallFunction((void *)&CompileCurrent);
 
379
        XOR(32, R(EAX), R(EAX)); // Return 0 cycles executed
 
380
        JMP(returnDispatcher);
 
381
        return entryPoint;
 
382
}
 
383
 
 
384
void DSPEmitter::CompileDispatcher()
 
385
{
 
386
        enterDispatcher = AlignCode16();
 
387
        ABI_PushAllCalleeSavedRegsAndAdjustStack();
 
388
 
 
389
        const u8 *dispatcherLoop = GetCodePtr();
 
390
 
 
391
        FixupBranch exceptionExit;
 
392
        if (DSPHost_OnThread())
 
393
        {
 
394
                CMP(8, M(const_cast<bool*>(&g_dsp.external_interrupt_waiting)), Imm8(0));
 
395
                exceptionExit = J_CC(CC_NE);
 
396
        }
 
397
 
 
398
        // Check for DSP halt
 
399
        TEST(8, M(&g_dsp.cr), Imm8(CR_HALT));
 
400
        FixupBranch _halt = J_CC(CC_NE);
 
401
 
 
402
 
 
403
        // Execute block. Cycles executed returned in EAX.
 
404
#ifdef _M_IX86
 
405
        MOVZX(32, 16, ECX, M(&g_dsp.pc));
 
406
        MOV(32, R(EBX), ImmPtr(blocks));
 
407
        JMPptr(MComplex(EBX, ECX, SCALE_4, 0));
 
408
#else
 
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));
 
412
#endif
 
413
 
 
414
        returnDispatcher = GetCodePtr();
 
415
 
 
416
        // Decrement cyclesLeft
 
417
        SUB(16, M(&cyclesLeft), R(EAX));
 
418
 
 
419
        J_CC(CC_A, dispatcherLoop);
 
420
 
 
421
        // DSP gave up the remaining cycles.
 
422
        SetJumpTarget(_halt);
 
423
        if (DSPHost_OnThread())
 
424
        {
 
425
                SetJumpTarget(exceptionExit);
 
426
        }
 
427
        //MOV(32, M(&cyclesLeft), Imm32(0));
 
428
        ABI_PopAllCalleeSavedRegsAndAdjustStack();
 
429
        RET();
 
430
}