diff --git a/CMakeLists.txt b/CMakeLists.txt
index 279f669c86..199cc0e0ae 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -968,6 +968,8 @@ add_library(${CoreLibName} ${CoreLinkType}
Core/HLE/FunctionWrappers.h
Core/HLE/HLE.cpp
Core/HLE/HLE.h
+ Core/HLE/ReplaceTables.cpp
+ Core/HLE/ReplaceTables.h
Core/HLE/HLETables.cpp
Core/HLE/HLETables.h
Core/HLE/KernelWaitHelpers.h
diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj
index 3fd32fd53d..fb115cb355 100644
--- a/Core/Core.vcxproj
+++ b/Core/Core.vcxproj
@@ -196,6 +196,7 @@
+
@@ -454,6 +455,7 @@
+
diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters
index 715e6ec4f0..8dedeb712a 100644
--- a/Core/Core.vcxproj.filters
+++ b/Core/Core.vcxproj.filters
@@ -505,6 +505,9 @@
Util
+
+ HLE
+
@@ -936,6 +939,9 @@
Util
+
+ HLE
+
diff --git a/Core/Debugger/SymbolMap.cpp b/Core/Debugger/SymbolMap.cpp
index 1482cd4d22..6176116567 100644
--- a/Core/Debugger/SymbolMap.cpp
+++ b/Core/Debugger/SymbolMap.cpp
@@ -50,6 +50,7 @@ bool SymbolMap::LoadSymbolMap(const char *filename) {
FILE *f = File::OpenCFile(filename, "r");
if (!f)
return false;
+
//char temp[256];
//fgets(temp,255,f); //.text section layout
//fgets(temp,255,f); // Starting Virtual
@@ -130,14 +131,14 @@ bool SymbolMap::LoadSymbolMap(const char *filename) {
return true;
}
-
-void SymbolMap::SaveSymbolMap(const char *filename) const
-{
+void SymbolMap::SaveSymbolMap(const char *filename) const {
lock_guard guard(lock_);
FILE *f = File::OpenCFile(filename, "w");
if (!f)
return;
+
fprintf(f,".text\n");
+
for (auto it = functions.begin(), end = functions.end(); it != end; ++it) {
const FunctionEntry& e = it->second;
fprintf(f,"%08x %08x %08x %i %s\n",it->first,e.size,it->first,ST_FUNCTION,GetLabelName(it->first));
@@ -150,8 +151,7 @@ void SymbolMap::SaveSymbolMap(const char *filename) const
fclose(f);
}
-bool SymbolMap::LoadNocashSym(const char *filename)
-{
+bool SymbolMap::LoadNocashSym(const char *filename) {
lock_guard guard(lock_);
FILE *f = File::OpenCFile(filename, "r");
if (!f)
@@ -191,7 +191,7 @@ bool SymbolMap::LoadNocashSym(const char *filename)
}
} else { // labels
int size = 1;
- char* seperator = strchr(value,',');
+ char* seperator = strchr(value, ',');
if (seperator != NULL) {
*seperator = 0;
sscanf(seperator+1,"%08X",&size);
@@ -217,8 +217,7 @@ SymbolType SymbolMap::GetSymbolType(u32 address) const {
return ST_NONE;
}
-bool SymbolMap::GetSymbolInfo(SymbolInfo *info, u32 address, SymbolType symmask) const
-{
+bool SymbolMap::GetSymbolInfo(SymbolInfo *info, u32 address, SymbolType symmask) const {
u32 functionAddress = INVALID_ADDRESS;
u32 dataAddress = INVALID_ADDRESS;
@@ -228,12 +227,9 @@ bool SymbolMap::GetSymbolInfo(SymbolInfo *info, u32 address, SymbolType symmask)
if (symmask & ST_DATA)
dataAddress = GetDataStart(address);
- if (functionAddress == INVALID_ADDRESS || dataAddress == INVALID_ADDRESS)
- {
- if (functionAddress != INVALID_ADDRESS)
- {
- if (info != NULL)
- {
+ if (functionAddress == INVALID_ADDRESS || dataAddress == INVALID_ADDRESS) {
+ if (functionAddress != INVALID_ADDRESS) {
+ if (info != NULL) {
info->type = ST_FUNCTION;
info->address = functionAddress;
info->size = GetFunctionSize(functionAddress);
@@ -242,10 +238,8 @@ bool SymbolMap::GetSymbolInfo(SymbolInfo *info, u32 address, SymbolType symmask)
return true;
}
- if (dataAddress != INVALID_ADDRESS)
- {
- if (info != NULL)
- {
+ if (dataAddress != INVALID_ADDRESS) {
+ if (info != NULL) {
info->type = ST_DATA;
info->address = dataAddress;
info->size = GetDataSize(dataAddress);
diff --git a/Core/Debugger/SymbolMap.h b/Core/Debugger/SymbolMap.h
index 284eb5d006..963b390dd3 100644
--- a/Core/Debugger/SymbolMap.h
+++ b/Core/Debugger/SymbolMap.h
@@ -89,6 +89,7 @@ public:
DataType GetDataType(u32 startAddress) const;
static const u32 INVALID_ADDRESS = (u32)-1;
+
private:
void AssignFunctionIndices();
diff --git a/Core/HLE/FunctionWrappers.h b/Core/HLE/FunctionWrappers.h
index bd5f672a30..e00f0c6e7e 100644
--- a/Core/HLE/FunctionWrappers.h
+++ b/Core/HLE/FunctionWrappers.h
@@ -17,7 +17,9 @@
#pragma once
-#include "../../Globals.h"
+#include "Globals.h"
+#include "Core/HLE/HLE.h"
+
// For easy parameter parsing and return value processing.
// 64bit wrappers
diff --git a/Core/HLE/ReplaceTables.cpp b/Core/HLE/ReplaceTables.cpp
new file mode 100644
index 0000000000..de9dba3c4b
--- /dev/null
+++ b/Core/HLE/ReplaceTables.cpp
@@ -0,0 +1,48 @@
+// 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/.
+
+#include "base/basictypes.h"
+#include "Core/HLE/ReplaceTables.h"
+#include "Core/HLE/FunctionWrappers.h"
+
+void NormalizeVector(u32 vecPtr) {
+ // TODO
+}
+
+// Can either replace with C functions or functions emitted in Asm/ArmAsm.
+// The latter is recommended for fast math functions.
+static const ReplacementTableEntry entries[] = {
+ { 0x0, 64, "NormalizeVector", WrapV_U, 20 }
+};
+
+int GetNumReplacementFuncs() {
+ return ARRAY_SIZE(entries);
+}
+
+int GetReplacementFuncIndex(u64 hash, int size) {
+ // TODO: Build a lookup and keep it around
+ for (int i = 0; i < ARRAY_SIZE(entries); i++) {
+ if (entries[i].hash == hash && entries[i].size == size) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+const ReplacementTableEntry *GetReplacementFunc(int i) {
+ return &entries[i];
+}
\ No newline at end of file
diff --git a/Core/HLE/ReplaceTables.h b/Core/HLE/ReplaceTables.h
new file mode 100644
index 0000000000..94c057225d
--- /dev/null
+++ b/Core/HLE/ReplaceTables.h
@@ -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 "Common/CommonTypes.h"
+
+typedef void (* ReplaceFunc)();
+
+// Kind of similar to HLE functions but with different data.
+struct ReplacementTableEntry {
+ u32 hash;
+ int size;
+ const char *name;
+ ReplaceFunc replaceFunc;
+ int cyclesToEat;
+};
+
+int GetNumReplacementFuncs();
+int GetReplacementFuncIndex(u64 hash, int size);
+const ReplacementTableEntry *GetReplacementFunc(int index);
\ No newline at end of file
diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp
index 2098084205..2156eb936c 100644
--- a/Core/HLE/sceKernelModule.cpp
+++ b/Core/HLE/sceKernelModule.cpp
@@ -315,6 +315,11 @@ public:
std::vector importedVars;
std::set impExpModuleNames;
+ // Keep track of the code region so we can throw out analysis results
+ // when unloaded.
+ u32 textStart;
+ u32 textEnd;
+
u32 memoryBlockAddr;
u32 memoryBlockSize;
bool isFake;
@@ -406,7 +411,7 @@ void __KernelModuleDoState(PointerWrap &p)
void __KernelModuleShutdown()
{
loadedModules.clear();
- MIPSAnalyst::Shutdown();
+ MIPSAnalyst::Reset();
}
// Sometimes there are multiple LO16's or HI16's per pair, even though the ABI says nothing of this.
@@ -671,6 +676,8 @@ void UnexportFuncSymbol(const FuncSymbolExport &func) {
}
void Module::Cleanup() {
+ MIPSAnalyst::ForgetFunctions(textStart, textEnd);
+
loadedModules.erase(GetUID());
for (auto it = exportedVars.begin(), end = exportedVars.end(); it != end; ++it) {
@@ -753,8 +760,7 @@ Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *erro
module->memoryBlockAddr = reader.GetVaddr();
module->memoryBlockSize = reader.GetTotalSize();
- struct PspModuleInfo
- {
+ struct PspModuleInfo {
// 0, 0, 1, 1 ?
u16_le moduleAttrs; //0x0000 User Mode, 0x1000 Kernel Mode
u16_le moduleVersion;
@@ -816,14 +822,17 @@ Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *erro
#if !defined(USING_GLES2)
if (!reader.LoadSymbols())
- MIPSAnalyst::ScanForFunctions(textStart, textStart+textSize);
+ MIPSAnalyst::ScanForFunctions(textStart, textStart + textSize, true);
+#else
+ // Scan for functions (for the analysis results which can help the JIT).
+ // But don't insert into the symbol map.
+ // MIPSAnalyst::ScanForFunctions(textStart, textStart + textSize, false);
#endif
}
INFO_LOG(LOADER,"Module %s: %08x %08x %08x", modinfo->name, modinfo->gp, modinfo->libent,modinfo->libstub);
- struct PspLibStubEntry
- {
+ struct PspLibStubEntry {
u32_le name;
u16_le version;
u16_le flags;
@@ -973,7 +982,7 @@ Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *erro
u32 textEnd = firstImportStubAddr - 4;
#if !defined(USING_GLES2)
if (!reader.LoadSymbols())
- MIPSAnalyst::ScanForFunctions(textStart, textEnd);
+ MIPSAnalyst::ScanForFunctions(textStart, textEnd, true);
#endif
}
diff --git a/Core/MIPS/ARM/ArmAsm.cpp b/Core/MIPS/ARM/ArmAsm.cpp
index 0a2174f102..3f66ecc35d 100644
--- a/Core/MIPS/ARM/ArmAsm.cpp
+++ b/Core/MIPS/ARM/ArmAsm.cpp
@@ -189,6 +189,7 @@ void Jit::GenerateFixedCode()
POP(9, R4, R5, R6, R7, R8, R9, R10, R11, _PC); // Returns
+
// Uncomment if you want to see the output...
// INFO_LOG(JIT, "THE DISASM ========================");
// DisassembleArm(enterCode, GetCodePtr() - enterCode);
diff --git a/Core/MIPS/ARM/ArmJit.cpp b/Core/MIPS/ARM/ArmJit.cpp
index 35eeb95e22..e6434ac9af 100644
--- a/Core/MIPS/ARM/ArmJit.cpp
+++ b/Core/MIPS/ARM/ArmJit.cpp
@@ -24,6 +24,7 @@
#include "Core/MIPS/MIPSCodeUtils.h"
#include "Core/MIPS/MIPSInt.h"
#include "Core/MIPS/MIPSTables.h"
+#include "Core/HLE/ReplaceTables.h"
#include "ArmRegCache.h"
#include "ArmJit.h"
@@ -338,6 +339,27 @@ void Jit::Comp_RunBlock(MIPSOpcode op)
ERROR_LOG(JIT, "Comp_RunBlock should never be reached!");
}
+void Jit::Comp_ReplacementFunc(MIPSOpcode op)
+{
+ // None of the code of this function is relevant so we'll just
+ // call the replacement and move RA to PC.
+ int index = op.encoding & 0xFFFFFF;
+ const ReplacementTableEntry *entry = GetReplacementFunc(index);
+
+ BL((void *)(entry->replaceFunc));
+ // Alternatively, we could inline it here, instead of calling out, if it's a function
+ // we can emit.
+
+ MOVI2R(R0, currentMIPS->r[MIPS_REG_RA], R1);
+ MovToPC(R0);
+ js.compiling = false;
+ js.downcountAmount = entry->cyclesToEat;
+ WriteDownCount(0);
+
+ // We could even do this in the jal that is branching to the function
+ // but having the op is necessary for the interpreter anyway.
+}
+
void Jit::Comp_Generic(MIPSOpcode op)
{
FlushAll();
diff --git a/Core/MIPS/ARM/ArmJit.h b/Core/MIPS/ARM/ArmJit.h
index 994e4e1a59..f32aaaa193 100644
--- a/Core/MIPS/ARM/ArmJit.h
+++ b/Core/MIPS/ARM/ArmJit.h
@@ -73,6 +73,7 @@ public:
void CompileDelaySlot(int flags);
void EatInstruction(MIPSOpcode op);
void Comp_RunBlock(MIPSOpcode op);
+ void Comp_ReplacementFunc(MIPSOpcode op);
// Ops
void Comp_ITypeMem(MIPSOpcode op);
diff --git a/Core/MIPS/JitCommon/JitCommon.h b/Core/MIPS/JitCommon/JitCommon.h
index 1c121d09fc..0f470be7fd 100644
--- a/Core/MIPS/JitCommon/JitCommon.h
+++ b/Core/MIPS/JitCommon/JitCommon.h
@@ -43,10 +43,12 @@ struct JitBlock;
#define MIPS_IS_EMUHACK(op) (((op) & 0xFC000000) == MIPS_EMUHACK_OPCODE) // masks away the subop
-
// There are 2 bits available for sub-opcodes, 0x03000000.
#define EMUOP_RUNBLOCK 0 // Runs a JIT block
#define EMUOP_RETKERNEL 1 // Returns to the simulated PSP kernel from a thread
+#define EMUOP_CALL_REPLACEMENT 2
+
+#define MIPS_EMUHACK_CALL_REPLACEMENT (MIPS_EMUHACK_OPCODE | (EMUOP_CALL_REPLACEMENT << 24))
namespace MIPSComp {
extern Jit *jit;
diff --git a/Core/MIPS/MIPSAnalyst.cpp b/Core/MIPS/MIPSAnalyst.cpp
index 9d2c5314be..9c571ac7e0 100644
--- a/Core/MIPS/MIPSAnalyst.cpp
+++ b/Core/MIPS/MIPSAnalyst.cpp
@@ -27,9 +27,33 @@
#include "Core/MIPS/MIPSCodeUtils.h"
#include "Core/Debugger/SymbolMap.h"
#include "Core/Debugger/DebugInterface.h"
+#include "Core/HLE/ReplaceTables.h"
+#include "Core/MIPS/JitCommon/JitCommon.h"
+#include "ext/xxhash.h"
using namespace MIPSCodeUtils;
-using namespace std;
+
+// Not in a namespace because MSVC's debugger doesn't like it
+static std::vector functions;
+
+// TODO: Try multimap instead
+// One function can appear in multiple copies in memory, and they will all have
+// the same hash and should all be replaced if possible.
+static std::map> hashToFunction;
+
+struct HashMapFunc {
+ char name[64];
+ u64 hash;
+ u32 size; //number of bytes
+
+ bool operator < (const HashMapFunc &other) const {
+ return hash < other.hash || (hash == other.hash && size < other.size);
+ }
+};
+
+static std::set hashMap;
+
+static std::string hashmapFileName;
namespace MIPSAnalyst {
// Only can ever output a single reg.
@@ -132,73 +156,48 @@ namespace MIPSAnalyst {
results.r[outReg].MarkWrite(addr);
}
- if (info & DELAYSLOT)
- {
+ if (info & DELAYSLOT) {
// Let's just finish the delay slot before bailing.
endAddr = addr + 4;
}
}
- int numUsedRegs=0;
- static int totalUsedRegs=0;
- static int numAnalyzings=0;
+ int numUsedRegs = 0;
+ static int totalUsedRegs = 0;
+ static int numAnalyzings = 0;
for (int i = 0; i < MIPS_NUM_GPRS; i++) {
if (results.r[i].used) {
numUsedRegs++;
}
}
- totalUsedRegs+=numUsedRegs;
+ totalUsedRegs += numUsedRegs;
numAnalyzings++;
- DEBUG_LOG(CPU,"[ %08x ] Used regs: %i Average: %f",address,numUsedRegs,(float)totalUsedRegs/(float)numAnalyzings);
+ VERBOSE_LOG(CPU, "[ %08x ] Used regs: %i Average: %f", address, numUsedRegs, (float)totalUsedRegs / (float)numAnalyzings);
return results;
}
-
-
- struct Function
- {
- u32 start;
- u32 end;
- u64 hash;
- u32 size;
- bool isStraightLeaf;
- bool hasHash;
- bool usesVFPU;
- char name[64];
- };
-
- vector functions;
-
- map hashToFunction;
-
- void Shutdown()
- {
+
+ void Reset() {
functions.clear();
hashToFunction.clear();
}
- // hm pointless :P
- void UpdateHashToFunctionMap()
- {
+ void UpdateHashToFunctionMap() {
hashToFunction.clear();
- for (vector::iterator iter = functions.begin(); iter != functions.end(); iter++)
- {
- Function &f = *iter;
- if (f.hasHash)
- {
- hashToFunction[f.hash] = &f;
+ for (auto iter = functions.begin(); iter != functions.end(); iter++) {
+ AnalyzedFunction &f = *iter;
+ if (f.hasHash) {
+ hashToFunction[f.hash].push_back(&f);
}
}
}
-
- bool IsRegisterUsed(MIPSGPReg reg, u32 addr)
- {
- while (true)
- {
+ // Look forwards to find if a register is used again in this block.
+ // Don't think we use this yet.
+ bool IsRegisterUsed(MIPSGPReg reg, u32 addr) {
+ while (true) {
MIPSOpcode op = Memory::Read_Instruction(addr);
MIPSInfo info = MIPSGetInfo(op);
-
if ((info & IN_RS) && (MIPS_GET_RS(op) == reg))
return true;
if ((info & IN_RT) && (MIPS_GET_RT(op) == reg))
@@ -213,7 +212,7 @@ namespace MIPSAnalyst {
return false; //the reg got clobbed! yay!
if ((info & OUT_RA) && (reg == MIPS_REG_RA))
return false; //the reg got clobbed! yay!
- addr+=4;
+ addr += 4;
}
return true;
}
@@ -222,7 +221,7 @@ namespace MIPSAnalyst {
std::vector buffer;
for (auto iter = functions.begin(), end = functions.end(); iter != end; iter++) {
- Function &f = *iter;
+ AnalyzedFunction &f = *iter;
// This is unfortunate. In case of emuhacks or relocs, we have to make a copy.
buffer.resize((f.end - f.start + 4) / 4);
@@ -315,15 +314,19 @@ namespace MIPSAnalyst {
return furthestJumpbackAddr;
}
- void ScanForFunctions(u32 startAddr, u32 endAddr /*, std::vector knownEntries*/) {
- Function currentFunction = {startAddr};
+ void ReplaceFunctions();
+
+ void ScanForFunctions(u32 startAddr, u32 endAddr, bool insertSymbols) {
+ AnalyzedFunction currentFunction = {startAddr};
u32 furthestBranch = 0;
bool looking = false;
bool end = false;
bool isStraightLeaf = true;
+
u32 addr;
- for (addr = startAddr; addr <= endAddr; addr+=4) {
+ for (addr = startAddr; addr <= endAddr; addr += 4) {
+ // Use pre-existing symbol map info if available. May be more reliable.
SymbolInfo syminfo;
if (symbolMap.GetSymbolInfo(&syminfo, addr, ST_FUNCTION)) {
addr = syminfo.address + syminfo.size - 4;
@@ -403,6 +406,7 @@ namespace MIPSAnalyst {
}
}
}
+
if (end) {
currentFunction.end = addr + 4;
currentFunction.isStraightLeaf = isStraightLeaf;
@@ -411,17 +415,20 @@ namespace MIPSAnalyst {
addr += 4;
looking = false;
end = false;
- isStraightLeaf=true;
+ isStraightLeaf = true;
currentFunction.start = addr+4;
}
}
+
currentFunction.end = addr + 4;
functions.push_back(currentFunction);
- for (auto iter = functions.begin(); iter != functions.end(); iter++) {
- iter->size = iter->end - iter->start + 4;
- char temp[256];
- symbolMap.AddFunction(DefaultFunctionName(temp, iter->start), iter->start, iter->end - iter->start + 4);
+ if (insertSymbols) {
+ for (auto iter = functions.begin(); iter != functions.end(); iter++) {
+ iter->size = iter->end - iter->start + 4;
+ char temp[256];
+ symbolMap.AddFunction(DefaultFunctionName(temp, iter->start), iter->start, iter->end - iter->start + 4);
+ }
}
HashFunctions();
@@ -430,30 +437,42 @@ namespace MIPSAnalyst {
LoadHashMap(GetSysDirectory(DIRECTORY_SYSTEM) + "knownfuncs.ini");
StoreHashMap(GetSysDirectory(DIRECTORY_SYSTEM) + "knownfuncs.ini");
}
+
+ // ReplaceFunctions();
}
- struct HashMapFunc {
- char name[64];
- u64 hash;
- u32 size; //number of bytes
+ void ForgetFunctions(u32 startAddr, u32 endAddr) {
+ StoreHashMap(GetSysDirectory(DIRECTORY_SYSTEM) + "knownfuncs.ini");
- bool operator < (const HashMapFunc &other) const {
- return hash < other.hash || (hash == other.hash && size < other.size);
- }
- };
- std::set hashMap;
+ // It makes sense to forget functions as modules are unloaded but it breaks
+ // the easy way of saving a hashmap by unloading and loading a game. I added
+ // an alternative way.
- void StoreHashMap(const std::string &filename) {
- HashFunctions();
-
- FILE *file = File::OpenCFile(filename, "wt");
- if (!file) {
- WARN_LOG(LOADER, "Could not store hash map: %s", filename.c_str());
- return;
+ // TODO: speedup
+ auto iter = functions.begin();
+ while (iter != functions.end()) {
+ if (iter->start >= startAddr && iter->start <= endAddr) {
+ iter = functions.erase(iter);
+ } else {
+ iter++;
+ }
}
+ // TODO: Also wipe them from hash->function map
+ }
+
+ void ReplaceFunctions() {
+ for (size_t i = 0; i < functions.size(); i++) {
+ int index = GetReplacementFuncIndex(functions[i].hash, functions[i].size);
+ if (index >= 0) {
+ Memory::Write_U32(MIPS_EMUHACK_CALL_REPLACEMENT | (int)i, functions[i].start);
+ }
+ }
+ }
+
+ void UpdateHashMap() {
for (auto it = functions.begin(), end = functions.end(); it != end; ++it) {
- const Function &f = *it;
+ const AnalyzedFunction &f = *it;
// Small functions aren't very interesting.
if (!f.hasHash || f.size < 12) {
continue;
@@ -468,6 +487,19 @@ namespace MIPSAnalyst {
strncpy(mf.name, name, sizeof(mf.name) - 1);
hashMap.insert(mf);
}
+ }
+
+ void StoreHashMap(std::string filename) {
+ if (filename.empty())
+ filename = hashmapFileName;
+
+ FILE *file = File::OpenCFile(filename, "wt");
+ if (!file) {
+ WARN_LOG(LOADER, "Could not store hash map: %s", filename.c_str());
+ return;
+ }
+
+ UpdateHashMap();
for (auto it = hashMap.begin(), end = hashMap.end(); it != end; ++it) {
const HashMapFunc &mf = *it;
@@ -480,7 +512,6 @@ namespace MIPSAnalyst {
}
void ApplyHashMap() {
- HashFunctions();
UpdateHashToFunctionMap();
for (auto mf = hashMap.begin(), end = hashMap.end(); mf != end; ++mf) {
@@ -490,26 +521,30 @@ namespace MIPSAnalyst {
}
// Yay, found a function.
- Function &f = *(iter->second);
- if (f.hash == mf->hash && f.size == mf->size) {
- strncpy(f.name, mf->name, sizeof(mf->name) - 1);
- const char *existingLabel = symbolMap.GetLabelName(f.start);
- char defaultLabel[256];
- // If it was renamed, keep it. Only change the name if it's still the default.
- if (existingLabel == NULL || !strcmp(existingLabel, DefaultFunctionName(defaultLabel, f.start))) {
- symbolMap.SetLabelName(mf->name, f.start);
+ for (int i = 0; i < iter->second.size(); i++) {
+ AnalyzedFunction &f = *(iter->second[i]);
+ if (f.hash == mf->hash && f.size == mf->size) {
+ strncpy(f.name, mf->name, sizeof(mf->name) - 1);
+
+ const char *existingLabel = symbolMap.GetLabelName(f.start);
+ char defaultLabel[256];
+ // If it was renamed, keep it. Only change the name if it's still the default.
+ if (existingLabel == NULL || !strcmp(existingLabel, DefaultFunctionName(defaultLabel, f.start))) {
+ symbolMap.SetLabelName(mf->name, f.start);
+ }
}
}
}
}
- void LoadHashMap(const std::string &filename) {
+ void LoadHashMap(std::string filename) {
FILE *file = File::OpenCFile(filename, "rt");
if (!file) {
WARN_LOG(LOADER, "Could not load hash map: %s", filename.c_str());
return;
}
+ hashmapFileName = filename;
while (!feof(file)) {
HashMapFunc mf = { "" };
@@ -561,11 +596,9 @@ namespace MIPSAnalyst {
// gather relevant address for alu operations
// that's usually the value of the dest register
- switch (MIPS_GET_OP(op))
- {
+ switch (MIPS_GET_OP(op)) {
case 0: // special
- switch (MIPS_GET_FUNC(op))
- {
+ switch (MIPS_GET_FUNC(op)) {
case 0x20: // add
case 0x21: // addu
info.hasRelevantAddress = true;
diff --git a/Core/MIPS/MIPSAnalyst.h b/Core/MIPS/MIPSAnalyst.h
index 4ae3c6be11..a32c511578 100644
--- a/Core/MIPS/MIPSAnalyst.h
+++ b/Core/MIPS/MIPSAnalyst.h
@@ -19,6 +19,7 @@
#include "Globals.h"
#include "Core/MIPS/MIPS.h"
+#include "Core/HLE/ReplaceTables.h"
class DebugInterface;
@@ -74,12 +75,33 @@ namespace MIPSAnalyst
AnalysisResults Analyze(u32 address);
+ bool IsRegisterUsed(MIPSGPReg reg, u32 addr);
+
+ struct AnalyzedFunction {
+ u32 start;
+ u32 end;
+ u64 hash;
+ u32 size;
+ bool isStraightLeaf;
+ bool hasHash;
+ bool usesVFPU;
+ char name[64];
+ };
+
+ void Reset();
bool IsRegisterUsed(u32 reg, u32 addr);
- void ScanForFunctions(u32 startAddr, u32 endAddr);
+ // This will not only create a database of "AnalyzedFunction" structs, it also
+ // will insert all the functions it finds into the symbol map, if insertSymbols is true.
+ void ScanForFunctions(u32 startAddr, u32 endAddr, bool insertSymbols);
+ void ForgetFunctions(u32 startAddr, u32 endAddr);
void CompileLeafs();
- void LoadHashMap(const std::string &filename);
- void StoreHashMap(const std::string &filename);
+ void LoadHashMap(std::string filename);
+ void StoreHashMap(std::string filename = "");
+ void ReplaceFunctions(const ReplacementTableEntry *e, int numEntries);
+
+ void UpdateHashMap();
+ void ApplyHashMap();
std::vector GetInputRegs(MIPSOpcode op);
std::vector GetOutputRegs(MIPSOpcode op);
@@ -93,8 +115,7 @@ namespace MIPSAnalyst
void Shutdown();
- typedef struct
- {
+ typedef struct {
DebugInterface* cpu;
u32 opcodeAddress;
MIPSOpcode encodedOpcode;
diff --git a/Core/MIPS/MIPSInt.cpp b/Core/MIPS/MIPSInt.cpp
index 471a6f0e6b..74a1590dad 100644
--- a/Core/MIPS/MIPSInt.cpp
+++ b/Core/MIPS/MIPSInt.cpp
@@ -31,6 +31,7 @@
#include "Core/Config.h"
#include "Core/HLE/HLE.h"
#include "Core/HLE/HLETables.h"
+#include "Core/HLE/ReplaceTables.h"
#include "Core/System.h"
#define R(i) (currentMIPS->r[i])
@@ -1010,7 +1011,19 @@ namespace MIPSInt
void Int_Emuhack(MIPSOpcode op)
{
- _dbg_assert_msg_(CPU,0,"Trying to interpret emuhack instruction that can't be interpreted");
+ if (((op >> 24) & 3) != 1) {
+ _dbg_assert_msg_(CPU,0,"Trying to interpret emuhack instruction that can't be interpreted");
+ }
+ // It's a replacement func!
+ int index = op.encoding & 0xFFFFFF;
+ const ReplacementTableEntry *entry = GetReplacementFunc(index);
+ if (entry) {
+ entry->replaceFunc();
+ } else {
+ ERROR_LOG(CPU, "Bad replacement function index %i", index);
+ }
+
+ PC = currentMIPS->r[MIPS_REG_RA];
}
diff --git a/Core/MIPS/MIPSTables.cpp b/Core/MIPS/MIPSTables.cpp
index 3bc8cbedba..152b4c9161 100644
--- a/Core/MIPS/MIPSTables.cpp
+++ b/Core/MIPS/MIPSTables.cpp
@@ -808,11 +808,10 @@ const MIPSInstruction tableALLEGREX0[32] = // 011111 ..... ..... ..... xxxxx 10
};
-const MIPSInstruction tableEMU[4] =
-{
+const MIPSInstruction tableEMU[4] = {
INSTR("RUNBLOCK", &Jit::Comp_RunBlock, Dis_Emuhack, Int_Emuhack, 0xFFFFFFFF),
INSTR("RetKrnl", 0, Dis_Emuhack, Int_Emuhack, 0),
- INVALID,
+ INSTR("CallRepl", &Jit::Comp_ReplacementFunc, Dis_Emuhack, Int_Emuhack, 0),
INVALID,
};
diff --git a/Core/MIPS/MIPSTables.h b/Core/MIPS/MIPSTables.h
index 986fd61483..2ae1287686 100644
--- a/Core/MIPS/MIPSTables.h
+++ b/Core/MIPS/MIPSTables.h
@@ -47,16 +47,16 @@ struct MIPSInfo {
#define CONDTYPE_FPUTRUE CONDTYPE_NE
// as long as the other flags are checked,
-// there is no way to misinterprete these
+// there is no way to misinterpret these
// as CONDTYPE_X
-#define MEMTYPE_MASK 0x00000007
-#define MEMTYPE_BYTE 0x00000001
-#define MEMTYPE_HWORD 0x00000002
-#define MEMTYPE_WORD 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 IS_CONDMOVE 0x00000008
#define DELAYSLOT 0x00000010
#define BAD_INSTRUCTION 0x00000020
#define LIKELY 0x00000040
diff --git a/Core/MIPS/PPC/PpcJit.cpp b/Core/MIPS/PPC/PpcJit.cpp
index f9ece053da..7f32a89b85 100644
--- a/Core/MIPS/PPC/PpcJit.cpp
+++ b/Core/MIPS/PPC/PpcJit.cpp
@@ -161,6 +161,17 @@ void Jit::Comp_RunBlock(MIPSOpcode op) {
ERROR_LOG(JIT, "Comp_RunBlock should never be reached!");
}
+void Jit::Comp_ReplacementFunc(MIPSOpcode op)
+{
+ // None of the code of this function is relevant so we'll just
+ // call the replacement and move RA to PC.
+ int replacementFunc = op & 0xFFFFFFF;
+
+
+ // We could even do this in the jal that is branching to the function
+ // but having the op is necessary for the interpreter anyway.
+}
+
void Jit::Comp_DoNothing(MIPSOpcode op) {
}
diff --git a/Core/MIPS/PPC/PpcJit.h b/Core/MIPS/PPC/PpcJit.h
index c3a2587262..4b5ed4cd89 100644
--- a/Core/MIPS/PPC/PpcJit.h
+++ b/Core/MIPS/PPC/PpcJit.h
@@ -170,6 +170,7 @@ namespace MIPSComp
void EatInstruction(MIPSOpcode op);
void Comp_RunBlock(MIPSOpcode op);
+ void Comp_ReplacementFunc(MIPSOpcode op);
// TODO: Eat VFPU prefixes here.
void EatPrefix() { js.EatPrefix(); }
diff --git a/Core/MIPS/x86/Jit.cpp b/Core/MIPS/x86/Jit.cpp
index b81503869d..5295e3f9cc 100644
--- a/Core/MIPS/x86/Jit.cpp
+++ b/Core/MIPS/x86/Jit.cpp
@@ -30,6 +30,7 @@
#include "Core/MIPS/MIPSCodeUtils.h"
#include "Core/MIPS/MIPSInt.h"
#include "Core/MIPS/MIPSTables.h"
+#include "Core/HLE/ReplaceTables.h"
#include "RegCache.h"
#include "Jit.h"
@@ -371,6 +372,28 @@ void Jit::Comp_RunBlock(MIPSOpcode op)
ERROR_LOG(JIT, "Comp_RunBlock");
}
+void Jit::Comp_ReplacementFunc(MIPSOpcode op)
+{
+ // None of the code of this function is relevant so we'll just
+ // call the replacement and move RA to PC.
+ int index = op.encoding & 0xFFFFFF;
+ const ReplacementTableEntry *entry = GetReplacementFunc(index);
+
+ CALL(entry->replaceFunc);
+ // Alternatively, we could inline it here, instead of calling out, if it's a function
+ // we can emit.
+
+ MOV(32, R(EAX), Imm32(currentMIPS->r[MIPS_REG_RA]));
+ MOV(32, M(&mips_->pc), R(EAX));
+ SUB(32, M(¤tMIPS->downcount), Imm32(entry->cyclesToEat));
+ JMP(asm_.dispatcher, true);
+
+ js.compiling = false;
+
+ // We could even do this in the jal that is branching to the function
+ // but having the op is necessary for the interpreter anyway.
+}
+
void Jit::Comp_Generic(MIPSOpcode op)
{
FlushAll();
diff --git a/Core/MIPS/x86/Jit.h b/Core/MIPS/x86/Jit.h
index c4a80ec764..c4fab0b5b6 100644
--- a/Core/MIPS/x86/Jit.h
+++ b/Core/MIPS/x86/Jit.h
@@ -28,6 +28,7 @@
#include "Common/x64Emitter.h"
#include "Core/MIPS/JitCommon/JitBlockCache.h"
#include "Core/MIPS/JitCommon/JitState.h"
+#include "Core/HLE/ReplaceTables.h"
#include "RegCache.h"
#include "RegCacheFPU.h"
@@ -78,6 +79,7 @@ public:
}
void Comp_RunBlock(MIPSOpcode op);
+ void Comp_ReplacementFunc(MIPSOpcode op);
// Ops
void Comp_ITypeMem(MIPSOpcode op);
diff --git a/Core/System.cpp b/Core/System.cpp
index 161af350ca..7c959ab4a9 100644
--- a/Core/System.cpp
+++ b/Core/System.cpp
@@ -30,6 +30,7 @@
#include "Core/MemMap.h"
#include "Core/MIPS/MIPS.h"
+#include "Core/MIPS/MIPSAnalyst.h"
#include "Core/MIPS/JitCommon/JitCommon.h"
#include "Core/System.h"
@@ -165,6 +166,8 @@ void CPU_Init() {
std::string filename = coreParameter.fileToStart;
IdentifiedFileType type = Identify_File(filename);
+ MIPSAnalyst::Reset();
+
switch (type) {
case FILETYPE_PSP_ISO:
case FILETYPE_PSP_ISO_NP:
@@ -319,6 +322,7 @@ bool PSP_IsInited() {
}
void PSP_Shutdown() {
+ MIPSAnalyst::StoreHashMap();
if (coreState == CORE_RUNNING)
Core_UpdateState(CORE_ERROR);
Core_NotifyShutdown();
diff --git a/Windows/Debugger/CtrlDisAsmView.cpp b/Windows/Debugger/CtrlDisAsmView.cpp
index b842893c28..2051636571 100644
--- a/Windows/Debugger/CtrlDisAsmView.cpp
+++ b/Windows/Debugger/CtrlDisAsmView.cpp
@@ -919,6 +919,8 @@ void CtrlDisAsmView::onMouseUp(WPARAM wParam, LPARAM lParam, int button)
if (InputBox_GetString(MainWindow::GetHInstance(), MainWindow::GetHWND(), L"New function name", name, newname))
{
symbolMap.SetLabelName(newname.c_str(),funcBegin);
+ MIPSAnalyst::UpdateHashMap();
+ MIPSAnalyst::ApplyHashMap();
SendMessage(GetParent(wnd),WM_DEB_MAPLOADED,0,0);
redraw();
}
diff --git a/android/jni/Android.mk b/android/jni/Android.mk
index f82259caf0..6e2f57842d 100644
--- a/android/jni/Android.mk
+++ b/android/jni/Android.mk
@@ -190,6 +190,7 @@ EXEC_AND_LIB_FILES := \
$(SRC)/Core/Dialog/SavedataParam.cpp \
$(SRC)/Core/Font/PGF.cpp \
$(SRC)/Core/HLE/HLETables.cpp \
+ $(SRC)/Core/HLE/ReplaceTables.cpp \
$(SRC)/Core/HLE/HLE.cpp \
$(SRC)/Core/HLE/sceAtrac.cpp \
$(SRC)/Core/HLE/__sceAudio.cpp.arm \