mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-23 13:30:02 +00:00
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:
parent
52cec1ca38
commit
2140892074
@ -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
|
||||
|
@ -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" />
|
||||
|
@ -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" />
|
||||
|
@ -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);
|
||||
|
@ -89,6 +89,7 @@ public:
|
||||
DataType GetDataType(u32 startAddress) const;
|
||||
|
||||
static const u32 INVALID_ADDRESS = (u32)-1;
|
||||
|
||||
private:
|
||||
void AssignFunctionIndices();
|
||||
|
||||
|
@ -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
|
||||
|
48
Core/HLE/ReplaceTables.cpp
Normal file
48
Core/HLE/ReplaceTables.cpp
Normal 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
36
Core/HLE/ReplaceTables.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 "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);
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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];
|
||||
}
|
||||
|
||||
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
||||
}
|
||||
|
@ -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(); }
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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 \
|
||||
|
Loading…
Reference in New Issue
Block a user