mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Merge remote-tracking branch 'upstream/master' into patch-13
This commit is contained in:
commit
fad8b77044
111 changed files with 3022 additions and 1285 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -17,6 +17,7 @@
|
|||
*.suo
|
||||
*.aps
|
||||
*.exp
|
||||
*.qdact
|
||||
Debug
|
||||
Release
|
||||
Windows/x64
|
||||
|
|
|
@ -746,6 +746,7 @@ add_library(${CoreLibName} ${CoreLinkType}
|
|||
Core/CwCheat.h
|
||||
Core/HDRemaster.cpp
|
||||
Core/HDRemaster.h
|
||||
Core/ThreadEventQueue.h
|
||||
Core/Debugger/Breakpoints.cpp
|
||||
Core/Debugger/Breakpoints.h
|
||||
Core/Debugger/DebugInterface.h
|
||||
|
@ -897,6 +898,8 @@ add_library(${CoreLibName} ${CoreLinkType}
|
|||
Core/HLE/scePauth.h
|
||||
Core/HW/atrac3plus.cpp
|
||||
Core/HW/atrac3plus.h
|
||||
Core/HW/AsyncIOManager.cpp
|
||||
Core/HW/AsyncIOManager.h
|
||||
Core/HW/MediaEngine.cpp
|
||||
Core/HW/MediaEngine.h
|
||||
Core/HW/MpegDemux.cpp
|
||||
|
@ -931,6 +934,8 @@ add_library(${CoreLibName} ${CoreLinkType}
|
|||
Core/MIPS/MIPSInt.h
|
||||
Core/MIPS/MIPSIntVFPU.cpp
|
||||
Core/MIPS/MIPSIntVFPU.h
|
||||
Core/MIPS/MIPSStackWalk.cpp
|
||||
Core/MIPS/MIPSStackWalk.h
|
||||
Core/MIPS/MIPSTables.cpp
|
||||
Core/MIPS/MIPSTables.h
|
||||
Core/MIPS/MIPSVFPUUtils.cpp
|
||||
|
|
|
@ -951,7 +951,7 @@ extern const VFPEnc VFPOps[16][2] = {
|
|||
{{ -1, -1}, {0x3B, 0x30}}, // 14: VABSi
|
||||
};
|
||||
|
||||
extern const char *VFPOpNames[16] = {
|
||||
const char *VFPOpNames[16] = {
|
||||
"VMLA",
|
||||
"VNMLA",
|
||||
"VMLS",
|
||||
|
|
|
@ -272,8 +272,8 @@ public:
|
|||
|
||||
u32 Imm12Mod()
|
||||
{
|
||||
// This is a IMM12 with the top four bits being rotation and the
|
||||
// bottom eight being a IMM. This is for instructions that need to
|
||||
// This is an IMM12 with the top four bits being rotation and the
|
||||
// bottom eight being an IMM. This is for instructions that need to
|
||||
// expand a 8bit IMM to a 32bit value and gives you some rotation as
|
||||
// well.
|
||||
// Each rotation rotates to the right by 2 bits
|
||||
|
|
|
@ -96,7 +96,7 @@ private:
|
|||
#define CHECK_HEAP_INTEGRITY()
|
||||
|
||||
// Alignment
|
||||
#define GC_ALIGNED16(x) __declspec(align(16)) x
|
||||
#define MEMORY_ALIGNED16(x) __declspec(align(16)) x
|
||||
#define GC_ALIGNED32(x) __declspec(align(32)) x
|
||||
#define GC_ALIGNED64(x) __declspec(align(64)) x
|
||||
#define GC_ALIGNED128(x) __declspec(align(128)) x
|
||||
|
@ -137,7 +137,7 @@ private:
|
|||
#endif
|
||||
|
||||
#define __forceinline inline __attribute__((always_inline))
|
||||
#define GC_ALIGNED16(x) __attribute__((aligned(16))) x
|
||||
#define MEMORY_ALIGNED16(x) __attribute__((aligned(16))) x
|
||||
#define GC_ALIGNED32(x) __attribute__((aligned(32))) x
|
||||
#define GC_ALIGNED64(x) __attribute__((aligned(64))) x
|
||||
#define GC_ALIGNED128(x) __attribute__((aligned(128))) x
|
||||
|
|
|
@ -424,7 +424,9 @@ const KeyMap_IntStrPair axis_names[] = {
|
|||
{JOYSTICK_AXIS_GAS, "Gas"},
|
||||
{JOYSTICK_AXIS_BRAKE, "Brake"},
|
||||
{JOYSTICK_AXIS_DISTANCE, "Distance"},
|
||||
{JOYSTICK_AXIS_TILT, "Tilt"}
|
||||
{JOYSTICK_AXIS_TILT, "Tilt"},
|
||||
{JOYSTICK_AXIS_MOUSE_REL_X, "MouseDX"},
|
||||
{JOYSTICK_AXIS_MOUSE_REL_Y, "MouseDY"},
|
||||
};
|
||||
|
||||
static std::string unknown_key_name = "??";
|
||||
|
|
|
@ -15,6 +15,7 @@ set(SRCS
|
|||
MIPS/MIPSDisVFPU.cpp
|
||||
MIPS/MIPSInt.cpp
|
||||
MIPS/MIPSIntVFPU.cpp
|
||||
MIPS/MIPSStackWalk.cpp
|
||||
MIPS/MIPSTables.cpp
|
||||
MIPS/MIPSVFPUUtils.cpp
|
||||
MIPS/JitCommon/JitCommon.cpp
|
||||
|
@ -74,6 +75,7 @@ set(SRCS
|
|||
HLE/sceParseHttp.cpp
|
||||
HLE/sceVaudio.cpp
|
||||
HLE/sceAudiocodec.cpp
|
||||
HW/AsyncIOManager.cpp
|
||||
HW/MemoryStick.cpp
|
||||
HW/MediaEngine.cpp
|
||||
HW/SasAudio.cpp
|
||||
|
|
|
@ -89,6 +89,8 @@ void Config::Load(const char *iniFileName)
|
|||
#else
|
||||
cpu->Get("Jit", &bJit, true);
|
||||
#endif
|
||||
cpu->Get("SeparateCPUThread", &bSeparateCPUThread, false);
|
||||
cpu->Get("SeparateIOThread", &bSeparateIOThread, false);
|
||||
cpu->Get("FastMemory", &bFastMemory, false);
|
||||
cpu->Get("CPUSpeed", &iLockedCPUSpeed, false);
|
||||
|
||||
|
@ -99,7 +101,14 @@ void Config::Load(const char *iniFileName)
|
|||
#else
|
||||
graphics->Get("ResolutionScale", &iWindowZoom, 1);
|
||||
#endif
|
||||
graphics->Get("RenderingMode", &iRenderingMode, 1); // default is buffered rendering mode
|
||||
graphics->Get("RenderingMode", &iRenderingMode,
|
||||
// Many ARMv6 devices have serious problems with buffered rendering.
|
||||
#if defined(ARM) && !defined(ARMV7)
|
||||
0
|
||||
#else
|
||||
1
|
||||
#endif
|
||||
); // default is buffered rendering mode
|
||||
graphics->Get("HardwareTransform", &bHardwareTransform, true);
|
||||
graphics->Get("TextureFiltering", &iTexFiltering, 1);
|
||||
graphics->Get("SSAA", &bAntiAliasing, 0);
|
||||
|
@ -137,7 +146,7 @@ void Config::Load(const char *iniFileName)
|
|||
sound->Get("VolumeSFX", &iSFXVolume, 7);
|
||||
|
||||
IniFile::Section *control = iniFile.GetOrCreateSection("Control");
|
||||
control->Get("ShowStick", &bShowAnalogStick, false);
|
||||
control->Get("ShowAnalogStick", &bShowAnalogStick, true);
|
||||
#ifdef BLACKBERRY
|
||||
control->Get("ShowTouchControls", &bShowTouchControls, pixel_xres != pixel_yres);
|
||||
#elif defined(USING_GLES2)
|
||||
|
@ -226,6 +235,8 @@ void Config::Save()
|
|||
|
||||
IniFile::Section *cpu = iniFile.GetOrCreateSection("CPU");
|
||||
cpu->Set("Jit", bJit);
|
||||
cpu->Set("SeparateCPUThread", bSeparateCPUThread);
|
||||
cpu->Set("SeparateIOThread", bSeparateIOThread);
|
||||
cpu->Set("FastMemory", bFastMemory);
|
||||
cpu->Set("CPUSpeed", iLockedCPUSpeed);
|
||||
|
||||
|
|
|
@ -56,6 +56,9 @@ public:
|
|||
bool bIgnoreBadMemAccess;
|
||||
bool bFastMemory;
|
||||
bool bJit;
|
||||
// Definitely cannot be changed while game is running.
|
||||
bool bSeparateCPUThread;
|
||||
bool bSeparateIOThread;
|
||||
int iLockedCPUSpeed;
|
||||
bool bAutoSaveSymbolMap;
|
||||
std::string sReportHost;
|
||||
|
|
|
@ -251,6 +251,7 @@
|
|||
<ClCompile Include="HW\MpegDemux.cpp" />
|
||||
<ClCompile Include="HW\OMAConvert.cpp" />
|
||||
<ClCompile Include="HW\SasAudio.cpp" />
|
||||
<ClCompile Include="HW\AsyncIOManager.cpp" />
|
||||
<ClCompile Include="Loaders.cpp" />
|
||||
<ClCompile Include="MemMap.cpp" />
|
||||
<ClCompile Include="MemmapFunctions.cpp" />
|
||||
|
@ -335,6 +336,7 @@
|
|||
<ClCompile Include="PSPMixer.cpp" />
|
||||
<ClCompile Include="Reporting.cpp" />
|
||||
<ClCompile Include="SaveState.cpp" />
|
||||
<ClCompile Include="MIPS\MIPSStackWalk.cpp" />
|
||||
<ClCompile Include="System.cpp" />
|
||||
<ClCompile Include="Util\BlockAllocator.cpp" />
|
||||
<ClCompile Include="Util\PPGeDraw.cpp" />
|
||||
|
@ -437,6 +439,7 @@
|
|||
<ClInclude Include="HW\OMAConvert.h" />
|
||||
<ClInclude Include="HW\SasAudio.h" />
|
||||
<ClInclude Include="HW\MemoryStick.h" />
|
||||
<ClInclude Include="HW\AsyncIOManager.h" />
|
||||
<ClInclude Include="Loaders.h" />
|
||||
<ClInclude Include="MemMap.h" />
|
||||
<ClInclude Include="MIPS\ARM\ArmAsm.h">
|
||||
|
@ -485,7 +488,9 @@
|
|||
<ClInclude Include="PSPMixer.h" />
|
||||
<ClInclude Include="Reporting.h" />
|
||||
<ClInclude Include="SaveState.h" />
|
||||
<ClInclude Include="MIPS\MIPSStackWalk.h" />
|
||||
<ClInclude Include="System.h" />
|
||||
<ClInclude Include="ThreadEventQueue.h" />
|
||||
<ClInclude Include="Util\BlockAllocator.h" />
|
||||
<ClInclude Include="Util\PPGeDraw.h" />
|
||||
<ClInclude Include="Util\ppge_atlas.h" />
|
||||
|
|
|
@ -451,6 +451,12 @@
|
|||
<ClCompile Include="FileSystems\VirtualDiscFileSystem.cpp">
|
||||
<Filter>FileSystems</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HW\AsyncIOManager.cpp">
|
||||
<Filter>HW</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MIPS\MIPSStackWalk.cpp">
|
||||
<Filter>MIPS</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="ELF\ElfReader.h">
|
||||
|
@ -837,6 +843,15 @@
|
|||
<ClInclude Include="FileSystems\VirtualDiscFileSystem.h">
|
||||
<Filter>FileSystems</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ThreadEventQueue.h">
|
||||
<Filter>Core</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HW\AsyncIOManager.h">
|
||||
<Filter>HW</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MIPS\MIPSStackWalk.h">
|
||||
<Filter>MIPS</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="CMakeLists.txt" />
|
||||
|
|
|
@ -40,6 +40,7 @@ struct CoreParameter
|
|||
|
||||
std::string fileToStart;
|
||||
std::string mountIso; // If non-empty, and fileToStart is an ELF or PBP, will mount this ISO in the background.
|
||||
std::string errorString;
|
||||
|
||||
bool startPaused;
|
||||
bool disableG3Dlog;
|
||||
|
|
|
@ -74,7 +74,7 @@ volatile u32 hasTsEvents = false;
|
|||
// as we can already reach that structure through a register.
|
||||
int slicelength;
|
||||
|
||||
s64 globalTimer;
|
||||
MEMORY_ALIGNED16(s64) globalTimer;
|
||||
s64 idledCycles;
|
||||
|
||||
static std::recursive_mutex externalEventSection;
|
||||
|
|
|
@ -52,7 +52,8 @@ static u32 ComputeHash(u32 start, u32 size)
|
|||
|
||||
void SymbolMap::SortSymbols()
|
||||
{
|
||||
std::sort(entries.begin(), entries.end());
|
||||
lock_guard guard(lock_);
|
||||
std::sort(entries.begin(), entries.end());
|
||||
}
|
||||
|
||||
void SymbolMap::AnalyzeBackwards()
|
||||
|
@ -104,6 +105,7 @@ void SymbolMap::AnalyzeBackwards()
|
|||
|
||||
void SymbolMap::ResetSymbolMap()
|
||||
{
|
||||
lock_guard guard(lock_);
|
||||
#ifdef BWLINKS
|
||||
for (int i=0; i<numEntries; i++)
|
||||
{
|
||||
|
@ -118,6 +120,7 @@ void SymbolMap::ResetSymbolMap()
|
|||
|
||||
void SymbolMap::AddSymbol(const char *symbolname, unsigned int vaddress, size_t size, SymbolType st)
|
||||
{
|
||||
lock_guard guard(lock_);
|
||||
MapEntry e;
|
||||
strncpy(e.name, symbolname, 127);
|
||||
e.name[127] = '\0';
|
||||
|
@ -134,6 +137,7 @@ void SymbolMap::AddSymbol(const char *symbolname, unsigned int vaddress, size_t
|
|||
|
||||
bool SymbolMap::LoadSymbolMap(const char *filename)
|
||||
{
|
||||
lock_guard guard(lock_);
|
||||
SymbolMap::ResetSymbolMap();
|
||||
FILE *f = fopen(filename,"r");
|
||||
if (!f)
|
||||
|
@ -148,7 +152,7 @@ bool SymbolMap::LoadSymbolMap(const char *filename)
|
|||
|
||||
while (!feof(f))
|
||||
{
|
||||
char line[512],temp[256];
|
||||
char line[512], temp[256] = {0};
|
||||
char *p = fgets(line,512,f);
|
||||
if(p == NULL)
|
||||
break;
|
||||
|
@ -207,6 +211,7 @@ bool SymbolMap::LoadSymbolMap(const char *filename)
|
|||
|
||||
void SymbolMap::SaveSymbolMap(const char *filename) const
|
||||
{
|
||||
lock_guard guard(lock_);
|
||||
FILE *f = fopen(filename,"w");
|
||||
if (!f)
|
||||
return;
|
||||
|
@ -221,13 +226,14 @@ void SymbolMap::SaveSymbolMap(const char *filename) const
|
|||
|
||||
bool SymbolMap::LoadNocashSym(const char *filename)
|
||||
{
|
||||
lock_guard guard(lock_);
|
||||
FILE *f = fopen(filename,"r");
|
||||
if (!f)
|
||||
return false;
|
||||
|
||||
while (!feof(f))
|
||||
{
|
||||
char line[256],value[256];
|
||||
char line[256], value[256] = {0};
|
||||
char *p = fgets(line,256,f);
|
||||
if(p == NULL)
|
||||
break;
|
||||
|
@ -389,6 +395,8 @@ void SymbolMap::FillSymbolListBox(HWND listbox,SymbolType symmask) const
|
|||
}
|
||||
}
|
||||
|
||||
lock_guard guard(lock_);
|
||||
|
||||
SendMessage(listbox, WM_SETREDRAW, FALSE, 0);
|
||||
SendMessage(listbox, LB_INITSTORAGE, (WPARAM)entries.size(), (LPARAM)entries.size() * 30);
|
||||
for (auto it = entries.begin(), end = entries.end(); it != end; ++it)
|
||||
|
@ -427,11 +435,13 @@ void SymbolMap::FillSymbolComboBox(HWND listbox,SymbolType symmask) const
|
|||
//ListBox_AddString(listbox,"(0x80002000)");
|
||||
//ListBox_SetItemData(listbox,1,0x80002000);
|
||||
|
||||
lock_guard guard(lock_);
|
||||
|
||||
SendMessage(listbox, WM_SETREDRAW, FALSE, 0);
|
||||
SendMessage(listbox, CB_INITSTORAGE, (WPARAM)entries.size(), (LPARAM)entries.size() * 30);
|
||||
for (auto it = entries.begin(), end = entries.end(); it != end; ++it)
|
||||
for (size_t i = 0, end = entries.size(); i < end; ++i)
|
||||
{
|
||||
const MapEntry &entry = *it;
|
||||
const MapEntry &entry = entries[i];
|
||||
if (entry.type & symmask)
|
||||
{
|
||||
char temp[256];
|
||||
|
@ -449,6 +459,8 @@ void SymbolMap::FillSymbolComboBox(HWND listbox,SymbolType symmask) const
|
|||
void SymbolMap::FillListBoxBLinks(HWND listbox, int num) const
|
||||
{
|
||||
ListBox_ResetContent(listbox);
|
||||
|
||||
lock_guard guard(lock_);
|
||||
|
||||
int style = GetWindowLong(listbox,GWL_STYLE);
|
||||
|
||||
|
|
|
@ -17,7 +17,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "../../Globals.h"
|
||||
#include "Globals.h"
|
||||
#include "native/base/mutex.h"
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
@ -106,6 +107,7 @@ private:
|
|||
std::set<MapEntryUniqueInfo> uniqueEntries;
|
||||
std::vector<MapEntry> entries;
|
||||
std::map<u32, u32> entryRanges;
|
||||
mutable recursive_mutex lock_;
|
||||
};
|
||||
|
||||
extern SymbolMap symbolMap;
|
||||
|
|
|
@ -401,8 +401,8 @@ std::wstring PSPOskDialog::CombinationKorean(bool isInput)
|
|||
}
|
||||
}
|
||||
} else {
|
||||
int tmp = GetIndex(kor_vowel, sw);
|
||||
if(tmp == -1) {
|
||||
int tmp3 = GetIndex(kor_vowel, sw);
|
||||
if (tmp3 == -1) {
|
||||
string += inputChars[i];
|
||||
if (inputChars.size() < FieldMaxLength()) {
|
||||
string += sw;
|
||||
|
@ -431,29 +431,29 @@ std::wstring PSPOskDialog::CombinationKorean(bool isInput)
|
|||
u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C + kor_lconsSpr[tmp2 + 1];
|
||||
string += code;
|
||||
|
||||
code = 0xAC00 + kor_lconsSpr[tmp2 + 2] * 0x24C + tmp * 0x1C;
|
||||
code = 0xAC00 + kor_lconsSpr[tmp2 + 2] * 0x24C + tmp3 * 0x1C;
|
||||
string += code;
|
||||
|
||||
if(isInput == true) {
|
||||
i_value[0] = kor_lconsSpr[tmp2 + 2];
|
||||
i_value[1] = tmp;
|
||||
i_value[1] = tmp3;
|
||||
i_level = 2;
|
||||
}
|
||||
} else {
|
||||
int tmp2 = GetIndex(kor_cons, kor_lcons[i_value[2]]);
|
||||
int tmp4 = GetIndex(kor_cons, kor_lcons[i_value[2]]);
|
||||
|
||||
if(tmp2 != -1) {
|
||||
if (tmp4 != -1) {
|
||||
u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C;
|
||||
|
||||
string += code;
|
||||
|
||||
code = 0xAC00 + tmp2 * 0x24C + tmp * 0x1C;
|
||||
code = 0xAC00 + tmp4 * 0x24C + tmp3 * 0x1C;
|
||||
|
||||
string += code;
|
||||
|
||||
if(isInput == true) {
|
||||
i_value[0] = tmp2;
|
||||
i_value[1] = tmp;
|
||||
i_value[0] = tmp4;
|
||||
i_value[1] = tmp3;
|
||||
i_level = 2;
|
||||
}
|
||||
} else {
|
||||
|
@ -794,7 +794,7 @@ int PSPOskDialog::NativeKeyboard()
|
|||
for (u32 i = 0, end = oskParams->fields[0].outtextlength; i < end; ++i)
|
||||
{
|
||||
u16 value = 0;
|
||||
if (i < ARRAY_SIZE(input))
|
||||
if (i < FieldMaxLength())
|
||||
value = input[i];
|
||||
outText[i] = value;
|
||||
}
|
||||
|
|
|
@ -159,7 +159,7 @@ int PSPSaveDialog::Init(int paramAddr)
|
|||
display = DS_NONE;
|
||||
break;
|
||||
|
||||
case SCE_UTILITY_SAVEDATA_TYPE_DELETE: // This run on PSP display a list of all save on the PSP. Weird. (Not really, it's to let you free up space)
|
||||
case SCE_UTILITY_SAVEDATA_TYPE_DELETE: // When run on a PSP, displays a list of all saves on the PSP. Weird. (Not really, it's to let you free up space)
|
||||
display = DS_DELETE_LIST_CHOICE;
|
||||
break;
|
||||
default:
|
||||
|
@ -631,7 +631,7 @@ int PSPSaveDialog::Update()
|
|||
DisplaySaveIcon();
|
||||
DisplaySaveDataInfo2();
|
||||
|
||||
DisplayMessage(d->T("Do you want to overwrite the data?"), true);
|
||||
DisplayMessage(d->T("Confirm Overwrite","Do you want to overwrite the data?"), true);
|
||||
|
||||
DisplayButtons(DS_BUTTON_OK | DS_BUTTON_CANCEL);
|
||||
DisplayBanner(DB_SAVE);
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "Core/Reporting.h"
|
||||
#include "Core/Dialog/SavedataParam.h"
|
||||
#include "Core/Dialog/PSPSaveDialog.h"
|
||||
|
@ -157,6 +159,16 @@ void SavedataParam::Init()
|
|||
{
|
||||
pspFileSystem.MkDir(savePath);
|
||||
}
|
||||
// Create a nomedia file to hide save icons form Android image viewer
|
||||
#ifdef ANDROID
|
||||
int handle = pspFileSystem.OpenFile(savePath + ".nomedia", (FileAccess)(FILEACCESS_CREATE | FILEACCESS_WRITE), 0);
|
||||
if (handle) {
|
||||
ILOG("Created .nomedia file");
|
||||
pspFileSystem.CloseFile(handle);
|
||||
} else {
|
||||
ILOG("Failed to create .nomedia file");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string SavedataParam::GetSaveDirName(SceUtilitySavedataParam* param, int saveId)
|
||||
|
|
|
@ -146,6 +146,14 @@ void ElfReader::LoadRelocations(Elf32_Rel *rels, int numRelocs)
|
|||
}
|
||||
break;
|
||||
|
||||
case R_MIPS_16:
|
||||
{
|
||||
char temp[256];
|
||||
MIPSDisAsm(op, 0, temp);
|
||||
ERROR_LOG_REPORT(LOADER, "WARNING: Unsupported R_MIPS_16 reloc @ %08x : %s", addr, temp);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0: // another GP reloc!
|
||||
{
|
||||
char temp[256];
|
||||
|
@ -158,7 +166,7 @@ void ElfReader::LoadRelocations(Elf32_Rel *rels, int numRelocs)
|
|||
{
|
||||
char temp[256];
|
||||
MIPSDisAsm(op, 0, temp);
|
||||
ERROR_LOG_REPORT(LOADER,"ARGH IT'S A UNKNOWN RELOCATION!!!!!!!! %08x, type=%d : %s", addr, type, temp);
|
||||
ERROR_LOG_REPORT(LOADER,"ARGH IT'S AN UNKNOWN RELOCATION!!!!!!!! %08x, type=%d : %s", addr, type, temp);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ PBPReader::PBPReader(const char *filename) : header_() {
|
|||
return;
|
||||
}
|
||||
|
||||
INFO_LOG(LOADER, "Loading PBP, version = %08x", header_.version);
|
||||
DEBUG_LOG(LOADER, "Loading PBP, version = %08x", header_.version);
|
||||
}
|
||||
|
||||
u8 *PBPReader::GetSubFile(PBPSubFile file, size_t *outSize) {
|
||||
|
|
|
@ -243,7 +243,7 @@ bool DirectoryFileHandle::Open(std::string& basePath, std::string& fileName, Fil
|
|||
|
||||
size_t DirectoryFileHandle::Read(u8* pointer, s64 size)
|
||||
{
|
||||
size_t bytesRead;
|
||||
size_t bytesRead = 0;
|
||||
#ifdef _WIN32
|
||||
::ReadFile(hFile, (LPVOID)pointer, (DWORD)size, (LPDWORD)&bytesRead, 0);
|
||||
#else
|
||||
|
@ -254,7 +254,7 @@ size_t DirectoryFileHandle::Read(u8* pointer, s64 size)
|
|||
|
||||
size_t DirectoryFileHandle::Write(const u8* pointer, s64 size)
|
||||
{
|
||||
size_t bytesWritten;
|
||||
size_t bytesWritten = 0;
|
||||
#ifdef _WIN32
|
||||
::WriteFile(hFile, (LPVOID)pointer, (DWORD)size, (LPDWORD)&bytesWritten, 0);
|
||||
#else
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
#include "Common/StringUtils.h"
|
||||
#include "Core/FileSystems/MetaFileSystem.h"
|
||||
#include "Core/HLE/sceKernelThread.h"
|
||||
|
@ -162,6 +163,7 @@ static bool RealPath(const std::string ¤tDirectory, const std::string &inP
|
|||
|
||||
IFileSystem *MetaFileSystem::GetHandleOwner(u32 handle)
|
||||
{
|
||||
lock_guard guard(lock);
|
||||
for (size_t i = 0; i < fileSystems.size(); i++)
|
||||
{
|
||||
if (fileSystems[i].system->OwnsHandle(handle))
|
||||
|
@ -171,9 +173,9 @@ IFileSystem *MetaFileSystem::GetHandleOwner(u32 handle)
|
|||
return 0;
|
||||
}
|
||||
|
||||
extern u32 ioErrorCode;
|
||||
bool MetaFileSystem::MapFilePath(const std::string &_inpath, std::string &outpath, MountPoint **system)
|
||||
{
|
||||
lock_guard guard(lock);
|
||||
std::string realpath;
|
||||
|
||||
// Special handling: host0:command.txt (as seen in Super Monkey Ball Adventures, for example)
|
||||
|
@ -193,7 +195,7 @@ bool MetaFileSystem::MapFilePath(const std::string &_inpath, std::string &outpat
|
|||
//Attempt to emulate SCE_KERNEL_ERROR_NOCWD / 8002032C: may break things requiring fixes elsewhere
|
||||
if (inpath.find(':') == std::string::npos /* means path is relative */)
|
||||
{
|
||||
ioErrorCode = SCE_KERNEL_ERROR_NOCWD;
|
||||
lastOpenError = SCE_KERNEL_ERROR_NOCWD;
|
||||
WARN_LOG_REPORT(HLE, "Path is relative, but current directory not set for thread %i. returning 8002032C(SCE_KERNEL_ERROR_NOCWD) instead.", currentThread);
|
||||
}
|
||||
}
|
||||
|
@ -225,14 +227,25 @@ bool MetaFileSystem::MapFilePath(const std::string &_inpath, std::string &outpat
|
|||
|
||||
void MetaFileSystem::Mount(std::string prefix, IFileSystem *system)
|
||||
{
|
||||
lock_guard guard(lock);
|
||||
MountPoint x;
|
||||
x.prefix=prefix;
|
||||
x.system=system;
|
||||
x.prefix = prefix;
|
||||
x.system = system;
|
||||
fileSystems.push_back(x);
|
||||
}
|
||||
|
||||
void MetaFileSystem::Unmount(std::string prefix, IFileSystem *system)
|
||||
{
|
||||
lock_guard guard(lock);
|
||||
MountPoint x;
|
||||
x.prefix = prefix;
|
||||
x.system = system;
|
||||
fileSystems.erase(std::remove(fileSystems.begin(), fileSystems.end(), x), fileSystems.end());
|
||||
}
|
||||
|
||||
void MetaFileSystem::Shutdown()
|
||||
{
|
||||
lock_guard guard(lock);
|
||||
current = 6;
|
||||
|
||||
// Ownership is a bit convoluted. Let's just delete everything once.
|
||||
|
@ -252,8 +265,18 @@ void MetaFileSystem::Shutdown()
|
|||
startingDirectory = "";
|
||||
}
|
||||
|
||||
u32 MetaFileSystem::OpenWithError(int &error, std::string filename, FileAccess access, const char *devicename)
|
||||
{
|
||||
lock_guard guard(lock);
|
||||
u32 h = OpenFile(filename, access, devicename);
|
||||
error = lastOpenError;
|
||||
return h;
|
||||
}
|
||||
|
||||
u32 MetaFileSystem::OpenFile(std::string filename, FileAccess access, const char *devicename)
|
||||
{
|
||||
lock_guard guard(lock);
|
||||
lastOpenError = 0;
|
||||
std::string of;
|
||||
MountPoint *mount;
|
||||
if (MapFilePath(filename, of, &mount))
|
||||
|
@ -268,6 +291,7 @@ u32 MetaFileSystem::OpenFile(std::string filename, FileAccess access, const char
|
|||
|
||||
PSPFileInfo MetaFileSystem::GetFileInfo(std::string filename)
|
||||
{
|
||||
lock_guard guard(lock);
|
||||
std::string of;
|
||||
IFileSystem *system;
|
||||
if (MapFilePath(filename, of, &system))
|
||||
|
@ -283,6 +307,7 @@ PSPFileInfo MetaFileSystem::GetFileInfo(std::string filename)
|
|||
|
||||
bool MetaFileSystem::GetHostPath(const std::string &inpath, std::string &outpath)
|
||||
{
|
||||
lock_guard guard(lock);
|
||||
std::string of;
|
||||
IFileSystem *system;
|
||||
if (MapFilePath(inpath, of, &system)) {
|
||||
|
@ -294,6 +319,7 @@ bool MetaFileSystem::GetHostPath(const std::string &inpath, std::string &outpath
|
|||
|
||||
std::vector<PSPFileInfo> MetaFileSystem::GetDirListing(std::string path)
|
||||
{
|
||||
lock_guard guard(lock);
|
||||
std::string of;
|
||||
IFileSystem *system;
|
||||
if (MapFilePath(path, of, &system))
|
||||
|
@ -309,11 +335,13 @@ std::vector<PSPFileInfo> MetaFileSystem::GetDirListing(std::string path)
|
|||
|
||||
void MetaFileSystem::ThreadEnded(int threadID)
|
||||
{
|
||||
lock_guard guard(lock);
|
||||
currentDir.erase(threadID);
|
||||
}
|
||||
|
||||
int MetaFileSystem::ChDir(const std::string &dir)
|
||||
{
|
||||
lock_guard guard(lock);
|
||||
// Retain the old path and fail if the arg is 1023 bytes or longer.
|
||||
if (dir.size() >= 1023)
|
||||
return SCE_KERNEL_ERROR_NAMETOOLONG;
|
||||
|
@ -348,6 +376,7 @@ int MetaFileSystem::ChDir(const std::string &dir)
|
|||
|
||||
bool MetaFileSystem::MkDir(const std::string &dirname)
|
||||
{
|
||||
lock_guard guard(lock);
|
||||
std::string of;
|
||||
IFileSystem *system;
|
||||
if (MapFilePath(dirname, of, &system))
|
||||
|
@ -362,6 +391,7 @@ bool MetaFileSystem::MkDir(const std::string &dirname)
|
|||
|
||||
bool MetaFileSystem::RmDir(const std::string &dirname)
|
||||
{
|
||||
lock_guard guard(lock);
|
||||
std::string of;
|
||||
IFileSystem *system;
|
||||
if (MapFilePath(dirname, of, &system))
|
||||
|
@ -376,6 +406,7 @@ bool MetaFileSystem::RmDir(const std::string &dirname)
|
|||
|
||||
int MetaFileSystem::RenameFile(const std::string &from, const std::string &to)
|
||||
{
|
||||
lock_guard guard(lock);
|
||||
std::string of;
|
||||
std::string rf;
|
||||
IFileSystem *osystem;
|
||||
|
@ -407,6 +438,7 @@ int MetaFileSystem::RenameFile(const std::string &from, const std::string &to)
|
|||
|
||||
bool MetaFileSystem::RemoveFile(const std::string &filename)
|
||||
{
|
||||
lock_guard guard(lock);
|
||||
std::string of;
|
||||
IFileSystem *system;
|
||||
if (MapFilePath(filename, of, &system))
|
||||
|
@ -421,6 +453,7 @@ bool MetaFileSystem::RemoveFile(const std::string &filename)
|
|||
|
||||
void MetaFileSystem::CloseFile(u32 handle)
|
||||
{
|
||||
lock_guard guard(lock);
|
||||
IFileSystem *sys = GetHandleOwner(handle);
|
||||
if (sys)
|
||||
sys->CloseFile(handle);
|
||||
|
@ -428,6 +461,7 @@ void MetaFileSystem::CloseFile(u32 handle)
|
|||
|
||||
size_t MetaFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size)
|
||||
{
|
||||
lock_guard guard(lock);
|
||||
IFileSystem *sys = GetHandleOwner(handle);
|
||||
if (sys)
|
||||
return sys->ReadFile(handle,pointer,size);
|
||||
|
@ -437,6 +471,7 @@ size_t MetaFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size)
|
|||
|
||||
size_t MetaFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size)
|
||||
{
|
||||
lock_guard guard(lock);
|
||||
IFileSystem *sys = GetHandleOwner(handle);
|
||||
if (sys)
|
||||
return sys->WriteFile(handle,pointer,size);
|
||||
|
@ -446,6 +481,7 @@ size_t MetaFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size)
|
|||
|
||||
size_t MetaFileSystem::SeekFile(u32 handle, s32 position, FileMove type)
|
||||
{
|
||||
lock_guard guard(lock);
|
||||
IFileSystem *sys = GetHandleOwner(handle);
|
||||
if (sys)
|
||||
return sys->SeekFile(handle,position,type);
|
||||
|
@ -455,6 +491,7 @@ size_t MetaFileSystem::SeekFile(u32 handle, s32 position, FileMove type)
|
|||
|
||||
void MetaFileSystem::DoState(PointerWrap &p)
|
||||
{
|
||||
lock_guard guard(lock);
|
||||
p.Do(current);
|
||||
|
||||
// Save/load per-thread current directory map
|
||||
|
|
|
@ -17,7 +17,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "FileSystem.h"
|
||||
#include "native/base/mutex.h"
|
||||
#include "Core/FileSystems/FileSystem.h"
|
||||
|
||||
class MetaFileSystem : public IHandleAllocator, public IFileSystem
|
||||
{
|
||||
|
@ -27,6 +28,11 @@ private:
|
|||
{
|
||||
std::string prefix;
|
||||
IFileSystem *system;
|
||||
|
||||
bool operator == (const MountPoint &other) const
|
||||
{
|
||||
return prefix == other.prefix && system == other.system;
|
||||
}
|
||||
};
|
||||
std::vector<MountPoint> fileSystems;
|
||||
|
||||
|
@ -34,6 +40,8 @@ private:
|
|||
currentDir_t currentDir;
|
||||
|
||||
std::string startingDirectory;
|
||||
int lastOpenError;
|
||||
recursive_mutex lock;
|
||||
|
||||
public:
|
||||
MetaFileSystem()
|
||||
|
@ -42,7 +50,7 @@ public:
|
|||
}
|
||||
|
||||
void Mount(std::string prefix, IFileSystem *system);
|
||||
void Unmount(IFileSystem *system);
|
||||
void Unmount(std::string prefix, IFileSystem *system);
|
||||
|
||||
void ThreadEnded(int threadID);
|
||||
|
||||
|
@ -72,7 +80,8 @@ public:
|
|||
bool GetHostPath(const std::string &inpath, std::string &outpath);
|
||||
|
||||
std::vector<PSPFileInfo> GetDirListing(std::string path);
|
||||
u32 OpenFile(std::string filename, FileAccess access, const char *devicename=NULL);
|
||||
u32 OpenFile(std::string filename, FileAccess access, const char *devicename = NULL);
|
||||
u32 OpenWithError(int &error, std::string filename, FileAccess access, const char *devicename = NULL);
|
||||
void CloseFile(u32 handle);
|
||||
size_t ReadFile(u32 handle, u8 *pointer, s64 size);
|
||||
size_t WriteFile(u32 handle, const u8 *pointer, s64 size);
|
||||
|
@ -94,6 +103,7 @@ public:
|
|||
// TODO: void IoCtl(...)
|
||||
|
||||
void SetStartingDirectory(const std::string &dir) {
|
||||
lock_guard guard(lock);
|
||||
startingDirectory = dir;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -558,7 +558,6 @@ PSPFileInfo VirtualDiscFileSystem::GetFileInfo(std::string filename) {
|
|||
|
||||
x.size = File::GetSize(fullName);
|
||||
|
||||
int fileIndex = getFileListIndex(filename);
|
||||
x.startSector = fileList[fileIndex].firstBlock;
|
||||
x.numSectors = (x.size+2047)/2048;
|
||||
|
||||
|
|
|
@ -219,13 +219,13 @@ void PGF::ReadPtr(const u8 *ptr, size_t dataSize) {
|
|||
}
|
||||
|
||||
// And shadow glyphs.
|
||||
for (size_t i = 0; i < shadowGlyphs.size(); i++) {
|
||||
for (size_t i = 0; i < glyphs.size(); i++) {
|
||||
size_t shadowId = glyphs[i].shadowID;
|
||||
if (shadowId < shadowMap.size()) {
|
||||
if ((shadowId < shadowMap.size()) && (shadowId < shadowGlyphs.size())) {
|
||||
size_t charId = shadowMap[shadowId];
|
||||
if (charId < glyphs.size()) {
|
||||
// TODO: check for pre existing shadow glyph
|
||||
GetGlyph(fontData, charPointers[charId] * 4 * 8 /* ??? */, FONT_PGF_SHADOWGLYPH, shadowGlyphs[i]);
|
||||
GetGlyph(fontData, charPointers[charId] * 4 * 8 /* ??? */, FONT_PGF_SHADOWGLYPH, shadowGlyphs[shadowId]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -337,50 +337,86 @@ bool PGF::GetGlyph(const u8 *fontdata, size_t charPtr, int glyphType, Glyph &gly
|
|||
glyph.shadowID = getBits(9, fontdata, charPtr);
|
||||
charPtr += 9;
|
||||
|
||||
int dimensionIndex = getBits(8, fontdata, charPtr);
|
||||
charPtr += 8;
|
||||
if ((glyph.flags & FONT_PGF_METRIC_DIMENSION_INDEX) == FONT_PGF_METRIC_DIMENSION_INDEX)
|
||||
{
|
||||
int dimensionIndex = getBits(8, fontdata, charPtr);
|
||||
charPtr += 8;
|
||||
|
||||
int xAdjustIndex = getBits(8, fontdata, charPtr);
|
||||
charPtr += 8;
|
||||
if (dimensionIndex < header.dimTableLength) {
|
||||
glyph.dimensionWidth = dimensionTable[0][dimensionIndex];
|
||||
glyph.dimensionHeight = dimensionTable[1][dimensionIndex];
|
||||
}
|
||||
|
||||
int yAdjustIndex = getBits(8, fontdata, charPtr);
|
||||
charPtr += 8;
|
||||
if (dimensionIndex == 0 && isJPCSPFont(fileName.c_str())) {
|
||||
// Fonts created by ttf2pgf do not contain complete Glyph information.
|
||||
// Provide default values.
|
||||
glyph.dimensionWidth = glyph.w << 6;
|
||||
glyph.dimensionHeight = glyph.h << 6;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
glyph.dimensionWidth = getBits(32, fontdata, charPtr);
|
||||
charPtr += 32;
|
||||
glyph.dimensionHeight = getBits(32, fontdata, charPtr);
|
||||
charPtr += 32;
|
||||
}
|
||||
|
||||
charPtr +=
|
||||
((glyph.flags & FONT_PGF_METRIC_FLAG1) ? 0 : 56) +
|
||||
((glyph.flags & FONT_PGF_METRIC_FLAG2) ? 0 : 56) +
|
||||
((glyph.flags & FONT_PGF_METRIC_FLAG3) ? 0 : 56);
|
||||
if ((glyph.flags & FONT_PGF_METRIC_BEARING_X_INDEX) == FONT_PGF_METRIC_BEARING_X_INDEX)
|
||||
{
|
||||
int xAdjustIndex = getBits(8, fontdata, charPtr);
|
||||
charPtr += 8;
|
||||
|
||||
if (xAdjustIndex < header.xAdjustTableLength) {
|
||||
glyph.xAdjustH = xAdjustTable[0][xAdjustIndex];
|
||||
glyph.xAdjustV = xAdjustTable[1][xAdjustIndex];
|
||||
}
|
||||
|
||||
if (xAdjustIndex == 0 && isJPCSPFont(fileName.c_str()))
|
||||
{
|
||||
// Fonts created by ttf2pgf do not contain complete Glyph information.
|
||||
// Provide default values.
|
||||
glyph.xAdjustH = glyph.left << 6;
|
||||
glyph.xAdjustV = glyph.left << 6;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
glyph.xAdjustH = getBits(32, fontdata, charPtr);
|
||||
charPtr += 32;
|
||||
glyph.xAdjustV = getBits(32, fontdata, charPtr);
|
||||
charPtr += 32;
|
||||
}
|
||||
|
||||
if ((glyph.flags & FONT_PGF_METRIC_BEARING_Y_INDEX) == FONT_PGF_METRIC_BEARING_Y_INDEX)
|
||||
{
|
||||
int yAdjustIndex = getBits(8, fontdata, charPtr);
|
||||
charPtr += 8;
|
||||
|
||||
if (yAdjustIndex < header.xAdjustTableLength) {
|
||||
glyph.yAdjustH = yAdjustTable[0][yAdjustIndex];
|
||||
glyph.yAdjustV = yAdjustTable[1][yAdjustIndex];
|
||||
}
|
||||
|
||||
if (yAdjustIndex == 0 && isJPCSPFont(fileName.c_str()))
|
||||
{
|
||||
// Fonts created by ttf2pgf do not contain complete Glyph information.
|
||||
// Provide default values.
|
||||
glyph.yAdjustH = glyph.top << 6;
|
||||
glyph.yAdjustV = glyph.top << 6;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
glyph.yAdjustH = getBits(32, fontdata, charPtr);
|
||||
charPtr += 32;
|
||||
glyph.yAdjustV = getBits(32, fontdata, charPtr);
|
||||
charPtr += 32;
|
||||
}
|
||||
|
||||
int advanceIndex = getBits(8, fontdata, charPtr);
|
||||
charPtr += 8;
|
||||
|
||||
if (dimensionIndex < header.dimTableLength) {
|
||||
glyph.dimensionWidth = dimensionTable[0][dimensionIndex];
|
||||
glyph.dimensionHeight = dimensionTable[1][dimensionIndex];
|
||||
}
|
||||
|
||||
if (xAdjustIndex < header.xAdjustTableLength) {
|
||||
glyph.xAdjustH = xAdjustTable[0][xAdjustIndex];
|
||||
glyph.xAdjustV = xAdjustTable[1][xAdjustIndex];
|
||||
}
|
||||
|
||||
if (yAdjustIndex < header.xAdjustTableLength) {
|
||||
glyph.yAdjustH = yAdjustTable[0][yAdjustIndex];
|
||||
glyph.yAdjustV = yAdjustTable[1][yAdjustIndex];
|
||||
}
|
||||
|
||||
if (dimensionIndex == 0 && xAdjustIndex == 0 && yAdjustIndex == 0 && isJPCSPFont(fileName.c_str())) {
|
||||
// Fonts created by ttf2pgf do not contain complete Glyph information.
|
||||
// Provide default values.
|
||||
glyph.dimensionWidth = glyph.w << 6;
|
||||
glyph.dimensionHeight = glyph.h << 6;
|
||||
// This stuff doesn't exactly look right.
|
||||
glyph.xAdjustH = glyph.left << 6;
|
||||
glyph.xAdjustV = glyph.left << 6;
|
||||
glyph.yAdjustH = glyph.top << 6;
|
||||
glyph.yAdjustV = glyph.top << 6;
|
||||
}
|
||||
|
||||
if (advanceIndex < header.advanceTableLength) {
|
||||
glyph.advanceH = advanceTable[0][advanceIndex];
|
||||
glyph.advanceV = advanceTable[1][advanceIndex];
|
||||
|
@ -578,4 +614,4 @@ u32 GetFontPixelColor(int color, int pixelformat) {
|
|||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,9 +35,10 @@ enum {
|
|||
FONT_PGF_BMP_H_ROWS = 0x01,
|
||||
FONT_PGF_BMP_V_ROWS = 0x02,
|
||||
FONT_PGF_BMP_OVERLAY = 0x03,
|
||||
FONT_PGF_METRIC_FLAG1 = 0x04,
|
||||
FONT_PGF_METRIC_FLAG2 = 0x08,
|
||||
FONT_PGF_METRIC_FLAG3 = 0x10,
|
||||
// Metric names according to JPCSP findings
|
||||
FONT_PGF_METRIC_DIMENSION_INDEX = 0x04,
|
||||
FONT_PGF_METRIC_BEARING_X_INDEX = 0x08,
|
||||
FONT_PGF_METRIC_BEARING_Y_INDEX = 0x10,
|
||||
FONT_PGF_CHARGLYPH = 0x20,
|
||||
FONT_PGF_SHADOWGLYPH = 0x40,
|
||||
};
|
||||
|
|
|
@ -1061,7 +1061,7 @@ int __AtracSetContext(Atrac *atrac)
|
|||
// select the audio stream
|
||||
ret = av_find_best_stream(atrac->pFormatCtx, AVMEDIA_TYPE_AUDIO, -1, -1, &pCodec, 0);
|
||||
if (ret < 0) {
|
||||
ERROR_LOG(HLE, "av_find_best_stream: Cannot find a audio stream in the input file %d", ret);
|
||||
ERROR_LOG(HLE, "av_find_best_stream: Cannot find an audio stream in the input file %d", ret);
|
||||
return -1;
|
||||
}
|
||||
atrac->audio_stream_index = ret;
|
||||
|
|
|
@ -431,6 +431,12 @@ int sceCccIsValidJIS(u32 c)
|
|||
return c != 0;
|
||||
}
|
||||
|
||||
int sceCccIsValidUnicode(u32 c)
|
||||
{
|
||||
WARN_LOG(HLE, "UNIMPL sceCccIsValidUnicode(%08x)", c);
|
||||
return c != 0;
|
||||
}
|
||||
|
||||
u32 sceCccSetErrorCharUTF8(u32 c)
|
||||
{
|
||||
DEBUG_LOG(HLE, "sceCccSetErrorCharUTF8(%08x)", c);
|
||||
|
@ -507,6 +513,7 @@ const HLEFunction sceCcc[] =
|
|||
{0x76e33e9c, WrapI_U<sceCccIsValidUCS2>, "sceCccIsValidUCS2"},
|
||||
{0xd2b18485, WrapI_U<sceCccIsValidUCS4>, "sceCccIsValidUCS4"},
|
||||
{0xa2d5d209, WrapI_U<sceCccIsValidJIS>, "sceCccIsValidJIS"},
|
||||
{0xbd11eef3, WrapI_U<sceCccIsValidUnicode>, "sceCccIsValidUnicode"},
|
||||
{0x17e1d813, WrapU_U<sceCccSetErrorCharUTF8>, "sceCccSetErrorCharUTF8"},
|
||||
{0xb8476cf4, WrapU_U<sceCccSetErrorCharUTF16>, "sceCccSetErrorCharUTF16"},
|
||||
{0xc56949ad, WrapU_U<sceCccSetErrorCharSJIS>, "sceCccSetErrorCharSJIS"},
|
||||
|
|
|
@ -77,7 +77,6 @@ static int holdMode;
|
|||
static int mode;
|
||||
static int width;
|
||||
static int height;
|
||||
static bool flipped;
|
||||
// Don't include this in the state, time increases regardless of state.
|
||||
static double curFrameTime;
|
||||
static double nextFrameTime;
|
||||
|
@ -412,30 +411,23 @@ void hleEnterVblank(u64 userdata, int cyclesLate) {
|
|||
framebuf = latchedFramebuf;
|
||||
framebufIsLatched = false;
|
||||
gpu->SetDisplayFramebuffer(framebuf.topaddr, framebuf.pspFramebufLinesize, framebuf.pspFramebufFormat);
|
||||
gpuStats.numFlips++;
|
||||
flipped = true;
|
||||
}
|
||||
}
|
||||
|
||||
gpuStats.numVBlanks++;
|
||||
|
||||
numVBlanksSinceFlip++;
|
||||
// GTA hack - it doesn't flip, it stretch blits a secondary buffer on top of a static frame buffer, ugh.
|
||||
if (numVBlanksSinceFlip == 2)
|
||||
flipped = true;
|
||||
|
||||
if (g_Config.iShowFPSCounter) {
|
||||
CalculateFPS();
|
||||
}
|
||||
|
||||
if (flipped) {
|
||||
flipped = false;
|
||||
// We flip only if the framebuffer was dirty. This eliminates flicker when using
|
||||
// non-buffered rendering. The interaction with frame skipping seems to need
|
||||
// some work.
|
||||
if (gpu->FramebufferDirty()) {
|
||||
gpuStats.numFlips++;
|
||||
|
||||
// Draw screen overlays before blitting. Saves and restores the Ge context.
|
||||
// Yeah, this has to be the right moment to end the frame. Give the graphics backend opportunity
|
||||
// to blit the framebuffer, in order to support half-framerate games that otherwise wouldn't have
|
||||
// anything to draw here.
|
||||
|
||||
bool wasSkipped = (gstate_c.skipDrawReason & SKIPDRAW_SKIPFRAME) != 0;
|
||||
gstate_c.skipDrawReason &= ~SKIPDRAW_SKIPFRAME;
|
||||
|
||||
|
@ -452,7 +444,7 @@ void hleEnterVblank(u64 userdata, int cyclesLate) {
|
|||
// Setting CORE_NEXTFRAME causes a swap.
|
||||
// Check first though, might've just quit / been paused.
|
||||
if (!wasSkipped) {
|
||||
if (coreState == CORE_RUNNING) { // && gpu->FramebufferDirty()) {
|
||||
if (coreState == CORE_RUNNING) {
|
||||
coreState = CORE_NEXTFRAME;
|
||||
gpu->CopyDisplayToOutput();
|
||||
}
|
||||
|
@ -524,8 +516,6 @@ u32 sceDisplaySetFramebuf(u32 topaddr, int linesize, int pixelformat, int sync)
|
|||
if (topaddr != framebuf.topaddr) {
|
||||
framebuf = fbstate;
|
||||
gpu->SetDisplayFramebuffer(framebuf.topaddr, framebuf.pspFramebufLinesize, framebuf.pspFramebufFormat);
|
||||
gpuStats.numFlips++;
|
||||
flipped = true;
|
||||
}
|
||||
} else {
|
||||
WARN_LOG(HLE, "%s: PSP_DISPLAY_SETBUF_IMMEDIATE without topaddr?", __FUNCTION__);
|
||||
|
|
|
@ -40,6 +40,11 @@ struct GeInterruptData
|
|||
static std::list<GeInterruptData> ge_pending_cb;
|
||||
static int geSyncEvent;
|
||||
static int geInterruptEvent;
|
||||
static int geCycleEvent;
|
||||
|
||||
// Let's try updating 10 times per vblank.
|
||||
const int geIntervalUs = 1000000 / (60 * 10);
|
||||
const int geBehindThresholdUs = 1000000 / (60 * 10);
|
||||
|
||||
class GeIntrHandler : public IntrHandler
|
||||
{
|
||||
|
@ -162,6 +167,20 @@ void __GeExecuteInterrupt(u64 userdata, int cyclesLate)
|
|||
__TriggerInterrupt(PSP_INTR_IMMEDIATE, PSP_GE_INTR, PSP_INTR_SUB_NONE);
|
||||
}
|
||||
|
||||
void __GeCheckCycles(u64 userdata, int cyclesLate)
|
||||
{
|
||||
u64 geTicks = gpu->GetTickEstimate();
|
||||
if (geTicks != 0)
|
||||
{
|
||||
if (CoreTiming::GetTicks() > geTicks + usToCycles(geBehindThresholdUs)) {
|
||||
u64 diff = CoreTiming::GetTicks() - geTicks;
|
||||
gpu->SyncThread();
|
||||
CoreTiming::Advance();
|
||||
}
|
||||
}
|
||||
CoreTiming::ScheduleEvent(usToCycles(geIntervalUs), geCycleEvent, 0);
|
||||
}
|
||||
|
||||
void __GeInit()
|
||||
{
|
||||
memset(&ge_used_callbacks, 0, sizeof(ge_used_callbacks));
|
||||
|
@ -170,6 +189,12 @@ void __GeInit()
|
|||
|
||||
geSyncEvent = CoreTiming::RegisterEvent("GeSyncEvent", &__GeExecuteSync);
|
||||
geInterruptEvent = CoreTiming::RegisterEvent("GeInterruptEvent", &__GeExecuteInterrupt);
|
||||
geCycleEvent = CoreTiming::RegisterEvent("GeCycleEvent", &__GeCheckCycles);
|
||||
|
||||
// When we're using separate CPU/GPU threads, we need to keep them in sync.
|
||||
if (IsOnSeparateCPUThread()) {
|
||||
CoreTiming::ScheduleEvent(usToCycles(geIntervalUs), geCycleEvent, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void __GeDoState(PointerWrap &p)
|
||||
|
@ -182,6 +207,8 @@ void __GeDoState(PointerWrap &p)
|
|||
CoreTiming::RestoreRegisterEvent(geSyncEvent, "GeSyncEvent", &__GeExecuteSync);
|
||||
p.Do(geInterruptEvent);
|
||||
CoreTiming::RestoreRegisterEvent(geInterruptEvent, "GeInterruptEvent", &__GeExecuteInterrupt);
|
||||
p.Do(geCycleEvent);
|
||||
CoreTiming::RestoreRegisterEvent(geCycleEvent, "GeCycleEvent", &__GeCheckCycles);
|
||||
|
||||
// Everything else is done in sceDisplay.
|
||||
p.DoMarker("sceGe");
|
||||
|
@ -215,14 +242,21 @@ bool __GeTriggerInterrupt(int listid, u32 pc, u64 atTicks)
|
|||
return true;
|
||||
}
|
||||
|
||||
void __GeWaitCurrentThread(WaitType type, SceUID waitId, const char *reason)
|
||||
{
|
||||
__KernelWaitCurThread(type, waitId, 0, 0, false, reason);
|
||||
}
|
||||
|
||||
void __GeTriggerWait(WaitType type, SceUID waitId, const char *reason, bool noSwitch)
|
||||
{
|
||||
__KernelTriggerWait(type, waitId, 0, reason, noSwitch);
|
||||
}
|
||||
|
||||
bool __GeHasPendingInterrupt()
|
||||
{
|
||||
return !ge_pending_cb.empty();
|
||||
}
|
||||
|
||||
// The GE is implemented wrong - it should be parallel to the CPU execution instead of
|
||||
// synchronous.
|
||||
|
||||
u32 sceGeEdramGetAddr()
|
||||
{
|
||||
u32 retVal = 0x04000000;
|
||||
|
@ -378,7 +412,8 @@ int sceGeUnsetCallback(u32 cbID)
|
|||
u32 sceGeSaveContext(u32 ctxAddr)
|
||||
{
|
||||
DEBUG_LOG(HLE, "sceGeSaveContext(%08x)", ctxAddr);
|
||||
gpu->Flush();
|
||||
gpu->SyncThread();
|
||||
|
||||
if (sizeof(gstate) > 512 * 4)
|
||||
{
|
||||
ERROR_LOG(HLE, "AARGH! sizeof(gstate) has grown too large!");
|
||||
|
@ -399,7 +434,7 @@ u32 sceGeSaveContext(u32 ctxAddr)
|
|||
u32 sceGeRestoreContext(u32 ctxAddr)
|
||||
{
|
||||
DEBUG_LOG(HLE, "sceGeRestoreContext(%08x)", ctxAddr);
|
||||
gpu->Flush();
|
||||
gpu->SyncThread();
|
||||
|
||||
if (sizeof(gstate) > 512 * 4)
|
||||
{
|
||||
|
|
|
@ -43,6 +43,8 @@ void __GeDoState(PointerWrap &p);
|
|||
void __GeShutdown();
|
||||
bool __GeTriggerSync(WaitType waitType, int id, u64 atTicks);
|
||||
bool __GeTriggerInterrupt(int listid, u32 pc, u64 atTicks);
|
||||
void __GeWaitCurrentThread(WaitType type, SceUID waitId, const char *reason);
|
||||
void __GeTriggerWait(WaitType type, SceUID waitId, const char *reason, bool noSwitch = false);
|
||||
bool __GeHasPendingInterrupt();
|
||||
|
||||
|
||||
|
|
|
@ -16,33 +16,36 @@
|
|||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include <cstdlib>
|
||||
#include "native/thread/thread.h"
|
||||
#include "native/thread/threadutil.h"
|
||||
#include "Core/Config.h"
|
||||
#include "Core/System.h"
|
||||
#include "Core/Host.h"
|
||||
#include "Core/SaveState.h"
|
||||
#include "HLE.h"
|
||||
#include "Core/HLE/HLE.h"
|
||||
#include "Core/MIPS/MIPS.h"
|
||||
#include "Core/HW/MemoryStick.h"
|
||||
#include "Core/HW/AsyncIOManager.h"
|
||||
#include "Core/CoreTiming.h"
|
||||
#include "Core/Reporting.h"
|
||||
|
||||
#include "../FileSystems/FileSystem.h"
|
||||
#include "../FileSystems/MetaFileSystem.h"
|
||||
#include "../FileSystems/ISOFileSystem.h"
|
||||
#include "../FileSystems/DirectoryFileSystem.h"
|
||||
#include "Core/FileSystems/FileSystem.h"
|
||||
#include "Core/FileSystems/MetaFileSystem.h"
|
||||
#include "Core/FileSystems/ISOFileSystem.h"
|
||||
#include "Core/FileSystems/DirectoryFileSystem.h"
|
||||
|
||||
extern "C" {
|
||||
#include "ext/libkirk/amctrl.h"
|
||||
};
|
||||
|
||||
#include "sceIo.h"
|
||||
#include "sceRtc.h"
|
||||
#include "sceKernel.h"
|
||||
#include "sceKernelMemory.h"
|
||||
#include "sceKernelThread.h"
|
||||
#include "Core/HLE/sceIo.h"
|
||||
#include "Core/HLE/sceRtc.h"
|
||||
#include "Core/HLE/sceKernel.h"
|
||||
#include "Core/HLE/sceKernelMemory.h"
|
||||
#include "Core/HLE/sceKernelThread.h"
|
||||
|
||||
// For headless screenshots.
|
||||
#include "sceDisplay.h"
|
||||
#include "Core/HLE/sceDisplay.h"
|
||||
|
||||
const int ERROR_ERRNO_FILE_NOT_FOUND = 0x80010002;
|
||||
const int ERROR_ERRNO_FILE_ALREADY_EXISTS = 0x80010011;
|
||||
|
@ -101,9 +104,14 @@ const int PSP_COUNT_FDS = 64;
|
|||
// TODO: Should be 3, and stdin/stdout/stderr are special values aliased to 0?
|
||||
const int PSP_MIN_FD = 4;
|
||||
static int asyncNotifyEvent = -1;
|
||||
static int syncNotifyEvent = -1;
|
||||
static SceUID fds[PSP_COUNT_FDS];
|
||||
static AsyncIOManager ioManager;
|
||||
static bool ioManagerThreadEnabled = false;
|
||||
static std::thread *ioManagerThread;
|
||||
|
||||
u32 ioErrorCode = 0;
|
||||
// TODO: Is it better to just put all on the thread?
|
||||
const int IO_THREAD_MIN_DATA_SIZE = 256;
|
||||
|
||||
#define SCE_STM_FDIR 0x1000
|
||||
#define SCE_STM_FREG 0x2000
|
||||
|
@ -294,40 +302,107 @@ void __IoAsyncNotify(u64 userdata, int cyclesLate) {
|
|||
}
|
||||
}
|
||||
|
||||
void __IoSyncNotify(u64 userdata, int cyclesLate) {
|
||||
SceUID threadID = userdata >> 32;
|
||||
int fd = (int) (userdata & 0xFFFFFFFF);
|
||||
|
||||
s64 result = -1;
|
||||
u32 error;
|
||||
FileNode *f = __IoGetFd(fd, error);
|
||||
if (f) {
|
||||
f->pendingAsyncResult = false;
|
||||
f->hasAsyncResult = true;
|
||||
|
||||
AsyncIOResult managerResult;
|
||||
if (ioManager.WaitResult(f->handle, managerResult)) {
|
||||
result = managerResult;
|
||||
} else {
|
||||
ERROR_LOG(HLE, "Unable to complete IO operation.");
|
||||
}
|
||||
}
|
||||
|
||||
SceUID waitID = __KernelGetWaitID(threadID, WAITTYPE_IO, error);
|
||||
if (waitID == fd && error == 0) {
|
||||
__KernelResumeThreadFromWait(threadID, result);
|
||||
}
|
||||
}
|
||||
|
||||
static DirectoryFileSystem *memstickSystem = NULL;
|
||||
#ifdef ANDROID
|
||||
static VFSFileSystem *flash0System = NULL;
|
||||
#else
|
||||
static DirectoryFileSystem *flash0System = NULL;
|
||||
#endif
|
||||
|
||||
void __IoManagerThread() {
|
||||
setCurrentThreadName("IOThread");
|
||||
while (ioManagerThreadEnabled) {
|
||||
ioManager.RunEventsUntil(CoreTiming::GetTicks() + msToCycles(1000));
|
||||
}
|
||||
}
|
||||
|
||||
void __IoInit() {
|
||||
INFO_LOG(HLE, "Starting up I/O...");
|
||||
|
||||
MemoryStick_SetFatState(PSP_FAT_MEMORYSTICK_STATE_ASSIGNED);
|
||||
|
||||
asyncNotifyEvent = CoreTiming::RegisterEvent("IoAsyncNotify", __IoAsyncNotify);
|
||||
syncNotifyEvent = CoreTiming::RegisterEvent("IoSyncNotify", __IoSyncNotify);
|
||||
|
||||
std::string memstickpath;
|
||||
std::string flash0path;
|
||||
GetSysDirectories(memstickpath, flash0path);
|
||||
|
||||
DirectoryFileSystem *memstick = new DirectoryFileSystem(&pspFileSystem, memstickpath);
|
||||
memstickSystem = new DirectoryFileSystem(&pspFileSystem, memstickpath);
|
||||
#ifdef ANDROID
|
||||
VFSFileSystem *flash0 = new VFSFileSystem(&pspFileSystem, "flash0");
|
||||
flash0System = new VFSFileSystem(&pspFileSystem, "flash0");
|
||||
#else
|
||||
DirectoryFileSystem *flash0 = new DirectoryFileSystem(&pspFileSystem, flash0path);
|
||||
flash0System = new DirectoryFileSystem(&pspFileSystem, flash0path);
|
||||
#endif
|
||||
pspFileSystem.Mount("ms0:", memstick);
|
||||
pspFileSystem.Mount("fatms0:", memstick);
|
||||
pspFileSystem.Mount("fatms:", memstick);
|
||||
pspFileSystem.Mount("flash0:", flash0);
|
||||
pspFileSystem.Mount("ms0:", memstickSystem);
|
||||
pspFileSystem.Mount("fatms0:", memstickSystem);
|
||||
pspFileSystem.Mount("fatms:", memstickSystem);
|
||||
pspFileSystem.Mount("flash0:", flash0System);
|
||||
|
||||
__KernelListenThreadEnd(&TellFsThreadEnded);
|
||||
|
||||
memset(fds, 0, sizeof(fds));
|
||||
|
||||
ioManagerThreadEnabled = g_Config.bSeparateIOThread;
|
||||
ioManager.SetThreadEnabled(ioManagerThreadEnabled);
|
||||
if (ioManagerThreadEnabled) {
|
||||
ioManagerThread = new std::thread(&__IoManagerThread);
|
||||
}
|
||||
}
|
||||
|
||||
void __IoDoState(PointerWrap &p) {
|
||||
ioManager.DoState(p);
|
||||
p.DoArray(fds, ARRAY_SIZE(fds));
|
||||
p.Do(asyncNotifyEvent);
|
||||
CoreTiming::RestoreRegisterEvent(asyncNotifyEvent, "IoAsyncNotify", __IoAsyncNotify);
|
||||
p.Do(syncNotifyEvent);
|
||||
CoreTiming::RestoreRegisterEvent(syncNotifyEvent, "IoSyncNotify", __IoSyncNotify);
|
||||
p.DoMarker("sceIo");
|
||||
}
|
||||
|
||||
void __IoShutdown() {
|
||||
ioManagerThreadEnabled = false;
|
||||
ioManager.SyncThread();
|
||||
ioManager.FinishEventLoop();
|
||||
if (ioManagerThread != NULL) {
|
||||
delete ioManagerThread;
|
||||
ioManagerThread = NULL;
|
||||
}
|
||||
|
||||
pspFileSystem.Unmount("ms0:", memstickSystem);
|
||||
pspFileSystem.Unmount("fatms0:", memstickSystem);
|
||||
pspFileSystem.Unmount("fatms:", memstickSystem);
|
||||
pspFileSystem.Unmount("flash0:", flash0System);
|
||||
|
||||
delete memstickSystem;
|
||||
memstickSystem = NULL;
|
||||
delete flash0System;
|
||||
flash0System = NULL;
|
||||
}
|
||||
|
||||
u32 __IoGetFileHandleFromId(u32 id, u32 &outError)
|
||||
|
@ -386,6 +461,12 @@ void __IoCompleteAsyncIO(int fd) {
|
|||
u32 error;
|
||||
FileNode *f = __IoGetFd(fd, error);
|
||||
if (f) {
|
||||
AsyncIOResult managerResult;
|
||||
if (ioManager.WaitResult(f->handle, managerResult)) {
|
||||
f->asyncResult = managerResult;
|
||||
} else {
|
||||
// It's okay, not all operations are deferred.
|
||||
}
|
||||
if (f->callbackID) {
|
||||
__KernelNotifyCallback(THREAD_CALLBACK_IO, f->callbackID, f->callbackArg);
|
||||
}
|
||||
|
@ -432,6 +513,14 @@ void __IoSchedAsync(FileNode *f, int fd, int usec) {
|
|||
f->hasAsyncResult = false;
|
||||
}
|
||||
|
||||
void __IoSchedSync(FileNode *f, int fd, int usec) {
|
||||
u64 param = ((u64)__KernelGetCurThread()) << 32 | fd;
|
||||
CoreTiming::ScheduleEvent(usToCycles(usec), syncNotifyEvent, param);
|
||||
|
||||
f->pendingAsyncResult = true;
|
||||
f->hasAsyncResult = false;
|
||||
}
|
||||
|
||||
u32 sceIoGetstat(const char *filename, u32 addr) {
|
||||
// TODO: Improve timing (although this seems normally slow..)
|
||||
int usec = 1000;
|
||||
|
@ -511,7 +600,7 @@ u32 npdrmRead(FileNode *f, u8 *data, int size) {
|
|||
return size;
|
||||
}
|
||||
|
||||
int __IoRead(int id, u32 data_addr, int size) {
|
||||
bool __IoRead(int &result, int id, u32 data_addr, int size) {
|
||||
if (id == 3) {
|
||||
DEBUG_LOG(HLE, "sceIoRead STDIN");
|
||||
return 0; //stdin
|
||||
|
@ -520,58 +609,88 @@ int __IoRead(int id, u32 data_addr, int size) {
|
|||
u32 error;
|
||||
FileNode *f = __IoGetFd(id, error);
|
||||
if (f) {
|
||||
if(!(f->openMode & FILEACCESS_READ))
|
||||
{
|
||||
return ERROR_KERNEL_BAD_FILE_DESCRIPTOR;
|
||||
}
|
||||
else if (Memory::IsValidAddress(data_addr)) {
|
||||
if (!(f->openMode & FILEACCESS_READ)) {
|
||||
result = ERROR_KERNEL_BAD_FILE_DESCRIPTOR;
|
||||
return true;
|
||||
} else if (Memory::IsValidAddress(data_addr)) {
|
||||
u8 *data = (u8*) Memory::GetPointer(data_addr);
|
||||
if(f->npdrm){
|
||||
return npdrmRead(f, data, size);
|
||||
}else{
|
||||
return (int) pspFileSystem.ReadFile(f->handle, data, size);
|
||||
if (f->npdrm) {
|
||||
result = npdrmRead(f, data, size);
|
||||
return true;
|
||||
} else if (__KernelIsDispatchEnabled() && ioManagerThreadEnabled && size > IO_THREAD_MIN_DATA_SIZE) {
|
||||
AsyncIOEvent ev = IO_EVENT_READ;
|
||||
ev.handle = f->handle;
|
||||
ev.buf = data;
|
||||
ev.bytes = size;
|
||||
ioManager.ScheduleOperation(ev);
|
||||
return false;
|
||||
} else {
|
||||
result = (int) pspFileSystem.ReadFile(f->handle, data, size);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
ERROR_LOG(HLE, "sceIoRead Reading into bad pointer %08x", data_addr);
|
||||
// TODO: Returning 0 because it wasn't being sign-extended in async result before.
|
||||
// What should this do?
|
||||
return 0;
|
||||
result = 0;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
ERROR_LOG(HLE, "sceIoRead ERROR: no file open");
|
||||
return error;
|
||||
result = error;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
u32 sceIoRead(int id, u32 data_addr, int size) {
|
||||
// TODO: Check id is valid first?
|
||||
if (!__KernelIsDispatchEnabled() && id > 2)
|
||||
if (!__KernelIsDispatchEnabled() && id > 2) {
|
||||
return -1;
|
||||
|
||||
int result = __IoRead(id, data_addr, size);
|
||||
if (result >= 0) {
|
||||
DEBUG_LOG(HLE, "%x=sceIoRead(%d, %08x, %x)", result, id, data_addr, size);
|
||||
// TODO: Timing is probably not very accurate, low estimate.
|
||||
int us = result/100;
|
||||
if(us==0)
|
||||
us = 100;
|
||||
return hleDelayResult(result, "io read", us);
|
||||
}
|
||||
else
|
||||
|
||||
// TODO: Timing is probably not very accurate, low estimate.
|
||||
int us = size / 100;
|
||||
if (us < 100) {
|
||||
us = 100;
|
||||
}
|
||||
|
||||
int result;
|
||||
bool complete = __IoRead(result, id, data_addr, size);
|
||||
if (!complete) {
|
||||
DEBUG_LOG(HLE, "sceIoRead(%d, %08x, %x): deferring result", id, data_addr, size);
|
||||
|
||||
u32 error;
|
||||
FileNode *f = __IoGetFd(id, error);
|
||||
__IoSchedSync(f, id, us);
|
||||
__KernelWaitCurThread(WAITTYPE_IO, id, 0, 0, false, "io read");
|
||||
return 0;
|
||||
} else if (result >= 0) {
|
||||
DEBUG_LOG(HLE, "%x=sceIoRead(%d, %08x, %x)", result, id, data_addr, size);
|
||||
return hleDelayResult(result, "io read", us);
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
u32 sceIoReadAsync(int id, u32 data_addr, int size) {
|
||||
// TODO: Not sure what the correct delay is (and technically we shouldn't read into the buffer yet...)
|
||||
int us = size / 100;
|
||||
if (us < 100) {
|
||||
us = 100;
|
||||
}
|
||||
|
||||
u32 error;
|
||||
FileNode *f = __IoGetFd(id, error);
|
||||
if (f) {
|
||||
f->asyncResult = __IoRead(id, data_addr, size);
|
||||
// TODO: Not sure what the correct delay is (and technically we shouldn't read into the buffer yet...)
|
||||
int us = f->asyncResult/100;
|
||||
if(us==0)
|
||||
us = 100;
|
||||
int result;
|
||||
bool complete = __IoRead(result, id, data_addr, size);
|
||||
if (complete) {
|
||||
f->asyncResult = result;
|
||||
DEBUG_LOG(HLE, "%llx=sceIoReadAsync(%d, %08x, %x)", f->asyncResult, id, data_addr, size);
|
||||
} else {
|
||||
DEBUG_LOG(HLE, "sceIoReadAsync(%d, %08x, %x): deferring result", id, data_addr, size);
|
||||
}
|
||||
__IoSchedAsync(f, id, us);
|
||||
DEBUG_LOG(HLE, "%llx=sceIoReadAsync(%d, %08x, %x)", f->asyncResult, id, data_addr, size);
|
||||
return 0;
|
||||
} else {
|
||||
ERROR_LOG(HLE, "sceIoReadAsync: bad file %d", id);
|
||||
|
@ -579,24 +698,37 @@ u32 sceIoReadAsync(int id, u32 data_addr, int size) {
|
|||
}
|
||||
}
|
||||
|
||||
int __IoWrite(int id, void *data_ptr, int size) {
|
||||
bool __IoWrite(int &result, int id, void *data_ptr, int size) {
|
||||
// Let's handle stdout/stderr specially.
|
||||
if (id == 1 || id == 2) {
|
||||
const char *str = (const char *) data_ptr;
|
||||
const int str_size = size == 0 ? 0 : (str[size - 1] == '\n' ? size - 1 : size);
|
||||
INFO_LOG(HLE, "%s: %.*s", id == 1 ? "stdout" : "stderr", str_size, str);
|
||||
return size;
|
||||
result = size;
|
||||
return true;
|
||||
}
|
||||
u32 error;
|
||||
FileNode *f = __IoGetFd(id, error);
|
||||
if (f) {
|
||||
if(!(f->openMode & FILEACCESS_WRITE)) {
|
||||
return ERROR_KERNEL_BAD_FILE_DESCRIPTOR;
|
||||
if (!(f->openMode & FILEACCESS_WRITE)) {
|
||||
result = ERROR_KERNEL_BAD_FILE_DESCRIPTOR;
|
||||
return true;
|
||||
}
|
||||
return (int) pspFileSystem.WriteFile(f->handle, (u8*) data_ptr, size);
|
||||
if (__KernelIsDispatchEnabled() && ioManagerThreadEnabled && size > IO_THREAD_MIN_DATA_SIZE) {
|
||||
AsyncIOEvent ev = IO_EVENT_WRITE;
|
||||
ev.handle = f->handle;
|
||||
ev.buf = (u8 *) data_ptr;
|
||||
ev.bytes = size;
|
||||
ioManager.ScheduleOperation(ev);
|
||||
return false;
|
||||
} else {
|
||||
result = (int) pspFileSystem.WriteFile(f->handle, (u8 *) data_ptr, size);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
ERROR_LOG(HLE, "sceIoWrite ERROR: no file open");
|
||||
return (s32) error;
|
||||
result = (s32) error;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -605,33 +737,53 @@ u32 sceIoWrite(int id, u32 data_addr, int size) {
|
|||
if (!__KernelIsDispatchEnabled() && id > 2)
|
||||
return -1;
|
||||
|
||||
int result = __IoWrite(id, Memory::GetPointer(data_addr), size);
|
||||
if (result >= 0) {
|
||||
DEBUG_LOG(HLE, "%x=sceIoWrite(%d, %08x, %x)", result, id, data_addr, size);
|
||||
// TODO: Timing is probably not very accurate, low estimate.
|
||||
int us = result/100;
|
||||
if(us==0)
|
||||
us = 100;
|
||||
if (__KernelIsDispatchEnabled())
|
||||
return hleDelayResult(result, "io write", us);
|
||||
else
|
||||
return result;
|
||||
// TODO: Timing is probably not very accurate, low estimate.
|
||||
int us = size / 100;
|
||||
if (us < 100) {
|
||||
us = 100;
|
||||
}
|
||||
else
|
||||
|
||||
int result;
|
||||
bool complete = __IoWrite(result, id, Memory::GetPointer(data_addr), size);
|
||||
if (!complete) {
|
||||
DEBUG_LOG(HLE, "sceIoWrite(%d, %08x, %x): deferring result", id, data_addr, size);
|
||||
|
||||
u32 error;
|
||||
FileNode *f = __IoGetFd(id, error);
|
||||
__IoSchedSync(f, id, us);
|
||||
__KernelWaitCurThread(WAITTYPE_IO, id, 0, 0, false, "io write");
|
||||
return 0;
|
||||
} else if (result >= 0) {
|
||||
DEBUG_LOG(HLE, "%x=sceIoWrite(%d, %08x, %x)", result, id, data_addr, size);
|
||||
if (__KernelIsDispatchEnabled()) {
|
||||
return hleDelayResult(result, "io write", us);
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
u32 sceIoWriteAsync(int id, u32 data_addr, int size) {
|
||||
// TODO: Not sure what the correct delay is (and technically we shouldn't read from the buffer yet...)
|
||||
int us = size / 100;
|
||||
if (us < 100) {
|
||||
us = 100;
|
||||
}
|
||||
|
||||
u32 error;
|
||||
FileNode *f = __IoGetFd(id, error);
|
||||
if (f) {
|
||||
f->asyncResult = __IoWrite(id, Memory::GetPointer(data_addr), size);
|
||||
// TODO: Not sure what the correct delay is (and technically we shouldn't read into the buffer yet...)
|
||||
int us = f->asyncResult/100;
|
||||
if(us==0)
|
||||
us = 100;
|
||||
int result;
|
||||
bool complete = __IoWrite(result, id, Memory::GetPointer(data_addr), size);
|
||||
if (complete) {
|
||||
f->asyncResult = result;
|
||||
DEBUG_LOG(HLE, "%llx=sceIoWriteAsync(%d, %08x, %x)", f->asyncResult, id, data_addr, size);
|
||||
} else {
|
||||
DEBUG_LOG(HLE, "sceIoWriteAsync(%d, %08x, %x): deferring result", id, data_addr, size);
|
||||
}
|
||||
__IoSchedAsync(f, id, us);
|
||||
DEBUG_LOG(HLE, "%llx=sceIoWriteAsync(%d, %08x, %x)", f->asyncResult, id, data_addr, size);
|
||||
return 0;
|
||||
} else {
|
||||
ERROR_LOG(HLE, "sceIoWriteAsync: bad file %d", id);
|
||||
|
@ -782,7 +934,7 @@ u32 sceIoLseek32Async(int id, int offset, int whence) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
FileNode *__IoOpen(const char* filename, int flags, int mode) {
|
||||
FileNode *__IoOpen(int &error, const char* filename, int flags, int mode) {
|
||||
//memory stick filename
|
||||
int access = FILEACCESS_NONE;
|
||||
if (flags & O_RDONLY)
|
||||
|
@ -796,9 +948,7 @@ FileNode *__IoOpen(const char* filename, int flags, int mode) {
|
|||
|
||||
PSPFileInfo info = pspFileSystem.GetFileInfo(filename);
|
||||
|
||||
ioErrorCode = 0;
|
||||
|
||||
u32 h = pspFileSystem.OpenFile(filename, (FileAccess) access);
|
||||
u32 h = pspFileSystem.OpenWithError(error, filename, (FileAccess) access);
|
||||
if (h == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
@ -817,15 +967,16 @@ FileNode *__IoOpen(const char* filename, int flags, int mode) {
|
|||
return f;
|
||||
}
|
||||
|
||||
u32 sceIoOpen(const char* filename, int flags, int mode) {
|
||||
u32 sceIoOpen(const char *filename, int flags, int mode) {
|
||||
if (!__KernelIsDispatchEnabled())
|
||||
return -1;
|
||||
|
||||
FileNode *f = __IoOpen(filename, flags, mode);
|
||||
int error;
|
||||
FileNode *f = __IoOpen(error, filename, flags, mode);
|
||||
if (f == NULL)
|
||||
{
|
||||
//Timing is not accurate, aiming low for now.
|
||||
if (ioErrorCode == SCE_KERNEL_ERROR_NOCWD)
|
||||
// Timing is not accurate, aiming low for now.
|
||||
if (error == (int)SCE_KERNEL_ERROR_NOCWD)
|
||||
{
|
||||
ERROR_LOG(HLE, "SCE_KERNEL_ERROR_NOCWD=sceIoOpen(%s, %08x, %08x) - no current working directory", filename, flags, mode);
|
||||
return hleDelayResult(SCE_KERNEL_ERROR_NOCWD , "no cwd", 10000);
|
||||
|
@ -1294,7 +1445,8 @@ u32 sceIoOpenAsync(const char *filename, int flags, int mode)
|
|||
if (!__KernelIsDispatchEnabled())
|
||||
sceKernelResumeDispatchThread(1);
|
||||
|
||||
FileNode *f = __IoOpen(filename, flags, mode);
|
||||
int error;
|
||||
FileNode *f = __IoOpen(error, filename, flags, mode);
|
||||
int fd;
|
||||
|
||||
// We have to return an fd here, which may have been destroyed when we reach Wait if it failed.
|
||||
|
@ -1305,7 +1457,7 @@ u32 sceIoOpenAsync(const char *filename, int flags, int mode)
|
|||
f = new FileNode();
|
||||
f->handle = kernelObjects.Create(f);
|
||||
f->fullpath = filename;
|
||||
f->asyncResult = ERROR_ERRNO_FILE_NOT_FOUND;
|
||||
f->asyncResult = error == 0 ? ERROR_ERRNO_FILE_NOT_FOUND : error;
|
||||
f->closePending = true;
|
||||
|
||||
fd = __IoAllocFd(f);
|
||||
|
|
|
@ -19,9 +19,9 @@
|
|||
|
||||
#include <string>
|
||||
|
||||
#include "../System.h"
|
||||
#include "HLE.h"
|
||||
#include "sceKernel.h"
|
||||
#include "Core/System.h"
|
||||
#include "Core/HLE/HLE.h"
|
||||
#include "Core/HLE/sceKernel.h"
|
||||
|
||||
void __IoInit();
|
||||
void __IoDoState(PointerWrap &p);
|
||||
|
|
|
@ -664,7 +664,6 @@ u32 sceKernelReferEventFlagStatus(SceUID id, u32 statusPtr)
|
|||
if (!Memory::IsValidAddress(statusPtr))
|
||||
return -1;
|
||||
|
||||
u32 error;
|
||||
for (auto iter = e->waitingThreads.begin(); iter != e->waitingThreads.end(); ++iter)
|
||||
{
|
||||
SceUID waitID = __KernelGetWaitID(iter->tid, WAITTYPE_EVENTFLAG, error);
|
||||
|
|
|
@ -1228,7 +1228,6 @@ int sceKernelReferVplStatus(SceUID uid, u32 infoPtr)
|
|||
{
|
||||
DEBUG_LOG(HLE, "sceKernelReferVplStatus(%i, %08x)", uid, infoPtr);
|
||||
|
||||
u32 error;
|
||||
for (auto iter = vpl->waitingThreads.begin(); iter != vpl->waitingThreads.end(); ++iter)
|
||||
{
|
||||
SceUID waitID = __KernelGetWaitID(iter->threadID, WAITTYPE_VPL, error);
|
||||
|
|
|
@ -704,7 +704,9 @@ Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *erro
|
|||
u8 vcount;
|
||||
u16_le fcount;
|
||||
u32_le resident;
|
||||
u32_le extra;
|
||||
u16_le vcountNew;
|
||||
u8 unknown1;
|
||||
u8 unknown2;
|
||||
};
|
||||
|
||||
u32_le *entPos = (u32_le *)Memory::GetPointer(modinfo->libent);
|
||||
|
@ -718,6 +720,7 @@ Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *erro
|
|||
continue;
|
||||
}
|
||||
|
||||
u32_le variableCount = ent->size <= 4 ? ent->vcount : std::max((u32)ent->vcount , (u32)ent->vcountNew);
|
||||
const char *name;
|
||||
if (Memory::IsValidAddress(ent->name)) {
|
||||
name = Memory::GetCharPointer(ent->name);
|
||||
|
@ -737,10 +740,10 @@ Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *erro
|
|||
}
|
||||
|
||||
u32_le *residentPtr = (u32_le *)Memory::GetPointer(ent->resident);
|
||||
u32_le *exportPtr = residentPtr + ent->fcount + ent->vcount;
|
||||
u32_le *exportPtr = residentPtr + ent->fcount + variableCount;
|
||||
|
||||
if (ent->size != 4) {
|
||||
WARN_LOG_REPORT(LOADER, "Unexpected export module entry size %d, extra=%08x", ent->size, ent->extra);
|
||||
WARN_LOG_REPORT(LOADER, "Unexpected export module entry size %d, vcountNew=%08x, unknown1=%08x, unknown2=&08x", ent->size, ent->vcountNew, ent->unknown1, ent->unknown2);
|
||||
}
|
||||
|
||||
for (u32 j = 0; j < ent->fcount; j++) {
|
||||
|
@ -768,7 +771,7 @@ Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *erro
|
|||
}
|
||||
}
|
||||
|
||||
for (u32 j = 0; j < ent->vcount; j++) {
|
||||
for (u32 j = 0; j < variableCount; j++) {
|
||||
u32 nid = residentPtr[ent->fcount + j];
|
||||
u32 exportAddr = exportPtr[ent->fcount + j];
|
||||
|
||||
|
|
|
@ -694,7 +694,6 @@ int sceKernelReferMutexStatus(SceUID id, u32 infoAddr)
|
|||
// Don't write if the size is 0. Anything else is A-OK, though, apparently.
|
||||
if (Memory::Read_U32(infoAddr) != 0)
|
||||
{
|
||||
u32 error;
|
||||
for (auto iter = m->waitingThreads.begin(); iter != m->waitingThreads.end(); ++iter)
|
||||
{
|
||||
SceUID waitID = __KernelGetWaitID(*iter, WAITTYPE_MUTEX, error);
|
||||
|
@ -1153,7 +1152,6 @@ int __KernelReferLwMutexStatus(SceUID uid, u32 infoPtr)
|
|||
{
|
||||
auto workarea = m->nm.workarea;
|
||||
|
||||
u32 error;
|
||||
for (auto iter = m->waitingThreads.begin(); iter != m->waitingThreads.end(); ++iter)
|
||||
{
|
||||
SceUID waitID = __KernelGetWaitID(*iter, WAITTYPE_LWMUTEX, error);
|
||||
|
|
|
@ -333,7 +333,6 @@ int sceKernelReferSemaStatus(SceUID id, u32 infoPtr)
|
|||
if (!Memory::IsValidAddress(infoPtr))
|
||||
return -1;
|
||||
|
||||
u32 error;
|
||||
for (auto iter = s->waitingThreads.begin(); iter != s->waitingThreads.end(); ++iter)
|
||||
{
|
||||
SceUID waitID = __KernelGetWaitID(*iter, WAITTYPE_SEMA, error);
|
||||
|
|
|
@ -2690,7 +2690,7 @@ int sceKernelResumeThread(SceUID threadID)
|
|||
DEBUG_LOG(HLE, "sceKernelResumeThread(%d)", threadID);
|
||||
t->nt.status &= ~THREADSTATUS_SUSPEND;
|
||||
|
||||
// If it was dormant, waiting, etc. before we don't flip it's ready state.
|
||||
// If it was dormant, waiting, etc. before we don't flip its ready state.
|
||||
if (t->nt.status == 0)
|
||||
__KernelChangeReadyState(t, threadID, true);
|
||||
return 0;
|
||||
|
@ -3539,6 +3539,8 @@ std::vector<DebugThreadInfo> GetThreadsInfo()
|
|||
info.name[KERNELOBJECT_MAX_NAME_LENGTH] = 0;
|
||||
info.status = t->nt.status;
|
||||
info.entrypoint = t->nt.entrypoint;
|
||||
info.initialStack = t->nt.initialStack;
|
||||
info.stackSize = (u32)t->nt.stackSize;
|
||||
info.priority = t->nt.currentPriority;
|
||||
info.waitType = t->nt.waitType;
|
||||
if(*iter == currentThread)
|
||||
|
|
|
@ -313,8 +313,10 @@ struct DebugThreadInfo
|
|||
SceUID id;
|
||||
char name[KERNELOBJECT_MAX_NAME_LENGTH+1];
|
||||
u32 status;
|
||||
int curPC;
|
||||
int entrypoint;
|
||||
u32 curPC;
|
||||
u32 entrypoint;
|
||||
u32 initialStack;
|
||||
int stackSize;
|
||||
int priority;
|
||||
WaitType waitType;
|
||||
bool isCurrent;
|
||||
|
|
|
@ -355,7 +355,7 @@ int sceMp3Init(u32 mp3) {
|
|||
/* select the audio stream */
|
||||
ret = av_find_best_stream(ctx->avformat_context, AVMEDIA_TYPE_AUDIO, -1, -1, &dec, 0);
|
||||
if (ret < 0) {
|
||||
ERROR_LOG(HLE, "av_find_best_stream: Cannot find a audio stream in the input file %d", ret);
|
||||
ERROR_LOG(HLE, "av_find_best_stream: Cannot find an audio stream in the input file %d", ret);
|
||||
return -1;
|
||||
}
|
||||
ctx->audio_stream_index = ret;
|
||||
|
|
|
@ -914,7 +914,7 @@ void PostPutAction::run(MipsCall &call) {
|
|||
WARN_LOG(HLE, "sceMpegRingbufferPut clamping packetsAdded old=%i new=%i", packetsAdded, ringbuffer.packetsFree);
|
||||
packetsAdded = ringbuffer.packetsFree;
|
||||
}
|
||||
int actuallyAdded = ctx->mediaengine->addStreamData(Memory::GetPointer(ringbuffer.data), packetsAdded * 2048) / 2048;
|
||||
int actuallyAdded = ctx->mediaengine == NULL ? 8 : ctx->mediaengine->addStreamData(Memory::GetPointer(ringbuffer.data), packetsAdded * 2048) / 2048;
|
||||
if (actuallyAdded != packetsAdded) {
|
||||
WARN_LOG_REPORT(HLE, "sceMpegRingbufferPut(): unable to enqueue all added packets, going to overwrite some frames.");
|
||||
}
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
#include <map>
|
||||
|
||||
// "Go Sudoku" is a good way to test this code...
|
||||
const int size = 2048;
|
||||
const int PSMF_VIDEO_STREAM_ID = 0xE0;
|
||||
const int PSMF_AUDIO_STREAM_ID = 0xBD;
|
||||
const int PSMF_AVC_STREAM = 0;
|
||||
|
|
97
Core/HW/AsyncIOManager.cpp
Normal file
97
Core/HW/AsyncIOManager.cpp
Normal file
|
@ -0,0 +1,97 @@
|
|||
// Copyright (c) 2012- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include "Core/Reporting.h"
|
||||
#include "Core/System.h"
|
||||
#include "Core/HW/AsyncIOManager.h"
|
||||
#include "Core/FileSystems/MetaFileSystem.h"
|
||||
|
||||
void AsyncIOManager::ScheduleOperation(AsyncIOEvent ev) {
|
||||
lock_guard guard(resultsLock_);
|
||||
resultsPending_.insert(ev.handle);
|
||||
ScheduleEvent(ev);
|
||||
}
|
||||
|
||||
bool AsyncIOManager::PopResult(u32 handle, AsyncIOResult &result) {
|
||||
lock_guard guard(resultsLock_);
|
||||
if (results_.find(handle) != results_.end()) {
|
||||
result = results_[handle];
|
||||
results_.erase(handle);
|
||||
resultsPending_.erase(handle);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool AsyncIOManager::WaitResult(u32 handle, AsyncIOResult &result) {
|
||||
lock_guard guard(resultsLock_);
|
||||
ScheduleEvent(IO_EVENT_SYNC);
|
||||
while (HasEvents() && resultsPending_.find(handle) != resultsPending_.end()) {
|
||||
if (PopResult(handle, result)) {
|
||||
return true;
|
||||
}
|
||||
resultsWait_.wait_for(resultsLock_, 16);
|
||||
}
|
||||
if (PopResult(handle, result)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void AsyncIOManager::ProcessEvent(AsyncIOEvent ev) {
|
||||
switch (ev.type) {
|
||||
case IO_EVENT_READ:
|
||||
Read(ev.handle, ev.buf, ev.bytes);
|
||||
break;
|
||||
|
||||
case IO_EVENT_WRITE:
|
||||
Write(ev.handle, ev.buf, ev.bytes);
|
||||
break;
|
||||
|
||||
default:
|
||||
ERROR_LOG(HLE, "Unsupported IO event type");
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncIOManager::Read(u32 handle, u8 *buf, size_t bytes) {
|
||||
size_t result = pspFileSystem.ReadFile(handle, buf, bytes);
|
||||
EventResult(handle, result);
|
||||
}
|
||||
|
||||
void AsyncIOManager::Write(u32 handle, u8 *buf, size_t bytes) {
|
||||
size_t result = pspFileSystem.WriteFile(handle, buf, bytes);
|
||||
EventResult(handle, result);
|
||||
}
|
||||
|
||||
void AsyncIOManager::EventResult(u32 handle, AsyncIOResult result) {
|
||||
lock_guard guard(resultsLock_);
|
||||
if (results_.find(handle) != results_.end()) {
|
||||
ERROR_LOG_REPORT(HLE, "Overwriting previous result for file action on handle %d", handle);
|
||||
}
|
||||
results_[handle] = result;
|
||||
resultsWait_.notify_one();
|
||||
}
|
||||
|
||||
void AsyncIOManager::DoState(PointerWrap &p) {
|
||||
SyncThread();
|
||||
lock_guard guard(resultsLock_);
|
||||
p.Do(resultsPending_);
|
||||
p.Do(results_);
|
||||
p.DoMarker("AsyncIOManager");
|
||||
}
|
75
Core/HW/AsyncIOManager.h
Normal file
75
Core/HW/AsyncIOManager.h
Normal file
|
@ -0,0 +1,75 @@
|
|||
// Copyright (c) 2012- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include "native/base/mutex.h"
|
||||
#include "Core/ThreadEventQueue.h"
|
||||
|
||||
class NoBase {
|
||||
};
|
||||
|
||||
enum AsyncIOEventType {
|
||||
IO_EVENT_INVALID,
|
||||
IO_EVENT_SYNC,
|
||||
IO_EVENT_FINISH,
|
||||
IO_EVENT_READ,
|
||||
IO_EVENT_WRITE,
|
||||
};
|
||||
|
||||
struct AsyncIOEvent {
|
||||
AsyncIOEvent(AsyncIOEventType t) : type(t) {}
|
||||
AsyncIOEventType type;
|
||||
u32 handle;
|
||||
u8 *buf;
|
||||
size_t bytes;
|
||||
|
||||
operator AsyncIOEventType() const {
|
||||
return type;
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: Something better.
|
||||
typedef size_t AsyncIOResult;
|
||||
|
||||
typedef ThreadEventQueue<NoBase, AsyncIOEvent, AsyncIOEventType, IO_EVENT_INVALID, IO_EVENT_SYNC, IO_EVENT_FINISH> IOThreadEventQueue;
|
||||
class AsyncIOManager : public IOThreadEventQueue {
|
||||
public:
|
||||
void DoState(PointerWrap &p);
|
||||
|
||||
void ScheduleOperation(AsyncIOEvent ev);
|
||||
|
||||
bool PopResult(u32 handle, AsyncIOResult &result);
|
||||
bool WaitResult(u32 handle, AsyncIOResult &result);
|
||||
|
||||
protected:
|
||||
virtual void ProcessEvent(AsyncIOEvent ref);
|
||||
virtual bool ShouldExitEventLoop() {
|
||||
return coreState == CORE_ERROR || coreState == CORE_POWERDOWN;
|
||||
}
|
||||
|
||||
private:
|
||||
void Read(u32 handle, u8 *buf, size_t bytes);
|
||||
void Write(u32 handle, u8 *buf, size_t bytes);
|
||||
|
||||
void EventResult(u32 handle, AsyncIOResult result);
|
||||
|
||||
recursive_mutex resultsLock_;
|
||||
condition_variable resultsWait_;
|
||||
std::set<u32> resultsPending_;
|
||||
std::map<u32, AsyncIOResult> results_;
|
||||
};
|
|
@ -46,7 +46,7 @@ bool isHeader(u8* audioStream, int offset)
|
|||
return (audioStream[offset] == header1) && (audioStream[offset+1] == header2);
|
||||
}
|
||||
|
||||
// header set to the headerbuf, and return it's size
|
||||
// header set to the headerbuf, and return its size
|
||||
int getOmaHeader(u8 codecId, u8 headerCode0, u8 headerCode1, u8 headerCode2, u8* headerbuf)
|
||||
{
|
||||
int pos = 0;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// It can simply convert a at3+ file or stream to oma format
|
||||
// It can simply convert an at3+ file or stream to oma format
|
||||
// Thanks to JPCSP project
|
||||
|
||||
#pragma once
|
||||
|
@ -7,9 +7,9 @@
|
|||
|
||||
namespace OMAConvert {
|
||||
|
||||
// output OMA to outputStream, and return it's size. You need to release it by use releaseStream()
|
||||
// output OMA to outputStream, and return its size. You need to release it by use releaseStream()
|
||||
int convertStreamtoOMA(u8* audioStream, int audioSize, u8** outputStream);
|
||||
// output OMA to outputStream, and return it's size. You need to release it by use releaseStream()
|
||||
// output OMA to outputStream, and return its size. You need to release it by use releaseStream()
|
||||
int convertRIFFtoOMA(u8* riff, int riffSize, u8** outputStream, int *readSize = 0);
|
||||
|
||||
void releaseStream(u8** stream);
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
#include "System.h"
|
||||
|
||||
// TODO : improve, look in the file more
|
||||
EmuFileType Identify_File(std::string &filename)
|
||||
IdentifiedFileType Identify_File(std::string &filename)
|
||||
{
|
||||
if (filename.size() == 0) {
|
||||
ERROR_LOG(LOADER, "invalid filename %s", filename.c_str());
|
||||
|
@ -73,7 +73,6 @@ EmuFileType Identify_File(std::string &filename)
|
|||
return FILETYPE_ERROR;
|
||||
}
|
||||
|
||||
|
||||
u32_le id;
|
||||
|
||||
size_t readSize = fread(&id, 4, 1, f);
|
||||
|
@ -83,31 +82,40 @@ EmuFileType Identify_File(std::string &filename)
|
|||
}
|
||||
|
||||
u32 psar_offset = 0, psar_id = 0;
|
||||
if (id == 'PBP\x00') {
|
||||
switch (id) {
|
||||
case 'PBP\x00':
|
||||
fseek(f, 0x24, SEEK_SET);
|
||||
fread(&psar_offset, 4, 1, f);
|
||||
fseek(f, psar_offset, SEEK_SET);
|
||||
fread(&psar_id, 4, 1, f);
|
||||
break;
|
||||
case '!raR':
|
||||
return FILETYPE_ARCHIVE_RAR;
|
||||
case '\x04\x03KP':
|
||||
case '\x06\x05KP':
|
||||
case '\x08\x07KP':
|
||||
return FILETYPE_ARCHIVE_ZIP;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
if (id == 'FLE\x7F')
|
||||
{
|
||||
if (id == 'FLE\x7F') {
|
||||
// There are a few elfs misnamed as pbp (like Trig Wars), accept that.
|
||||
if (!strcasecmp(extension.c_str(), ".plf") || strstr(filename.c_str(),"BOOT.BIN") ||
|
||||
!strcasecmp(extension.c_str(), ".elf") || !strcasecmp(extension.c_str(), ".prx") ||
|
||||
!strcasecmp(extension.c_str(), ".pbp"))
|
||||
{
|
||||
!strcasecmp(extension.c_str(), ".pbp")) {
|
||||
return FILETYPE_PSP_ELF;
|
||||
}
|
||||
return FILETYPE_UNKNOWN_ELF;
|
||||
}
|
||||
else if (id == 'PBP\x00')
|
||||
{
|
||||
else if (id == 'PBP\x00') {
|
||||
if (psar_id == 'MUPN') {
|
||||
return FILETYPE_PSP_ISO_NP;
|
||||
}
|
||||
// PS1 PSAR begins with "PSISOIMG0000"
|
||||
if (psar_id == 'SISP') {
|
||||
return FILETYPE_PSP_PS1_PBP;
|
||||
}
|
||||
|
||||
// Let's check if we got pointed to a PBP within such a directory.
|
||||
// If so we just move up and return the directory itself as the game.
|
||||
|
@ -120,14 +128,19 @@ EmuFileType Identify_File(std::string &filename)
|
|||
}
|
||||
return FILETYPE_PSP_PBP;
|
||||
}
|
||||
else if (!strcasecmp(extension.c_str(),".pbp"))
|
||||
{
|
||||
else if (!strcasecmp(extension.c_str(),".pbp")) {
|
||||
ERROR_LOG(LOADER, "A PBP with the wrong magic number?");
|
||||
return FILETYPE_PSP_PBP;
|
||||
}
|
||||
else if (!strcasecmp(extension.c_str(),".bin"))
|
||||
{
|
||||
} else if (!strcasecmp(extension.c_str(),".bin")) {
|
||||
return FILETYPE_UNKNOWN_BIN;
|
||||
} else if (!strcasecmp(extension.c_str(),".zip")) {
|
||||
return FILETYPE_ARCHIVE_ZIP;
|
||||
} else if (!strcasecmp(extension.c_str(),".rar")) {
|
||||
return FILETYPE_ARCHIVE_RAR;
|
||||
} else if (!strcasecmp(extension.c_str(),".r00")) {
|
||||
return FILETYPE_ARCHIVE_RAR;
|
||||
} else if (!strcasecmp(extension.c_str(),".r01")) {
|
||||
return FILETYPE_ARCHIVE_RAR;
|
||||
}
|
||||
return FILETYPE_UNKNOWN;
|
||||
}
|
||||
|
@ -168,11 +181,23 @@ bool LoadFile(std::string &filename, std::string *error_string) {
|
|||
pspFileSystem.SetStartingDirectory("disc0:/PSP_GAME/USRDIR");
|
||||
return Load_PSP_ISO(filename.c_str(), error_string);
|
||||
|
||||
case FILETYPE_PSP_PS1_PBP:
|
||||
*error_string = "PS1 EBOOTs are not supported by PPSSPP.";
|
||||
break;
|
||||
|
||||
case FILETYPE_ERROR:
|
||||
ERROR_LOG(LOADER, "Could not read file");
|
||||
*error_string = "Error reading file";
|
||||
break;
|
||||
|
||||
case FILETYPE_ARCHIVE_RAR:
|
||||
*error_string = "File is compressed (RAR).\nPlease decompress first (try WinRAR)";
|
||||
break;
|
||||
|
||||
case FILETYPE_ARCHIVE_ZIP:
|
||||
*error_string = "File is compressed (ZIP).\nPlease decompress first (try WinRAR)";
|
||||
break;
|
||||
|
||||
case FILETYPE_UNKNOWN_BIN:
|
||||
case FILETYPE_UNKNOWN_ELF:
|
||||
case FILETYPE_UNKNOWN:
|
||||
|
|
|
@ -17,8 +17,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
enum EmuFileType
|
||||
{
|
||||
enum IdentifiedFileType {
|
||||
FILETYPE_ERROR,
|
||||
|
||||
FILETYPE_PSP_PBP_DIRECTORY,
|
||||
|
@ -33,12 +32,17 @@ enum EmuFileType
|
|||
FILETYPE_UNKNOWN_BIN,
|
||||
FILETYPE_UNKNOWN_ELF,
|
||||
|
||||
// Try to reduce support emails...
|
||||
FILETYPE_ARCHIVE_RAR,
|
||||
FILETYPE_ARCHIVE_ZIP,
|
||||
FILETYPE_PSP_PS1_PBP,
|
||||
|
||||
FILETYPE_UNKNOWN
|
||||
};
|
||||
|
||||
// This can modify the string, for example for stripping off the "/EBOOT.PBP"
|
||||
// for a FILETYPE_PSP_PBP_DIRECTORY.
|
||||
EmuFileType Identify_File(std::string &str);
|
||||
IdentifiedFileType Identify_File(std::string &str);
|
||||
|
||||
// Can modify the string filename, as it calls IdentifyFile above.
|
||||
bool LoadFile(std::string &filename, std::string *error_string);
|
||||
|
|
|
@ -592,12 +592,22 @@ namespace MIPSComp
|
|||
fpr.ReleaseSpillLocksAndDiscardTemps();
|
||||
}
|
||||
|
||||
void Jit::Comp_Vhoriz(u32 op) {
|
||||
DISABLE;
|
||||
|
||||
switch ((op >> 16) & 31) {
|
||||
case 6: // vfad
|
||||
break;
|
||||
case 7: // vavg
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Jit::Comp_VHdp(u32 op) {
|
||||
// Similar to vdot
|
||||
DISABLE;
|
||||
}
|
||||
|
||||
|
||||
void Jit::Comp_VecDo3(u32 op) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
|
@ -655,7 +665,7 @@ namespace MIPSComp
|
|||
break;
|
||||
// Unfortunately there is no VMIN/VMAX on ARM without NEON.
|
||||
case 27: //VFPU3
|
||||
switch ((op >> 23) & 3) {
|
||||
switch ((op >> 23) & 7) {
|
||||
case 2: // vmin
|
||||
VCMP(fpr.V(sregs[i]), fpr.V(tregs[i]));
|
||||
VMRS_APSR();
|
||||
|
@ -674,6 +684,26 @@ namespace MIPSComp
|
|||
VMOV(fpr.V(tempregs[i]), fpr.V(tregs[i]));
|
||||
SetCC(CC_AL);
|
||||
break;
|
||||
case 6: // vsge
|
||||
DISABLE; // pending testing
|
||||
VCMP(fpr.V(tregs[i]), fpr.V(sregs[i]));
|
||||
VMRS_APSR();
|
||||
SetCC(CC_GE);
|
||||
MOVI2F(fpr.V(tempregs[i]), 1.0f, R0);
|
||||
SetCC(CC_LT);
|
||||
MOVI2F(fpr.V(tempregs[i]), 0.0f, R0);
|
||||
SetCC(CC_AL);
|
||||
break;
|
||||
case 7: // vslt
|
||||
DISABLE; // pending testing
|
||||
VCMP(fpr.V(tregs[i]), fpr.V(sregs[i]));
|
||||
VMRS_APSR();
|
||||
SetCC(CC_LT);
|
||||
MOVI2F(fpr.V(tempregs[i]), 1.0f, R0);
|
||||
SetCC(CC_GE);
|
||||
MOVI2F(fpr.V(tempregs[i]), 0.0f, R0);
|
||||
SetCC(CC_AL);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1273,14 +1303,6 @@ namespace MIPSComp
|
|||
fpr.ReleaseSpillLocksAndDiscardTemps();
|
||||
}
|
||||
|
||||
void Jit::Comp_Vsge(u32 op) {
|
||||
DISABLE;
|
||||
}
|
||||
|
||||
void Jit::Comp_Vslt(u32 op) {
|
||||
DISABLE;
|
||||
}
|
||||
|
||||
void Jit::Comp_Vcmp(u32 op) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
|
@ -1523,10 +1545,6 @@ namespace MIPSComp
|
|||
fpr.ReleaseSpillLocksAndDiscardTemps();
|
||||
}
|
||||
|
||||
void Jit::Comp_Vhoriz(u32 op) {
|
||||
DISABLE;
|
||||
}
|
||||
|
||||
static float sincostemp[2];
|
||||
|
||||
void SinCos(float angle) {
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "Core/MIPS/MIPSDebugInterface.h"
|
||||
#include "Core/MIPS/MIPSVFPUUtils.h"
|
||||
#include "Core/MIPS/JitCommon/JitBlockCache.h"
|
||||
#include "Core/Reporting.h"
|
||||
#include "Core/System.h"
|
||||
#include "Core/HLE/sceDisplay.h"
|
||||
|
||||
|
@ -203,6 +204,7 @@ void MIPSState::WriteFCR(int reg, int value)
|
|||
}
|
||||
else
|
||||
{
|
||||
WARN_LOG_REPORT(CPU, "WriteFCR: Unexpected reg %d (value %08x)", reg, value);
|
||||
// MessageBox(0, "Invalid FCR","...",0);
|
||||
}
|
||||
DEBUG_LOG(CPU, "FCR%i written to, value %08x", reg, value);
|
||||
|
@ -222,6 +224,7 @@ u32 MIPSState::ReadFCR(int reg)
|
|||
}
|
||||
else
|
||||
{
|
||||
WARN_LOG_REPORT(CPU, "ReadFCR: Unexpected reg %d", reg);
|
||||
// MessageBox(0, "Invalid FCR","...",0);
|
||||
}
|
||||
return 0;
|
||||
|
|
|
@ -54,7 +54,7 @@ namespace MIPSAnalyst
|
|||
if (MIPS_GET_RT(opinfo) == reg)
|
||||
return true;
|
||||
}
|
||||
if (opinfo & (IN_RS | IN_RS_ADDR | IN_RS_SHIFT))
|
||||
if (opinfo & IN_RS)
|
||||
{
|
||||
if (MIPS_GET_RS(opinfo) == reg)
|
||||
return true;
|
||||
|
@ -87,75 +87,26 @@ namespace MIPSAnalyst
|
|||
}
|
||||
}
|
||||
|
||||
// Temporary, returns true for common ops which have proper flags in the table.
|
||||
bool IsDelaySlotInfoSafe(u32 op)
|
||||
{
|
||||
const char *safeOps[] = {
|
||||
"addi", "addiu", "slti", "sltiu", "andi", "ori", "xori", "lui",
|
||||
"lb", "lh", "lwl", "lw", "lbu", "lhu", "lwr",
|
||||
"sb", "sh", "swl", "sw", "swr",
|
||||
"sll", "srl", "sra", "sllv", "srlv", "srav",
|
||||
"add", "addu", "sub", "subu", "and", "or", "xor", "nor",
|
||||
"slt", "sltu",
|
||||
};
|
||||
const char *opName = MIPSGetName(op);
|
||||
for (size_t i = 0; i < ARRAY_SIZE(safeOps); ++i)
|
||||
{
|
||||
if (!strcmp(safeOps[i], opName))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsDelaySlotNiceReg(u32 branchOp, u32 op, int reg1, int reg2)
|
||||
{
|
||||
// NOOPs are always nice.
|
||||
if (op == 0)
|
||||
return true;
|
||||
// $0 is never an out reg, it's always 0.
|
||||
if (reg1 != 0 && GetOutReg(op) == reg1)
|
||||
return false;
|
||||
if (reg2 != 0 && GetOutReg(op) == reg2)
|
||||
return false;
|
||||
|
||||
// TODO: Once the flags are all correct on the tables, remove this safety.
|
||||
if (IsDelaySlotInfoSafe(op))
|
||||
{
|
||||
// $0 is never an out reg, it's always 0.
|
||||
if (reg1 != 0 && GetOutReg(op) == reg1)
|
||||
return false;
|
||||
if (reg2 != 0 && GetOutReg(op) == reg2)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsDelaySlotNiceVFPU(u32 branchOp, u32 op)
|
||||
{
|
||||
// NOOPs are always nice.
|
||||
if (op == 0)
|
||||
return true;
|
||||
|
||||
// TODO: Once the flags are all correct on the tables, remove this safety.
|
||||
if (IsDelaySlotInfoSafe(op))
|
||||
{
|
||||
// TODO: There may be IS_VFPU cases which are safe...
|
||||
return (MIPSGetInfo(op) & IS_VFPU) == 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
// TODO: There may be IS_VFPU cases which are safe...
|
||||
return (MIPSGetInfo(op) & IS_VFPU) == 0;
|
||||
}
|
||||
|
||||
bool IsDelaySlotNiceFPU(u32 branchOp, u32 op)
|
||||
{
|
||||
// NOOPs are always nice.
|
||||
if (op == 0)
|
||||
return true;
|
||||
|
||||
// TODO: Once the flags are all correct on the tables, remove this safety.
|
||||
if (IsDelaySlotInfoSafe(op))
|
||||
return (MIPSGetInfo(op) & OUT_FPUFLAG) == 0;
|
||||
|
||||
return false;
|
||||
return (MIPSGetInfo(op) & OUT_FPUFLAG) == 0;
|
||||
}
|
||||
|
||||
bool IsSyscall(u32 op)
|
||||
|
@ -190,8 +141,7 @@ namespace MIPSAnalyst
|
|||
int rd = MIPS_GET_RD(op);
|
||||
|
||||
if (
|
||||
((info & IN_RS) && (rs == reg)) ||
|
||||
((info & IN_RS_SHIFT) && (rs == reg)) ||
|
||||
((info & IN_RS) && (info & IN_RS_ADDR) == IN_RS && (rs == reg)) ||
|
||||
((info & IN_RT) && (rt == reg)))
|
||||
{
|
||||
if (regAnal[reg].firstRead == -1)
|
||||
|
@ -293,13 +243,7 @@ namespace MIPSAnalyst
|
|||
u32 op = Memory::Read_Instruction(addr);
|
||||
u32 info = MIPSGetInfo(op);
|
||||
|
||||
if (
|
||||
((info & IN_RS) ||
|
||||
(info & IN_RS_SHIFT) ||
|
||||
(info & IN_RS_ADDR))
|
||||
&&
|
||||
(MIPS_GET_RS(op) == reg)
|
||||
)
|
||||
if ((info & IN_RS) && (MIPS_GET_RS(op) == reg))
|
||||
return true;
|
||||
if ((info & IN_RT) && (MIPS_GET_RT(op) == reg))
|
||||
return true;
|
||||
|
@ -644,8 +588,12 @@ namespace MIPSAnalyst
|
|||
info.dataSize = 2;
|
||||
break;
|
||||
case MEMTYPE_WORD:
|
||||
case MEMTYPE_FLOAT:
|
||||
info.dataSize = 4;
|
||||
break;
|
||||
|
||||
case MEMTYPE_VQUAD:
|
||||
info.dataSize = 16;
|
||||
}
|
||||
|
||||
u32 rs = cpu->GetRegValue(0,MIPS_GET_RS(op));
|
||||
|
|
|
@ -26,14 +26,16 @@ namespace MIPSCodeUtils
|
|||
#define FULLOP_JR_RA 0x03e00008
|
||||
#define OP_SYSCALL 0x0000000c
|
||||
#define OP_SYSCALL_MASK 0xFC00003F
|
||||
#define _RS ((op>>21) & 0x1F)
|
||||
#define _RT ((op>>16) & 0x1F)
|
||||
|
||||
u32 GetJumpTarget(u32 addr)
|
||||
{
|
||||
u32 op = Memory::Read_Instruction(addr);
|
||||
if (op)
|
||||
{
|
||||
u32 maskedOp = op & 0xFC000000;
|
||||
if (maskedOp == 0x0C000000 || maskedOp == 0x08000000) //jal
|
||||
u32 info = MIPSGetInfo(op);
|
||||
if ((info & IS_JUMP) && (info & IN_IMM26))
|
||||
{
|
||||
u32 target = (addr & 0xF0000000) | ((op&0x03FFFFFF) << 2);
|
||||
return target;
|
||||
|
@ -87,9 +89,40 @@ namespace MIPSCodeUtils
|
|||
u32 info = MIPSGetInfo(op);
|
||||
if (info & IS_CONDBRANCH)
|
||||
{
|
||||
//TODO: safer check
|
||||
if ((op & 0xFFFF0000) == 0x10000000)
|
||||
return addr + ((signed short)(op&0xFFFF)<<2);
|
||||
bool sure;
|
||||
bool takeBranch;
|
||||
switch (info & CONDTYPE_MASK)
|
||||
{
|
||||
case CONDTYPE_EQ:
|
||||
sure = _RS == _RT;
|
||||
takeBranch = true;
|
||||
break;
|
||||
|
||||
case CONDTYPE_NE:
|
||||
sure = _RS == _RT;
|
||||
takeBranch = false;
|
||||
break;
|
||||
|
||||
case CONDTYPE_LEZ:
|
||||
case CONDTYPE_GEZ:
|
||||
sure = _RS == 0;
|
||||
takeBranch = true;
|
||||
break;
|
||||
|
||||
case CONDTYPE_LTZ:
|
||||
case CONDTYPE_GTZ:
|
||||
sure = _RS == 0;
|
||||
takeBranch = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
sure = false;
|
||||
}
|
||||
|
||||
if (sure && takeBranch)
|
||||
return addr + 4 + ((signed short)(op&0xFFFF)<<2);
|
||||
else if (sure && !takeBranch)
|
||||
return addr + 8;
|
||||
else
|
||||
return INVALIDTARGET;
|
||||
}
|
||||
|
|
|
@ -230,6 +230,7 @@ namespace MIPSInt
|
|||
int imm = (signed short)(op&0xFFFF)<<2;
|
||||
u32 addr = PC + imm + 4;
|
||||
|
||||
// x, y, z, w, any, all, (invalid), (invalid)
|
||||
int imm3 = (op>>18)&7;
|
||||
int val = (currentMIPS->vfpuCtrl[VFPU_CTRL_CC] >> imm3) & 1;
|
||||
|
||||
|
@ -290,7 +291,7 @@ namespace MIPSInt
|
|||
_dbg_assert_msg_(CPU,0,"Jump in delay slot :(");
|
||||
}
|
||||
|
||||
int rs = (op>>21)&0x1f;
|
||||
int rs = _RS;
|
||||
u32 addr = R(rs);
|
||||
switch (op & 0x3f)
|
||||
{
|
||||
|
@ -339,10 +340,10 @@ namespace MIPSInt
|
|||
|
||||
void Int_StoreSync(u32 op)
|
||||
{
|
||||
s32 imm = (signed short)(op&0xFFFF);
|
||||
int base = ((op >> 21) & 0x1f);
|
||||
int rt = (op >> 16) & 0x1f;
|
||||
u32 addr = R(base) + imm;
|
||||
int imm = (signed short)(op&0xFFFF);
|
||||
int rt = _RT;
|
||||
int rs = _RS;
|
||||
u32 addr = R(rs) + imm;
|
||||
|
||||
switch (op >> 26)
|
||||
{
|
||||
|
@ -946,7 +947,7 @@ namespace MIPSInt
|
|||
{
|
||||
case 0:
|
||||
if (!reported) {
|
||||
Reporting::ReportMessage("INTERRUPT instruction hit");
|
||||
Reporting::ReportMessage("INTERRUPT instruction hit (%08x) at %08x", op, currentMIPS->pc);
|
||||
WARN_LOG(CPU,"Disable/Enable Interrupt CPU instruction");
|
||||
reported = 1;
|
||||
}
|
||||
|
|
|
@ -215,31 +215,29 @@ namespace MIPSInt
|
|||
switch (op >> 26)
|
||||
{
|
||||
case 53: //lvl.q/lvr.q
|
||||
if (addr & 0x3)
|
||||
{
|
||||
_dbg_assert_msg_(CPU, 0, "Misaligned lvX.q");
|
||||
}
|
||||
if ((op&2) == 0)
|
||||
{
|
||||
// It's an LVL
|
||||
float d[4];
|
||||
ReadVector(d, V_Quad, vt);
|
||||
int offset = (addr >> 2) & 3;
|
||||
for (int i = 0; i < offset + 1; i++)
|
||||
if (addr & 0x3)
|
||||
{
|
||||
d[3 - i] = Memory::Read_Float(addr - i * 4);
|
||||
_dbg_assert_msg_(CPU, 0, "Misaligned lvX.q");
|
||||
}
|
||||
WriteVector(d, V_Quad, vt);
|
||||
}
|
||||
else
|
||||
{
|
||||
// It's an LVR
|
||||
float d[4];
|
||||
ReadVector(d, V_Quad, vt);
|
||||
int offset = (addr >> 2) & 3;
|
||||
for (int i = 0; i < (3 - offset) + 1; i++)
|
||||
if ((op & 2) == 0)
|
||||
{
|
||||
d[i] = Memory::Read_Float(addr + 4 * i);
|
||||
// It's an LVL
|
||||
for (int i = 0; i < offset + 1; i++)
|
||||
{
|
||||
d[3 - i] = Memory::Read_Float(addr - 4 * i);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// It's an LVR
|
||||
for (int i = 0; i < (3 - offset) + 1; i++)
|
||||
{
|
||||
d[i] = Memory::Read_Float(addr + 4 * i);
|
||||
}
|
||||
}
|
||||
WriteVector(d, V_Quad, vt);
|
||||
}
|
||||
|
@ -254,33 +252,32 @@ namespace MIPSInt
|
|||
break;
|
||||
|
||||
case 61: // svl.q/svr.q
|
||||
if (addr & 0x3)
|
||||
{
|
||||
_dbg_assert_msg_(CPU, 0, "Misaligned svX.q");
|
||||
}
|
||||
if ((op&2) == 0)
|
||||
{
|
||||
// It's an SVL
|
||||
if (addr & 0x3)
|
||||
{
|
||||
_dbg_assert_msg_(CPU, 0, "Misaligned svX.q");
|
||||
}
|
||||
float d[4];
|
||||
ReadVector(d, V_Quad, vt);
|
||||
int offset = (addr >> 2) & 3;
|
||||
for (int i = 0; i < offset + 1; i++)
|
||||
if ((op&2) == 0)
|
||||
{
|
||||
Memory::Write_Float(d[3 - i], addr - i * 4);
|
||||
// It's an SVL
|
||||
for (int i = 0; i < offset + 1; i++)
|
||||
{
|
||||
Memory::Write_Float(d[3 - i], addr - i * 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// It's an SVR
|
||||
float d[4];
|
||||
ReadVector(d, V_Quad, vt);
|
||||
int offset = (addr >> 2) & 3;
|
||||
for (int i = 0; i < (3 - offset) + 1; i++)
|
||||
else
|
||||
{
|
||||
Memory::Write_Float(d[i], addr + 4 * i);
|
||||
// It's an SVR
|
||||
for (int i = 0; i < (3 - offset) + 1; i++)
|
||||
{
|
||||
Memory::Write_Float(d[i], addr + 4 * i);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 62: //sv.q
|
||||
if (addr & 0xF)
|
||||
|
@ -331,6 +328,8 @@ namespace MIPSInt
|
|||
case 7: m=one; break; // vmone
|
||||
default:
|
||||
_dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted");
|
||||
PC += 4;
|
||||
EatPrefixes();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -353,6 +352,8 @@ namespace MIPSInt
|
|||
case 7: v=ones; break; //vone
|
||||
default:
|
||||
_dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted");
|
||||
PC += 4;
|
||||
EatPrefixes();
|
||||
return;
|
||||
}
|
||||
float o[4];
|
||||
|
@ -1258,7 +1259,7 @@ namespace MIPSInt
|
|||
case 1: d[i] = (float)currentMIPS->rng.R32(); break; // vrndi - TODO: copy bits instead?
|
||||
case 2: d[i] = 1.0f + ((float)currentMIPS->rng.R32() / 0xFFFFFFFF); break; // vrndf1 TODO: make more accurate
|
||||
case 3: d[i] = 2.0f + 2 * ((float)currentMIPS->rng.R32() / 0xFFFFFFFF); break; // vrndf2 TODO: make more accurate
|
||||
case 4: d[i] = 0.0f; // Should not get here
|
||||
default: _dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted");
|
||||
}
|
||||
}
|
||||
ApplyPrefixD(d, sz);
|
||||
|
@ -1354,7 +1355,7 @@ namespace MIPSInt
|
|||
{
|
||||
s32 imm = (signed short)(op&0xFFFC);
|
||||
int vt = ((op >> 16) & 0x1f) | ((op & 3) << 5);
|
||||
int rs = (op >> 21) & 0x1f;
|
||||
int rs = _RS;
|
||||
u32 addr = R(rs) + imm;
|
||||
|
||||
switch (op >> 26)
|
||||
|
@ -1532,6 +1533,8 @@ namespace MIPSInt
|
|||
break;
|
||||
default:
|
||||
_dbg_assert_msg_(CPU,0,"unknown min/max op %d", cond);
|
||||
PC += 4;
|
||||
EatPrefixes();
|
||||
return;
|
||||
}
|
||||
ApplyPrefixD(d, sz);
|
||||
|
@ -1540,6 +1543,7 @@ namespace MIPSInt
|
|||
EatPrefixes();
|
||||
}
|
||||
|
||||
// This doesn't quite pass all the tests :/
|
||||
void Int_Vscmp(u32 op) {
|
||||
int vt = _VT;
|
||||
int vs = _VS;
|
||||
|
|
173
Core/MIPS/MIPSStackWalk.cpp
Normal file
173
Core/MIPS/MIPSStackWalk.cpp
Normal file
|
@ -0,0 +1,173 @@
|
|||
// Copyright (c) 2012- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/Debugger/SymbolMap.h"
|
||||
#include "Core/MIPS/MIPSCodeUtils.h"
|
||||
#include "Core/MIPS/MIPSStackWalk.h"
|
||||
|
||||
#define _RS ((op >> 21) & 0x1F)
|
||||
#define _RT ((op >> 16) & 0x1F)
|
||||
#define _RD ((op >> 11) & 0x1F)
|
||||
#define _IMM16 ((signed short)(op & 0xFFFF))
|
||||
|
||||
#define MIPSTABLE_IMM_MASK 0xFC000000
|
||||
#define MIPSTABLE_SPECIAL_MASK 0xFC00003F
|
||||
|
||||
namespace MIPSStackWalk {
|
||||
using namespace MIPSCodeUtils;
|
||||
|
||||
// In the worst case, we scan this far above the pc for an entry.
|
||||
const int MAX_FUNC_SIZE = 32768 * 4;
|
||||
// After this we assume we're stuck.
|
||||
const size_t MAX_DEPTH = 1024;
|
||||
|
||||
static u32 GuessEntry(u32 pc) {
|
||||
SymbolInfo info;
|
||||
if (symbolMap.GetSymbolInfo(&info, pc)) {
|
||||
return info.address;
|
||||
}
|
||||
return INVALIDTARGET;
|
||||
}
|
||||
|
||||
bool IsSWInstr(u32 op) {
|
||||
return (op & MIPSTABLE_IMM_MASK) == 0xAC000000;
|
||||
}
|
||||
|
||||
bool IsAddImmInstr(u32 op) {
|
||||
return (op & MIPSTABLE_IMM_MASK) == 0x20000000 || (op & MIPSTABLE_IMM_MASK) == 0x24000000;
|
||||
}
|
||||
|
||||
bool IsMovRegsInstr(u32 op) {
|
||||
// TODO: There are more options here. Let's assume addu for now.
|
||||
if ((op & MIPSTABLE_SPECIAL_MASK) == 0x00000021) {
|
||||
return _RS == 0 || _RT == 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ScanForAllocaSignature(u32 pc) {
|
||||
// In God Eater Burst, for example, after 0880E750, there's what looks like an alloca().
|
||||
// It's surrounded by "mov fp, sp" and "mov sp, fp", which is unlikely to be used for other reasons.
|
||||
|
||||
// It ought to be pretty close.
|
||||
u32 stop = pc - 32 * 4;
|
||||
for (; Memory::IsValidAddress(pc) && pc >= stop; pc -= 4) {
|
||||
u32 op = Memory::Read_Instruction(pc);
|
||||
|
||||
// We're looking for a "mov fp, sp" close by a "addiu sp, sp, -N".
|
||||
if (IsMovRegsInstr(op) && _RD == MIPS_REG_FP && (_RS == MIPS_REG_SP || _RT == MIPS_REG_SP)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ScanForEntry(StackFrame &frame, u32 entry, u32 &ra) {
|
||||
// TODO: Check if found entry is in the same symbol? Might be wrong sometimes...
|
||||
|
||||
int ra_offset = -1;
|
||||
u32 stop = entry == INVALIDTARGET ? 0 : entry;
|
||||
for (u32 pc = frame.pc; Memory::IsValidAddress(pc) && pc >= stop; pc -= 4) {
|
||||
u32 op = Memory::Read_Instruction(pc);
|
||||
|
||||
// Here's where they store the ra address.
|
||||
if (IsSWInstr(op) && _RT == MIPS_REG_RA && _RS == MIPS_REG_SP) {
|
||||
ra_offset = _IMM16;
|
||||
}
|
||||
|
||||
if (IsAddImmInstr(op) && _RT == MIPS_REG_SP && _RS == MIPS_REG_SP) {
|
||||
// A positive imm either means alloca() or we went too far.
|
||||
if (_IMM16 > 0) {
|
||||
// TODO: Maybe check for any alloca() signature and bail?
|
||||
continue;
|
||||
}
|
||||
if (ScanForAllocaSignature(pc)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
frame.entry = pc;
|
||||
frame.stackSize = -_IMM16;
|
||||
if (ra_offset != -1) {
|
||||
ra = Memory::Read_U32(frame.sp + ra_offset);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DetermineFrameInfo(StackFrame &frame, u32 possibleEntry, u32 threadEntry, u32 &ra) {
|
||||
if (ScanForEntry(frame, possibleEntry, ra)) {
|
||||
// Awesome, found one that looks right.
|
||||
return true;
|
||||
} else if (ra != INVALIDTARGET && possibleEntry != INVALIDTARGET) {
|
||||
// Let's just assume it's a leaf.
|
||||
frame.entry = possibleEntry;
|
||||
frame.stackSize = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Okay, we failed to get one. Our possibleEntry could be wrong, it often is.
|
||||
// Let's just scan upward.
|
||||
u32 newPossibleEntry = frame.pc > threadEntry ? threadEntry : frame.pc - MAX_FUNC_SIZE;
|
||||
if (ScanForEntry(frame, newPossibleEntry, ra)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<StackFrame> Walk(u32 pc, u32 ra, u32 sp, u32 threadEntry, u32 threadStackTop) {
|
||||
std::vector<StackFrame> frames;
|
||||
StackFrame current;
|
||||
current.pc = pc;
|
||||
current.sp = sp;
|
||||
current.entry = INVALIDTARGET;
|
||||
current.stackSize = -1;
|
||||
|
||||
u32 prevEntry = INVALIDTARGET;
|
||||
while (pc != threadEntry) {
|
||||
u32 possibleEntry = GuessEntry(current.pc);
|
||||
if (DetermineFrameInfo(current, possibleEntry, threadEntry, ra)) {
|
||||
frames.push_back(current);
|
||||
if (current.entry == threadEntry || GuessEntry(current.entry) == threadEntry) {
|
||||
break;
|
||||
}
|
||||
if (current.entry == prevEntry || frames.size() >= MAX_DEPTH) {
|
||||
// Recursion, means we're screwed. Let's just give up.
|
||||
break;
|
||||
}
|
||||
prevEntry = current.entry;
|
||||
|
||||
current.pc = ra;
|
||||
current.sp += current.stackSize;
|
||||
ra = INVALIDTARGET;
|
||||
current.entry = INVALIDTARGET;
|
||||
current.stackSize = -1;
|
||||
} else {
|
||||
// Well, we got as far as we could.
|
||||
current.entry = possibleEntry;
|
||||
current.stackSize = 0;
|
||||
frames.push_back(current);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return frames;
|
||||
}
|
||||
};
|
36
Core/MIPS/MIPSStackWalk.h
Normal file
36
Core/MIPS/MIPSStackWalk.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
// Copyright (c) 2012- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
namespace MIPSStackWalk {
|
||||
struct StackFrame {
|
||||
// Beginning of function symbol (may be estimated.)
|
||||
u32 entry;
|
||||
// Next position within function.
|
||||
u32 pc;
|
||||
// Value of SP inside this function (assuming no alloca()...)
|
||||
u32 sp;
|
||||
// Size of stack frame in bytes.
|
||||
int stackSize;
|
||||
};
|
||||
|
||||
std::vector<StackFrame> Walk(u32 pc, u32 ra, u32 sp, u32 threadEntry, u32 threadStackTop);
|
||||
};
|
File diff suppressed because it is too large
Load diff
|
@ -30,10 +30,12 @@
|
|||
// as long as the other flags are checked,
|
||||
// there is no way to misinterprete these
|
||||
// as CONDTYPE_X
|
||||
#define MEMTYPE_MASK 0x00000003
|
||||
#define MEMTYPE_MASK 0x00000007
|
||||
#define MEMTYPE_BYTE 0x00000001
|
||||
#define MEMTYPE_HWORD 0x00000002
|
||||
#define MEMTYPE_WORD 0x00000003
|
||||
#define MEMTYPE_FLOAT 0x00000004
|
||||
#define MEMTYPE_VQUAD 0x00000005
|
||||
|
||||
#define IS_CONDMOVE 0x00000008
|
||||
#define DELAYSLOT 0x00000010
|
||||
|
@ -43,9 +45,9 @@
|
|||
#define IS_CONDBRANCH 0x00000100
|
||||
#define IS_JUMP 0x00000200
|
||||
|
||||
#define IN_RS_SHIFT 0x00000400
|
||||
#define IN_RS_ADDR 0x00000800
|
||||
#define IN_RS 0x00001000
|
||||
#define IN_RS 0x00000400
|
||||
#define IN_RS_ADDR (0x00000800 | IN_RS)
|
||||
#define IN_RS_SHIFT (0x00001000 | IN_RS)
|
||||
#define IN_RT 0x00002000
|
||||
#define IN_SA 0x00004000
|
||||
#define IN_IMM16 0x00008000
|
||||
|
|
|
@ -87,7 +87,7 @@ void Jit::Comp_FPU3op(u32 op)
|
|||
}
|
||||
}
|
||||
|
||||
static u32 GC_ALIGNED16(ssLoadStoreTemp);
|
||||
static u32 MEMORY_ALIGNED16(ssLoadStoreTemp);
|
||||
|
||||
void Jit::Comp_FPULS(u32 op)
|
||||
{
|
||||
|
@ -147,9 +147,9 @@ void Jit::Comp_FPULS(u32 op)
|
|||
}
|
||||
}
|
||||
|
||||
static const u64 GC_ALIGNED16(ssOneBits[2]) = {0x0000000100000001ULL, 0x0000000100000001ULL};
|
||||
static const u64 GC_ALIGNED16(ssSignBits2[2]) = {0x8000000080000000ULL, 0x8000000080000000ULL};
|
||||
static const u64 GC_ALIGNED16(ssNoSignMask[2]) = {0x7FFFFFFF7FFFFFFFULL, 0x7FFFFFFF7FFFFFFFULL};
|
||||
static const u64 MEMORY_ALIGNED16(ssOneBits[2]) = {0x0000000100000001ULL, 0x0000000100000001ULL};
|
||||
static const u64 MEMORY_ALIGNED16(ssSignBits2[2]) = {0x8000000080000000ULL, 0x8000000080000000ULL};
|
||||
static const u64 MEMORY_ALIGNED16(ssNoSignMask[2]) = {0x7FFFFFFF7FFFFFFFULL, 0x7FFFFFFF7FFFFFFFULL};
|
||||
|
||||
static u32 ssCompareTemp;
|
||||
|
||||
|
|
|
@ -57,8 +57,9 @@ static const float one = 1.0f;
|
|||
static const float minus_one = -1.0f;
|
||||
static const float zero = 0.0f;
|
||||
|
||||
const u32 GC_ALIGNED16( noSignMask[4] ) = {0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF};
|
||||
const u32 GC_ALIGNED16( signBitLower[4] ) = {0x80000000, 0, 0, 0};
|
||||
const u32 MEMORY_ALIGNED16( noSignMask[4] ) = {0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF};
|
||||
const u32 MEMORY_ALIGNED16( signBitLower[4] ) = {0x80000000, 0, 0, 0};
|
||||
const float MEMORY_ALIGNED16( oneOneOneOne[4] ) = {1.0f, 1.0f, 1.0f, 1.0f};
|
||||
|
||||
void Jit::Comp_VPFX(u32 op)
|
||||
{
|
||||
|
@ -195,7 +196,7 @@ bool IsOverlapSafe(int dreg, int di, int sn, u8 sregs[], int tn = 0, u8 tregs[]
|
|||
return IsOverlapSafeAllowS(dreg, di, sn, sregs, tn, tregs) && sregs[di] != dreg;
|
||||
}
|
||||
|
||||
static u32 GC_ALIGNED16(ssLoadStoreTemp);
|
||||
static u32 MEMORY_ALIGNED16(ssLoadStoreTemp);
|
||||
|
||||
void Jit::Comp_SV(u32 op) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
@ -271,6 +272,73 @@ void Jit::Comp_SVQ(u32 op)
|
|||
|
||||
switch (op >> 26)
|
||||
{
|
||||
case 53: //lvl.q/lvr.q
|
||||
{
|
||||
if (!g_Config.bFastMemory) {
|
||||
DISABLE;
|
||||
}
|
||||
DISABLE;
|
||||
|
||||
gpr.BindToRegister(rs, true, true);
|
||||
gpr.FlushLockX(ECX);
|
||||
u8 vregs[4];
|
||||
GetVectorRegs(vregs, V_Quad, vt);
|
||||
MOV(32, R(EAX), gpr.R(rs));
|
||||
ADD(32, R(EAX), Imm32(imm));
|
||||
#ifdef _M_IX86
|
||||
AND(32, R(EAX), Imm32(Memory::MEMVIEW32_MASK));
|
||||
#endif
|
||||
MOV(32, R(ECX), R(EAX));
|
||||
SHR(32, R(EAX), Imm8(2));
|
||||
AND(32, R(EAX), Imm32(0x3));
|
||||
CMP(32, R(EAX), Imm32(0));
|
||||
FixupBranch next = J_CC(CC_NE);
|
||||
|
||||
fpr.MapRegsV(vregs, V_Quad, MAP_DIRTY);
|
||||
|
||||
// Offset = 0
|
||||
MOVSS(fpr.RX(vregs[3]), MRegSum(RBX, RAX));
|
||||
|
||||
FixupBranch skip0 = J();
|
||||
SetJumpTarget(next);
|
||||
CMP(32, R(EAX), Imm32(1));
|
||||
next = J_CC(CC_NE);
|
||||
|
||||
// Offset = 1
|
||||
MOVSS(fpr.RX(vregs[3]), MComplex(RBX, RAX, 1, 4));
|
||||
MOVSS(fpr.RX(vregs[2]), MComplex(RBX, RAX, 1, 0));
|
||||
|
||||
FixupBranch skip1 = J();
|
||||
SetJumpTarget(next);
|
||||
CMP(32, R(EAX), Imm32(2));
|
||||
next = J_CC(CC_NE);
|
||||
|
||||
// Offset = 2
|
||||
MOVSS(fpr.RX(vregs[3]), MComplex(RBX, RAX, 1, 8));
|
||||
MOVSS(fpr.RX(vregs[2]), MComplex(RBX, RAX, 1, 4));
|
||||
MOVSS(fpr.RX(vregs[1]), MComplex(RBX, RAX, 1, 0));
|
||||
|
||||
FixupBranch skip2 = J();
|
||||
SetJumpTarget(next);
|
||||
CMP(32, R(EAX), Imm32(3));
|
||||
next = J_CC(CC_NE);
|
||||
|
||||
// Offset = 3
|
||||
MOVSS(fpr.RX(vregs[3]), MComplex(RBX, RAX, 1, 12));
|
||||
MOVSS(fpr.RX(vregs[2]), MComplex(RBX, RAX, 1, 8));
|
||||
MOVSS(fpr.RX(vregs[1]), MComplex(RBX, RAX, 1, 4));
|
||||
MOVSS(fpr.RX(vregs[0]), MComplex(RBX, RAX, 1, 0));
|
||||
|
||||
SetJumpTarget(next);
|
||||
SetJumpTarget(skip0);
|
||||
SetJumpTarget(skip1);
|
||||
SetJumpTarget(skip2);
|
||||
|
||||
gpr.UnlockAll();
|
||||
fpr.ReleaseSpillLocks();
|
||||
}
|
||||
break;
|
||||
|
||||
case 54: //lv.q
|
||||
{
|
||||
gpr.BindToRegister(rs, true, true);
|
||||
|
@ -336,6 +404,8 @@ void Jit::Comp_SVQ(u32 op)
|
|||
}
|
||||
break;
|
||||
|
||||
|
||||
|
||||
default:
|
||||
DISABLE;
|
||||
break;
|
||||
|
@ -658,50 +728,43 @@ void Jit::Comp_VecDo3(u32 op) {
|
|||
if (js.HasUnknownPrefix())
|
||||
DISABLE;
|
||||
|
||||
void (XEmitter::*xmmop)(X64Reg, OpArg) = NULL;
|
||||
switch (op >> 26)
|
||||
{
|
||||
// Check that we can support the ops, and prepare temporary values for ops that need it.
|
||||
switch (op >> 26) {
|
||||
case 24: //VFPU0
|
||||
switch ((op >> 23)&7)
|
||||
{
|
||||
switch ((op >> 23) & 7) {
|
||||
case 0: // d[i] = s[i] + t[i]; break; //vadd
|
||||
xmmop = &XEmitter::ADDSS;
|
||||
break;
|
||||
case 1: // d[i] = s[i] - t[i]; break; //vsub
|
||||
xmmop = &XEmitter::SUBSS;
|
||||
break;
|
||||
case 7: // d[i] = s[i] / t[i]; break; //vdiv
|
||||
xmmop = &XEmitter::DIVSS;
|
||||
break;
|
||||
default:
|
||||
DISABLE;
|
||||
}
|
||||
break;
|
||||
case 25: //VFPU1
|
||||
switch ((op >> 23) & 7)
|
||||
{
|
||||
switch ((op >> 23) & 7) {
|
||||
case 0: // d[i] = s[i] * t[i]; break; //vmul
|
||||
xmmop = &XEmitter::MULSS;
|
||||
break;
|
||||
default:
|
||||
DISABLE;
|
||||
}
|
||||
break;
|
||||
case 27: //VFPU3
|
||||
switch ((op >> 23) & 3)
|
||||
{
|
||||
switch ((op >> 23) & 7) {
|
||||
case 2: // vmin
|
||||
xmmop = &XEmitter::MINSS;
|
||||
break;
|
||||
case 3: // vmax
|
||||
xmmop = &XEmitter::MAXSS;
|
||||
break;
|
||||
case 6: // vsge
|
||||
case 7: // vslt
|
||||
break;
|
||||
default:
|
||||
DISABLE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
_dbg_assert_msg_(CPU,0,"invalid VecDo3");
|
||||
DISABLE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (xmmop == NULL)
|
||||
DISABLE;
|
||||
|
||||
VectorSize sz = GetVecSize(op);
|
||||
int n = GetNumVectorElements(sz);
|
||||
|
||||
|
@ -740,8 +803,50 @@ void Jit::Comp_VecDo3(u32 op) {
|
|||
MOVSS(tempxregs[i], fpr.V(sregs[i]));
|
||||
}
|
||||
|
||||
for (int i = 0; i < n; ++i)
|
||||
(this->*xmmop)(tempxregs[i], fpr.V(tregs[i]));
|
||||
for (int i = 0; i < n; ++i) {
|
||||
switch (op >> 26) {
|
||||
case 24: //VFPU0
|
||||
switch ((op >> 23) & 7) {
|
||||
case 0: // d[i] = s[i] + t[i]; break; //vadd
|
||||
ADDSS(tempxregs[i], fpr.V(tregs[i]));
|
||||
break;
|
||||
case 1: // d[i] = s[i] - t[i]; break; //vsub
|
||||
SUBSS(tempxregs[i], fpr.V(tregs[i]));
|
||||
break;
|
||||
case 7: // d[i] = s[i] / t[i]; break; //vdiv
|
||||
DIVSS(tempxregs[i], fpr.V(tregs[i]));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 25: //VFPU1
|
||||
switch ((op >> 23) & 7)
|
||||
{
|
||||
case 0: // d[i] = s[i] * t[i]; break; //vmul
|
||||
MULSS(tempxregs[i], fpr.V(tregs[i]));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 27: //VFPU3
|
||||
switch ((op >> 23) & 7)
|
||||
{
|
||||
case 2: // vmin
|
||||
MINSS(tempxregs[i], fpr.V(tregs[i]));
|
||||
break;
|
||||
case 3: // vmax
|
||||
MAXSS(tempxregs[i], fpr.V(tregs[i]));
|
||||
break;
|
||||
case 6: // vsge
|
||||
CMPNLTSS(tempxregs[i], fpr.V(tregs[i]));
|
||||
ANDPS(tempxregs[i], M((void *)&oneOneOneOne));
|
||||
break;
|
||||
case 7: // vslt
|
||||
CMPLTSS(tempxregs[i], fpr.V(tregs[i]));
|
||||
ANDPS(tempxregs[i], M((void *)&oneOneOneOne));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "Core/System.h"
|
||||
#include "Core/CoreTiming.h"
|
||||
#include "Core/Config.h"
|
||||
#include "Core/Reporting.h"
|
||||
#include "Core/MIPS/MIPS.h"
|
||||
#include "Core/MIPS/MIPSCodeUtils.h"
|
||||
#include "Core/MIPS/MIPSInt.h"
|
||||
|
@ -278,11 +279,7 @@ const u8 *Jit::DoJit(u32 em_address, JitBlock *b)
|
|||
fpr.Start(mips_, analysis);
|
||||
|
||||
js.numInstructions = 0;
|
||||
while (js.compiling)
|
||||
{
|
||||
if (js.prefixS & 0xF0000000) {
|
||||
ERROR_LOG(CPU, "GARBAGE prefix S : %08x at %08x : %s", js.prefixS, js.compilerPC, currentMIPS->DisasmAt(js.compilerPC));
|
||||
}
|
||||
while (js.compiling) {
|
||||
// Jit breakpoints are quite fast, so let's do them in release too.
|
||||
CheckJitBreakpoint(js.compilerPC, 0);
|
||||
|
||||
|
@ -291,8 +288,7 @@ const u8 *Jit::DoJit(u32 em_address, JitBlock *b)
|
|||
|
||||
MIPSCompileOp(inst);
|
||||
|
||||
if (js.afterOp & JitState::AFTER_CORE_STATE)
|
||||
{
|
||||
if (js.afterOp & JitState::AFTER_CORE_STATE) {
|
||||
// TODO: Save/restore?
|
||||
FlushAll();
|
||||
|
||||
|
@ -314,9 +310,6 @@ const u8 *Jit::DoJit(u32 em_address, JitBlock *b)
|
|||
js.compilerPC += 4;
|
||||
js.numInstructions++;
|
||||
}
|
||||
if (js.prefixS & 0xF0000000) {
|
||||
ERROR_LOG(CPU, "GARBAGE prefix S : %08x at %08x : %s", js.prefixS, js.compilerPC, currentMIPS->DisasmAt(js.compilerPC));
|
||||
}
|
||||
|
||||
b->codeSize = (u32)(GetCodePtr() - b->normalEntry);
|
||||
NOP();
|
||||
|
@ -346,7 +339,7 @@ void Jit::Comp_Generic(u32 op)
|
|||
ABI_CallFunctionC((void *)func, op);
|
||||
}
|
||||
else
|
||||
_dbg_assert_msg_(JIT, 0, "Trying to compile instruction that can't be interpreted");
|
||||
ERROR_LOG_REPORT(JIT, "Trying to compile instruction that can't be interpreted");
|
||||
|
||||
const int info = MIPSGetInfo(op);
|
||||
if ((info & IS_VFPU) != 0 && (info & VFPU_NO_PREFIX) == 0)
|
||||
|
@ -360,7 +353,7 @@ void Jit::Comp_Generic(u32 op)
|
|||
void Jit::WriteExit(u32 destination, int exit_num)
|
||||
{
|
||||
if (!Memory::IsValidAddress(destination)) {
|
||||
ERROR_LOG(JIT, "Trying to write block exit to illegal destination %08x: pc = %08x", destination, currentMIPS->pc);
|
||||
ERROR_LOG_REPORT(JIT, "Trying to write block exit to illegal destination %08x: pc = %08x", destination, currentMIPS->pc);
|
||||
}
|
||||
// If we need to verify coreState and rewind, we may not jump yet.
|
||||
if (js.afterOp & (JitState::AFTER_CORE_STATE | JitState::AFTER_REWIND_PC_BAD_STATE))
|
||||
|
|
|
@ -165,16 +165,6 @@ void Memset(const u32 _Address, const u8 _iValue, const u32 _iLength)
|
|||
}
|
||||
}
|
||||
|
||||
void Memcpy(const u32 to_address, const void *from_data, const u32 len)
|
||||
{
|
||||
memcpy(GetPointer(to_address), from_data, len);
|
||||
}
|
||||
|
||||
void Memcpy(void *to_data, const u32 from_address, const u32 len)
|
||||
{
|
||||
memcpy(to_data,GetPointer(from_address),len);
|
||||
}
|
||||
|
||||
void GetString(std::string& _string, const u32 em_address)
|
||||
{
|
||||
char stringBuffer[2048];
|
||||
|
|
|
@ -117,6 +117,13 @@ u64 Read_U64(const u32 _Address);
|
|||
#define _M_ARM
|
||||
#endif
|
||||
|
||||
inline u8* GetPointerUnchecked(const u32 address) {
|
||||
#if defined(_M_IX86) || defined(_M_ARM32)
|
||||
return (u8 *)(base + (address & MEMVIEW32_MASK));
|
||||
#else
|
||||
return (u8 *)(base + address);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef SAFE_MEMORY
|
||||
u32 ReadUnchecked_U32(const u32 _Address);
|
||||
|
@ -212,8 +219,30 @@ inline const char* GetCharPointer(const u32 address) {
|
|||
}
|
||||
|
||||
void Memset(const u32 _Address, const u8 _Data, const u32 _iLength);
|
||||
void Memcpy(const u32 to_address, const void *from_data, const u32 len);
|
||||
void Memcpy(void *to_data, const u32 from_address, const u32 len);
|
||||
|
||||
inline void Memcpy(const u32 to_address, const void *from_data, const u32 len)
|
||||
{
|
||||
u8 *to = GetPointer(to_address);
|
||||
if (to) {
|
||||
memcpy(to, from_data, len);
|
||||
}
|
||||
// if not, GetPointer will log.
|
||||
}
|
||||
|
||||
inline void Memcpy(void *to_data, const u32 from_address, const u32 len)
|
||||
{
|
||||
const u8 *from = GetPointer(from_address);
|
||||
if (from) {
|
||||
memcpy(to_data, from, len);
|
||||
}
|
||||
// if not, GetPointer will log.
|
||||
}
|
||||
|
||||
inline void MemcpyUnchecked(void *to_data, const u32 from_address, const u32 len)
|
||||
{
|
||||
memcpy(to_data, GetPointerUnchecked(from_address), len);
|
||||
}
|
||||
|
||||
|
||||
template<class T>
|
||||
void ReadStruct(u32 address, T *ptr)
|
||||
|
|
|
@ -88,6 +88,7 @@ namespace SaveState
|
|||
if (Core_IsInactive() && __KernelIsRunning())
|
||||
{
|
||||
// Warning: this may run on a different thread.
|
||||
needsProcess = true;
|
||||
Process();
|
||||
}
|
||||
else
|
||||
|
|
244
Core/System.cpp
244
Core/System.cpp
|
@ -19,6 +19,10 @@
|
|||
#include "Common/CommonWindows.h"
|
||||
#endif
|
||||
|
||||
#include "native/thread/thread.h"
|
||||
#include "native/thread/threadutil.h"
|
||||
#include "native/base/mutex.h"
|
||||
|
||||
#include "Core/MemMap.h"
|
||||
|
||||
#include "Core/MIPS/MIPS.h"
|
||||
|
@ -41,29 +45,80 @@
|
|||
#include "Core/SaveState.h"
|
||||
#include "Common/LogManager.h"
|
||||
|
||||
#include "GPU/GPUState.h"
|
||||
#include "GPU/GPUInterface.h"
|
||||
|
||||
enum CPUThreadState {
|
||||
CPU_THREAD_NOT_RUNNING,
|
||||
CPU_THREAD_PENDING,
|
||||
CPU_THREAD_STARTING,
|
||||
CPU_THREAD_RUNNING,
|
||||
CPU_THREAD_SHUTDOWN,
|
||||
|
||||
CPU_THREAD_EXECUTE,
|
||||
};
|
||||
|
||||
MetaFileSystem pspFileSystem;
|
||||
ParamSFOData g_paramSFO;
|
||||
GlobalUIState globalUIState;
|
||||
static CoreParameter coreParameter;
|
||||
static PSPMixer *mixer;
|
||||
static std::thread *cpuThread = NULL;
|
||||
static recursive_mutex cpuThreadLock;
|
||||
static condition_variable cpuThreadCond;
|
||||
static u64 cpuThreadUntil;
|
||||
|
||||
// This can be read and written from ANYWHERE.
|
||||
volatile CoreState coreState = CORE_STEPPING;
|
||||
// Note: intentionally not used for CORE_NEXTFRAME.
|
||||
volatile bool coreStatePending = false;
|
||||
static volatile CPUThreadState cpuThreadState = CPU_THREAD_NOT_RUNNING;
|
||||
|
||||
void Core_UpdateState(CoreState newState)
|
||||
{
|
||||
if ((coreState == CORE_RUNNING || coreState == CORE_NEXTFRAME) && newState != CORE_RUNNING)
|
||||
coreStatePending = true;
|
||||
coreState = newState;
|
||||
bool IsOnSeparateCPUThread() {
|
||||
if (cpuThread != NULL) {
|
||||
return cpuThread->get_id() == std::this_thread::get_id();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool PSP_Init(const CoreParameter &coreParam, std::string *error_string)
|
||||
{
|
||||
INFO_LOG(HLE, "PPSSPP %s", PPSSPP_GIT_VERSION);
|
||||
bool CPU_NextState(CPUThreadState from, CPUThreadState to) {
|
||||
if (cpuThreadState == from) {
|
||||
cpuThreadState = to;
|
||||
cpuThreadCond.notify_one();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
coreParameter = coreParam;
|
||||
void CPU_SetState(CPUThreadState to) {
|
||||
cpuThreadState = to;
|
||||
cpuThreadCond.notify_one();
|
||||
}
|
||||
|
||||
bool CPU_IsReady() {
|
||||
return cpuThreadState == CPU_THREAD_RUNNING || cpuThreadState == CPU_THREAD_NOT_RUNNING;
|
||||
}
|
||||
|
||||
bool CPU_IsShutdown() {
|
||||
return cpuThreadState == CPU_THREAD_NOT_RUNNING;
|
||||
}
|
||||
|
||||
bool CPU_HasPendingAction() {
|
||||
return cpuThreadState != CPU_THREAD_RUNNING;
|
||||
}
|
||||
|
||||
void CPU_WaitStatus(bool (*pred)()) {
|
||||
cpuThreadLock.lock();
|
||||
while (!pred())
|
||||
cpuThreadCond.wait_for(cpuThreadLock, 16);
|
||||
cpuThreadLock.unlock();
|
||||
}
|
||||
|
||||
void CPU_Shutdown();
|
||||
|
||||
void CPU_Init() {
|
||||
currentCPU = &mipsr4k;
|
||||
numCPUs = 1;
|
||||
|
||||
|
@ -73,11 +128,18 @@ bool PSP_Init(const CoreParameter &coreParam, std::string *error_string)
|
|||
g_RemasterMode = false;
|
||||
g_DoubleTextureCoordinates = false;
|
||||
|
||||
std::string filename = coreParam.fileToStart;
|
||||
EmuFileType type = Identify_File(filename);
|
||||
std::string filename = coreParameter.fileToStart;
|
||||
IdentifiedFileType type = Identify_File(filename);
|
||||
|
||||
if(type == FILETYPE_PSP_ISO || type == FILETYPE_PSP_ISO_NP || type == FILETYPE_PSP_DISC_DIRECTORY)
|
||||
switch (type) {
|
||||
case FILETYPE_PSP_ISO:
|
||||
case FILETYPE_PSP_ISO_NP:
|
||||
case FILETYPE_PSP_DISC_DIRECTORY:
|
||||
InitMemoryForGameISO(filename);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Memory::Init();
|
||||
mipsr4k.Reset();
|
||||
|
@ -85,14 +147,12 @@ bool PSP_Init(const CoreParameter &coreParam, std::string *error_string)
|
|||
|
||||
host->AttemptLoadSymbolMap();
|
||||
|
||||
if (coreParameter.enableSound)
|
||||
{
|
||||
if (coreParameter.enableSound) {
|
||||
mixer = new PSPMixer();
|
||||
host->InitSound(mixer);
|
||||
}
|
||||
|
||||
if (coreParameter.disableG3Dlog)
|
||||
{
|
||||
if (coreParameter.disableG3Dlog) {
|
||||
LogManager::GetInstance()->SetEnable(LogTypes::G3D, false);
|
||||
}
|
||||
|
||||
|
@ -104,64 +164,148 @@ bool PSP_Init(const CoreParameter &coreParam, std::string *error_string)
|
|||
// TODO: Check Game INI here for settings, patches and cheats, and modify coreParameter accordingly
|
||||
|
||||
// Why did we check for CORE_POWERDOWN here?
|
||||
if (!LoadFile(filename, error_string)) { // || coreState == CORE_POWERDOWN) {
|
||||
pspFileSystem.Shutdown();
|
||||
CoreTiming::Shutdown();
|
||||
__KernelShutdown();
|
||||
HLEShutdown();
|
||||
host->ShutdownSound();
|
||||
Memory::Shutdown();
|
||||
if (!LoadFile(filename, &coreParameter.errorString)) {
|
||||
CPU_Shutdown();
|
||||
coreParameter.fileToStart = "";
|
||||
return false;
|
||||
CPU_SetState(CPU_THREAD_NOT_RUNNING);
|
||||
return;
|
||||
}
|
||||
|
||||
if (coreParam.updateRecent)
|
||||
if (coreParameter.updateRecent) {
|
||||
g_Config.AddRecent(filename);
|
||||
}
|
||||
|
||||
// Setup JIT here.
|
||||
if (coreParameter.startPaused)
|
||||
coreState = CORE_STEPPING;
|
||||
else
|
||||
coreState = CORE_RUNNING;
|
||||
return true;
|
||||
coreState = coreParameter.startPaused ? CORE_STEPPING : CORE_RUNNING;
|
||||
}
|
||||
|
||||
bool PSP_IsInited()
|
||||
{
|
||||
return currentCPU != 0;
|
||||
}
|
||||
|
||||
void PSP_Shutdown()
|
||||
{
|
||||
pspFileSystem.Shutdown();
|
||||
void CPU_Shutdown() {
|
||||
if (g_Config.bAutoSaveSymbolMap) {
|
||||
host->SaveSymbolMap();
|
||||
}
|
||||
|
||||
CoreTiming::Shutdown();
|
||||
|
||||
if (g_Config.bAutoSaveSymbolMap)
|
||||
host->SaveSymbolMap();
|
||||
|
||||
if (coreParameter.enableSound)
|
||||
{
|
||||
__KernelShutdown();
|
||||
HLEShutdown();
|
||||
if (coreParameter.enableSound) {
|
||||
host->ShutdownSound();
|
||||
mixer = 0; // deleted in ShutdownSound
|
||||
}
|
||||
__KernelShutdown();
|
||||
HLEShutdown();
|
||||
pspFileSystem.Shutdown();
|
||||
Memory::Shutdown();
|
||||
currentCPU = 0;
|
||||
}
|
||||
|
||||
void CPU_RunLoop() {
|
||||
setCurrentThreadName("CPUThread");
|
||||
if (!CPU_NextState(CPU_THREAD_PENDING, CPU_THREAD_STARTING)) {
|
||||
ERROR_LOG(CPU, "CPU thread in unexpected state: %d", cpuThreadState);
|
||||
return;
|
||||
}
|
||||
|
||||
CPU_Init();
|
||||
CPU_NextState(CPU_THREAD_STARTING, CPU_THREAD_RUNNING);
|
||||
|
||||
while (cpuThreadState != CPU_THREAD_SHUTDOWN)
|
||||
{
|
||||
CPU_WaitStatus(&CPU_HasPendingAction);
|
||||
switch (cpuThreadState) {
|
||||
case CPU_THREAD_EXECUTE:
|
||||
mipsr4k.RunLoopUntil(cpuThreadUntil);
|
||||
gpu->FinishEventLoop();
|
||||
CPU_NextState(CPU_THREAD_EXECUTE, CPU_THREAD_RUNNING);
|
||||
break;
|
||||
|
||||
// These are fine, just keep looping.
|
||||
case CPU_THREAD_RUNNING:
|
||||
case CPU_THREAD_SHUTDOWN:
|
||||
break;
|
||||
|
||||
default:
|
||||
ERROR_LOG(CPU, "CPU thread in unexpected state: %d", cpuThreadState);
|
||||
// Begin shutdown, otherwise we'd just spin on this bad state.
|
||||
CPU_SetState(CPU_THREAD_SHUTDOWN);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (coreState != CORE_ERROR) {
|
||||
coreState = CORE_POWERDOWN;
|
||||
}
|
||||
|
||||
CPU_Shutdown();
|
||||
CPU_SetState(CPU_THREAD_NOT_RUNNING);
|
||||
}
|
||||
|
||||
void Core_UpdateState(CoreState newState) {
|
||||
if ((coreState == CORE_RUNNING || coreState == CORE_NEXTFRAME) && newState != CORE_RUNNING)
|
||||
coreStatePending = true;
|
||||
coreState = newState;
|
||||
}
|
||||
|
||||
bool PSP_Init(const CoreParameter &coreParam, std::string *error_string) {
|
||||
INFO_LOG(HLE, "PPSSPP %s", PPSSPP_GIT_VERSION);
|
||||
|
||||
coreParameter = coreParam;
|
||||
coreParameter.errorString = "";
|
||||
|
||||
if (g_Config.bSeparateCPUThread) {
|
||||
CPU_SetState(CPU_THREAD_PENDING);
|
||||
cpuThread = new std::thread(&CPU_RunLoop);
|
||||
CPU_WaitStatus(&CPU_IsReady);
|
||||
} else {
|
||||
CPU_Init();
|
||||
}
|
||||
|
||||
bool success = coreParameter.fileToStart != "";
|
||||
*error_string = coreParameter.errorString;
|
||||
if (success) {
|
||||
GPU_Init();
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool PSP_IsInited() {
|
||||
return currentCPU != 0;
|
||||
}
|
||||
|
||||
void PSP_Shutdown() {
|
||||
if (coreState == CORE_RUNNING)
|
||||
coreState = CORE_ERROR;
|
||||
if (cpuThread != NULL) {
|
||||
CPU_SetState(CPU_THREAD_SHUTDOWN);
|
||||
CPU_WaitStatus(&CPU_IsShutdown);
|
||||
delete cpuThread;
|
||||
cpuThread = 0;
|
||||
} else {
|
||||
CPU_Shutdown();
|
||||
}
|
||||
GPU_Shutdown();
|
||||
}
|
||||
|
||||
void PSP_RunLoopUntil(u64 globalticks) {
|
||||
SaveState::Process();
|
||||
mipsr4k.RunLoopUntil(globalticks);
|
||||
|
||||
if (cpuThread != NULL) {
|
||||
cpuThreadUntil = globalticks;
|
||||
if (CPU_NextState(CPU_THREAD_RUNNING, CPU_THREAD_EXECUTE)) {
|
||||
// The CPU doesn't actually respect cpuThreadUntil well, especially when skipping frames.
|
||||
// TODO: Something smarter? Or force CPU to bail periodically?
|
||||
while (!CPU_IsReady()) {
|
||||
gpu->RunEventsUntil(CoreTiming::GetTicks() + msToCycles(100));
|
||||
}
|
||||
} else {
|
||||
ERROR_LOG(CPU, "Unable to execute CPU run loop, unexpected state: %d", cpuThreadState);
|
||||
}
|
||||
} else {
|
||||
mipsr4k.RunLoopUntil(globalticks);
|
||||
}
|
||||
}
|
||||
|
||||
void PSP_RunLoopFor(int cycles) {
|
||||
PSP_RunLoopUntil(CoreTiming::GetTicks() + cycles);
|
||||
}
|
||||
|
||||
CoreParameter &PSP_CoreParameter()
|
||||
{
|
||||
CoreParameter &PSP_CoreParameter() {
|
||||
return coreParameter;
|
||||
}
|
||||
|
||||
|
|
|
@ -44,6 +44,8 @@ void PSP_Shutdown();
|
|||
void PSP_RunLoopUntil(u64 globalticks);
|
||||
void PSP_RunLoopFor(int cycles);
|
||||
|
||||
bool IsOnSeparateCPUThread();
|
||||
|
||||
void GetSysDirectories(std::string &memstickpath, std::string &flash0path);
|
||||
|
||||
// RUNNING must be at 0, NEXTFRAME must be at 1.
|
||||
|
|
120
Core/ThreadEventQueue.h
Normal file
120
Core/ThreadEventQueue.h
Normal file
|
@ -0,0 +1,120 @@
|
|||
// Copyright (c) 2013- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "native/base/mutex.h"
|
||||
#include "Core/System.h"
|
||||
#include "Core/CoreTiming.h"
|
||||
#include <deque>
|
||||
|
||||
template <typename B, typename Event, typename EventType, EventType EVENT_INVALID, EventType EVENT_SYNC, EventType EVENT_FINISH>
|
||||
struct ThreadEventQueue : public B {
|
||||
void SetThreadEnabled(bool threadEnabled) {
|
||||
threadEnabled_ = threadEnabled;
|
||||
}
|
||||
|
||||
void ScheduleEvent(Event ev) {
|
||||
{
|
||||
lock_guard guard(eventsLock_);
|
||||
events_.push_back(ev);
|
||||
eventsWait_.notify_one();
|
||||
}
|
||||
|
||||
if (!threadEnabled_) {
|
||||
RunEventsUntil(0);
|
||||
}
|
||||
}
|
||||
|
||||
bool HasEvents() {
|
||||
lock_guard guard(eventsLock_);
|
||||
return !events_.empty();
|
||||
}
|
||||
|
||||
Event GetNextEvent() {
|
||||
lock_guard guard(eventsLock_);
|
||||
if (events_.empty()) {
|
||||
eventsDrain_.notify_one();
|
||||
return EVENT_INVALID;
|
||||
}
|
||||
|
||||
Event ev = events_.front();
|
||||
events_.pop_front();
|
||||
return ev;
|
||||
}
|
||||
|
||||
void RunEventsUntil(u64 globalticks) {
|
||||
lock_guard guard(eventsWaitLock_);
|
||||
do {
|
||||
for (Event ev = GetNextEvent(); EventType(ev) != EVENT_INVALID; ev = GetNextEvent()) {
|
||||
switch (EventType(ev)) {
|
||||
case EVENT_FINISH:
|
||||
// Stop waiting.
|
||||
globalticks = 0;
|
||||
break;
|
||||
|
||||
case EVENT_SYNC:
|
||||
break;
|
||||
|
||||
default:
|
||||
ProcessEvent(ev);
|
||||
}
|
||||
}
|
||||
|
||||
// Quit the loop if the queue is drained and coreState has tripped, or threading is disabled.
|
||||
if (ShouldExitEventLoop() || !threadEnabled_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// coreState changes won't wake us, so recheck periodically.
|
||||
eventsWait_.wait_for(eventsWaitLock_, 1);
|
||||
} while (CoreTiming::GetTicks() < globalticks);
|
||||
}
|
||||
|
||||
void SyncThread() {
|
||||
if (!threadEnabled_) {
|
||||
return;
|
||||
}
|
||||
|
||||
lock_guard guard(eventsDrainLock_);
|
||||
// While processing the last event, HasEvents() will be false even while not done.
|
||||
// So we schedule a nothing event and wait for that to finish.
|
||||
ScheduleEvent(EVENT_SYNC);
|
||||
while (HasEvents() && coreState == CORE_RUNNING) {
|
||||
eventsDrain_.wait_for(eventsDrainLock_, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void FinishEventLoop() {
|
||||
if (threadEnabled_) {
|
||||
ScheduleEvent(EVENT_FINISH);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void ProcessEvent(Event ev) = 0;
|
||||
virtual bool ShouldExitEventLoop() = 0;
|
||||
|
||||
private:
|
||||
bool threadEnabled_;
|
||||
std::deque<Event> events_;
|
||||
recursive_mutex eventsLock_;
|
||||
recursive_mutex eventsWaitLock_;
|
||||
recursive_mutex eventsDrainLock_;
|
||||
condition_variable eventsWait_;
|
||||
condition_variable eventsDrain_;
|
||||
};
|
|
@ -115,7 +115,7 @@ static const u8 flushOnChangedBeforeCommandList[] = {
|
|||
GE_CMD_CLUTFORMAT,
|
||||
GE_CMD_TEXFILTER,
|
||||
GE_CMD_TEXWRAP,
|
||||
GE_CMD_TEXLEVEL,
|
||||
// GE_CMD_TEXLEVEL, // we don't support this anyway, no need to flush.
|
||||
GE_CMD_TEXFUNC,
|
||||
GE_CMD_TEXENVCOLOR,
|
||||
//GE_CMD_TEXFLUSH,
|
||||
|
@ -234,6 +234,8 @@ void GLES_GPU::BuildReportingInfo() {
|
|||
}
|
||||
|
||||
void GLES_GPU::DeviceLost() {
|
||||
// Should only be executed on the GL thread.
|
||||
|
||||
// Simply drop all caches and textures.
|
||||
// FBOs appear to survive? Or no?
|
||||
shaderManager_->ClearCache(false);
|
||||
|
@ -242,6 +244,10 @@ void GLES_GPU::DeviceLost() {
|
|||
}
|
||||
|
||||
void GLES_GPU::InitClear() {
|
||||
ScheduleEvent(GPU_EVENT_INIT_CLEAR);
|
||||
}
|
||||
|
||||
void GLES_GPU::InitClearInternal() {
|
||||
bool useBufferedRendering = g_Config.iRenderingMode != 0 ? 1 : 0;
|
||||
if (!useBufferedRendering) {
|
||||
glstate.depthWrite.set(GL_TRUE);
|
||||
|
@ -257,6 +263,10 @@ void GLES_GPU::DumpNextFrame() {
|
|||
}
|
||||
|
||||
void GLES_GPU::BeginFrame() {
|
||||
ScheduleEvent(GPU_EVENT_BEGIN_FRAME);
|
||||
}
|
||||
|
||||
void GLES_GPU::BeginFrameInternal() {
|
||||
// Turn off vsync when unthrottled
|
||||
int desiredVSyncInterval = g_Config.bVSync ? 1 : 0;
|
||||
if ((PSP_CoreParameter().unthrottle) || (PSP_CoreParameter().fpsLimit == 1))
|
||||
|
@ -294,6 +304,11 @@ void GLES_GPU::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat fo
|
|||
}
|
||||
|
||||
bool GLES_GPU::FramebufferDirty() {
|
||||
// FIXME: Workaround for displaylists sometimes hanging unprocessed. Not yet sure of the cause.
|
||||
ScheduleEvent(GPU_EVENT_PROCESS_QUEUE);
|
||||
// Allow it to process fully before deciding if it's dirty.
|
||||
SyncThread();
|
||||
|
||||
VirtualFramebuffer *vfb = framebufferManager_.GetDisplayFBO();
|
||||
if (vfb)
|
||||
return vfb->dirtyAfterDisplay;
|
||||
|
@ -301,6 +316,10 @@ bool GLES_GPU::FramebufferDirty() {
|
|||
}
|
||||
|
||||
void GLES_GPU::CopyDisplayToOutput() {
|
||||
ScheduleEvent(GPU_EVENT_COPY_DISPLAY_TO_OUTPUT);
|
||||
}
|
||||
|
||||
void GLES_GPU::CopyDisplayToOutputInternal() {
|
||||
glstate.depthWrite.set(GL_TRUE);
|
||||
glstate.colorMask.set(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
|
||||
|
@ -340,6 +359,29 @@ void GLES_GPU::FastRunLoop(DisplayList &list) {
|
|||
}
|
||||
}
|
||||
|
||||
void GLES_GPU::ProcessEvent(GPUEvent ev) {
|
||||
switch (ev.type) {
|
||||
case GPU_EVENT_INIT_CLEAR:
|
||||
InitClearInternal();
|
||||
break;
|
||||
|
||||
case GPU_EVENT_BEGIN_FRAME:
|
||||
BeginFrameInternal();
|
||||
break;
|
||||
|
||||
case GPU_EVENT_COPY_DISPLAY_TO_OUTPUT:
|
||||
CopyDisplayToOutputInternal();
|
||||
break;
|
||||
|
||||
case GPU_EVENT_INVALIDATE_CACHE:
|
||||
InvalidateCacheInternal(ev.invalidate_cache.addr, ev.invalidate_cache.size, ev.invalidate_cache.type);
|
||||
break;
|
||||
|
||||
default:
|
||||
GPUCommon::ProcessEvent(ev);
|
||||
}
|
||||
}
|
||||
|
||||
inline void GLES_GPU::CheckFlushOp(int cmd, u32 diff) {
|
||||
if (flushBeforeCommand_[cmd] == 1 || (diff && flushBeforeCommand_[cmd] == 2))
|
||||
{
|
||||
|
@ -879,7 +921,7 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) {
|
|||
shaderManager_->DirtyUniform(DIRTY_WORLDMATRIX);
|
||||
}
|
||||
num++;
|
||||
gstate.worldmtxnum = (gstate.worldmtxnum & 0xFF000000) | (num & 0xF);
|
||||
gstate.worldmtxnum = (GE_CMD_WORLDMATRIXNUMBER << 24) | (num & 0xF);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -897,7 +939,7 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) {
|
|||
shaderManager_->DirtyUniform(DIRTY_VIEWMATRIX);
|
||||
}
|
||||
num++;
|
||||
gstate.viewmtxnum = (gstate.viewmtxnum & 0xFF000000) | (num & 0xF);
|
||||
gstate.viewmtxnum = (GE_CMD_VIEWMATRIXNUMBER << 24) | (num & 0xF);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -915,7 +957,7 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) {
|
|||
shaderManager_->DirtyUniform(DIRTY_PROJMATRIX | DIRTY_PROJTHROUGHMATRIX);
|
||||
}
|
||||
num++;
|
||||
gstate.projmtxnum = (gstate.projmtxnum & 0xFF000000) | (num & 0xF);
|
||||
gstate.projmtxnum = (GE_CMD_PROJMATRIXNUMBER << 24) | (num & 0xF);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -933,7 +975,7 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) {
|
|||
shaderManager_->DirtyUniform(DIRTY_TEXMATRIX);
|
||||
}
|
||||
num++;
|
||||
gstate.texmtxnum = (gstate.texmtxnum & 0xFF000000) | (num & 0xF);
|
||||
gstate.texmtxnum = (GE_CMD_TGENMATRIXNUMBER << 24) | (num & 0xF);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -951,7 +993,7 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) {
|
|||
shaderManager_->DirtyUniform(DIRTY_BONEMATRIX0 << (num / 12));
|
||||
}
|
||||
num++;
|
||||
gstate.boneMatrixNumber = (gstate.boneMatrixNumber & 0xFF000000) | (num & 0x7F);
|
||||
gstate.boneMatrixNumber = (GE_CMD_BONEMATRIXNUMBER << 24) | (num & 0x7F);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1053,6 +1095,14 @@ void GLES_GPU::DoBlockTransfer() {
|
|||
}
|
||||
|
||||
void GLES_GPU::InvalidateCache(u32 addr, int size, GPUInvalidationType type) {
|
||||
GPUEvent ev(GPU_EVENT_INVALIDATE_CACHE);
|
||||
ev.invalidate_cache.addr = addr;
|
||||
ev.invalidate_cache.size = size;
|
||||
ev.invalidate_cache.type = type;
|
||||
ScheduleEvent(ev);
|
||||
}
|
||||
|
||||
void GLES_GPU::InvalidateCacheInternal(u32 addr, int size, GPUInvalidationType type) {
|
||||
if (size > 0)
|
||||
textureCache_.Invalidate(addr, size, type);
|
||||
else
|
||||
|
@ -1070,11 +1120,6 @@ void GLES_GPU::ClearCacheNextFrame() {
|
|||
textureCache_.ClearNextFrame();
|
||||
}
|
||||
|
||||
|
||||
void GLES_GPU::Flush() {
|
||||
transformDraw_.Flush();
|
||||
}
|
||||
|
||||
void GLES_GPU::Resized() {
|
||||
framebufferManager_.Resized();
|
||||
}
|
||||
|
|
|
@ -49,13 +49,11 @@ public:
|
|||
virtual void DeviceLost(); // Only happens on Android. Drop all textures and shaders.
|
||||
|
||||
virtual void DumpNextFrame();
|
||||
virtual void Flush();
|
||||
virtual void DoState(PointerWrap &p);
|
||||
|
||||
// Called by the window system if the window size changed. This will be reflected in PSPCoreParam.pixel*.
|
||||
virtual void Resized();
|
||||
virtual bool DecodeTexture(u8* dest, GPUgstate state)
|
||||
{
|
||||
virtual bool DecodeTexture(u8* dest, GPUgstate state) {
|
||||
return textureCache_.DecodeTexture(dest, state);
|
||||
}
|
||||
virtual bool FramebufferDirty();
|
||||
|
@ -68,12 +66,20 @@ public:
|
|||
|
||||
protected:
|
||||
virtual void FastRunLoop(DisplayList &list);
|
||||
virtual void ProcessEvent(GPUEvent ev);
|
||||
|
||||
private:
|
||||
void Flush() {
|
||||
transformDraw_.Flush();
|
||||
}
|
||||
void DoBlockTransfer();
|
||||
void ApplyDrawState(int prim);
|
||||
void CheckFlushOp(int cmd, u32 diff);
|
||||
void BuildReportingInfo();
|
||||
void InitClearInternal();
|
||||
void BeginFrameInternal();
|
||||
void CopyDisplayToOutputInternal();
|
||||
void InvalidateCacheInternal(u32 addr, int size, GPUInvalidationType type);
|
||||
|
||||
FramebufferManager framebufferManager_;
|
||||
TextureCache textureCache_;
|
||||
|
|
|
@ -299,6 +299,10 @@ void FramebufferManager::DrawPixels(const u8 *framebuf, GEBufferFormat pixelForm
|
|||
memcpy(dst, src, 4 * 480);
|
||||
}
|
||||
break;
|
||||
|
||||
case GE_FORMAT_INVALID:
|
||||
_dbg_assert_msg_(G3D, false, "Invalid pixelFormat passed to DrawPixels().");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -446,7 +446,7 @@ void LinkedShader::updateUniforms() {
|
|||
dirtyUniforms = 0;
|
||||
}
|
||||
|
||||
ShaderManager::ShaderManager() : lastShader(NULL), globalDirty(0xFFFFFFFF), shaderSwitchDirty(0) {
|
||||
ShaderManager::ShaderManager() : lastShader_(NULL), globalDirty_(0xFFFFFFFF), shaderSwitchDirty_(0) {
|
||||
codeBuffer_ = new char[16384];
|
||||
}
|
||||
|
||||
|
@ -454,27 +454,22 @@ ShaderManager::~ShaderManager() {
|
|||
delete [] codeBuffer_;
|
||||
}
|
||||
|
||||
|
||||
void ShaderManager::DirtyUniform(u32 what) {
|
||||
globalDirty |= what;
|
||||
}
|
||||
|
||||
void ShaderManager::Clear() {
|
||||
for (auto iter = linkedShaderCache.begin(); iter != linkedShaderCache.end(); ++iter) {
|
||||
for (auto iter = linkedShaderCache_.begin(); iter != linkedShaderCache_.end(); ++iter) {
|
||||
delete iter->ls;
|
||||
}
|
||||
for (auto iter = fsCache.begin(); iter != fsCache.end(); ++iter) {
|
||||
for (auto iter = fsCache_.begin(); iter != fsCache_.end(); ++iter) {
|
||||
delete iter->second;
|
||||
}
|
||||
for (auto iter = vsCache.begin(); iter != vsCache.end(); ++iter) {
|
||||
for (auto iter = vsCache_.begin(); iter != vsCache_.end(); ++iter) {
|
||||
delete iter->second;
|
||||
}
|
||||
linkedShaderCache.clear();
|
||||
fsCache.clear();
|
||||
vsCache.clear();
|
||||
globalDirty = 0xFFFFFFFF;
|
||||
lastFSID.clear();
|
||||
lastVSID.clear();
|
||||
linkedShaderCache_.clear();
|
||||
fsCache_.clear();
|
||||
vsCache_.clear();
|
||||
globalDirty_ = 0xFFFFFFFF;
|
||||
lastFSID_.clear();
|
||||
lastVSID_.clear();
|
||||
DirtyShader();
|
||||
}
|
||||
|
||||
|
@ -482,29 +477,28 @@ void ShaderManager::ClearCache(bool deleteThem) {
|
|||
Clear();
|
||||
}
|
||||
|
||||
|
||||
void ShaderManager::DirtyShader() {
|
||||
// Forget the last shader ID
|
||||
lastFSID.clear();
|
||||
lastVSID.clear();
|
||||
lastShader = 0;
|
||||
globalDirty = 0xFFFFFFFF;
|
||||
shaderSwitchDirty = 0;
|
||||
lastFSID_.clear();
|
||||
lastVSID_.clear();
|
||||
lastShader_ = 0;
|
||||
globalDirty_ = 0xFFFFFFFF;
|
||||
shaderSwitchDirty_ = 0;
|
||||
}
|
||||
|
||||
void ShaderManager::EndFrame() { // disables vertex arrays
|
||||
if (lastShader)
|
||||
lastShader->stop();
|
||||
lastShader = 0;
|
||||
if (lastShader_)
|
||||
lastShader_->stop();
|
||||
lastShader_ = 0;
|
||||
}
|
||||
|
||||
|
||||
LinkedShader *ShaderManager::ApplyShader(int prim) {
|
||||
if (globalDirty) {
|
||||
if (lastShader)
|
||||
lastShader->dirtyUniforms |= globalDirty;
|
||||
shaderSwitchDirty |= globalDirty;
|
||||
globalDirty = 0;
|
||||
if (globalDirty_) {
|
||||
if (lastShader_)
|
||||
lastShader_->dirtyUniforms |= globalDirty_;
|
||||
shaderSwitchDirty_ |= globalDirty_;
|
||||
globalDirty_ = 0;
|
||||
}
|
||||
|
||||
bool useHWTransform = CanUseHardwareTransform(prim);
|
||||
|
@ -515,22 +509,22 @@ LinkedShader *ShaderManager::ApplyShader(int prim) {
|
|||
ComputeFragmentShaderID(&FSID);
|
||||
|
||||
// Just update uniforms if this is the same shader as last time.
|
||||
if (lastShader != 0 && VSID == lastVSID && FSID == lastFSID) {
|
||||
lastShader->updateUniforms();
|
||||
return lastShader; // Already all set.
|
||||
if (lastShader_ != 0 && VSID == lastVSID_ && FSID == lastFSID_) {
|
||||
lastShader_->updateUniforms();
|
||||
return lastShader_; // Already all set.
|
||||
}
|
||||
|
||||
if (lastShader != 0) {
|
||||
if (lastShader_ != 0) {
|
||||
// There was a previous shader and we're switching.
|
||||
lastShader->stop();
|
||||
lastShader_->stop();
|
||||
}
|
||||
|
||||
lastVSID = VSID;
|
||||
lastFSID = FSID;
|
||||
lastVSID_ = VSID;
|
||||
lastFSID_ = FSID;
|
||||
|
||||
VSCache::iterator vsIter = vsCache.find(VSID);
|
||||
VSCache::iterator vsIter = vsCache_.find(VSID);
|
||||
Shader *vs;
|
||||
if (vsIter == vsCache.end()) {
|
||||
if (vsIter == vsCache_.end()) {
|
||||
// Vertex shader not in cache. Let's compile it.
|
||||
GenerateVertexShader(prim, codeBuffer_, useHWTransform);
|
||||
vs = new Shader(codeBuffer_, GL_VERTEX_SHADER, useHWTransform);
|
||||
|
@ -549,18 +543,18 @@ LinkedShader *ShaderManager::ApplyShader(int prim) {
|
|||
vs = new Shader(codeBuffer_, GL_VERTEX_SHADER, false);
|
||||
}
|
||||
|
||||
vsCache[VSID] = vs;
|
||||
vsCache_[VSID] = vs;
|
||||
} else {
|
||||
vs = vsIter->second;
|
||||
}
|
||||
|
||||
FSCache::iterator fsIter = fsCache.find(FSID);
|
||||
FSCache::iterator fsIter = fsCache_.find(FSID);
|
||||
Shader *fs;
|
||||
if (fsIter == fsCache.end()) {
|
||||
if (fsIter == fsCache_.end()) {
|
||||
// Fragment shader not in cache. Let's compile it.
|
||||
GenerateFragmentShader(codeBuffer_);
|
||||
fs = new Shader(codeBuffer_, GL_FRAGMENT_SHADER, useHWTransform);
|
||||
fsCache[FSID] = fs;
|
||||
fsCache_[FSID] = fs;
|
||||
} else {
|
||||
fs = fsIter->second;
|
||||
}
|
||||
|
@ -568,24 +562,24 @@ LinkedShader *ShaderManager::ApplyShader(int prim) {
|
|||
// Okay, we have both shaders. Let's see if there's a linked one.
|
||||
LinkedShader *ls = NULL;
|
||||
|
||||
for (auto iter = linkedShaderCache.begin(); iter != linkedShaderCache.end(); ++iter) {
|
||||
for (auto iter = linkedShaderCache_.begin(); iter != linkedShaderCache_.end(); ++iter) {
|
||||
// Deferred dirtying! Let's see if we can make this even more clever later.
|
||||
iter->ls->dirtyUniforms |= shaderSwitchDirty;
|
||||
iter->ls->dirtyUniforms |= shaderSwitchDirty_;
|
||||
|
||||
if (iter->vs == vs && iter->fs == fs) {
|
||||
ls = iter->ls;
|
||||
}
|
||||
}
|
||||
shaderSwitchDirty = 0;
|
||||
shaderSwitchDirty_ = 0;
|
||||
|
||||
if (ls == NULL) {
|
||||
ls = new LinkedShader(vs, fs, vs->UseHWTransform()); // This does "use" automatically
|
||||
const LinkedShaderCacheEntry entry(vs, fs, ls);
|
||||
linkedShaderCache.push_back(entry);
|
||||
linkedShaderCache_.push_back(entry);
|
||||
} else {
|
||||
ls->use();
|
||||
}
|
||||
|
||||
lastShader = ls;
|
||||
lastShader_ = ls;
|
||||
return ls;
|
||||
}
|
||||
|
|
|
@ -156,12 +156,14 @@ public:
|
|||
void ClearCache(bool deleteThem); // TODO: deleteThem currently not respected
|
||||
LinkedShader *ApplyShader(int prim);
|
||||
void DirtyShader();
|
||||
void DirtyUniform(u32 what);
|
||||
void DirtyUniform(u32 what) {
|
||||
globalDirty_ |= what;
|
||||
}
|
||||
void EndFrame(); // disables vertex arrays
|
||||
|
||||
int NumVertexShaders() const { return (int)vsCache.size(); }
|
||||
int NumFragmentShaders() const { return (int)fsCache.size(); }
|
||||
int NumPrograms() const { return (int)linkedShaderCache.size(); }
|
||||
int NumVertexShaders() const { return (int)vsCache_.size(); }
|
||||
int NumFragmentShaders() const { return (int)fsCache_.size(); }
|
||||
int NumPrograms() const { return (int)linkedShaderCache_.size(); }
|
||||
|
||||
private:
|
||||
void Clear();
|
||||
|
@ -176,18 +178,18 @@ private:
|
|||
};
|
||||
typedef std::vector<LinkedShaderCacheEntry> LinkedShaderCache;
|
||||
|
||||
LinkedShaderCache linkedShaderCache;
|
||||
FragmentShaderID lastFSID;
|
||||
VertexShaderID lastVSID;
|
||||
LinkedShaderCache linkedShaderCache_;
|
||||
FragmentShaderID lastFSID_;
|
||||
VertexShaderID lastVSID_;
|
||||
|
||||
LinkedShader *lastShader;
|
||||
u32 globalDirty;
|
||||
u32 shaderSwitchDirty;
|
||||
LinkedShader *lastShader_;
|
||||
u32 globalDirty_;
|
||||
u32 shaderSwitchDirty_;
|
||||
char *codeBuffer_;
|
||||
|
||||
typedef std::map<FragmentShaderID, Shader *> FSCache;
|
||||
FSCache fsCache;
|
||||
FSCache fsCache_;
|
||||
|
||||
typedef std::map<VertexShaderID, Shader *> VSCache;
|
||||
VSCache vsCache;
|
||||
VSCache vsCache_;
|
||||
};
|
||||
|
|
|
@ -254,10 +254,6 @@ void TextureCache::NotifyFramebuffer(u32 address, VirtualFramebuffer *framebuffe
|
|||
}
|
||||
}
|
||||
|
||||
static u32 GetClutAddr() {
|
||||
return ((gstate.clutaddr & 0xFFFFFF) | ((gstate.clutaddrupper << 8) & 0x0F000000));
|
||||
}
|
||||
|
||||
static u32 GetClutIndex(u32 index) {
|
||||
const u32 clutBase = gstate.getClutIndexStartPos();
|
||||
const u32 clutMask = gstate.getClutIndexMask();
|
||||
|
@ -276,9 +272,9 @@ void *TextureCache::UnswizzleFromMem(u32 texaddr, u32 bufw, u32 bytesPerPixel, u
|
|||
u32 ydest = 0;
|
||||
if (rowWidth >= 16) {
|
||||
const u32 *src = (u32 *) Memory::GetPointer(texaddr);
|
||||
u32 *ydest = tmpTexBuf32.data();
|
||||
u32 *ydestp = tmpTexBuf32.data();
|
||||
for (int by = 0; by < byc; by++) {
|
||||
u32 *xdest = ydest;
|
||||
u32 *xdest = ydestp;
|
||||
for (int bx = 0; bx < bxc; bx++) {
|
||||
u32 *dest = xdest;
|
||||
for (int n = 0; n < 8; n++) {
|
||||
|
@ -288,7 +284,7 @@ void *TextureCache::UnswizzleFromMem(u32 texaddr, u32 bufw, u32 bytesPerPixel, u
|
|||
}
|
||||
xdest += 4;
|
||||
}
|
||||
ydest += (rowWidth * 8) / 4;
|
||||
ydestp += (rowWidth * 8) / 4;
|
||||
}
|
||||
} else if (rowWidth == 8) {
|
||||
const u32 *src = (u32 *) Memory::GetPointer(texaddr);
|
||||
|
@ -877,10 +873,10 @@ inline bool TextureCache::TexCacheEntry::Matches(u16 dim2, u8 format2, int maxLe
|
|||
}
|
||||
|
||||
void TextureCache::LoadClut() {
|
||||
u32 clutAddr = GetClutAddr();
|
||||
u32 clutAddr = ((gstate.clutaddr & 0xFFFFFF) | ((gstate.clutaddrupper << 8) & 0x0F000000));
|
||||
clutTotalBytes_ = (gstate.loadclut & 0x3f) * 32;
|
||||
if (Memory::IsValidAddress(clutAddr)) {
|
||||
Memory::Memcpy(clutBufRaw_, clutAddr, clutTotalBytes_);
|
||||
Memory::MemcpyUnchecked(clutBufRaw_, clutAddr, clutTotalBytes_);
|
||||
} else {
|
||||
memset(clutBufRaw_, 0xFF, clutTotalBytes_);
|
||||
}
|
||||
|
|
|
@ -1109,10 +1109,7 @@ VertexArrayInfo::~VertexArrayInfo() {
|
|||
glDeleteBuffers(1, &ebo);
|
||||
}
|
||||
|
||||
void TransformDrawEngine::Flush() {
|
||||
if (!numDrawCalls)
|
||||
return;
|
||||
|
||||
void TransformDrawEngine::DoFlush() {
|
||||
gpuStats.numFlushes++;
|
||||
|
||||
gpuStats.numTrackedVertexArrays = (int)vai_.size();
|
||||
|
|
|
@ -100,7 +100,6 @@ public:
|
|||
void DrawBezier(int ucount, int vcount);
|
||||
void DrawSpline(int ucount, int vcount, int utype, int vtype);
|
||||
void DecodeVerts();
|
||||
void Flush();
|
||||
void SetShaderManager(ShaderManager *shaderManager) {
|
||||
shaderManager_ = shaderManager;
|
||||
}
|
||||
|
@ -122,7 +121,15 @@ public:
|
|||
// This requires a SetupVertexDecoder call first.
|
||||
int EstimatePerVertexCost();
|
||||
|
||||
// So that this can be inlined
|
||||
void Flush() {
|
||||
if (!numDrawCalls)
|
||||
return;
|
||||
DoFlush();
|
||||
}
|
||||
|
||||
private:
|
||||
void DoFlush();
|
||||
void SoftwareTransformAndDraw(int prim, u8 *decoded, LinkedShader *program, int vertexCount, u32 vertexType, void *inds, int indexType, const DecVtxFormat &decVtxFormat, int maxIndex);
|
||||
void ApplyDrawState(int prim);
|
||||
bool IsReallyAClear(int numVerts) const;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include <algorithm>
|
||||
#include "base/timeutil.h"
|
||||
#include "native/base/mutex.h"
|
||||
#include "native/base/timeutil.h"
|
||||
#include "GeDisasm.h"
|
||||
#include "GPUCommon.h"
|
||||
#include "GPUState.h"
|
||||
|
@ -9,9 +10,7 @@
|
|||
#include "Core/MemMap.h"
|
||||
#include "Core/Host.h"
|
||||
#include "Core/Reporting.h"
|
||||
#include "Core/HLE/sceKernelInterrupt.h"
|
||||
#include "Core/HLE/sceKernelMemory.h"
|
||||
#include "Core/HLE/sceKernelThread.h"
|
||||
#include "Core/HLE/sceGe.h"
|
||||
|
||||
GPUCommon::GPUCommon() :
|
||||
|
@ -21,16 +20,19 @@ GPUCommon::GPUCommon() :
|
|||
busyTicks(0),
|
||||
dumpNextFrame_(false),
|
||||
dumpThisFrame_(false),
|
||||
interruptsEnabled_(true)
|
||||
interruptsEnabled_(true),
|
||||
curTickEst_(0)
|
||||
{
|
||||
memset(dls, 0, sizeof(dls));
|
||||
for (int i = 0; i < DisplayListMaxCount; ++i) {
|
||||
dls[i].state = PSP_GE_DL_STATE_NONE;
|
||||
dls[i].waitTicks = 0;
|
||||
}
|
||||
SetThreadEnabled(g_Config.bSeparateCPUThread);
|
||||
}
|
||||
|
||||
void GPUCommon::PopDLQueue() {
|
||||
easy_guard guard(listLock);
|
||||
if(!dlQueue.empty()) {
|
||||
dlQueue.pop_front();
|
||||
if(!dlQueue.empty()) {
|
||||
|
@ -45,13 +47,19 @@ void GPUCommon::PopDLQueue() {
|
|||
}
|
||||
|
||||
u32 GPUCommon::DrawSync(int mode) {
|
||||
// FIXME: Workaround for displaylists sometimes hanging unprocessed. Not yet sure of the cause.
|
||||
ScheduleEvent(GPU_EVENT_PROCESS_QUEUE);
|
||||
// Sync first, because the CPU is usually faster than the emulated GPU.
|
||||
SyncThread();
|
||||
|
||||
easy_guard guard(listLock);
|
||||
if (mode < 0 || mode > 1)
|
||||
return SCE_KERNEL_ERROR_INVALID_MODE;
|
||||
|
||||
if (mode == 0) {
|
||||
// TODO: What if dispatch / interrupts disabled?
|
||||
if (drawCompleteTicks > CoreTiming::GetTicks()) {
|
||||
__KernelWaitCurThread(WAITTYPE_GEDRAWSYNC, 1, 0, 0, false, "GeDrawSync");
|
||||
__GeWaitCurrentThread(WAITTYPE_GEDRAWSYNC, 1, "GeDrawSync");
|
||||
} else {
|
||||
for (int i = 0; i < DisplayListMaxCount; ++i) {
|
||||
if (dls[i].state == PSP_GE_DL_STATE_COMPLETED) {
|
||||
|
@ -79,16 +87,21 @@ u32 GPUCommon::DrawSync(int mode) {
|
|||
return PSP_GE_LIST_DRAWING;
|
||||
}
|
||||
|
||||
void GPUCommon::CheckDrawSync()
|
||||
{
|
||||
void GPUCommon::CheckDrawSync() {
|
||||
easy_guard guard(listLock);
|
||||
if (dlQueue.empty()) {
|
||||
for (int i = 0; i < DisplayListMaxCount; ++i)
|
||||
dls[i].state = PSP_GE_DL_STATE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
int GPUCommon::ListSync(int listid, int mode)
|
||||
{
|
||||
int GPUCommon::ListSync(int listid, int mode) {
|
||||
// FIXME: Workaround for displaylists sometimes hanging unprocessed. Not yet sure of the cause.
|
||||
ScheduleEvent(GPU_EVENT_PROCESS_QUEUE);
|
||||
// Sync first, because the CPU is usually faster than the emulated GPU.
|
||||
SyncThread();
|
||||
|
||||
easy_guard guard(listLock);
|
||||
if (listid < 0 || listid >= DisplayListMaxCount)
|
||||
return SCE_KERNEL_ERROR_INVALID_ID;
|
||||
|
||||
|
@ -120,13 +133,13 @@ int GPUCommon::ListSync(int listid, int mode)
|
|||
}
|
||||
|
||||
if (dl.waitTicks > CoreTiming::GetTicks()) {
|
||||
__KernelWaitCurThread(WAITTYPE_GELISTSYNC, listid, 0, 0, false, "GeListSync");
|
||||
__GeWaitCurrentThread(WAITTYPE_GELISTSYNC, listid, "GeListSync");
|
||||
}
|
||||
return PSP_GE_LIST_COMPLETED;
|
||||
}
|
||||
|
||||
u32 GPUCommon::EnqueueList(u32 listpc, u32 stall, int subIntrBase, bool head)
|
||||
{
|
||||
u32 GPUCommon::EnqueueList(u32 listpc, u32 stall, int subIntrBase, bool head) {
|
||||
easy_guard guard(listLock);
|
||||
// TODO Check the stack values in missing arg and ajust the stack depth
|
||||
|
||||
// Check alignment
|
||||
|
@ -210,14 +223,15 @@ u32 GPUCommon::EnqueueList(u32 listpc, u32 stall, int subIntrBase, bool head)
|
|||
drawCompleteTicks = (u64)-1;
|
||||
|
||||
// TODO save context when starting the list if param is set
|
||||
guard.unlock();
|
||||
ProcessDLQueue();
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
u32 GPUCommon::DequeueList(int listid)
|
||||
{
|
||||
u32 GPUCommon::DequeueList(int listid) {
|
||||
easy_guard guard(listLock);
|
||||
if (listid < 0 || listid >= DisplayListMaxCount || dls[listid].state == PSP_GE_DL_STATE_NONE)
|
||||
return SCE_KERNEL_ERROR_INVALID_ID;
|
||||
|
||||
|
@ -232,15 +246,15 @@ u32 GPUCommon::DequeueList(int listid)
|
|||
dlQueue.remove(listid);
|
||||
|
||||
dls[listid].waitTicks = 0;
|
||||
__KernelTriggerWait(WAITTYPE_GELISTSYNC, listid, 0, "GeListSync");
|
||||
__GeTriggerWait(WAITTYPE_GELISTSYNC, listid, "GeListSync");
|
||||
|
||||
CheckDrawSync();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 GPUCommon::UpdateStall(int listid, u32 newstall)
|
||||
{
|
||||
u32 GPUCommon::UpdateStall(int listid, u32 newstall) {
|
||||
easy_guard guard(listLock);
|
||||
if (listid < 0 || listid >= DisplayListMaxCount || dls[listid].state == PSP_GE_DL_STATE_NONE)
|
||||
return SCE_KERNEL_ERROR_INVALID_ID;
|
||||
|
||||
|
@ -249,13 +263,14 @@ u32 GPUCommon::UpdateStall(int listid, u32 newstall)
|
|||
if (dls[listid].signal == PSP_GE_SIGNAL_HANDLER_PAUSE)
|
||||
dls[listid].signal = PSP_GE_SIGNAL_HANDLER_SUSPEND;
|
||||
|
||||
guard.unlock();
|
||||
ProcessDLQueue();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 GPUCommon::Continue()
|
||||
{
|
||||
u32 GPUCommon::Continue() {
|
||||
easy_guard guard(listLock);
|
||||
if (!currentList)
|
||||
return 0;
|
||||
|
||||
|
@ -291,12 +306,13 @@ u32 GPUCommon::Continue()
|
|||
return -1;
|
||||
}
|
||||
|
||||
guard.unlock();
|
||||
ProcessDLQueue();
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 GPUCommon::Break(int mode)
|
||||
{
|
||||
u32 GPUCommon::Break(int mode) {
|
||||
easy_guard guard(listLock);
|
||||
if (mode < 0 || mode > 1)
|
||||
return SCE_KERNEL_ERROR_INVALID_MODE;
|
||||
|
||||
|
@ -359,20 +375,19 @@ u32 GPUCommon::Break(int mode)
|
|||
return currentList->id;
|
||||
}
|
||||
|
||||
bool GPUCommon::InterpretList(DisplayList &list)
|
||||
{
|
||||
bool GPUCommon::InterpretList(DisplayList &list) {
|
||||
// Initialized to avoid a race condition with bShowDebugStats changing.
|
||||
double start = 0.0;
|
||||
if (g_Config.bShowDebugStats)
|
||||
{
|
||||
if (g_Config.bShowDebugStats) {
|
||||
time_update();
|
||||
start = time_now_d();
|
||||
}
|
||||
|
||||
easy_guard guard(listLock);
|
||||
|
||||
// TODO: This has to be right... but it freezes right now?
|
||||
//if (list.state == PSP_GE_DL_STATE_PAUSED)
|
||||
// return false;
|
||||
|
||||
currentList = &list;
|
||||
|
||||
// I don't know if this is the correct place to zero this, but something
|
||||
|
@ -384,9 +399,9 @@ bool GPUCommon::InterpretList(DisplayList &list)
|
|||
ERROR_LOG_REPORT(G3D, "DL PC = %08x WTF!!!!", list.pc);
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(USING_QT_UI)
|
||||
if(host->GpuStep())
|
||||
{
|
||||
if (host->GpuStep()) {
|
||||
host->SendGPUStart();
|
||||
}
|
||||
#endif
|
||||
|
@ -397,32 +412,43 @@ bool GPUCommon::InterpretList(DisplayList &list)
|
|||
list.interrupted = false;
|
||||
|
||||
gpuState = list.pc == list.stall ? GPUSTATE_STALL : GPUSTATE_RUNNING;
|
||||
guard.unlock();
|
||||
|
||||
const bool dumpThisFrame = dumpThisFrame_;
|
||||
// TODO: Add check for displaylist debugger.
|
||||
const bool useFastRunLoop = !dumpThisFrame;
|
||||
while (gpuState == GPUSTATE_RUNNING)
|
||||
{
|
||||
if (list.pc == list.stall)
|
||||
while (gpuState == GPUSTATE_RUNNING) {
|
||||
{
|
||||
gpuState = GPUSTATE_STALL;
|
||||
downcount = 0;
|
||||
easy_guard innerGuard(listLock);
|
||||
if (list.pc == list.stall) {
|
||||
gpuState = GPUSTATE_STALL;
|
||||
downcount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (useFastRunLoop)
|
||||
if (useFastRunLoop) {
|
||||
FastRunLoop(list);
|
||||
else
|
||||
} else {
|
||||
SlowRunLoop(list);
|
||||
}
|
||||
|
||||
downcount = list.stall == 0 ? 0xFFFFFFF : (list.stall - list.pc) / 4;
|
||||
{
|
||||
easy_guard innerGuard(listLock);
|
||||
downcount = list.stall == 0 ? 0xFFFFFFF : (list.stall - list.pc) / 4;
|
||||
|
||||
if (gpuState == GPUSTATE_STALL && list.stall != list.pc) {
|
||||
// Unstalled.
|
||||
gpuState = GPUSTATE_RUNNING;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We haven't run the op at list.pc, so it shouldn't count.
|
||||
if (cycleLastPC != list.pc)
|
||||
if (cycleLastPC != list.pc) {
|
||||
UpdatePC(list.pc - 4, list.pc);
|
||||
}
|
||||
|
||||
if (g_Config.bShowDebugStats)
|
||||
{
|
||||
if (g_Config.bShowDebugStats) {
|
||||
time_update();
|
||||
gpuStats.msProcessingDisplayLists += time_now_d() - start;
|
||||
}
|
||||
|
@ -460,8 +486,7 @@ void GPUCommon::SlowRunLoop(DisplayList &list)
|
|||
}
|
||||
|
||||
// The newPC parameter is used for jumps, we don't count cycles between.
|
||||
inline void GPUCommon::UpdatePC(u32 currentPC, u32 newPC)
|
||||
{
|
||||
inline void GPUCommon::UpdatePC(u32 currentPC, u32 newPC) {
|
||||
// Rough estimate, 2 CPU ticks (it's double the clock rate) per GPU instruction.
|
||||
cyclesExecuted += 2 * (currentPC - cycleLastPC) / 4;
|
||||
gpuStats.otherGPUCycles += 2 * (currentPC - cycleLastPC) / 4;
|
||||
|
@ -471,8 +496,15 @@ inline void GPUCommon::UpdatePC(u32 currentPC, u32 newPC)
|
|||
downcount = 0;
|
||||
}
|
||||
|
||||
void GPUCommon::ReapplyGfxState()
|
||||
{
|
||||
void GPUCommon::ReapplyGfxState() {
|
||||
if (IsOnSeparateCPUThread()) {
|
||||
ScheduleEvent(GPU_EVENT_REAPPLY_GFX_STATE);
|
||||
} else {
|
||||
ReapplyGfxStateInternal();
|
||||
}
|
||||
}
|
||||
|
||||
void GPUCommon::ReapplyGfxStateInternal() {
|
||||
// ShaderManager_DirtyShader();
|
||||
// The commands are embedded in the command memory so we can just reexecute the words. Convenient.
|
||||
// To be safe we pass 0xFFFFFFF as the diff.
|
||||
|
@ -488,37 +520,50 @@ void GPUCommon::ReapplyGfxState()
|
|||
ExecuteOp(gstate.cmdmem[GE_CMD_SCISSOR2], 0xFFFFFFFF);
|
||||
*/
|
||||
|
||||
for (int i = GE_CMD_VERTEXTYPE; i < GE_CMD_BONEMATRIXNUMBER; i++)
|
||||
{
|
||||
if (i != GE_CMD_ORIGIN)
|
||||
for (int i = GE_CMD_VERTEXTYPE; i < GE_CMD_BONEMATRIXNUMBER; i++) {
|
||||
if (i != GE_CMD_ORIGIN) {
|
||||
ExecuteOp(gstate.cmdmem[i], 0xFFFFFFFF);
|
||||
}
|
||||
}
|
||||
|
||||
// Can't write to bonematrixnumber here
|
||||
|
||||
for (int i = GE_CMD_MORPHWEIGHT0; i < GE_CMD_PATCHFACING; i++)
|
||||
{
|
||||
for (int i = GE_CMD_MORPHWEIGHT0; i < GE_CMD_PATCHFACING; i++) {
|
||||
ExecuteOp(gstate.cmdmem[i], 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
// There are a few here in the middle that we shouldn't execute...
|
||||
|
||||
for (int i = GE_CMD_VIEWPORTX1; i < GE_CMD_TRANSFERSTART; i++)
|
||||
{
|
||||
for (int i = GE_CMD_VIEWPORTX1; i < GE_CMD_TRANSFERSTART; i++) {
|
||||
ExecuteOp(gstate.cmdmem[i], 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
// TODO: there's more...
|
||||
}
|
||||
|
||||
inline void GPUCommon::UpdateState(GPUState state)
|
||||
{
|
||||
inline void GPUCommon::UpdateState(GPUState state) {
|
||||
gpuState = state;
|
||||
if (state != GPUSTATE_RUNNING)
|
||||
downcount = 0;
|
||||
}
|
||||
|
||||
void GPUCommon::ProcessEvent(GPUEvent ev) {
|
||||
switch (ev.type) {
|
||||
case GPU_EVENT_PROCESS_QUEUE:
|
||||
ProcessDLQueueInternal();
|
||||
break;
|
||||
|
||||
case GPU_EVENT_REAPPLY_GFX_STATE:
|
||||
ReapplyGfxStateInternal();
|
||||
break;
|
||||
|
||||
default:
|
||||
ERROR_LOG(G3D, "Unexpected GPU event type: %d", (int)ev);
|
||||
}
|
||||
}
|
||||
|
||||
int GPUCommon::GetNextListIndex() {
|
||||
easy_guard guard(listLock);
|
||||
auto iter = dlQueue.begin();
|
||||
if (iter != dlQueue.end()) {
|
||||
return *iter;
|
||||
|
@ -528,32 +573,41 @@ int GPUCommon::GetNextListIndex() {
|
|||
}
|
||||
|
||||
bool GPUCommon::ProcessDLQueue() {
|
||||
ScheduleEvent(GPU_EVENT_PROCESS_QUEUE);
|
||||
return true;
|
||||
}
|
||||
|
||||
void GPUCommon::ProcessDLQueueInternal() {
|
||||
startingTicks = CoreTiming::GetTicks();
|
||||
cyclesExecuted = 0;
|
||||
UpdateTickEstimate(std::max(busyTicks, startingTicks + cyclesExecuted));
|
||||
|
||||
if (startingTicks < busyTicks) {
|
||||
DEBUG_LOG(HLE, "Can't execute a list yet, still busy for %lld ticks", busyTicks - startingTicks);
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
for (int listIndex = GetNextListIndex(); listIndex != -1; listIndex = GetNextListIndex()) {
|
||||
DisplayList &l = dls[listIndex];
|
||||
DEBUG_LOG(G3D, "Okay, starting DL execution at %08x - stall = %08x", l.pc, l.stall);
|
||||
if (!InterpretList(l)) {
|
||||
return false;
|
||||
return;
|
||||
} else {
|
||||
easy_guard guard(listLock);
|
||||
// At the end, we can remove it from the queue and continue.
|
||||
dlQueue.erase(std::remove(dlQueue.begin(), dlQueue.end(), listIndex), dlQueue.end());
|
||||
UpdateTickEstimate(std::max(busyTicks, startingTicks + cyclesExecuted));
|
||||
}
|
||||
}
|
||||
|
||||
easy_guard guard(listLock);
|
||||
currentList = NULL;
|
||||
|
||||
drawCompleteTicks = startingTicks + cyclesExecuted;
|
||||
busyTicks = std::max(busyTicks, drawCompleteTicks);
|
||||
__GeTriggerSync(WAITTYPE_GEDRAWSYNC, 1, drawCompleteTicks);
|
||||
|
||||
return true; //no more lists!
|
||||
// Since the event is in CoreTiming, we're in sync. Just set 0 now.
|
||||
UpdateTickEstimate(0);
|
||||
}
|
||||
|
||||
void GPUCommon::PreExecuteOp(u32 op, u32 diff) {
|
||||
|
@ -574,11 +628,15 @@ void GPUCommon::ExecuteOp(u32 op, u32 diff) {
|
|||
break;
|
||||
|
||||
case GE_CMD_ORIGIN:
|
||||
gstate_c.offsetAddr = currentList->pc;
|
||||
{
|
||||
easy_guard guard(listLock);
|
||||
gstate_c.offsetAddr = currentList->pc;
|
||||
}
|
||||
break;
|
||||
|
||||
case GE_CMD_JUMP:
|
||||
{
|
||||
easy_guard guard(listLock);
|
||||
u32 target = gstate_c.getRelativeAddress(data);
|
||||
if (Memory::IsValidAddress(target)) {
|
||||
UpdatePC(currentList->pc, target - 4);
|
||||
|
@ -591,6 +649,7 @@ void GPUCommon::ExecuteOp(u32 op, u32 diff) {
|
|||
|
||||
case GE_CMD_CALL:
|
||||
{
|
||||
easy_guard guard(listLock);
|
||||
// Saint Seiya needs correct support for relative calls.
|
||||
u32 retval = currentList->pc + 4;
|
||||
u32 target = gstate_c.getRelativeAddress(data);
|
||||
|
@ -610,6 +669,7 @@ void GPUCommon::ExecuteOp(u32 op, u32 diff) {
|
|||
|
||||
case GE_CMD_RET:
|
||||
{
|
||||
easy_guard guard(listLock);
|
||||
if (currentList->stackptr == 0) {
|
||||
ERROR_LOG_REPORT(G3D, "RET: Stack empty!");
|
||||
} else {
|
||||
|
@ -632,6 +692,7 @@ void GPUCommon::ExecuteOp(u32 op, u32 diff) {
|
|||
break;
|
||||
|
||||
case GE_CMD_END: {
|
||||
easy_guard guard(listLock);
|
||||
u32 prev = Memory::ReadUnchecked_U32(currentList->pc - 4);
|
||||
UpdatePC(currentList->pc);
|
||||
switch (prev >> 24) {
|
||||
|
@ -767,6 +828,8 @@ void GPUCommon::ExecuteOp(u32 op, u32 diff) {
|
|||
}
|
||||
|
||||
void GPUCommon::DoState(PointerWrap &p) {
|
||||
easy_guard guard(listLock);
|
||||
|
||||
p.Do<int>(dlQueue);
|
||||
p.DoArray(dls, ARRAY_SIZE(dls));
|
||||
int currentID = 0;
|
||||
|
@ -790,12 +853,11 @@ void GPUCommon::DoState(PointerWrap &p) {
|
|||
p.DoMarker("GPUCommon");
|
||||
}
|
||||
|
||||
void GPUCommon::InterruptStart(int listid)
|
||||
{
|
||||
void GPUCommon::InterruptStart(int listid) {
|
||||
interruptRunning = true;
|
||||
}
|
||||
void GPUCommon::InterruptEnd(int listid)
|
||||
{
|
||||
void GPUCommon::InterruptEnd(int listid) {
|
||||
easy_guard guard(listLock);
|
||||
interruptRunning = false;
|
||||
isbreak = false;
|
||||
|
||||
|
@ -803,18 +865,19 @@ void GPUCommon::InterruptEnd(int listid)
|
|||
// TODO: Unless the signal handler could change it?
|
||||
if (dl.state == PSP_GE_DL_STATE_COMPLETED || dl.state == PSP_GE_DL_STATE_NONE) {
|
||||
dl.waitTicks = 0;
|
||||
__KernelTriggerWait(WAITTYPE_GELISTSYNC, listid, 0, "GeListSync", true);
|
||||
__GeTriggerWait(WAITTYPE_GELISTSYNC, listid, "GeListSync", true);
|
||||
}
|
||||
|
||||
if (dl.signal == PSP_GE_SIGNAL_HANDLER_PAUSE)
|
||||
dl.signal = PSP_GE_SIGNAL_HANDLER_SUSPEND;
|
||||
|
||||
guard.unlock();
|
||||
ProcessDLQueue();
|
||||
}
|
||||
|
||||
// TODO: Maybe cleaner to keep this in GE and trigger the clear directly?
|
||||
void GPUCommon::SyncEnd(WaitType waitType, int listid, bool wokeThreads)
|
||||
{
|
||||
void GPUCommon::SyncEnd(WaitType waitType, int listid, bool wokeThreads) {
|
||||
easy_guard guard(listLock);
|
||||
if (waitType == WAITTYPE_GEDRAWSYNC && wokeThreads)
|
||||
{
|
||||
for (int i = 0; i < DisplayListMaxCount; ++i) {
|
||||
|
|
|
@ -1,8 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
#include "GPUInterface.h"
|
||||
#include "Common/Common.h"
|
||||
#include "Core/ThreadEventQueue.h"
|
||||
#include "GPU/GPUInterface.h"
|
||||
|
||||
class GPUCommon : public GPUInterface
|
||||
#if defined(ANDROID)
|
||||
#include <atomic>
|
||||
#elif defined(_M_SSE)
|
||||
#include <xmmintrin.h>
|
||||
#endif
|
||||
|
||||
typedef ThreadEventQueue<GPUInterface, GPUEvent, GPUEventType, GPU_EVENT_INVALID, GPU_EVENT_SYNC_THREAD, GPU_EVENT_FINISH_EVENT_LOOP> GPUThreadEventQueue;
|
||||
|
||||
class GPUCommon : public GPUThreadEventQueue
|
||||
{
|
||||
public:
|
||||
GPUCommon();
|
||||
|
@ -25,11 +35,28 @@ public:
|
|||
virtual int ListSync(int listid, int mode);
|
||||
virtual u32 DrawSync(int mode);
|
||||
virtual void DoState(PointerWrap &p);
|
||||
virtual bool FramebufferDirty() { return true; }
|
||||
virtual bool FramebufferDirty() {
|
||||
SyncThread();
|
||||
return true;
|
||||
}
|
||||
virtual u32 Continue();
|
||||
virtual u32 Break(int mode);
|
||||
virtual void ReapplyGfxState();
|
||||
|
||||
virtual u64 GetTickEstimate() {
|
||||
#if defined(_M_X64) || defined(ANDROID)
|
||||
return curTickEst_;
|
||||
#elif defined(_M_SSE)
|
||||
__m64 result = *(__m64 *)&curTickEst_;
|
||||
u64 safeResult = *(u64 *)&result;
|
||||
_mm_empty();
|
||||
return safeResult;
|
||||
#else
|
||||
lock_guard guard(curTickEstLock_);
|
||||
return curTickEst_;
|
||||
#endif
|
||||
}
|
||||
|
||||
protected:
|
||||
// To avoid virtual calls to PreExecuteOp().
|
||||
virtual void FastRunLoop(DisplayList &list) = 0;
|
||||
|
@ -39,12 +66,31 @@ protected:
|
|||
void PopDLQueue();
|
||||
void CheckDrawSync();
|
||||
int GetNextListIndex();
|
||||
void ProcessDLQueueInternal();
|
||||
void ReapplyGfxStateInternal();
|
||||
virtual void ProcessEvent(GPUEvent ev);
|
||||
virtual bool ShouldExitEventLoop() {
|
||||
return coreState != CORE_RUNNING;
|
||||
}
|
||||
|
||||
// Allows early unlocking with a guard. Do not double unlock.
|
||||
class easy_guard {
|
||||
public:
|
||||
easy_guard(recursive_mutex &mtx) : mtx_(mtx), locked_(true) { mtx_.lock(); }
|
||||
~easy_guard() { if (locked_) mtx_.unlock(); }
|
||||
void unlock() { if (locked_) mtx_.unlock(); else Crash(); locked_ = false; }
|
||||
|
||||
private:
|
||||
recursive_mutex &mtx_;
|
||||
bool locked_;
|
||||
};
|
||||
|
||||
typedef std::list<int> DisplayListQueue;
|
||||
|
||||
DisplayList dls[DisplayListMaxCount];
|
||||
DisplayList *currentList;
|
||||
DisplayListQueue dlQueue;
|
||||
recursive_mutex listLock;
|
||||
|
||||
bool interruptRunning;
|
||||
GPUState gpuState;
|
||||
|
@ -61,6 +107,27 @@ protected:
|
|||
bool dumpThisFrame_;
|
||||
bool interruptsEnabled_;
|
||||
|
||||
// For CPU/GPU sync.
|
||||
#ifdef ANDROID
|
||||
std::atomic<u64> curTickEst_;
|
||||
#else
|
||||
volatile MEMORY_ALIGNED16(u64) curTickEst_;
|
||||
recursive_mutex curTickEstLock_;
|
||||
#endif
|
||||
|
||||
virtual void UpdateTickEstimate(u64 value) {
|
||||
#if defined(_M_X64) || defined(ANDROID)
|
||||
curTickEst_ = value;
|
||||
#elif defined(_M_SSE)
|
||||
__m64 result = *(__m64 *)&value;
|
||||
*(__m64 *)&curTickEst_ = result;
|
||||
_mm_empty();
|
||||
#else
|
||||
lock_guard guard(curTickEstLock_);
|
||||
curTickEst_ = value;
|
||||
#endif
|
||||
}
|
||||
|
||||
public:
|
||||
virtual DisplayList* getList(int listid)
|
||||
{
|
||||
|
|
|
@ -140,6 +140,35 @@ enum GPUInvalidationType {
|
|||
GPU_INVALIDATE_SAFE,
|
||||
};
|
||||
|
||||
enum GPUEventType {
|
||||
GPU_EVENT_INVALID,
|
||||
GPU_EVENT_PROCESS_QUEUE,
|
||||
GPU_EVENT_INIT_CLEAR,
|
||||
GPU_EVENT_BEGIN_FRAME,
|
||||
GPU_EVENT_COPY_DISPLAY_TO_OUTPUT,
|
||||
GPU_EVENT_REAPPLY_GFX_STATE,
|
||||
GPU_EVENT_INVALIDATE_CACHE,
|
||||
GPU_EVENT_FINISH_EVENT_LOOP,
|
||||
GPU_EVENT_SYNC_THREAD,
|
||||
};
|
||||
|
||||
struct GPUEvent {
|
||||
GPUEvent(GPUEventType t) : type(t) {}
|
||||
GPUEventType type;
|
||||
union {
|
||||
// GPU_EVENT_INVALIDATE_CACHE
|
||||
struct {
|
||||
u32 addr;
|
||||
int size;
|
||||
GPUInvalidationType type;
|
||||
} invalidate_cache;
|
||||
};
|
||||
|
||||
operator GPUEventType() const {
|
||||
return type;
|
||||
}
|
||||
};
|
||||
|
||||
class GPUInterface
|
||||
{
|
||||
public:
|
||||
|
@ -150,6 +179,9 @@ public:
|
|||
// Initialization
|
||||
virtual void InitClear() = 0;
|
||||
|
||||
virtual void RunEventsUntil(u64 globalticks) = 0;
|
||||
virtual void FinishEventLoop() = 0;
|
||||
|
||||
// Draw queue management
|
||||
virtual DisplayList* getList(int listid) = 0;
|
||||
// TODO: Much of this should probably be shared between the different GPU implementations.
|
||||
|
@ -190,8 +222,9 @@ public:
|
|||
virtual void EnableInterrupts(bool enable) = 0;
|
||||
|
||||
virtual void DeviceLost() = 0;
|
||||
virtual void Flush() = 0;
|
||||
virtual void ReapplyGfxState() = 0;
|
||||
virtual void SyncThread() = 0;
|
||||
virtual u64 GetTickEstimate() = 0;
|
||||
virtual void DoState(PointerWrap &p) = 0;
|
||||
|
||||
// Called by the window system if the window size changed. This will be reflected in PSPCoreParam.pixel*.
|
||||
|
|
|
@ -28,6 +28,25 @@ GPUStateCache gstate_c;
|
|||
GPUInterface *gpu;
|
||||
GPUStatistics gpuStats;
|
||||
|
||||
void GPU_Init() {
|
||||
switch (PSP_CoreParameter().gpuCore) {
|
||||
case GPU_NULL:
|
||||
gpu = new NullGPU();
|
||||
break;
|
||||
case GPU_GLES:
|
||||
gpu = new GLES_GPU();
|
||||
break;
|
||||
case GPU_SOFTWARE:
|
||||
gpu = new NullGPU();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GPU_Shutdown() {
|
||||
delete gpu;
|
||||
gpu = 0;
|
||||
}
|
||||
|
||||
void InitGfxState()
|
||||
{
|
||||
memset(&gstate, 0, sizeof(gstate));
|
||||
|
@ -56,24 +75,10 @@ void InitGfxState()
|
|||
for (int i = 0; i < 8; i++) {
|
||||
memcpy(gstate.boneMatrix + i * 12, identity4x3, 12 * sizeof(float));
|
||||
}
|
||||
|
||||
switch (PSP_CoreParameter().gpuCore) {
|
||||
case GPU_NULL:
|
||||
gpu = new NullGPU();
|
||||
break;
|
||||
case GPU_GLES:
|
||||
gpu = new GLES_GPU();
|
||||
break;
|
||||
case GPU_SOFTWARE:
|
||||
gpu = new NullGPU();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ShutdownGfxState()
|
||||
{
|
||||
delete gpu;
|
||||
gpu = NULL;
|
||||
}
|
||||
|
||||
// When you have changed state outside the psp gfx core,
|
||||
|
|
|
@ -446,6 +446,9 @@ struct GPUStatistics {
|
|||
int numFBOs;
|
||||
};
|
||||
|
||||
void GPU_Init();
|
||||
void GPU_Shutdown();
|
||||
|
||||
void InitGfxState();
|
||||
void ShutdownGfxState();
|
||||
void ReapplyGfxState();
|
||||
|
|
|
@ -896,9 +896,9 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer) {
|
|||
|
||||
case GE_CMD_TEXLEVEL:
|
||||
if (data & ~0xFF0003)
|
||||
sprintf(buffer, "TexWrap mode: %i Offset: %i (extra %x)", data&3, data >> 16, data);
|
||||
sprintf(buffer, "TexLevel mode: %i Offset: %i (extra %x)", data&3, data >> 16, data);
|
||||
else
|
||||
sprintf(buffer, "TexWrap mode: %i Offset: %i", data&3, data >> 16);
|
||||
sprintf(buffer, "TexLevel mode: %i Offset: %i", data&3, data >> 16);
|
||||
break;
|
||||
|
||||
case GE_CMD_FOG1:
|
||||
|
|
|
@ -37,7 +37,6 @@ public:
|
|||
virtual void InvalidateCache(u32 addr, int size, GPUInvalidationType type);
|
||||
virtual void UpdateMemory(u32 dest, u32 src, int size);
|
||||
virtual void ClearCacheNextFrame() {};
|
||||
virtual void Flush() {}
|
||||
|
||||
virtual void DeviceLost() {}
|
||||
virtual void DumpNextFrame() {}
|
||||
|
|
|
@ -58,7 +58,7 @@ public:
|
|||
std::string title; // for easy access, also available in paramSFO.
|
||||
std::string id;
|
||||
std::string id_version;
|
||||
EmuFileType fileType;
|
||||
IdentifiedFileType fileType;
|
||||
ParamSFOData paramSFO;
|
||||
bool paramSFOLoaded;
|
||||
|
||||
|
|
|
@ -100,6 +100,7 @@ void GameScreen::DrawBackground(UIContext &dc) {
|
|||
void GameScreen::update(InputState &input) {
|
||||
UIScreen::update(input);
|
||||
|
||||
I18NCategory *g = GetI18NCategory("General");
|
||||
GameInfo *info = g_gameInfoCache.GetInfo(gamePath_, true);
|
||||
|
||||
if (tvTitle_)
|
||||
|
@ -112,9 +113,9 @@ void GameScreen::update(InputState &input) {
|
|||
|
||||
if (info->gameSize) {
|
||||
char temp[256];
|
||||
sprintf(temp, "Game: %1.1f MB", (float)(info->gameSize) / 1024.f / 1024.f);
|
||||
sprintf(temp, "%s: %1.1f %s", g->T("Game"), (float) (info->gameSize) / 1024.f / 1024.f, g->T("MB"));
|
||||
tvGameSize_->SetText(temp);
|
||||
sprintf(temp, "SaveData: %1.2f MB", (float)(info->saveDataSize) / 1024.f / 1024.f);
|
||||
sprintf(temp, "%s: %1.2f %s", g->T("SaveData"), (float) (info->saveDataSize) / 1024.f / 1024.f, g->T("MB"));
|
||||
tvSaveDataSize_->SetText(temp);
|
||||
}
|
||||
}
|
||||
|
@ -139,10 +140,12 @@ UI::EventReturn GameScreen::OnGameSettings(UI::EventParams &e) {
|
|||
}
|
||||
|
||||
UI::EventReturn GameScreen::OnDeleteSaveData(UI::EventParams &e) {
|
||||
I18NCategory *d = GetI18NCategory("Dialog");
|
||||
I18NCategory *g = GetI18NCategory("General");
|
||||
GameInfo *info = g_gameInfoCache.GetInfo(gamePath_, true);
|
||||
if (info) {
|
||||
screenManager()->push(
|
||||
new PromptScreen("Do you really want to delete all\nyour save data for this game?", "Delete Savedata", "Cancel",
|
||||
new PromptScreen(d->T("DeleteConfirmAll", "Do you really want to delete all\nyour save data for this game?"), g->T("Delete Savedata"), g->T("Cancel"),
|
||||
std::bind(&GameScreen::CallbackDeleteSaveData, this, placeholder::_1)));
|
||||
}
|
||||
|
||||
|
@ -158,10 +161,12 @@ void GameScreen::CallbackDeleteSaveData(bool yes) {
|
|||
}
|
||||
|
||||
UI::EventReturn GameScreen::OnDeleteGame(UI::EventParams &e) {
|
||||
I18NCategory *d = GetI18NCategory("Dialog");
|
||||
I18NCategory *g = GetI18NCategory("General");
|
||||
GameInfo *info = g_gameInfoCache.GetInfo(gamePath_, true);
|
||||
if (info) {
|
||||
screenManager()->push(
|
||||
new PromptScreen("Do you really want to delete all\nthis game entirely? You can't undo this.", "Delete Game", "Cancel",
|
||||
new PromptScreen(d->T("DeleteGame", "Do you really want to delete all\nthis game entirely? You can't undo this."), g->T("Delete Game"), g->T("Cancel"),
|
||||
std::bind(&GameScreen::CallbackDeleteGame, this, placeholder::_1)));
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
// set game specific settings, etc.
|
||||
// Uses GameInfoCache heavily to implement the functionality.
|
||||
|
||||
class GameScreen : public UIScreen {
|
||||
class GameScreen : public DialogScreen {
|
||||
public:
|
||||
GameScreen(std::string gamePath) : gamePath_(gamePath) {}
|
||||
|
||||
|
|
|
@ -41,7 +41,6 @@ namespace MainWindow {
|
|||
}
|
||||
#endif
|
||||
|
||||
|
||||
namespace UI {
|
||||
|
||||
// Reads and writes value to determine the current selection.
|
||||
|
@ -157,7 +156,7 @@ EventReturn PopupSliderChoiceFloat::HandleClick(EventParams &e) {
|
|||
|
||||
void PopupSliderChoiceFloat::Draw(UIContext &dc) {
|
||||
Choice::Draw(dc);
|
||||
char temp[4];
|
||||
char temp[5];
|
||||
sprintf(temp, "%2.2f", *value_);
|
||||
dc.Draw()->DrawText(dc.theme->uiFont, temp, bounds_.x2() - 8, bounds_.centerY(), 0xFFFFFFFF, ALIGN_RIGHT | ALIGN_VCENTER);
|
||||
}
|
||||
|
@ -180,6 +179,7 @@ void GameSettingsScreen::CreateViews() {
|
|||
I18NCategory *c = GetI18NCategory("Controls");
|
||||
I18NCategory *a = GetI18NCategory("Audio");
|
||||
I18NCategory *s = GetI18NCategory("System");
|
||||
I18NCategory *ms = GetI18NCategory("MainSettings");
|
||||
|
||||
Margins actionMenuMargins(0, 0, 15, 0);
|
||||
|
||||
|
@ -189,9 +189,9 @@ void GameSettingsScreen::CreateViews() {
|
|||
root_->Add(leftColumn);
|
||||
|
||||
leftColumn->Add(new Spacer(new LinearLayoutParams(1.0)));
|
||||
leftColumn->Add(new Choice(g->T("Back"), "", false, new AnchorLayoutParams(205, WRAP_CONTENT, 30, NONE, NONE, 10)))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
|
||||
leftColumn->Add(new Choice(g->T("Back"), "", false, new AnchorLayoutParams(150, WRAP_CONTENT, 10, NONE, NONE, 10)))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
|
||||
|
||||
TabHolder *tabHolder = new TabHolder(ORIENT_VERTICAL, 200, new LinearLayoutParams(910, FILL_PARENT, actionMenuMargins));
|
||||
TabHolder *tabHolder = new TabHolder(ORIENT_VERTICAL, 200, new LinearLayoutParams(800, FILL_PARENT, actionMenuMargins));
|
||||
|
||||
root_->Add(tabHolder);
|
||||
|
||||
|
@ -201,7 +201,7 @@ void GameSettingsScreen::CreateViews() {
|
|||
ViewGroup *graphicsSettingsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT));
|
||||
ViewGroup *graphicsSettings = new LinearLayout(ORIENT_VERTICAL);
|
||||
graphicsSettingsScroll->Add(graphicsSettings);
|
||||
tabHolder->AddTab("Graphics", graphicsSettingsScroll);
|
||||
tabHolder->AddTab(ms->T("Graphics"), graphicsSettingsScroll);
|
||||
|
||||
graphicsSettings->Add(new ItemHeader(gs->T("Rendering Mode")));
|
||||
#ifndef USING_GLES2
|
||||
|
@ -251,7 +251,7 @@ void GameSettingsScreen::CreateViews() {
|
|||
ViewGroup *audioSettingsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT));
|
||||
ViewGroup *audioSettings = new LinearLayout(ORIENT_VERTICAL);
|
||||
audioSettingsScroll->Add(audioSettings);
|
||||
tabHolder->AddTab("Audio", audioSettingsScroll);
|
||||
tabHolder->AddTab(ms->T("Audio"), audioSettingsScroll);
|
||||
|
||||
std::string atracString;
|
||||
atracString.assign(Atrac3plus_Decoder::IsInstalled() ? "Redownload Atrac3+ plugin" : "Download Atrac3+ plugin");
|
||||
|
@ -266,19 +266,21 @@ void GameSettingsScreen::CreateViews() {
|
|||
ViewGroup *controlsSettingsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT));
|
||||
ViewGroup *controlsSettings = new LinearLayout(ORIENT_VERTICAL);
|
||||
controlsSettingsScroll->Add(controlsSettings);
|
||||
tabHolder->AddTab("Controls", controlsSettingsScroll);
|
||||
tabHolder->AddTab(ms->T("Controls"), controlsSettingsScroll);
|
||||
controlsSettings->Add(new CheckBox(&g_Config.bShowTouchControls, c->T("OnScreen", "On-Screen Touch Controls")));
|
||||
controlsSettings->Add(new CheckBox(&g_Config.bShowAnalogStick, c->T("Show Left Analog Stick")));
|
||||
controlsSettings->Add(new CheckBox(&g_Config.bAccelerometerToAnalogHoriz, c->T("Tilt", "Tilt to Analog (horizontal)")));
|
||||
controlsSettings->Add(new Choice(gs->T("Control Mapping")))->OnClick.Handle(this, &GameSettingsScreen::OnControlMapping);
|
||||
controlsSettings->Add(new PopupSliderChoice(&g_Config.iTouchButtonOpacity, 15, 65, c->T("Button Opacity"), screenManager()));
|
||||
controlsSettings->Add(new PopupSliderChoice(&g_Config.iTouchButtonOpacity, 0, 85, c->T("Button Opacity"), screenManager()));
|
||||
controlsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fButtonScale, 1.15, 2.05, c->T("Button Scaling"), screenManager()));
|
||||
|
||||
// System
|
||||
ViewGroup *systemSettingsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT));
|
||||
ViewGroup *systemSettings = new LinearLayout(ORIENT_VERTICAL);
|
||||
systemSettingsScroll->Add(systemSettings);
|
||||
tabHolder->AddTab("System", systemSettingsScroll);
|
||||
tabHolder->AddTab(ms->T("System"), systemSettingsScroll);
|
||||
systemSettings->Add(new CheckBox(&g_Config.bJit, s->T("Dynarec", "Dynarec (JIT)")));
|
||||
systemSettings->Add(new CheckBox(&g_Config.bSeparateCPUThread, s->T("Multithreaded (experimental)")));
|
||||
systemSettings->Add(new CheckBox(&g_Config.bFastMemory, s->T("Fast Memory", "Fast Memory (Unstable)")));
|
||||
systemSettings->Add(new PopupSliderChoice(&g_Config.iLockedCPUSpeed, 0, 1000, gs->T("Unlock CPU Clock"), screenManager()));
|
||||
systemSettings->Add(new CheckBox(&g_Config.bDayLightSavings, s->T("Day Light Saving")));
|
||||
|
@ -319,6 +321,7 @@ void GameSettingsScreen::DrawBackground(UIContext &dc) {
|
|||
dc.RebindTexture();
|
||||
}*/
|
||||
}
|
||||
|
||||
void GameSettingsScreen::update(InputState &input) {
|
||||
UIScreen::update(input);
|
||||
g_Config.iForceMaxEmulatedFPS = cap60FPS_ ? 60 : 0;
|
||||
|
@ -334,11 +337,12 @@ void GlobalSettingsScreen::CreateViews() {
|
|||
I18NCategory *gs = GetI18NCategory("Graphics");
|
||||
|
||||
LinearLayout *list = root_->Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(1.0f)));
|
||||
list->Add(new ItemHeader("General"));
|
||||
list->Add(new ItemHeader(g->T("General")));
|
||||
list->Add(new CheckBox(&g_Config.bNewUI, gs->T("Enable New UI")));
|
||||
list->Add(new CheckBox(&enableReports_, gs->T("Enable Compatibility Server Reports")));
|
||||
list->Add(new CheckBox(&g_Config.bEnableCheats, gs->T("Enable Cheats")));
|
||||
list->Add(new CheckBox(&g_Config.bScreenshotsAsPNG, gs->T("Screenshots as PNG")));
|
||||
list->Add(new CheckBox(&g_Config.bDirectLoad, gs->T("Enable Direct Load")));
|
||||
list->Add(new Choice(gs->T("System Language")))->OnClick.Handle(this, &GlobalSettingsScreen::OnLanguage);
|
||||
list->Add(new Choice(gs->T("Developer Tools")))->OnClick.Handle(this, &GlobalSettingsScreen::OnDeveloperTools);
|
||||
list->Add(new Choice(g->T("Back")))->OnClick.Handle(this, &GlobalSettingsScreen::OnBack);
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
// Per-game settings screen - enables you to configure graphic options, control options, etc
|
||||
// per game.
|
||||
class GameSettingsScreen : public UIScreenWithBackground {
|
||||
class GameSettingsScreen : public UIDialogScreenWithBackground {
|
||||
public:
|
||||
GameSettingsScreen(std::string gamePath, std::string gameID = "") : gamePath_(gamePath), gameID_(gameID) {}
|
||||
|
||||
|
@ -48,7 +48,7 @@ private:
|
|||
};
|
||||
|
||||
// TODO: Move to its own file.
|
||||
class GlobalSettingsScreen : public UIScreenWithBackground {
|
||||
class GlobalSettingsScreen : public UIDialogScreenWithBackground {
|
||||
public:
|
||||
GlobalSettingsScreen() {}
|
||||
|
||||
|
@ -66,7 +66,7 @@ private:
|
|||
bool enableReports_;
|
||||
};
|
||||
|
||||
class DeveloperToolsScreen : public UIScreenWithBackground {
|
||||
class DeveloperToolsScreen : public UIDialogScreenWithBackground {
|
||||
public:
|
||||
DeveloperToolsScreen() {}
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ void MultiTouchButton::Draw(UIContext &dc) {
|
|||
float scale = scale_;
|
||||
if (IsDown()) {
|
||||
scale *= 2.0f;
|
||||
opacity = 100.0f;
|
||||
opacity *= 1.15f;
|
||||
}
|
||||
uint32_t colorBg = colorAlpha(0xc0b080, opacity);
|
||||
uint32_t color = colorAlpha(0xFFFFFF, opacity);
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "UI/ui_atlas.h"
|
||||
#include "Core/Config.h"
|
||||
#include "GPU/GPUInterface.h"
|
||||
#include "i18n/i18n.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
namespace MainWindow {
|
||||
|
@ -62,6 +63,8 @@ public:
|
|||
virtual void Update(const InputState &input_state) {
|
||||
if (down_)
|
||||
holdFrameCount_++;
|
||||
else
|
||||
holdFrameCount_ = 0;
|
||||
// Hold button for 1.5 seconds to launch the game directly
|
||||
if (holdFrameCount_ > 90) {
|
||||
UI::EventParams e;
|
||||
|
@ -114,14 +117,16 @@ void GameButton::Draw(UIContext &dc) {
|
|||
// Render button
|
||||
int dropsize = 10;
|
||||
if (texture) {
|
||||
if (txOffset) {
|
||||
dropsize = 3;
|
||||
y += txOffset * 2;
|
||||
}
|
||||
if (HasFocus()) {
|
||||
// dc.Draw()->DrawImage4Grid(I_DROP_SHADOW, x - dropsize, y, x+w + dropsize, y+h+dropsize*1.5, alphaMul(color, 0.5f), 1.0f);
|
||||
// dc.Draw()->Flush();
|
||||
} else {
|
||||
if (txOffset) {
|
||||
dropsize = 3;
|
||||
y += txOffset * 2;
|
||||
}
|
||||
dc.Draw()->Flush();
|
||||
dc.RebindTexture();
|
||||
dc.Draw()->DrawImage4Grid(I_DROP_SHADOW, x - dropsize, y, x+w + dropsize, y+h+dropsize*1.5, alphaMul(shadowColor, 0.5f), 1.0f);
|
||||
dc.Draw()->Flush();
|
||||
}
|
||||
|
@ -336,6 +341,8 @@ void MainScreen::CreateViews() {
|
|||
// Scrolling action menu to the right.
|
||||
using namespace UI;
|
||||
|
||||
I18NCategory *m = GetI18NCategory("MainMenu");
|
||||
|
||||
Margins actionMenuMargins(0, 100, 15, 0);
|
||||
|
||||
root_ = new LinearLayout(ORIENT_HORIZONTAL);
|
||||
|
@ -355,9 +362,9 @@ void MainScreen::CreateViews() {
|
|||
scrollAllGames->Add(tabAllGames);
|
||||
scrollHomebrew->Add(tabHomebrew);
|
||||
|
||||
leftColumn->AddTab("Recent", scrollRecentGames);
|
||||
leftColumn->AddTab("Games", scrollAllGames);
|
||||
leftColumn->AddTab("Homebrew & Demos", scrollHomebrew);
|
||||
leftColumn->AddTab(m->T("Recent"), scrollRecentGames);
|
||||
leftColumn->AddTab(m->T("Games"), scrollAllGames);
|
||||
leftColumn->AddTab(m->T("Homebrew & Demos"), scrollHomebrew);
|
||||
|
||||
tabRecentGames->OnChoice.Handle(this, &MainScreen::OnGameSelected);
|
||||
tabAllGames->OnChoice.Handle(this, &MainScreen::OnGameSelected);
|
||||
|
@ -382,12 +389,12 @@ void MainScreen::CreateViews() {
|
|||
rightColumn->Add(rightColumnItems);
|
||||
|
||||
#ifdef _WIN32
|
||||
rightColumnItems->Add(new Choice("Load..."))->OnClick.Handle(this, &MainScreen::OnLoadFile);
|
||||
rightColumnItems->Add(new Choice(m->T("Load","Load...")))->OnClick.Handle(this, &MainScreen::OnLoadFile);
|
||||
#endif
|
||||
rightColumnItems->Add(new Choice("Settings"))->OnClick.Handle(this, &MainScreen::OnSettings);
|
||||
rightColumnItems->Add(new Choice("Exit"))->OnClick.Handle(this, &MainScreen::OnExit);
|
||||
rightColumnItems->Add(new Choice("Credits"))->OnClick.Handle(this, &MainScreen::OnCredits);
|
||||
rightColumnItems->Add(new Choice("Support PPSSPP"))->OnClick.Handle(this, &MainScreen::OnSupport);
|
||||
rightColumnItems->Add(new Choice(m->T("Settings")))->OnClick.Handle(this, &MainScreen::OnSettings);
|
||||
rightColumnItems->Add(new Choice(m->T("Exit")))->OnClick.Handle(this, &MainScreen::OnExit);
|
||||
rightColumnItems->Add(new Choice(m->T("Credits")))->OnClick.Handle(this, &MainScreen::OnCredits);
|
||||
rightColumnItems->Add(new Choice(m->T("Support PPSSPP")))->OnClick.Handle(this, &MainScreen::OnSupport);
|
||||
}
|
||||
|
||||
void MainScreen::sendMessage(const char *message, const char *value) {
|
||||
|
@ -477,6 +484,8 @@ GamePauseScreen::~GamePauseScreen() {
|
|||
void GamePauseScreen::CreateViews() {
|
||||
using namespace UI;
|
||||
Margins actionMenuMargins(0, 100, 15, 0);
|
||||
I18NCategory *gs = GetI18NCategory("Graphics");
|
||||
I18NCategory *i = GetI18NCategory("Pause");
|
||||
|
||||
root_ = new LinearLayout(ORIENT_HORIZONTAL);
|
||||
|
||||
|
@ -495,8 +504,8 @@ void GamePauseScreen::CreateViews() {
|
|||
saveSlots_->AddChoice("4");
|
||||
saveSlots_->SetSelection(g_Config.iCurrentStateSlot);
|
||||
|
||||
leftColumnItems->Add(new Choice("Save State"))->OnClick.Handle(this, &GamePauseScreen::OnSaveState);
|
||||
leftColumnItems->Add(new Choice("Load State"))->OnClick.Handle(this, &GamePauseScreen::OnLoadState);
|
||||
leftColumnItems->Add(new Choice(gs->T("Save State")))->OnClick.Handle(this, &GamePauseScreen::OnSaveState);
|
||||
leftColumnItems->Add(new Choice(gs->T("Load State")))->OnClick.Handle(this, &GamePauseScreen::OnLoadState);
|
||||
|
||||
ViewGroup *rightColumn = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(300, FILL_PARENT, actionMenuMargins));
|
||||
root_->Add(rightColumn);
|
||||
|
@ -504,10 +513,10 @@ void GamePauseScreen::CreateViews() {
|
|||
ViewGroup *rightColumnItems = new LinearLayout(ORIENT_VERTICAL);
|
||||
rightColumn->Add(rightColumnItems);
|
||||
|
||||
rightColumnItems->Add(new Choice("Continue"))->OnClick.Handle(this, &GamePauseScreen::OnContinue);
|
||||
rightColumnItems->Add(new Choice("Game Settings"))->OnClick.Handle(this, &GamePauseScreen::OnGameSettings);
|
||||
rightColumnItems->Add(new Choice("Main Settings"))->OnClick.Handle(this, &GamePauseScreen::OnMainSettings);
|
||||
rightColumnItems->Add(new Choice("Exit to menu"))->OnClick.Handle(this, &GamePauseScreen::OnExitToMenu);
|
||||
rightColumnItems->Add(new Choice(i->T("Continue")))->OnClick.Handle(this, &GamePauseScreen::OnContinue);
|
||||
rightColumnItems->Add(new Choice(i->T("Game Settings")))->OnClick.Handle(this, &GamePauseScreen::OnGameSettings);
|
||||
rightColumnItems->Add(new Choice(i->T("Main Settings")))->OnClick.Handle(this, &GamePauseScreen::OnMainSettings);
|
||||
rightColumnItems->Add(new Choice(i->T("Exit to menu")))->OnClick.Handle(this, &GamePauseScreen::OnExitToMenu);
|
||||
}
|
||||
|
||||
UI::EventReturn GamePauseScreen::OnMainSettings(UI::EventParams &e) {
|
||||
|
|
|
@ -253,6 +253,7 @@ void MenuScreen::render() {
|
|||
VLinear vlinear(dp_xres + xoff, 100, 20);
|
||||
|
||||
I18NCategory *m = GetI18NCategory("MainMenu");
|
||||
I18NCategory *p = GetI18NCategory("Plugin");
|
||||
|
||||
if (UIButton(GEN_ID, vlinear, w, 0, m->T("Load", "Load..."), ALIGN_RIGHT)) {
|
||||
#if defined(USING_QT_UI) && !defined(MEEGO_EDITION_HARMATTAN)
|
||||
|
@ -312,7 +313,7 @@ void MenuScreen::render() {
|
|||
}
|
||||
|
||||
if (showAtracShortcut_) {
|
||||
if (UIButton(GEN_ID, Pos(10,dp_yres - 10), 500, 50, "Download audio plugin", ALIGN_BOTTOMLEFT)) {
|
||||
if (UIButton(GEN_ID, Pos(10, dp_yres - 10), 500, 50, p->T("Download audio plugin"), ALIGN_BOTTOMLEFT)) {
|
||||
screenManager()->push(new PluginScreen());
|
||||
}
|
||||
}
|
||||
|
@ -844,6 +845,7 @@ void AudioScreen::render() {
|
|||
|
||||
I18NCategory *g = GetI18NCategory("General");
|
||||
I18NCategory *a = GetI18NCategory("Audio");
|
||||
I18NCategory *p = GetI18NCategory("Plugin");
|
||||
|
||||
ui_draw2d.SetFontScale(1.5f, 1.5f);
|
||||
ui_draw2d.DrawTextShadow(UBUNTU24, a->T("Audio Settings"), dp_xres / 2, 10, 0xFFFFFFFF, ALIGN_HCENTER);
|
||||
|
@ -866,7 +868,7 @@ void AudioScreen::render() {
|
|||
// Show the download button even if not installed - might want to upgrade.
|
||||
VLinear vlinear(30, 400, 20);
|
||||
std::string atracString;
|
||||
atracString.assign(Atrac3plus_Decoder::IsInstalled() ? "Redownload Atrac3+ plugin" : "Download Atrac3+ plugin");
|
||||
atracString.assign(Atrac3plus_Decoder::IsInstalled() ? p->T("Redownload Atrac3+ plugin") : p->T("Download Atrac3+ plugin"));
|
||||
if (UIButton(GEN_ID, vlinear, 400, 0, a->T(atracString.c_str()), ALIGN_LEFT)) {
|
||||
screenManager()->push(new PluginScreen());
|
||||
}
|
||||
|
@ -1436,6 +1438,8 @@ void SystemScreen::render() {
|
|||
if (g_Config.bJit)
|
||||
UICheckBox(GEN_ID, x, y += stride, s->T("Fast Memory", "Fast Memory (unstable)"), ALIGN_TOPLEFT, &g_Config.bFastMemory);
|
||||
|
||||
UICheckBox(GEN_ID, x, y += stride, s->T("Multithreaded (experimental)"), ALIGN_TOPLEFT, &g_Config.bSeparateCPUThread);
|
||||
|
||||
bool LockCPUSpeed = g_Config.iLockedCPUSpeed != 0;
|
||||
UICheckBox(GEN_ID, x, y += stride, s->T("Unlock CPU Clock"), ALIGN_TOPLEFT, &LockCPUSpeed);
|
||||
if(LockCPUSpeed) {
|
||||
|
@ -1542,7 +1546,7 @@ void SystemScreen::render() {
|
|||
char nickname[512];
|
||||
memset(nickname, 0, sizeof(nickname));
|
||||
|
||||
sprintf(nickname, "%s %s", s->T("System Nickname: "), g_Config.sNickName.c_str());
|
||||
sprintf(nickname, "%s %s", s->T("System Nickname:"), g_Config.sNickName.c_str());
|
||||
ui_draw2d.DrawTextShadow(UBUNTU24, nickname, x, y += stride, 0xFFFFFFFF, ALIGN_LEFT);
|
||||
|
||||
HLinear hlinearNick(x + 400, y, 10);
|
||||
|
@ -1804,7 +1808,7 @@ void KeyMappingScreen::render() {
|
|||
char temp[256];
|
||||
sprintf(temp, "%s (%i/%i)", controllerMaps[currentMap_].name.c_str(), currentMap_ + 1, (int)controllerMaps.size());
|
||||
UIText(0, Pos(10, dp_yres-170), temp, 0xFFFFFFFF, 1.0f, ALIGN_BOTTOMLEFT);
|
||||
UICheckBox(GEN_ID,10, dp_yres - 80, "Mapping Active", ALIGN_BOTTOMLEFT, &controllerMaps[currentMap_].active);
|
||||
UICheckBox(GEN_ID,10, dp_yres - 80, keyI18N->T("Mapping Active"), ALIGN_BOTTOMLEFT, &controllerMaps[currentMap_].active);
|
||||
UIEnd();
|
||||
}
|
||||
|
||||
|
@ -1837,7 +1841,7 @@ void KeyMappingNewKeyDialog::render() {
|
|||
int left = 10;
|
||||
int stride = 70;
|
||||
KeyScale(1.6f);
|
||||
KeyText(left, top, keyI18N->T("Map a new key for button: "));
|
||||
KeyText(left, top, keyI18N->T("Map a new key for button:"));
|
||||
KeyText(left, top += stride, (KeyMap::GetPspButtonName(this->pspBtn)).c_str());
|
||||
KeyScale(1.3f);
|
||||
KeyText(left, top += stride, keyI18N->T("Current key"));
|
||||
|
|
|
@ -34,6 +34,11 @@ void UIScreenWithBackground::DrawBackground(UIContext &dc) {
|
|||
dc.Flush();
|
||||
}
|
||||
|
||||
void UIDialogScreenWithBackground::DrawBackground(UIContext &dc) {
|
||||
::DrawBackground(1.0f);
|
||||
dc.Flush();
|
||||
}
|
||||
|
||||
void PromptScreen::CreateViews() {
|
||||
// Information in the top left.
|
||||
// Back button to the bottom left.
|
||||
|
@ -108,6 +113,7 @@ NewLanguageScreen::NewLanguageScreen() : ListPopupScreen("Language") {
|
|||
VFSGetFileListing("lang", &langs_, "ini");
|
||||
#endif
|
||||
std::vector<std::string> listing;
|
||||
int selected = -1;
|
||||
for (size_t i = 0; i < langs_.size(); i++) {
|
||||
// Skip README
|
||||
if (langs_[i].name.find("README") != std::string::npos) {
|
||||
|
@ -129,10 +135,12 @@ NewLanguageScreen::NewLanguageScreen() : ListPopupScreen("Language") {
|
|||
buttonTitle = langValuesMapping[code].first;
|
||||
}
|
||||
}
|
||||
if (g_Config.languageIni == code)
|
||||
selected = i;
|
||||
listing.push_back(buttonTitle);
|
||||
}
|
||||
|
||||
adaptor_ = UI::StringVectorListAdaptor(listing, 0);
|
||||
adaptor_ = UI::StringVectorListAdaptor(listing, selected);
|
||||
}
|
||||
|
||||
void NewLanguageScreen::OnCompleted() {
|
||||
|
|
|
@ -34,8 +34,14 @@ protected:
|
|||
virtual void DrawBackground(UIContext &dc);
|
||||
};
|
||||
|
||||
class UIDialogScreenWithBackground : public DialogScreen {
|
||||
public:
|
||||
UIDialogScreenWithBackground() : DialogScreen() {}
|
||||
protected:
|
||||
virtual void DrawBackground(UIContext &dc);
|
||||
};
|
||||
|
||||
class PromptScreen : public UIScreenWithBackground {
|
||||
class PromptScreen : public UIDialogScreenWithBackground {
|
||||
public:
|
||||
PromptScreen(std::string message, std::string yesButtonText, std::string noButtonText)
|
||||
: message_(message), yesButtonText_(yesButtonText), noButtonText_(noButtonText) {
|
||||
|
|
|
@ -48,7 +48,7 @@ void PluginScreen::CreateViews() {
|
|||
Margins textMargins(20,17);
|
||||
Margins buttonMargins(10,10);
|
||||
|
||||
root_->Add(new TextView(UBUNTU24, "Atrac3+ Audio Support", ALIGN_HCENTER, 1.5f, new LinearLayoutParams(textMargins)));
|
||||
root_->Add(new TextView(UBUNTU24, p->T("Atrac3+ Audio Support"), ALIGN_HCENTER, 1.5f, new LinearLayoutParams(textMargins)));
|
||||
|
||||
ViewGroup *scroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0));
|
||||
LinearLayout *scrollContents = new LinearLayout(ORIENT_VERTICAL);
|
||||
|
|
|
@ -9,7 +9,7 @@ namespace DSound
|
|||
#define MAXWAIT 20 //ms
|
||||
|
||||
CRITICAL_SECTION soundCriticalSection;
|
||||
HANDLE soundSyncEvent;
|
||||
HANDLE soundSyncEvent = NULL;
|
||||
HANDLE hThread;
|
||||
|
||||
StreamCallback callback;
|
||||
|
@ -183,7 +183,8 @@ namespace DSound
|
|||
|
||||
void DSound_UpdateSound()
|
||||
{
|
||||
SetEvent(soundSyncEvent);
|
||||
if (soundSyncEvent != NULL)
|
||||
SetEvent(soundSyncEvent);
|
||||
}
|
||||
|
||||
|
||||
|
@ -201,6 +202,7 @@ namespace DSound
|
|||
ds->Release();
|
||||
|
||||
CloseHandle(soundSyncEvent);
|
||||
soundSyncEvent = NULL;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -847,6 +847,7 @@ void CtrlDisAsmView::updateStatusBarText()
|
|||
sprintf(text,"[%08X] = %04X",info.dataAddress,Memory::Read_U16(info.dataAddress));
|
||||
break;
|
||||
case 4:
|
||||
// TODO: Could also be a float...
|
||||
{
|
||||
u32 data = Memory::Read_U32(info.dataAddress);
|
||||
const char* addressSymbol = debugger->findSymbolForAddress(data);
|
||||
|
@ -858,6 +859,9 @@ void CtrlDisAsmView::updateStatusBarText()
|
|||
}
|
||||
break;
|
||||
}
|
||||
case 16:
|
||||
// TODO: vector
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -138,10 +138,17 @@ CDisasm::CDisasm(HINSTANCE _hInstance, HWND _hParent, DebugInterface *_cpu) : Di
|
|||
threadList->setDialogItem(GetDlgItem(m_hDlg,IDC_THREADLIST));
|
||||
threadList->reloadThreads();
|
||||
|
||||
stackTraceView = new CtrlStackTraceView();
|
||||
stackTraceView->setCpu(cpu);
|
||||
stackTraceView->setDisasm(ptr);
|
||||
stackTraceView->setDialogItem(GetDlgItem(m_hDlg,IDC_STACKFRAMES));
|
||||
stackTraceView->loadStackTrace();
|
||||
|
||||
// init memory/breakpoint "tab"
|
||||
ShowWindow(GetDlgItem(m_hDlg, IDC_BREAKPOINTLIST), SW_HIDE);
|
||||
ShowWindow(GetDlgItem(m_hDlg, IDC_DEBUGMEMVIEW), SW_NORMAL);
|
||||
ShowWindow(GetDlgItem(m_hDlg, IDC_THREADLIST), SW_HIDE);
|
||||
ShowWindow(GetDlgItem(m_hDlg, IDC_STACKFRAMES), SW_HIDE);
|
||||
|
||||
// init status bar
|
||||
statusBarWnd = CreateStatusWindow(WS_CHILD | WS_VISIBLE, "", m_hDlg, IDC_DISASMSTATUSBAR);
|
||||
|
@ -206,6 +213,9 @@ BOOL CDisasm::DlgProc(UINT message, WPARAM wParam, LPARAM lParam)
|
|||
case IDC_THREADLIST:
|
||||
threadList->handleNotify(lParam);
|
||||
break;
|
||||
case IDC_STACKFRAMES:
|
||||
stackTraceView->handleNotify(lParam);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case WM_COMMAND:
|
||||
|
@ -283,6 +293,7 @@ BOOL CDisasm::DlgProc(UINT message, WPARAM wParam, LPARAM lParam)
|
|||
|
||||
CtrlMemView::getFrom(GetDlgItem(m_hDlg,IDC_DEBUGMEMVIEW))->redraw();
|
||||
threadList->reloadThreads();
|
||||
stackTraceView->loadStackTrace();
|
||||
updateThreadLabel(false);
|
||||
}
|
||||
break;
|
||||
|
@ -504,23 +515,34 @@ BOOL CDisasm::DlgProc(UINT message, WPARAM wParam, LPARAM lParam)
|
|||
HWND bp = GetDlgItem(m_hDlg, IDC_BREAKPOINTLIST);
|
||||
HWND mem = GetDlgItem(m_hDlg, IDC_DEBUGMEMVIEW);
|
||||
HWND threads = GetDlgItem(m_hDlg, IDC_THREADLIST);
|
||||
|
||||
HWND stackFrames = GetDlgItem(m_hDlg, IDC_STACKFRAMES);
|
||||
|
||||
if (IsWindowVisible(bp))
|
||||
{
|
||||
ShowWindow(bp,SW_HIDE);
|
||||
ShowWindow(mem,SW_HIDE);
|
||||
ShowWindow(bp,SW_HIDE);
|
||||
ShowWindow(threads,SW_NORMAL);
|
||||
ShowWindow(stackFrames,SW_HIDE);
|
||||
SetFocus(threads);
|
||||
} else if (IsWindowVisible(threads))
|
||||
{
|
||||
ShowWindow(mem,SW_HIDE);
|
||||
ShowWindow(bp,SW_HIDE);
|
||||
ShowWindow(mem,SW_NORMAL);
|
||||
ShowWindow(threads,SW_HIDE);
|
||||
ShowWindow(stackFrames,SW_NORMAL);
|
||||
SetFocus(stackFrames);
|
||||
} else if (IsWindowVisible(stackFrames))
|
||||
{
|
||||
ShowWindow(mem,SW_NORMAL);
|
||||
ShowWindow(bp,SW_HIDE);
|
||||
ShowWindow(threads,SW_HIDE);
|
||||
ShowWindow(stackFrames,SW_HIDE);
|
||||
SetFocus(mem);
|
||||
} else {
|
||||
ShowWindow(bp,SW_NORMAL);
|
||||
ShowWindow(mem,SW_HIDE);
|
||||
ShowWindow(bp,SW_NORMAL);
|
||||
ShowWindow(threads,SW_HIDE);
|
||||
ShowWindow(stackFrames,SW_HIDE);
|
||||
SetFocus(bp);
|
||||
}
|
||||
}
|
||||
|
@ -590,6 +612,7 @@ void CDisasm::UpdateSize(WORD width, WORD height)
|
|||
HWND breakpointList = GetDlgItem(m_hDlg, IDC_BREAKPOINTLIST);
|
||||
HWND memView = GetDlgItem(m_hDlg, IDC_DEBUGMEMVIEW);
|
||||
HWND threads = GetDlgItem(m_hDlg, IDC_THREADLIST);
|
||||
HWND stackFrame = GetDlgItem(m_hDlg,IDC_STACKFRAMES);
|
||||
|
||||
if (g_Config.bDisplayStatusBar)
|
||||
{
|
||||
|
@ -617,6 +640,7 @@ void CDisasm::UpdateSize(WORD width, WORD height)
|
|||
MoveWindow(breakpointList,8,breakpointTop,width-16,breakpointHeight,TRUE);
|
||||
MoveWindow(memView,8,breakpointTop,width-16,breakpointHeight,TRUE);
|
||||
MoveWindow(threads,8,breakpointTop,width-16,breakpointHeight,TRUE);
|
||||
MoveWindow(stackFrame,8,breakpointTop,width-16,breakpointHeight,TRUE);
|
||||
|
||||
GetWindowRect(GetDlgItem(m_hDlg, IDC_REGLIST),®Rect);
|
||||
GetWindowRect(GetDlgItem(m_hDlg, IDC_DISASMVIEW),&disRect);
|
||||
|
@ -646,6 +670,7 @@ void CDisasm::SetDebugMode(bool _bDebug)
|
|||
CBreakPoints::ClearTemporaryBreakPoints();
|
||||
breakpointList->update();
|
||||
threadList->reloadThreads();
|
||||
stackTraceView->loadStackTrace();
|
||||
updateThreadLabel(false);
|
||||
|
||||
EnableWindow( GetDlgItem(hDlg, IDC_GO), TRUE);
|
||||
|
|
|
@ -30,6 +30,7 @@ private:
|
|||
HWND statusBarWnd;
|
||||
CtrlBreakpointList* breakpointList;
|
||||
CtrlThreadList* threadList;
|
||||
CtrlStackTraceView* stackTraceView;
|
||||
std::vector<BreakPoint> displayedBreakPoints_;
|
||||
std::vector<MemCheck> displayedMemChecks_;
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "Windows/resource.h"
|
||||
#include "Windows/main.h"
|
||||
#include "BreakpointWindow.h"
|
||||
#include "../../Core/HLE/sceKernelThread.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
|
@ -16,6 +17,7 @@ typedef struct
|
|||
|
||||
enum { TL_NAME, TL_PROGRAMCOUNTER, TL_ENTRYPOINT, TL_PRIORITY, TL_STATE, TL_WAITTYPE, TL_COLUMNCOUNT };
|
||||
enum { BPL_TYPE, BPL_OFFSET, BPL_SIZELABEL, BPL_OPCODE, BPL_CONDITION, BPL_HITS, BPL_ENABLED, BPL_COLUMNCOUNT };
|
||||
enum { SF_ENTRY, SF_ENTRYNAME, SF_CURPC, SF_CUROPCODE, SF_CURSP, SF_FRAMESIZE, SF_COLUMNCOUNT };
|
||||
|
||||
ListViewColumn threadColumns[TL_COLUMNCOUNT] = {
|
||||
{ "Name", 0.20f },
|
||||
|
@ -36,6 +38,15 @@ ListViewColumn breakpointColumns[BPL_COLUMNCOUNT] = {
|
|||
{ "Enabled", 0.08f }
|
||||
};
|
||||
|
||||
ListViewColumn stackTraceColumns[SF_COLUMNCOUNT] = {
|
||||
{ "Entry", 0.12f },
|
||||
{ "Name", 0.24f },
|
||||
{ "PC", 0.12f },
|
||||
{ "Opcode", 0.28f },
|
||||
{ "SP", 0.12f },
|
||||
{ "Frame Size", 0.12f }
|
||||
};
|
||||
|
||||
const int POPUP_SUBMENU_ID_BREAKPOINTLIST = 5;
|
||||
const int POPUP_SUBMENU_ID_THREADLIST = 6;
|
||||
const int POPUP_SUBMENU_ID_NEWBREAKPOINT = 7;
|
||||
|
@ -710,3 +721,163 @@ void CtrlBreakpointList::showBreakpointMenu(int itemIndex, const POINT &pt)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// CtrlStackTraceView
|
||||
//
|
||||
|
||||
void CtrlStackTraceView::setDialogItem(HWND hwnd)
|
||||
{
|
||||
wnd = hwnd;
|
||||
|
||||
SetWindowLongPtr(wnd,GWLP_USERDATA,(LONG_PTR)this);
|
||||
oldProc = (WNDPROC) SetWindowLongPtr(wnd,GWLP_WNDPROC,(LONG_PTR)wndProc);
|
||||
|
||||
SendMessage(wnd, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT);
|
||||
|
||||
LVCOLUMN lvc;
|
||||
lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
|
||||
lvc.iSubItem = 0;
|
||||
lvc.fmt = LVCFMT_LEFT;
|
||||
|
||||
RECT rect;
|
||||
GetWindowRect(wnd,&rect);
|
||||
|
||||
int totalListSize = (rect.right-rect.left-20);
|
||||
for (int i = 0; i < SF_COLUMNCOUNT; i++)
|
||||
{
|
||||
lvc.cx = stackTraceColumns[i].size * totalListSize;
|
||||
lvc.pszText = stackTraceColumns[i].name;
|
||||
ListView_InsertColumn(wnd, i, &lvc);
|
||||
}
|
||||
}
|
||||
|
||||
LRESULT CALLBACK CtrlStackTraceView::wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
CtrlStackTraceView* sv = (CtrlStackTraceView*) GetWindowLongPtr(hwnd,GWLP_USERDATA);
|
||||
|
||||
switch (msg)
|
||||
{
|
||||
case WM_SIZE:
|
||||
{
|
||||
int width = LOWORD(lParam);
|
||||
RECT rect;
|
||||
GetWindowRect(hwnd,&rect);
|
||||
|
||||
int totalListSize = (rect.right-rect.left-20);
|
||||
for (int i = 0; i < SF_COLUMNCOUNT; i++)
|
||||
{
|
||||
ListView_SetColumnWidth(hwnd,i,stackTraceColumns[i].size * totalListSize);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case WM_KEYDOWN:
|
||||
if (wParam == VK_TAB)
|
||||
{
|
||||
SendMessage(GetParent(hwnd),WM_DEB_TABPRESSED,0,0);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case WM_GETDLGCODE:
|
||||
if (lParam && ((MSG*)lParam)->message == WM_KEYDOWN)
|
||||
{
|
||||
if (wParam == VK_TAB) return DLGC_WANTMESSAGE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return (LRESULT)CallWindowProc((WNDPROC)sv->oldProc,hwnd,msg,wParam,lParam);
|
||||
}
|
||||
|
||||
void CtrlStackTraceView::handleNotify(LPARAM lParam)
|
||||
{
|
||||
LPNMHDR mhdr = (LPNMHDR) lParam;
|
||||
|
||||
if (mhdr->code == NM_DBLCLK)
|
||||
{
|
||||
LPNMITEMACTIVATE item = (LPNMITEMACTIVATE) lParam;
|
||||
SendMessage(GetParent(wnd),WM_DEB_GOTOWPARAM,frames[item->iItem].pc,0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mhdr->code == LVN_GETDISPINFO)
|
||||
{
|
||||
NMLVDISPINFO* dispInfo = (NMLVDISPINFO*)lParam;
|
||||
int index = dispInfo->item.iItem;
|
||||
|
||||
stringBuffer[0] = 0;
|
||||
switch (dispInfo->item.iSubItem)
|
||||
{
|
||||
case SF_ENTRY:
|
||||
sprintf(stringBuffer,"%08X",frames[index].entry);
|
||||
break;
|
||||
case SF_ENTRYNAME:
|
||||
{
|
||||
const char* sym = cpu->findSymbolForAddress(frames[index].entry);
|
||||
if (sym != NULL)
|
||||
{
|
||||
strcpy(stringBuffer,sym);
|
||||
} else {
|
||||
strcpy(stringBuffer,"-");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SF_CURPC:
|
||||
sprintf(stringBuffer,"%08X",frames[index].pc);
|
||||
break;
|
||||
case SF_CUROPCODE:
|
||||
disasm->getOpcodeText(frames[index].pc,stringBuffer);
|
||||
break;
|
||||
case SF_CURSP:
|
||||
sprintf(stringBuffer,"%08X",frames[index].sp);
|
||||
break;
|
||||
case SF_FRAMESIZE:
|
||||
sprintf(stringBuffer,"%08X",frames[index].stackSize);
|
||||
break;
|
||||
}
|
||||
|
||||
if (stringBuffer[0] == 0) strcat(stringBuffer,"Invalid");
|
||||
dispInfo->item.pszText = stringBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
void CtrlStackTraceView::loadStackTrace()
|
||||
{
|
||||
auto threads = GetThreadsInfo();
|
||||
|
||||
u32 entry, stackTop;
|
||||
for (size_t i = 0; i < threads.size(); i++)
|
||||
{
|
||||
if (threads[i].isCurrent)
|
||||
{
|
||||
entry = threads[i].entrypoint;
|
||||
stackTop = threads[i].initialStack;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
frames = MIPSStackWalk::Walk(cpu->GetPC(),cpu->GetRegValue(0,31),cpu->GetRegValue(0,29),entry,stackTop);
|
||||
|
||||
int items = ListView_GetItemCount(wnd);
|
||||
while (items < (int)frames.size())
|
||||
{
|
||||
LVITEM lvI;
|
||||
lvI.pszText = LPSTR_TEXTCALLBACK; // Sends an LVN_GETDISPINFO message.
|
||||
lvI.mask = LVIF_TEXT | LVIF_IMAGE |LVIF_STATE;
|
||||
lvI.stateMask = 0;
|
||||
lvI.iSubItem = 0;
|
||||
lvI.state = 0;
|
||||
lvI.iItem = items;
|
||||
lvI.iImage = items;
|
||||
|
||||
ListView_InsertItem(wnd, &lvI);
|
||||
items++;
|
||||
}
|
||||
|
||||
while (items > (int)frames.size())
|
||||
{
|
||||
ListView_DeleteItem(wnd,--items);
|
||||
}
|
||||
|
||||
InvalidateRect(wnd,NULL,true);
|
||||
UpdateWindow(wnd);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "../../Core/Debugger/DebugInterface.h"
|
||||
#include "../../Core/HLE/sceKernelThread.h"
|
||||
#include "../../Core/Debugger/Breakpoints.h"
|
||||
#include "../../Core/MIPS/MIPSStackWalk.h"
|
||||
|
||||
class CtrlThreadList
|
||||
{
|
||||
|
@ -53,4 +54,28 @@ public:
|
|||
void handleNotify(LPARAM lParam);
|
||||
void showMenu(int itemIndex, const POINT &pt);
|
||||
static LRESULT CALLBACK wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
};
|
||||
|
||||
class CtrlStackTraceView
|
||||
{
|
||||
HWND wnd;
|
||||
WNDPROC oldProc;
|
||||
std::vector<MIPSStackWalk::StackFrame> frames;
|
||||
DebugInterface* cpu;
|
||||
CtrlDisAsmView* disasm;
|
||||
char stringBuffer[256];
|
||||
|
||||
public:
|
||||
void setCpu(DebugInterface* cpu)
|
||||
{
|
||||
this->cpu = cpu;
|
||||
};
|
||||
void setDisasm(CtrlDisAsmView* disasm)
|
||||
{
|
||||
this->disasm = disasm;
|
||||
};
|
||||
void setDialogItem(HWND hwnd);
|
||||
void loadStackTrace();
|
||||
void handleNotify(LPARAM lParam);
|
||||
static LRESULT CALLBACK wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
};
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue