https://wiki.pcsx2.net/api.php?action=feedcontributions&user=Krysto&feedformat=atomPCSX2 Wiki - User contributions [en]2024-03-28T18:00:59ZUser contributionsMediaWiki 1.41.0https://wiki.pcsx2.net/index.php?title=PCSX2_Documentation&diff=38715PCSX2 Documentation2015-07-22T20:09:41Z<p>Krysto: </p>
<hr />
<div>==Building and Compiling PCSX2==<br />
*[[PCSX2 Documentation/Compiling on Windows|Compiling on Windows]]<br />
*[[PCSX2 Documentation/Compiling on Linux|Compiling on Linux]]<br />
*[[PCSX2 Documentation/PCSX2 Build Scripts|PCSX2 Build Scripts]]<br />
*[[PCSX2 Documentation/Chroot and 64-bit Linux|Chroot and 64-bit Linux]]<br />
*[[PCSX2 Documentation/GNU Debugger Tips|GNU Debugger Tips]]<br />
*[[PCSX2 Documentation/Git Survival Guide|Git Survival Guide]]<br />
*[[PCSX2 Documentation/How to Create Useful and Valid Issues|How to Create Useful and Valid Issues]]<br />
*[[PCSX2 Documentation/Commenting Etiquette|Commenting Etiquette]]<br />
*[[PCSX2 Documentation/Code Formatting Guidelines|Code Formatting Guidelines]]<br />
*[[PCSX2 Documentation/Contributing To-Do List|Contributing To-Do List]]<br />
<br />
==WxWidgets Framework==<br />
*[[PCSX2 Documentation/Measuring the Benefits of wxWidgets|Measuring the Benefits of wxWidgets]]<br />
*[[PCSX2 Documentation/WxWidgets Coding Strategies|WxWidgets Coding Strategies]]<br />
*[[PCSX2 Documentation/The PCSX2 Program Flow|The PCSX2 Program Flow]]<br />
<br />
==Threading==<br />
*[[PCSX2 Documentation/Passing Settings Between Threads|Passing Settings Between Threads]]<br />
*[[PCSX2 Documentation/Threading Basics|Threading Basics]]<br />
*[[PCSX2 Documentation/Threading Advanced|Threading Advanced]]<br />
*[[PCSX2 Documentation/Thread Synchronization|Thread Synchronization]]<br />
*[[PCSX2 Documentation/Thread Counting|Thread Counting]]<br />
*[[PCSX2 Documentation/Benchmarking Multithreaded PCSX2|Benchmarking Multithreaded PCSX2]]<br />
*[[PCSX2 Documentation/Threading VU1|Threading VU1]]<br />
<br />
==PCSX2 Core==<br />
*[[PCSX2 Documentation/PCSX2 Optimization|PCSX2 Optimization]]<br />
*[[PCSX2 Documentation/So maybe it's about time we explained VTLB|So maybe it's about time we explained VTLB]]<br />
*[[PCSX2 Documentation/Events o' Plenty|Events o' Plenty]]<br />
*[[PCSX2 Documentation/Recompilers: All 'dems buzzwords?|Recompilers: All 'dems buzzwords?]]<br />
*[[PCSX2 Documentation/C++ exceptions can be an optimization|C++ exceptions can be an optimization]]<br />
*[[PCSX2 Documentation/What's clamping? And why do we need it?|What's clamping? And why do we need it?]]<br />
*[[PCSX2 Documentation/PS2 VU (Vector Unit) Documentation Part 1|PS2 VU (Vector Unit) Documentation Part 1]]<br />
*[[PCSX2 Documentation/A new kind of fullscreen!|A new kind of fullscreen!]]<br />
*[[PCSX2 Documentation/Introduction to Dynamic Recompilation|Introduction to Dynamic Recompilation]]<br />
*[[PCSX2 Documentation/The return of the Commandline!|The return of the Commandline!]]<br />
*[[PCSX2 Documentation/Advanced memory management|Advanced memory management]]<br />
*[[PCSX2 Documentation/VirtualAlloc on Linux|VirtualAlloc on Linux]]<br />
*[[PCSX2 Documentation/PS2's Programmable DMA|PS2's Programmable DMA]]<br />
*[[PCSX2 Documentation/MSVC 2008 optimizer fail|MSVC 2008 optimizer fail]]<br />
*[[PCSX2 Documentation/Path 3 Masking / Geometry Syncing|Path 3 Masking / Geometry Syncing]]<br />
*[[PCSX2 Documentation/MMU mini-series|MMU mini-series]]<br />
*[[PCSX2 Documentation/Virtual Memory|Virtual Memory]]<br />
*[[PCSX2 Documentation/Nightmare on Floating-Point Street|Nightmare on Floating-Point Street]]<br />
*[[PCSX2 Documentation/64-bit Recompilation|64-bit Recompilation]]<br />
<br />
==PCSX2 Plugins==<br />
*[[PCSX2 Documentation/PCSX2 Plugin Interface|PCSX2 Plugin Interface]]<br />
<br />
===Graphics Synthesizer===<br />
*[[PCSX2 Documentation/Graphics Synthesizer, GPUs and Dual Cores|Graphics Synthesizer, GPUs and Dual Cores]]<br />
*[[PCSX2 Documentation/GSdx Demystified|GSdx Demystified]]<br />
<br />
===Sound===<br />
*[[PCSX2 Documentation/SPU2 is more than just sound!|SPU2 is more than just sound!]]<br />
*[[PCSX2 Documentation/Reverb Engine|Reverb Engine]]<br />
<br />
==Translation Info==<br />
*[[PCSX2 Documentation/Supported Languages|Supported Languages]]<br />
*[[PCSX2 Documentation/Translation Guide|Translation Guide]]<br />
*[[PCSX2 Documentation/Using Poedit|Using Poedit]]<br />
<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation&diff=38712PCSX2 Documentation2015-07-22T20:08:27Z<p>Krysto: </p>
<hr />
<div>==Building and Compiling PCSX2==<br />
*[[PCSX2 Documentation/Compiling on Windows|Compiling on Windows]]<br />
*[[PCSX2 Documentation/Compiling on Linux|Compiling on Linux]]<br />
*[[PCSX2 Documentation/PCSX2 Build Scripts|PCSX2 Build Scripts]]<br />
*[[PCSX2 Documentation/Chroot and 64-bit Linux|Chroot and 64-bit Linux]]<br />
*[[PCSX2 Documentation/GNU Debugger Tips|GNU Debugger Tips]]<br />
*[[PCSX2 Documentation/Git Survival Guide|Git Survival Guide]]<br />
*[[PCSX2 Documentation/How to Create Useful and Valid Issues|How to Create Useful and Valid Issues]]<br />
*[[PCSX2 Documentation/Commenting Etiquette|Commenting Etiquette]]<br />
*[[PCSX2 Documentation/Code Formatting Guidelines|Code Formatting Guidelines]]<br />
*[[PCSX2 Documentation/Contributing To-Do List|Contributing To-Do List]]<br />
<br />
==WxWidgets Framework==<br />
*[[PCSX2 Documentation/Measuring the Benefits of wxWidgets|Measuring the Benefits of wxWidgets]]<br />
*[[PCSX2 Documentation/WxWidgets Coding Strategies|WxWidgets Coding Strategies]]<br />
*[[PCSX2 Documentation/The PCSX2 Program Flow|The PCSX2 Program Flow]]<br />
<br />
==Threading==<br />
*[[PCSX2 Documentation/Passing Settings Between Threads|Passing Settings Between Threads]]<br />
*[[PCSX2 Documentation/Threading Basics|Threading Basics]]<br />
*[[PCSX2 Documentation/Threading Advanced|Threading Advanced]]<br />
*[[PCSX2 Documentation/Thread Synchronization|Thread Synchronization]]<br />
*[[PCSX2 Documentation/Thread Counting|Thread Counting]]<br />
*[[PCSX2 Documentation/Benchmarking Multithreaded PCSX2|Benchmarking Multithreaded PCSX2]]<br />
*[[PCSX2 Documentation/Threading VU1|Threading VU1]]<br />
<br />
==PCSX2 Core==<br />
*[[PCSX2 Documentation/PCSX2 Optimization|PCSX2 Optimization]]<br />
*[[PCSX2 Documentation/So maybe it's about time we explained VTLB|So maybe it's about time we explained VTLB]]<br />
*[[PCSX2 Documentation/Events o' Plenty|Events o' Plenty]]<br />
*[[PCSX2 Documentation/Recompilers: All 'dems buzzwords?|Recompilers: All 'dems buzzwords?]]<br />
*[[PCSX2 Documentation/C++ exceptions can be an optimization|C++ exceptions can be an optimization]]<br />
*[[PCSX2 Documentation/What's clamping? And why do we need it?|What's clamping? And why do we need it?]]<br />
*[[PCSX2 Documentation/PS2 VU (Vector Unit) Documentation Part 1|PS2 VU (Vector Unit) Documentation Part 1]]<br />
*[[PCSX2 Documentation/A new kind of fullscreen!|A new kind of fullscreen!]]<br />
*[[PCSX2 Documentation/Introduction to Dynamic Recompilation|Introduction to Dynamic Recompilation]]<br />
*[[PCSX2 Documentation/The return of the Commandline!|The return of the Commandline!]]<br />
*[[PCSX2 Documentation/Advanced memory management|Advanced memory management]]<br />
*[[PCSX2 Documentation/VirtualAlloc on Linux|VirtualAlloc on Linux]]<br />
*[[PCSX2 Documentation/PS2's Programmable DMA|PS2's Programmable DMA]]<br />
*[[PCSX2 Documentation/MSVC 2008 optimizer fail|MSVC 2008 optimizer fail]]<br />
*[[PCSX2 Documentation/Path 3 Masking / Geometry Syncing|Path 3 Masking / Geometry Syncing]]<br />
*[[PCSX2 Documentation/MMU mini-series|MMU mini-series]]<br />
*[[PCSX2 Documentation/Virtual Memory|Virtual Memory]]<br />
*[[PCSX2 Documentation/Nightmare on Floating-Point Street|Nightmare on Floating-Point Street]]<br />
*[[PCSX2 Documentation/64-bit Recompilation|64-bit Recompilation]]<br />
<br />
==PCSX2 Plugins==<br />
===Graphics Synthesizer===<br />
*[[PCSX2 Documentation/Graphics Synthesizer, GPUs and Dual Cores|Graphics Synthesizer, GPUs and Dual Cores]]<br />
*[[PCSX2 Documentation/GSdx Demystified|GSdx Demystified]]<br />
<br />
===Sound===<br />
*[[PCSX2 Documentation/SPU2 is more than just sound!|SPU2 is more than just sound!]]<br />
*[[PCSX2 Documentation/Reverb Engine|Reverb Engine]]<br />
<br />
==Translation Info==<br />
*[[PCSX2 Documentation/Supported Languages|Supported Languages]]<br />
*[[PCSX2 Documentation/Translation Guide|Translation Guide]]<br />
*[[PCSX2 Documentation/Using Poedit|Using Poedit]]<br />
<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation&diff=38710PCSX2 Documentation2015-07-22T20:07:56Z<p>Krysto: </p>
<hr />
<div>==Building and Compiling PCSX2==<br />
*[[PCSX2 Documentation/Compiling on Windows|Compiling on Windows]]<br />
*[[PCSX2 Documentation/Compiling on Linux|Compiling on Linux]]<br />
*[[PCSX2 Documentation/PCSX2 Build Scripts|PCSX2 Build Scripts]]<br />
*[[PCSX2 Documentation/Chroot and 64-bit Linux|Chroot and 64-bit Linux]]<br />
*[[PCSX2 Documentation/GNU Debugger Tips|GNU Debugger Tips]]<br />
*[[PCSX2 Documentation/Git Survival Guide|Git Survival Guide]]<br />
*[[PCSX2 Documentation/How to Create Useful and Valid Issues|How to Create Useful and Valid Issues]]<br />
*[[PCSX2 Documentation/Commenting Etiquette|Commenting Etiquette]]<br />
*[[PCSX2 Documentation/Code Formatting Guidelines|Code Formatting Guidelines]]<br />
*[[PCSX2 Documentation/Contributing To-Do List|Contributing To-Do List]]<br />
<br />
==WxWidgets Framework==<br />
*[[PCSX2 Documentation/Measuring the Benefits of wxWidgets|Measuring the Benefits of wxWidgets]]<br />
*[[PCSX2 Documentation/WxWidgets Coding Strategies|WxWidgets Coding Strategies]]<br />
*[[PCSX2 Documentation/The PCSX2 Program Flow|The PCSX2 Program Flow]]<br />
<br />
==Threading==<br />
*[[PCSX2 Documentation/Passing Settings Between Threads|Passing Settings Between Threads]]<br />
*[[PCSX2 Documentation/Threading Basics|Threading Basics]]<br />
*[[PCSX2 Documentation/Threading Advanced|Threading Advanced]]<br />
*[[PCSX2 Documentation/Thread Synchronization|Thread Synchronization]]<br />
*[[PCSX2 Documentation/Thread Counting|Thread Counting]]<br />
*[[PCSX2 Documentation/Benchmarking Multithreaded PCSX2|Benchmarking Multithreaded PCSX2]]<br />
*[[PCSX2 Documentation/Threading VU1|Threading VU1]]<br />
<br />
==PCSX2 Core==<br />
*[[PCSX2 Documentation/PCSX2 Optimization|PCSX2 Optimization]]<br />
*[[PCSX2 Documentation/So maybe it's about time we explained VTLB|So maybe it's about time we explained VTLB]]<br />
*[[PCSX2 Documentation/Events o' Plenty|Events o' Plenty]]<br />
*[[PCSX2 Documentation/Recompilers: All 'dems buzzwords?|Recompilers: All 'dems buzzwords?]]<br />
*[[PCSX2 Documentation/C++ exceptions can be an optimization|C++ exceptions can be an optimization]]<br />
*[[PCSX2 Documentation/What's clamping? And why do we need it?|What's clamping? And why do we need it?]]<br />
*[[PCSX2 Documentation/PS2 VU (Vector Unit) Documentation Part 1|PS2 VU (Vector Unit) Documentation Part 1]]<br />
*[[PCSX2 Documentation/A new kind of fullscreen!|A new kind of fullscreen!]]<br />
*[[PCSX2 Documentation/Introduction to Dynamic Recompilation|Introduction to Dynamic Recompilation]]<br />
*[[PCSX2 Documentation/The return of the Commandline!|The return of the Commandline!]]<br />
*[[PCSX2 Documentation/Advanced memory management|Advanced memory management]]<br />
*[[PCSX2 Documentation/VirtualAlloc on Linux|VirtualAlloc on Linux]]<br />
*[[PCSX2 Documentation/PS2's Programmable DMA|PS2's Programmable DMA]]<br />
*[[PCSX2 Documentation/MSVC 2008 optimizer fail|MSVC 2008 optimizer fail]]<br />
*[[PCSX2 Documentation/Path 3 Masking / Geometry Syncing|Path 3 Masking / Geometry Syncing]]<br />
*[[PCSX2 Documentation/MMU mini-series|MMU mini-series]]<br />
*[[PCSX2 Documentation/Virtual Memory|Virtual Memory]]<br />
*[[PCSX2 Documentation/Nightmare on Floating-Point Street|Nightmare on Floating-Point Street]]<br />
*[[PCSX2 Documentation/64-bit Recompilation|64-bit Recompilation]]<br />
<br />
==PCSX2 Plugins==<br />
*[[PCSX2 Documentation/SPU2 is more than just sound!|SPU2 is more than just sound!]]<br />
*[[PCSX2 Documentation/Reverb Engine|Reverb Engine]]<br />
<br />
===Graphics Synthesizer===<br />
*[[PCSX2 Documentation/Graphics Synthesizer, GPUs and Dual Cores|Graphics Synthesizer, GPUs and Dual Cores]]<br />
*[[PCSX2 Documentation/GSdx Demystified|GSdx Demystified]]<br />
<br />
==Translation Info==<br />
*[[PCSX2 Documentation/Supported Languages|Supported Languages]]<br />
*[[PCSX2 Documentation/Translation Guide|Translation Guide]]<br />
*[[PCSX2 Documentation/Using Poedit|Using Poedit]]<br />
<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/The_PCSX2_Program_Flow&diff=38369PCSX2 Documentation/The PCSX2 Program Flow2015-07-19T20:22:51Z<p>Krysto: </p>
<hr />
<div>''The following article is a very general overview of the flow of the PCSX2 application. It's a bit sloppy right now, I will clean it up more as I go along.''<br />
<br />
==AppMain.cpp - It all starts here==<br />
There is a lot that happens under the hood with wxWidgets. We don't need to worry about that. All we need to worry about is this this line of code:<br />
<br />
'''in [https://github.com/PCSX2/pcsx2/blob/master/pcsx2/gui/AppMain.cpp AppMain.cpp]'''<br />
<source lang="cpp">IMPLEMENT_APP(Pcsx2App)</source><br />
<br />
This macro tells the wxWidgets framework that we want to fire up Pcsx2App. Easy, right?<br />
<br />
==Pcsx2App - the part we care about==<br />
Next, Let's take a look at this code here.<br />
<br />
<br />
'''in [https://github.com/PCSX2/pcsx2/blob/master/pcsx2/gui/App.h App.h]'''<br />
<source lang=cpp><br />
class Pcsx2App : public wxAppWithHelpers{}<br />
</source><br />
You can see here that the Pcsx2App class is an extension of the wxAppWithHelpers class. WxWidgets applications are defined as classes, which are instantiated into an object when we start the program. Pcsx2App contains methods that wxWidgets is going to call upon at various times. So what gets called when we fire up Pcsx2App? That would be OnInit(). Let's take a look, I have written some notes on what happens here:<br />
<br />
<br />
'''in [https://github.com/PCSX2/pcsx2/blob/master/pcsx2/gui/AppInit.cpp AppInit.cpp]'''<br />
<source lang="cpp"><br />
bool Pcsx2App::OnInit()<br />
{<br />
EnableAllLogging();<br />
Console.WriteLn("Interface is initializing. Entering Pcsx2App::OnInit!");<br />
</source><br />
Here we enable logging to the pcsx2 frame, and write some messages in it.<br />
<br />
<br />
<source lang="cpp"><br />
InitCPUTicks();<br />
</source><br />
Misc. windows performance benchmarking, defined [https://github.com/PCSX2/pcsx2/blob/e726f82344fa4e8c2e9d7be99364dbac35429499/common/src/Utilities/Windows/WinMisc.cpp here].<br />
<br />
<br />
<source lang="cpp"><br />
pxDoAssert = AppDoAssert;<br />
pxDoOutOfMemory = SysOutOfMemory_EmergencyResponse;<br />
</source><br />
These are function pointers for the pxThread, which is the main threading class for the PCSX2 virtual machine. Basically here we are telling pxThread how to handle certain function calls. For example, if <code>pxDoAssert</code> is called, then we will actually call <code>AppDoAssert</code>, which defined [https://github.com/PCSX2/pcsx2/blob/63ba78b6642b2cf617b4d7c9970a47b7038f492d/pcsx2/gui/AppAssert.cpp here]. Similarly, <code>pxDoOutOfMemory</code> will actually call <code>SysOutOfMemory_EmergencyResponse</code>, defined [https://github.com/PCSX2/pcsx2/blob/c5e6013d75057af0aa667788fbb30d2c024f9079/pcsx2/System.cpp here].<br />
<br />
<br />
<source lang="cpp"><br />
wxInitAllImageHandlers();<br />
<br />
Console.WriteLn("Applying operating system default language...");<br />
i18n_SetLanguage( wxLANGUAGE_DEFAULT );<br />
<br />
Console.WriteLn("Command line parsing...");<br />
if( !_parent::OnInit() ) return false;<br />
Console.WriteLn("Command line parsed!");<br />
<br />
i18n_SetLanguagePath();<br />
</source><br />
This is really straightforward. We're just doing some more initialization for the gui. We're creating an instance of AppConfig, which contains all kinds of options for wxWidgets. The declarations are [https://github.com/PCSX2/pcsx2/blob/e525f95f22bab06fa318d0a4e0feb1728b425a2f/pcsx2/gui/AppConfig.h here]. We're also setting the PCSX2 language to whatever the operating system language is.<br />
<br />
==WxWidgets Frames==<br />
<br />
In addition to Application classes, there are also Frame classes. So when we call the OpenMainFrame function, what happens? Take a look at this code:<br />
<br />
'''in [https://github.com/PCSX2/pcsx2/blob/master/pcsx2/gui/AppInit.cpp pcsx2/gui/AppInit.cpp]'''<br />
<source lang="cpp"><br />
void Pcsx2App::OpenMainFrame()<br />
{<br />
if( AppRpc_TryInvokeAsync( &Pcsx2App::OpenMainFrame ) ) return;<br />
<br />
if( GetMainFramePtr() != NULL ) return;<br />
<br />
MainEmuFrame* mainFrame = new MainEmuFrame( NULL, pxGetAppName() );<br />
<br />
...<br />
}<br />
</source><br />
<br />
Here we're creating an instance of the MainEmuFrame class. That class contains members for all the cool GUI elements, buttons, menus, stuff like that. So let's take a step further. Suppose we want to boot up a game. When we select Boot DVD in the pcsx2 menus, we are calling upon this function here:<br />
<br />
'''in [https://github.com/PCSX2/pcsx2/blob/f3bb434b27737849546290bbfc8d09c61103081c/pcsx2/gui/MainMenuClicks.cpp MainMenuClicks.cpp]'''<br />
<source lang="cpp"><br />
void MainEmuFrame::_DoBootCdvd()<br />
</source><br />
<br />
==Into The Core==<br />
OK, this is where things start to get a little complicated. PCSX2 is basically divided up into a few different parts:<br />
<br />
====SysExecutorThread====<br />
Handles the PCSX2 "Core" (Virtual Machine, GUI, etc). Instance of ExecutorThread, which is a "proxy". It basically queues up jobs and then distributes them to a worker thread.<br />
<br />
====SysMtgsThread====<br />
Handles the Graphics Synthesizer (GSdx or other plugins).<br />
<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/The_PCSX2_Program_Flow&diff=38368PCSX2 Documentation/The PCSX2 Program Flow2015-07-19T20:22:05Z<p>Krysto: </p>
<hr />
<div>''The following article is a very general overview of the flow of the PCSX2 application. It's a bit sloppy right now, I will clean it up more as I go along.''<br />
<br />
==AppMain.cpp - It all starts here==<br />
There is a lot that happens under the hood with wxWidgets. We don't need to worry about that. All we need to worry about is this this line of code:<br />
<br />
'''in [https://github.com/PCSX2/pcsx2/blob/master/pcsx2/gui/AppMain.cpp AppMain.cpp]'''<br />
<source lang="cpp">IMPLEMENT_APP(Pcsx2App)</source><br />
<br />
This macro tells the wxWidgets framework that we want to fire up Pcsx2App. Easy, right?<br />
<br />
==Pcsx2App - the part we care about==<br />
Next, Let's take a look at this code here.<br />
<br />
<br />
'''in [https://github.com/PCSX2/pcsx2/blob/master/pcsx2/gui/App.h App.h]'''<br />
<source lang=cpp><br />
class Pcsx2App : public wxAppWithHelpers{}<br />
</source><br />
You can see here that the Pcsx2App class is an extension of the wxAppWithHelpers class. WxWidgets applications are defined as classes, which are instantiated into an object when we start the program. Pcsx2App contains methods that wxWidgets is going to call upon at various times. So what gets called when we fire up Pcsx2App? That would be OnInit(). Let's take a look, I have written some notes on what happens here:<br />
<br />
<br />
<code>in [https://github.com/PCSX2/pcsx2/blob/master/pcsx2/gui/AppInit.cpp AppInit.cpp]</code><br />
<source lang="cpp"><br />
bool Pcsx2App::OnInit()<br />
{<br />
EnableAllLogging();<br />
Console.WriteLn("Interface is initializing. Entering Pcsx2App::OnInit!");<br />
</source><br />
Here we enable logging to the pcsx2 frame, and write some messages in it.<br />
<br />
<br />
<source lang="cpp"><br />
InitCPUTicks();<br />
</source><br />
Misc. windows performance benchmarking, defined [https://github.com/PCSX2/pcsx2/blob/e726f82344fa4e8c2e9d7be99364dbac35429499/common/src/Utilities/Windows/WinMisc.cpp here].<br />
<br />
<br />
<source lang="cpp"><br />
pxDoAssert = AppDoAssert;<br />
pxDoOutOfMemory = SysOutOfMemory_EmergencyResponse;<br />
</source><br />
These are function pointers for the pxThread, which is the main threading class for the PCSX2 virtual machine. Basically here we are telling pxThread how to handle certain function calls. For example, if <code>pxDoAssert</code> is called, then we will actually call <code>AppDoAssert</code>, which defined [https://github.com/PCSX2/pcsx2/blob/63ba78b6642b2cf617b4d7c9970a47b7038f492d/pcsx2/gui/AppAssert.cpp here]. Similarly, <code>pxDoOutOfMemory</code> will actually call <code>SysOutOfMemory_EmergencyResponse</code>, defined [https://github.com/PCSX2/pcsx2/blob/c5e6013d75057af0aa667788fbb30d2c024f9079/pcsx2/System.cpp here].<br />
<br />
<br />
<source lang="cpp"><br />
wxInitAllImageHandlers();<br />
<br />
Console.WriteLn("Applying operating system default language...");<br />
i18n_SetLanguage( wxLANGUAGE_DEFAULT );<br />
<br />
Console.WriteLn("Command line parsing...");<br />
if( !_parent::OnInit() ) return false;<br />
Console.WriteLn("Command line parsed!");<br />
<br />
i18n_SetLanguagePath();<br />
</source><br />
This is really straightforward. We're just doing some more initialization for the gui. We're creating an instance of AppConfig, which contains all kinds of options for wxWidgets. The declarations are [https://github.com/PCSX2/pcsx2/blob/e525f95f22bab06fa318d0a4e0feb1728b425a2f/pcsx2/gui/AppConfig.h here]. We're also setting the PCSX2 language to whatever the operating system language is.<br />
<br />
==WxWidgets Frames==<br />
<br />
In addition to Application classes, there are also Frame classes. So when we call the OpenMainFrame function, what happens? Take a look at this code:<br />
<br />
<code>in [https://github.com/PCSX2/pcsx2/blob/master/pcsx2/gui/AppInit.cpp pcsx2/gui/AppInit.cpp]</code><br />
<source lang="cpp"><br />
void Pcsx2App::OpenMainFrame()<br />
{<br />
if( AppRpc_TryInvokeAsync( &Pcsx2App::OpenMainFrame ) ) return;<br />
<br />
if( GetMainFramePtr() != NULL ) return;<br />
<br />
MainEmuFrame* mainFrame = new MainEmuFrame( NULL, pxGetAppName() );<br />
<br />
...<br />
}<br />
</source><br />
<br />
Here we're creating an instance of the MainEmuFrame class. That class contains members for all the cool GUI elements, buttons, menus, stuff like that. So let's take a step further. Suppose we want to boot up a game. When we select Boot DVD in the pcsx2 menus, we are calling upon this function here:<br />
<br />
<code>in [https://github.com/PCSX2/pcsx2/blob/f3bb434b27737849546290bbfc8d09c61103081c/pcsx2/gui/MainMenuClicks.cpp MainMenuClicks.cpp]</code><br />
<source lang="cpp"><br />
void MainEmuFrame::_DoBootCdvd()<br />
</source><br />
<br />
==Into The Core==<br />
OK, this is where things start to get a little complicated. PCSX2 is basically divided up into a few different parts:<br />
<br />
====SysExecutorThread====<br />
Handles the PCSX2 "Core" (Virtual Machine, GUI, etc). Instance of ExecutorThread, which is a "proxy". It basically queues up jobs and then distributes them to a worker thread.<br />
<br />
====SysMtgsThread====<br />
Handles the Graphics Synthesizer (GSdx or other plugins).<br />
<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/The_PCSX2_Program_Flow&diff=38367PCSX2 Documentation/The PCSX2 Program Flow2015-07-19T20:21:34Z<p>Krysto: </p>
<hr />
<div>''The following article is a very general overview of the flow of the PCSX2 application. It's a bit sloppy right now, I will clean it up more as I go along.''<br />
<br />
==AppMain.cpp - It all starts here==<br />
There is a lot that happens under the hood with wxWidgets. We don't need to worry about that. All we need to worry about is this this line of code:<br />
<br />
<code>in [https://github.com/PCSX2/pcsx2/blob/master/pcsx2/gui/AppMain.cpp AppMain.cpp]</code><br />
<source lang="cpp">IMPLEMENT_APP(Pcsx2App)</source><br />
<br />
This macro tells the wxWidgets framework that we want to fire up Pcsx2App. Easy, right?<br />
<br />
==Pcsx2App - the part we care about==<br />
Next, Let's take a look at this code here.<br />
<br />
<br />
<code>in [https://github.com/PCSX2/pcsx2/blob/master/pcsx2/gui/App.h App.h]</code><br />
<source lang=cpp><br />
class Pcsx2App : public wxAppWithHelpers{}<br />
</source><br />
You can see here that the Pcsx2App class is an extension of the wxAppWithHelpers class. WxWidgets applications are defined as classes, which are instantiated into an object when we start the program. Pcsx2App contains methods that wxWidgets is going to call upon at various times. So what gets called when we fire up Pcsx2App? That would be OnInit(). Let's take a look, I have written some notes on what happens here:<br />
<br />
<br />
<code>in [https://github.com/PCSX2/pcsx2/blob/master/pcsx2/gui/AppInit.cpp AppInit.cpp]</code><br />
<source lang="cpp"><br />
bool Pcsx2App::OnInit()<br />
{<br />
EnableAllLogging();<br />
Console.WriteLn("Interface is initializing. Entering Pcsx2App::OnInit!");<br />
</source><br />
Here we enable logging to the pcsx2 frame, and write some messages in it.<br />
<br />
<br />
<source lang="cpp"><br />
InitCPUTicks();<br />
</source><br />
Misc. windows performance benchmarking, defined [https://github.com/PCSX2/pcsx2/blob/e726f82344fa4e8c2e9d7be99364dbac35429499/common/src/Utilities/Windows/WinMisc.cpp here].<br />
<br />
<br />
<source lang="cpp"><br />
pxDoAssert = AppDoAssert;<br />
pxDoOutOfMemory = SysOutOfMemory_EmergencyResponse;<br />
</source><br />
These are function pointers for the pxThread, which is the main threading class for the PCSX2 virtual machine. Basically here we are telling pxThread how to handle certain function calls. For example, if <code>pxDoAssert</code> is called, then we will actually call <code>AppDoAssert</code>, which defined [https://github.com/PCSX2/pcsx2/blob/63ba78b6642b2cf617b4d7c9970a47b7038f492d/pcsx2/gui/AppAssert.cpp here]. Similarly, <code>pxDoOutOfMemory</code> will actually call <code>SysOutOfMemory_EmergencyResponse</code>, defined [https://github.com/PCSX2/pcsx2/blob/c5e6013d75057af0aa667788fbb30d2c024f9079/pcsx2/System.cpp here].<br />
<br />
<br />
<source lang="cpp"><br />
wxInitAllImageHandlers();<br />
<br />
Console.WriteLn("Applying operating system default language...");<br />
i18n_SetLanguage( wxLANGUAGE_DEFAULT );<br />
<br />
Console.WriteLn("Command line parsing...");<br />
if( !_parent::OnInit() ) return false;<br />
Console.WriteLn("Command line parsed!");<br />
<br />
i18n_SetLanguagePath();<br />
</source><br />
This is really straightforward. We're just doing some more initialization for the gui. We're creating an instance of AppConfig, which contains all kinds of options for wxWidgets. The declarations are [https://github.com/PCSX2/pcsx2/blob/e525f95f22bab06fa318d0a4e0feb1728b425a2f/pcsx2/gui/AppConfig.h here]. We're also setting the PCSX2 language to whatever the operating system language is.<br />
<br />
==WxWidgets Frames==<br />
<br />
In addition to Application classes, there are also Frame classes. So when we call the OpenMainFrame function, what happens? Take a look at this code:<br />
<br />
<code>in [https://github.com/PCSX2/pcsx2/blob/master/pcsx2/gui/AppInit.cpp pcsx2/gui/AppInit.cpp]</code><br />
<source lang="cpp"><br />
void Pcsx2App::OpenMainFrame()<br />
{<br />
if( AppRpc_TryInvokeAsync( &Pcsx2App::OpenMainFrame ) ) return;<br />
<br />
if( GetMainFramePtr() != NULL ) return;<br />
<br />
MainEmuFrame* mainFrame = new MainEmuFrame( NULL, pxGetAppName() );<br />
<br />
...<br />
}<br />
</source><br />
<br />
Here we're creating an instance of the MainEmuFrame class. That class contains members for all the cool GUI elements, buttons, menus, stuff like that. So let's take a step further. Suppose we want to boot up a game. When we select Boot DVD in the pcsx2 menus, we are calling upon this function here:<br />
<br />
<code>in [https://github.com/PCSX2/pcsx2/blob/f3bb434b27737849546290bbfc8d09c61103081c/pcsx2/gui/MainMenuClicks.cpp MainMenuClicks.cpp]</code><br />
<source lang="cpp"><br />
void MainEmuFrame::_DoBootCdvd()<br />
</source><br />
<br />
==Into The Core==<br />
OK, this is where things start to get a little complicated. PCSX2 is basically divided up into a few different parts:<br />
<br />
====SysExecutorThread====<br />
Handles the PCSX2 "Core" (Virtual Machine, GUI, etc). Instance of ExecutorThread, which is a "proxy". It basically queues up jobs and then distributes them to a worker thread.<br />
<br />
====SysMtgsThread====<br />
Handles the Graphics Synthesizer (GSdx or other plugins).<br />
<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/Threading_Advanced&diff=38366PCSX2 Documentation/Threading Advanced2015-07-19T20:10:03Z<p>Krysto: </p>
<hr />
<div>''Threading In-Depth''<br />
<br />
For definitions of Mutex, Semaphore, and Atomic Operations, please use google or see our [[PCSX2_Documentation/Threading Basics | Threading Basics]] section. This section will cover advanced threading concepts that are important for allowing a program like PCSX2 to be able to operate smoothly -- responsive to the user and relatively deadlock-free. If you plan to be doing programming work on PCSX2 user interfaces or virtual machine management, then this section will be an important read.<br />
<br />
==Avoiding Unresponsiveness and Deadlock==<br />
In order to make an application as robust against deadlock as possible, it should adhere to the following basic rules:<br />
<br />
#There must be at least one thread in the application that does not depend on any other thread. This is typically called the Main or UI thread. (dependency is defined below)<br />
#Communication between the Main/UI thread and other worker threads should be conducted through a proxy thread that simply queues messages from the main threads, and re-dispatches messages to workers.<br />
<br />
==Thread Dependency Defined==<br />
Thread 'A' is said to be dependent on thread 'B' if it has any shared mutex or semaphore with thread 'B'. If thread 'B' locks a multi-threaded resource (mutex or semaphore) indefinitely, and Thread 'A' attempts to acquire the same resource, Thread A will also stall. If thread 'B' thread deadlocks while it has acquired a mutex or sempahore, thread 'A' will also deadlock when it tries to acquire the resource.<br />
<br />
This is why a Main/UI thread must be as free as possible of mutexes and semaphores. If it stalls on a mutex that is acquired by another thread that is busy or deadlocked, the main thread will stall or deadlock itself.<br />
<br />
==Proxy Queue Threads==<br />
Having our main thread completely avoid the use of mutexes and semaphores, however, is an unrealistic impossibility. There would be no way for it to communicate with other threads without them. So how do we make the main/ui thread not be dependent on our stall-prone virtual machine threads? By using a proxy queue thread to break the dependency chain!<br />
<br />
A proxy queue thread works as a safety valve because the task it performs is simple enough that we can guarantee it will not deadlock (or at least, if a deadlock does occur its because of some fairly critical system failure). It only locks shared resources -- a message queue list -- for extremely short periods of time, and only to add and remove messages. When the proxy thread forwards messages to the virtual machine, the two threads share a common resource (the virtual machine's message queue); which means that if the VM thread is deadlocked, the proxy thread may deadlock as well. But the proxy thread's own message queue, which it shares with the Main/UI thread, will be unlocked. Thusly, the Main thread does not risk deadlock.<br />
<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/Threading_Advanced&diff=38365PCSX2 Documentation/Threading Advanced2015-07-19T20:09:49Z<p>Krysto: </p>
<hr />
<div>''Threading In-Depth''<br />
<br />
For definitions of Mutex, Semaphore, and Atomic Operations, please use google or see our [[PCSX2_Documentation/Threading Basics]] section. This section will cover advanced threading concepts that are important for allowing a program like PCSX2 to be able to operate smoothly -- responsive to the user and relatively deadlock-free. If you plan to be doing programming work on PCSX2 user interfaces or virtual machine management, then this section will be an important read.<br />
<br />
==Avoiding Unresponsiveness and Deadlock==<br />
In order to make an application as robust against deadlock as possible, it should adhere to the following basic rules:<br />
<br />
#There must be at least one thread in the application that does not depend on any other thread. This is typically called the Main or UI thread. (dependency is defined below)<br />
#Communication between the Main/UI thread and other worker threads should be conducted through a proxy thread that simply queues messages from the main threads, and re-dispatches messages to workers.<br />
<br />
==Thread Dependency Defined==<br />
Thread 'A' is said to be dependent on thread 'B' if it has any shared mutex or semaphore with thread 'B'. If thread 'B' locks a multi-threaded resource (mutex or semaphore) indefinitely, and Thread 'A' attempts to acquire the same resource, Thread A will also stall. If thread 'B' thread deadlocks while it has acquired a mutex or sempahore, thread 'A' will also deadlock when it tries to acquire the resource.<br />
<br />
This is why a Main/UI thread must be as free as possible of mutexes and semaphores. If it stalls on a mutex that is acquired by another thread that is busy or deadlocked, the main thread will stall or deadlock itself.<br />
<br />
==Proxy Queue Threads==<br />
Having our main thread completely avoid the use of mutexes and semaphores, however, is an unrealistic impossibility. There would be no way for it to communicate with other threads without them. So how do we make the main/ui thread not be dependent on our stall-prone virtual machine threads? By using a proxy queue thread to break the dependency chain!<br />
<br />
A proxy queue thread works as a safety valve because the task it performs is simple enough that we can guarantee it will not deadlock (or at least, if a deadlock does occur its because of some fairly critical system failure). It only locks shared resources -- a message queue list -- for extremely short periods of time, and only to add and remove messages. When the proxy thread forwards messages to the virtual machine, the two threads share a common resource (the virtual machine's message queue); which means that if the VM thread is deadlocked, the proxy thread may deadlock as well. But the proxy thread's own message queue, which it shares with the Main/UI thread, will be unlocked. Thusly, the Main thread does not risk deadlock.<br />
<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/Using_Poedit&diff=38364PCSX2 Documentation/Using Poedit2015-07-19T20:08:23Z<p>Krysto: </p>
<hr />
<div>''This wiki page is a work-in-progress. Information presented here may be partial or incomplete.''<br />
<br />
When working with PCSX2 translations, Poedit is not only recommended, but is nearly a necessity. You will need to reference the PCSX2 source code frequently if you want to make accurate translations, and Poedit is the only PO editor I could find that has such a feature built-in (and actually works as expected).<br />
<br />
Important!! Make sure that you check out the right version/revision of PCSX2 that corresponds to the PO file you've downloaded.<br />
<br />
==Download and Install Poedit==<br />
http://www.poedit.net/download.php<br />
<br />
==Download the latest POT files provided by us.==<br />
The latest stable release POT file pack can be found here: https://code.google.com/p/pcsx2/downloads/list ('''Warning''': this link has expired and as of may 2015 there are no alternatives)<br />
<br />
(look for the one labeled PCSX2_POT with some extra version information)<br />
<br />
You may also download the POT files individually from our SVN server. POT files are housed in the /locales/templates/ folder. The latest templates for the active trunk/devel branch of PCSX2 can be found here: https://pcsx2.googlecode.com/svn/trunk/locales/templates/. Note that working from the trunk POTs is not recommended, except in cases where it has been quite a while since the latest stable or beta releases, or when the PCSX2 developers have entered Release Candidate stages for an upcoming stable release. In most other cases you will want to check out a stable release branch instead, and use the templates from that.<br />
<br />
As of the last update to this wiki, the latest POT file pack is the 0.9.8 release translation POT.<br />
<br />
==Check out PCSX2 sources==<br />
Make sure to check out the exact revision indicated by the POT archive! This is important since the source code associations listed in the POT file may not match newer revisions of PCSX2, making it difficult to resolve iconized identifiers. The PCSX2 revision can be gleaned from the comment section at the top of the POT files included in the archive.<br />
<br />
Note that Poedit does have the ability to update the translation table to match a newer revision of PCSX2. As a translator, you are welcome to update the translation table and get a head start on translating for an upcoming version of PCSX2. However, translations for public/stable releases of PCSX2 should be based on the POT files provided by the PCSX2 Team.<br />
<br />
==Configure Poedit==<br />
Go to File->New Catalog from POT file..., and select one of the POT files provided in the package you downloaded in Step 2. You will see the following dialog:<br />
<br />
[[File:poedit-info.png]]<br />
<br />
The project name and version info will be provided by the POT files. Do not modify it! All other blanks can be filled out accordingly, as indicated above. For the Language option, be sure to select the name of the language you're translating to.<br />
<br />
Supported languages are hard-coded into wxWidgets, and PCSX2 cannot support anything else at this time. (so no Klingon... yet!) When you click OK you will be prompted for a save location for your new .PO file. Save it into your installed PCSX2 location, under Langs/canonical_code/ (create the Langs folder if it does not already exist). You can determine your language's canonical code using our SupportedLanguagesChart.<br />
<br />
Make sure that the filename matches the original filename in the archive (see above for a list of PO files that PCSX2 looks for). Poedit will then create both PO and MO files for you. The PO file is the one you will later submit to the PCSX2 team.<br />
<br />
==Testing your translation!==<br />
Poedit will have saved a MO file in your PCSX2 Langs folder in Step 4, and if the MO file is in the right place and has the right filename, PCSX2 will automatically detect it and offer your language as an option. Run PCSX2 and change the current language to the one you've just created. You may have to restart PCSX2 for the new changes to take effect in all areas of the program.<br />
<br />
==Submitting Your Translated Work==<br />
Before starting a translation, please apply for it in this thread (if there has been no other applicant before you): http://forums.pcsx2.net/Thread-Program-and-Guide-translation-applications<br />
<br />
When you're done, you will be given the rights to post in the specific forum for the language you translated into in our board. There you can attach either the actual PO file archive you have created, or a patchfile that can be applied against the current svn copy of the translation.<br />
<br />
For those of you who are familiar and comfortable with Subversion (SVN): The PCSX2 Team hosts all available translations from our GIT repository. Available translations and their submission history can be viewed online at http://web.archive.org/web/20120418041748/http://pcsx2.googlecode.com/svn/trunk/locales/. You can also use an Svn tool such as TortoiseSvn to perform more detailed commit history analysis.<br />
<br />
Translations are branched according to the releases of PCSX2 that they conform to.<br />
<br />
<br />
{{PCSX2 Main Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/Translation_Guide&diff=38363PCSX2 Documentation/Translation Guide2015-07-19T20:06:22Z<p>Krysto: </p>
<hr />
<div>==PCSX2 and GNU gettext Translations==<br />
PCSX2 uses an enhanced version of the wxWidgets internationalization module, which itself is based around GNU's gettext system. I'll cover the basics of gettext first, and then apply the PCSX2 enhancements afterward.<br />
<br />
==How gettext Works (sorta)==<br />
GNU gettext works by taking a program which is written in English, and finds the matching translated message in a language file (which is what you as a translator would be making). This means that the actual English version of each message is specified directly in the code itself where the message is used, like so:<br />
<br />
<source lang="cpp"><br />
MessageBox( _('PCSX2 Critical Error'), _('Your game sucks and we refuse to emulate it properly.') );<br />
</source><br />
<br />
... in the above example the _() portion is what invokes the GNU gettext translator, and the strings inside the _() are what gettext uses to look up the translation. The translation is stored in a language file which is given a *.mo extension type, and this file is created from a human-readable text database (clled a *.po file) which is typically generated by the gettext tool itself, and then updated by hand by the translator, using a text editor.<br />
<br />
Fortunately there are tools now available to make editing .po files easier. The recommended tool for creating and updating .po files is Poedit. It's an open source app that makes using gettext (usually a terse command line tool) almost a pleasurable experience. Since I personally have no earthly idea how to use gettext, and have only ever used Poedit, the rest of this quick tutorial will read as if Poedit is the only way to actually get the job done.<br />
<br />
See our UsingPoedit page for details on how to download, install, and configure Poedit.<br />
<br />
==PCSX2-specific Extension: Iconized gettext Identifiers==<br />
The gettext system has many conveniences over other methods of internationalization, which typically rely on the use of pre-processor macros or dynamically linked functions that return string values. The main drawback is that this method relies on the exact English representation, including punctuation, which means that even the slightest change to a text string in PCSX2 will break the translation for that string. For most short messages, such as menu items and button labels, this is fine and is in fact desirable behavior. But for longer messages that serve as descriptions of actions or checkbox options, some of which include formating to fit them to the dialog box more neatly, it can be problematic. This is why PCSX2 has an extension to gettext that uses iconized string matching instead ot the usual literal string matching.<br />
<br />
==How String Iconization Works==<br />
For especially long strings (such as dialog box option descriptions), or strings that contain newline or tab formatting, PCSX2 will use an iconized identifier for it's gettext lookup. These are identified in the gettext database of messages via their '!' prefix, which was chosen to help search and sort for these messages. Because this is a special PCSX2 extension to GNU gettext, you will need to reference the PCSX2 source code when translating them. You should probably be referencing source code during translation as a generla habit anyway, since the source code gives good hints as to the context of messages.<br />
<br />
When referencing the source code, these iconized translation messages are used like so:<br />
<br />
<source lang="cpp"><br />
someCheckBox->SetToolTip( pxE( "!ContextTip:SomeOption", L"This is some option which will make your game run a lot faster, but probably makes it uglier too." ) );<br />
</source><br />
The first string is the icon, which reads "!Tooltips:SomeOption". The second string is the current English literal translation. Poedit will show you the iconized version in its messages list, and you will need to open the source code reference for it to see what the current English translation is. It is important that your Poedit options are set up properly so that viewing the PCSX2 program source code works.<br />
<br />
Message icons use a somewhat standard naming convention which will help you decide what's important for translating and/or what sort of formatting you should apply to the message. The common classes are:<br />
*Popup - Messages that are popped up to the user for errors or confirmations. Very High Priority!<br />
*Panel - This is a top-level message on a dialog box or panel. These have a high translation priority.<br />
*Tooltip - These are tips that appear over toolbar buttons. Since button images are rarely fully self-explanatory these types of messages should be translated, so they have a high translation priority. (typically most or all such tooltips won't be iconized anyway, but I'm leaving the door open in case some would be better suited as such).<br />
*ContextTip - This is a popup box that gives additional extra information or details about various options on dialog boxes. Because these are usually supplementary to the dialog box option name and top-level description, translating them is not really necessary for someone to use the program. Translation priority is low.<br />
Compile/Generate pot/po/mo file on linux<br />
*A script (generate_pot.sh) is provided to generates all files on linux. The script does the 3 following steps:<br />
*#xgettext -> parse all source files then build the several pot files.<br />
*#msgmerge -> sync already translated po files with the previous pot generated. It would drop old string, add new ones and fuzzy string which was updated.<br />
*#msgfmt -> compile the po files into mo files then install the results into bin/Langs directory.<br />
To update/upload a new translation, it is as easy as copy the new po into the locales directory, call the script, commit the result.<br />
<br />
==Downloading and Using POT files==<br />
The latest stable release POT file pack can be found here: https://code.google.com/p/pcsx2/downloads/list ('''Warning''': this link has expired and as of may 2015 there are no alternatives)<br />
<br />
(look for the one labeled PCSX2_POT with some extra version information)<br />
<br />
You may also download the POT files individually from our SVN server. POT files are housed in the /locales/templates/ folder. The latest templates for the active trunk/devel branch of PCSX2 can be found here: http://web.archive.org/web/20141024194015/https://pcsx2.googlecode.com/svn/trunk/locales/templates/. Note that working from the trunk POTs is not recommended, except in cases where it has been quite a while since the latest stable or beta releases, or when the PCSX2 developers have entered Release Candidiate stages for an upcoming stable release. In most other cases you will want to check out a stable release branch instead, and use the templates from that.<br />
<br />
As of the last update to this wiki, the latest POT file pack is the 0.9.8 release translation POT.<br />
<br />
==The Multiple POT files in PCSX2==<br />
To assist translators, PCSX2 text messages have been split into a handful of different POT files, as follows:<br />
*pcsx2_main - menu items, buttons, and most labels. High priority; most or all of this file should be translated for a translation to be considered useful.<br />
*pcsx2_iconized - iconized notices, popups, and errors. Most of these are also very high priority; and most or all of this file should be translated for a translation to be considered useful.<br />
*pcsx2_tertiary - Context tips, wizards, and other non-critical user interface elements. All of these strings are also in iconized form.<br />
*pcsx2_devel - Strings used only by devel and debug builds of PCSX2. These mostly consist of loggig and menu options available only in development builds (and excluded from release builds for performance reasons), and tooltips associated with them. Translating these items is purely optional -- most people will never know or care about them.<br />
<br />
Only the main PO is required for PCSX2 to detect and use a language. Iconized and tertiary POs are optional. For strings in the iconized and tertiary POTs, you will have to reference the source code to obtain the original English text. It is highly recommended that you use Poedit, since it has a very nice code reference feature built-in. All strings in pcsx2_main.pot are standard gettext strings, and can be translated easily using any regular text editor.<br />
<br />
Future versions of PCSX2 may divide the POT files differently.<br />
<br />
==Submitting your contribution==<br />
Before starting a translation, please apply for it in this thread (if there has been no other applicant before you): http://forums.pcsx2.net/Thread-Program-and-Guide-translation-applications<br />
<br />
When you're done, you will be given the rights to post in the specific forum for the language you translated into in our board. There you can attach either the actual PO file archive you have created, or a patchfile that can be applied against the current svn copy of the translation.<br />
<br />
For those of you who are familiar and comfortable with Subversion (SVN): The PCSX2 Team hosts all available translations from our googlecode repository. Available translations and their submission history can be viewed online at https://pcsx2.googlecode.com/svn/trunk/locales/. You can also use an Svn tool such as TortoiseSvn to perform more detailed commit history analysis.<br />
<br />
Translations are branched according to the releases of PCSX2 that they conform to.<br />
<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/Translation_Guide&diff=38362PCSX2 Documentation/Translation Guide2015-07-19T20:05:39Z<p>Krysto: </p>
<hr />
<div>==PCSX2 and GNU gettext Translations==<br />
PCSX2 uses an enhanced version of the wxWidgets internationalization module, which itself is based around GNU's gettext system. I'll cover the basics of gettext first, and then apply the PCSX2 enhancements afterward.<br />
<br />
==How gettext Works (sorta)==<br />
GNU gettext works by taking a program which is written in English, and finds the matching translated message in a language file (which is what you as a translator would be making). This means that the actual English version of each message is specified directly in the code itself where the message is used, like so:<br />
<br />
<source lang="cpp"><br />
MessageBox( _('PCSX2 Critical Error'), _('Your game sucks and we refuse to emulate it properly.') );<br />
</source><br />
<br />
... in the above example the _() portion is what invokes the GNU gettext translator, and the strings inside the _() are what gettext uses to look up the translation. The translation is stored in a language file which is given a *.mo extension type, and this file is created from a human-readable text database (clled a *.po file) which is typically generated by the gettext tool itself, and then updated by hand by the translator, using a text editor.<br />
<br />
Fortunately there are tools now available to make editing .po files easier. The recommended tool for creating and updating .po files is Poedit. It's an open source app that makes using gettext (usually a terse command line tool) almost a pleasurable experience. Since I personally have no earthly idea how to use gettext, and have only ever used Poedit, the rest of this quick tutorial will read as if Poedit is the only way to actually get the job done.<br />
<br />
See our UsingPoedit page for details on how to download, install, and configure Poedit.<br />
<br />
==PCSX2-specific Extension: Iconized gettext Identifiers==<br />
The gettext system has many conveniences over other methods of internationalization, which typically rely on the use of pre-processor macros or dynamically linked functions that return string values. The main drawback is that this method relies on the exact English representation, including punctuation, which means that even the slightest change to a text string in PCSX2 will break the translation for that string. For most short messages, such as menu items and button labels, this is fine and is in fact desirable behavior. But for longer messages that serve as descriptions of actions or checkbox options, some of which include formating to fit them to the dialog box more neatly, it can be problematic. This is why PCSX2 has an extension to gettext that uses iconized string matching instead ot the usual literal string matching.<br />
<br />
==How String Iconization Works==<br />
For especially long strings (such as dialog box option descriptions), or strings that contain newline or tab formatting, PCSX2 will use an iconized identifier for it's gettext lookup. These are identified in the gettext database of messages via their '!' prefix, which was chosen to help search and sort for these messages. Because this is a special PCSX2 extension to GNU gettext, you will need to reference the PCSX2 source code when translating them. You should probably be referencing source code during translation as a generla habit anyway, since the source code gives good hints as to the context of messages.<br />
<br />
When referencing the source code, these iconized translation messages are used like so:<br />
<br />
<source lang="cpp"><br />
someCheckBox->SetToolTip( pxE( "!ContextTip:SomeOption", L"This is some option which will make your game run a lot faster, but probably makes it uglier too." ) );<br />
</source><br />
The first string is the icon, which reads "!Tooltips:SomeOption". The second string is the current English literal translation. Poedit will show you the iconized version in its messages list, and you will need to open the source code reference for it to see what the current English translation is. It is important that your Poedit options are set up properly so that viewing the PCSX2 program source code works.<br />
<br />
Message icons use a somewhat standard naming convention which will help you decide what's important for translating and/or what sort of formatting you should apply to the message. The common classes are:<br />
*Popup - Messages that are popped up to the user for errors or confirmations. Very High Priority!<br />
*Panel - This is a top-level message on a dialog box or panel. These have a high translation priority.<br />
*Tooltip - These are tips that appear over toolbar buttons. Since button images are rarely fully self-explanatory these types of messages should be translated, so they have a high translation priority. (typically most or all such tooltips won't be iconized anyway, but I'm leaving the door open in case some would be better suited as such).<br />
*ContextTip - This is a popup box that gives additional extra information or details about various options on dialog boxes. Because these are usually supplementary to the dialog box option name and top-level description, translating them is not really necessary for someone to use the program. Translation priority is low.<br />
Compile/Generate pot/po/mo file on linux<br />
*A script (generate_pot.sh) is provided to generates all files on linux. The script does the 3 following steps:<br />
*#xgettext -> parse all source files then build the several pot files.<br />
*#msgmerge -> sync already translated po files with the previous pot generated. It would drop old string, add new ones and fuzzy string which was updated.<br />
*#msgfmt -> compile the po files into mo files then install the results into bin/Langs directory.<br />
To update/upload a new translation, it is as easy as copy the new po into the locales directory, call the script, commit the result.<br />
<br />
==Downloading and Using POT files==<br />
The latest stable release POT file pack can be found here: https://code.google.com/p/pcsx2/downloads/list ('''Warning''': this link has expired and as of may 2015 there are no alternatives)<br />
<br />
(look for the one labeled PCSX2_POT with some extra version information)<br />
<br />
You may also download the POT files individually from our SVN server. POT files are housed in the /locales/templates/ folder. The latest templates for the active trunk/devel branch of PCSX2 can be found here: http://web.archive.org/web/20141024194015/https://pcsx2.googlecode.com/svn/trunk/locales/templates/. Note that working from the trunk POTs is not recommended, except in cases where it has been quite a while since the latest stable or beta releases, or when the PCSX2 developers have entered Release Candidiate stages for an upcoming stable release. In most other cases you will want to check out a stable release branch instead, and use the templates from that.<br />
<br />
As of the last update to this wiki, the latest POT file pack is the 0.9.8 release translation POT.<br />
<br />
==The Multiple POT files in PCSX2==<br />
To assist translators, PCSX2 text messages have been split into a handful of different POT files, as follows:<br />
*pcsx2_main - menu items, buttons, and most labels. High priority; most or all of this file should be translated for a translation to be considered useful.<br />
*pcsx2_iconized - iconized notices, popups, and errors. Most of these are also very high priority; and most or all of this file should be translated for a translation to be considered useful.<br />
*pcsx2_tertiary - Context tips, wizards, and other non-critical user interface elements. All of these strings are also in iconized form.<br />
*pcsx2_devel - Strings used only by devel and debug builds of PCSX2. These mostly consist of loggig and menu options available only in development builds (and excluded from release builds for performance reasons), and tooltips associated with them. Translating these items is purely optional -- most people will never know or care about them.<br />
<br />
Only the main PO is required for PCSX2 to detect and use a language. Iconized and tertiary POs are optional. For strings in the iconized and tertiary POTs, you will have to reference the source code to obtain the original English text. It is highly recommended that you use Poedit, since it has a very nice code reference feature built-in. All strings in pcsx2_main.pot are standard gettext strings, and can be translated easily using any regular text editor.<br />
<br />
Future versions of PCSX2 may divide the POT files differently.<br />
<br />
==Submitting your contribution==<br />
Before starting a translation, please apply for it in this thread (if there has been no other applicant before you): http://forums.pcsx2.net/Thread-Program-and-Guide-translation-applications<br />
<br />
When you're done, you will be given the rights to post in the specific forum for the language you translated into in our board. There you can attach either the actual PO file archive you have created, or a patchfile that can be applied against the current svn copy of the translation.<br />
<br />
For those of you who are familiar and comfortable with Subversion (SVN): The PCSX2 Team hosts all available translations from our googlecode repository. Available translations and their submission history can be viewed online at https://pcsx2.googlecode.com/svn/trunk/locales/. You can also use an Svn tool such as TortoiseSvn to perform more detailed commit history analysis.<br />
<br />
Translations are branched according to the releases of PCSX2 that they conform to.<br />
<br />
<br />
{{PCSX2 Main Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/Translation_Guide&diff=38361PCSX2 Documentation/Translation Guide2015-07-19T20:03:28Z<p>Krysto: </p>
<hr />
<div>==PCSX2 and GNU gettext Translations==<br />
PCSX2 uses an enhanced version of the wxWidgets internationalization module, which itself is based around GNU's gettext system. I'll cover the basics of gettext first, and then apply the PCSX2 enhancements afterward.<br />
<br />
==How gettext Works (sorta)==<br />
GNU gettext works by taking a program which is written in English, and finds the matching translated message in a language file (which is what you as a translator would be making). This means that the actual English version of each message is specified directly in the code itself where the message is used, like so:<br />
<br />
<source lang="cpp"><br />
MessageBox( _('PCSX2 Critical Error'), _('Your game sucks and we refuse to emulate it properly.') );<br />
</source><br />
<br />
... in the above example the _() portion is what invokes the GNU gettext translator, and the strings inside the _() are what gettext uses to look up the translation. The translation is stored in a language file which is given a *.mo extension type, and this file is created from a human-readable text database (clled a *.po file) which is typically generated by the gettext tool itself, and then updated by hand by the translator, using a text editor.<br />
<br />
Fortunately there are tools now available to make editing .po files easier. The recommended tool for creating and updating .po files is Poedit. It's an open source app that makes using gettext (usually a terse command line tool) almost a pleasurable experience. Since I personally have no earthly idea how to use gettext, and have only ever used Poedit, the rest of this quick tutorial will read as if Poedit is the only way to actually get the job done.<br />
<br />
See our UsingPoedit page for details on how to download, install, and configure Poedit.<br />
<br />
==PCSX2-specific Extension: Iconized gettext Identifiers==<br />
The gettext system has many conveniences over other methods of internationalization, which typically rely on the use of pre-processor macros or dynamically linked functions that return string values. The main drawback is that this method relies on the exact English representation, including punctuation, which means that even the slightest change to a text string in PCSX2 will break the translation for that string. For most short messages, such as menu items and button labels, this is fine and is in fact desirable behavior. But for longer messages that serve as descriptions of actions or checkbox options, some of which include formating to fit them to the dialog box more neatly, it can be problematic. This is why PCSX2 has an extension to gettext that uses iconized string matching instead ot the usual literal string matching.<br />
<br />
==How String Iconization Works==<br />
For especially long strings (such as dialog box option descriptions), or strings that contain newline or tab formatting, PCSX2 will use an iconized identifier for it's gettext lookup. These are identified in the gettext database of messages via their '!' prefix, which was chosen to help search and sort for these messages. Because this is a special PCSX2 extension to GNU gettext, you will need to reference the PCSX2 source code when translating them. You should probably be referencing source code during translation as a generla habit anyway, since the source code gives good hints as to the context of messages.<br />
<br />
When referencing the source code, these iconized translation messages are used like so:<br />
<br />
<source lang="cpp"><br />
someCheckBox->SetToolTip( pxE( "!ContextTip:SomeOption", L"This is some option which will make your game run a lot faster, but probably makes it uglier too." ) );<br />
</source><br />
The first string is the icon, which reads "!Tooltips:SomeOption". The second string is the current English literal translation. Poedit will show you the iconized version in its messages list, and you will need to open the source code reference for it to see what the current English translation is. It is important that your Poedit options are set up properly so that viewing the PCSX2 program source code works.<br />
<br />
Message icons use a somewhat standard naming convention which will help you decide what's important for translating and/or what sort of formatting you should apply to the message. The common classes are:<br />
*Popup - Messages that are popped up to the user for errors or confirmations. Very High Priority!<br />
*Panel - This is a top-level message on a dialog box or panel. These have a high translation priority.<br />
*Tooltip - These are tips that appear over toolbar buttons. Since button images are rarely fully self-explanatory these types of messages should be translated, so they have a high translation priority. (typically most or all such tooltips won't be iconized anyway, but I'm leaving the door open in case some would be better suited as such).<br />
*ContextTip - This is a popup box that gives additional extra information or details about various options on dialog boxes. Because these are usually supplementary to the dialog box option name and top-level description, translating them is not really necessary for someone to use the program. Translation priority is low.<br />
Compile/Generate pot/po/mo file on linux<br />
*A script (generate_pot.sh) is provided to generates all files on linux. The script does the 3 following steps:<br />
*#xgettext -> parse all source files then build the several pot files.<br />
*#msgmerge -> sync already translated po files with the previous pot generated. It would drop old string, add new ones and fuzzy string which was updated.<br />
*#msgfmt -> compile the po files into mo files then install the results into bin/Langs directory.<br />
To update/upload a new translation, it is as easy as copy the new po into the locales directory, call the script, commit the result.<br />
Downloading and Using POT files<br />
The latest stable release POT file pack can be found here: https://code.google.com/p/pcsx2/downloads/list ('''Warning''': this link has expired and as of may 2015 there are no alternatives)<br />
<br />
(look for the one labeled PCSX2_POT with some extra version information)<br />
<br />
You may also download the POT files individually from our SVN server. POT files are housed in the /locales/templates/ folder. The latest templates for the active trunk/devel branch of PCSX2 can be found here: http://web.archive.org/web/20141024194015/https://pcsx2.googlecode.com/svn/trunk/locales/templates/. Note that working from the trunk POTs is not recommended, except in cases where it has been quite a while since the latest stable or beta releases, or when the PCSX2 developers have entered Release Candidiate stages for an upcoming stable release. In most other cases you will want to check out a stable release branch instead, and use the templates from that.<br />
<br />
As of the last update to this wiki, the latest POT file pack is the 0.9.8 release translation POT.<br />
<br />
The Multiple POT files in PCSX2<br />
To assist translators, PCSX2 text messages have been split into a handful of different POT files, as follows:<br />
<br />
pcsx2_main - menu items, buttons, and most labels. High priority; most or all of this file should be translated for a translation to be considered useful.<br />
pcsx2_iconized - iconized notices, popups, and errors. Most of these are also very high priority; and most or all of this file should be translated for a translation to be considered useful.<br />
pcsx2_tertiary - Context tips, wizards, and other non-critical user interface elements. All of these strings are also in iconized form.<br />
pcsx2_devel - Strings used only by devel and debug builds of PCSX2. These mostly consist of loggig and menu options available only in development builds (and excluded from release builds for performance reasons), and tooltips associated with them. Translating these items is purely optional -- most people will never know or care about them.<br />
Only the main PO is required for PCSX2 to detect and use a language. Iconized and tertiary POs are optional. For strings in the iconized and tertiary POTs, you will have to reference the source code to obtain the original English text. It is highly recommended that you use Poedit, since it has a very nice code reference feature built-in. All strings in pcsx2_main.pot are standard gettext strings, and can be translated easily using any regular text editor.<br />
<br />
Future versions of PCSX2 may divide the POT files differently.<br />
<br />
Submitting your contribution<br />
Before starting a translation, please apply for it in this thread (if there has been no other applicant before you): http://forums.pcsx2.net/Thread-Program-and-Guide-translation-applications<br />
<br />
When you're done, you will be given the rights to post in the specific forum for the language you translated into in our board. There you can attach either the actual PO file archive you have created, or a patchfile that can be applied against the current svn copy of the translation.<br />
<br />
For those of you who are familiar and comfortable with Subversion (SVN): The PCSX2 Team hosts all available translations from our googlecode repository. Available translations and their submission history can be viewed online at https://pcsx2.googlecode.com/svn/trunk/locales/. You can also use an Svn tool such as TortoiseSvn to perform more detailed commit history analysis.<br />
<br />
Translations are branched according to the releases of PCSX2 that they conform to.<br />
<br />
{{PCSX2 Main Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/Supported_Languages&diff=38360PCSX2 Documentation/Supported Languages2015-07-19T20:00:05Z<p>Krysto: </p>
<hr />
<div>List of Canonical Codes of Languages Recognized by PCSX2<br />
<br />
Canonical codes for recognized languages are listed below. When creating a new translation PO/MO set for PCSX2, you will need to create a new folder in the form of /PCSX2 Install/Langs/[canonical]. Example: /PCSX2/Langs/fi_FI for Finnish.<br />
<br />
<source lang="text"><br />
Language Canonical Name<br />
<br />
Abkhazian ab<br />
Afar aa<br />
Afrikaans af_ZA<br />
Albanian sq_AL<br />
Amharic am<br />
Arabic ar<br />
Arabic (Algeria) ar_DZ<br />
Arabic (Bahrain) ar_BH<br />
Arabic (Egypt) ar_EG<br />
Arabic (Iraq) ar_IQ<br />
Arabic (Jordan) ar_JO<br />
Arabic (Kuwait) ar_KW<br />
Arabic (Lebanon) ar_LB<br />
Arabic (Libya) ar_LY<br />
Arabic (Morocco) ar_MA<br />
Arabic (Oman) ar_OM<br />
Arabic (Qatar) ar_QA<br />
Arabic (Saudi Arabia) ar_SA<br />
Arabic (Sudan) ar_SD<br />
Arabic (Syria) ar_SY<br />
Arabic (Tunisia) ar_TN<br />
Arabic (Uae) ar_AE<br />
Arabic (Yemen) ar_YE<br />
Armenian hy<br />
Assamese as<br />
Aymara ay<br />
Azeri az<br />
Azeri (Cyrillic) az<br />
Azeri (Latin) az<br />
Bashkir ba<br />
Basque eu_ES<br />
Belarusian be_BY<br />
Bengali bn<br />
Bhutani dz<br />
Bihari bh<br />
Bislama bi<br />
Breton br<br />
Bulgarian bg_BG<br />
Burmese my<br />
Cambodian km<br />
Catalan ca_ES<br />
Chinese (Simplified) zh_CN<br />
Chinese (Traditional) zh_TW<br />
Chinese (Hongkong) zh_HK<br />
Chinese (Macau) zh_MO<br />
Chinese (Singapore) zh_SG<br />
Corsican co<br />
Croatian hr_HR<br />
Czech cs_CZ<br />
Danish da_DK<br />
Dutch nl_NL<br />
Dutch (Belgian) nl_BE<br />
English (U.K.) en_GB<br />
English (U.S.) en_US<br />
English (Australia) en_AU<br />
English (Belize) en_BZ<br />
English (Botswana) en_BW<br />
English (Canada) en_CA<br />
English (Caribbean) en_CB<br />
English (Denmark) en_DK<br />
English (Eire) en_IE<br />
English (Jamaica) en_JM<br />
English (New Zealand) en_NZ<br />
English (Philippines) en_PH<br />
English (South Africa) en_ZA<br />
English (Trinidad) en_TT<br />
English (Zimbabwe) en_ZW<br />
Esperanto eo<br />
Estonian et_EE<br />
Faeroese fo_FO<br />
Farsi fa_IR<br />
Fiji fj<br />
Finnish fi_FI<br />
French fr_FR<br />
French (Belgian) fr_BE<br />
French (Canadian) fr_CA<br />
French (Luxembourg) fr_LU<br />
French (Monaco) fr_MC<br />
French (Swiss) fr_CH<br />
Frisian fy<br />
Galician gl_ES<br />
Georgian ka_GE<br />
German de_DE<br />
German (Austrian) de_AT<br />
German (Belgium) de_BE<br />
German (Liechtenstein) de_LI<br />
German (Luxembourg) de_LU<br />
German (Swiss) de_CH<br />
Greek el_GR<br />
Greenlandic kl_GL<br />
Guarani gn<br />
Gujarati gu<br />
Hausa ha<br />
Hebrew he_IL<br />
Hindi hi_IN<br />
Hungarian hu_HU<br />
Icelandic is_IS<br />
Indonesian id_ID<br />
Interlingua ia<br />
Interlingue ie<br />
Inuktitut iu<br />
Inupiak ik<br />
Irish ga_IE<br />
Italian it_IT<br />
Italian (Swiss) it_CH<br />
Japanese ja_JP<br />
Javanese jw<br />
Kannada kn<br />
Kashmiri ks<br />
Kashmiri (India) ks_IN<br />
Kazakh kk<br />
Kernewek kw_GB<br />
Kinyarwanda rw<br />
Kirghiz ky<br />
Kirundi rn<br />
Konkani <br />
Korean ko_KR<br />
Kurdish ku_TR<br />
Laothian lo<br />
Latin la<br />
Latvian lv_LV<br />
Lingala ln<br />
Lithuanian lt_LT<br />
Macedonian mk_MK<br />
Malagasy mg<br />
Malay ms_MY<br />
Malayalam ml<br />
Malay (Brunei Darussalam) ms_BN<br />
Malay (Malaysia) ms_MY<br />
Maltese mt_MT<br />
Manipuri <br />
Maori mi<br />
Marathi mr_IN<br />
Moldavian mo<br />
Mongolian mn<br />
Nauru na<br />
Nepali ne_NP<br />
Nepali (India) ne_IN<br />
Norwegian (Bokmal) nb_NO<br />
Norwegian (Nynorsk) nn_NO<br />
Occitan oc<br />
Oriya or<br />
(Afan) Oromo om<br />
Pashto, Pushto ps<br />
Polish pl_PL<br />
Portuguese pt_PT<br />
Portuguese (Brazilian) pt_BR<br />
Punjabi pa<br />
Quechua qu<br />
Rhaeto-Romance rm<br />
Romanian ro_RO<br />
Russian ru_RU<br />
Russian (Ukraine) ru_UA<br />
Samoan sm<br />
Sangho sg<br />
Sanskrit sa<br />
Scots Gaelic gd<br />
Serbian (Cyrillic) sr_RS<br />
Serbian (Latin) sr_RS@latin<br />
Serbo-Croatian sh<br />
Sesotho st<br />
Setswana tn<br />
Shona sn<br />
Sindhi sd<br />
Sinhalese si<br />
Siswati ss<br />
Slovak sk_SK<br />
Slovenian sl_SI<br />
Somali so<br />
Spanish (Argentina) es_AR<br />
Spanish (Bolivia) es_BO<br />
Spanish (Chile) es_CL<br />
Spanish (Colombia) es_CO<br />
Spanish (Costa Rica) es_CR<br />
Spanish (Dominican republic) es_DO<br />
Spanish (Ecuador) es_EC<br />
Spanish (El Salvador) es_SV<br />
Spanish (Guatemala) es_GT<br />
Spanish (Honduras) es_HN<br />
Spanish (Mexican) es_MX<br />
Spanish (Modern) es_ES<br />
Spanish (Nicaragua) es_NI<br />
Spanish (Panama) es_PA<br />
Spanish (Paraguay) es_PY<br />
Spanish (Peru) es_PE<br />
Spanish (Puerto Rico) es_PR<br />
Spanish (Uruguay) es_UY<br />
Spanish (U.S.) es_US<br />
Spanish (Venezuela) es_VE<br />
Sundanese su<br />
Swahili sw_KE<br />
Swedish sv_SE<br />
Swedish (Finland) sv_FI<br />
Tagalog tl_PH<br />
Tajik tg<br />
Tamil ta<br />
Tatar tt<br />
Telugu te<br />
Thai th_TH<br />
Tibetan bo<br />
Tigrinya ti<br />
Tonga to<br />
Tsonga ts<br />
Turkish tr_TR<br />
Turkmen tk<br />
Twi tw<br />
Uighur ug<br />
Ukrainian uk_UA<br />
Urdu ur<br />
Urdu (India) ur_IN<br />
Urdu (Pakistan) ur_PK<br />
Uzbek uz<br />
Uzbek (Cyrillic) uz<br />
Uzbek (Latin) uz<br />
Vietnamese vi_VN<br />
Volapuk vo<br />
Welsh cy<br />
Wolof wo<br />
Xhosa xh<br />
Yiddish yi<br />
Yoruba yo<br />
Zhuang za<br />
Zulu zu<br />
</source><br />
<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/Supported_Languages&diff=38359PCSX2 Documentation/Supported Languages2015-07-19T19:59:42Z<p>Krysto: </p>
<hr />
<div>List of Canonical Codes of Languages Recognized by PCSX2<br />
<br />
Canonical codes for recognized languages are listed below. When creating a new translation PO/MO set for PCSX2, you will need to create a new folder in the form of /PCSX2 Install/Langs/[canonical]. Example: /PCSX2/Langs/fi_FI for Finnish.<br />
<br />
<source lang="text"><br />
Language Canonical Name<br />
<br />
Abkhazian ab<br />
Afar aa<br />
Afrikaans af_ZA<br />
Albanian sq_AL<br />
Amharic am<br />
Arabic ar<br />
Arabic (Algeria) ar_DZ<br />
Arabic (Bahrain) ar_BH<br />
Arabic (Egypt) ar_EG<br />
Arabic (Iraq) ar_IQ<br />
Arabic (Jordan) ar_JO<br />
Arabic (Kuwait) ar_KW<br />
Arabic (Lebanon) ar_LB<br />
Arabic (Libya) ar_LY<br />
Arabic (Morocco) ar_MA<br />
Arabic (Oman) ar_OM<br />
Arabic (Qatar) ar_QA<br />
Arabic (Saudi Arabia) ar_SA<br />
Arabic (Sudan) ar_SD<br />
Arabic (Syria) ar_SY<br />
Arabic (Tunisia) ar_TN<br />
Arabic (Uae) ar_AE<br />
Arabic (Yemen) ar_YE<br />
Armenian hy<br />
Assamese as<br />
Aymara ay<br />
Azeri az<br />
Azeri (Cyrillic) az<br />
Azeri (Latin) az<br />
Bashkir ba<br />
Basque eu_ES<br />
Belarusian be_BY<br />
Bengali bn<br />
Bhutani dz<br />
Bihari bh<br />
Bislama bi<br />
Breton br<br />
Bulgarian bg_BG<br />
Burmese my<br />
Cambodian km<br />
Catalan ca_ES<br />
Chinese (Simplified) zh_CN<br />
Chinese (Traditional) zh_TW<br />
Chinese (Hongkong) zh_HK<br />
Chinese (Macau) zh_MO<br />
Chinese (Singapore) zh_SG<br />
Corsican co<br />
Croatian hr_HR<br />
Czech cs_CZ<br />
Danish da_DK<br />
Dutch nl_NL<br />
Dutch (Belgian) nl_BE<br />
English (U.K.) en_GB<br />
English (U.S.) en_US<br />
English (Australia) en_AU<br />
English (Belize) en_BZ<br />
English (Botswana) en_BW<br />
English (Canada) en_CA<br />
English (Caribbean) en_CB<br />
English (Denmark) en_DK<br />
English (Eire) en_IE<br />
English (Jamaica) en_JM<br />
English (New Zealand) en_NZ<br />
English (Philippines) en_PH<br />
English (South Africa) en_ZA<br />
English (Trinidad) en_TT<br />
English (Zimbabwe) en_ZW<br />
Esperanto eo<br />
Estonian et_EE<br />
Faeroese fo_FO<br />
Farsi fa_IR<br />
Fiji fj<br />
Finnish fi_FI<br />
French fr_FR<br />
French (Belgian) fr_BE<br />
French (Canadian) fr_CA<br />
French (Luxembourg) fr_LU<br />
French (Monaco) fr_MC<br />
French (Swiss) fr_CH<br />
Frisian fy<br />
Galician gl_ES<br />
Georgian ka_GE<br />
German de_DE<br />
German (Austrian) de_AT<br />
German (Belgium) de_BE<br />
German (Liechtenstein) de_LI<br />
German (Luxembourg) de_LU<br />
German (Swiss) de_CH<br />
Greek el_GR<br />
Greenlandic kl_GL<br />
Guarani gn<br />
Gujarati gu<br />
Hausa ha<br />
Hebrew he_IL<br />
Hindi hi_IN<br />
Hungarian hu_HU<br />
Icelandic is_IS<br />
Indonesian id_ID<br />
Interlingua ia<br />
Interlingue ie<br />
Inuktitut iu<br />
Inupiak ik<br />
Irish ga_IE<br />
Italian it_IT<br />
Italian (Swiss) it_CH<br />
Japanese ja_JP<br />
Javanese jw<br />
Kannada kn<br />
Kashmiri ks<br />
Kashmiri (India) ks_IN<br />
Kazakh kk<br />
Kernewek kw_GB<br />
Kinyarwanda rw<br />
Kirghiz ky<br />
Kirundi rn<br />
Konkani <br />
Korean ko_KR<br />
Kurdish ku_TR<br />
Laothian lo<br />
Latin la<br />
Latvian lv_LV<br />
Lingala ln<br />
Lithuanian lt_LT<br />
Macedonian mk_MK<br />
Malagasy mg<br />
Malay ms_MY<br />
Malayalam ml<br />
Malay (Brunei Darussalam) ms_BN<br />
Malay (Malaysia) ms_MY<br />
Maltese mt_MT<br />
Manipuri <br />
Maori mi<br />
Marathi mr_IN<br />
Moldavian mo<br />
Mongolian mn<br />
Nauru na<br />
Nepali ne_NP<br />
Nepali (India) ne_IN<br />
Norwegian (Bokmal) nb_NO<br />
Norwegian (Nynorsk) nn_NO<br />
Occitan oc<br />
Oriya or<br />
(Afan) Oromo om<br />
Pashto, Pushto ps<br />
Polish pl_PL<br />
Portuguese pt_PT<br />
Portuguese (Brazilian) pt_BR<br />
Punjabi pa<br />
Quechua qu<br />
Rhaeto-Romance rm<br />
Romanian ro_RO<br />
Russian ru_RU<br />
Russian (Ukraine) ru_UA<br />
Samoan sm<br />
Sangho sg<br />
Sanskrit sa<br />
Scots Gaelic gd<br />
Serbian (Cyrillic) sr_RS<br />
Serbian (Latin) sr_RS@latin<br />
Serbo-Croatian sh<br />
Sesotho st<br />
Setswana tn<br />
Shona sn<br />
Sindhi sd<br />
Sinhalese si<br />
Siswati ss<br />
Slovak sk_SK<br />
Slovenian sl_SI<br />
Somali so<br />
Spanish (Argentina) es_AR<br />
Spanish (Bolivia) es_BO<br />
Spanish (Chile) es_CL<br />
Spanish (Colombia) es_CO<br />
Spanish (Costa Rica) es_CR<br />
Spanish (Dominican republic) es_DO<br />
Spanish (Ecuador) es_EC<br />
Spanish (El Salvador) es_SV<br />
Spanish (Guatemala) es_GT<br />
Spanish (Honduras) es_HN<br />
Spanish (Mexican) es_MX<br />
Spanish (Modern) es_ES<br />
Spanish (Nicaragua) es_NI<br />
Spanish (Panama) es_PA<br />
Spanish (Paraguay) es_PY<br />
Spanish (Peru) es_PE<br />
Spanish (Puerto Rico) es_PR<br />
Spanish (Uruguay) es_UY<br />
Spanish (U.S.) es_US<br />
Spanish (Venezuela) es_VE<br />
Sundanese su<br />
Swahili sw_KE<br />
Swedish sv_SE<br />
Swedish (Finland) sv_FI<br />
Tagalog tl_PH<br />
Tajik tg<br />
Tamil ta<br />
Tatar tt<br />
Telugu te<br />
Thai th_TH<br />
Tibetan bo<br />
Tigrinya ti<br />
Tonga to<br />
Tsonga ts<br />
Turkish tr_TR<br />
Turkmen tk<br />
Twi tw<br />
Uighur ug<br />
Ukrainian uk_UA<br />
Urdu ur<br />
Urdu (India) ur_IN<br />
Urdu (Pakistan) ur_PK<br />
Uzbek uz<br />
Uzbek (Cyrillic) uz<br />
Uzbek (Latin) uz<br />
Vietnamese vi_VN<br />
Volapuk vo<br />
Welsh cy<br />
Wolof wo<br />
Xhosa xh<br />
Yiddish yi<br />
Yoruba yo<br />
Zhuang za<br />
Zulu zu<br />
</source><br />
<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/Graphics_Synthesizer,_GPUs_and_Dual_Cores&diff=38358PCSX2 Documentation/Graphics Synthesizer, GPUs and Dual Cores2015-07-19T19:53:13Z<p>Krysto: </p>
<hr />
<div>''Originally written by ZeroFrog''<br />
<br />
It was apparent early on the project that the GS plugin was going to be a big bottleneck during 3D scenes. It isn't that the GS plugin itself does a lot of computation on the CPU, but the fact that it needs to communicate with the graphics card means that unnecessary stalls will occur in the graphics driver as the GPU and CPU are synchronized. During these stalls, the CPU basically goes to lunch until the GPU is ready. Graphics drivers and libraries are aware of this and try as little as possible to communicate with the graphics card. They usually cache render state changes, shader changes, and texture changes up until actual geometry is rendered. They also take advantage of FIFOs (first in first out buffers). The CPU just writes to the FIFO and the GPU just reads from it, this makes all the difference in terms of keeping the GPU active while the CPU isn't and vise versa.<br />
<br />
The biggest challenge when designing games and hardcore applications that need to use the GPU to its full potential is to make sure that graphics driver stalls are minimal at all costs. What kills games isn't sending geometry down the graphics pipeline, but it is when render targets are switched, render targets are used as textures in the next draw call, textures are locked, and when render targets are transferred from GPU memory to CPU memory (in the last case, the CPU not only goes to lunch, but has dinner also). GPU optimization talks usually appear in every Game Developers Conference and there are many papers on them on the net, so there is a lot more to the story than written here.<br />
<br />
All this means is that single-threaded applications really need to design their GPU algorithms well to see fast frame rates. This unfortunately is not possible with Pcsx2 and the GS plugin. The GS plugin has to draw geometry in the same order as it was received. This kills almost all caching techniques used by modern games because the GS and PC GPUs have very different performance bottlenecks. In modern GPUs, it is advantageous to group as much geometry as possible in one draw call. The GS doesn't suffer from such bottlenecks. The GS also has two different contexts which makes things twice as difficult. ZeroGS can only do a limited amount of work-arounds before compatibility starts dropping, so the only other option is to try to multithread the GS. Note that using graphics libraries from multiple threads is not a trivial task.<br />
<br />
Fortunately, the GS plugin is very unique in its nature. 99% of the communication that happens between the GS plugin and the rest of the systems components happens in the direction to the GS. The only times the EE needs to synchronize with the GS is when it reads back the FINISH/SIGNAL registers and part of the 4Mb GS memory. Register readbacks are used frequently, so this suggests that tight synchronization will be needed with the GS. The GS memory readbacks aren't as frequent; however, they require some special considerations with Virtual Memory and DMAs. The rest of the 99% of communication that goes to the GS happens with a GS FIFO.<br />
<br />
When first started, Pcsx2 creates a GS thread and reserves special memory for the GS FIFO. The GS plugin then creates the Direct3D device/GL context only for that thread. Then when the game runs, the EE copes all its GS packets to the FIFO and then notifies the GS thread. The GS thread then checks if the FIFO has data, and then sends it to the GS plugin. This sounds easier than it actually is because very tight synchronization needs to happen to make sure no overwriting occurs in the FIFO. The FINISH/SIGNAL register synchronization actually doesn't happen across the EE and GS thread boundaries. Instead the EE thread peeks at all the packets ahead of time and handles it in its own routines.<br />
<br />
What makes the "Dual Core" option special is the notifies part of the last explanation. The GS thread can either sleep waiting for a notification from EE, which can be done by WaitForSingleObject and SetEvent functions. Or it can continually check if the GS FIFO is empty without ever stopping. The latter option kills single cores but goes much faster on dual cores. The results of clicking on the MTGS and DC options on dual cores are phenomenal. Usually frame rates go up or even surpass 2x.<br />
<br />
Multithreading in games is going to be very big in the future. The times have passed when there is one CPU that does everything and one GPU that just renders. The biggest problem is which game processing to divide into which thread, and how these threads will communicate with each other. Many of these issues are still open and current game companies are struggling with the added complication of concurrent execution.<br />
<br />
Moral of the blog - GPUs have become so powerful that people are staring to do tasks like stereo vision and general purpose computation with them. Learn how to use them. I recommend ShaderX3: Advanced Rendering with DirectX and OpenGL by Wolfgang Engel and GPU Gems 2: Programming Techniques for High-Performance Graphics and General-Purpose Computation by Matt Pharr, Randima Fernando, and the 20+ researchers that contributed to it.<br />
<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/Graphics_Synthesizer,_GPUs_and_Dual_Cores&diff=38357PCSX2 Documentation/Graphics Synthesizer, GPUs and Dual Cores2015-07-19T19:52:55Z<p>Krysto: </p>
<hr />
<div>''Originally written by ZeroFrog''<br />
<br />
It was apparent early on the project that the GS plugin was going to be a big bottleneck during 3D scenes. It isn't that the GS plugin itself does a lot of computation on the CPU, but the fact that it needs to communicate with the graphics card means that unnecessary stalls will occur in the graphics driver as the GPU and CPU are synchronized. During these stalls, the CPU basically goes to lunch until the GPU is ready. Graphics drivers and libraries are aware of this and try as little as possible to communicate with the graphics card. They usually cache render state changes, shader changes, and texture changes up until actual geometry is rendered. They also take advantage of FIFOs (first in first out buffers). The CPU just writes to the FIFO and the GPU just reads from it, this makes all the difference in terms of keeping the GPU active while the CPU isn't and vise versa.<br />
<br />
The biggest challenge when designing games and hardcore applications that need to use the GPU to its full potential is to make sure that graphics driver stalls are minimal at all costs. What kills games isn't sending geometry down the graphics pipeline, but it is when render targets are switched, render targets are used as textures in the next draw call, textures are locked, and when render targets are transferred from GPU memory to CPU memory (in the last case, the CPU not only goes to lunch, but has dinner also). GPU optimization talks usually appear in every Game Developers Conference and there are many papers on them on the net, so there is a lot more to the story than written here.<br />
<br />
All this means is that single-threaded applications really need to design their GPU algorithms well to see fast frame rates. This unfortunately is not possible with Pcsx2 and the GS plugin. The GS plugin has to draw geometry in the same order as it was received. This kills almost all caching techniques used by modern games because the GS and PC GPUs have very different performance bottlenecks. In modern GPUs, it is advantageous to group as much geometry as possible in one draw call. The GS doesn't suffer from such bottlenecks. The GS also has two different contexts which makes things twice as difficult. ZeroGS can only do a limited amount of work-arounds before compatibility starts dropping, so the only other option is to try to multithread the GS. Note that using graphics libraries from multiple threads is not a trivial task.<br />
<br />
Fortunately, the GS plugin is very unique in its nature. 99% of the communication that happens between the GS plugin and the rest of the systems components happens in the direction to the GS. The only times the EE needs to synchronize with the GS is when it reads back the FINISH/SIGNAL registers and part of the 4Mb GS memory. Register readbacks are used frequently, so this suggests that tight synchronization will be needed with the GS. The GS memory readbacks aren't as frequent; however, they require some special considerations with Virtual Memory and DMAs. The rest of the 99% of communication that goes to the GS happens with a GS FIFO.<br />
<br />
When first started, Pcsx2 creates a GS thread and reserves special memory for the GS FIFO. The GS plugin then creates the Direct3D device/GL context only for that thread. Then when the game runs, the EE copes all its GS packets to the FIFO and then notifies the GS thread. The GS thread then checks if the FIFO has data, and then sends it to the GS plugin. This sounds easier than it actually is because very tight synchronization needs to happen to make sure no overwriting occurs in the FIFO. The FINISH/SIGNAL register synchronization actually doesn't happen across the EE and GS thread boundaries. Instead the EE thread peeks at all the packets ahead of time and handles it in its own routines.<br />
<br />
What makes the "Dual Core" option special is the notifies part of the last explanation. The GS thread can either sleep waiting for a notification from EE, which can be done by WaitForSingleObject and SetEvent functions. Or it can continually check if the GS FIFO is empty without ever stopping. The latter option kills single cores but goes much faster on dual cores. The results of clicking on the MTGS and DC options on dual cores are phenomenal. Usually frame rates go up or even surpass 2x.<br />
<br />
Multithreading in games is going to be very big in the future. The times have passed when there is one CPU that does everything and one GPU that just renders. The biggest problem is which game processing to divide into which thread, and how these threads will communicate with each other. Many of these issues are still open and current game companies are struggling with the added complication of concurrent execution.<br />
<br />
Moral of the blog - GPUs have become so powerful that people are staring to do tasks like stereo vision and general purpose computation with them. Learn how to use them. I recommend ShaderX3: Advanced Rendering with DirectX and OpenGL by Wolfgang Engel and GPU Gems 2: Programming Techniques for High-Performance Graphics and General-Purpose Computation by Matt Pharr, Randima Fernando, and the 20+ researchers that contributed to it.<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/Reverb_Engine&diff=38356PCSX2 Documentation/Reverb Engine2015-07-19T19:51:42Z<p>Krysto: </p>
<hr />
<div>==Introduction==<br />
I will try to explain here what I know of the SPU2's Reverb, which is practically identical to the one in the PS1. All my knowledge comes mostly form the great (but not quite accurate) document by Neill Corlett, and some pages I found online about reverberation and impulse responses.<br />
<br />
==The Reverb Engine==<br />
The basis of the reverb engine is a standard Schroeder Reverberator. This reverberator is formed by four parallel Comb filters, taking data form four sample queues which are fed from the input. These filters are the source of the main echos, the first reflections of the sound waves coming from the walls of the virtual room. The mixed output from the four Comb filters passes through a pair of All-pass filters, which contain a sample queue each, and they feed themselves the data, causing a controlled feedback loop which multiplies the density of the echos, but reduces them in volume over time.<br />
<br />
==The SPU2 Reverb Engine==<br />
Instead of having a series of separate queues, the SPU2 has a single rotating buffer and uses offsets to split this buffer into the queues it will require. The different blocks of the reverb engine will write to different locations of the buffer, which will advance on each processing cycle, and then data will be read from locations some samples away.<br />
<br />
While the data is eventually overwritten by other queues, it does not matter because the readers have already used the data while it was available. The reverb engine takes the data from the input lines (stereo), passes it through a configurable IIR filter, and puts it in the input queue. At the same time, the four comb filters take data from different points of the input queue, which is shared for all of them. The All-pass filters have their own queue each, and seem to match the standard design.<br />
<br />
==Pseudo-C Implementation==<br />
In the following code, Buffer represents the rotating buffer, which will be used using something similar to<br />
<br />
<source lang="cpp"><br />
Buffer[x] === Spu2_Memory[ EffectsBufferBase + (EffectsBufferPosition + offset) % EffectsBufferSize ]<br />
</source><br />
The Revb structure contains all the (wrong) register names currently used. If this implementation turns out to work correctly, I will change the names of those registers so they represent their actual usage.<br />
<br />
This is the current state of the reference implementation I used in an Impulse Response Analyzer I wrote:<br />
<br />
<source lang="cpp"><br />
// Input filter<br />
// Writes the data to the input queues for the echos below<br />
{<br />
var INPUT_SAMPLE_L = Input.Left * Revb.IN_COEF_L;<br />
var INPUT_SAMPLE_R = Input.Right * Revb.IN_COEF_R;<br />
<br />
var IIR_INPUT_A0 = Buffer[Revb.IIR_SRC_A0] * Revb.IIR_COEF + INPUT_SAMPLE_L;<br />
var IIR_INPUT_A1 = Buffer[Revb.IIR_SRC_A1] * Revb.IIR_COEF + INPUT_SAMPLE_R;<br />
var IIR_INPUT_B0 = Buffer[Revb.IIR_SRC_B0] * Revb.IIR_COEF + INPUT_SAMPLE_L;<br />
var IIR_INPUT_B1 = Buffer[Revb.IIR_SRC_B1] * Revb.IIR_COEF + INPUT_SAMPLE_R;<br />
<br />
var IIR_DA0 = Buffer[Revb.IIR_DEST_A0];<br />
var IIR_DA1 = Buffer[Revb.IIR_DEST_A1];<br />
var IIR_DB0 = Buffer[Revb.IIR_DEST_B0];<br />
var IIR_DB1 = Buffer[Revb.IIR_DEST_B1];<br />
<br />
Buffer[Revb.IIR_DEST_A0 + one] = clamp_mix( IIR_DA0 + ((((IIR_INPUT_A0 >> 16) - IIR_DA0) * Revb.IIR_ALPHA) >> 16) );<br />
Buffer[Revb.IIR_DEST_A1 + one] = clamp_mix( IIR_DA1 + ((((IIR_INPUT_A1 >> 16) - IIR_DA1) * Revb.IIR_ALPHA) >> 16) );<br />
Buffer[Revb.IIR_DEST_B0 + one] = clamp_mix( IIR_DB0 + ((((IIR_INPUT_B0 >> 16) - IIR_DB0) * Revb.IIR_ALPHA) >> 16) );<br />
Buffer[Revb.IIR_DEST_B1 + one] = clamp_mix( IIR_DB1 + ((((IIR_INPUT_B1 >> 16) - IIR_DB1) * Revb.IIR_ALPHA) >> 16) );<br />
}<br />
<br />
int ACC0 = 0;<br />
int ACC1 = 0;<br />
<br />
// Classic Schroeder Reverberator:<br />
{<br />
// 4 Comb filters<br />
ACC0 += Buffer[Revb.ACC_SRC_A0] * Revb.ACC_COEF_A;<br />
ACC1 += Buffer[Revb.ACC_SRC_A1] * Revb.ACC_COEF_A;<br />
ACC0 += Buffer[Revb.ACC_SRC_B0] * Revb.ACC_COEF_B;<br />
ACC1 += Buffer[Revb.ACC_SRC_B1] * Revb.ACC_COEF_B;<br />
ACC0 += Buffer[Revb.ACC_SRC_C0] * Revb.ACC_COEF_C;<br />
ACC1 += Buffer[Revb.ACC_SRC_C1] * Revb.ACC_COEF_C;<br />
ACC0 += Buffer[Revb.ACC_SRC_D0] * Revb.ACC_COEF_D;<br />
ACC1 += Buffer[Revb.ACC_SRC_D1] * Revb.ACC_COEF_D;<br />
<br />
// First All-pass filter:<br />
{<br />
// Take delayed input<br />
var FB_A0 = Buffer[Revb.MIX_DEST_A0 - Revb.FB_SRC_A];<br />
var FB_A1 = Buffer[Revb.MIX_DEST_A1 - Revb.FB_SRC_A];<br />
<br />
// Apply gain and add to input<br />
var MIX_A0 = (ACC0 + FB_A0 * Revb.FB_ALPHA) >> 16;<br />
var MIX_A1 = (ACC1 + FB_A1 * Revb.FB_ALPHA) >> 16;<br />
<br />
// Write to queue<br />
Buffer[Revb.MIX_DEST_A0] = clamp_mix(MIX_A0);<br />
Buffer[Revb.MIX_DEST_A1] = clamp_mix(MIX_A1);<br />
<br />
// Apply second gain and add<br />
ACC0 += (FB_A0 << 16) - MIX_A0 * Revb.FB_ALPHA;<br />
ACC1 += (FB_A1 << 16) - MIX_A1 * Revb.FB_ALPHA;<br />
}<br />
<br />
// Second All-pass filter:<br />
{<br />
// Take delayed input<br />
var FB_B0 = Buffer[Revb.MIX_DEST_B0 - Revb.FB_SRC_B];<br />
var FB_B1 = Buffer[Revb.MIX_DEST_B1 - Revb.FB_SRC_B];<br />
<br />
// Apply gain and add to input<br />
var MIX_B0 = (ACC0 + FB_B0 * Revb.FB_X) >> 16;<br />
var MIX_B1 = (ACC1 + FB_B1 * Revb.FB_X) >> 16;<br />
<br />
// Write to queue<br />
Buffer[Revb.MIX_DEST_B0] = clamp_mix(MIX_B0);<br />
Buffer[Revb.MIX_DEST_B1] = clamp_mix(MIX_B1);<br />
<br />
// Apply second gain and add<br />
ACC0 += (FB_B0 << 16) - MIX_B0 * Revb.FB_X;<br />
ACC1 += (FB_B1 << 16) - MIX_B1 * Revb.FB_X;<br />
}<br />
<br />
}<br />
<br />
return new StereoOut32( ACC0 >> 16, ACC1 >> 16);<br />
</source><br />
<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/Reverb_Engine&diff=38355PCSX2 Documentation/Reverb Engine2015-07-19T19:51:29Z<p>Krysto: </p>
<hr />
<div>==Introduction==<br />
I will try to explain here what I know of the SPU2's Reverb, which is practically identical to the one in the PS1. All my knowledge comes mostly form the great (but not quite accurate) document by Neill Corlett, and some pages I found online about reverberation and impulse responses.<br />
<br />
==The Reverb Engine==<br />
The basis of the reverb engine is a standard Schroeder Reverberator. This reverberator is formed by four parallel Comb filters, taking data form four sample queues which are fed from the input. These filters are the source of the main echos, the first reflections of the sound waves coming from the walls of the virtual room. The mixed output from the four Comb filters passes through a pair of All-pass filters, which contain a sample queue each, and they feed themselves the data, causing a controlled feedback loop which multiplies the density of the echos, but reduces them in volume over time.<br />
<br />
==The SPU2 Reverb Engine==<br />
Instead of having a series of separate queues, the SPU2 has a single rotating buffer and uses offsets to split this buffer into the queues it will require. The different blocks of the reverb engine will write to different locations of the buffer, which will advance on each processing cycle, and then data will be read from locations some samples away.<br />
<br />
While the data is eventually overwritten by other queues, it does not matter because the readers have already used the data while it was available. The reverb engine takes the data from the input lines (stereo), passes it through a configurable IIR filter, and puts it in the input queue. At the same time, the four comb filters take data from different points of the input queue, which is shared for all of them. The All-pass filters have their own queue each, and seem to match the standard design.<br />
<br />
==Pseudo-C Implementation==<br />
In the following code, Buffer represents the rotating buffer, which will be used using something similar to<br />
<br />
<source lang="cpp"><br />
Buffer[x] === Spu2_Memory[ EffectsBufferBase + (EffectsBufferPosition + offset) % EffectsBufferSize ]<br />
</source><br />
The Revb structure contains all the (wrong) register names currently used. If this implementation turns out to work correctly, I will change the names of those registers so they represent their actual usage.<br />
<br />
This is the current state of the reference implementation I used in an Impulse Response Analyzer I wrote:<br />
<br />
<source lang="cpp"><br />
// Input filter<br />
// Writes the data to the input queues for the echos below<br />
{<br />
var INPUT_SAMPLE_L = Input.Left * Revb.IN_COEF_L;<br />
var INPUT_SAMPLE_R = Input.Right * Revb.IN_COEF_R;<br />
<br />
var IIR_INPUT_A0 = Buffer[Revb.IIR_SRC_A0] * Revb.IIR_COEF + INPUT_SAMPLE_L;<br />
var IIR_INPUT_A1 = Buffer[Revb.IIR_SRC_A1] * Revb.IIR_COEF + INPUT_SAMPLE_R;<br />
var IIR_INPUT_B0 = Buffer[Revb.IIR_SRC_B0] * Revb.IIR_COEF + INPUT_SAMPLE_L;<br />
var IIR_INPUT_B1 = Buffer[Revb.IIR_SRC_B1] * Revb.IIR_COEF + INPUT_SAMPLE_R;<br />
<br />
var IIR_DA0 = Buffer[Revb.IIR_DEST_A0];<br />
var IIR_DA1 = Buffer[Revb.IIR_DEST_A1];<br />
var IIR_DB0 = Buffer[Revb.IIR_DEST_B0];<br />
var IIR_DB1 = Buffer[Revb.IIR_DEST_B1];<br />
<br />
Buffer[Revb.IIR_DEST_A0 + one] = clamp_mix( IIR_DA0 + ((((IIR_INPUT_A0 >> 16) - IIR_DA0) * Revb.IIR_ALPHA) >> 16) );<br />
Buffer[Revb.IIR_DEST_A1 + one] = clamp_mix( IIR_DA1 + ((((IIR_INPUT_A1 >> 16) - IIR_DA1) * Revb.IIR_ALPHA) >> 16) );<br />
Buffer[Revb.IIR_DEST_B0 + one] = clamp_mix( IIR_DB0 + ((((IIR_INPUT_B0 >> 16) - IIR_DB0) * Revb.IIR_ALPHA) >> 16) );<br />
Buffer[Revb.IIR_DEST_B1 + one] = clamp_mix( IIR_DB1 + ((((IIR_INPUT_B1 >> 16) - IIR_DB1) * Revb.IIR_ALPHA) >> 16) );<br />
}<br />
<br />
int ACC0 = 0;<br />
int ACC1 = 0;<br />
<br />
// Classic Schroeder Reverberator:<br />
{<br />
// 4 Comb filters<br />
ACC0 += Buffer[Revb.ACC_SRC_A0] * Revb.ACC_COEF_A;<br />
ACC1 += Buffer[Revb.ACC_SRC_A1] * Revb.ACC_COEF_A;<br />
ACC0 += Buffer[Revb.ACC_SRC_B0] * Revb.ACC_COEF_B;<br />
ACC1 += Buffer[Revb.ACC_SRC_B1] * Revb.ACC_COEF_B;<br />
ACC0 += Buffer[Revb.ACC_SRC_C0] * Revb.ACC_COEF_C;<br />
ACC1 += Buffer[Revb.ACC_SRC_C1] * Revb.ACC_COEF_C;<br />
ACC0 += Buffer[Revb.ACC_SRC_D0] * Revb.ACC_COEF_D;<br />
ACC1 += Buffer[Revb.ACC_SRC_D1] * Revb.ACC_COEF_D;<br />
<br />
// First All-pass filter:<br />
{<br />
// Take delayed input<br />
var FB_A0 = Buffer[Revb.MIX_DEST_A0 - Revb.FB_SRC_A];<br />
var FB_A1 = Buffer[Revb.MIX_DEST_A1 - Revb.FB_SRC_A];<br />
<br />
// Apply gain and add to input<br />
var MIX_A0 = (ACC0 + FB_A0 * Revb.FB_ALPHA) >> 16;<br />
var MIX_A1 = (ACC1 + FB_A1 * Revb.FB_ALPHA) >> 16;<br />
<br />
// Write to queue<br />
Buffer[Revb.MIX_DEST_A0] = clamp_mix(MIX_A0);<br />
Buffer[Revb.MIX_DEST_A1] = clamp_mix(MIX_A1);<br />
<br />
// Apply second gain and add<br />
ACC0 += (FB_A0 << 16) - MIX_A0 * Revb.FB_ALPHA;<br />
ACC1 += (FB_A1 << 16) - MIX_A1 * Revb.FB_ALPHA;<br />
}<br />
<br />
// Second All-pass filter:<br />
{<br />
// Take delayed input<br />
var FB_B0 = Buffer[Revb.MIX_DEST_B0 - Revb.FB_SRC_B];<br />
var FB_B1 = Buffer[Revb.MIX_DEST_B1 - Revb.FB_SRC_B];<br />
<br />
// Apply gain and add to input<br />
var MIX_B0 = (ACC0 + FB_B0 * Revb.FB_X) >> 16;<br />
var MIX_B1 = (ACC1 + FB_B1 * Revb.FB_X) >> 16;<br />
<br />
// Write to queue<br />
Buffer[Revb.MIX_DEST_B0] = clamp_mix(MIX_B0);<br />
Buffer[Revb.MIX_DEST_B1] = clamp_mix(MIX_B1);<br />
<br />
// Apply second gain and add<br />
ACC0 += (FB_B0 << 16) - MIX_B0 * Revb.FB_X;<br />
ACC1 += (FB_B1 << 16) - MIX_B1 * Revb.FB_X;<br />
}<br />
<br />
}<br />
<br />
return new StereoOut32( ACC0 >> 16, ACC1 >> 16);<br />
</source><br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/SPU2_is_more_than_just_sound!&diff=38354PCSX2 Documentation/SPU2 is more than just sound!2015-07-19T19:49:37Z<p>Krysto: </p>
<hr />
<div>''Originally written by Jake Stine''<br />
<br />
The SPU2 is the Sound Processing Unit for the Playstation 2, and works a lot like the sound card in your own PC; albeit still quite unique in its approach to mixing sounds/voices and the programmable interface it provides for that. But the SPU2 is more than just sound. It's one of the more reliable timing mechanisms on the PS2 and games tend to use it as such. Without at least basic SPU2 emulation, no games will boot at all. This isn't too surprising if you understand how console hardware typically works, but what might be surprising is realizing how many games won't boot even with what appears to be fairly competent SPU2 emulation.<br />
<br />
Until SPU2-X 1.4, no SPU2 plugin had gone the distance on implementing IRQs (Interrupt Requests). IRQs are scheduled via specific SPU2 memory addresses. When a marked memory address is accessed anywhere in SPU2 memory (either read or write), the IRQ is signaled to the IOP. The most important IRQs on DMAs and audible voice playback have been supported for eons; without these no games would boot, period! Meanwhile, many of the lacking IRQ checks were known, but glossed over because of overhead required for the checks (a couple other checks were simply overlooked). The three main culprits for causing emulation errors were as follows:<br />
#the "free run" feature of SPU2 voices.<br />
#the write-back areas for each core's mixed output.<br />
#Reverb Processing, which uses a series of overlapping buffers to generate feedback.<br />
<br />
==Free Running Voices==<br />
<br />
The SPU2 has 48 total voices (24 voices for each core), plus two dedicated streaming audio input sources. Each voice can play a sound effect or stream audio, and can either be stopped, looping, or 'free running.' Free running voices typically zero out their volume rather than stopping or looping, and continue to 'play' forever (albeit silently). These free running voices access inaudible areas of SPU2 memory and thus trigger IRQs unexpectedly -- except, of course, some games are cleverly designed to expect these unexpected IRQs!<br />
<br />
Because of the overhead required to free-run otherwise silent voices, all other SPU2 plugins (until now!) have opted to ignore processing them. This is the feature that fixes Fatal Frame 2 (Project Zero 2) and a dozen more games.<br />
<br />
==Output Write-back Areas==<br />
<br />
The SPU2 defines a handful of special areas of memory where it writes back sound data at various stages of the mixing process. It's perfectly legal for a game to set an IRQ address within these buffers, and then expect it to trigger when the SPU2 does its write-back to that address. The write-back areas are mapped as follows:<br />
<br />
<source lang="cpp"><br />
0x0400 - 0x05FF : Core 0, Voice 1<br />
0x0600 - 0x07FF : Core 0, Voice 3<br />
0x0800 - 0x09FF : Core 0 Output (Left) [includes Wet/Dry/ADMA sources]<br />
0x0A00 - 0x0BFF : Core 0 Output (Right) [includes Wet/Dry/ADMA sources]<br />
0x0C00 - 0x0DFF : Core 1, Voice 1<br />
0x0E00 - 0x0FFF : Core 1, Voice 3<br />
<br />
// Following are results of mixing all 24 voices for the given Core.<br />
<br />
0x1000 - 0x11FF : Core 0, Dry Mix (Left)<br />
0x1200 - 0x13FF : Core 0, Dry Mix (Right)<br />
0x1400 - 0x15FF : Core 0, Wet Mix (Left)<br />
0x1600 - 0x17FF : Core 0, Wet Mix (Right)<br />
0x1800 - 0x19FF : Core 1, Dry Mix (Left)<br />
0x1A00 - 0x1BFF : Core 1, Dry Mix (Right)<br />
0x1C00 - 0x1DFF : Core 1, Wet Mix (Left)<br />
0x1E00 - 0x1FFF : Core 1, Wet Mix (Right)<br />
</source><br />
<br />
In specific, some games set an IRQA for Core0's write-back area. The IRQ can either be used as a timing mechanism, or as a synchronization point for post-processing audio effects. Most SPU2 plugins properly handled the write-backs, but overlooked the necessity of doing IRQ checks for them.<br />
<br />
==Reverb Processing==<br />
<br />
The SPU2 employs a clever reverberation algorithm that utilizes multiple overlapping read and writeback buffers within SPU2 memory to generate feedback. Each step of the reverb process accesses memory and must test against the IRQ address; for a grand total of 24 IRQ tests per Core. Fortunately, all reverb activity occurs within a specified area of SPU2 memory, so for most games a single simple test can be used to exclude the IRQ test.<br />
<br />
==And It All Applies to SPU2null!==<br />
<br />
This is the boring part that I'm going to look to implementing soon: In order for SPU2null to be fully emulation-compliant, it must properly simulate all of these things, which basically means it needs to have a complete sound mixer implemented; including reverb buffering/addressing logic. It probably seems silly, but SPU2null would still be without any platform dependent code or sound drivers, making it an ideal base for emulation analysis and as a base for future plugins.<br />
<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/SPU2_is_more_than_just_sound!&diff=38353PCSX2 Documentation/SPU2 is more than just sound!2015-07-19T19:49:01Z<p>Krysto: </p>
<hr />
<div>''Originally written by Jake Stine''<br />
<br />
The SPU2 is the Sound Processing Unit for the Playstation 2, and works a lot like the sound card in your own PC; albeit still quite unique in its approach to mixing sounds/voices and the programmable interface it provides for that. But the SPU2 is more than just sound. It's one of the more reliable timing mechanisms on the PS2 and games tend to use it as such. Without at least basic SPU2 emulation, no games will boot at all. This isn't too surprising if you understand how console hardware typically works, but what might be surprising is realizing how many games won't boot even with what appears to be fairly competent SPU2 emulation.<br />
<br />
Until SPU2-X 1.4, no SPU2 plugin had gone the distance on implementing IRQs (Interrupt Requests). IRQs are scheduled via specific SPU2 memory addresses. When a marked memory address is accessed anywhere in SPU2 memory (either read or write), the IRQ is signaled to the IOP. The most important IRQs on DMAs and audible voice playback have been supported for eons; without these no games would boot, period! Meanwhile, many of the lacking IRQ checks were known, but glossed over because of overhead required for the checks (a couple other checks were simply overlooked). The three main culprits for causing emulation errors were as follows:<br />
#the "free run" feature of SPU2 voices.<br />
#the write-back areas for each core's mixed output.<br />
#Reverb Processing, which uses a series of overlapping buffers to generate feedback.<br />
<br />
<br />
==Free Running Voices==<br />
<br />
The SPU2 has 48 total voices (24 voices for each core), plus two dedicated streaming audio input sources. Each voice can play a sound effect or stream audio, and can either be stopped, looping, or 'free running.' Free running voices typically zero out their volume rather than stopping or looping, and continue to 'play' forever (albeit silently). These free running voices access inaudible areas of SPU2 memory and thus trigger IRQs unexpectedly -- except, of course, some games are cleverly designed to expect these unexpected IRQs!<br />
<br />
Because of the overhead required to free-run otherwise silent voices, all other SPU2 plugins (until now!) have opted to ignore processing them. This is the feature that fixes Fatal Frame 2 (Project Zero 2) and a dozen more games.<br />
<br />
==Output Write-back Areas==<br />
<br />
The SPU2 defines a handful of special areas of memory where it writes back sound data at various stages of the mixing process. It's perfectly legal for a game to set an IRQ address within these buffers, and then expect it to trigger when the SPU2 does its write-back to that address. The write-back areas are mapped as follows:<br />
<br />
<source lang="cpp"><br />
0x0400 - 0x05FF : Core 0, Voice 1<br />
0x0600 - 0x07FF : Core 0, Voice 3<br />
0x0800 - 0x09FF : Core 0 Output (Left) [includes Wet/Dry/ADMA sources]<br />
0x0A00 - 0x0BFF : Core 0 Output (Right) [includes Wet/Dry/ADMA sources]<br />
0x0C00 - 0x0DFF : Core 1, Voice 1<br />
0x0E00 - 0x0FFF : Core 1, Voice 3<br />
<br />
// Following are results of mixing all 24 voices for the given Core.<br />
<br />
0x1000 - 0x11FF : Core 0, Dry Mix (Left)<br />
0x1200 - 0x13FF : Core 0, Dry Mix (Right)<br />
0x1400 - 0x15FF : Core 0, Wet Mix (Left)<br />
0x1600 - 0x17FF : Core 0, Wet Mix (Right)<br />
0x1800 - 0x19FF : Core 1, Dry Mix (Left)<br />
0x1A00 - 0x1BFF : Core 1, Dry Mix (Right)<br />
0x1C00 - 0x1DFF : Core 1, Wet Mix (Left)<br />
0x1E00 - 0x1FFF : Core 1, Wet Mix (Right)<br />
</source><br />
<br />
In specific, some games set an IRQA for Core0's write-back area. The IRQ can either be used as a timing mechanism, or as a synchronization point for post-processing audio effects. Most SPU2 plugins properly handled the write-backs, but overlooked the necessity of doing IRQ checks for them.<br />
<br />
==Reverb Processing==<br />
<br />
The SPU2 employs a clever reverberation algorithm that utilizes multiple overlapping read and writeback buffers within SPU2 memory to generate feedback. Each step of the reverb process accesses memory and must test against the IRQ address; for a grand total of 24 IRQ tests per Core. Fortunately, all reverb activity occurs within a specified area of SPU2 memory, so for most games a single simple test can be used to exclude the IRQ test.<br />
<br />
<br />
And It All Applies to SPU2null!<br />
<br />
This is the boring part that I'm going to look to implementing soon: In order for SPU2null to be fully emulation-compliant, it must properly simulate all of these things, which basically means it needs to have a complete sound mixer implemented; including reverb buffering/addressing logic. It probably seems silly, but SPU2null would still be without any platform dependent code or sound drivers, making it an ideal base for emulation analysis and as a base for future plugins.<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/64-bit_Recompilation&diff=38352PCSX2 Documentation/64-bit Recompilation2015-07-19T19:47:09Z<p>Krysto: </p>
<hr />
<div>''Originally written by ZeroFrog''<br />
<br />
Many 64 bit architectures have been proposed; however, the x86-64 (aka AMD64) architecture has picked up a lot of speed since its initial proposal a couple of years ago. Most 64bit CPUs today support it, so it looks like a good candidate for 64bit recompilation. The x86-64 architecture offers many more registers and can potentially speed up games by a significant amount. Up to now, Pcsx2 has largely been ignoring the 64 bit arena because there have been massive compatibility issues, the developers weren't sure if it was really worth it, and adding a new bug-free and fast recompiler to the existing code base is a very painful process. Anyone seriously suggesting this to a dev would have been laughed out of the chat room. However, the upcoming 0.9.2 release is looking very stable and after doing some research, we have decided to add support for x86-64 recompilation, both for 64bit versions of Linux and Windows (yes, Linux support is returning).<br />
<br />
Before going into technical details, I want to cover the current Pcsx2 recompilation model.<br />
<br />
==Pcsx2 Recompilation==<br />
<br />
Every different instruction set requires either an interpreter or a recompiler to execute it on the PC. Both are important in emulation. Interpreters are implemented with regular high-level languages and are platform independent. They are easy to program, easy to debug, but slow. They are extremely important for testing and debugging purposes. For example, interpreting a simple 32bit EE MIPS instruction (code) might look like:<br />
<br />
<source lang="cpp"><br />
switch(code>>26) {<br />
case 0x02: // J - jump to <br />
pc = (code & 0x03ffffff)*4; // change the program counter<br />
break;<br />
case 0x23: // LW - load word, sign extend<br />
gpr[Rt] = (long long)*(long*)(memory+gpr[Rs]+(short)code);<br />
break;<br />
...<br />
}<br />
</source><br />
<br />
Recompilers, on the other hand, try to cut as many corners as possible. For example, we know the instruction at address 0x1000 will never change, so there is no reason why the CPU needs to execute the switch statement and decode the instruction every single time it executes it. So recompilers generate the minimal amount of assembly the CPU needs to execute to emulate that instruction. Because we're working with assembly, recompilation is a very platform dependent process.<br />
<br />
Simple recompilers look at one instruction at a time and keep all target platform (in this case, the PS2) registers in memory. For every new instruction, the used registers are read from memory and stored in real CPU registers, then some instructions are executed, and finally the register with the result is stored back in memory. Before 0.9, Pcsx2 used to employ this type of recompilation.<br />
<br />
More complex recompilers divide the code into simple blocks (no jumps/branches) and try to preserve target platform registers across instructions in the real CPU registers. There are many different types of register allocation algorithms using graph coloring. Such compilers might also do constant propagation elimination. A common pattern in the MIPS Emotion Engine is something like:<br />
<br />
<source lang="asm"><br />
lui s0, 0x1000<br />
lw s0, 0x2000(s0)<br />
</source><br />
<br />
If we propagated the constants at the lw, we know that the read address is 0x10002000.<br />
<br />
A little more complex recompiler will know that 0x10002000 corresponds to the IPU, so the assembly will call the IPU straight away (without worrying about memory location translation).<br />
<br />
There are many such local optimizations, however they aren't enough. At the end of every block, all the registers will have to be pushed to memory because the next simple block that needs to be executed can't be predicted at recompilation time (ie: branch if x >= 0 depends on the value of x at runtime).<br />
<br />
An even more complex recompiler can work on the global scale by finding out which simple blocks are connected to which. Once it knows, it can get rid of the register flushing at the end of every simple block by simply telling the next blocks to allocate the same real CPU register to the same target platform register. This is called global register allocation and sometimes uses Markov blankets for block synchronization. For those people that know Bayes nets, this is very similar, except it applies to the global simple block graph. Just think about the nodes necessary for making a specific node independent with respect to the whole graph. This will include the node's parents, children, and the children's parents. For those that just got lost... don't worry.<br />
<br />
The Pcsx2 recompilers also use MMX and SSE(1/2/3) interchangeably. So an EE register can be in an MMX, SSE, or regular x86 register at any point in time depending on the current types of instructions (this is a nightmare to manage).<br />
<br />
Console emulators rarely need to go through such complex recompilers because up until a couple of years ago, consoles weren't that powerful. But starting with the PS2, consoles got powerful and the Pcsx2 recompilers for the EmotionEngine and Vectors Units got complex really fast. Pcsx2 0.9.1 supports all the above mentioned optimizations plus many more unmentioned ones. The VU recompiler (code named SuperVU) is by far the most complex and fastest. Anyone who wants to keep their sanity should stay away from it.<br />
<br />
For those that remember what it was like in the 0.8.1 days can appreciate how powerful the 0.9.1 Pcsx2 optimizations are.<br />
<br />
==x86-64==<br />
<br />
So why isn't x86-32 enough? Well, for starters the Playstation 2 EE has 32 128bit regular registers, 32 32bit floating point registers, and some COP0 registers. Most instructions work on 64 bits, the MMI instructions work on the full 128bits. On the other hand, the x86 CPU has 8 32bit general purpose registers (one is for stack), 8 64bit registers (MMX), and 8 128bit registers(SSE). And you can't combine the three that easily (ie: you can't add an x86 register with a SSE register before first transferring the x86 to SSE or vice versa). So there's a very big difference in registers sizes. Because of the small number of x86 registers, the recompiler does a lot of register thrashing (registers are spilled to memory very frequently). Each memory read/write is pretty slow, so the more thrashing, the slower the recompiler becomes. Also, x86-32 is inherently 32bit, so a 64bit add would require 2 32bit instructions and 4 regular x86 registers for the source and result (2 if reading from memory). The EE recompiler tries to alleviate the register pressure by using the 64bit arithmetic capabilities of MMX, but MMX has a pretty limited ISA and intra-register set transfers kill performance.<br />
<br />
The registers on the x86-64 architecture are: 16 64bit general purpose registers, 8 64bit MMX registers, and 16 128bit SSE registers. This amounts to twice the number of register memory! This means much less register thrashing. On top of that, 64bit adds/shifts/etc can all be done in one instruction.<br />
<br />
However, the story isn't as simple as it sounds. The recompiler has to interface with regular C++ code constantly (ie: calling plugin functions), so the calling conventions on the recompiler boundaries must be followed exactly. The x86-64 specification can be found here and is pretty straightforward. However, Microsoft decided that it wanted its own specification (for reasons not quite known to anyone else).. so now there are two different calling conventions with a different set of registers specifying arguments to functions and another different set acting as non-volatile data! (Thanks Microsoft, it wasn't difficult enough)<br />
<br />
Because the size of the registers changed, all pointers are now 64 bits, which adds many difficulties to reading and writing from memory, incrementing the stack, etc.<br />
<br />
Virtual memory is yet another obstacle to overcome with 64bit OSs. The AWE mapping trick (described in an early blog) has to be refined. But now that the address range is much bigger, there are less limitations. VM builds for Linux also need a completely new implementation.<br />
<br />
Finally, if anyone has seen Pcsx2 code, they would know that inline assembly is pretty frequent in the recompilers. The reasons we use inline assembly rather than C++ code are many. Actually, some things like dynamic dispatching become impossible to do with C++ code. So, inline is necessary... and it looks like Microsoft has disabled all functionality for inline assembly in 64bit editions of Visual C++!!!! (Thanks again Microsoft, you just know where to strike hardest)<br />
<br />
With all the mentioned challenges, it will take a couple of months to get things working reasonably stable. By that time, more people would have switched to 64bit OSs. If we're even half right in our estimates, Pcsx2 will run much faster on a 64bit OS than on a 32bit OS on the same computer once x86-64 recompilation is done.<br />
<br />
zerofrog,<br />
<br />
Moral of the blog Most recompiler theory discussed here actually comes straight from compiler theory. Compilers will always be necessary as long as engineers keep coming with new instruction set architectures (ISAs). Learn how a compiler works. I recommend Compilers: Principles, Techniques, and Tools by Alfred V. Aho, Ravi Sethi, and Jeffrey D. Ullman.<br />
<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/64-bit_Recompilation&diff=38351PCSX2 Documentation/64-bit Recompilation2015-07-19T19:46:39Z<p>Krysto: </p>
<hr />
<div>''Originally written by ZeroFrog''<br />
<br />
Many 64 bit architectures have been proposed; however, the x86-64 (aka AMD64) architecture has picked up a lot of speed since its initial proposal a couple of years ago. Most 64bit CPUs today support it, so it looks like a good candidate for 64bit recompilation. The x86-64 architecture offers many more registers and can potentially speed up games by a significant amount. Up to now, Pcsx2 has largely been ignoring the 64 bit arena because there have been massive compatibility issues, the developers weren't sure if it was really worth it, and adding a new bug-free and fast recompiler to the existing code base is a very painful process. Anyone seriously suggesting this to a dev would have been laughed out of the chat room. However, the upcoming 0.9.2 release is looking very stable and after doing some research, we have decided to add support for x86-64 recompilation, both for 64bit versions of Linux and Windows (yes, Linux support is returning).<br />
<br />
Before going into technical details, I want to cover the current Pcsx2 recompilation model.<br />
<br />
==Pcsx2 Recompilation==<br />
<br />
Every different instruction set requires either an interpreter or a recompiler to execute it on the PC. Both are important in emulation. Interpreters are implemented with regular high-level languages and are platform independent. They are easy to program, easy to debug, but slow. They are extremely important for testing and debugging purposes. For example, interpreting a simple 32bit EE MIPS instruction (code) might look like:<br />
<br />
<source lang="cpp"><br />
switch(code>>26) {<br />
case 0x02: // J - jump to <br />
pc = (code & 0x03ffffff)*4; // change the program counter<br />
break;<br />
case 0x23: // LW - load word, sign extend<br />
gpr[Rt] = (long long)*(long*)(memory+gpr[Rs]+(short)code);<br />
break;<br />
...<br />
}<br />
</source><br />
<br />
Recompilers, on the other hand, try to cut as many corners as possible. For example, we know the instruction at address 0x1000 will never change, so there is no reason why the CPU needs to execute the switch statement and decode the instruction every single time it executes it. So recompilers generate the minimal amount of assembly the CPU needs to execute to emulate that instruction. Because we're working with assembly, recompilation is a very platform dependent process.<br />
<br />
Simple recompilers look at one instruction at a time and keep all target platform (in this case, the PS2) registers in memory. For every new instruction, the used registers are read from memory and stored in real CPU registers, then some instructions are executed, and finally the register with the result is stored back in memory. Before 0.9, Pcsx2 used to employ this type of recompilation.<br />
<br />
More complex recompilers divide the code into simple blocks (no jumps/branches) and try to preserve target platform registers across instructions in the real CPU registers. There are many different types of register allocation algorithms using graph coloring. Such compilers might also do constant propagation elimination. A common pattern in the MIPS Emotion Engine is something like:<br />
<br />
<source lang="asm"><br />
lui s0, 0x1000<br />
lw s0, 0x2000(s0)<br />
</source><br />
<br />
If we propagated the constants at the lw, we know that the read address is 0x10002000.<br />
<br />
A little more complex recompiler will know that 0x10002000 corresponds to the IPU, so the assembly will call the IPU straight away (without worrying about memory location translation).<br />
<br />
There are many such local optimizations, however they aren't enough. At the end of every block, all the registers will have to be pushed to memory because the next simple block that needs to be executed can't be predicted at recompilation time (ie: branch if x >= 0 depends on the value of x at runtime).<br />
<br />
An even more complex recompiler can work on the global scale by finding out which simple blocks are connected to which. Once it knows, it can get rid of the register flushing at the end of every simple block by simply telling the next blocks to allocate the same real CPU register to the same target platform register. This is called global register allocation and sometimes uses Markov blankets for block synchronization. For those people that know Bayes nets, this is very similar, except it applies to the global simple block graph. Just think about the nodes necessary for making a specific node independent with respect to the whole graph. This will include the node's parents, children, and the children's parents. For those that just got lost... don't worry.<br />
<br />
The Pcsx2 recompilers also use MMX and SSE(1/2/3) interchangeably. So an EE register can be in an MMX, SSE, or regular x86 register at any point in time depending on the current types of instructions (this is a nightmare to manage).<br />
<br />
Console emulators rarely need to go through such complex recompilers because up until a couple of years ago, consoles weren't that powerful. But starting with the PS2, consoles got powerful and the Pcsx2 recompilers for the EmotionEngine and Vectors Units got complex really fast. Pcsx2 0.9.1 supports all the above mentioned optimizations plus many more unmentioned ones. The VU recompiler (code named SuperVU) is by far the most complex and fastest. Anyone who wants to keep their sanity should stay away from it.<br />
<br />
For those that remember what it was like in the 0.8.1 days can appreciate how powerful the 0.9.1 Pcsx2 optimizations are.<br />
<br />
==x86-64==<br />
<br />
So why isn't x86-32 enough? Well, for starters the Playstation 2 EE has 32 128bit regular registers, 32 32bit floating point registers, and some COP0 registers. Most instructions work on 64 bits, the MMI instructions work on the full 128bits. On the other hand, the x86 CPU has 8 32bit general purpose registers (one is for stack), 8 64bit registers (MMX), and 8 128bit registers(SSE). And you can't combine the three that easily (ie: you can't add an x86 register with a SSE register before first transferring the x86 to SSE or vice versa). So there's a very big difference in registers sizes. Because of the small number of x86 registers, the recompiler does a lot of register thrashing (registers are spilled to memory very frequently). Each memory read/write is pretty slow, so the more thrashing, the slower the recompiler becomes. Also, x86-32 is inherently 32bit, so a 64bit add would require 2 32bit instructions and 4 regular x86 registers for the source and result (2 if reading from memory). The EE recompiler tries to alleviate the register pressure by using the 64bit arithmetic capabilities of MMX, but MMX has a pretty limited ISA and intra-register set transfers kill performance.<br />
<br />
The registers on the x86-64 architecture are: 16 64bit general purpose registers, 8 64bit MMX registers, and 16 128bit SSE registers. This amounts to twice the number of register memory! This means much less register thrashing. On top of that, 64bit adds/shifts/etc can all be done in one instruction.<br />
<br />
However, the story isn't as simple as it sounds. The recompiler has to interface with regular C++ code constantly (ie: calling plugin functions), so the calling conventions on the recompiler boundaries must be followed exactly. The x86-64 specification can be found here and is pretty straightforward. However, Microsoft decided that it wanted its own specification (for reasons not quite known to anyone else).. so now there are two different calling conventions with a different set of registers specifying arguments to functions and another different set acting as non-volatile data! (Thanks Microsoft, it wasn't difficult enough)<br />
<br />
Because the size of the registers changed, all pointers are now 64 bits, which adds many difficulties to reading and writing from memory, incrementing the stack, etc.<br />
<br />
Virtual memory is yet another obstacle to overcome with 64bit OSs. The AWE mapping trick (described in an early blog) has to be refined. But now that the address range is much bigger, there are less limitations. VM builds for Linux also need a completely new implementation.<br />
<br />
Finally, if anyone has seen Pcsx2 code, they would know that inline assembly is pretty frequent in the recompilers. The reasons we use inline assembly rather than C++ code are many. Actually, some things like dynamic dispatching become impossible to do with C++ code. So, inline is necessary... and it looks like Microsoft has disabled all functionality for inline assembly in 64bit editions of Visual C++!!!! (Thanks again Microsoft, you just know where to strike hardest)<br />
<br />
With all the mentioned challenges, it will take a couple of months to get things working reasonably stable. By that time, more people would have switched to 64bit OSs. If we're even half right in our estimates, Pcsx2 will run much faster on a 64bit OS than on a 32bit OS on the same computer once x86-64 recompilation is done.<br />
<br />
zerofrog,<br />
<br />
Moral of the blog Most recompiler theory discussed here actually comes straight from compiler theory. Compilers will always be necessary as long as engineers keep coming with new instruction set architectures (ISAs). Learn how a compiler works. I recommend Compilers: Principles, Techniques, and Tools by Alfred V. Aho, Ravi Sethi, and Jeffrey D. Ullman.<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/Nightmare_on_Floating-Point_Street&diff=38350PCSX2 Documentation/Nightmare on Floating-Point Street2015-07-19T19:42:59Z<p>Krysto: </p>
<hr />
<div>''Originally written by ZeroFrog''<br />
<br />
It is very hard to emulate the floating-point calculations of the R5900 FPU and the Vector Units on an x86 CPU because the Playstation 2 does not follow the IEEE standard. Multiplying two numbers on the FPU, VU, and an x86 processor can give you 3 different results all differing by a couple of bits! Operations like square root and division are even more imprecise.<br />
<br />
Originally, we thought that a couple of bits shouldn't matter, that game developers would be crazy to rely on such precise calculation. Floating points are mostly used for world transformations or interpolation calculations, so no one would care if their Holy Sword of Armageddon was 0.00001 meters off from the main player's hand. To put it shortly, we were wrong and game developers are crazier than we thought. Games started breaking just by changing the floating point rounding mode!<br />
<br />
While rounding mode is a problem, the bigger nightmare is the floating-point infinities. The IEEE standard states that when a number overflows (meaning that it is larger than 3.4028234663852886E+38), the result will be infinity. Any number multiplied by infinity is infinity (even 0 * infinity = infinity). That sounds great until you figure out that the VUs don't support infinities. Instead they clamp all large numbers to the max floating point possible. This discrepancy breaks a lot of games!<br />
<br />
For example, let's say a game developer tries to normalize a zero vector by dividing by its length, which is 0. On the VU, the end result will be (0,0,0). On x86/IEEE, the result will be (infinity, infinity, infinity). Now if the game developer uses this vector to perturb some faces for artificial hair or some type of animation, all final positions on the PS2 will remain the same. All final positions on x86 will go to infinity... and there goes the game's graphics, now figure out where the problem occurred.<br />
<br />
The simplest solution is to clamp the written vector of the current instruction. This requires 2 SSE operations and is SLOW; and it doesn't work sometimes. To top it off, you can never dismiss the fact that game developers can be loading bad floating-point data to the VUs to begin with! Some games zero out vectors by multiplying them with a zero, so the VU doesn't care at all what kind of garbage the original vector's data has, x86 does care.<br />
<br />
These two problems make floating-point emulation very hard to do fast and accurate. The range of bugs are from screen flickering when a fade occurs, to disappearing characters, to spiky polygon syndrome (the most common problem and widely known as SPS).<br />
<br />
In the end Pcsx2 does all its floating-point operations with SSE since it is easier to cache the registers. Two different rounding modes are used for the FPU and VUs. Whenever a divide or rsqrt occur on the FPU, overflow is checked. Overflow is checked much more frequently with the VUs. The fact that VUs handle both integer and floating-point data in the same SSE register makes the checking a little longer. In the future, Pcsx2 will read the rounding mode and overflow settings from the patch files. This is so that all games can be accommodated with the best/fastest settings.<br />
<br />
'''Moral of the blog''' <br />
<br />
When comparing two floating point numbers a and b, never use <code>a == b</code>. Instead use something along the lines of <code>fabs(a-b)< epsilon</code> where epsilon is some very small number.<br />
<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/Virtual_Memory&diff=38349PCSX2 Documentation/Virtual Memory2015-07-19T19:42:19Z<p>Krysto: </p>
<hr />
<div>''Originally written by ZeroFrog''<br />
<br />
The Playstation 2 uses co-processor 0 to implement virtual paging. Even without COP0, the Playstation 2 memory map is pretty complex and the mapping can change depending on which processor you use to read the memory from. A simple version of how the default mapping looks from the Emotion Engine side is:<br />
*The 32Mb of main memory occupying 0000_0000 - 01ff_ffff<br />
*Hardware registers occupying 1000_0000 - 1000_ffff<br />
*VU/BIOS/SPU2 addresses in 1100_0000-1fff_ffff<br />
*Special kernel modes etc in 8000_0000-bfff_ffff<br />
*A scratch pad in some other address<br />
*...And of course can't forget the hidden addresses (thanks SONY)<br />
<br />
To make matters worse, these mappings can change depending on the setting of COP0. (Note that at the time of writing, Pcsx2 doesn't emulate even half of COP0 correctly.) The simplest and most straightforward way to emulate this is to have another memory layer through a software Translation-Lookaside-Buffer (TLB). You pass it the PS2 address, and out comes the real physical address or some special code signifying a hardware register, etc. The problem is that every read/write has to be preceded by a TLB lookup. Considering that reads/writes are as common as addition, that's a lot of wasted cycles.<br />
<br />
Well, the OS also uses virtual memory. In fact, every process has its own special virtual memory driven by a real hardware TLB. If we could get away by mapping the 4Gb PS2 memory map onto the process's virtual memory, we could eliminate the need for the software translation (Figure 1). Looking at the virtual manipulation functions Windows XP offers, there are two major problems with this:<br />
<br />
[[File:vmem.jpg]]<br />
<br />
#WindowsXP reserves more than half the address space for OS specific stuff. A good amount is also reserved for all of Pcsx2's working memory, executable code, and plugins (especially ZeroGS). It looks like we are left with less than 1.5 Gb of address range to implement the 4Gb PS2 memory map. Note that this problem doesn't exist on 64bit operating systems where the address range is practically... infinite (don't quote me on this 20 years down the road).<br />
#Playstation 2 allows more than one virtual page to point to the same physical page, Windows XP doesn't (I don't know about Linux). Assume that PS2 address 0x1000 points to the same physical page as address 0x0000, each page is 4Kb. Now a write occurs at 0x1000. The game can retrieve that same value just by reading from 0x0000. In Windows XP, this has to be two different pages; so unless some clever solution/technology is discovered, we could kiss our VM dreams goodbye.<br />
<br />
The first problem was solved somehow by introducing special address transformations before a read/write occurs.<br />
<br />
And thankfully a clever technology presented itself for the second problem: Address Windowing Extensions. This lets Pcsx2 handle the actual physical page instead of a virtual page. We still can't map two virtual pages to the same physical page; however, what we can do instead is switch the mapping of the physical page as many times as needed! To achieve this, Pcsx2 hacks into the root exception handler and intercepts every exception the program generates. Whenever an illegal virtual page is accessed (ie, no physical page mapped to it), Pcsx2 gets a EXCEPTION_ACCESS_VIOLATION then it remaps the correct physical page to that empty virtual page and returns. Although I haven't calculated precisely, I'm pretty sure that switching physical pages around is pretty expensive, computationally speaking. So all this works fine under the assumption that game developers won't be crazy and access two virtual pages mapping to the same physical page back-and-forth frequently... [pause].<br />
<br />
Alas, we were wrong... again (see floating-point article). It turns out that there are uncached and cached address ranges; so it is optimal to do such a bi-mapping trick: write in one virtual range and read from another. Pcsx2 tries to detect such cases and work around, but there's no clean solution.<br />
<br />
And I'm going to stop here before this becomes a book.<br />
<br />
So the ultimate question is: why doesn't VM work on some computers with 1Gb of RAM and the newest updates, while works on others? Turns out that real-time monitoring applications like to take up some of the 1.5 Gb of left over addresses on certain processes. (this might be OS specific programs too). I have also observed that performance/debugging monitors like NvPerfHud do similar tricks. There probably might be other reasons for VM builds of Pcsx2 not working because virtual memory is a pretty complicated issue.<br />
<br />
'''Moral of the blog''' <br />
<br />
Read an OS book. I recommend Operating System Concepts (the dinosaur book) by Abraham Silberschatz, Peter Baer Galvin, Greg Gagne.<br />
<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/MMU_mini-series&diff=38348PCSX2 Documentation/MMU mini-series2015-07-19T19:41:00Z<p>Krysto: </p>
<hr />
<div>''Originally written by Gregory''<br />
<br />
Hello PCSX2 followers,<br />
<br />
It's been a while since the last developer blog entry. I would like to resume this old tradition.<br />
<br />
I will present you a mini series on the MMU (memory management unit) and virtual memory. Jake and ZeroFrog already wrote some posts relating to this topic:<br />
* http://pcsx2.net/developer-blog/218-so-maybe-it-s-about-time-we-explained-vtlb.html<br />
* http://pcsx2.net/developer-blog/231-virtual-memory.html<br />
<br />
This time, I will explain the goal of MMU and why it is mandatory for modern systems. The MMU is a cornerstone for stability and security. I will also explain why it could be costly for the performance on the native system.<br />
<br />
On the next article, I will present you the PS2 address space and how the MMU is really used.<br />
<br />
Let's begin with the root, the Operating System (OS). Hardware can run without any OS but it is often a necessary evil. The OS has 2 major goals:<br />
<br />
1. The first goal of the OS is to control privilege access of processes.<br />
Maybe you want to keep a resource for the hardware or OS internal, for example a hardware watchdog. Maybe you only want to check the validity of a command. It could be as simple as only allowing a stop command after a start command.<br />
<br />
Let's see a basic example: starting some hardware counters<br />
<br />
[[File:Blog_Entry_23_November_2014.png]]<br />
<br />
As you can see, without any OS you could easily kill your system. The OS allows the separation of privileged and standard resources.<br />
<br />
2. The second goal is to share the resources between processes<br />
You need to understand that processes are independent for coding simplicity and security.<br />
Let's reuse our hardware counter example<br />
<br />
<br />
[[File:mmu-schematic2.png]]<br />
<br />
This time we want to start the same counter from two different threads. Unfortunately process 1 will be corrupted by process 2. It's pretty bad, process 1 will likely crash. Again the OS is the solution. It replaces the corruption with an error that can be handled by the process.<br />
<br />
Concluding this mini presentation, the OS is the gatekeeper of the hardware. The OS is a real god but be aware "with great power, comes great responsibility". System stability depends on the quality of the OS. On the contrary the process is isolated. A bad process will die alone, yes life is tough.<br />
<br />
Now let's talk about virtualization. In our 2nd example, the OS throws an error because the counter is already used by another process. However our system can support several counters, it is a shame to wait for the first counter to be available. The process wants to start a counter, it doesn't care which one. So it might be possible to handle this situation better. The solution is virtualization.<br />
<br />
Here's the idea, let's create a pool of virtual counters by process and then the OS will remap them to a real physical counter. As a bonus, we don't need to limit the number of counters based on the hardware. You can have 10 virtual counters for only 2 physical counters.<br />
<br />
Let's redo the previous example with a virtualization layer in the OS.<br />
<br />
[[File:mmu-schematic3.png]]<br />
<br />
As a side note, virtualization is a common solution for resource management. On the internet you have a logical IP address instead of using the Mac address directly. HDD access can be virtualized too with a tool like LVM http://en.wikipedia.org/wiki/Logical_Volume_Manager_%28Linux%29 . Sometimes the whole machine is virtual, see VirtualBox or even PCSX2.<br />
<br />
<br />
The hardware counter was an example but the subject of the blog is the MMU so let's get back to virtual memory. And yes you got it right, virtual memory is the same concept as we discussed earlier. The process will access virtual memory. The OS will translate it to the physical address.<br />
<br />
I said that the OS will remap the virtual memory to physical memory but that is not exactly correct. Memory access is very common in a process, let's say every 2 or 5 instructions. Imagine if the kernel did a lookup of the physical address every 4 instructions, it would be slow as hell. Actually it is even worse because the program counter (PC) is also a virtual address. Software is deemed to be slow, it needs to be hardware accelerated.<br />
<br />
Here comes the MMU that allows the CPU to do the conversion on the fly. The MMU uses a dedicated cache named TLB (Translation Lookup-aside Buffer). However a hardware cache is very costly, you can't store a physical address for each logical address, besides remember that the virtual memory could be bigger than the physical memory. So the memory address space is split in pages, often 4K but various CPUs can support several page sizes. The PS2 supports 7 sizes of pages from 4K to 16MB.<br />
<br />
A TLB miss will cost you hundreds of CPU cycles and for good reason, a 4GB virtual space requires 1 million table entries of 4K-pages. A common trick to reduce cache misses is to increase the page size so you can fit "more memory" in the TLB cache. However there is a terrible issue, the waste of resources. One page is an atomic resource, that means either the page is free or the page is locked by the process. If the process allocates a single byte on a 16MB page, nearly 16MB is lost for other processes. It's not a real solution for the PC which uses hundreds of processes but that is very different for the PS2. We will discuss this next time.<br />
<br />
See below a typical use model of the MMU:<br />
<br />
[[File:mmu-schematic4.png]]<br />
<br />
<br />
The MMU is a nice and complex solution to handle memory access. But there is more. You can attach some flags to each page. For example on a modern PC, you have readable/writable/executable. The PS2 doesn't support all those flags, yes it's not modern anymore, however it has another class of flag that controls cache behavior: cacheable/uncached accelerated/uncached. Note for completeness sake that the PS2 also has read only flags. It is a powerful mechanism but we will discuss the implications on the next chapter.<br />
<br />
<br />
Mini quiz:<br />
* The first 512KB of RAM is not virtually mapped because it is at the location of the OS, do you know how the kernel will access this location?<br />
* A hardware component needs to access the memory without the CPUs intervention. Do you know which one? Do you know how the addresses are handled?<br />
<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/MMU_mini-series&diff=38347PCSX2 Documentation/MMU mini-series2015-07-19T19:40:25Z<p>Krysto: </p>
<hr />
<div>''Originally written by Gregory''<br />
<br />
Hello PCSX2 followers,<br />
<br />
It's been a while since the last developer blog entry. I would like to resume this old tradition.<br />
<br />
I will present you a mini series on the MMU (memory management unit) and virtual memory. Jake and ZeroFrog already wrote some posts relating to this topic:<br />
* http://pcsx2.net/developer-blog/218-so-maybe-it-s-about-time-we-explained-vtlb.html<br />
* http://pcsx2.net/developer-blog/231-virtual-memory.html<br />
<br />
This time, I will explain the goal of MMU and why it is mandatory for modern systems. The MMU is a cornerstone for stability and security. I will also explain why it could be costly for the performance on the native system.<br />
<br />
On the next article, I will present you the PS2 address space and how the MMU is really used.<br />
<br />
Let's begin with the root, the Operating System (OS). Hardware can run without any OS but it is often a necessary evil. The OS has 2 major goals:<br />
<br />
1/ The first goal of the OS is to control privilege access of processes.<br />
Maybe you want to keep a resource for the hardware or OS internal, for example a hardware watchdog. Maybe you only want to check the validity of a command. It could be as simple as only allowing a stop command after a start command.<br />
<br />
Let's see a basic example: starting some hardware counters<br />
<br />
[[File:Blog_Entry_23_November_2014.png]]<br />
<br />
As you can see, without any OS you could easily kill your system. The OS allows the separation of privileged and standard resources.<br />
<br />
2/ The second goal is to share the resources between processes<br />
You need to understand that processes are independent for coding simplicity and security.<br />
Let's reuse our hardware counter example<br />
<br />
<br />
[[File:mmu-schematic2.png]]<br />
<br />
This time we want to start the same counter from two different threads. Unfortunately process 1 will be corrupted by process 2. It's pretty bad, process 1 will likely crash. Again the OS is the solution. It replaces the corruption with an error that can be handled by the process.<br />
<br />
Concluding this mini presentation, the OS is the gatekeeper of the hardware. The OS is a real god but be aware "with great power, comes great responsibility". System stability depends on the quality of the OS. On the contrary the process is isolated. A bad process will die alone, yes life is tough.<br />
<br />
Now let's talk about virtualization. In our 2nd example, the OS throws an error because the counter is already used by another process. However our system can support several counters, it is a shame to wait for the first counter to be available. The process wants to start a counter, it doesn't care which one. So it might be possible to handle this situation better. The solution is virtualization.<br />
<br />
Here's the idea, let's create a pool of virtual counters by process and then the OS will remap them to a real physical counter. As a bonus, we don't need to limit the number of counters based on the hardware. You can have 10 virtual counters for only 2 physical counters.<br />
<br />
Let's redo the previous example with a virtualization layer in the OS.<br />
<br />
[[File:mmu-schematic3.png]]<br />
<br />
As a side note, virtualization is a common solution for resource management. On the internet you have a logical IP address instead of using the Mac address directly. HDD access can be virtualized too with a tool like LVM http://en.wikipedia.org/wiki/Logical_Volume_Manager_%28Linux%29 . Sometimes the whole machine is virtual, see VirtualBox or even PCSX2.<br />
<br />
<br />
The hardware counter was an example but the subject of the blog is the MMU so let's get back to virtual memory. And yes you got it right, virtual memory is the same concept as we discussed earlier. The process will access virtual memory. The OS will translate it to the physical address.<br />
<br />
I said that the OS will remap the virtual memory to physical memory but that is not exactly correct. Memory access is very common in a process, let's say every 2 or 5 instructions. Imagine if the kernel did a lookup of the physical address every 4 instructions, it would be slow as hell. Actually it is even worse because the program counter (PC) is also a virtual address. Software is deemed to be slow, it needs to be hardware accelerated.<br />
<br />
Here comes the MMU that allows the CPU to do the conversion on the fly. The MMU uses a dedicated cache named TLB (Translation Lookup-aside Buffer). However a hardware cache is very costly, you can't store a physical address for each logical address, besides remember that the virtual memory could be bigger than the physical memory. So the memory address space is split in pages, often 4K but various CPUs can support several page sizes. The PS2 supports 7 sizes of pages from 4K to 16MB.<br />
<br />
A TLB miss will cost you hundreds of CPU cycles and for good reason, a 4GB virtual space requires 1 million table entries of 4K-pages. A common trick to reduce cache misses is to increase the page size so you can fit "more memory" in the TLB cache. However there is a terrible issue, the waste of resources. One page is an atomic resource, that means either the page is free or the page is locked by the process. If the process allocates a single byte on a 16MB page, nearly 16MB is lost for other processes. It's not a real solution for the PC which uses hundreds of processes but that is very different for the PS2. We will discuss this next time.<br />
<br />
See below a typical use model of the MMU:<br />
<br />
[[File:mmu-schematic4.png]]<br />
<br />
<br />
The MMU is a nice and complex solution to handle memory access. But there is more. You can attach some flags to each page. For example on a modern PC, you have readable/writable/executable. The PS2 doesn't support all those flags, yes it's not modern anymore, however it has another class of flag that controls cache behavior: cacheable/uncached accelerated/uncached. Note for completeness sake that the PS2 also has read only flags. It is a powerful mechanism but we will discuss the implications on the next chapter.<br />
<br />
<br />
Mini quiz:<br />
* The first 512KB of RAM is not virtually mapped because it is at the location of the OS, do you know how the kernel will access this location?<br />
* A hardware component needs to access the memory without the CPUs intervention. Do you know which one? Do you know how the addresses are handled?<br />
<br />
{{PCSX2 Developers Blog Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/Path_3_Masking_/_Geometry_Syncing&diff=38346PCSX2 Documentation/Path 3 Masking / Geometry Syncing2015-07-19T19:39:14Z<p>Krysto: </p>
<hr />
<div>''Originally written by refraction''<br />
<br />
I promised myself ages ago I would write a blog about this, more for the PS2 community than anything else as this seems to be almost a dark art in terms of understanding how it works.<br />
<br />
Some of you may be familiar with Path 3 masking, some of you may not. In any versions around 0.9.4 or older, have you ever played a game where say some of the floor textures have had writing on them or just looked like absolute crap? Well the likelihood is that was due to Path 3 Masking problems. For those not familiar, here is a picture example of Persona 4 displaying Path 3 masking issues.<br />
<br />
[[File:personapath3.jpg]]<br />
<br />
==So, what is it exactly?==<br />
<br />
Path 3 masking is a method of synchronizing the order in which geometry data (polygons etc) and the textures that go on them are sent to the GS (Graphical Synthesizer). It was a pretty efficient method of transferring information, which took completely different path routes to make optimal use of the PS2 Memory BUS and used a stalling method to keep the texture and geometry lists in order instead of interrupts which took extra cpu time. This way, developers could queue up massive amounts of textures and stream them to the GS in time with the geometry data efficiently.<br />
<br />
Here is a badly drawn diagram showing what the Mask does:<br />
<br />
[[File:Path3mask.jpg]]<br />
<br />
As you can see, the mask stops PATH3 from transferring while the VU is busy, then lets it go and almost immediately puts the mask back on, this ensures that at the end of the texture transfer, PATH3 stops again!<br />
<br />
==So what was the problem? Why did everything look like crap?==<br />
<br />
Truthfully? we never had proper control over these packets. The GIF packets can come through the PATH3 DMA channel lumped together, where in actual fact, we should have been stalling half way through it, but in the emulator, we didn't really care what these packets were, so we just threw it all at the GS and ignored any packet ends and this is what caused the Texture/Geometry lists to go out of sync, this is where the crap on the screen came from. As the emulator evolved it became progressively easier to fix this issue, although we had the theory down, developers would handle it in different ways with different timings for the masks, so it took a fair amount of testing and tweaking to get right.<br />
<br />
Fortunately these days we have got this pretty much solved and we completely analyze the GIF Packets before sending them on their way, so we know where we can stop the transfers, however we do still get the odd time where this rears its ugly head, due to how we have to time everything perfectly, but due to the nature of emulators, timing is painful to get correct and will probably never be so. Generally however, using gamefixes such as "EE Timing Fix" can get around these issues, without compromising too much on stability.<br />
<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/Path_3_Masking_/_Geometry_Syncing&diff=38345PCSX2 Documentation/Path 3 Masking / Geometry Syncing2015-07-19T19:38:54Z<p>Krysto: </p>
<hr />
<div>''Originally written by refraction''<br />
<br />
I promised myself ages ago I would write a blog about this, more for the PS2 community than anything else as this seems to be almost a dark art in terms of understanding how it works.<br />
<br />
Some of you may be familiar with Path 3 masking, some of you may not. In any versions around 0.9.4 or older, have you ever played a game where say some of the floor textures have had writing on them or just looked like absolute crap? Well the likelihood is that was due to Path 3 Masking problems. For those not familiar, here is a picture example of Persona 4 displaying Path 3 masking issues.<br />
<br />
[[File:personapath3.jpg]]<br />
<br />
So, what is it exactly?<br />
<br />
Path 3 masking is a method of synchronizing the order in which geometry data (polygons etc) and the textures that go on them are sent to the GS (Graphical Synthesizer). It was a pretty efficient method of transferring information, which took completely different path routes to make optimal use of the PS2 Memory BUS and used a stalling method to keep the texture and geometry lists in order instead of interrupts which took extra cpu time. This way, developers could queue up massive amounts of textures and stream them to the GS in time with the geometry data efficiently.<br />
<br />
Here is a badly drawn diagram showing what the Mask does:<br />
<br />
[[File:Path3mask.jpg]]<br />
<br />
As you can see, the mask stops PATH3 from transferring while the VU is busy, then lets it go and almost immediately puts the mask back on, this ensures that at the end of the texture transfer, PATH3 stops again!<br />
<br />
So what was the problem? Why did everything look like crap?<br />
<br />
Truthfully? we never had proper control over these packets. The GIF packets can come through the PATH3 DMA channel lumped together, where in actual fact, we should have been stalling half way through it, but in the emulator, we didn't really care what these packets were, so we just threw it all at the GS and ignored any packet ends and this is what caused the Texture/Geometry lists to go out of sync, this is where the crap on the screen came from. As the emulator evolved it became progressively easier to fix this issue, although we had the theory down, developers would handle it in different ways with different timings for the masks, so it took a fair amount of testing and tweaking to get right.<br />
<br />
Fortunately these days we have got this pretty much solved and we completely analyze the GIF Packets before sending them on their way, so we know where we can stop the transfers, however we do still get the odd time where this rears its ugly head, due to how we have to time everything perfectly, but due to the nature of emulators, timing is painful to get correct and will probably never be so. Generally however, using gamefixes such as "EE Timing Fix" can get around these issues, without compromising too much on stability.<br />
<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/MSVC_2008_optimizer_fail&diff=38344PCSX2 Documentation/MSVC 2008 optimizer fail2015-07-19T19:38:13Z<p>Krysto: </p>
<hr />
<div>''Originally written by Jake Stine''<br />
<br />
Over the past two years I have become dearly intimate with Microsoft's Visual C++ 2008 compiler, and the methods it uses for optimizing code. Now generally speaking MSVC 2008 does well -- very well -- especially for everyday "not-so-clever" code. Its global optimization feature (aka Linktime Code Generation, or LTCG) is also a tremendous advantage over GCC -- though GCC is in the process of (finally!) adding LTCG to their own C/C++ compiler. MSVC does have a few very annoying failings as an optimizer, though. The most glaring of which has to do with templated code and inlined functions.<br />
<br />
'''Disclaimer:''' This analysis is for Visual C++ 2008 only. I have not yet analyzed MSVC 2010's code generation. Some of these glitches may be improved or different (or worse even) in 2010. I'll post an update if/when I compile information it in the future.<br />
<br />
'''Edit/Update:''' This bug only appears to manifest itself when the input parameters are 1st or 2nd generation propagated constants (which is hard to explain if you don't know what that means). So chances of hitting this bug are not actually all that common, but still plausible in many coding scenarios.<br />
<br />
==Inline Functions==<br />
<br />
Inline functions are the simpler sort, so I'll cover those first. Here's a simple example of some code that will be optimized away in certain situations.<br />
<br />
<source lang="cpp"><br />
static bool g_global = false;<br />
__forceinline void DoSomething( void* dest, size_t size )<br />
{<br />
if (dest &amp;&amp; size) memset(dest,0,size);<br />
}<br />
<br />
void main()<br />
{<br />
[... code ...]<br />
<br />
// dest and size are known constants, so the compiler will inline the above <br />
// function and eliminate all its code -- ie, this line will be effectively ignored.<br />
DoSomething( NULL, 0 );<br />
<br />
[... code ...]<br />
}<br />
</source><br />
<br />
The problem is that even though the DoSomething() call is effectively ignored, Visual C++ will still generate code that assumes the function is modifying global memory. Why? Because the compiler's initial analysis of the function doesn't take into consideration the fact that it is being called/inlined with constants as parameters. That means the calling function (void main() in this case)will have to flush/reload any global variables that may have otherwise been able to remain in registers.<br />
<br />
This problem becomes worse the longer a function grows, because every new piece of code int he function can introduce additional optimization dependencies. For example, if a function contains SSE instructions and 128-bit stack operations, it may require mandatory stack-frame alignment, even if the actual SSE code portions are optimized away.<br />
<br />
==Templates==<br />
<br />
For those who do not know, C++ (and C99) has a feature called templating; which is at its core a type-safe and debug-friendly replacement for macros. PCSX2 uses templates extensively to generate function call dispatches for various customizable features of the PS2. A common technique in templates is to use switch statements to simplify code:<br />
<br />
<source lang="cpp"><br />
template&lt; uint value > void Dispatch()<br />
{<br />
[.. setup code ..]<br />
<br />
switch(value)<br />
{<br />
case 1: [.. do stuff ..] break;<br />
case 2: [.. do stuff ..] break;<br />
case 3: [.. do stuff ..] break;<br />
}<br />
<br />
[.. cleanup code..]<br />
}<br />
</source><br />
<br />
In the above example, we've created a function that executes one of four possible actions. The only thing that changes between each action is the interior -- all actions share the same basic setup/cleanup code. Instead of using separate functions and/or macros to do four separate instances of the setup and cleanup code, we're able to merge everything into a single template function. The compiler will automatically optimize the function to use only the selected path. If 'value' is 1, it runs switch case 1. If it is 0, the entire switch is disregarded, etc.<br />
<br />
The problem is the same as with the inlined function above: Visual C++'s optimizer bases a lot of its optimization on the whole function anyway, so dead code that isn't even part of a particular template can adversely impact MSVC's code generation strategy. If only one of the switch cases modifies global memory, any call to any other case will still result in the compiler flushing global registers. Fortunately this particular optimization is minor, and losing it has barely any noticeable impact on performance on modern CPUs.<br />
<br />
==Sparse Switches and Binary Irony==<br />
<br />
A second and more serious optimization failure occurs in templated/inlined functions, however; if the function happens to use sparse switches. A sparse switch is one where the values are not contigious. Example:<br />
<br />
<source lang="cpp"><br />
switch(value)<br />
{<br />
case 0x0: if(toggle) { code; } break;<br />
case 0x100: if(toggle) { code; } break;<br />
case 0x101: if(toggle) { code; } break;<br />
case 0x102: if(toggle) { code; } break;<br />
case 0x520: if(toggle) { code; } break;<br />
case 0x521: if(toggle) { code; } break;<br />
case 0x522: if(toggle) { code; } break;<br />
case 0x733: if(toggle) { code; } break;<br />
}<br />
</source><br />
<br />
In this example, MSVC's optimizer will employ the use of a binary search to dispatch the switch. Rather than compare each value individually (8 compares), it will divide the switch into halves or quarters. The resulting optimized code typically finds the right case in two compares, with a worst case of 3-5 compares typically (a vast improvement over an individual linear search, which has a median of 4 compares and worst case of 8 compares). This a great and wonderful optimization and is often times faster than using function lookup tables. Smile<br />
<br />
... but it actually backfires if the toggle value is a known constant (such as a template parameter). The optimization method of the switch statement is made by MSVC 2008 before it eliminates unused code. So even if you explicitly assign a value of 0x101, MSVC 2008 will include its clever binary partition logic! The resulting pseudo-code generated by the MSVC optimizer ends up looking something like this:<br />
<br />
<source lang="cpp"><br />
if(value >= 0x520) return;<br />
if(value &lt; 0x100) return;<br />
<br />
return; // which is the result of case 0x101 with toggle==false;<br />
</source><br />
<br />
The explicit checks for equality are optimized out, as are all unused cases -- just the umbrella binary search logic remains, and all it does is return from the function without doing anything. So what should be a null function ends up having 2 pointless compares; ironically caused by a clever and highly effective optimization strategy in any other normal situation.<br />
<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/MSVC_2008_optimizer_fail&diff=38343PCSX2 Documentation/MSVC 2008 optimizer fail2015-07-19T19:37:47Z<p>Krysto: </p>
<hr />
<div>''Originally written by Jake Stine''<br />
<br />
Over the past two years I have become dearly intimate with Microsoft's Visual C++ 2008 compiler, and the methods it uses for optimizing code. Now generally speaking MSVC 2008 does well -- very well -- especially for everyday "not-so-clever" code. Its global optimization feature (aka Linktime Code Generation, or LTCG) is also a tremendous advantage over GCC -- though GCC is in the process of (finally!) adding LTCG to their own C/C++ compiler. MSVC does have a few very annoying failings as an optimizer, though. The most glaring of which has to do with templated code and inlined functions.<br />
<br />
'''Disclaimer:''' This analysis is for Visual C++ 2008 only. I have not yet analyzed MSVC 2010's code generation. Some of these glitches may be improved or different (or worse even) in 2010. I'll post an update if/when I compile information it in the future.<br />
<br />
'''Edit/Update:''' This bug only appears to manifest itself when the input parameters are 1st or 2nd generation propagated constants (which is hard to explain if you don't know what that means). So chances of hitting this bug are not actually all that common, but still plausible in many coding scenarios.<br />
<br />
<br />
==Inline Functions==<br />
<br />
Inline functions are the simpler sort, so I'll cover those first. Here's a simple example of some code that will be optimized away in certain situations.<br />
<br />
<source lang="cpp"><br />
static bool g_global = false;<br />
__forceinline void DoSomething( void* dest, size_t size )<br />
{<br />
if (dest &amp;&amp; size) memset(dest,0,size);<br />
}<br />
<br />
void main()<br />
{<br />
[... code ...]<br />
<br />
// dest and size are known constants, so the compiler will inline the above <br />
// function and eliminate all its code -- ie, this line will be effectively ignored.<br />
DoSomething( NULL, 0 );<br />
<br />
[... code ...]<br />
}<br />
</source><br />
<br />
The problem is that even though the DoSomething() call is effectively ignored, Visual C++ will still generate code that assumes the function is modifying global memory. Why? Because the compiler's initial analysis of the function doesn't take into consideration the fact that it is being called/inlined with constants as parameters. That means the calling function (void main() in this case)will have to flush/reload any global variables that may have otherwise been able to remain in registers.<br />
<br />
This problem becomes worse the longer a function grows, because every new piece of code int he function can introduce additional optimization dependencies. For example, if a function contains SSE instructions and 128-bit stack operations, it may require mandatory stack-frame alignment, even if the actual SSE code portions are optimized away.<br />
<br />
==Templates==<br />
<br />
For those who do not know, C++ (and C99) has a feature called templating; which is at its core a type-safe and debug-friendly replacement for macros. PCSX2 uses templates extensively to generate function call dispatches for various customizable features of the PS2. A common technique in templates is to use switch statements to simplify code:<br />
<br />
<source lang="cpp"><br />
template&lt; uint value > void Dispatch()<br />
{<br />
[.. setup code ..]<br />
<br />
switch(value)<br />
{<br />
case 1: [.. do stuff ..] break;<br />
case 2: [.. do stuff ..] break;<br />
case 3: [.. do stuff ..] break;<br />
}<br />
<br />
[.. cleanup code..]<br />
}<br />
</source><br />
<br />
In the above example, we've created a function that executes one of four possible actions. The only thing that changes between each action is the interior -- all actions share the same basic setup/cleanup code. Instead of using separate functions and/or macros to do four separate instances of the setup and cleanup code, we're able to merge everything into a single template function. The compiler will automatically optimize the function to use only the selected path. If 'value' is 1, it runs switch case 1. If it is 0, the entire switch is disregarded, etc.<br />
<br />
The problem is the same as with the inlined function above: Visual C++'s optimizer bases a lot of its optimization on the whole function anyway, so dead code that isn't even part of a particular template can adversely impact MSVC's code generation strategy. If only one of the switch cases modifies global memory, any call to any other case will still result in the compiler flushing global registers. Fortunately this particular optimization is minor, and losing it has barely any noticeable impact on performance on modern CPUs.<br />
<br />
==Sparse Switches and Binary Irony==<br />
<br />
A second and more serious optimization failure occurs in templated/inlined functions, however; if the function happens to use sparse switches. A sparse switch is one where the values are not contigious. Example:<br />
<br />
<source lang="cpp"><br />
switch(value)<br />
{<br />
case 0x0: if(toggle) { code; } break;<br />
case 0x100: if(toggle) { code; } break;<br />
case 0x101: if(toggle) { code; } break;<br />
case 0x102: if(toggle) { code; } break;<br />
case 0x520: if(toggle) { code; } break;<br />
case 0x521: if(toggle) { code; } break;<br />
case 0x522: if(toggle) { code; } break;<br />
case 0x733: if(toggle) { code; } break;<br />
}<br />
</source><br />
<br />
In this example, MSVC's optimizer will employ the use of a binary search to dispatch the switch. Rather than compare each value individually (8 compares), it will divide the switch into halves or quarters. The resulting optimized code typically finds the right case in two compares, with a worst case of 3-5 compares typically (a vast improvement over an individual linear search, which has a median of 4 compares and worst case of 8 compares). This a great and wonderful optimization and is often times faster than using function lookup tables. Smile<br />
<br />
... but it actually backfires if the toggle value is a known constant (such as a template parameter). The optimization method of the switch statement is made by MSVC 2008 before it eliminates unused code. So even if you explicitly assign a value of 0x101, MSVC 2008 will include its clever binary partition logic! The resulting pseudo-code generated by the MSVC optimizer ends up looking something like this:<br />
<br />
<source lang="cpp"><br />
if(value >= 0x520) return;<br />
if(value &lt; 0x100) return;<br />
<br />
return; // which is the result of case 0x101 with toggle==false;<br />
</source><br />
<br />
The explicit checks for equality are optimized out, as are all unused cases -- just the umbrella binary search logic remains, and all it does is return from the function without doing anything. So what should be a null function ends up having 2 pointless compares; ironically caused by a clever and highly effective optimization strategy in any other normal situation.<br />
<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/PS2%27s_Programmable_DMA&diff=38342PCSX2 Documentation/PS2's Programmable DMA2015-07-19T19:35:28Z<p>Krysto: </p>
<hr />
<div>''Originally written by Jake Stine''<br />
<br />
For those who don't know, DMA stands for Direct Memory Access, and it refers to logic circuits in a computer that allow for the automated transfer of system memory to and from peripherals. DMAs are beneficial because they are simple circuits that do work in parallel to the CPU -- while a DMA transfers data, the CPU is free to do other work.that requires more complex computations and logic. The end result is better utilization of the computer's maximum memory transfer bandwidth and computational/logical ability.<br />
<br />
Traditionally DMAs are pretty simple. The Playstation 2's EmotionEngine, however, has an 'intelligent' programmable DMA controller (DMAC). Neatly translated, it means that the DMAC can do a lot more than just move raw data from place to place. It supports several modes of operation and has a number of special features to take advantage of the unique multi-core design of the EE. Furthermore, the EE's DMAC is much more tightly integrated with its memory bus than traditional DMAs, allowing it to transfer data with exceptional efficiency. These two features combined make the EE's DMAC a key component to PS2 games developers -- in quite a few games, the DMAC actually does more raw work than the EE Core CPU (R5900).<br />
<br />
==How The Real Thing Works==<br />
<br />
While emulating the actual hardware of the DMAC isn't usually needed, it can still be helpful to understand exactly how the PS2's real DMAC works at a hardware level. The EE DMAC operates at 147mhz (1/2th the EE's core clock speed), and transfers 128 bits (16 bytes) of memory per cycle; meaning that the theoretical maximum transfer rate of the DMAC is 2.4 GB/s (147mhz * 16 bytes). It's a nice number, but is technically unattainable even in ideal conditions. Further explanation will make it clear why.<br />
<br />
The DMAC connects the PS2's 32 MB of Main Memory (RAM) to various peripheral interfaces, such as VIF (VPU), SIF (IOP), GIF (GS), and IPU (mpeg decoder). VIF, GIF, and IPU are all part of the Emotion Engine and operate at 147mhz, same as the DMAC itself. Thus each of those interfaces can send/receive data at roughly 2.4GB/s. SIF is limited by the IOP's own DMA controller and memry bus, which operates at 1/8th the speed of the EE's DMAC, or about 154MB/s.<br />
<br />
==Peripheral FIFOs==<br />
<br />
Each peripheral (VIF, GIF, SIF, IPU, etc) has a 128 or 256 byte FIFO. The FIFO helps mitigate occasional latency differences between Main Memory/SPRAM and the peripheral (some peripherals, in particular the GIF, can incur cycle stalls depending on data sent to them). Thanks to the FIFOs, data can be burst to/from memory in 128-byte blocks, which helps maximize data transfer rates since the EE's memory bus was built to operate most efficiently in those conditions. However, the maximum bandwidth of Main Memory (32MB) in ideal conditions is only ~1.2GB/s (half of the DMAC), and has additional memory bank related latencies, reducing its effective transfer rates even further. If DMA transfers are only done to/from Main Memory, the DMAC will only be able to come within about 40% of its theoretical maximum throughput.<br />
<br />
==Enter the Scratchpad!==<br />
<br />
The Scratchpad (SPRAM) is 16KB of memory integrated directly into the EmotionEngine. Because it is directly integrated on-die, it has no read/write latencies and can always be accessed at the maximum transfer rate of 2.4gb/s. The integrated nature of the SPRAM means it has to be small in order to fit -- and its lack of size is what limits its usefulness.<br />
<br />
So in order to utilize the bandwidth potential of the EE DMAC, a PS2 programmer must find ways to use a combination of Main Memory and Scratchpad transfers in parallel: When main memory stalls due to inherent latencies, the DMAC will automatically busy itself with a pending SPRAM transfer. Likewise, while the DMAC is transferring to/from SPRAM, the EE's Main Memory becomes available to the CPU, which further improves the system's CPU throughput.<br />
<br />
==The Scratchpad's MemoryFIFO (MFIFO)==<br />
<br />
The MemoryFIFO function of the EE DMAC performs and managed two simultaneous DMA transfers, as follows:<br />
*Scratchpad -> Main Memory (RAM)<br />
*Main Memory (RAM) -> Peripheral (VIF1 or GIF)<br />
<br />
As the buffer in memory is filled by Scratchpad, it is simultaneously drained by the attached peripheral, either VIF1 or GIF. On the surface, the MFIFO can appear to be somewhat silly, since the DMAC already has the ability to transfer direcly from SPRAM -> Peripheral. Adding a stop in Main Memory might seem like a waste of the DMAC's bandwidth capacity, but in some situations the 'extra work' can result in a general improvement in overall transfer speeds.<br />
<br />
The PS2 engineers introduced the MFIFO for two reasons:<br />
<br />
#The scratchpad is too small. MFIFO can be used by the EE core as a place to "empty" the scratchpad after its completed a set of data processing. While the data in the MFIFO awaits the DMAC to transfer it, the EE is free to load new raw data into Scratchpad for processing.<br />
#The GIF has additional bandwidth constraints since it has direct connections to three PATHs: the the VU1 co-processor (GIF PATH1), VIF1 FIFO (GIF PATH2), and the DMAC's GIF channel (GIF PATH3). When transfers are active on any one of the paths, the other two paths must idle/stall until the current path's transfer completes; meaning that DMAC transfers to both GIF and VIF1 channels can have unexpectedly long stalls.<br />
<br />
So by using MFIFO, the EE core can mitigate the unpredictable GIF/VIF1 stalls while it works on entirely new sets of data in parallel. If a GIF transfer via DMA is stalled because of other PATH1 or PATH2 transfers, the DMAC can busy itself with other transfers in meantime, such as SPRAM->memory or memory->SPRAM. These transfers are nearly 'free' in a sense, since the DMAC would have been idle regardless -- but thanks to the MFIFO concept, the SPRAM itself will be free for use by the EE Core to continue processing data. Thus while the DMAC's overall productivity isn't affected, the EE's overall computational ability improves.<br />
<br />
I'll talk a bit more on actual emulation details of the PS2's programmable DMA controller in future blogs, so this is To Be Continued...<br />
<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/VirtualAlloc_on_Linux&diff=38341PCSX2 Documentation/VirtualAlloc on Linux2015-07-19T19:33:33Z<p>Krysto: </p>
<hr />
<div>''Originally written by Jake Stine''<br />
<br />
Yes, there is a way to simulate Microsoft's VirtualAlloc behavior on Linux. Much searching of the internet did not reveal a satisfactory answer; only hints that when combined with some applied tests of my own yielded the following result:<br />
<br />
<source lang="cpp"><br />
// to RESERVE memory in Linux, use mmap with a private, anonymous, non-accessible mapping.<br />
// The following line reserves 1gb of ram starting at 0x10000000.<br />
<br />
void* result = mmap((void*)0x10000000, 0x40000000, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);<br />
<br />
// to COMMIT memory in Linux, use mprotect on the range of memory you'd like to commit, and<br />
// grant the memory READ and/or WRITE access.<br />
// The following line commits 1mb of the buffer. It will return -1 on out of memory errors.<br />
<br />
int result3 = mprotect((void*)0x10000000, 0x100000, PROT_READ | PROT_WRITE);<br />
</source><br />
<br />
When using mmap, you can create a simple uncommitted reservation of memory simply by specifying PROT_NONE on any anonymous mapping (in the world of mmap, anonymous means it has no associated file/pipe -- it's just a memory block). This is sufficient for reserving a large contiguous address range from being fragmented up by the likes of malloc. Granting the memory read and/or write privileges tells Linux to commit the memory (equivalent to VirtualAlloc with MEM_COMMIT). If there is not enough system memory to complete the call, it returns -1.<br />
<br />
Oddly enough, though, Linux makes it so that it isn't even necessary to bother with the above solution, via a strange little hacky technique called...<br />
<br />
==Over-committing Memory==<br />
<br />
This 'feature' is enabled by default in most modern Linux kernels (anything 2.6 or newer). Basically all this means is that Linux will let programs commit a lot more RAM than is actually available to the operating system! Instead of performing a "strict contract" on commit that says "oh yes we absolutely have this much ram available", Linux looks at the ram and looks at the request, and makes some arbitrary judgement call on if the program will actually use that much ram or not. In other words, just because your call to malloc returned a valid non-NULL pointer doesn't mean there's actually anywhere near that much memory available to your app. It just means that Linux doesn't think you're going to use that much.<br />
<br />
Instead, as a program references its allocated memory, Linux commits the memory on-demand. Most of the time, programs that malloc huge amounts of ram only use a wee bit of it, so that's fine. By using overcommitted memory management, Linux avoids the dreaded "Low on virtual memory!" error that can sometimes plague Windows. This is actually highly ideal for apps like PCSX2 and the Java virtual machine, for example. Kudos!<br />
<br />
.. oh but things do get fun if apps over-step their bounds!<br />
<br />
Thanks to over-committing, Linux programs that run out of memory do not get error codes or NULL pointers. Instead they will typically be KILLED INSTANTLY by the kernel. They do not get out of memory errors, and they don't even get SIGSEGV or anything else that can be handled or logged. They just DIE -- because doing anything else would risk system stability. So in the long run, its still a good idea to use the Reserve/Commit management strategy even on Linux (mmap / mprotect as described above); because your app will be more likely to get proper out-of-memory errors instead of just causing itself (and possibly other processes on the system) to die suddenly and without warning or error.<br />
<br />
Another positive for the the above mmap / mprotect example is that it will also work well on Linux systems that have over-commit disabled, since it basically does what over-commit does but without the hacky "programs die instantly without error" part if the system runs out of physical memory.<br />
<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/VirtualAlloc_on_Linux&diff=38340PCSX2 Documentation/VirtualAlloc on Linux2015-07-19T19:33:04Z<p>Krysto: </p>
<hr />
<div>''Originally written by Jake Stine''<br />
<br />
Yes, there is a way to simulate Microsoft's VirtualAlloc behavior on Linux. Much searching of the internet did not reveal a satisfactory answer; only hints that when combined with some applied tests of my own yielded the following result:<br />
<br />
<source lang="cpp"><br />
// to RESERVE memory in Linux, use mmap with a private, anonymous, non-accessible mapping.<br />
// The following line reserves 1gb of ram starting at 0x10000000.<br />
<br />
void* result = mmap((void*)0x10000000, 0x40000000, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);<br />
<br />
// to COMMIT memory in Linux, use mprotect on the range of memory you'd like to commit, and<br />
// grant the memory READ and/or WRITE access.<br />
// The following line commits 1mb of the buffer. It will return -1 on out of memory errors.<br />
<br />
int result3 = mprotect((void*)0x10000000, 0x100000, PROT_READ | PROT_WRITE);<br />
</source><br />
<br />
When using mmap, you can create a simple uncommitted reservation of memory simply by specifying PROT_NONE on any anonymous mapping (in the world of mmap, anonymous means it has no associated file/pipe -- it's just a memory block). This is sufficient for reserving a large contiguous address range from being fragmented up by the likes of malloc. Granting the memory read and/or write privileges tells Linux to commit the memory (equivalent to VirtualAlloc with MEM_COMMIT). If there is not enough system memory to complete the call, it returns -1.<br />
<br />
Oddly enough, though, Linux makes it so that it isn't even necessary to bother with the above solution, via a strange little hacky technique called...<br />
<br />
==Over-committing Memory==<br />
<br />
This 'feature' is enabled by default in most modern Linux kernels (anything 2.6 or newer). Basically all this means is that Linux will let programs commit a lot more RAM than is actually available to the operating system! Instead of performing a "strict contract" on commit that says "oh yes we absolutely have this much ram available", Linux looks at the ram and looks at the request, and makes some arbitrary judgement call on if the program will actually use that much ram or not. In other words, just because your call to malloc returned a valid non-NULL pointer doesn't mean there's actually anywhere near that much memory available to your app. It just means that Linux doesn't think you're going to use that much.<br />
<br />
Instead, as a program references its allocated memory, Linux commits the memory on-demand. Most of the time, programs that malloc huge amounts of ram only use a wee bit of it, so that's fine. By using overcommitted memory management, Linux avoids the dreaded "Low on virtual memory!" error that can sometimes plague Windows. This is actually highly ideal for apps like PCSX2 and the Java virtual machine, for example. Kudos!<br />
<br />
.. oh but things do get fun if apps over-step their bounds!<br />
<br />
Thanks to over-committing, Linux programs that run out of memory do not get error codes or NULL pointers. Instead they will typically be KILLED INSTANTLY by the kernel. They do not get out of memory errors, and they don't even get SIGSEGV or anything else that can be handled or logged. They just DIE -- because doing anything else would risk system stability. So in the long run, its still a good idea to use the Reserve/Commit management strategy even on Linux (mmap / mprotect as described above); because your app will be more likely to get proper out-of-memory errors instead of just causing itself (and possibly other processes on the system) to die suddenly and without warning or error.<br />
<br />
Another positive for the the above mmap / mprotect example is that it will also work well on Linux systems that have over-commit disabled, since it basically does what over-commit does but without the hacky "programs die instantly without error" part if the system runs out of physical memory.<br />
<br />
<br />
{{PCSX2 Developers Blog Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/Advanced_memory_management&diff=38339PCSX2 Documentation/Advanced memory management2015-07-19T19:31:34Z<p>Krysto: </p>
<hr />
<div>''Originally written by Jake Stine''<br />
<br />
Being an emulator of a fairly robust system (the PS2), PCSX2 typically consumes a lot of system RAM. It needs multitudes of caches and buffers for various things. Just to give an idea, I'll list some of the larger stuff and their current defaults:<br />
*PS2 main memory [32mb]<br />
*IOP memory [2mb]<br />
*EE/IOP BIOS roms [6mb]<br />
*Scratchpad, Hardware registers, VU memory, DMA buffers, etc [4mb]<br />
*VTLB indexes, lookups, and protection tables [8mb]<br />
*EE/R5900 recompiler cache [16mb]<br />
*EE/R5900 recompiler block/pc translation table [48mb]<br />
*R5900 memory protection mirror [32mb]<br />
*IOP/R3000A recompiler cache and translation table [10mb]<br />
*microVU recompiler code caches [16mb * 2]<br />
*superVU recompiler code caches [8mb]<br />
<br />
If all of these things are reserved when PCSX2 starts, we have a base memory footprint of over 200 megs before even a single instruction of PS2 code is executed! The worst part is that we could really stand to allocate even more ram: some games need over 120 mb of recompiler caches to run properly. Currently those games are dealt with by issuing periodic recompiler resets (sluggish).<br />
<br />
Fortunately modern operating systems have a lot of built-in features that help us out. Both Windows and Linux OSes use virtual memory mapping features of our Intel/AMD cpus to perform "virtual" allocations of large memory reserves. What this means is that initially the allocated memory has no actual physical equivalent. It is only given a physical presence once the memory is accessed (read or written). Explained as a process:<br />
#App requests 1gb of RAM via malloc.<br />
#Operating system "reserves" the 1gb of RAM, which marks the virtual addresses for use by this memory only. In this case the memory might be reserved from 0x10000000 (0.4gb) -> 0x50000000 (1.4gb).<br />
#Operating system "commits" the 1gb of RAM, ensuring there is enough physical and swapfile RAM to accommodate it. No actual memory or swapfile changes are made; only the tracked amount of ram/swap in reserve is altered.<br />
#App receives a pointer to the reserved ram.<br />
#App reads or writes data -- 128mb worth, let's say.<br />
#OS receives a page fault exception for that memory, and allocates a chunk of physical RAM for it. Other processes may be swapped out to disk at this time to make room for the memory in-use.<br />
<br />
Only at Point 5 does any actual physical ram get used by the program. Prior to Point 5, the app has used exactly 0 byte of RAM, in spite of allocating 1gb via malloc. This feature is implicit to both Windows and Linux and already helps work wonders on PCSX2's overall memory footprint. This is also why you might get "Low on virtual memory!" errors even though it appears as though you have lots of free ram in the System Monitor / Process Explorer, because some apps commit lots of memory but only actually access a small fraction of it.<br />
<br />
There are ways, however, to fine tune memory access and get even better memory management than the implicit Windows / Linux provisions via malloc. The first rule is a simple one, but one many programmers probably have no idea about it:<br />
<br />
Do not use calloc, and do not clear allocated memory by default unless you absolutely have to!<br />
<br />
Calling calloc instead of malloc causes the entire allocation (1gb in our above example) to be committed to physical memory because of its being cleared to zero. Likewise, manually clearing buffers to zero (or some other value) has the same effect. Even if only a small portion of the array ends up being used later on, its too late: the whole thing is sucking up resources for no good reason except to express a patterned fill value. Sometimes clearing buffers cannot be avoided, but most of the time buffers need not be cleared at all, and programmers simply use calloc or manual clears out of habit.<br />
<br />
==Using Reserve and Commit to manage recompiled code buffers.==<br />
<br />
There are two phases to allocating memory on a virtual memory system, as noted in the small ordered list above. By default, malloc will reserve and commit ram together. This is done so that the system can ensure that there is enough ram and swap to free to give the program the entire allocation -- if it happens to ever need it. If the commit phase fails due to there not being enough physical ram, malloc returns NULL. If you manage the reserve and commit phases separately, then you can reserve extra large swatches of memory addresses without affecting the rest of the operating system in any way; and then later on commit portions of the reserve only as needed. There aren't a whole lot of reasons why you'd need to micro-manage the virtual memory system in this way, and for most purposes simply using malloc and letting the OS do its own internal management suffices nicely. Lucky for us, PCSX2 has one!<br />
<br />
One of the troubles with recompiled code is that it can't be allowed to move. Typically use of malloc and realloc results in allocated memory moving around as it grows or shrinks. This is fine for most purposes, but is disastrous to executable code since it invalidates all block pointers and long jumps (which use absolute addressing). In order to grow a recompiled code cache using traditional malloc, you have to clear the cache and start over -- a recompiler reset. This usually causes a lengthy hiccup in emulation speed when it happens.<br />
<br />
Virtual memory techniques can be used to get around that. When we reserve the recompiled code cache, we reserve the upper limit of what we deem a sane cache size. In this case, the R5900 cache should be a maximum of 48mb. The 48mb is reserved from 0x30000000->0x33000000 when PCSX2 starts, the first 4mb are committed when PCSX2 starts executing R5900 code. When the cache fills, PCSX2 automatically commits more memory in 128k increments, up to 48mb -- at which point the emulator will reset the cache and start over. Thanks to the virtual memory strategy described above, only a fraction of the 48meg allocation actually exists in physical ram unless more of the allocation is actually needed. Furthermore, computers with limited RAM resources or disabled swapfiles will still be able to run PCSX2 nicely.<br />
<br />
Committing blocks of memory from the 48meg reserve never alters the base address of the memory, so no pointers become invalid, and no memory needs to be copied or shuffled in order to make room for the larger caches. The end result is near instantaneous increases in cache size, on-the-fly! ... and all-the-while maintaining compact and efficient memory footprint for games that don't need more than the basic caches.<br />
<br />
On Windows this technique is implemented using VirtualAlloc, which is fairly well documented via the linked MSDN page. On Linux, however, things get a bit strange. The technique can be implemented using a combination of mmap and mprotect, but unfortunately the Linux man pages lack any actual explanation of how to perform independent reserve and commit actions (but rest assured, it can be done). Furthermore, Linux has an implicit system enabled by default called Over-committing, which basically skips phase (3) described above -- and always returns a valid pointer on calls to malloc, even if the system hasn't enough ram to accommodate the request.<br />
<br />
Over-committing is so surpassingly hacky and evil that it deserves a blog post all to itself, so stay tuned. ;)<br />
<br />
To be continued...<br />
<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/The_return_of_the_Commandline!&diff=38338PCSX2 Documentation/The return of the Commandline!2015-07-19T19:27:38Z<p>Krysto: </p>
<hr />
<div>''Originally written by Jake Stine''<br />
<br />
After its absence for many moons, the Commandline functionality will finally be restored to PCSX2. Third-party frontend and config-manager authors rejoice! ... and hopefully stop hating my guts, too. To paraphrase Darth Vader: '''"Witness the power of this fully armed and operational Command line-driven battlestation."''' (we all know a command line-driven death star would have been way cooler than some click-and-drag crap.) The new PCSX2 command line should be functional in our next beta release, which should be out pretty soon, and it will work as follows:<br />
<br />
'''Syntax:''' <br />
<code>pcsx2 [IsoFile] --toggle --option=value ... etc</code><br /><br />
Where <code>[IsoFile]</code> is an optional ISO image to load and run on startup; uses the PCSX2 internal ISO loader.<br />
<br />
<br />
General Options:<br />
*'''<code>--cfg=[file]</code>''' specify a custom configuration file to use instead of PCSX2.ini (does not affect plugins)<br />
*'''<code>--cfgpath=[dir]</code>''' specifies the config folder; applies to pcsx2 + plugins<br />
*'''<code>--help</code>''' display this help text<br />
*'''<code>--forcewiz</code>''' forces running of the First-time Wizard (selection of docs folders and what-not)<br />
<br />
Auto-Run Options:<br />
*'''<code>--elf=[file]</code>''' executes an ELF image<br />
*'''<code>--nogui</code>''' disables display of the gui on exit (program auto-exits)<br />
*'''<code>--nodisc</code>''' boots with an empty dvd tray; use this to boot into the PS2 system menu<br />
*'''<code>--usecd</code>''' uses the configured CDVD plugin instead of IsoFile<br />
<br />
Compatibility Options:<br />
*'''<code>--nohacks</code>''' disables all speedhacks<br />
*'''<code>--gamefixes=[fix,fix]</code>''' Enable specific gamefixes for this session. Valid fixes in 0.9.7 are: VuAddSub, VuClipFlag, FpuCompare, FpuNegDiv, XGKick, IpuWait, EETiming, SkipMpeg<br />
*'''<code>--fullboot</code>''' disables the quick boot feature, forcing you to sit through the PS2 startup splash screens<br />
<br />
Plugin Overrides (specified dlls will be used in place of configured dlls):<br />
*'''<code>--cdvd=[dllpath]</code>''' override for the CDVD plugin<br />
*'''<code>--gs=[dllpath]</code>''' override for the GS plugin<br />
*'''<code>--spu=[dllpath]</code>''' override for the SPU2 plugin<br />
*'''<code>--pad=[dllpath]</code>''' override for the PAD plugin only<br />
*'''<code>--dev9=[dllpath]</code>''' override for the DEV9 plugin<br />
*'''<code>--usb=[dllpath]</code>''' override for the USB plugin only<br />
<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/The_return_of_the_Commandline!&diff=38337PCSX2 Documentation/The return of the Commandline!2015-07-19T19:27:24Z<p>Krysto: </p>
<hr />
<div>''Originally written by Jake Stine''<br />
<br />
After its absence for many moons, the Commandline functionality will finally be restored to PCSX2. Third-party frontend and config-manager authors rejoice! ... and hopefully stop hating my guts, too. To paraphrase Darth Vader: '''"Witness the power of this fully armed and operational Command line-driven battlestation."''' (we all know a command line-driven death star would have been way cooler than some click-and-drag crap.) The new PCSX2 command line should be functional in our next beta release, which should be out pretty soon, and it will work as follows:<br />
<br />
'''Syntax:''' <br />
<code>pcsx2 [IsoFile] --toggle --option=value ... etc</code><br /><br />
Where <code>[IsoFile]</code> is an optional ISO image to load and run on startup; uses the PCSX2 internal ISO loader.<br />
<br />
<br />
General Options:<br />
*'''<code>--cfg=[file]</code>''' specify a custom configuration file to use instead of PCSX2.ini (does not affect plugins)<br />
*'''<code>--cfgpath=[dir]</code>''' specifies the config folder; applies to pcsx2 + plugins<br />
*'''<code>--help</code>''' display this help text<br />
*'''<code>--forcewiz</code>''' forces running of the First-time Wizard (selection of docs folders and what-not)<br />
<br />
Auto-Run Options:<br />
*'''<code>--elf=[file]</code>''' executes an ELF image<br />
*'''<code>--nogui</code>''' disables display of the gui on exit (program auto-exits)<br />
*'''<code>--nodisc</code>''' boots with an empty dvd tray; use this to boot into the PS2 system menu<br />
*'''<code>--usecd</code>''' uses the configured CDVD plugin instead of IsoFile<br />
<br />
Compatibility Options:<br />
*'''<code>--nohacks</code>''' disables all speedhacks<br />
*'''<code>--gamefixes=[fix,fix]</code>''' Enable specific gamefixes for this session. Valid fixes in 0.9.7 are: VuAddSub, VuClipFlag, FpuCompare, FpuNegDiv, XGKick, IpuWait, EETiming, SkipMpeg<br />
*'''<code>--fullboot</code>''' disables the quick boot feature, forcing you to sit through the PS2 startup splash screens<br />
<br />
Plugin Overrides (specified dlls will be used in place of configured dlls):<br />
*'''<code>--cdvd=[dllpath]</code>''' override for the CDVD plugin<br />
*'''<code>--gs=[dllpath]</code>''' override for the GS plugin<br />
*'''<code>--spu=[dllpath]</code>''' override for the SPU2 plugin<br />
*'''<code>--pad=[dllpath]</code>''' override for the PAD plugin only<br />
*'''<code>--dev9=[dllpath]</code>''' override for the DEV9 plugin<br />
*'''<code>--usb=[dllpath]</code>''' override for the USB plugin only<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/The_return_of_the_Commandline!&diff=38336PCSX2 Documentation/The return of the Commandline!2015-07-19T19:26:59Z<p>Krysto: </p>
<hr />
<div>''Originally written by Jake Stine''<br />
<br />
After its absence for many moons, the Commandline functionality will finally be restored to PCSX2. Third-party frontend and config-manager authors rejoice! ... and hopefully stop hating my guts, too. To paraphrase Darth Vader: '''"Witness the power of this fully armed and operational Command line-driven battlestation."''' (we all know a command line-driven death star would have been way cooler than some click-and-drag crap.) The new PCSX2 command line should be functional in our next beta release, which should be out pretty soon, and it will work as follows:<br />
<br />
'''Syntax:''' <br />
<code>pcsx2 [IsoFile] --toggle --option=value ... etc</code><br /><br />
Where <code>[IsoFile]</code> is an optional ISO image to load and run on startup; uses the PCSX2 internal ISO loader.<br />
<br />
<br />
'''General Options:'''<br />
*'''<code>--cfg=[file]</code>''' specify a custom configuration file to use instead of PCSX2.ini (does not affect plugins)<br />
*'''<code>--cfgpath=[dir]</code>''' specifies the config folder; applies to pcsx2 + plugins<br />
*'''<code>--help</code>''' display this help text<br />
*'''<code>--forcewiz</code>''' forces running of the First-time Wizard (selection of docs folders and what-not)<br />
<br />
'''Auto-Run Options:'''<br />
*'''<code>--elf=[file]</code>''' executes an ELF image<br />
*'''<code>--nogui</code>''' disables display of the gui on exit (program auto-exits)<br />
*'''<code>--nodisc</code>''' boots with an empty dvd tray; use this to boot into the PS2 system menu<br />
*'''<code>--usecd</code>''' uses the configured CDVD plugin instead of IsoFile<br />
<br />
'''Compatibility Options:'''<br />
*'''<code>--nohacks</code>''' disables all speedhacks<br />
*'''<code>--gamefixes=[fix,fix]</code>''' Enable specific gamefixes for this session. Valid fixes in 0.9.7 are: VuAddSub, VuClipFlag, FpuCompare, FpuNegDiv, XGKick, IpuWait, EETiming, SkipMpeg<br />
*'''<code>--fullboot</code>''' disables the quick boot feature, forcing you to sit through the PS2 startup splash screens<br />
<br />
'''Plugin Overrides (specified dlls will be used in place of configured dlls):'''<br />
*'''<code>--cdvd=[dllpath]</code>''' override for the CDVD plugin<br />
*'''<code>--gs=[dllpath]</code>''' override for the GS plugin<br />
*'''<code>--spu=[dllpath]</code>''' override for the SPU2 plugin<br />
*'''<code>--pad=[dllpath]</code>''' override for the PAD plugin only<br />
*'''<code>--dev9=[dllpath]</code>''' override for the DEV9 plugin<br />
*'''<code>--usb=[dllpath]</code>''' override for the USB plugin only<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/The_return_of_the_Commandline!&diff=38335PCSX2 Documentation/The return of the Commandline!2015-07-19T19:26:15Z<p>Krysto: </p>
<hr />
<div>''Originally written by Jake Stine''<br />
<br />
After its absence for many moons, the Commandline functionality will finally be restored to PCSX2. Third-party frontend and config-manager authors rejoice! ... and hopefully stop hating my guts, too. To paraphrase Darth Vader: '''"Witness the power of this fully armed and operational Command line-driven battlestation."''' (we all know a command line-driven death star would have been way cooler than some click-and-drag crap.) The new PCSX2 command line should be functional in our next beta release, which should be out pretty soon, and it will work as follows:<br />
<br />
'''Syntax:''' <br />
<code>pcsx2 [IsoFile] --toggle --option=value ... etc</code><br /><br />
Where <code>[IsoFile]</code> is an optional ISO image to load and run on startup; uses the PCSX2 internal ISO loader.<br />
<br />
'''General Options:'''<br />
*'''<code>--cfg=[file]</code>''' specify a custom configuration file to use instead of PCSX2.ini (does not affect plugins)<br />
*'''<code>--cfgpath=[dir]</code>''' specifies the config folder; applies to pcsx2 + plugins<br />
*'''<code>--help</code>''' display this help text<br />
*'''<code>--forcewiz</code>''' forces running of the First-time Wizard (selection of docs folders and what-not)<br />
<br />
Auto-Run Options :<br />
*'''<code>--elf=[file]</code>''' executes an ELF image<br />
*'''<code>--nogui</code>''' disables display of the gui on exit (program auto-exits)<br />
*'''<code>--nodisc</code>''' boots with an empty dvd tray; use this to boot into the PS2 system menu<br />
*'''<code>--usecd</code>''' uses the configured CDVD plugin instead of IsoFile<br />
<br />
Compatibility Options:<br />
*'''<code>--nohacks</code>''' disables all speedhacks<br />
*'''<code>--gamefixes=[fix,fix]</code>''' Enable specific gamefixes for this session. Valid fixes in 0.9.7 are: VuAddSub, VuClipFlag, FpuCompare, FpuNegDiv, XGKick, IpuWait, EETiming, SkipMpeg<br />
*'''<code>--fullboot</code>''' disables the quick boot feature, forcing you to sit through the PS2 startup splash screens<br />
<br />
Plugin Overrides (specified dlls will be used in place of configured dlls):<br />
*'''<code>--cdvd=[dllpath]</code>''' override for the CDVD plugin<br />
*'''<code>--gs=[dllpath]</code>''' override for the GS plugin<br />
*'''<code>--spu=[dllpath]</code>''' override for the SPU2 plugin<br />
*'''<code>--pad=[dllpath]</code>''' override for the PAD plugin only<br />
*'''<code>--dev9=[dllpath]</code>''' override for the DEV9 plugin<br />
*'''<code>--usb=[dllpath]</code>''' override for the USB plugin only<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/The_return_of_the_Commandline!&diff=38334PCSX2 Documentation/The return of the Commandline!2015-07-19T19:25:56Z<p>Krysto: </p>
<hr />
<div>''Originally written by Jake Stine''<br />
<br />
After its absence for many moons, the Commandline functionality will finally be restored to PCSX2. Third-party frontend and config-manager authors rejoice! ... and hopefully stop hating my guts, too. To paraphrase Darth Vader: '''"Witness the power of this fully armed and operational Command line-driven battlestation."''' (we all know a command line-driven death star would have been way cooler than some click-and-drag crap.) The new PCSX2 command line should be functional in our next beta release, which should be out pretty soon, and it will work as follows:<br />
<br />
'''Syntax:''' <br />
<code>pcsx2 [IsoFile] --toggle --option=value ... etc</code><br />
<br />
Where <code>[IsoFile]</code> is an optional ISO image to load and run on startup; uses the PCSX2 internal ISO loader.<br />
<br />
'''General Options:'''<br />
*'''<code>--cfg=[file]</code>''' specify a custom configuration file to use instead of PCSX2.ini (does not affect plugins)<br />
*'''<code>--cfgpath=[dir]</code>''' specifies the config folder; applies to pcsx2 + plugins<br />
*'''<code>--help</code>''' display this help text<br />
*'''<code>--forcewiz</code>''' forces running of the First-time Wizard (selection of docs folders and what-not)<br />
<br />
Auto-Run Options :<br />
*'''<code>--elf=[file]</code>''' executes an ELF image<br />
*'''<code>--nogui</code>''' disables display of the gui on exit (program auto-exits)<br />
*'''<code>--nodisc</code>''' boots with an empty dvd tray; use this to boot into the PS2 system menu<br />
*'''<code>--usecd</code>''' uses the configured CDVD plugin instead of IsoFile<br />
<br />
Compatibility Options:<br />
*'''<code>--nohacks</code>''' disables all speedhacks<br />
*'''<code>--gamefixes=[fix,fix]</code>''' Enable specific gamefixes for this session. Valid fixes in 0.9.7 are: VuAddSub, VuClipFlag, FpuCompare, FpuNegDiv, XGKick, IpuWait, EETiming, SkipMpeg<br />
*'''<code>--fullboot</code>''' disables the quick boot feature, forcing you to sit through the PS2 startup splash screens<br />
<br />
Plugin Overrides (specified dlls will be used in place of configured dlls):<br />
*'''<code>--cdvd=[dllpath]</code>''' override for the CDVD plugin<br />
*'''<code>--gs=[dllpath]</code>''' override for the GS plugin<br />
*'''<code>--spu=[dllpath]</code>''' override for the SPU2 plugin<br />
*'''<code>--pad=[dllpath]</code>''' override for the PAD plugin only<br />
*'''<code>--dev9=[dllpath]</code>''' override for the DEV9 plugin<br />
*'''<code>--usb=[dllpath]</code>''' override for the USB plugin only<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/The_return_of_the_Commandline!&diff=38333PCSX2 Documentation/The return of the Commandline!2015-07-19T19:24:58Z<p>Krysto: </p>
<hr />
<div>''Originally written by Jake Stine''<br />
<br />
After its absence for many moons, the Commandline functionality will finally be restored to PCSX2. Third-party frontend and config-manager authors rejoice! ... and hopefully stop hating my guts, too. To paraphrase Darth Vader: '''"Witness the power of this fully armed and operational Command line-driven battlestation."''' (we all know a command line-driven death star would have been way cooler than some click-and-drag crap.) The new PCSX2 command line should be functional in our next beta release, which should be out pretty soon, and it will work as follows:<br />
<br />
Syntax: <code>pcsx2 [IsoFile] --toggle --option=value ... etc</code><br />
<br />
<code>IsoFile</code> - optional ISO image to load and run on startup; uses the PCSX2 internal ISO loader.<br />
<br />
General Options :<br />
*'''<code>--cfg=[file]</code>''' specify a custom configuration file to use instead of PCSX2.ini (does not affect plugins)<br />
*'''<code>--cfgpath=[dir]</code>''' specifies the config folder; applies to pcsx2 + plugins<br />
*'''<code>--help</code>''' display this help text<br />
*'''<code>--forcewiz</code>''' forces running of the First-time Wizard (selection of docs folders and what-not)<br />
<br />
Auto-Run Options :<br />
*'''<code>--elf=[file]</code>''' executes an ELF image<br />
*'''<code>--nogui</code>''' disables display of the gui on exit (program auto-exits)<br />
*'''<code>--nodisc</code>''' boots with an empty dvd tray; use this to boot into the PS2 system menu<br />
*'''<code>--usecd</code>''' uses the configured CDVD plugin instead of IsoFile<br />
<br />
Compatibility Options:<br />
*'''<code>--nohacks</code>''' disables all speedhacks<br />
*'''<code>--gamefixes=[fix,fix]</code>''' Enable specific gamefixes for this session. Valid fixes in 0.9.7 are: VuAddSub, VuClipFlag, FpuCompare, FpuNegDiv, XGKick, IpuWait, EETiming, SkipMpeg<br />
*'''<code>--fullboot</code>''' disables the quick boot feature, forcing you to sit through the PS2 startup splash screens<br />
<br />
Plugin Overrides (specified dlls will be used in place of configured dlls):<br />
*'''<code>--cdvd=[dllpath]</code>''' override for the CDVD plugin<br />
*'''<code>--gs=[dllpath]</code>''' override for the GS plugin<br />
*'''<code>--spu=[dllpath]</code>''' override for the SPU2 plugin<br />
*'''<code>--pad=[dllpath]</code>''' override for the PAD plugin only<br />
*'''<code>--dev9=[dllpath]</code>''' override for the DEV9 plugin<br />
*'''<code>--usb=[dllpath]</code>''' override for the USB plugin only<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/The_return_of_the_Commandline!&diff=38332PCSX2 Documentation/The return of the Commandline!2015-07-19T19:24:30Z<p>Krysto: </p>
<hr />
<div>''Originally written by Jake Stine''<br />
<br />
After its absence for many moons, the Commandline functionality will finally be restored to PCSX2. Third-party frontend and config-manager authors rejoice! ... and hopefully stop hating my guts, too. To paraphrase Darth Vader: '''"Witness the power of this fully armed and operational Command line-driven battlestation."'''<br />
<br />
''(we all know a command line-driven death star would have been way cooler than some click-and-drag crap.)''<br />
<br />
The new PCSX2 command line should be functional in our next beta release, which should be out pretty soon, and it will work as follows:<br />
<br />
Syntax: <code>pcsx2 [IsoFile] --toggle --option=value ... etc</code><br />
<br />
<code>IsoFile</code> - optional ISO image to load and run on startup; uses the PCSX2 internal ISO loader.<br />
<br />
General Options :<br />
*'''<code>--cfg=[file]</code>''' specify a custom configuration file to use instead of PCSX2.ini (does not affect plugins)<br />
*'''<code>--cfgpath=[dir]</code>''' specifies the config folder; applies to pcsx2 + plugins<br />
*'''<code>--help</code>''' display this help text<br />
*'''<code>--forcewiz</code>''' forces running of the First-time Wizard (selection of docs folders and what-not)<br />
<br />
Auto-Run Options :<br />
*'''<code>--elf=[file]</code>''' executes an ELF image<br />
*'''<code>--nogui</code>''' disables display of the gui on exit (program auto-exits)<br />
*'''<code>--nodisc</code>''' boots with an empty dvd tray; use this to boot into the PS2 system menu<br />
*'''<code>--usecd</code>''' uses the configured CDVD plugin instead of IsoFile<br />
<br />
Compatibility Options:<br />
*'''<code>--nohacks</code>''' disables all speedhacks<br />
*'''<code>--gamefixes=[fix,fix]</code>''' Enable specific gamefixes for this session. Valid fixes in 0.9.7 are: VuAddSub, VuClipFlag, FpuCompare, FpuNegDiv, XGKick, IpuWait, EETiming, SkipMpeg<br />
*'''<code>--fullboot</code>''' disables the quick boot feature, forcing you to sit through the PS2 startup splash screens<br />
<br />
Plugin Overrides (specified dlls will be used in place of configured dlls):<br />
*'''<code>--cdvd=[dllpath]</code>''' override for the CDVD plugin<br />
*'''<code>--gs=[dllpath]</code>''' override for the GS plugin<br />
*'''<code>--spu=[dllpath]</code>''' override for the SPU2 plugin<br />
*'''<code>--pad=[dllpath]</code>''' override for the PAD plugin only<br />
*'''<code>--dev9=[dllpath]</code>''' override for the DEV9 plugin<br />
*'''<code>--usb=[dllpath]</code>''' override for the USB plugin only<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/The_return_of_the_Commandline!&diff=38331PCSX2 Documentation/The return of the Commandline!2015-07-19T19:23:59Z<p>Krysto: </p>
<hr />
<div>''Originally written by Jake Stine''<br />
<br />
After its absence for many moons, the Commandline functionality will finally be restored to PCSX2. Third-party frontend and config-manager authors rejoice! ... and hopefully stop hating my guts, too.<br />
<br />
To paraphrase Darth Vader: '''"Witness the power of this fully armed and operational Command line-driven battlestation."'''<br />
''(we all know a command line-driven death star would have been way cooler than some click-and-drag crap.)''<br />
<br />
The new PCSX2 command line should be functional in our next beta release, which should be out pretty soon, and it will work as follows:<br />
<br />
Syntax: <code>pcsx2 [IsoFile] --toggle --option=value ... etc</code><br />
<br />
<code>IsoFile</code> - optional ISO image to load and run on startup; uses the PCSX2 internal ISO loader.<br />
<br />
General Options :<br />
*'''<code>--cfg=[file]</code>''' specify a custom configuration file to use instead of PCSX2.ini (does not affect plugins)<br />
*'''<code>--cfgpath=[dir]</code>''' specifies the config folder; applies to pcsx2 + plugins<br />
*'''<code>--help</code>''' display this help text<br />
*'''<code>--forcewiz</code>''' forces running of the First-time Wizard (selection of docs folders and what-not)<br />
<br />
Auto-Run Options :<br />
*'''<code>--elf=[file]</code>''' executes an ELF image<br />
*'''<code>--nogui</code>''' disables display of the gui on exit (program auto-exits)<br />
*'''<code>--nodisc</code>''' boots with an empty dvd tray; use this to boot into the PS2 system menu<br />
*'''<code>--usecd</code>''' uses the configured CDVD plugin instead of IsoFile<br />
<br />
Compatibility Options:<br />
*'''<code>--nohacks</code>''' disables all speedhacks<br />
*'''<code>--gamefixes=[fix,fix]</code>''' Enable specific gamefixes for this session. Valid fixes in 0.9.7 are: VuAddSub, VuClipFlag, FpuCompare, FpuNegDiv, XGKick, IpuWait, EETiming, SkipMpeg<br />
*'''<code>--fullboot</code>''' disables the quick boot feature, forcing you to sit through the PS2 startup splash screens<br />
<br />
Plugin Overrides (specified dlls will be used in place of configured dlls):<br />
*'''<code>--cdvd=[dllpath]</code>''' override for the CDVD plugin<br />
*'''<code>--gs=[dllpath]</code>''' override for the GS plugin<br />
*'''<code>--spu=[dllpath]</code>''' override for the SPU2 plugin<br />
*'''<code>--pad=[dllpath]</code>''' override for the PAD plugin only<br />
*'''<code>--dev9=[dllpath]</code>''' override for the DEV9 plugin<br />
*'''<code>--usb=[dllpath]</code>''' override for the USB plugin only<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/The_return_of_the_Commandline!&diff=38330PCSX2 Documentation/The return of the Commandline!2015-07-19T19:23:04Z<p>Krysto: </p>
<hr />
<div>''Originally written by Jake Stine''<br />
<br />
After its absence for many moons, the Commandline functionality will finally be restored to PCSX2. Third-party frontend and config-manager authors rejoice! ... and hopefully stop hating my guts, too.<br />
<br />
To paraphrase Darth Vader: '''"Witness the power of this fully armed and operational Command line-driven battlestation."'''<br />
''(we all know a command line-driven death star would have been way cooler than some click-and-drag crap.)''<br />
<br />
The new PCSX2 command line should be functional in our next beta release, which should be out pretty soon, and it will work as follows:<br />
<br />
Syntax: <code>pcsx2 [IsoFile] --toggle --option=value ... etc</code><br />
<br />
<code>IsoFile</code> - optional ISO image to load and run on startup; uses the PCSX2 internal ISO loader.<br />
<br />
General Options :<br />
*'''<code>--cfg=[file]</code>''' specify a custom configuration file to use instead of PCSX2.ini (does not affect plugins)<br />
*<code>--cfgpath=[dir]</code> specifies the config folder; applies to pcsx2 + plugins<br />
*<code>--help</code> display this help text<br />
*<code>--forcewiz</code> forces running of the First-time Wizard (selection of docs folders and what-not)<br />
<br />
Auto-Run Options :<br />
*<code>--elf=[file]</code> executes an ELF image<br />
*<code>--nogui</code> disables display of the gui on exit (program auto-exits)<br />
*<code>--nodisc</code> boots with an empty dvd tray; use this to boot into the PS2 system menu<br />
*<code>--usecd</code> uses the configured CDVD plugin instead of IsoFile<br />
<br />
Compatibility Options:<br />
*<code>--nohacks</code> disables all speedhacks<br />
*<code>--gamefixes=[fix,fix]</code> Enable specific gamefixes for this session. Valid fixes in 0.9.7 are: VuAddSub, VuClipFlag, FpuCompare, FpuNegDiv, XGKick, IpuWait, EETiming, SkipMpeg<br />
*<code>--fullboot</code> disables the quick boot feature, forcing you to sit through the PS2 startup splash screens<br />
<br />
Plugin Overrides (specified dlls will be used in place of configured dlls):<br />
*<code>--cdvd=[dllpath]</code> override for the CDVD plugin<br />
*<code>--gs=[dllpath]</code> override for the GS plugin<br />
*<code>--spu=[dllpath]</code> override for the SPU2 plugin<br />
*<code>--pad=[dllpath]</code> override for the PAD plugin only<br />
*<code>--dev9=[dllpath]</code> override for the DEV9 plugin<br />
*<code>--usb=[dllpath]</code> override for the USB plugin only<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/The_return_of_the_Commandline!&diff=38329PCSX2 Documentation/The return of the Commandline!2015-07-19T19:22:11Z<p>Krysto: </p>
<hr />
<div>''Originally written by Jake Stine''<br />
<br />
After its absence for many moons, the Commandline functionality will finally be restored to PCSX2. Third-party frontend and config-manager authors rejoice! ... and hopefully stop hating my guts, too.<br />
<br />
To paraphrase Darth Vader: '''"Witness the power of this fully armed and operational Command line-driven battlestation."'''<br />
''(we all know a command line-driven death star would have been way cooler than some click-and-drag crap.)''<br />
<br />
The new PCSX2 command line should be functional in our next beta release, which should be out pretty soon, and it will work as follows:<br />
<br />
Syntax: <code>pcsx2 [IsoFile] --toggle --option=value ... etc</code><br />
<br />
<code>IsoFile</code> - optional ISO image to load and run on startup; uses the PCSX2 internal ISO loader.<br />
<br />
General Options :<br />
*<code>--cfg=[file]</code> specify a custom configuration file to use instead of PCSX2.ini (does not affect plugins)<br />
*<code>--cfgpath=[dir]</code> specifies the config folder; applies to pcsx2 + plugins<br />
*<code>--help</code> display this help text<br />
*<code>--forcewiz</code> forces running of the First-time Wizard (selection of docs folders and what-not)<br />
<br />
Auto-Run Options :<br />
*<code>--elf=[file]</code> executes an ELF image<br />
*<code>--nogui</code> disables display of the gui on exit (program auto-exits)<br />
*<code>--nodisc</code> boots with an empty dvd tray; use this to boot into the PS2 system menu<br />
*<code>--usecd</code> uses the configured CDVD plugin instead of IsoFile<br />
<br />
Compatibility Options:<br />
*<code>--nohacks</code> disables all speedhacks<br />
*<code>--gamefixes=[fix,fix]</code> Enable specific gamefixes for this session. Valid fixes in 0.9.7 are: VuAddSub, VuClipFlag, FpuCompare, FpuNegDiv, XGKick, IpuWait, EETiming, SkipMpeg<br />
*<code>--fullboot</code> disables the quick boot feature, forcing you to sit through the PS2 startup splash screens<br />
<br />
Plugin Overrides (specified dlls will be used in place of configured dlls):<br />
*<code>--cdvd=[dllpath]</code> override for the CDVD plugin<br />
*<code>--gs=[dllpath]</code> override for the GS plugin<br />
*<code>--spu=[dllpath]</code> override for the SPU2 plugin<br />
*<code>--pad=[dllpath]</code> override for the PAD plugin only<br />
*<code>--dev9=[dllpath]</code> override for the DEV9 plugin<br />
*<code>--usb=[dllpath]</code> override for the USB plugin only<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/The_return_of_the_Commandline!&diff=38328PCSX2 Documentation/The return of the Commandline!2015-07-19T19:20:52Z<p>Krysto: </p>
<hr />
<div>''Originally written by Jake Stine''<br />
<br />
After its absence for many moons, the Commandline functionality will finally be restored to PCSX2. Third-party frontend and config-manager authors rejoice! ... and hopefully stop hating my guts, too.<br />
<br />
To paraphrase Darth Vader: '''"Witness the power of this fully armed and operational Command line-driven battlestation."'''<br />
''(we all know a command line-driven death star would have been way cooler than some click-and-drag crap.)''<br />
<br />
The new PCSX2 command line should be functional in our next beta release, which should be out pretty soon, and it will work as follows:<br />
<br />
Syntax: <code>pcsx2 [IsoFile] --toggle --option=value ... etc</code><br />
<br />
<code>IsoFile</code> - optional ISO image to load and run on startup; uses the PCSX2 internal ISO loader.<br />
<br />
General Options :<br />
*<code>--cfg=[file]</code> {specify a custom configuration file to use instead of PCSX2.ini (does not affect plugins)}<br />
*<code>--cfgpath=[dir] {specifies the config folder; applies to pcsx2 + plugins}<br />
*<code>--help {display this help text}<br />
*<code>--forcewiz {forces running of the First-time Wizard (selection of docs folders and what-not)}<br />
<br />
Auto-Run Options :<br />
*<code>--elf=[file] {executes an ELF image}<br />
*<code>--nogui {disables display of the gui on exit (program auto-exits)}<br />
*<code>--nodisc {boots with an empty dvd tray; use this to boot into the PS2 system menu}<br />
*<code>--usecd {uses the configured CDVD plugin instead of IsoFile}<br />
<br />
Compatibility Options:<br />
*<code>--nohacks {disables all speedhacks}<br />
*<code>--gamefixes=[fix,fix] {Enable specific gamefixes for this session. Valid fixes in 0.9.7 are: VuAddSub, VuClipFlag, FpuCompare, FpuNegDiv, XGKick, IpuWait, EETiming, SkipMpeg }<br />
*<code>--fullboot {disables the quick boot feature, forcing you to sit through the PS2 startup splash screens}<br />
<br />
Plugin Overrides (specified dlls will be used in place of configured dlls):<br />
*<code>--cdvd=[dllpath] {override for the CDVD plugin}<br />
*<code>--gs=[dllpath] {override for the GS plugin}<br />
*<code>--spu=[dllpath] {override for the SPU2 plugin}<br />
*<code>--pad=[dllpath] {override for the PAD plugin only}<br />
*<code>--dev9=[dllpath] {override for the DEV9 plugin}<br />
*<code>--usb=[dllpath] {override for the USB plugin only}<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/The_return_of_the_Commandline!&diff=38327PCSX2 Documentation/The return of the Commandline!2015-07-19T19:20:08Z<p>Krysto: </p>
<hr />
<div>''Originally written by Jake Stine''<br />
<br />
After its absence for many moons, the Commandline functionality will finally be restored to PCSX2. Third-party frontend and config-manager authors rejoice! ... and hopefully stop hating my guts, too.<br />
<br />
To paraphrase Darth Vader: '''"Witness the power of this fully armed and operational Command line-driven battlestation."'''<br />
''(we all know a command line-driven death star would have been way cooler than some click-and-drag crap.)''<br />
<br />
The new PCSX2 command line should be functional in our next beta release, which should be out pretty soon, and it will work as follows:<br />
<br />
Syntax: <code>pcsx2 [IsoFile] --toggle --option=value ... etc</code><br />
<br />
<code>IsoFile</code> - optional ISO image to load and run on startup; uses the PCSX2 internal ISO loader.<br />
<br />
General Options :<br />
*--cfg=[file] {specify a custom configuration file to use instead of PCSX2.ini (does not affect plugins)}<br />
*--cfgpath=[dir] {specifies the config folder; applies to pcsx2 + plugins}<br />
*--help {display this help text}<br />
*--forcewiz {forces running of the First-time Wizard (selection of docs folders and what-not)}<br />
<br />
Auto-Run Options :<br />
*--elf=[file] {executes an ELF image}<br />
*--nogui {disables display of the gui on exit (program auto-exits)}<br />
*--nodisc {boots with an empty dvd tray; use this to boot into the PS2 system menu}<br />
*--usecd {uses the configured CDVD plugin instead of IsoFile}<br />
<br />
Compatibility Options:<br />
*--nohacks {disables all speedhacks}<br />
*--gamefixes=[fix,fix] {Enable specific gamefixes for this session. Valid fixes in 0.9.7 are: VuAddSub, VuClipFlag, FpuCompare, FpuNegDiv, XGKick, IpuWait, EETiming, SkipMpeg }<br />
*--fullboot {disables the quick boot feature, forcing you to sit through the PS2 startup splash screens}<br />
<br />
Plugin Overrides (specified dlls will be used in place of configured dlls):<br />
*--cdvd=[dllpath] {override for the CDVD plugin}<br />
*--gs=[dllpath] {override for the GS plugin}<br />
*--spu=[dllpath] {override for the SPU2 plugin}<br />
*--pad=[dllpath] {override for the PAD plugin only}<br />
*--dev9=[dllpath] {override for the DEV9 plugin}<br />
*--usb=[dllpath] {override for the USB plugin only}<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/Introduction_to_Dynamic_Recompilation&diff=38325PCSX2 Documentation/Introduction to Dynamic Recompilation2015-07-19T19:16:07Z<p>Krysto: </p>
<hr />
<div>''Originally written by cottonvibes''<br />
<br />
This blog post is an introduction to dynamic recompilers (dynarecs), and hopes to provide some insight on how they work and why pcsx2 uses them to speed up emulation. To first understand why dynarecs are useful, you must first be familiar with a basic interpreter emulator.<br />
<br />
Assume we are emulating a very simple processor. Processors have instruction sets which are a set of different instructions they can compute.<br />
Lets assume the processor we are emulating is a made-up chip I'll call SL3 (super lame 3), and has only these 3 instructions (and each instruction has fixed width of 4 bytes):<br />
<br />
<source lang="asm"><br />
MOV dest_reg, src1_reg // Move source register to destination register<br />
ADD dest_reg, src1_reg, src2_reg // Add source1 and source2 registers, and store the result in destination register<br />
BR relative_address // Branch (jump) to relative address (PC += relative_address * 4)<br />
</source><br />
<br />
Processors generally have what we call registers which can hold data, and the processor's instructions perform the operations on these registers.<br />
For our example, we will assume that our SL3 processor has 8 registers, and the size of these registers is 32 bits (so each register holds 32 bits of data).<br />
<br />
Now to program for this processor, we can have the following code:<br />
<br />
<source lang="asm"><br />
MOV reg1, reg0<br />
ADD reg4, reg2, reg3<br />
BR 5<br />
</source><br />
<br />
What this code does is:<br />
#It moves register 0 to register 1 (so now register 1 holds a copy of register 0's data).<br />
#It adds register 2 and register 3 together, and stores the result in register 4.<br />
#It branches 5 instructions further away (so now it jumps to some code that is further down (not shown in above example))<br />
<br />
So that is how we can program for the SL3 processor in assembly code. But how do we emulate it?<br />
<br />
To actually emulate this processor we can use an interpreter. An interpreter simply fetches each instruction opcode and executes them accordingly (e.g. by calling emulated methods for each different instruction). The rest of the emulator (emulating other processors/peripherals of our system) can then get updated sometime in between instructions or after a group of cpu instructions are run. Interpreters are a simple and complete way to emulate a system.<br />
<br />
{http://forums.pcsx2.net/Thread-blog-Introduction-to-Dynamic-Recompilation?pid=102002#pid102002 Click here to see a C++ code example of a simple interpreter]<br />
<br />
Using interpreters we constantly have to be fetching and executing instructions one-by-one. There is a lot of overhead in this, and minimal room for optimization since most special case optimizations will have the overhead of checking for them (so it will for example add extra if-statements and conditionals... reducing the gain from the optimization). But there's a faster way to do processor emulation which doesn't have these draw-backs... using dynamic recompilation!<br />
<br />
The basic idea of dynamic recompilation is to translate emulated instructions once, cache the emitted translated instructions, and then run the emitted native instructions as much times as needed.<br />
<br />
Since the instructions you read are not changing (lets leave out self-modifying code for this example), you can translate the emulated instructions into native cpu instructions (in our case x86-32 asm instructions) and then cache the translated instructions into 'blocks' of code, then just execute these blocks of native code instead of having to do the translation over and over again whenever the code needs to be read again.<br />
<br />
So for instance remember our above SL3 program:<br />
<br />
<source lang="asm"><br />
MOV reg1, reg0<br />
ADD reg4, reg2, reg3<br />
BR 5<br />
</source><br />
<br />
Lets assume this code is part of some function and gets called 100's of times a second (this could sound crazy, but games/applications commonly call the same code hundreds or thousands of times a second).<br />
<br />
Now our runCPU() interpreter function above will have to translate every instruction before it can actually compute the result.<br />
<br />
That is, it needs to fetch the opcode of every instruction, call the emulated function based on the opcode number, then actually compute the instruction result.<br />
But dynarecs allow us to skip the "fetch opcode" and the "call the emulated function" part, by only doing this once, and then caching the translated code into native asm blocks.<br />
<br />
To make a good dynarec, we first need a code emitter.<br />
An emitter is a series of functions we call that write native asm to some memory block we give it.<br />
So we use an x86-32 emitter to write native x86-32 asm code to blocks of memory, and then later we can execute these blocks as if they were normal c++ generated functions!<br />
<br />
PCSX2 has a very cool emitter that looks very similar to x86-32 assembly, except the instructions have an 'x' before them.<br />
So for example:<br />
<source lang="asm"><br />
mov eax, ecx;<br />
</source><br />
is<br />
<source lang="asm"><br />
xMOV(eax, ecx);<br />
</source><br />
with the pcsx2 emitter.<br />
<br />
Now the idea behind the dynarec we are going to write now, is that we will end blocks whenever a branch instruction is detected (which will jump to other blocks).<br />
<br />
The code for actually recompiling these blocks looks something like this:<br />
<br />
<source lang="cpp"><br />
// This is our emulated MOV instruction<br />
void MOV() {<br />
u8 dest = fetch(); // Get destination register number<br />
u8 reg1 = fetch(); // Get source 1 register number<br />
<br />
xMOV(eax, ptr[&amp;cpuRegs[reg1]]); // Move reg1's data to eax<br />
xMOV(ptr[&amp;cpuRegs[dest]], eax); // Move eax to dest register<br />
<br />
fetch(); // This fetch is needed because every instruction in our SL3 processor is 4 bytes<br />
}<br />
<br />
// This is our emulated ADD instruction<br />
void ADD() {<br />
u8 dest = fetch(); // Get destination register number<br />
u8 reg1 = fetch(); // Get source 1 register number<br />
u8 reg2 = fetch(); // Get source 2 register number<br />
<br />
xMOV(eax, ptr[&amp;cpuRegs[reg1]]); // Move reg1's data to eax<br />
xADD(eax, ptr[&amp;cpuRegs[reg2]]); // Add eax with reg2's data <br />
xMOV(ptr[&amp;cpuRegs[dest]], eax); // Move eax to dest register<br />
}<br />
<br />
// This is our emulated BR (jump) instruction<br />
void BR() {<br />
s8 addr = fetch(); // Gets a number by which we will increment (or decrement if negative) PC by<br />
PC = (PC - 2) + (addr * 4);<br />
<br />
// Get a pointer to a block of x86-32 compiled code<br />
// that was recompiled by the recompileSL3() function<br />
u8* block_pointer = getBlock(PC);<br />
<br />
xJMP(block_pointer); // Jump to the pointer returned by getBlock()<br />
}<br />
<br />
// This goes through instructions and recompiles them<br />
// It recompiles instructions until it reaches a BR() instruction.<br />
u8* recompileSL3(u32 startPC) {<br />
u8* startPtr = xGetPtr(); // Gets pointer to where the emitter is currently pointing to (the start pointer of the block)<br />
PC = startPC; // Set PC to the start PC of this block<br />
bool do_recompile = true;<br />
while (do_recompile) {<br />
u8 opcode = fetch();<br />
switch (opcode) {<br />
case 0: MOV(); break;<br />
case 1: ADD(); break;<br />
case 2: // We stop recompiling on branches<br />
BR();<br />
do_recompile = false;<br />
break;<br />
}<br />
}<br />
return startPtr; // Returns the pointer to where our block of x86 generated code starts at<br />
}<br />
<br />
// This holds all the pointers to our blocks that were recompiled based on<br />
// starting PC address. We will assume that the instruction memory for<br />
// this processor is 16kb, which means that it can hold at-most 1024*16 bytes <br />
// worth of instructions. And therefor we we have at-most 1024*16 block pointers.<br />
static u8* blockArray[1024*16];<br />
<br />
// This returns a pointer to our recompiled block<br />
// If it hasn't been compiled, it'll recompile the block and then return that pointer.<br />
// We use __fastcall because it lets us pass the startPC parameter in the ecx register<br />
// instead of having to use the x86 stack...<br />
u8* __fastcall getBlock(u32 startPC) {<br />
if (blockArray[startPC] == null) {<br />
blockArray[startPC] = recompileSL3(startPC);<br />
}<br />
return blockArray[startPC];<br />
}<br />
<br />
// Basic cpu emulator using dynamic recompilation<br />
void runCPU() {<br />
// This sets our emitter to start emitting instructions to rec_cache<br />
// which is a large block of memory where we can write lots of<br />
// x86 asm instructions to...<br />
x86setPtr(rec_cache); <br />
<br />
__asm {<br />
pushad; // Save all our registers<br />
mov ecx, PC; // Move PC parameter into ecx register (for __fastcall)<br />
call getBlock; // Call the getBlock function<br />
jmp eax; // The block to jump to is returned in eax<br />
}<br />
}<br />
</source><br />
<br />
Note the above code doesn't have any logic to successfully exit once it starts executing recompiled blocks... I left this stuff out in order to not complicate things... so assume that somehow execution ends and we can get back to running the other parts of the emulator... <br />
Also note we need to restore registers when we exit execution, and we also need to set rec_cache to the new x86 emitter address (so it can continue where it left off instead of writing over already recompiled memory blocks).<br />
(Click here for a more complete sl3 recompiler code with cycle counting and exit logic)<br />
<br />
Now how does this work?<br />
Well we basically call runCPU() which calls the getBlock() function with the original PC value.<br />
getBlock() then checks if we have already recompiled a block with that start PC value, which we havn't yet, so it will go on to call recompileSL3() and give it the startPC value.<br />
recompileSL3() will loop through the opcodes, fetching them and then calling the appropriate emulated functions which will write to memory x86-32 asm instructions computing the correct results.<br />
recompileSL3() will continue looping until it reaches a BR() instruction (which will recursively call getBlock() until all the addresses have been recompiled and no more recompilation needs to happen).<br />
<br />
Once everything has been recompiled we jump to the starting block's execution address, and that's how we actually run the execution.<br />
The code that ends up being executed after we recompiled is only the emitted code, which were the functions prefixed with 'x' (xMOV, xADD, etc...).<br />
Notice that that's a lot of code omitted as we don't have to fetch anything anymore, but instead just run a series of fast generated asm functions...<br />
This means that although we have a lot of extra overhead on the actual recompilation, we end up generating some really fast code, that could be called 1000's of times a second, therefor making the initial overhead well worth it!<br />
<br />
We can also perform many optimizations while recompiling such as regalloc, constant propagation, optimized asm generation for special opcode cases, etc... (we didn't do this stuff in our example to avoid complexities).<br />
<br />
<br />
Hopefully this article has helped you gain a bit of understanding on how dynamic recompilation works, and why we use it in pcsx2 to gain a lot more speed over interpreters!<br />
Also note that dynarecs can be used for more than processor emulation, we also used dynamic recompilation in pcsx2 to cache asm optimized unpack routines that the ps2's vif does.<br />
<br />
I should also add that currently pcsx2 has the following dynarecs:<br />
*EE recompiler (for MIPS R5900 ee-core processor)<br />
*IOP recompiler (for MIPS R3000A i/o processor)<br />
*microVU recompiler (for VU0/VU1, and COP2 instructions)<br />
*Super VU recompiler (can be used instead of microVU for VU0/VU1)<br />
*newVIF unpack recompiler (recompiles vif unpack routines)<br />
*r3000air (not yet finished, but should one day supersede the IOP recompiler)<br />
<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/Introduction_to_Dynamic_Recompilation&diff=38324PCSX2 Documentation/Introduction to Dynamic Recompilation2015-07-19T19:15:06Z<p>Krysto: </p>
<hr />
<div>''Originally written by cottonvibes''<br />
<br />
This blog post is an introduction to dynamic recompilers (dynarecs), and hopes to provide some insight on how they work and why pcsx2 uses them to speed up emulation. To first understand why dynarecs are useful, you must first be familiar with a basic interpreter emulator.<br />
<br />
Assume we are emulating a very simple processor. Processors have instruction sets which are a set of different instructions they can compute.<br />
Lets assume the processor we are emulating is a made-up chip I'll call SL3 (super lame 3), and has only these 3 instructions (and each instruction has fixed width of 4 bytes):<br />
<br />
<source lang="asm"><br />
MOV dest_reg, src1_reg // Move source register to destination register<br />
ADD dest_reg, src1_reg, src2_reg // Add source1 and source2 registers, and store the result in destination register<br />
BR relative_address // Branch (jump) to relative address (PC += relative_address * 4)<br />
</source><br />
<br />
Processors generally have what we call registers which can hold data, and the processor's instructions perform the operations on these registers.<br />
For our example, we will assume that our SL3 processor has 8 registers, and the size of these registers is 32 bits (so each register holds 32 bits of data).<br />
<br />
Now to program for this processor, we can have the following code:<br />
<br />
<source lang="asm"><br />
MOV reg1, reg0<br />
ADD reg4, reg2, reg3<br />
BR 5<br />
</source><br />
<br />
What this code does is:<br />
#It moves register 0 to register 1 (so now register 1 holds a copy of register 0's data).<br />
#It adds register 2 and register 3 together, and stores the result in register 4.<br />
#It branches 5 instructions further away (so now it jumps to some code that is further down (not shown in above example))<br />
<br />
So that is how we can program for the SL3 processor in assembly code. But how do we emulate it?<br />
<br />
To actually emulate this processor we can use an interpreter. An interpreter simply fetches each instruction opcode and executes them accordingly (e.g. by calling emulated methods for each different instruction). The rest of the emulator (emulating other processors/peripherals of our system) can then get updated sometime in between instructions or after a group of cpu instructions are run. Interpreters are a simple and complete way to emulate a system.<br />
<br />
{http://forums.pcsx2.net/Thread-blog-Introduction-to-Dynamic-Recompilation?pid=102002#pid102002 Click here to see a C++ code example of a simple interpreter]<br />
<br />
Using interpreters we constantly have to be fetching and executing instructions one-by-one. There is a lot of overhead in this, and minimal room for optimization since most special case optimizations will have the overhead of checking for them (so it will for example add extra if-statements and conditionals... reducing the gain from the optimization). But there's a faster way to do processor emulation which doesn't have these draw-backs... using dynamic recompilation!<br />
<br />
The basic idea of dynamic recompilation is to translate emulated instructions once, cache the emitted translated instructions, and then run the emitted native instructions as much times as needed.<br />
<br />
Since the instructions you read are not changing (lets leave out self-modifying code for this example), you can translate the emulated instructions into native cpu instructions (in our case x86-32 asm instructions) and then cache the translated instructions into 'blocks' of code, then just execute these blocks of native code instead of having to do the translation over and over again whenever the code needs to be read again.<br />
<br />
So for instance remember our above SL3 program:<br />
<br />
<source lang="asm"><br />
MOV reg1, reg0<br />
ADD reg4, reg2, reg3<br />
BR 5<br />
</source><br />
<br />
Lets assume this code is part of some function and gets called 100's of times a second (this could sound crazy, but games/applications commonly call the same code hundreds or thousands of times a second).<br />
<br />
Now our runCPU() interpreter function above will have to translate every instruction before it can actually compute the result.<br />
<br />
That is, it needs to fetch the opcode of every instruction, call the emulated function based on the opcode number, then actually compute the instruction result.<br />
But dynarecs allow us to skip the "fetch opcode" and the "call the emulated function" part, by only doing this once, and then caching the translated code into native asm blocks.<br />
<br />
To make a good dynarec, we first need a code emitter.<br />
An emitter is a series of functions we call that write native asm to some memory block we give it.<br />
So we use an x86-32 emitter to write native x86-32 asm code to blocks of memory, and then later we can execute these blocks as if they were normal c++ generated functions!<br />
<br />
PCSX2 has a very cool emitter that looks very similar to x86-32 assembly, except the instructions have an 'x' before them.<br />
So for example:<br />
<source lang="asm"><br />
mov eax, ecx;<br />
</source><br />
is<br />
<source lang="asm"><br />
xMOV(eax, ecx);<br />
</source><br />
with the pcsx2 emitter.<br />
<br />
Now the idea behind the dynarec we are going to write now, is that we will end blocks whenever a branch instruction is detected (which will jump to other blocks).<br />
<br />
The code for actually recompiling these blocks looks something like this:<br />
<br />
<source lang="cpp"><br />
// This is our emulated MOV instruction<br />
void MOV() {<br />
u8 dest = fetch(); // Get destination register number<br />
u8 reg1 = fetch(); // Get source 1 register number<br />
<br />
xMOV(eax, ptr[&amp;cpuRegs[reg1]]); // Move reg1's data to eax<br />
xMOV(ptr[&amp;cpuRegs[dest]], eax); // Move eax to dest register<br />
<br />
fetch(); // This fetch is needed because every instruction in our SL3 processor is 4 bytes<br />
}<br />
<br />
// This is our emulated ADD instruction<br />
void ADD() {<br />
u8 dest = fetch(); // Get destination register number<br />
u8 reg1 = fetch(); // Get source 1 register number<br />
u8 reg2 = fetch(); // Get source 2 register number<br />
<br />
xMOV(eax, ptr[&amp;cpuRegs[reg1]]); // Move reg1's data to eax<br />
xADD(eax, ptr[&amp;cpuRegs[reg2]]); // Add eax with reg2's data <br />
xMOV(ptr[&amp;cpuRegs[dest]], eax); // Move eax to dest register<br />
}<br />
<br />
// This is our emulated BR (jump) instruction<br />
void BR() {<br />
s8 addr = fetch(); // Gets a number by which we will increment (or decrement if negative) PC by<br />
PC = (PC - 2) + (addr * 4);<br />
<br />
// Get a pointer to a block of x86-32 compiled code<br />
// that was recompiled by the recompileSL3() function<br />
u8* block_pointer = getBlock(PC);<br />
<br />
xJMP(block_pointer); // Jump to the pointer returned by getBlock()<br />
}<br />
<br />
// This goes through instructions and recompiles them<br />
// It recompiles instructions until it reaches a BR() instruction.<br />
u8* recompileSL3(u32 startPC) {<br />
u8* startPtr = xGetPtr(); // Gets pointer to where the emitter is currently pointing to (the start pointer of the block)<br />
PC = startPC; // Set PC to the start PC of this block<br />
bool do_recompile = true;<br />
while (do_recompile) {<br />
u8 opcode = fetch();<br />
switch (opcode) {<br />
case 0: MOV(); break;<br />
case 1: ADD(); break;<br />
case 2: // We stop recompiling on branches<br />
BR();<br />
do_recompile = false;<br />
break;<br />
}<br />
}<br />
return startPtr; // Returns the pointer to where our block of x86 generated code starts at<br />
}<br />
<br />
// This holds all the pointers to our blocks that were recompiled based on<br />
// starting PC address. We will assume that the instruction memory for<br />
// this processor is 16kb, which means that it can hold at-most 1024*16 bytes <br />
// worth of instructions. And therefor we we have at-most 1024*16 block pointers.<br />
static u8* blockArray[1024*16];<br />
<br />
// This returns a pointer to our recompiled block<br />
// If it hasn't been compiled, it'll recompile the block and then return that pointer.<br />
// We use __fastcall because it lets us pass the startPC parameter in the ecx register<br />
// instead of having to use the x86 stack...<br />
u8* __fastcall getBlock(u32 startPC) {<br />
if (blockArray[startPC] == null) {<br />
blockArray[startPC] = recompileSL3(startPC);<br />
}<br />
return blockArray[startPC];<br />
}<br />
<br />
// Basic cpu emulator using dynamic recompilation<br />
void runCPU() {<br />
// This sets our emitter to start emitting instructions to rec_cache<br />
// which is a large block of memory where we can write lots of<br />
// x86 asm instructions to...<br />
x86setPtr(rec_cache); <br />
<br />
__asm {<br />
pushad; // Save all our registers<br />
mov ecx, PC; // Move PC parameter into ecx register (for __fastcall)<br />
call getBlock; // Call the getBlock function<br />
jmp eax; // The block to jump to is returned in eax<br />
}<br />
}<br />
</source><br />
<br />
Note the above code doesn't have any logic to successfully exit once it starts executing recompiled blocks... I left this stuff out in order to not complicate things... so assume that somehow execution ends and we can get back to running the other parts of the emulator... <br />
Also note we need to restore registers when we exit execution, and we also need to set rec_cache to the new x86 emitter address (so it can continue where it left off instead of writing over already recompiled memory blocks).<br />
(Click here for a more complete sl3 recompiler code with cycle counting and exit logic)<br />
<br />
Now how does this work?<br />
Well we basically call runCPU() which calls the getBlock() function with the original PC value.<br />
getBlock() then checks if we have already recompiled a block with that start PC value, which we havn't yet, so it will go on to call recompileSL3() and give it the startPC value.<br />
recompileSL3() will loop through the opcodes, fetching them and then calling the appropriate emulated functions which will write to memory x86-32 asm instructions computing the correct results.<br />
recompileSL3() will continue looping until it reaches a BR() instruction (which will recursively call getBlock() until all the addresses have been recompiled and no more recompilation needs to happen).<br />
<br />
Once everything has been recompiled we jump to the starting block's execution address, and that's how we actually run the execution.<br />
The code that ends up being executed after we recompiled is only the emitted code, which were the functions prefixed with 'x' (xMOV, xADD, etc...).<br />
Notice that that's a lot of code omitted as we don't have to fetch anything anymore, but instead just run a series of fast generated asm functions...<br />
This means that although we have a lot of extra overhead on the actual recompilation, we end up generating some really fast code, that could be called 1000's of times a second, therefor making the initial overhead well worth it!<br />
<br />
We can also perform many optimizations while recompiling such as regalloc, constant propagation, optimized asm generation for special opcode cases, etc... (we didn't do this stuff in our example to avoid complexities).<br />
<br />
<br />
Hopefully this article has helped you gain a bit of understanding on how dynamic recompilation works, and why we use it in pcsx2 to gain a lot more speed over interpreters!<br />
Also note that dynarecs can be used for more than processor emulation, we also used dynamic recompilation in pcsx2 to cache asm optimized unpack routines that the ps2's vif does.<br />
<br />
I should also add that currently pcsx2 has the following dynarecs:<br />
*EE recompiler (for MIPS R5900 ee-core processor)<br />
*IOP recompiler (for MIPS R3000A i/o processor)<br />
*microVU recompiler (for VU0/VU1, and COP2 instructions)<br />
*Super VU recompiler (can be used instead of microVU for VU0/VU1)<br />
*newVIF unpack recompiler (recompiles vif unpack routines)<br />
*r3000air (not yet finished, but should one day supersede the IOP recompiler)<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/Introduction_to_Dynamic_Recompilation&diff=38323PCSX2 Documentation/Introduction to Dynamic Recompilation2015-07-19T19:14:29Z<p>Krysto: </p>
<hr />
<div>''Originally written by cottonvibes''<br />
<br />
This blog post is an introduction to dynamic recompilers (dynarecs), and hopes to provide some insight on how they work and why pcsx2 uses them to speed up emulation. To first understand why dynarecs are useful, you must first be familiar with a basic interpreter emulator.<br />
<br />
Assume we are emulating a very simple processor. Processors have instruction sets which are a set of different instructions they can compute.<br />
Lets assume the processor we are emulating is a made-up chip I'll call SL3 (super lame 3), and has only these 3 instructions (and each instruction has fixed width of 4 bytes):<br />
<br />
<source lang="asm"><br />
MOV dest_reg, src1_reg // Move source register to destination register<br />
ADD dest_reg, src1_reg, src2_reg // Add source1 and source2 registers, and store the result in destination register<br />
BR relative_address // Branch (jump) to relative address (PC += relative_address * 4)<br />
</source><br />
<br />
Processors generally have what we call registers which can hold data, and the processor's instructions perform the operations on these registers.<br />
For our example, we will assume that our SL3 processor has 8 registers, and the size of these registers is 32 bits (so each register holds 32 bits of data).<br />
<br />
Now to program for this processor, we can have the following code:<br />
<br />
<source lang="asm"><br />
MOV reg1, reg0<br />
ADD reg4, reg2, reg3<br />
BR 5<br />
</source><br />
<br />
What this code does is:<br />
#It moves register 0 to register 1 (so now register 1 holds a copy of register 0's data).<br />
#It adds register 2 and register 3 together, and stores the result in register 4.<br />
#It branches 5 instructions further away (so now it jumps to some code that is further down (not shown in above example))<br />
<br />
So that is how we can program for the SL3 processor in assembly code. But how do we emulate it?<br />
<br />
To actually emulate this processor we can use an interpreter. An interpreter simply fetches each instruction opcode and executes them accordingly (e.g. by calling emulated methods for each different instruction). The rest of the emulator (emulating other processors/peripherals of our system) can then get updated sometime in between instructions or after a group of cpu instructions are run. Interpreters are a simple and complete way to emulate a system.<br />
<br />
{http://forums.pcsx2.net/Thread-blog-Introduction-to-Dynamic-Recompilation?pid=102002#pid102002 Click here to see a C++ code example of a simple interpreter]<br />
<br />
Using interpreters we constantly have to be fetching and executing instructions one-by-one. There is a lot of overhead in this, and minimal room for optimization since most special case optimizations will have the overhead of checking for them (so it will for example add extra if-statements and conditionals... reducing the gain from the optimization). But there's a faster way to do processor emulation which doesn't have these draw-backs... using dynamic recompilation!<br />
<br />
The basic idea of dynamic recompilation is to translate emulated instructions once, cache the emitted translated instructions, and then run the emitted native instructions as much times as needed.<br />
<br />
Since the instructions you read are not changing (lets leave out self-modifying code for this example), you can translate the emulated instructions into native cpu instructions (in our case x86-32 asm instructions) and then cache the translated instructions into 'blocks' of code, then just execute these blocks of native code instead of having to do the translation over and over again whenever the code needs to be read again.<br />
<br />
So for instance remember our above SL3 program:<br />
<br />
<source lang="asm"><br />
MOV reg1, reg0<br />
ADD reg4, reg2, reg3<br />
BR 5<br />
</source><br />
<br />
Lets assume this code is part of some function and gets called 100's of times a second (this could sound crazy, but games/applications commonly call the same code hundreds or thousands of times a second).<br />
<br />
Now our runCPU() interpreter function above will have to translate every instruction before it can actually compute the result.<br />
<br />
That is, it needs to fetch the opcode of every instruction, call the emulated function based on the opcode number, then actually compute the instruction result.<br />
But dynarecs allow us to skip the "fetch opcode" and the "call the emulated function" part, by only doing this once, and then caching the translated code into native asm blocks.<br />
<br />
To make a good dynarec, we first need a code emitter.<br />
An emitter is a series of functions we call that write native asm to some memory block we give it.<br />
So we use an x86-32 emitter to write native x86-32 asm code to blocks of memory, and then later we can execute these blocks as if they were normal c++ generated functions!<br />
<br />
PCSX2 has a very cool emitter that looks very similar to x86-32 assembly, except the instructions have an 'x' before them.<br />
So for example:<br />
<source lang="asm"><br />
mov eax, ecx;<br />
</source><br />
is<br />
<source lang="asm"><br />
xMOV(eax, ecx);<br />
</source><br />
with the pcsx2 emitter.<br />
<br />
Now the idea behind the dynarec we are going to write now, is that we will end blocks whenever a branch instruction is detected (which will jump to other blocks).<br />
<br />
The code for actually recompiling these blocks looks something like this:<br />
<br />
<source lang="asm"><br />
// This is our emulated MOV instruction<br />
void MOV() {<br />
u8 dest = fetch(); // Get destination register number<br />
u8 reg1 = fetch(); // Get source 1 register number<br />
<br />
xMOV(eax, ptr[&amp;cpuRegs[reg1]]); // Move reg1's data to eax<br />
xMOV(ptr[&amp;cpuRegs[dest]], eax); // Move eax to dest register<br />
<br />
fetch(); // This fetch is needed because every instruction in our SL3 processor is 4 bytes<br />
}<br />
<br />
// This is our emulated ADD instruction<br />
void ADD() {<br />
u8 dest = fetch(); // Get destination register number<br />
u8 reg1 = fetch(); // Get source 1 register number<br />
u8 reg2 = fetch(); // Get source 2 register number<br />
<br />
xMOV(eax, ptr[&amp;cpuRegs[reg1]]); // Move reg1's data to eax<br />
xADD(eax, ptr[&amp;cpuRegs[reg2]]); // Add eax with reg2's data <br />
xMOV(ptr[&amp;cpuRegs[dest]], eax); // Move eax to dest register<br />
}<br />
<br />
// This is our emulated BR (jump) instruction<br />
void BR() {<br />
s8 addr = fetch(); // Gets a number by which we will increment (or decrement if negative) PC by<br />
PC = (PC - 2) + (addr * 4);<br />
<br />
// Get a pointer to a block of x86-32 compiled code<br />
// that was recompiled by the recompileSL3() function<br />
u8* block_pointer = getBlock(PC);<br />
<br />
xJMP(block_pointer); // Jump to the pointer returned by getBlock()<br />
}<br />
<br />
// This goes through instructions and recompiles them<br />
// It recompiles instructions until it reaches a BR() instruction.<br />
u8* recompileSL3(u32 startPC) {<br />
u8* startPtr = xGetPtr(); // Gets pointer to where the emitter is currently pointing to (the start pointer of the block)<br />
PC = startPC; // Set PC to the start PC of this block<br />
bool do_recompile = true;<br />
while (do_recompile) {<br />
u8 opcode = fetch();<br />
switch (opcode) {<br />
case 0: MOV(); break;<br />
case 1: ADD(); break;<br />
case 2: // We stop recompiling on branches<br />
BR();<br />
do_recompile = false;<br />
break;<br />
}<br />
}<br />
return startPtr; // Returns the pointer to where our block of x86 generated code starts at<br />
}<br />
<br />
// This holds all the pointers to our blocks that were recompiled based on<br />
// starting PC address. We will assume that the instruction memory for<br />
// this processor is 16kb, which means that it can hold at-most 1024*16 bytes <br />
// worth of instructions. And therefor we we have at-most 1024*16 block pointers.<br />
static u8* blockArray[1024*16];<br />
<br />
// This returns a pointer to our recompiled block<br />
// If it hasn't been compiled, it'll recompile the block and then return that pointer.<br />
// We use __fastcall because it lets us pass the startPC parameter in the ecx register<br />
// instead of having to use the x86 stack...<br />
u8* __fastcall getBlock(u32 startPC) {<br />
if (blockArray[startPC] == null) {<br />
blockArray[startPC] = recompileSL3(startPC);<br />
}<br />
return blockArray[startPC];<br />
}<br />
<br />
// Basic cpu emulator using dynamic recompilation<br />
void runCPU() {<br />
// This sets our emitter to start emitting instructions to rec_cache<br />
// which is a large block of memory where we can write lots of<br />
// x86 asm instructions to...<br />
x86setPtr(rec_cache); <br />
<br />
__asm {<br />
pushad; // Save all our registers<br />
mov ecx, PC; // Move PC parameter into ecx register (for __fastcall)<br />
call getBlock; // Call the getBlock function<br />
jmp eax; // The block to jump to is returned in eax<br />
}<br />
}<br />
</source><br />
<br />
Note the above code doesn't have any logic to successfully exit once it starts executing recompiled blocks... I left this stuff out in order to not complicate things... so assume that somehow execution ends and we can get back to running the other parts of the emulator... <br />
Also note we need to restore registers when we exit execution, and we also need to set rec_cache to the new x86 emitter address (so it can continue where it left off instead of writing over already recompiled memory blocks).<br />
(Click here for a more complete sl3 recompiler code with cycle counting and exit logic)<br />
<br />
Now how does this work?<br />
Well we basically call runCPU() which calls the getBlock() function with the original PC value.<br />
getBlock() then checks if we have already recompiled a block with that start PC value, which we havn't yet, so it will go on to call recompileSL3() and give it the startPC value.<br />
recompileSL3() will loop through the opcodes, fetching them and then calling the appropriate emulated functions which will write to memory x86-32 asm instructions computing the correct results.<br />
recompileSL3() will continue looping until it reaches a BR() instruction (which will recursively call getBlock() until all the addresses have been recompiled and no more recompilation needs to happen).<br />
<br />
Once everything has been recompiled we jump to the starting block's execution address, and that's how we actually run the execution.<br />
The code that ends up being executed after we recompiled is only the emitted code, which were the functions prefixed with 'x' (xMOV, xADD, etc...).<br />
Notice that that's a lot of code omitted as we don't have to fetch anything anymore, but instead just run a series of fast generated asm functions...<br />
This means that although we have a lot of extra overhead on the actual recompilation, we end up generating some really fast code, that could be called 1000's of times a second, therefor making the initial overhead well worth it!<br />
<br />
We can also perform many optimizations while recompiling such as regalloc, constant propagation, optimized asm generation for special opcode cases, etc... (we didn't do this stuff in our example to avoid complexities).<br />
<br />
<br />
Hopefully this article has helped you gain a bit of understanding on how dynamic recompilation works, and why we use it in pcsx2 to gain a lot more speed over interpreters!<br />
Also note that dynarecs can be used for more than processor emulation, we also used dynamic recompilation in pcsx2 to cache asm optimized unpack routines that the ps2's vif does.<br />
<br />
I should also add that currently pcsx2 has the following dynarecs:<br />
*EE recompiler (for MIPS R5900 ee-core processor)<br />
*IOP recompiler (for MIPS R3000A i/o processor)<br />
*microVU recompiler (for VU0/VU1, and COP2 instructions)<br />
*Super VU recompiler (can be used instead of microVU for VU0/VU1)<br />
*newVIF unpack recompiler (recompiles vif unpack routines)<br />
*r3000air (not yet finished, but should one day supersede the IOP recompiler)<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krystohttps://wiki.pcsx2.net/index.php?title=PCSX2_Documentation/Introduction_to_Dynamic_Recompilation&diff=38322PCSX2 Documentation/Introduction to Dynamic Recompilation2015-07-19T19:14:05Z<p>Krysto: </p>
<hr />
<div>''Originally written by cottonvibes''<br />
<br />
This blog post is an introduction to dynamic recompilers (dynarecs), and hopes to provide some insight on how they work and why pcsx2 uses them to speed up emulation. To first understand why dynarecs are useful, you must first be familiar with a basic interpreter emulator.<br />
<br />
Assume we are emulating a very simple processor. Processors have instruction sets which are a set of different instructions they can compute.<br />
Lets assume the processor we are emulating is a made-up chip I'll call SL3 (super lame 3), and has only these 3 instructions (and each instruction has fixed width of 4 bytes):<br />
<br />
<code lang="asm"><br />
MOV dest_reg, src1_reg // Move source register to destination register<br />
ADD dest_reg, src1_reg, src2_reg // Add source1 and source2 registers, and store the result in destination register<br />
BR relative_address // Branch (jump) to relative address (PC += relative_address * 4)<br />
</source><br />
<br />
Processors generally have what we call registers which can hold data, and the processor's instructions perform the operations on these registers.<br />
For our example, we will assume that our SL3 processor has 8 registers, and the size of these registers is 32 bits (so each register holds 32 bits of data).<br />
<br />
Now to program for this processor, we can have the following code:<br />
<br />
<source lang="asm"><br />
MOV reg1, reg0<br />
ADD reg4, reg2, reg3<br />
BR 5<br />
</source><br />
<br />
What this code does is:<br />
#It moves register 0 to register 1 (so now register 1 holds a copy of register 0's data).<br />
#It adds register 2 and register 3 together, and stores the result in register 4.<br />
#It branches 5 instructions further away (so now it jumps to some code that is further down (not shown in above example))<br />
<br />
So that is how we can program for the SL3 processor in assembly code. But how do we emulate it?<br />
<br />
To actually emulate this processor we can use an interpreter. An interpreter simply fetches each instruction opcode and executes them accordingly (e.g. by calling emulated methods for each different instruction). The rest of the emulator (emulating other processors/peripherals of our system) can then get updated sometime in between instructions or after a group of cpu instructions are run. Interpreters are a simple and complete way to emulate a system.<br />
<br />
{http://forums.pcsx2.net/Thread-blog-Introduction-to-Dynamic-Recompilation?pid=102002#pid102002 Click here to see a C++ code example of a simple interpreter]<br />
<br />
Using interpreters we constantly have to be fetching and executing instructions one-by-one. There is a lot of overhead in this, and minimal room for optimization since most special case optimizations will have the overhead of checking for them (so it will for example add extra if-statements and conditionals... reducing the gain from the optimization). But there's a faster way to do processor emulation which doesn't have these draw-backs... using dynamic recompilation!<br />
<br />
The basic idea of dynamic recompilation is to translate emulated instructions once, cache the emitted translated instructions, and then run the emitted native instructions as much times as needed.<br />
<br />
Since the instructions you read are not changing (lets leave out self-modifying code for this example), you can translate the emulated instructions into native cpu instructions (in our case x86-32 asm instructions) and then cache the translated instructions into 'blocks' of code, then just execute these blocks of native code instead of having to do the translation over and over again whenever the code needs to be read again.<br />
<br />
So for instance remember our above SL3 program:<br />
<br />
<source lang="asm"><br />
MOV reg1, reg0<br />
ADD reg4, reg2, reg3<br />
BR 5<br />
</source><br />
<br />
Lets assume this code is part of some function and gets called 100's of times a second (this could sound crazy, but games/applications commonly call the same code hundreds or thousands of times a second).<br />
<br />
Now our runCPU() interpreter function above will have to translate every instruction before it can actually compute the result.<br />
<br />
That is, it needs to fetch the opcode of every instruction, call the emulated function based on the opcode number, then actually compute the instruction result.<br />
But dynarecs allow us to skip the "fetch opcode" and the "call the emulated function" part, by only doing this once, and then caching the translated code into native asm blocks.<br />
<br />
To make a good dynarec, we first need a code emitter.<br />
An emitter is a series of functions we call that write native asm to some memory block we give it.<br />
So we use an x86-32 emitter to write native x86-32 asm code to blocks of memory, and then later we can execute these blocks as if they were normal c++ generated functions!<br />
<br />
PCSX2 has a very cool emitter that looks very similar to x86-32 assembly, except the instructions have an 'x' before them.<br />
So for example:<br />
<source lang="asm"><br />
mov eax, ecx;<br />
</source><br />
is<br />
<source lang="asm"><br />
xMOV(eax, ecx);<br />
</source><br />
with the pcsx2 emitter.<br />
<br />
Now the idea behind the dynarec we are going to write now, is that we will end blocks whenever a branch instruction is detected (which will jump to other blocks).<br />
<br />
The code for actually recompiling these blocks looks something like this:<br />
<br />
<source lang="asm"><br />
// This is our emulated MOV instruction<br />
void MOV() {<br />
u8 dest = fetch(); // Get destination register number<br />
u8 reg1 = fetch(); // Get source 1 register number<br />
<br />
xMOV(eax, ptr[&amp;cpuRegs[reg1]]); // Move reg1's data to eax<br />
xMOV(ptr[&amp;cpuRegs[dest]], eax); // Move eax to dest register<br />
<br />
fetch(); // This fetch is needed because every instruction in our SL3 processor is 4 bytes<br />
}<br />
<br />
// This is our emulated ADD instruction<br />
void ADD() {<br />
u8 dest = fetch(); // Get destination register number<br />
u8 reg1 = fetch(); // Get source 1 register number<br />
u8 reg2 = fetch(); // Get source 2 register number<br />
<br />
xMOV(eax, ptr[&amp;cpuRegs[reg1]]); // Move reg1's data to eax<br />
xADD(eax, ptr[&amp;cpuRegs[reg2]]); // Add eax with reg2's data <br />
xMOV(ptr[&amp;cpuRegs[dest]], eax); // Move eax to dest register<br />
}<br />
<br />
// This is our emulated BR (jump) instruction<br />
void BR() {<br />
s8 addr = fetch(); // Gets a number by which we will increment (or decrement if negative) PC by<br />
PC = (PC - 2) + (addr * 4);<br />
<br />
// Get a pointer to a block of x86-32 compiled code<br />
// that was recompiled by the recompileSL3() function<br />
u8* block_pointer = getBlock(PC);<br />
<br />
xJMP(block_pointer); // Jump to the pointer returned by getBlock()<br />
}<br />
<br />
// This goes through instructions and recompiles them<br />
// It recompiles instructions until it reaches a BR() instruction.<br />
u8* recompileSL3(u32 startPC) {<br />
u8* startPtr = xGetPtr(); // Gets pointer to where the emitter is currently pointing to (the start pointer of the block)<br />
PC = startPC; // Set PC to the start PC of this block<br />
bool do_recompile = true;<br />
while (do_recompile) {<br />
u8 opcode = fetch();<br />
switch (opcode) {<br />
case 0: MOV(); break;<br />
case 1: ADD(); break;<br />
case 2: // We stop recompiling on branches<br />
BR();<br />
do_recompile = false;<br />
break;<br />
}<br />
}<br />
return startPtr; // Returns the pointer to where our block of x86 generated code starts at<br />
}<br />
<br />
// This holds all the pointers to our blocks that were recompiled based on<br />
// starting PC address. We will assume that the instruction memory for<br />
// this processor is 16kb, which means that it can hold at-most 1024*16 bytes <br />
// worth of instructions. And therefor we we have at-most 1024*16 block pointers.<br />
static u8* blockArray[1024*16];<br />
<br />
// This returns a pointer to our recompiled block<br />
// If it hasn't been compiled, it'll recompile the block and then return that pointer.<br />
// We use __fastcall because it lets us pass the startPC parameter in the ecx register<br />
// instead of having to use the x86 stack...<br />
u8* __fastcall getBlock(u32 startPC) {<br />
if (blockArray[startPC] == null) {<br />
blockArray[startPC] = recompileSL3(startPC);<br />
}<br />
return blockArray[startPC];<br />
}<br />
<br />
// Basic cpu emulator using dynamic recompilation<br />
void runCPU() {<br />
// This sets our emitter to start emitting instructions to rec_cache<br />
// which is a large block of memory where we can write lots of<br />
// x86 asm instructions to...<br />
x86setPtr(rec_cache); <br />
<br />
__asm {<br />
pushad; // Save all our registers<br />
mov ecx, PC; // Move PC parameter into ecx register (for __fastcall)<br />
call getBlock; // Call the getBlock function<br />
jmp eax; // The block to jump to is returned in eax<br />
}<br />
}<br />
</source><br />
<br />
Note the above code doesn't have any logic to successfully exit once it starts executing recompiled blocks... I left this stuff out in order to not complicate things... so assume that somehow execution ends and we can get back to running the other parts of the emulator... <br />
Also note we need to restore registers when we exit execution, and we also need to set rec_cache to the new x86 emitter address (so it can continue where it left off instead of writing over already recompiled memory blocks).<br />
(Click here for a more complete sl3 recompiler code with cycle counting and exit logic)<br />
<br />
Now how does this work?<br />
Well we basically call runCPU() which calls the getBlock() function with the original PC value.<br />
getBlock() then checks if we have already recompiled a block with that start PC value, which we havn't yet, so it will go on to call recompileSL3() and give it the startPC value.<br />
recompileSL3() will loop through the opcodes, fetching them and then calling the appropriate emulated functions which will write to memory x86-32 asm instructions computing the correct results.<br />
recompileSL3() will continue looping until it reaches a BR() instruction (which will recursively call getBlock() until all the addresses have been recompiled and no more recompilation needs to happen).<br />
<br />
Once everything has been recompiled we jump to the starting block's execution address, and that's how we actually run the execution.<br />
The code that ends up being executed after we recompiled is only the emitted code, which were the functions prefixed with 'x' (xMOV, xADD, etc...).<br />
Notice that that's a lot of code omitted as we don't have to fetch anything anymore, but instead just run a series of fast generated asm functions...<br />
This means that although we have a lot of extra overhead on the actual recompilation, we end up generating some really fast code, that could be called 1000's of times a second, therefor making the initial overhead well worth it!<br />
<br />
We can also perform many optimizations while recompiling such as regalloc, constant propagation, optimized asm generation for special opcode cases, etc... (we didn't do this stuff in our example to avoid complexities).<br />
<br />
<br />
Hopefully this article has helped you gain a bit of understanding on how dynamic recompilation works, and why we use it in pcsx2 to gain a lot more speed over interpreters!<br />
Also note that dynarecs can be used for more than processor emulation, we also used dynamic recompilation in pcsx2 to cache asm optimized unpack routines that the ps2's vif does.<br />
<br />
I should also add that currently pcsx2 has the following dynarecs:<br />
*EE recompiler (for MIPS R5900 ee-core processor)<br />
*IOP recompiler (for MIPS R3000A i/o processor)<br />
*microVU recompiler (for VU0/VU1, and COP2 instructions)<br />
*Super VU recompiler (can be used instead of microVU for VU0/VU1)<br />
*newVIF unpack recompiler (recompiles vif unpack routines)<br />
*r3000air (not yet finished, but should one day supersede the IOP recompiler)<br />
<br />
{{PCSX2 Documentation Navbox}}</div>Krysto