Initial preparations for ability to replace game functions with custom implementations.

Also auto-saves hashmap additions and reapplies the hashmap on function
rename so that if you rename a function that exists in several copies
they will all be labelled.

Note that actual function replacement is not activated yet.
This commit is contained in:
Henrik Rydgard 2013-11-30 20:57:44 +01:00
parent 52cec1ca38
commit 2140892074
25 changed files with 362 additions and 126 deletions

View File

@ -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

View File

@ -196,6 +196,7 @@
<ClCompile Include="HLE\HLE.cpp" />
<ClCompile Include="HLE\HLETables.cpp" />
<ClCompile Include="HLE\proAdhoc.cpp" />
<ClCompile Include="HLE\ReplaceTables.cpp" />
<ClCompile Include="HLE\sceAtrac.cpp" />
<ClCompile Include="HLE\sceAudio.cpp" />
<ClCompile Include="HLE\sceAudiocodec.cpp" />
@ -454,6 +455,7 @@
<ClInclude Include="HLE\HLETables.h" />
<ClInclude Include="HLE\KernelWaitHelpers.h" />
<ClInclude Include="HLE\proAdhoc.h" />
<ClInclude Include="HLE\ReplaceTables.h" />
<ClInclude Include="HLE\sceAtrac.h" />
<ClInclude Include="HLE\sceAudio.h" />
<ClInclude Include="HLE\sceAudiocodec.h" />

View File

@ -505,6 +505,9 @@
<ClCompile Include="Util\GameManager.cpp">
<Filter>Util</Filter>
</ClCompile>
<ClCompile Include="HLE\ReplaceTables.cpp">
<Filter>HLE</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="ELF\ElfReader.h">
@ -936,6 +939,9 @@
<ClInclude Include="Util\GameManager.h">
<Filter>Util</Filter>
</ClInclude>
<ClInclude Include="HLE\ReplaceTables.h">
<Filter>HLE</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="CMakeLists.txt" />

View File

@ -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);

View File

@ -89,6 +89,7 @@ public:
DataType GetDataType(u32 startAddress) const;
static const u32 INVALID_ADDRESS = (u32)-1;
private:
void AssignFunctionIndices();

View File

@ -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

View File

@ -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<NormalizeVector>, 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];
}

36
Core/HLE/ReplaceTables.h Normal file
View 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 "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);

View File

@ -315,6 +315,11 @@ public:
std::vector<VarSymbolImport> importedVars;
std::set<std::string> 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
}

View File

@ -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);

View File

@ -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();

View File

@ -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);

View File

@ -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;

View File

@ -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<MIPSAnalyst::AnalyzedFunction> 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<u64, std::vector<MIPSAnalyst::AnalyzedFunction*>> 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<HashMapFunc> 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<Function> functions;
map<u64, Function*> hashToFunction;
void Shutdown()
{
void Reset() {
functions.clear();
hashToFunction.clear();
}
// hm pointless :P
void UpdateHashToFunctionMap()
{
void UpdateHashToFunctionMap() {
hashToFunction.clear();
for (vector<Function>::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<u32> 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<u32> 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<HashMapFunc> 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;

View File

@ -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<MIPSGPReg> GetInputRegs(MIPSOpcode op);
std::vector<MIPSGPReg> GetOutputRegs(MIPSOpcode op);
@ -93,8 +115,7 @@ namespace MIPSAnalyst
void Shutdown();
typedef struct
{
typedef struct {
DebugInterface* cpu;
u32 opcodeAddress;
MIPSOpcode encodedOpcode;

View File

@ -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];
}

View File

@ -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,
};

View File

@ -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

View File

@ -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) {
}

View File

@ -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(); }

View File

@ -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(&currentMIPS->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();

View File

@ -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);

View File

@ -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();

View File

@ -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();
}

View File

@ -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 \