/[pcsx2_0.9.7]/trunk/pcsx2/R5900.cpp
ViewVC logotype

Contents of /trunk/pcsx2/R5900.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 401 - (show annotations) (download)
Fri Feb 25 17:31:09 2011 UTC (9 years, 4 months ago) by william
File size: 16929 byte(s)
Auto Commited Import of: pcsx2-0.9.7-DEBUG (upstream: v0.9.7.4358 local: v0.9.7.313-latest) in ./trunk
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
17 #include "PrecompiledHeader.h"
18 #include "Common.h"
19
20 #include "R5900.h"
21 #include "R3000A.h"
22 #include "VUmicro.h"
23 #include "COP0.h"
24
25 #include "System/SysThreads.h"
26 #include "R5900Exceptions.h"
27
28 #include "Hardware.h"
29 #include "IPU/IPUdma.h"
30
31 #include "Elfheader.h"
32 #include "CDVD/CDVD.h"
33 #include "Patch.h"
34 #include "GameDatabase.h"
35
36 using namespace R5900; // for R5900 disasm tools
37
38 s32 EEsCycle; // used to sync the IOP to the EE
39 u32 EEoCycle;
40
41 __aligned16 cpuRegisters cpuRegs;
42 __aligned16 fpuRegisters fpuRegs;
43 __aligned16 tlbs tlb[48];
44 R5900cpu *Cpu = NULL;
45
46 bool g_SkipBiosHack; // set at boot if the skip bios hack is on, reset before the game has started
47 bool g_GameStarted; // set when we reach the game's entry point or earlier if the entry point cannot be determined
48
49 static const uint eeWaitCycles = 3072;
50
51 bool eeEventTestIsActive = false;
52
53 extern SysMainMemory& GetVmMemory();
54
55 void cpuReset()
56 {
57 if (GetMTGS().IsOpen())
58 GetMTGS().WaitGS(); // GS better be done processing before we reset the EE, just in case.
59
60 GetVmMemory().ResetAll();
61
62 memzero(cpuRegs);
63 memzero(fpuRegs);
64 memzero(tlb);
65
66 cpuRegs.pc = 0xbfc00000; //set pc reg to stack
67 cpuRegs.CP0.n.Config = 0x440;
68 cpuRegs.CP0.n.Status.val= 0x70400004; //0x10900000 <-- wrong; // COP0 enabled | BEV = 1 | TS = 1
69 cpuRegs.CP0.n.PRid = 0x00002e20; // PRevID = Revision ID, same as R5900
70 fpuRegs.fprc[0] = 0x00002e00; // fpu Revision..
71 fpuRegs.fprc[31] = 0x01000001; // fpu Status/Control
72
73 g_nextEventCycle = cpuRegs.cycle + 4;
74 EEsCycle = 0;
75 EEoCycle = cpuRegs.cycle;
76
77 hwReset();
78 rcntInit();
79 psxReset();
80
81 extern void Deci2Reset(); // lazy, no good header for it yet.
82 Deci2Reset();
83
84 g_GameStarted = false;
85 g_SkipBiosHack = EmuConfig.UseBOOT2Injection;
86
87 ElfCRC = 0;
88 DiscSerial = L"";
89 ElfEntry = -1;
90
91 // FIXME: LastELF should be reset on media changes as well as on CPU resets, in
92 // the very unlikely case that a user swaps to another media source that "looks"
93 // the same (identical ELF names) but is actually different (devs actually could
94 // run into this while testing minor binary hacked changes to ISO images, which
95 // is why I found out about this) --air
96 LastELF = L"";
97 }
98
99 void cpuShutdown()
100 {
101 hwShutdown();
102 }
103
104 __ri void cpuException(u32 code, u32 bd)
105 {
106 bool errLevel2, checkStatus;
107 u32 offset;
108
109 cpuRegs.branch = 0; // Tells the interpreter that an exception occurred during a branch.
110 cpuRegs.CP0.n.Cause = code & 0xffff;
111
112 if(cpuRegs.CP0.n.Status.b.ERL == 0)
113 {
114 //Error Level 0-1
115 errLevel2 = FALSE;
116 checkStatus = (cpuRegs.CP0.n.Status.b.BEV == 0); // for TLB/general exceptions
117
118 if (((code & 0x7C) >= 0x8) && ((code & 0x7C) <= 0xC))
119 offset = 0x0; //TLB Refill
120 else if ((code & 0x7C) == 0x0)
121 offset = 0x200; //Interrupt
122 else
123 offset = 0x180; // Everything else
124 }
125 else
126 {
127 //Error Level 2
128 errLevel2 = TRUE;
129 checkStatus = (cpuRegs.CP0.n.Status.b.DEV == 0); // for perf/debug exceptions
130
131 Console.Error("*PCSX2* FIX ME: Level 2 cpuException");
132 if ((code & 0x38000) <= 0x8000 )
133 {
134 //Reset / NMI
135 cpuRegs.pc = 0xBFC00000;
136 Console.Warning("Reset request");
137 cpuUpdateOperationMode();
138 return;
139 }
140 else if((code & 0x38000) == 0x10000)
141 offset = 0x80; //Performance Counter
142 else if((code & 0x38000) == 0x18000)
143 offset = 0x100; //Debug
144 else
145 Console.Error("Unknown Level 2 Exception!! Cause %x", code);
146 }
147
148 if (cpuRegs.CP0.n.Status.b.EXL == 0)
149 {
150 cpuRegs.CP0.n.Status.b.EXL = 1;
151 if (bd)
152 {
153 Console.Warning("branch delay!!");
154 cpuRegs.CP0.n.EPC = cpuRegs.pc - 4;
155 cpuRegs.CP0.n.Cause |= 0x80000000;
156 }
157 else
158 {
159 cpuRegs.CP0.n.EPC = cpuRegs.pc;
160 cpuRegs.CP0.n.Cause &= ~0x80000000;
161 }
162 }
163 else
164 {
165 offset = 0x180; //Override the cause
166 if (errLevel2) Console.Warning("cpuException: Status.EXL = 1 cause %x", code);
167 }
168
169 if (checkStatus)
170 cpuRegs.pc = 0x80000000 + offset;
171 else
172 cpuRegs.pc = 0xBFC00200 + offset;
173
174 cpuUpdateOperationMode();
175 }
176
177 void cpuTlbMiss(u32 addr, u32 bd, u32 excode)
178 {
179 Console.Error("cpuTlbMiss pc:%x, cycl:%x, addr: %x, status=%x, code=%x",
180 cpuRegs.pc, cpuRegs.cycle, addr, cpuRegs.CP0.n.Status.val, excode);
181
182 if (bd) Console.Warning("branch delay!!");
183
184 pxFail( "TLB Miss handler is uninished code." ); // temporary
185
186 cpuRegs.CP0.n.BadVAddr = addr;
187 cpuRegs.CP0.n.Context &= 0xFF80000F;
188 cpuRegs.CP0.n.Context |= (addr >> 9) & 0x007FFFF0;
189 cpuRegs.CP0.n.EntryHi = (addr & 0xFFFFE000) | (cpuRegs.CP0.n.EntryHi & 0x1FFF);
190
191 cpuRegs.CP0.n.Cause = excode;
192 if (!(cpuRegs.CP0.n.Status.val & 0x2)) { // EXL bit
193 cpuRegs.CP0.n.EPC = cpuRegs.pc - 4;
194 }
195
196 if (!cpuRegs.CP0.n.Status.b.IE) {
197 cpuRegs.pc = 0x80000000;
198 } else {
199 cpuRegs.pc = 0x80000180;
200 }
201
202 cpuRegs.CP0.n.Status.b.EXL = 1;
203 cpuUpdateOperationMode();
204 // Log=1; varLog|= 0x40000000;
205 }
206
207 void cpuTlbMissR(u32 addr, u32 bd) {
208 cpuTlbMiss(addr, bd, EXC_CODE_TLBL);
209 }
210
211 void cpuTlbMissW(u32 addr, u32 bd) {
212 cpuTlbMiss(addr, bd, EXC_CODE_TLBS);
213 }
214
215 // sets a branch test to occur some time from an arbitrary starting point.
216 __fi void cpuSetNextEvent( u32 startCycle, s32 delta )
217 {
218 // typecast the conditional to signed so that things don't blow up
219 // if startCycle is greater than our next branch cycle.
220
221 if( (int)(g_nextEventCycle - startCycle) > delta )
222 {
223 g_nextEventCycle = startCycle + delta;
224 }
225 }
226
227 // sets a branch to occur some time from the current cycle
228 __fi void cpuSetNextEventDelta( s32 delta )
229 {
230 cpuSetNextEvent( cpuRegs.cycle, delta );
231 }
232
233 // tests the cpu cycle against the given start and delta values.
234 // Returns true if the delta time has passed.
235 __fi int cpuTestCycle( u32 startCycle, s32 delta )
236 {
237 // typecast the conditional to signed so that things don't explode
238 // if the startCycle is ahead of our current cpu cycle.
239
240 return (int)(cpuRegs.cycle - startCycle) >= delta;
241 }
242
243 // tells the EE to run the branch test the next time it gets a chance.
244 __fi void cpuSetEvent()
245 {
246 g_nextEventCycle = cpuRegs.cycle;
247 }
248
249 __fi void cpuClearInt( uint i )
250 {
251 jASSUME( i < 32 );
252 cpuRegs.interrupt &= ~(1 << i);
253 }
254
255 static __fi void TESTINT( u8 n, void (*callback)() )
256 {
257 if( !(cpuRegs.interrupt & (1 << n)) ) return;
258
259 if( cpuTestCycle( cpuRegs.sCycle[n], cpuRegs.eCycle[n] ) )
260 {
261 cpuClearInt( n );
262 callback();
263 }
264 else
265 cpuSetNextEvent( cpuRegs.sCycle[n], cpuRegs.eCycle[n] );
266 }
267
268 // [TODO] move this function to LegacyDmac.cpp, and remove most of the DMAC-related headers from
269 // being included into R5900.cpp.
270 static __fi void _cpuTestInterrupts()
271 {
272 if (!dmacRegs.ctrl.DMAE || (psHu8(DMAC_ENABLER+2) & 1))
273 {
274 //Console.Write("DMAC Disabled or suspended");
275 return;
276 }
277 /* These are 'pcsx2 interrupts', they handle asynchronous stuff
278 that depends on the cycle timings */
279
280 TESTINT(DMAC_VIF1, vif1Interrupt);
281 TESTINT(DMAC_GIF, gsInterrupt);
282 TESTINT(DMAC_SIF0, EEsif0Interrupt);
283 TESTINT(DMAC_SIF1, EEsif1Interrupt);
284
285 // Profile-guided Optimization (sorta)
286 // The following ints are rarely called. Encasing them in a conditional
287 // as follows helps speed up most games.
288
289 if( cpuRegs.interrupt & 0xF19 ) // Bits 0 3 4 8 9 10 11 ( 111100011001 )
290 {
291 TESTINT(DMAC_VIF0, vif0Interrupt);
292
293 TESTINT(DMAC_FROM_IPU, ipu0Interrupt);
294 TESTINT(DMAC_TO_IPU, ipu1Interrupt);
295
296 TESTINT(DMAC_FROM_SPR, SPRFROMinterrupt);
297 TESTINT(DMAC_TO_SPR, SPRTOinterrupt);
298
299 TESTINT(DMAC_MFIFO_VIF, vifMFIFOInterrupt);
300 TESTINT(DMAC_MFIFO_GIF, gifMFIFOInterrupt);
301 }
302 }
303
304 static __fi void _cpuTestTIMR()
305 {
306 cpuRegs.CP0.n.Count += cpuRegs.cycle-s_iLastCOP0Cycle;
307 s_iLastCOP0Cycle = cpuRegs.cycle;
308
309 // fixme: this looks like a hack to make up for the fact that the TIMR
310 // doesn't yet have a proper mechanism for setting itself up on a nextEventCycle.
311 // A proper fix would schedule the TIMR to trigger at a specific cycle anytime
312 // the Count or Compare registers are modified.
313
314 if ( (cpuRegs.CP0.n.Status.val & 0x8000) &&
315 cpuRegs.CP0.n.Count >= cpuRegs.CP0.n.Compare && cpuRegs.CP0.n.Count < cpuRegs.CP0.n.Compare+1000 )
316 {
317 Console.WriteLn( Color_Magenta, "timr intr: %x, %x", cpuRegs.CP0.n.Count, cpuRegs.CP0.n.Compare);
318 cpuException(0x808000, cpuRegs.branch);
319 }
320 }
321
322 static __fi void _cpuTestPERF()
323 {
324 // Perfs are updated when read by games (COP0's MFC0/MTC0 instructions), so we need
325 // only update them at semi-regular intervals to keep cpuRegs.cycle from wrapping
326 // around twice on us btween updates. Hence this function is called from the cpu's
327 // Counters update.
328
329 COP0_UpdatePCCR();
330 }
331
332 // Checks the COP0.Status for exception enablings.
333 // Exception handling for certain modes is *not* currently supported, this function filters
334 // them out. Exceptions while the exception handler is active (EIE), or exceptions of any
335 // level other than 0 are ignored here.
336
337 static bool cpuIntsEnabled(int Interrupt)
338 {
339 bool IntType = !!(cpuRegs.CP0.n.Status.val & Interrupt); //Choose either INTC or DMAC, depending on what called it
340
341 return IntType && cpuRegs.CP0.n.Status.b.EIE && cpuRegs.CP0.n.Status.b.IE &&
342 !cpuRegs.CP0.n.Status.b.EXL && (cpuRegs.CP0.n.Status.b.ERL == 0);
343 }
344
345 // if cpuRegs.cycle is greater than this cycle, should check cpuEventTest for updates
346 u32 g_nextEventCycle = 0;
347
348 // Shared portion of the branch test, called from both the Interpreter
349 // and the recompiler. (moved here to help alleviate redundant code)
350 __fi void _cpuEventTest_Shared()
351 {
352 ScopedBool etest(eeEventTestIsActive);
353 g_nextEventCycle = cpuRegs.cycle + eeWaitCycles;
354
355 // ---- INTC / DMAC (CPU-level Exceptions) -----------------
356 // Done first because exceptions raised during event tests need to be postponed a few
357 // cycles (fixes Grandia II [PAL], which does a spin loop on a vsync and expects to
358 // be able to read the value before the exception handler clears it).
359
360 uint mask = intcInterrupt() | dmacInterrupt();
361 if (cpuIntsEnabled(mask)) cpuException(mask, cpuRegs.branch);
362
363
364 // ---- Counters -------------
365 // Important: the vsync counter must be the first to be checked. It includes emulation
366 // escape/suspend hooks, and it's really a good idea to suspend/resume emulation before
367 // doing any actual meaningful branchtest logic.
368
369 if( cpuTestCycle( nextsCounter, nextCounter ) )
370 {
371 rcntUpdate();
372 _cpuTestPERF();
373 }
374
375 rcntUpdate_hScanline();
376
377 _cpuTestTIMR();
378
379 // ---- Interrupts -------------
380 // These are basically just DMAC-related events, which also piggy-back the same bits as
381 // the PS2's own DMA channel IRQs and IRQ Masks.
382
383 _cpuTestInterrupts();
384
385 // ---- IOP -------------
386 // * It's important to run a iopEventTest before calling ExecuteBlock. This
387 // is because the IOP does not always perform branch tests before returning
388 // (during the prev branch) and also so it can act on the state the EE has
389 // given it before executing any code.
390 //
391 // * The IOP cannot always be run. If we run IOP code every time through the
392 // cpuEventTest, the IOP generally starts to run way ahead of the EE.
393
394 EEsCycle += cpuRegs.cycle - EEoCycle;
395 EEoCycle = cpuRegs.cycle;
396
397 if( EEsCycle > 0 )
398 iopEventAction = true;
399
400 iopEventTest();
401
402 if( iopEventAction )
403 {
404 //if( EEsCycle < -450 )
405 // Console.WriteLn( " IOP ahead by: %d cycles", -EEsCycle );
406
407 EEsCycle = psxCpu->ExecuteBlock( EEsCycle );
408
409 iopEventAction = false;
410 }
411
412 // ---- VU0 -------------
413 // We're in a EventTest. All dynarec registers are flushed
414 // so there is no need to freeze registers here.
415 CpuVU0->ExecuteBlock();
416
417 // Note: We don't update the VU1 here because it runs it's micro-programs in
418 // one shot always. That is, when a program is executed the VU1 doesn't even
419 // bother to return until the program is completely finished.
420
421 // ---- Schedule Next Event Test --------------
422
423 if( EEsCycle > 192 )
424 {
425 // EE's running way ahead of the IOP still, so we should branch quickly to give the
426 // IOP extra timeslices in short order.
427
428 cpuSetNextEventDelta( 48 );
429 //Console.Warning( "EE ahead of the IOP -- Rapid Event! %d", EEsCycle );
430 }
431
432 // The IOP could be running ahead/behind of us, so adjust the iop's next branch by its
433 // relative position to the EE (via EEsCycle)
434 cpuSetNextEventDelta( ((g_iopNextEventCycle-psxRegs.cycle)*8) - EEsCycle );
435
436 // Apply the hsync counter's nextCycle
437 cpuSetNextEvent( hsyncCounter.sCycle, hsyncCounter.CycleT );
438
439 // Apply vsync and other counter nextCycles
440 cpuSetNextEvent( nextsCounter, nextCounter );
441 }
442
443 __ri void cpuTestINTCInts()
444 {
445 // Check the COP0's Status register for general interrupt disables, and the 0x400
446 // bit (which is INTC master toggle).
447 if( !cpuIntsEnabled(0x400) ) return;
448
449 if( (psHu32(INTC_STAT) & psHu32(INTC_MASK)) == 0 ) return;
450
451 cpuSetNextEventDelta( 4 );
452 if(eeEventTestIsActive && (iopCycleEE > 0))
453 {
454 iopBreak += iopCycleEE; // record the number of cycles the IOP didn't run.
455 iopCycleEE = 0;
456 }
457 }
458
459 __fi void cpuTestDMACInts()
460 {
461 // Check the COP0's Status register for general interrupt disables, and the 0x800
462 // bit (which is the DMAC master toggle).
463 if( !cpuIntsEnabled(0x800) ) return;
464
465 if ( ( (psHu16(0xe012) & psHu16(0xe010)) == 0) &&
466 ( (psHu16(0xe010) & 0x8000) == 0) ) return;
467
468 cpuSetNextEventDelta( 4 );
469 if(eeEventTestIsActive && (iopCycleEE > 0))
470 {
471 iopBreak += iopCycleEE; // record the number of cycles the IOP didn't run.
472 iopCycleEE = 0;
473 }
474 }
475
476 __fi void cpuTestTIMRInts() {
477 if ((cpuRegs.CP0.n.Status.val & 0x10007) == 0x10001) {
478 _cpuTestPERF();
479 _cpuTestTIMR();
480 }
481 }
482
483 __fi void cpuTestHwInts() {
484 cpuTestINTCInts();
485 cpuTestDMACInts();
486 cpuTestTIMRInts();
487 }
488
489 __fi void CPU_INT( EE_EventType n, s32 ecycle)
490 {
491 if( n != 2 && cpuRegs.interrupt & (1<<n) ){ //2 is Gif, and every path 3 masking game triggers this :/
492 DevCon.Warning( "***** EE > Twice-thrown int on IRQ %d", n );
493 }
494
495 // EE events happen 8 cycles in the future instead of whatever was requested.
496 // This can be used on games with PATH3 masking issues for example, or when
497 // some FMV look bad.
498 if(CHECK_EETIMINGHACK) ecycle = 8;
499
500 cpuRegs.interrupt|= 1 << n;
501 cpuRegs.sCycle[n] = cpuRegs.cycle;
502 cpuRegs.eCycle[n] = ecycle;
503
504 // Interrupt is happening soon: make sure both EE and IOP are aware.
505
506 if( ecycle <= 28 && iopCycleEE > 0 )
507 {
508 // If running in the IOP, force it to break immediately into the EE.
509 // the EE's branch test is due to run.
510
511 iopBreak += iopCycleEE; // record the number of cycles the IOP didn't run.
512 iopCycleEE = 0;
513 }
514
515 cpuSetNextEventDelta( cpuRegs.eCycle[n] );
516 }
517
518 // Called from recompilers; __fastcall define is mandatory.
519 void __fastcall eeGameStarting()
520 {
521 if (!g_GameStarted)
522 {
523 //Console.WriteLn( Color_Green, "(R5900) ELF Entry point! [addr=0x%08X]", ElfEntry );
524 g_GameStarted = true;
525 GetCoreThread().GameStartingInThread();
526
527 // GameStartingInThread may issue a reset of the cpu and/or recompilers. Check for and
528 // handle such things here:
529 Cpu->CheckExecutionState();
530 }
531 else
532 {
533 Console.WriteLn( Color_Green, "(R5900) Re-executed ELF Entry point (ignored) [addr=0x%08X]", ElfEntry );
534 }
535 }
536
537 // Called from recompilers; __fastcall define is mandatory.
538 void __fastcall eeloadReplaceOSDSYS()
539 {
540 g_SkipBiosHack = false;
541
542 const wxString &elf_override = GetCoreThread().GetElfOverride();
543
544 if (!elf_override.IsEmpty())
545 cdvdReloadElfInfo(L"host:" + elf_override);
546 else
547 cdvdReloadElfInfo();
548
549 // didn't recognize an ELF
550 if (ElfEntry == -1) {
551 eeGameStarting();
552 return;
553 }
554
555 static u32 osdsys = 0, osdsys_p = 0;
556 // Memory this high is safe before the game's running presumably
557 // Other options are kernel memory (first megabyte) or the scratchpad
558 // PS2LOGO is loaded at 16MB, let's use 17MB
559 const u32 safemem = 0x1100000;
560
561 // The strings are all 64-bit aligned. Why? I don't know, but they are
562 for (u32 i = EELOAD_START; i < EELOAD_START + EELOAD_SIZE; i += 8) {
563 if (!strcmp((char*)PSM(i), "rom0:OSDSYS")) {
564 osdsys = i;
565 break;
566 }
567 }
568 pxAssert(osdsys);
569
570 for (u32 i = osdsys - 4; i >= EELOAD_START; i -= 4) {
571 if (memRead32(i) == osdsys) {
572 osdsys_p = i;
573 break;
574 }
575 }
576 pxAssert(osdsys_p);
577
578 std::string elfname;
579
580 if (!elf_override.IsEmpty())
581 {
582 elfname += "host:";
583 elfname += elf_override.ToUTF8();
584 }
585 else
586 {
587 wxString boot2;
588 if (GetPS2ElfName(boot2) == 2)
589 elfname = boot2.ToUTF8();
590 }
591
592 if (!elfname.empty())
593 {
594 strcpy((char*)PSM(safemem), elfname.c_str());
595 memWrite32(osdsys_p, safemem);
596 }
597 // else... uh...?
598 }

  ViewVC Help
Powered by ViewVC 1.1.22