/[pcsx2_0.9.7]/trunk/pcsx2/x86/microVU_Compile.inl
ViewVC logotype

Contents of /trunk/pcsx2/x86/microVU_Compile.inl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 31 - (show annotations) (download)
Tue Sep 7 03:24:11 2010 UTC (9 years, 9 months ago) by william
File size: 16073 byte(s)
committing r3113 initial commit again...
1 /* PCSX2 - PS2 Emulator for PCs
2 * Copyright (C) 2002-2010 PCSX2 Dev Team
3 *
4 * PCSX2 is free software: you can redistribute it and/or modify it under the terms
5 * of the GNU Lesser General Public License as published by the Free Software Found-
6 * ation, either version 3 of the License, or (at your option) any later version.
7 *
8 * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
10 * PURPOSE. See the GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License along with PCSX2.
13 * If not, see <http://www.gnu.org/licenses/>.
14 */
15
16 #pragma once
17
18 //------------------------------------------------------------------
19 // Helper Macros
20 //------------------------------------------------------------------
21
22 #define calcCycles(reg, x) { reg = ((reg > x) ? (reg - x) : 0); }
23 #define optimizeReg(rState) { rState = (rState==1) ? 0 : rState; }
24 #define tCycles(dest, src) { dest = aMax(dest, src); }
25 #define incP() { mVU->p = (mVU->p+1) & 1; }
26 #define incQ() { mVU->q = (mVU->q+1) & 1; }
27 #define doUpperOp() { mVUopU(mVU, 1); mVUdivSet(mVU); }
28 #define doLowerOp() { incPC(-1); mVUopL(mVU, 1); incPC(1); }
29
30 //------------------------------------------------------------------
31 // Helper Functions
32 //------------------------------------------------------------------
33
34 // Used by mVUsetupRange
35 _f void mVUcheckIsSame(mV) {
36 if (mVU->prog.isSame == -1) {
37 mVU->prog.isSame = !memcmp_mmx((u8*)mVUcurProg.data, mVU->regs->Micro, mVU->microMemSize);
38 }
39 if (mVU->prog.isSame == 0) {
40 if (!isVU1) mVUcacheProg<0>(*mVU->prog.cur);
41 else mVUcacheProg<1>(*mVU->prog.cur);
42 mVU->prog.isSame = 1;
43 }
44 }
45
46 // Sets up microProgram PC ranges based on whats been recompiled
47 _f void mVUsetupRange(microVU* mVU, s32 pc, bool isStartPC) {
48 deque<microRange>*& ranges = mVUcurProg.ranges;
49 pc &= mVU->microMemSize - 8;
50
51 if (isStartPC) { // Check if startPC is already within a block we've recompiled
52 deque<microRange>::const_iterator it = ranges->begin();
53 for ( ; it != ranges->end(); it++) {
54 if ((pc >= it[0].start) && (pc <= it[0].end)) {
55 if (it[0].start != it[0].end)
56 return; // Last case makes sure its not a 1-opcode EvilBlock
57 }
58 }
59 }
60 elif (mVUrange.end != -1) return; // Above case was true
61
62 mVUcheckIsSame(mVU);
63
64 if (isStartPC) {
65 microRange mRange = {pc, -1};
66 ranges->push_front(mRange);
67 return;
68 }
69 if (mVUrange.start <= pc) {
70 mVUrange.end = pc;
71 bool mergedRange = 0;
72 s32 rStart = mVUrange.start;
73 s32 rEnd = mVUrange.end;
74 deque<microRange>::iterator it = ranges->begin();
75 for (it++; it != ranges->end(); it++) {
76 if((it[0].start >= rStart) && (it[0].start <= rEnd)) {
77 it[0].end = aMax(it[0].end, rEnd);
78 mergedRange = 1;
79 }
80 elif((it[0].end >= rStart) && (it[0].end <= rEnd)) {
81 it[0].start = aMin(it[0].start, rStart);
82 mergedRange = 1;
83 }
84 }
85 if (mergedRange) {
86 //DevCon.WriteLn(Color_Green, "microVU%d: Prog Range Merging", mVU->index);
87 ranges->erase(ranges->begin());
88 }
89 }
90 else {
91 //DevCon.WriteLn(Color_Green, "microVU%d: Prog Range Wrap [%04x] [%d]", mVU->index, mVUrange.start, mVUrange.end);
92 mVUrange.end = mVU->microMemSize;
93 microRange mRange = {0, pc};
94 ranges->push_front(mRange);
95 }
96 }
97
98 _f void startLoop(mV) {
99 if (curI & _Mbit_) { Console.WriteLn(Color_Green, "microVU%d: M-bit set!", getIndex); }
100 if (curI & _Dbit_) { DevCon.WriteLn (Color_Green, "microVU%d: D-bit set!", getIndex); }
101 if (curI & _Tbit_) { DevCon.WriteLn (Color_Green, "microVU%d: T-bit set!", getIndex); }
102 memzero(mVUinfo);
103 memzero(mVUregsTemp);
104 }
105
106 _f void doIbit(mV) {
107 if (mVUup.iBit) {
108 incPC(-1);
109 u32 tempI;
110 mVU->regAlloc->clearRegVF(33);
111
112 if (CHECK_VU_OVERFLOW && ((curI & 0x7fffffff) >= 0x7f800000)) {
113 Console.WriteLn(Color_Green,"microVU%d: Clamping I Reg", mVU->index);
114 tempI = (0x80000000 & curI) | 0x7f7fffff; // Clamp I Reg
115 }
116 else tempI = curI;
117
118 MOV32ItoM((uptr)&mVU->regs->VI[REG_I].UL, tempI);
119 incPC(1);
120 }
121 }
122
123 _f void doSwapOp(mV) {
124 if (mVUinfo.backupVF && !mVUlow.noWriteVF) {
125 DevCon.WriteLn(Color_Green, "microVU%d: Backing Up VF Reg [%04x]", getIndex, xPC);
126 int t1 = mVU->regAlloc->allocReg(mVUlow.VF_write.reg);
127 int t2 = mVU->regAlloc->allocReg();
128 SSE_MOVAPS_XMM_to_XMM(t2, t1);
129 mVU->regAlloc->clearNeeded(t1);
130 mVUopL(mVU, 1);
131 t1 = mVU->regAlloc->allocReg(mVUlow.VF_write.reg, mVUlow.VF_write.reg, 0xf, 0);
132 SSE_XORPS_XMM_to_XMM(t2, t1);
133 SSE_XORPS_XMM_to_XMM(t1, t2);
134 SSE_XORPS_XMM_to_XMM(t2, t1);
135 mVU->regAlloc->clearNeeded(t1);
136 incPC(1);
137 doUpperOp();
138 t1 = mVU->regAlloc->allocReg(-1, mVUlow.VF_write.reg, 0xf);
139 SSE_MOVAPS_XMM_to_XMM(t1, t2);
140 mVU->regAlloc->clearNeeded(t1);
141 mVU->regAlloc->clearNeeded(t2);
142 }
143 else { mVUopL(mVU, 1); incPC(1); doUpperOp(); }
144 }
145
146 _f void branchWarning(mV) {
147 incPC(-2);
148 if (mVUup.eBit && mVUbranch) {
149 incPC(2);
150 Console.Error("microVU%d Warning: Branch in E-bit delay slot! [%04x]", mVU->index, xPC);
151 mVUlow.isNOP = 1;
152 }
153 else incPC(2);
154 if (mVUinfo.isBdelay) { // Check if VI Reg Written to on Branch Delay Slot Instruction
155 if (mVUlow.VI_write.reg && mVUlow.VI_write.used && !mVUlow.readFlags) {
156 mVUlow.backupVI = 1;
157 mVUregs.viBackUp = mVUlow.VI_write.reg;
158 }
159 }
160 }
161
162 _f void eBitPass1(mV, int& branch) {
163 if (mVUregs.blockType != 1) {
164 branch = 1;
165 mVUup.eBit = 1;
166 }
167 }
168
169 _f void eBitWarning(mV) {
170 if (mVUpBlock->pState.blockType == 1) Console.Error("microVU%d Warning: Branch, E-bit, Branch! [%04x]", mVU->index, xPC);
171 if (mVUpBlock->pState.blockType == 2) Console.Error("microVU%d Warning: Branch, Branch, Branch! [%04x]", mVU->index, xPC);
172 incPC(2);
173 if (curI & _Ebit_) {
174 DevCon.Warning("microVU%d: E-bit in Branch delay slot! [%04x]", mVU->index, xPC);
175 mVUregs.blockType = 1;
176 }
177 incPC(-2);
178 }
179
180 // Optimizes the End Pipeline State Removing Unnecessary Info
181 _f void mVUoptimizePipeState(mV) {
182 for (int i = 0; i < 32; i++) {
183 optimizeReg(mVUregs.VF[i].x);
184 optimizeReg(mVUregs.VF[i].y);
185 optimizeReg(mVUregs.VF[i].z);
186 optimizeReg(mVUregs.VF[i].w);
187 }
188 for (int i = 0; i < 16; i++) {
189 optimizeReg(mVUregs.VI[i]);
190 }
191 if (mVUregs.q) { optimizeReg(mVUregs.q); if (!mVUregs.q) { incQ(); } }
192 if (mVUregs.p) { optimizeReg(mVUregs.p); if (!mVUregs.p) { incP(); } }
193 mVUregs.r = 0; // There are no stalls on the R-reg, so its Safe to discard info
194 }
195
196 _f void mVUincCycles(mV, int x) {
197 mVUcycles += x;
198 for (int z = 31; z > 0; z--) {
199 calcCycles(mVUregs.VF[z].x, x);
200 calcCycles(mVUregs.VF[z].y, x);
201 calcCycles(mVUregs.VF[z].z, x);
202 calcCycles(mVUregs.VF[z].w, x);
203 }
204 for (int z = 16; z > 0; z--) {
205 calcCycles(mVUregs.VI[z], x);
206 }
207 if (mVUregs.q) {
208 if (mVUregs.q > 4) { calcCycles(mVUregs.q, x); if (mVUregs.q <= 4) { mVUinfo.doDivFlag = 1; } }
209 else { calcCycles(mVUregs.q, x); }
210 if (!mVUregs.q) { incQ(); }
211 }
212 if (mVUregs.p) {
213 calcCycles(mVUregs.p, x);
214 if (!mVUregs.p || mVUregsTemp.p) { incP(); }
215 }
216 if (mVUregs.xgkick) {
217 calcCycles(mVUregs.xgkick, x);
218 if (!mVUregs.xgkick) { mVUinfo.doXGKICK = 1; }
219 }
220 calcCycles(mVUregs.r, x);
221 }
222
223 #define cmpVFregs(VFreg1, VFreg2, xVar) { \
224 if (VFreg1.reg == VFreg2.reg) { \
225 if ((VFreg1.x && VFreg2.x) \
226 || (VFreg1.y && VFreg2.y) \
227 || (VFreg1.z && VFreg2.z) \
228 || (VFreg1.w && VFreg2.w)) \
229 { xVar = 1; } \
230 } \
231 }
232
233 _f void mVUsetCycles(mV) {
234 mVUincCycles(mVU, mVUstall);
235 // If upper Op && lower Op write to same VF reg:
236 if ((mVUregsTemp.VFreg[0] == mVUregsTemp.VFreg[1]) && mVUregsTemp.VFreg[0]) {
237 if (mVUregsTemp.r || mVUregsTemp.VI) mVUlow.noWriteVF = 1;
238 else mVUlow.isNOP = 1; // If lower Op doesn't modify anything else, then make it a NOP
239 }
240 // If lower op reads a VF reg that upper Op writes to:
241 if ((mVUlow.VF_read[0].reg || mVUlow.VF_read[1].reg) && mVUup.VF_write.reg) {
242 cmpVFregs(mVUup.VF_write, mVUlow.VF_read[0], mVUinfo.swapOps);
243 cmpVFregs(mVUup.VF_write, mVUlow.VF_read[1], mVUinfo.swapOps);
244 }
245 // If above case is true, and upper op reads a VF reg that lower Op Writes to:
246 if (mVUinfo.swapOps && ((mVUup.VF_read[0].reg || mVUup.VF_read[1].reg) && mVUlow.VF_write.reg)) {
247 cmpVFregs(mVUlow.VF_write, mVUup.VF_read[0], mVUinfo.backupVF);
248 cmpVFregs(mVUlow.VF_write, mVUup.VF_read[1], mVUinfo.backupVF);
249 }
250
251 tCycles(mVUregs.VF[mVUregsTemp.VFreg[0]].x, mVUregsTemp.VF[0].x);
252 tCycles(mVUregs.VF[mVUregsTemp.VFreg[0]].y, mVUregsTemp.VF[0].y);
253 tCycles(mVUregs.VF[mVUregsTemp.VFreg[0]].z, mVUregsTemp.VF[0].z);
254 tCycles(mVUregs.VF[mVUregsTemp.VFreg[0]].w, mVUregsTemp.VF[0].w);
255
256 tCycles(mVUregs.VF[mVUregsTemp.VFreg[1]].x, mVUregsTemp.VF[1].x);
257 tCycles(mVUregs.VF[mVUregsTemp.VFreg[1]].y, mVUregsTemp.VF[1].y);
258 tCycles(mVUregs.VF[mVUregsTemp.VFreg[1]].z, mVUregsTemp.VF[1].z);
259 tCycles(mVUregs.VF[mVUregsTemp.VFreg[1]].w, mVUregsTemp.VF[1].w);
260
261 tCycles(mVUregs.VI[mVUregsTemp.VIreg], mVUregsTemp.VI);
262 tCycles(mVUregs.q, mVUregsTemp.q);
263 tCycles(mVUregs.p, mVUregsTemp.p);
264 tCycles(mVUregs.r, mVUregsTemp.r);
265 tCycles(mVUregs.xgkick, mVUregsTemp.xgkick);
266 }
267
268 void __fastcall mVUwarning0(mV) { Console.Error("microVU0 Warning: Exiting from Possible Infinite Loop [%04x] [%x]", xPC, mVU->prog.cur); }
269 void __fastcall mVUwarning1(mV) { Console.Error("microVU1 Warning: Exiting from Possible Infinite Loop [%04x] [%x]", xPC, mVU->prog.cur); }
270 void __fastcall mVUprintPC1(u32 PC) { Console.Write("Block PC [%04x] ", PC); }
271 void __fastcall mVUprintPC2(u32 PC) { Console.Write("[%04x]\n", PC); }
272
273 // vu0 is allowed to exit early, so are dev builds (for inf loops)
274 _f bool doEarlyExit(microVU* mVU) {
275 return IsDevBuild || !isVU1;
276 }
277
278 // Saves Pipeline State for resuming from early exits
279 _f void mVUsavePipelineState(microVU* mVU) {
280 u32* lpS = (u32*)&mVU->prog.lpState.vi15;
281 for (int i = 0; i < (sizeof(microRegInfo)-4)/4; i++, lpS++) {
282 MOV32ItoM((uptr)lpS, lpS[0]);
283 }
284 }
285
286 _f void mVUtestCycles(microVU* mVU) {
287 //u32* vu0jmp;
288 iPC = mVUstartPC;
289 mVUdebugNOW(0);
290 if (doEarlyExit(mVU)) {
291 CMP32ItoM((uptr)&mVU->cycles, 0);
292 u32* jmp32 = JG32(0);
293 //if (!isVU1) { TEST32ItoM((uptr)&mVU->regs->flags, VUFLAG_MFLAGSET); vu0jmp = JZ32(0); }
294 MOV32ItoR(gprT2, (uptr)mVU);
295 if (isVU1) CALLFunc((uptr)mVUwarning1);
296 //else CALLFunc((uptr)mVUwarning0); // VU0 is allowed early exit for COP2 Interlock Simulation
297 mVUsavePipelineState(mVU);
298 mVUendProgram(mVU, NULL, 0);
299 //if (!isVU1) x86SetJ32(vu0jmp);
300 x86SetJ32(jmp32);
301 }
302 SUB32ItoM((uptr)&mVU->cycles, mVUcycles);
303 }
304
305 // Initialize VI Constants (vi15 propagates through blocks)
306 _f void mVUinitConstValues(microVU* mVU) {
307 for (int i = 0; i < 16; i++) {
308 mVUconstReg[i].isValid = 0;
309 mVUconstReg[i].regValue = 0;
310 }
311 mVUconstReg[15].isValid = mVUregs.vi15 >> 31;
312 mVUconstReg[15].regValue = mVUconstReg[15].isValid ? (mVUregs.vi15&0xffff) : 0;
313 }
314
315 // Initialize Variables
316 _f void mVUinitFirstPass(microVU* mVU, uptr pState, u8* thisPtr) {
317 mVUstartPC = iPC; // Block Start PC
318 mVUbranch = 0; // Branch Type
319 mVUcount = 0; // Number of instructions ran
320 mVUcycles = 0; // Skips "M" phase, and starts counting cycles at "T" stage
321 mVU->p = 0; // All blocks start at p index #0
322 mVU->q = 0; // All blocks start at q index #0
323 if ((uptr)&mVUregs != pState) { // Loads up Pipeline State Info
324 memcpy_const((u8*)&mVUregs, (u8*)pState, sizeof(microRegInfo));
325 }
326 if (doEarlyExit(mVU) && ((uptr)&mVU->prog.lpState != pState)) {
327 memcpy_const((u8*)&mVU->prog.lpState, (u8*)pState, sizeof(microRegInfo));
328 }
329 mVUblock.x86ptrStart = thisPtr;
330 mVUpBlock = mVUblocks[mVUstartPC/2]->add(&mVUblock); // Add this block to block manager
331 mVUregs.blockType = 0;
332 mVUregs.viBackUp = 0;
333 mVUregs.flags = 0;
334 mVUregs.needExactMatch = 0;
335 mVUsFlagHack = CHECK_VU_FLAGHACK;
336 mVUinitConstValues(mVU);
337 }
338
339 //------------------------------------------------------------------
340 // Recompiler
341 //------------------------------------------------------------------
342
343 _r void* mVUcompile(microVU* mVU, u32 startPC, uptr pState) {
344
345 microFlagCycles mFC;
346 u8* thisPtr = x86Ptr;
347 const u32 endCount = (((microRegInfo*)pState)->blockType) ? 1 : (mVU->microMemSize / 8);
348
349 // First Pass
350 iPC = startPC / 4;
351 mVUsetupRange(mVU, startPC, 1); // Setup Program Bounds/Range
352 mVU->regAlloc->reset(); // Reset regAlloc
353 mVUinitFirstPass(mVU, pState, thisPtr);
354 for (int branch = 0; mVUcount < endCount; mVUcount++) {
355 incPC(1);
356 startLoop(mVU);
357 mVUincCycles(mVU, 1);
358 mVUopU(mVU, 0);
359 if (curI & _Ebit_) { eBitPass1(mVU, branch); }
360 if (curI & _DTbit_) { branch = 4; }
361 if (curI & _Mbit_) { mVUup.mBit = 1; }
362 if (curI & _Ibit_) { mVUlow.isNOP = 1; mVUup.iBit = 1; }
363 else { incPC(-1); mVUopL(mVU, 0); incPC(1); }
364 mVUsetCycles(mVU);
365 mVUinfo.readQ = mVU->q;
366 mVUinfo.writeQ = !mVU->q;
367 mVUinfo.readP = mVU->p;
368 mVUinfo.writeP = !mVU->p;
369 if (branch >= 2) { mVUinfo.isEOB = 1; if (branch == 3) { mVUinfo.isBdelay = 1; } mVUcount++; branchWarning(mVU); break; }
370 else if (branch == 1) { branch = 2; }
371 if (mVUbranch) { mVUsetFlagInfo(mVU); eBitWarning(mVU); branch = 3; mVUbranch = 0; }
372 incPC(1);
373 }
374
375 // Fix up vi15 const info for propagation through blocks
376 mVUregs.vi15 = (mVUconstReg[15].isValid && CHECK_VU_CONSTPROP) ? ((1<<31) | (mVUconstReg[15].regValue&0xffff)) : 0;
377
378 mVUsetFlags(mVU, mFC); // Sets Up Flag instances
379 mVUoptimizePipeState(mVU); // Optimize the End Pipeline State for nicer Block Linking
380 mVUtestCycles(mVU); // Update VU Cycles and Exit Early if Necessary
381
382 // Second Pass
383 iPC = mVUstartPC;
384 setCode();
385 mVUbranch = 0;
386 u32 x = 0;
387 for (; x < endCount; x++) {
388 if (mVUinfo.isEOB) { x = 0xffff; }
389 if (mVUup.mBit) { OR32ItoM((uptr)&mVU->regs->flags, VUFLAG_MFLAGSET); }
390 if (mVUlow.isNOP) { incPC(1); doUpperOp(); doIbit(mVU); }
391 else if (!mVUinfo.swapOps) { incPC(1); doUpperOp(); doLowerOp(); }
392 else { doSwapOp(mVU); }
393 if (mVUinfo.doXGKICK) { mVU_XGKICK_DELAY(mVU, 1); }
394 if (!doRegAlloc) { mVU->regAlloc->flushAll(); }
395 if (isEvilBlock) { mVUsetupRange(mVU, xPC, 0); normJumpCompile(mVU, mFC, 1); return thisPtr; }
396 else if (!mVUinfo.isBdelay) { incPC(1); }
397 else {
398 mVUsetupRange(mVU, xPC, 0);
399 mVUdebugNOW(1);
400 incPC(-3); // Go back to branch opcode
401 switch (mVUlow.branch) {
402 case 1: case 2: normBranch(mVU, mFC); return thisPtr; // B/BAL
403 case 9: case 10: normJump (mVU, mFC); return thisPtr; // JR/JALR
404 case 3: condBranch(mVU, mFC, Jcc_Equal); return thisPtr; // IBEQ
405 case 4: condBranch(mVU, mFC, Jcc_GreaterOrEqual); return thisPtr; // IBGEZ
406 case 5: condBranch(mVU, mFC, Jcc_Greater); return thisPtr; // IBGTZ
407 case 6: condBranch(mVU, mFC, Jcc_LessOrEqual); return thisPtr; // IBLEQ
408 case 7: condBranch(mVU, mFC, Jcc_Less); return thisPtr; // IBLTZ
409 case 8: condBranch(mVU, mFC, Jcc_NotEqual); return thisPtr; // IBNEQ
410 }
411 }
412 }
413 if ((x == endCount) && (x!=1)) { Console.Error("microVU%d: Possible infinite compiling loop!", mVU->index); }
414
415 // E-bit End
416 mVUsetupRange(mVU, xPC-8, 0);
417 mVUendProgram(mVU, &mFC, 1);
418 return thisPtr;
419 }
420
421 // Returns the entry point of the block (compiles it if not found)
422 _f void* mVUentryGet(microVU* mVU, microBlockManager* block, u32 startPC, uptr pState) {
423 microBlock* pBlock = block->search((microRegInfo*)pState);
424 if (pBlock) return pBlock->x86ptrStart;
425 else return mVUcompile(mVU, startPC, pState);
426 }
427
428 // Search for Existing Compiled Block (if found, return x86ptr; else, compile and return x86ptr)
429 _f void* mVUblockFetch(microVU* mVU, u32 startPC, uptr pState) {
430
431 if (startPC > mVU->microMemSize-8) { DevCon.Error("microVU%d: invalid startPC [%04x]", mVU->index, startPC); }
432 startPC &= mVU->microMemSize-8;
433
434 blockCreate(startPC/8);
435 return mVUentryGet(mVU, mVUblocks[startPC/8], startPC, pState);
436 }
437
438 // mVUcompileJIT() - Called By JR/JALR during execution
439 _mVUt void* __fastcall mVUcompileJIT(u32 startPC, uptr pState) {
440 //return mVUblockFetch(mVUx, startPC, pState);
441 return mVUsearchProg<vuIndex>(startPC, pState); // Find and set correct program
442 }

  ViewVC Help
Powered by ViewVC 1.1.22