/[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 290 - (show annotations) (download)
Sat Dec 25 02:51:30 2010 UTC (9 years, 2 months ago) by william
File size: 19491 byte(s)
Auto Commited Import of: pcsx2-0.9.7-DEBUG (upstream: v0.9.7.4139 local: v0.9.7.283-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
246 if( x86caps.hasMultimediaExtensionsExt ) features[1].Add( L"MMX2 " );
247 if( x86caps.has3DNOWInstructionExtensions ) features[1].Add( L"3DNOW " );
248 if( x86caps.has3DNOWInstructionExtensionsExt ) features[1].Add( L"3DNOW2" );
249 if( x86caps.hasStreamingSIMD4ExtensionsA ) features[1].Add( L"SSE4a " );
250
251 const wxString result[2] =
252 {
253 JoinString( features[0], L".. " ),
254 JoinString( features[1], L".. " )
255 };
256
257 Console.WriteLn( Color_StrongBlack, L"x86 Features Detected:" );
258 Console.Indent().WriteLn( result[0] + (result[1].IsEmpty() ? L"" : (L"\n" + result[1])) );
259 Console.Newline();
260 }
261
262 template< typename CpuType >
263 class CpuInitializer
264 {
265 public:
266 ScopedPtr<CpuType> MyCpu;
267 ScopedExcept ExThrown;
268
269 CpuInitializer();
270 virtual ~CpuInitializer() throw();
271
272 bool IsAvailable() const
273 {
274 return !!MyCpu;
275 }
276
277 CpuType* GetPtr() { return MyCpu.GetPtr(); }
278 const CpuType* GetPtr() const { return MyCpu.GetPtr(); }
279
280 operator CpuType*() { return GetPtr(); }
281 operator const CpuType*() const { return GetPtr(); }
282 };
283
284 // --------------------------------------------------------------------------------------
285 // CpuInitializer Template
286 // --------------------------------------------------------------------------------------
287 // Helper for initializing various PCSX2 CPU providers, and handing errors and cleanup.
288 //
289 template< typename CpuType >
290 CpuInitializer< CpuType >::CpuInitializer()
291 {
292 try {
293 MyCpu = new CpuType();
294 MyCpu->Reserve();
295 }
296 catch( Exception::RuntimeError& ex )
297 {
298 Console.Error( L"CPU provider error:\n\t" + ex.FormatDiagnosticMessage() );
299 MyCpu = NULL;
300 ExThrown = ex.Clone();
301 }
302 catch( std::runtime_error& ex )
303 {
304 Console.Error( L"CPU provider error (STL Exception)\n\tDetails:" + fromUTF8( ex.what() ) );
305 MyCpu = NULL;
306 ExThrown = new Exception::RuntimeError(ex);
307 }
308 }
309
310 template< typename CpuType >
311 CpuInitializer< CpuType >::~CpuInitializer() throw()
312 {
313 if (MyCpu)
314 MyCpu->Shutdown();
315 }
316
317 // --------------------------------------------------------------------------------------
318 // CpuInitializerSet
319 // --------------------------------------------------------------------------------------
320 class CpuInitializerSet
321 {
322 public:
323 // Note: Allocate sVU first -- it's the most picky.
324
325 CpuInitializer<recSuperVU0> superVU0;
326 CpuInitializer<recSuperVU1> superVU1;
327
328 CpuInitializer<recMicroVU0> microVU0;
329 CpuInitializer<recMicroVU1> microVU1;
330
331 CpuInitializer<InterpVU0> interpVU0;
332 CpuInitializer<InterpVU1> interpVU1;
333
334 public:
335 CpuInitializerSet() {}
336 virtual ~CpuInitializerSet() throw() {}
337 };
338
339
340 // returns the translated error message for the Virtual Machine failing to allocate!
341 static wxString GetMemoryErrorVM()
342 {
343 return pxE( "!Notice:EmuCore::MemoryForVM",
344 L"PCSX2 is unable to allocate memory needed for the PS2 virtual machine. "
345 L"Close out some memory hogging background tasks and try again."
346 );
347 }
348
349 // --------------------------------------------------------------------------------------
350 // SysReserveVM (implementations)
351 // --------------------------------------------------------------------------------------
352 SysMainMemory::SysMainMemory()
353 {
354 }
355
356 SysMainMemory::~SysMainMemory() throw()
357 {
358 ReleaseAll();
359 }
360
361 void SysMainMemory::ReserveAll()
362 {
363 pxInstallSignalHandler();
364
365 DevCon.WriteLn( Color_StrongBlue, "Mapping host memory for virtual systems..." );
366 ConsoleIndentScope indent(1);
367
368 m_ee.Reserve();
369 m_iop.Reserve();
370 m_vu.Reserve();
371
372 reserveNewVif(0);
373 reserveNewVif(1);
374 }
375
376 void SysMainMemory::CommitAll()
377 {
378 vtlb_Core_Alloc();
379 if (m_ee.IsCommitted() && m_iop.IsCommitted() && m_vu.IsCommitted()) return;
380
381 DevCon.WriteLn( Color_StrongBlue, "Allocating host memory for virtual systems..." );
382 ConsoleIndentScope indent(1);
383
384 m_ee.Commit();
385 m_iop.Commit();
386 m_vu.Commit();
387 }
388
389
390 void SysMainMemory::ResetAll()
391 {
392 CommitAll();
393
394 DevCon.WriteLn( Color_StrongBlue, "Resetting host memory for virtual systems..." );
395 ConsoleIndentScope indent(1);
396
397 m_ee.Reset();
398 m_iop.Reset();
399 m_vu.Reset();
400
401 // Note: newVif is reset as part of other VIF structures.
402 }
403
404 void SysMainMemory::DecommitAll()
405 {
406 if (!m_ee.IsCommitted() && !m_iop.IsCommitted() && !m_vu.IsCommitted()) return;
407
408 Console.WriteLn( Color_Blue, "Decommitting host memory for virtual systems..." );
409 ConsoleIndentScope indent(1);
410
411 m_ee.Decommit();
412 m_iop.Decommit();
413 m_vu.Decommit();
414
415 closeNewVif(0);
416 closeNewVif(1);
417
418 vtlb_Core_Free();
419 }
420
421 void SysMainMemory::ReleaseAll()
422 {
423 DecommitAll();
424
425 Console.WriteLn( Color_Blue, "Releasing host memory maps for virtual systems..." );
426 ConsoleIndentScope indent(1);
427
428 vtlb_Core_Free(); // Just to be sure... (calling order could result in it getting missed during Decommit).
429
430 releaseNewVif(0);
431 releaseNewVif(1);
432
433 m_ee.Decommit();
434 m_iop.Decommit();
435 m_vu.Decommit();
436
437 safe_delete(Source_PageFault);
438 }
439
440
441 // --------------------------------------------------------------------------------------
442 // SysCpuProviderPack (implementations)
443 // --------------------------------------------------------------------------------------
444 SysCpuProviderPack::SysCpuProviderPack()
445 {
446 Console.WriteLn( Color_StrongBlue, "Reserving memory for recompilers..." );
447 ConsoleIndentScope indent(1);
448
449 CpuProviders = new CpuInitializerSet();
450
451 try {
452 recCpu.Reserve();
453 }
454 catch( Exception::RuntimeError& ex )
455 {
456 m_RecExceptionEE = ex.Clone();
457 Console.Error( L"EE Recompiler Reservation Failed:\n" + ex.FormatDiagnosticMessage() );
458 recCpu.Shutdown();
459 }
460
461 try {
462 psxRec.Reserve();
463 }
464 catch( Exception::RuntimeError& ex )
465 {
466 m_RecExceptionIOP = ex.Clone();
467 Console.Error( L"IOP Recompiler Reservation Failed:\n" + ex.FormatDiagnosticMessage() );
468 psxRec.Shutdown();
469 }
470
471 // hmm! : VU0 and VU1 pre-allocations should do sVU and mVU separately? Sounds complicated. :(
472 }
473
474 bool SysCpuProviderPack::IsRecAvailable_MicroVU0() const { return CpuProviders->microVU0.IsAvailable(); }
475 bool SysCpuProviderPack::IsRecAvailable_MicroVU1() const { return CpuProviders->microVU1.IsAvailable(); }
476 BaseException* SysCpuProviderPack::GetException_MicroVU0() const { return CpuProviders->microVU0.ExThrown; }
477 BaseException* SysCpuProviderPack::GetException_MicroVU1() const { return CpuProviders->microVU1.ExThrown; }
478
479 bool SysCpuProviderPack::IsRecAvailable_SuperVU0() const { return CpuProviders->superVU0.IsAvailable(); }
480 bool SysCpuProviderPack::IsRecAvailable_SuperVU1() const { return CpuProviders->superVU1.IsAvailable(); }
481 BaseException* SysCpuProviderPack::GetException_SuperVU0() const { return CpuProviders->superVU0.ExThrown; }
482 BaseException* SysCpuProviderPack::GetException_SuperVU1() const { return CpuProviders->superVU1.ExThrown; }
483
484
485 void SysCpuProviderPack::CleanupMess() throw()
486 {
487 try
488 {
489 psxRec.Shutdown();
490 recCpu.Shutdown();
491 }
492 DESTRUCTOR_CATCHALL
493 }
494
495 SysCpuProviderPack::~SysCpuProviderPack() throw()
496 {
497 CleanupMess();
498 }
499
500 bool SysCpuProviderPack::HadSomeFailures( const Pcsx2Config::RecompilerOptions& recOpts ) const
501 {
502 return (recOpts.EnableEE && !IsRecAvailable_EE()) ||
503 (recOpts.EnableIOP && !IsRecAvailable_IOP()) ||
504 (recOpts.EnableVU0 && recOpts.UseMicroVU0 && !IsRecAvailable_MicroVU0()) ||
505 (recOpts.EnableVU1 && recOpts.UseMicroVU0 && !IsRecAvailable_MicroVU1()) ||
506 (recOpts.EnableVU0 && !recOpts.UseMicroVU0 && !IsRecAvailable_SuperVU0()) ||
507 (recOpts.EnableVU1 && !recOpts.UseMicroVU1 && !IsRecAvailable_SuperVU1());
508
509 }
510
511 BaseVUmicroCPU* CpuVU0 = NULL;
512 BaseVUmicroCPU* CpuVU1 = NULL;
513
514 void SysCpuProviderPack::ApplyConfig() const
515 {
516 Cpu = CHECK_EEREC ? &recCpu : &intCpu;
517 psxCpu = CHECK_IOPREC ? &psxRec : &psxInt;
518
519 CpuVU0 = CpuProviders->interpVU0;
520 CpuVU1 = CpuProviders->interpVU1;
521
522 if( EmuConfig.Cpu.Recompiler.EnableVU0 )
523 CpuVU0 = EmuConfig.Cpu.Recompiler.UseMicroVU0 ? (BaseVUmicroCPU*)CpuProviders->microVU0 : (BaseVUmicroCPU*)CpuProviders->superVU0;
524
525 if( EmuConfig.Cpu.Recompiler.EnableVU1 )
526 CpuVU1 = EmuConfig.Cpu.Recompiler.UseMicroVU1 ? (BaseVUmicroCPU*)CpuProviders->microVU1 : (BaseVUmicroCPU*)CpuProviders->superVU1;
527 }
528
529 // This is a semi-hacky function for convenience
530 BaseVUmicroCPU* SysCpuProviderPack::getVUprovider(int whichProvider, int vuIndex) const {
531 switch (whichProvider) {
532 case 0: return vuIndex ? (BaseVUmicroCPU*)CpuProviders->interpVU1 : (BaseVUmicroCPU*)CpuProviders->interpVU0;
533 case 1: return vuIndex ? (BaseVUmicroCPU*)CpuProviders->superVU1 : (BaseVUmicroCPU*)CpuProviders->superVU0;
534 case 2: return vuIndex ? (BaseVUmicroCPU*)CpuProviders->microVU1 : (BaseVUmicroCPU*)CpuProviders->microVU0;
535 }
536 return NULL;
537 }
538
539 // Resets all PS2 cpu execution caches, which does not affect that actual PS2 state/condition.
540 // This can be called at any time outside the context of a Cpu->Execute() block without
541 // bad things happening (recompilers will slow down for a brief moment since rec code blocks
542 // are dumped).
543 // Use this method to reset the recs when important global pointers like the MTGS are re-assigned.
544 void SysClearExecutionCache()
545 {
546 GetCpuProviders().ApplyConfig();
547
548 Cpu->Reset();
549 psxCpu->Reset();
550
551 // mVU's VU0 needs to be properly initialized for macro mode even if it's not used for micro mode!
552 if (CHECK_EEREC)
553 ((BaseVUmicroCPU*)GetCpuProviders().CpuProviders->microVU0)->Reset();
554
555 CpuVU0->Reset();
556 CpuVU1->Reset();
557
558 resetNewVif(0);
559 resetNewVif(1);
560 }
561
562 // Maps a block of memory for use as a recompiled code buffer, and ensures that the
563 // allocation is below a certain memory address (specified in "bounds" parameter).
564 // The allocated block has code execution privileges.
565 // Returns NULL on allocation failure.
566 u8* SysMmapEx(uptr base, u32 size, uptr bounds, const char *caller)
567 {
568 u8* Mem = (u8*)HostSys::Mmap( base, size );
569
570 if( (Mem == NULL) || (bounds != 0 && (((uptr)Mem + size) > bounds)) )
571 {
572 if( base )
573 {
574 DbgCon.Warning( "First try failed allocating %s at address 0x%x", caller, base );
575
576 // Let's try again at an OS-picked memory area, and then hope it meets needed
577 // boundschecking criteria below.
578 SafeSysMunmap( Mem, size );
579 Mem = (u8*)HostSys::Mmap( 0, size );
580 }
581
582 if( (bounds != 0) && (((uptr)Mem + size) > bounds) )
583 {
584 DevCon.Warning( "Second try failed allocating %s, block ptr 0x%x does not meet required criteria.", caller, Mem );
585 SafeSysMunmap( Mem, size );
586
587 // returns NULL, caller should throw an exception.
588 }
589 }
590 return Mem;
591 }
592
593 // This function always returns a valid DiscID -- using the Sony serial when possible, and
594 // falling back on the CRC checksum of the ELF binary if the PS2 software being run is
595 // homebrew or some other serial-less item.
596 wxString SysGetDiscID()
597 {
598 if( !DiscSerial.IsEmpty() ) return DiscSerial;
599
600 if( !ElfCRC )
601 {
602 // FIXME: system is currently running the BIOS, so it should return a serial based on
603 // the BIOS being run (either a checksum of the BIOS roms, and/or a string based on BIOS
604 // region and revision).
605
606 return wxEmptyString;
607 }
608
609 return pxsFmt( L"%08x", ElfCRC );
610 }

  ViewVC Help
Powered by ViewVC 1.1.22