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

Contents of /trunk/pcsx2/System.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 401 - (show annotations) (download)
Fri Feb 25 17:31:09 2011 UTC (9 years, 9 months ago) by william
File size: 19772 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 #include "PrecompiledHeader.h"
17 #include "Common.h"
18 #include "IopCommon.h"
19 #include "VUmicro.h"
20 #include "newVif.h"
21
22 #include "SamplProf.h"
23
24 #include "Elfheader.h"
25
26 #include "System/RecTypes.h"
27
28 #include "Utilities/MemsetFast.inl"
29
30
31 // --------------------------------------------------------------------------------------
32 // RecompiledCodeReserve (implementations)
33 // --------------------------------------------------------------------------------------
34
35 // Constructor!
36 // Parameters:
37 // name - a nice long name that accurately describes the contents of this reserve.
38 RecompiledCodeReserve::RecompiledCodeReserve( const wxString& name, uint defCommit )
39 : BaseVmReserveListener( name )
40 {
41 m_blocksize = (1024 * 128) / __pagesize;
42 m_prot_mode = PageAccess_Any();
43 m_def_commit = defCommit / __pagesize;
44
45 m_profiler_registered = false;
46 }
47
48 RecompiledCodeReserve::~RecompiledCodeReserve() throw()
49 {
50 _termProfiler();
51 }
52
53 void RecompiledCodeReserve::_registerProfiler()
54 {
55 if (m_profiler_name.IsEmpty() || !IsOk()) return;
56 ProfilerRegisterSource( m_profiler_name, m_baseptr, GetReserveSizeInBytes() );
57 m_profiler_registered = true;
58 }
59
60 void RecompiledCodeReserve::_termProfiler()
61 {
62 if (m_profiler_registered)
63 ProfilerTerminateSource( m_profiler_name );
64 }
65
66 uint RecompiledCodeReserve::_calcDefaultCommitInBlocks() const
67 {
68 return (m_def_commit + m_blocksize - 1) / m_blocksize;
69 }
70
71 void* RecompiledCodeReserve::Reserve( size_t size, uptr base, uptr upper_bounds )
72 {
73 if (!_parent::Reserve(size, base, upper_bounds)) return NULL;
74 _registerProfiler();
75 return m_baseptr;
76 }
77
78
79 // Sets the abbreviated name used by the profiler. Name should be under 10 characters long.
80 // After a name has been set, a profiler source will be automatically registered and cleared
81 // in accordance with changes in the reserve area.
82 RecompiledCodeReserve& RecompiledCodeReserve::SetProfilerName( const wxString& shortname )
83 {
84 m_profiler_name = shortname;
85 _registerProfiler();
86 return *this;
87 }
88
89 void RecompiledCodeReserve::DoCommitAndProtect( uptr page )
90 {
91 CommitBlocks(page, (m_pages_commited || !m_def_commit) ? 1 : _calcDefaultCommitInBlocks() );
92 }
93
94 void RecompiledCodeReserve::OnCommittedBlock( void* block )
95 {
96 if (IsDevBuild)
97 {
98 // Clear the recompiled code block to 0xcc (INT3) -- this helps disasm tools show
99 // the assembly dump more cleanly. We don't clear the block on Release builds since
100 // it can add a noticeable amount of overhead to large block recompilations.
101
102 memset_sse_a<0xcc>( block, m_blocksize * __pagesize );
103 }
104 }
105
106 // This error message is shared by R5900, R3000, and microVU recompilers. It is not used by the
107 // SuperVU recompiler, since it has its own customized message.
108 void RecompiledCodeReserve::ThrowIfNotOk() const
109 {
110 if (IsOk()) return;
111
112 throw Exception::OutOfMemory(m_name)
113 .SetDiagMsg(pxsFmt( L"Recompiled code cache could not be mapped." ))
114 .SetUserMsg( pxE( "!Notice:Recompiler:VirtualMemoryAlloc",
115 L"This recompiler was unable to reserve contiguous memory required for internal caches. "
116 L"This error can be caused by low virtual memory resources, such as a small or disabled swapfile, "
117 L"or by another program that is hogging a lot of memory. You can also try reducing the default "
118 L"cache sizes for all PCSX2 recompilers, found under Host Settings."
119 ));
120 }
121
122
123 void SysOutOfMemory_EmergencyResponse(uptr blocksize)
124 {
125 // An out of memory error occurred. All we can try to do in response is reset the various
126 // recompiler caches (which can sometimes total over 120megs, so it can be quite helpful).
127 // If the user is using interpreters, or if the memory allocation failure was on a very small
128 // allocation, then this code could fail; but that's fine. We're already trying harder than
129 // 99.995% of all programs ever written. -- air
130
131 if (Cpu)
132 {
133 Cpu->SetCacheReserve( (Cpu->GetCacheReserve() * 2) / 3 );
134 Cpu->Reset();
135 }
136
137 if (CpuVU0)
138 {
139 CpuVU0->SetCacheReserve( (CpuVU0->GetCacheReserve() * 2) / 3 );
140 CpuVU0->Reset();
141 }
142
143 if (CpuVU1)
144 {
145 CpuVU1->SetCacheReserve( (CpuVU1->GetCacheReserve() * 2) / 3 );
146 CpuVU1->Reset();
147 }
148
149 if (psxCpu)
150 {
151 psxCpu->SetCacheReserve( (psxCpu->GetCacheReserve() * 2) / 3 );
152 psxCpu->Reset();
153 }
154 }
155
156
157 #if _MSC_VER
158 # include "svnrev.h"
159 #endif
160
161 const Pcsx2Config EmuConfig;
162
163 // Provides an accessor for quick modification of GS options. All GS options are allowed to be
164 // changed "on the fly" by the *main/gui thread only*.
165 Pcsx2Config::GSOptions& SetGSConfig()
166 {
167 //DbgCon.WriteLn( "Direct modification of EmuConfig.GS detected" );
168 AffinityAssert_AllowFrom_MainUI();
169 return const_cast<Pcsx2Config::GSOptions&>(EmuConfig.GS);
170 }
171
172 // Provides an accessor for quick modification of Recompiler options.
173 // Used by loadGameSettings() to set clamp modes via database at game startup.
174 Pcsx2Config::RecompilerOptions& SetRecompilerConfig()
175 {
176 //DbgCon.WriteLn( "Direct modification of EmuConfig.Gamefixes detected" );
177 AffinityAssert_AllowFrom_MainUI();
178 return const_cast<Pcsx2Config::RecompilerOptions&>(EmuConfig.Cpu.Recompiler);
179 }
180
181 // Provides an accessor for quick modification of Gamefix options.
182 // Used by loadGameSettings() to set gamefixes via database at game startup.
183 Pcsx2Config::GamefixOptions& SetGameFixConfig()
184 {
185 //DbgCon.WriteLn( "Direct modification of EmuConfig.Gamefixes detected" );
186 AffinityAssert_AllowFrom_MainUI();
187 return const_cast<Pcsx2Config::GamefixOptions&>(EmuConfig.Gamefixes);
188 }
189
190 TraceLogFilters& SetTraceConfig()
191 {
192 //DbgCon.WriteLn( "Direct modification of EmuConfig.TraceLog detected" );
193 AffinityAssert_AllowFrom_MainUI();
194 return const_cast<TraceLogFilters&>(EmuConfig.Trace);
195 }
196
197
198 // This function should be called once during program execution.
199 void SysLogMachineCaps()
200 {
201 Console.WriteLn( Color_StrongGreen, "PCSX2 %s - compiled on " __DATE__, PCSX2_FULL_VERSION());
202
203 Console.WriteLn( "Savestate version: 0x%x", g_SaveVersion);
204 Console.Newline();
205
206 Console.WriteLn( Color_StrongBlack, "Host Machine Init:" );
207
208 Console.Indent().WriteLn(
209 L"Operating System = %s\n"
210 L"Physical RAM = %u MB",
211
212 GetOSVersionString().c_str(),
213 (u32)(GetPhysicalMemory() / _1mb)
214 );
215
216 u32 speed = x86caps.CalculateMHz();
217
218 Console.Indent().WriteLn(
219 L"CPU name = %s\n"
220 L"Vendor/Model = %s (stepping %02X)\n"
221 L"CPU speed = %u.%03u ghz (%u logical thread%s)\n"
222 L"x86PType = %s\n"
223 L"x86Flags = %08x %08x\n"
224 L"x86EFlags = %08x",
225 fromUTF8( x86caps.FamilyName ).Trim().Trim(false).c_str(),
226 fromUTF8( x86caps.VendorName ).c_str(), x86caps.StepID,
227 speed / 1000, speed % 1000,
228 x86caps.LogicalCores, (x86caps.LogicalCores==1) ? L"" : L"s",
229 x86caps.GetTypeName().c_str(),
230 x86caps.Flags, x86caps.Flags2,
231 x86caps.EFlags
232 );
233
234 Console.Newline();
235
236 wxArrayString features[2]; // 2 lines, for readability!
237
238 if( x86caps.hasMultimediaExtensions ) features[0].Add( L"MMX" );
239 if( x86caps.hasStreamingSIMDExtensions ) features[0].Add( L"SSE" );
240 if( x86caps.hasStreamingSIMD2Extensions ) features[0].Add( L"SSE2" );
241 if( x86caps.hasStreamingSIMD3Extensions ) features[0].Add( L"SSE3" );
242 if( x86caps.hasSupplementalStreamingSIMD3Extensions ) features[0].Add( L"SSSE3" );
243 if( x86caps.hasStreamingSIMD4Extensions ) features[0].Add( L"SSE4.1" );
244 if( x86caps.hasStreamingSIMD4Extensions2 ) features[0].Add( L"SSE4.2" );
245 if( x86caps.hasAVX ) features[0].Add( L"AVX" );
246 if( x86caps.hasFMA) features[0].Add( L"FMA" );
247
248 if( x86caps.hasMultimediaExtensionsExt ) features[1].Add( L"MMX2 " );
249 if( x86caps.has3DNOWInstructionExtensions ) features[1].Add( L"3DNOW " );
250 if( x86caps.has3DNOWInstructionExtensionsExt ) features[1].Add( L"3DNOW2" );
251 if( x86caps.hasStreamingSIMD4ExtensionsA ) features[1].Add( L"SSE4a " );
252
253 const wxString result[2] =
254 {
255 JoinString( features[0], L".. " ),
256 JoinString( features[1], L".. " )
257 };
258
259 Console.WriteLn( Color_StrongBlack, L"x86 Features Detected:" );
260 Console.Indent().WriteLn( result[0] + (result[1].IsEmpty() ? L"" : (L"\n" + result[1])) );
261 Console.Newline();
262 }
263
264 template< typename CpuType >
265 class CpuInitializer
266 {
267 public:
268 ScopedPtr<CpuType> MyCpu;
269 ScopedExcept ExThrown;
270
271 CpuInitializer();
272 virtual ~CpuInitializer() throw();
273
274 bool IsAvailable() const
275 {
276 return !!MyCpu;
277 }
278
279 CpuType* GetPtr() { return MyCpu.GetPtr(); }
280 const CpuType* GetPtr() const { return MyCpu.GetPtr(); }
281
282 operator CpuType*() { return GetPtr(); }
283 operator const CpuType*() const { return GetPtr(); }
284 };
285
286 // --------------------------------------------------------------------------------------
287 // CpuInitializer Template
288 // --------------------------------------------------------------------------------------
289 // Helper for initializing various PCSX2 CPU providers, and handing errors and cleanup.
290 //
291 template< typename CpuType >
292 CpuInitializer< CpuType >::CpuInitializer()
293 {
294 try {
295 MyCpu = new CpuType();
296 MyCpu->Reserve();
297 }
298 catch( Exception::RuntimeError& ex )
299 {
300 Console.Error( L"CPU provider error:\n\t" + ex.FormatDiagnosticMessage() );
301 MyCpu = NULL;
302 ExThrown = ex.Clone();
303 }
304 catch( std::runtime_error& ex )
305 {
306 Console.Error( L"CPU provider error (STL Exception)\n\tDetails:" + fromUTF8( ex.what() ) );
307 MyCpu = NULL;
308 ExThrown = new Exception::RuntimeError(ex);
309 }
310 }
311
312 template< typename CpuType >
313 CpuInitializer< CpuType >::~CpuInitializer() throw()
314 {
315 if (MyCpu)
316 MyCpu->Shutdown();
317 }
318
319 // --------------------------------------------------------------------------------------
320 // CpuInitializerSet
321 // --------------------------------------------------------------------------------------
322 class CpuInitializerSet
323 {
324 public:
325 // Note: Allocate sVU first -- it's the most picky.
326
327 CpuInitializer<recSuperVU0> superVU0;
328 CpuInitializer<recSuperVU1> superVU1;
329
330 CpuInitializer<recMicroVU0> microVU0;
331 CpuInitializer<recMicroVU1> microVU1;
332
333 CpuInitializer<InterpVU0> interpVU0;
334 CpuInitializer<InterpVU1> interpVU1;
335
336 public:
337 CpuInitializerSet() {}
338 virtual ~CpuInitializerSet() throw() {}
339 };
340
341
342 // returns the translated error message for the Virtual Machine failing to allocate!
343 static wxString GetMemoryErrorVM()
344 {
345 return pxE( "!Notice:EmuCore::MemoryForVM",
346 L"PCSX2 is unable to allocate memory needed for the PS2 virtual machine. "
347 L"Close out some memory hogging background tasks and try again."
348 );
349 }
350
351 // --------------------------------------------------------------------------------------
352 // SysReserveVM (implementations)
353 // --------------------------------------------------------------------------------------
354 SysMainMemory::SysMainMemory()
355 {
356 }
357
358 SysMainMemory::~SysMainMemory() throw()
359 {
360 ReleaseAll();
361 }
362
363 void SysMainMemory::ReserveAll()
364 {
365 pxInstallSignalHandler();
366
367 DevCon.WriteLn( Color_StrongBlue, "Mapping host memory for virtual systems..." );
368 ConsoleIndentScope indent(1);
369
370 m_ee.Reserve();
371 m_iop.Reserve();
372 m_vu.Reserve();
373
374 reserveNewVif(0);
375 reserveNewVif(1);
376 }
377
378 void SysMainMemory::CommitAll()
379 {
380 vtlb_Core_Alloc();
381 if (m_ee.IsCommitted() && m_iop.IsCommitted() && m_vu.IsCommitted()) return;
382
383 DevCon.WriteLn( Color_StrongBlue, "Allocating host memory for virtual systems..." );
384 ConsoleIndentScope indent(1);
385
386 m_ee.Commit();
387 m_iop.Commit();
388 m_vu.Commit();
389 }
390
391
392 void SysMainMemory::ResetAll()
393 {
394 CommitAll();
395
396 DevCon.WriteLn( Color_StrongBlue, "Resetting host memory for virtual systems..." );
397 ConsoleIndentScope indent(1);
398
399 m_ee.Reset();
400 m_iop.Reset();
401 m_vu.Reset();
402
403 // Note: newVif is reset as part of other VIF structures.
404 }
405
406 void SysMainMemory::DecommitAll()
407 {
408 if (!m_ee.IsCommitted() && !m_iop.IsCommitted() && !m_vu.IsCommitted()) return;
409
410 Console.WriteLn( Color_Blue, "Decommitting host memory for virtual systems..." );
411 ConsoleIndentScope indent(1);
412
413 m_ee.Decommit();
414 m_iop.Decommit();
415 m_vu.Decommit();
416
417 closeNewVif(0);
418 closeNewVif(1);
419
420 vtlb_Core_Free();
421 }
422
423 void SysMainMemory::ReleaseAll()
424 {
425 DecommitAll();
426
427 Console.WriteLn( Color_Blue, "Releasing host memory maps for virtual systems..." );
428 ConsoleIndentScope indent(1);
429
430 vtlb_Core_Free(); // Just to be sure... (calling order could result in it getting missed during Decommit).
431
432 releaseNewVif(0);
433 releaseNewVif(1);
434
435 m_ee.Decommit();
436 m_iop.Decommit();
437 m_vu.Decommit();
438
439 safe_delete(Source_PageFault);
440 }
441
442
443 // --------------------------------------------------------------------------------------
444 // SysCpuProviderPack (implementations)
445 // --------------------------------------------------------------------------------------
446 SysCpuProviderPack::SysCpuProviderPack()
447 {
448 Console.WriteLn( Color_StrongBlue, "Reserving memory for recompilers..." );
449 ConsoleIndentScope indent(1);
450
451 CpuProviders = new CpuInitializerSet();
452
453 try {
454 recCpu.Reserve();
455 }
456 catch( Exception::RuntimeError& ex )
457 {
458 m_RecExceptionEE = ex.Clone();
459 Console.Error( L"EE Recompiler Reservation Failed:\n" + ex.FormatDiagnosticMessage() );
460 recCpu.Shutdown();
461 }
462
463 try {
464 psxRec.Reserve();
465 }
466 catch( Exception::RuntimeError& ex )
467 {
468 m_RecExceptionIOP = ex.Clone();
469 Console.Error( L"IOP Recompiler Reservation Failed:\n" + ex.FormatDiagnosticMessage() );
470 psxRec.Shutdown();
471 }
472
473 // hmm! : VU0 and VU1 pre-allocations should do sVU and mVU separately? Sounds complicated. :(
474
475 if (newVifDynaRec)
476 {
477 dVifReserve(0);
478 dVifReserve(1);
479 }
480 }
481
482 bool SysCpuProviderPack::IsRecAvailable_MicroVU0() const { return CpuProviders->microVU0.IsAvailable(); }
483 bool SysCpuProviderPack::IsRecAvailable_MicroVU1() const { return CpuProviders->microVU1.IsAvailable(); }
484 BaseException* SysCpuProviderPack::GetException_MicroVU0() const { return CpuProviders->microVU0.ExThrown; }
485 BaseException* SysCpuProviderPack::GetException_MicroVU1() const { return CpuProviders->microVU1.ExThrown; }
486
487 bool SysCpuProviderPack::IsRecAvailable_SuperVU0() const { return CpuProviders->superVU0.IsAvailable(); }
488 bool SysCpuProviderPack::IsRecAvailable_SuperVU1() const { return CpuProviders->superVU1.IsAvailable(); }
489 BaseException* SysCpuProviderPack::GetException_SuperVU0() const { return CpuProviders->superVU0.ExThrown; }
490 BaseException* SysCpuProviderPack::GetException_SuperVU1() const { return CpuProviders->superVU1.ExThrown; }
491
492
493 void SysCpuProviderPack::CleanupMess() throw()
494 {
495 try
496 {
497 psxRec.Shutdown();
498 recCpu.Shutdown();
499
500 if (newVifDynaRec)
501 {
502 dVifRelease(0);
503 dVifRelease(1);
504 }
505 }
506 DESTRUCTOR_CATCHALL
507 }
508
509 SysCpuProviderPack::~SysCpuProviderPack() throw()
510 {
511 CleanupMess();
512 }
513
514 bool SysCpuProviderPack::HadSomeFailures( const Pcsx2Config::RecompilerOptions& recOpts ) const
515 {
516 return (recOpts.EnableEE && !IsRecAvailable_EE()) ||
517 (recOpts.EnableIOP && !IsRecAvailable_IOP()) ||
518 (recOpts.EnableVU0 && recOpts.UseMicroVU0 && !IsRecAvailable_MicroVU0()) ||
519 (recOpts.EnableVU1 && recOpts.UseMicroVU0 && !IsRecAvailable_MicroVU1()) ||
520 (recOpts.EnableVU0 && !recOpts.UseMicroVU0 && !IsRecAvailable_SuperVU0()) ||
521 (recOpts.EnableVU1 && !recOpts.UseMicroVU1 && !IsRecAvailable_SuperVU1());
522
523 }
524
525 BaseVUmicroCPU* CpuVU0 = NULL;
526 BaseVUmicroCPU* CpuVU1 = NULL;
527
528 void SysCpuProviderPack::ApplyConfig() const
529 {
530 Cpu = CHECK_EEREC ? &recCpu : &intCpu;
531 psxCpu = CHECK_IOPREC ? &psxRec : &psxInt;
532
533 CpuVU0 = CpuProviders->interpVU0;
534 CpuVU1 = CpuProviders->interpVU1;
535
536 if( EmuConfig.Cpu.Recompiler.EnableVU0 )
537 CpuVU0 = EmuConfig.Cpu.Recompiler.UseMicroVU0 ? (BaseVUmicroCPU*)CpuProviders->microVU0 : (BaseVUmicroCPU*)CpuProviders->superVU0;
538
539 if( EmuConfig.Cpu.Recompiler.EnableVU1 )
540 CpuVU1 = EmuConfig.Cpu.Recompiler.UseMicroVU1 ? (BaseVUmicroCPU*)CpuProviders->microVU1 : (BaseVUmicroCPU*)CpuProviders->superVU1;
541 }
542
543 // This is a semi-hacky function for convenience
544 BaseVUmicroCPU* SysCpuProviderPack::getVUprovider(int whichProvider, int vuIndex) const {
545 switch (whichProvider) {
546 case 0: return vuIndex ? (BaseVUmicroCPU*)CpuProviders->interpVU1 : (BaseVUmicroCPU*)CpuProviders->interpVU0;
547 case 1: return vuIndex ? (BaseVUmicroCPU*)CpuProviders->superVU1 : (BaseVUmicroCPU*)CpuProviders->superVU0;
548 case 2: return vuIndex ? (BaseVUmicroCPU*)CpuProviders->microVU1 : (BaseVUmicroCPU*)CpuProviders->microVU0;
549 }
550 return NULL;
551 }
552
553 // Resets all PS2 cpu execution caches, which does not affect that actual PS2 state/condition.
554 // This can be called at any time outside the context of a Cpu->Execute() block without
555 // bad things happening (recompilers will slow down for a brief moment since rec code blocks
556 // are dumped).
557 // Use this method to reset the recs when important global pointers like the MTGS are re-assigned.
558 void SysClearExecutionCache()
559 {
560 GetCpuProviders().ApplyConfig();
561
562 Cpu->Reset();
563 psxCpu->Reset();
564
565 // mVU's VU0 needs to be properly initialized for macro mode even if it's not used for micro mode!
566 if (CHECK_EEREC)
567 ((BaseVUmicroCPU*)GetCpuProviders().CpuProviders->microVU0)->Reset();
568
569 CpuVU0->Reset();
570 CpuVU1->Reset();
571
572 if (newVifDynaRec)
573 {
574 dVifReset(0);
575 dVifReset(1);
576 }
577 }
578
579 // Maps a block of memory for use as a recompiled code buffer, and ensures that the
580 // allocation is below a certain memory address (specified in "bounds" parameter).
581 // The allocated block has code execution privileges.
582 // Returns NULL on allocation failure.
583 u8* SysMmapEx(uptr base, u32 size, uptr bounds, const char *caller)
584 {
585 u8* Mem = (u8*)HostSys::Mmap( base, size );
586
587 if( (Mem == NULL) || (bounds != 0 && (((uptr)Mem + size) > bounds)) )
588 {
589 if( base )
590 {
591 DbgCon.Warning( "First try failed allocating %s at address 0x%x", caller, base );
592
593 // Let's try again at an OS-picked memory area, and then hope it meets needed
594 // boundschecking criteria below.
595 SafeSysMunmap( Mem, size );
596 Mem = (u8*)HostSys::Mmap( 0, size );
597 }
598
599 if( (bounds != 0) && (((uptr)Mem + size) > bounds) )
600 {
601 DevCon.Warning( "Second try failed allocating %s, block ptr 0x%x does not meet required criteria.", caller, Mem );
602 SafeSysMunmap( Mem, size );
603
604 // returns NULL, caller should throw an exception.
605 }
606 }
607 return Mem;
608 }
609
610 // This function always returns a valid DiscID -- using the Sony serial when possible, and
611 // falling back on the CRC checksum of the ELF binary if the PS2 software being run is
612 // homebrew or some other serial-less item.
613 wxString SysGetDiscID()
614 {
615 if( !DiscSerial.IsEmpty() ) return DiscSerial;
616
617 if( !ElfCRC )
618 {
619 // FIXME: system is currently running the BIOS, so it should return a serial based on
620 // the BIOS being run (either a checksum of the BIOS roms, and/or a string based on BIOS
621 // region and revision).
622
623 return wxEmptyString;
624 }
625
626 return pxsFmt( L"%08x", ElfCRC );
627 }

  ViewVC Help
Powered by ViewVC 1.1.22