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

Annotation of /trunk/pcsx2/R5900.cpp

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.22