mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-09-29 07:46:38 +02:00
Merge pull request #11148 from unknownbrackets/debugger-arm
Enable debugger / breakpoints / stepping on ARM / ARM64
This commit is contained in:
+52
-42
@@ -87,7 +87,6 @@ void Core_NotifyLifecycle(CoreLifecycle stage) {
|
||||
|
||||
void Core_Stop() {
|
||||
Core_UpdateState(CORE_POWERDOWN);
|
||||
m_StepCond.notify_all();
|
||||
}
|
||||
|
||||
bool Core_IsStepping() {
|
||||
@@ -102,6 +101,14 @@ bool Core_IsInactive() {
|
||||
return coreState != CORE_RUNNING && coreState != CORE_NEXTFRAME && !coreStatePending;
|
||||
}
|
||||
|
||||
static inline void Core_StateProcessed() {
|
||||
if (coreStatePending) {
|
||||
std::lock_guard<std::mutex> guard(m_hInactiveMutex);
|
||||
coreStatePending = false;
|
||||
m_InactiveCond.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
void Core_WaitInactive() {
|
||||
while (Core_IsActive()) {
|
||||
std::unique_lock<std::mutex> guard(m_hInactiveMutex);
|
||||
@@ -173,6 +180,7 @@ bool UpdateScreenScale(int width, int height) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Note: not used on Android.
|
||||
void UpdateRunLoop() {
|
||||
if (windowHidden && g_Config.bPauseWhenMinimized) {
|
||||
sleep_ms(16);
|
||||
@@ -191,6 +199,8 @@ void KeepScreenAwake() {
|
||||
void Core_RunLoop(GraphicsContext *ctx) {
|
||||
graphicsContext = ctx;
|
||||
while ((GetUIState() != UISTATE_INGAME || !PSP_IsInited()) && GetUIState() != UISTATE_EXIT) {
|
||||
// In case it was pending, we're not in game anymore. We won't get to Core_Run().
|
||||
Core_StateProcessed();
|
||||
time_update();
|
||||
double startTime = time_now_d();
|
||||
UpdateRunLoop();
|
||||
@@ -227,22 +237,55 @@ void Core_RunLoop(GraphicsContext *ctx) {
|
||||
}
|
||||
|
||||
void Core_DoSingleStep() {
|
||||
std::lock_guard<std::mutex> guard(m_hStepMutex);
|
||||
singleStepPending = true;
|
||||
m_StepCond.notify_all();
|
||||
}
|
||||
|
||||
void Core_UpdateSingleStep() {
|
||||
std::lock_guard<std::mutex> guard(m_hStepMutex);
|
||||
m_StepCond.notify_all();
|
||||
}
|
||||
|
||||
void Core_SingleStep() {
|
||||
currentMIPS->SingleStep();
|
||||
if (coreState == CORE_STEPPING)
|
||||
steppingCounter++;
|
||||
}
|
||||
|
||||
static inline void CoreStateProcessed() {
|
||||
if (coreStatePending) {
|
||||
coreStatePending = false;
|
||||
m_InactiveCond.notify_all();
|
||||
static inline bool Core_WaitStepping() {
|
||||
std::unique_lock<std::mutex> guard(m_hStepMutex);
|
||||
if (!singleStepPending && coreState == CORE_STEPPING)
|
||||
m_StepCond.wait(guard);
|
||||
|
||||
bool result = singleStepPending;
|
||||
singleStepPending = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
void Core_ProcessStepping() {
|
||||
Core_StateProcessed();
|
||||
|
||||
// Check if there's any pending save state actions.
|
||||
SaveState::Process();
|
||||
if (coreState != CORE_STEPPING) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We're not inside jit now, so it's safe to clear the breakpoints.
|
||||
CBreakPoints::ClearTemporaryBreakPoints();
|
||||
host->UpdateDisassembly();
|
||||
host->UpdateMemView();
|
||||
|
||||
// Need to check inside the lock to avoid races.
|
||||
bool doStep = Core_WaitStepping();
|
||||
|
||||
// We may still be stepping without singleStepPending to process a save state.
|
||||
if (doStep && coreState == CORE_STEPPING) {
|
||||
Core_SingleStep();
|
||||
// Update disasm dialog.
|
||||
host->UpdateDisassembly();
|
||||
host->UpdateMemView();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,9 +294,8 @@ static inline void CoreStateProcessed() {
|
||||
void Core_Run(GraphicsContext *ctx) {
|
||||
host->UpdateDisassembly();
|
||||
while (true) {
|
||||
reswitch:
|
||||
if (GetUIState() != UISTATE_INGAME) {
|
||||
CoreStateProcessed();
|
||||
Core_StateProcessed();
|
||||
if (GetUIState() == UISTATE_EXIT) {
|
||||
UpdateRunLoop();
|
||||
return;
|
||||
@@ -264,51 +306,20 @@ reswitch:
|
||||
|
||||
switch (coreState) {
|
||||
case CORE_RUNNING:
|
||||
case CORE_STEPPING:
|
||||
// enter a fast runloop
|
||||
Core_RunLoop(ctx);
|
||||
break;
|
||||
|
||||
// We should never get here on Android.
|
||||
case CORE_STEPPING:
|
||||
singleStepPending = false;
|
||||
CoreStateProcessed();
|
||||
|
||||
// Check if there's any pending savestate actions.
|
||||
SaveState::Process();
|
||||
if (coreState == CORE_POWERDOWN) {
|
||||
Core_StateProcessed();
|
||||
return;
|
||||
}
|
||||
|
||||
// wait for step command..
|
||||
host->UpdateDisassembly();
|
||||
host->UpdateMemView();
|
||||
host->SendCoreWait(true);
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> guard(m_hStepMutex);
|
||||
m_StepCond.wait(guard);
|
||||
}
|
||||
|
||||
host->SendCoreWait(false);
|
||||
// No step pending? Let's go back to the wait.
|
||||
if (!singleStepPending || coreState != CORE_STEPPING) {
|
||||
if (coreState == CORE_POWERDOWN) {
|
||||
return;
|
||||
}
|
||||
goto reswitch;
|
||||
}
|
||||
|
||||
Core_SingleStep();
|
||||
// update disasm dialog
|
||||
host->UpdateDisassembly();
|
||||
host->UpdateMemView();
|
||||
break;
|
||||
|
||||
case CORE_POWERUP:
|
||||
case CORE_POWERDOWN:
|
||||
case CORE_ERROR:
|
||||
// Exit loop!!
|
||||
CoreStateProcessed();
|
||||
Core_StateProcessed();
|
||||
|
||||
return;
|
||||
|
||||
@@ -320,7 +331,6 @@ reswitch:
|
||||
|
||||
void Core_EnableStepping(bool step) {
|
||||
if (step) {
|
||||
sleep_ms(1);
|
||||
host->SetDebugMode(true);
|
||||
Core_UpdateState(CORE_STEPPING);
|
||||
steppingCounter++;
|
||||
|
||||
@@ -34,6 +34,7 @@ void Core_SetGraphicsContext(GraphicsContext *ctx);
|
||||
void Core_EnableStepping(bool step);
|
||||
void Core_DoSingleStep();
|
||||
void Core_UpdateSingleStep();
|
||||
void Core_ProcessStepping();
|
||||
// Changes every time we enter stepping.
|
||||
int Core_GetSteppingCounter();
|
||||
|
||||
@@ -54,6 +55,7 @@ bool Core_IsStepping();
|
||||
|
||||
bool Core_IsActive();
|
||||
bool Core_IsInactive();
|
||||
// Warning: these currently work only on Windows.
|
||||
void Core_WaitInactive();
|
||||
void Core_WaitInactive(int milliseconds);
|
||||
|
||||
|
||||
@@ -33,11 +33,6 @@ u64 CBreakPoints::breakSkipFirstTicks_ = 0;
|
||||
std::vector<MemCheck> CBreakPoints::memChecks_;
|
||||
std::vector<MemCheck *> CBreakPoints::cleanupMemChecks_;
|
||||
|
||||
MemCheck::MemCheck()
|
||||
{
|
||||
numHits = 0;
|
||||
}
|
||||
|
||||
void MemCheck::Log(u32 addr, bool write, int size, u32 pc) {
|
||||
if (result & BREAK_ACTION_LOG) {
|
||||
if (logFormat.empty()) {
|
||||
@@ -493,17 +488,20 @@ u32 CBreakPoints::CheckSkipFirst()
|
||||
return 0;
|
||||
}
|
||||
|
||||
const std::vector<MemCheck> CBreakPoints::GetMemCheckRanges()
|
||||
{
|
||||
const std::vector<MemCheck> CBreakPoints::GetMemCheckRanges(bool write) {
|
||||
std::vector<MemCheck> ranges = memChecks_;
|
||||
for (auto it = memChecks_.begin(), end = memChecks_.end(); it != end; ++it)
|
||||
{
|
||||
MemCheck check = *it;
|
||||
for (const auto &check : memChecks_) {
|
||||
if (!(check.cond & MEMCHECK_READ) && !write)
|
||||
continue;
|
||||
if (!(check.cond & MEMCHECK_WRITE) && write)
|
||||
continue;
|
||||
|
||||
MemCheck copy = check;
|
||||
// Toggle the cached part of the address.
|
||||
check.start ^= 0x40000000;
|
||||
if (check.end != 0)
|
||||
check.end ^= 0x40000000;
|
||||
ranges.push_back(check);
|
||||
copy.start ^= 0x40000000;
|
||||
if (copy.end != 0)
|
||||
copy.end ^= 0x40000000;
|
||||
ranges.push_back(copy);
|
||||
}
|
||||
|
||||
return ranges;
|
||||
|
||||
+18
-30
@@ -21,8 +21,7 @@
|
||||
|
||||
#include "Core/Debugger/DebugInterface.h"
|
||||
|
||||
enum BreakAction
|
||||
{
|
||||
enum BreakAction {
|
||||
BREAK_ACTION_IGNORE = 0x00,
|
||||
BREAK_ACTION_LOG = 0x01,
|
||||
BREAK_ACTION_PAUSE = 0x02,
|
||||
@@ -37,35 +36,27 @@ static inline BreakAction operator | (const BreakAction &lhs, const BreakAction
|
||||
return BreakAction((u32)lhs | (u32)rhs);
|
||||
}
|
||||
|
||||
struct BreakPointCond
|
||||
{
|
||||
DebugInterface *debug;
|
||||
struct BreakPointCond {
|
||||
DebugInterface *debug = nullptr;
|
||||
PostfixExpression expression;
|
||||
std::string expressionString;
|
||||
|
||||
BreakPointCond() : debug(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
u32 Evaluate()
|
||||
{
|
||||
u32 Evaluate() {
|
||||
u32 result;
|
||||
if (debug->parseExpression(expression,result) == false) return 0;
|
||||
if (debug->parseExpression(expression, result) == false)
|
||||
return 0;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
struct BreakPoint
|
||||
{
|
||||
BreakPoint() : hasCond(false) {}
|
||||
|
||||
struct BreakPoint {
|
||||
u32 addr;
|
||||
bool temporary;
|
||||
|
||||
BreakAction result;
|
||||
BreakAction result = BREAK_ACTION_IGNORE;
|
||||
std::string logFormat;
|
||||
|
||||
bool hasCond;
|
||||
bool hasCond = false;
|
||||
BreakPointCond cond;
|
||||
|
||||
bool IsEnabled() const {
|
||||
@@ -80,8 +71,7 @@ struct BreakPoint
|
||||
}
|
||||
};
|
||||
|
||||
enum MemCheckCondition
|
||||
{
|
||||
enum MemCheckCondition {
|
||||
MEMCHECK_READ = 0x01,
|
||||
MEMCHECK_WRITE = 0x02,
|
||||
MEMCHECK_WRITE_ONCHANGE = 0x04,
|
||||
@@ -89,21 +79,19 @@ enum MemCheckCondition
|
||||
MEMCHECK_READWRITE = 0x03,
|
||||
};
|
||||
|
||||
struct MemCheck
|
||||
{
|
||||
MemCheck();
|
||||
struct MemCheck {
|
||||
u32 start;
|
||||
u32 end;
|
||||
|
||||
MemCheckCondition cond;
|
||||
BreakAction result;
|
||||
MemCheckCondition cond = MEMCHECK_READ;
|
||||
BreakAction result = BREAK_ACTION_IGNORE;
|
||||
std::string logFormat;
|
||||
|
||||
u32 numHits;
|
||||
u32 numHits = 0;
|
||||
|
||||
u32 lastPC;
|
||||
u32 lastAddr;
|
||||
int lastSize;
|
||||
u32 lastPC = 0;
|
||||
u32 lastAddr = 0;
|
||||
int lastSize = 0;
|
||||
|
||||
BreakAction Action(u32 addr, bool write, int size, u32 pc);
|
||||
void JitBefore(u32 addr, bool write, int size, u32 pc);
|
||||
@@ -168,7 +156,7 @@ public:
|
||||
static u32 CheckSkipFirst();
|
||||
|
||||
// Includes uncached addresses.
|
||||
static const std::vector<MemCheck> GetMemCheckRanges();
|
||||
static const std::vector<MemCheck> GetMemCheckRanges(bool write);
|
||||
|
||||
static const std::vector<MemCheck> GetMemChecks();
|
||||
static const std::vector<BreakPoint> GetBreakpoints();
|
||||
|
||||
+13
-16
@@ -152,10 +152,10 @@ static int Replace_memcpy() {
|
||||
}
|
||||
}
|
||||
RETURN(destPtr);
|
||||
#ifndef MOBILE_DEVICE
|
||||
|
||||
CBreakPoints::ExecMemCheck(srcPtr, false, bytes, currentMIPS->pc);
|
||||
CBreakPoints::ExecMemCheck(destPtr, true, bytes, currentMIPS->pc);
|
||||
#endif
|
||||
|
||||
return 10 + bytes / 4; // approximation
|
||||
}
|
||||
|
||||
@@ -194,10 +194,10 @@ static int Replace_memcpy_jak() {
|
||||
currentMIPS->r[MIPS_REG_A2] = 0;
|
||||
currentMIPS->r[MIPS_REG_A3] = destPtr + bytes;
|
||||
RETURN(destPtr);
|
||||
#ifndef MOBILE_DEVICE
|
||||
|
||||
CBreakPoints::ExecMemCheck(srcPtr, false, bytes, currentMIPS->pc);
|
||||
CBreakPoints::ExecMemCheck(destPtr, true, bytes, currentMIPS->pc);
|
||||
#endif
|
||||
|
||||
return 5 + bytes * 8 + 2; // approximation. This is a slow memcpy - a byte copy loop..
|
||||
}
|
||||
|
||||
@@ -222,10 +222,10 @@ static int Replace_memcpy16() {
|
||||
}
|
||||
}
|
||||
RETURN(destPtr);
|
||||
#ifndef MOBILE_DEVICE
|
||||
|
||||
CBreakPoints::ExecMemCheck(srcPtr, false, bytes, currentMIPS->pc);
|
||||
CBreakPoints::ExecMemCheck(destPtr, true, bytes, currentMIPS->pc);
|
||||
#endif
|
||||
|
||||
return 10 + bytes / 4; // approximation
|
||||
}
|
||||
|
||||
@@ -260,10 +260,10 @@ static int Replace_memcpy_swizzled() {
|
||||
}
|
||||
|
||||
RETURN(0);
|
||||
#ifndef MOBILE_DEVICE
|
||||
|
||||
CBreakPoints::ExecMemCheck(srcPtr, false, pitch * h, currentMIPS->pc);
|
||||
CBreakPoints::ExecMemCheck(destPtr, true, pitch * h, currentMIPS->pc);
|
||||
#endif
|
||||
|
||||
return 10 + (pitch * h) / 4; // approximation
|
||||
}
|
||||
|
||||
@@ -288,10 +288,10 @@ static int Replace_memmove() {
|
||||
}
|
||||
}
|
||||
RETURN(destPtr);
|
||||
#ifndef MOBILE_DEVICE
|
||||
|
||||
CBreakPoints::ExecMemCheck(srcPtr, false, bytes, currentMIPS->pc);
|
||||
CBreakPoints::ExecMemCheck(destPtr, true, bytes, currentMIPS->pc);
|
||||
#endif
|
||||
|
||||
return 10 + bytes / 4; // approximation
|
||||
}
|
||||
|
||||
@@ -310,9 +310,9 @@ static int Replace_memset() {
|
||||
}
|
||||
}
|
||||
RETURN(destPtr);
|
||||
#ifndef MOBILE_DEVICE
|
||||
|
||||
CBreakPoints::ExecMemCheck(destPtr, true, bytes, currentMIPS->pc);
|
||||
#endif
|
||||
|
||||
return 10 + bytes / 4; // approximation
|
||||
}
|
||||
|
||||
@@ -342,9 +342,8 @@ static int Replace_memset_jak() {
|
||||
currentMIPS->r[MIPS_REG_A3] = -1;
|
||||
RETURN(destPtr);
|
||||
|
||||
#ifndef MOBILE_DEVICE
|
||||
CBreakPoints::ExecMemCheck(destPtr, true, bytes, currentMIPS->pc);
|
||||
#endif
|
||||
|
||||
return 5 + bytes * 6 + 2; // approximation (hm, inspecting the disasm this should be 5 + 6 * bytes + 2, but this is what works..)
|
||||
}
|
||||
|
||||
@@ -591,11 +590,9 @@ static int Replace_dl_write_matrix() {
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef MOBILE_DEVICE
|
||||
CBreakPoints::ExecMemCheck(PARAM(2), false, count * sizeof(float), currentMIPS->pc);
|
||||
CBreakPoints::ExecMemCheck(PARAM(0) + 2 * sizeof(u32), true, sizeof(u32), currentMIPS->pc);
|
||||
CBreakPoints::ExecMemCheck(dlStruct[2], true, (count + 1) * sizeof(u32), currentMIPS->pc);
|
||||
#endif
|
||||
|
||||
dlStruct[2] += (1 + count) * 4;
|
||||
RETURN(dlStruct[2]);
|
||||
|
||||
@@ -654,10 +654,10 @@ static u32 sceKernelMemcpy(u32 dst, u32 src, u32 size)
|
||||
*dstp++ = *srcp++;
|
||||
}
|
||||
}
|
||||
#ifndef MOBILE_DEVICE
|
||||
|
||||
CBreakPoints::ExecMemCheck(src, false, size, currentMIPS->pc);
|
||||
CBreakPoints::ExecMemCheck(dst, true, size, currentMIPS->pc);
|
||||
#endif
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
|
||||
+2
-12
@@ -766,9 +766,8 @@ int MediaEngine::writeVideoImage(u32 bufferPtr, int frameWidth, int videoPixelMo
|
||||
delete [] imgbuf;
|
||||
}
|
||||
|
||||
#ifndef MOBILE_DEVICE
|
||||
CBreakPoints::ExecMemCheck(bufferPtr, true, videoImageSize, currentMIPS->pc);
|
||||
#endif
|
||||
|
||||
return videoImageSize;
|
||||
#endif // USE_FFMPEG
|
||||
return 0;
|
||||
@@ -822,9 +821,7 @@ int MediaEngine::writeVideoImageWithRange(u32 bufferPtr, int frameWidth, int vid
|
||||
writeVideoLineRGBA(imgbuf, data, width);
|
||||
data += m_desWidth * sizeof(u32);
|
||||
imgbuf += videoLineSize;
|
||||
#ifndef MOBILE_DEVICE
|
||||
CBreakPoints::ExecMemCheck(bufferPtr + y * frameWidth * sizeof(u32), true, width * sizeof(u32), currentMIPS->pc);
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -834,9 +831,7 @@ int MediaEngine::writeVideoImageWithRange(u32 bufferPtr, int frameWidth, int vid
|
||||
writeVideoLineABGR5650(imgbuf, data, width);
|
||||
data += m_desWidth * sizeof(u16);
|
||||
imgbuf += videoLineSize;
|
||||
#ifndef MOBILE_DEVICE
|
||||
CBreakPoints::ExecMemCheck(bufferPtr + y * frameWidth * sizeof(u16), true, width * sizeof(u16), currentMIPS->pc);
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -846,9 +841,7 @@ int MediaEngine::writeVideoImageWithRange(u32 bufferPtr, int frameWidth, int vid
|
||||
writeVideoLineABGR5551(imgbuf, data, width);
|
||||
data += m_desWidth * sizeof(u16);
|
||||
imgbuf += videoLineSize;
|
||||
#ifndef MOBILE_DEVICE
|
||||
CBreakPoints::ExecMemCheck(bufferPtr + y * frameWidth * sizeof(u16), true, width * sizeof(u16), currentMIPS->pc);
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -858,9 +851,7 @@ int MediaEngine::writeVideoImageWithRange(u32 bufferPtr, int frameWidth, int vid
|
||||
writeVideoLineABGR4444(imgbuf, data, width);
|
||||
data += m_desWidth * sizeof(u16);
|
||||
imgbuf += videoLineSize;
|
||||
#ifndef MOBILE_DEVICE
|
||||
CBreakPoints::ExecMemCheck(bufferPtr + y * frameWidth * sizeof(u16), true, width * sizeof(u16), currentMIPS->pc);
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -954,9 +945,8 @@ int MediaEngine::getAudioSamples(u32 bufferPtr) {
|
||||
if (!m_audioContext->Decode(audioFrame, frameSize, buffer, &outbytes)) {
|
||||
ERROR_LOG(ME, "Audio (%s) decode failed during video playback", GetCodecName(m_audioType));
|
||||
}
|
||||
#ifndef MOBILE_DEVICE
|
||||
|
||||
CBreakPoints::ExecMemCheck(bufferPtr, true, outbytes, currentMIPS->pc);
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0x2000;
|
||||
|
||||
@@ -50,8 +50,6 @@ public:
|
||||
virtual void SaveSymbolMap() {}
|
||||
virtual void SetWindowTitle(const char *message) {}
|
||||
|
||||
virtual void SendCoreWait(bool) {}
|
||||
|
||||
// While debugging is active, it's perfectly fine for these to block.
|
||||
virtual bool GPUDebuggingActive() { return false; }
|
||||
virtual void GPUNotifyCommand(u32 pc) {}
|
||||
|
||||
@@ -238,7 +238,6 @@ void ArmJit::GenerateFixedCode() {
|
||||
B_CC(CC_EQ, outerLoop);
|
||||
|
||||
SetJumpTarget(badCoreState);
|
||||
breakpointBailout = GetCodePtr();
|
||||
|
||||
SaveDowncount();
|
||||
RestoreRoundingMode(true);
|
||||
|
||||
@@ -93,6 +93,7 @@ extern int logBlocks;
|
||||
void ArmJit::Comp_FPULS(MIPSOpcode op)
|
||||
{
|
||||
CONDITIONAL_DISABLE;
|
||||
CheckMemoryBreakpoint();
|
||||
|
||||
s32 offset = (s16)(op & 0xFFFF);
|
||||
int ft = _FT;
|
||||
|
||||
@@ -112,6 +112,7 @@ namespace MIPSComp
|
||||
|
||||
void ArmJit::Comp_ITypeMemLR(MIPSOpcode op, bool load) {
|
||||
CONDITIONAL_DISABLE;
|
||||
CheckMemoryBreakpoint();
|
||||
int offset = (signed short)(op & 0xFFFF);
|
||||
MIPSGPReg rt = _RT;
|
||||
MIPSGPReg rs = _RS;
|
||||
@@ -120,6 +121,7 @@ namespace MIPSComp
|
||||
if (!js.inDelaySlot) {
|
||||
// Optimisation: Combine to single unaligned load/store
|
||||
bool isLeft = (o == 34 || o == 42);
|
||||
CheckMemoryBreakpoint(1);
|
||||
MIPSOpcode nextOp = GetOffsetInstruction(1);
|
||||
// Find a matching shift in opposite direction with opposite offset.
|
||||
if (nextOp == (isLeft ? (op.encoding + (4<<26) - 3)
|
||||
@@ -259,6 +261,7 @@ namespace MIPSComp
|
||||
void ArmJit::Comp_ITypeMem(MIPSOpcode op)
|
||||
{
|
||||
CONDITIONAL_DISABLE;
|
||||
CheckMemoryBreakpoint();
|
||||
int offset = (signed short)(op&0xFFFF);
|
||||
bool load = false;
|
||||
MIPSGPReg rt = _RT;
|
||||
|
||||
@@ -226,6 +226,7 @@ namespace MIPSComp
|
||||
void ArmJit::Comp_SV(MIPSOpcode op) {
|
||||
NEON_IF_AVAILABLE(CompNEON_SV);
|
||||
CONDITIONAL_DISABLE;
|
||||
CheckMemoryBreakpoint();
|
||||
|
||||
s32 offset = (signed short)(op & 0xFFFC);
|
||||
int vt = ((op >> 16) & 0x1f) | ((op & 3) << 5);
|
||||
@@ -332,6 +333,7 @@ namespace MIPSComp
|
||||
{
|
||||
NEON_IF_AVAILABLE(CompNEON_SVQ);
|
||||
CONDITIONAL_DISABLE;
|
||||
CheckMemoryBreakpoint();
|
||||
|
||||
int imm = (signed short)(op&0xFFFC);
|
||||
int vt = (((op >> 16) & 0x1f)) | ((op&1) << 5);
|
||||
|
||||
@@ -159,6 +159,7 @@ void ArmJit::CompNEON_VecDo3(MIPSOpcode op) {
|
||||
|
||||
void ArmJit::CompNEON_SV(MIPSOpcode op) {
|
||||
CONDITIONAL_DISABLE;
|
||||
CheckMemoryBreakpoint();
|
||||
|
||||
// Remember to use single lane stores here and not VLDR/VSTR - switching usage
|
||||
// between NEON and VFPU can be expensive on some chips.
|
||||
@@ -276,6 +277,7 @@ inline int MIPS_GET_VQVT(u32 op) {
|
||||
|
||||
void ArmJit::CompNEON_SVQ(MIPSOpcode op) {
|
||||
CONDITIONAL_DISABLE;
|
||||
CheckMemoryBreakpoint();
|
||||
|
||||
int offset = (signed short)(op & 0xFFFC);
|
||||
int vt = MIPS_GET_VQVT(op.encoding);
|
||||
|
||||
+111
-4
@@ -26,6 +26,7 @@
|
||||
#include "Core/Config.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/CoreTiming.h"
|
||||
#include "Core/Debugger/Breakpoints.h"
|
||||
#include "Core/Debugger/SymbolMap.h"
|
||||
#include "Core/MemMap.h"
|
||||
|
||||
@@ -45,7 +46,7 @@
|
||||
|
||||
using namespace ArmJitConstants;
|
||||
|
||||
void DisassembleArm(const u8 *data, int size) {
|
||||
static void DisassembleArm(const u8 *data, int size) {
|
||||
char temp[256];
|
||||
for (int i = 0; i < size; i += 4) {
|
||||
const u32 *codePtr = (const u32 *)(data + i);
|
||||
@@ -69,6 +70,33 @@ void DisassembleArm(const u8 *data, int size) {
|
||||
}
|
||||
}
|
||||
|
||||
static u32 JitBreakpoint() {
|
||||
// Should we skip this breakpoint?
|
||||
if (CBreakPoints::CheckSkipFirst() == currentMIPS->pc)
|
||||
return 0;
|
||||
|
||||
BreakAction result = CBreakPoints::ExecBreakPoint(currentMIPS->pc);
|
||||
if ((result & BREAK_ACTION_PAUSE) == 0)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static u32 JitMemCheck(u32 pc) {
|
||||
if (CBreakPoints::CheckSkipFirst() == currentMIPS->pc)
|
||||
return 0;
|
||||
|
||||
// Note: pc may be the delay slot.
|
||||
const auto op = Memory::Read_Instruction(pc, true);
|
||||
s32 offset = (s16)(op & 0xFFFF);
|
||||
if (MIPSGetInfo(op) & IS_VFPU)
|
||||
offset &= 0xFFFC;
|
||||
u32 addr = currentMIPS->r[MIPS_GET_RS(op)] + offset;
|
||||
|
||||
CBreakPoints::ExecOpMemCheck(addr, pc);
|
||||
return coreState == CORE_RUNNING || coreState == CORE_NEXTFRAME ? 0 : 1;
|
||||
}
|
||||
|
||||
namespace MIPSComp
|
||||
{
|
||||
using namespace ArmGen;
|
||||
@@ -86,6 +114,10 @@ ArmJit::ArmJit(MIPSState *mips) : blocks(mips, this), gpr(mips, &js, &jo), fpr(m
|
||||
INFO_LOG(JIT, "ARM JIT initialized: %d MB of code space", GetSpaceLeft() / (1024 * 1024));
|
||||
|
||||
js.startDefaultPrefix = mips_->HasDefaultPrefix();
|
||||
|
||||
// The debugger sets this so that "go" on a breakpoint will actually... go.
|
||||
// But if they reset, we can end up hitting it by mistake, since it's based on PC and ticks.
|
||||
CBreakPoints::SetSkipFirst(0);
|
||||
}
|
||||
|
||||
ArmJit::~ArmJit() {
|
||||
@@ -104,6 +136,10 @@ void ArmJit::DoState(PointerWrap &p)
|
||||
} else {
|
||||
js.hasSetRounding = 1;
|
||||
}
|
||||
|
||||
// The debugger sets this so that "go" on a breakpoint will actually... go.
|
||||
// But if they reset, we can end up hitting it by mistake, since it's based on PC and ticks.
|
||||
CBreakPoints::SetSkipFirst(0);
|
||||
}
|
||||
|
||||
void ArmJit::UpdateFCR31() {
|
||||
@@ -158,13 +194,16 @@ void ArmJit::EatInstruction(MIPSOpcode op) {
|
||||
ERROR_LOG_REPORT_ONCE(ateInDelaySlot, JIT, "Ate an instruction inside a delay slot.");
|
||||
}
|
||||
|
||||
CheckJitBreakpoint(GetCompilerPC() + 4, 0);
|
||||
js.numInstructions++;
|
||||
js.compilerPC += 4;
|
||||
js.downcountAmount += MIPSGetInstructionCycleEstimate(op);
|
||||
}
|
||||
|
||||
void ArmJit::CompileDelaySlot(int flags)
|
||||
{
|
||||
void ArmJit::CompileDelaySlot(int flags) {
|
||||
// Need to offset the downcount which was already incremented for the branch + delay slot.
|
||||
CheckJitBreakpoint(GetCompilerPC() + 4, -2);
|
||||
|
||||
// preserve flag around the delay slot! Maybe this is not always necessary on ARM where
|
||||
// we can (mostly) control whether we set the flag or not. Of course, if someone puts an slt in to the
|
||||
// delay slot, we're screwed.
|
||||
@@ -295,6 +334,9 @@ const u8 *ArmJit::DoJit(u32 em_address, JitBlock *b)
|
||||
while (js.compiling)
|
||||
{
|
||||
gpr.SetCompilerPC(GetCompilerPC()); // Let it know for log messages
|
||||
// Jit breakpoints are quite fast, so let's do them in release too.
|
||||
CheckJitBreakpoint(GetCompilerPC(), 0);
|
||||
|
||||
MIPSOpcode inst = Memory::Read_Opcode_JIT(GetCompilerPC());
|
||||
//MIPSInfo info = MIPSGetInfo(inst);
|
||||
//if (info & IS_VFPU) {
|
||||
@@ -492,7 +534,18 @@ void ArmJit::Comp_ReplacementFunc(MIPSOpcode op)
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry->flags & REPFLAG_DISABLED) {
|
||||
u32 funcSize = g_symbolMap->GetFunctionSize(GetCompilerPC());
|
||||
bool disabled = (entry->flags & REPFLAG_DISABLED) != 0;
|
||||
if (!disabled && funcSize != SymbolMap::INVALID_ADDRESS && funcSize > sizeof(u32)) {
|
||||
// We don't need to disable hooks, the code will still run.
|
||||
if ((entry->flags & (REPFLAG_HOOKENTER | REPFLAG_HOOKEXIT)) == 0) {
|
||||
// Any breakpoint at the func entry was already tripped, so we can still run the replacement.
|
||||
// That's a common case - just to see how often the replacement hits.
|
||||
disabled = CBreakPoints::RangeContainsBreakPoint(GetCompilerPC() + sizeof(u32), funcSize - sizeof(u32));
|
||||
}
|
||||
}
|
||||
|
||||
if (disabled) {
|
||||
MIPSCompileOp(Memory::Read_Instruction(GetCompilerPC(), true), this);
|
||||
} else if (entry->jitReplaceFunc) {
|
||||
MIPSReplaceFunc repl = entry->jitReplaceFunc;
|
||||
@@ -686,6 +739,60 @@ void ArmJit::WriteSyscallExit()
|
||||
B((const void *)dispatcherCheckCoreState);
|
||||
}
|
||||
|
||||
bool ArmJit::CheckJitBreakpoint(u32 addr, int downcountOffset) {
|
||||
if (CBreakPoints::IsAddressBreakPoint(addr)) {
|
||||
MRS(R8);
|
||||
FlushAll();
|
||||
MOVI2R(SCRATCHREG1, GetCompilerPC());
|
||||
MovToPC(SCRATCHREG1);
|
||||
RestoreRoundingMode();
|
||||
QuickCallFunction(SCRATCHREG1, &JitBreakpoint);
|
||||
|
||||
// If 0, the conditional breakpoint wasn't taken.
|
||||
CMPI2R(R0, 0, SCRATCHREG2);
|
||||
FixupBranch skip = B_CC(CC_EQ);
|
||||
WriteDownCount(downcountOffset);
|
||||
ApplyRoundingMode();
|
||||
B((const void *)dispatcherCheckCoreState);
|
||||
SetJumpTarget(skip);
|
||||
|
||||
ApplyRoundingMode();
|
||||
_MSR(true, false, R8);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ArmJit::CheckMemoryBreakpoint(int instructionOffset) {
|
||||
if (CBreakPoints::HasMemChecks()) {
|
||||
int off = instructionOffset + (js.inDelaySlot ? 1 : 0);
|
||||
|
||||
MRS(R8);
|
||||
FlushAll();
|
||||
RestoreRoundingMode();
|
||||
MOVI2R(R0, GetCompilerPC());
|
||||
MovToPC(R0);
|
||||
if (off != 0)
|
||||
ADDI2R(R0, R0, off, SCRATCHREG2);
|
||||
QuickCallFunction(SCRATCHREG2, &JitMemCheck);
|
||||
|
||||
// If 0, the breakpoint wasn't tripped.
|
||||
CMPI2R(R0, 0, SCRATCHREG2);
|
||||
FixupBranch skip = B_CC(CC_EQ);
|
||||
WriteDownCount(-1 - off);
|
||||
ApplyRoundingMode();
|
||||
B((const void *)dispatcherCheckCoreState);
|
||||
SetJumpTarget(skip);
|
||||
|
||||
ApplyRoundingMode();
|
||||
_MSR(true, false, R8);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ArmJit::Comp_DoNothing(MIPSOpcode op) { }
|
||||
|
||||
MIPSOpcode ArmJit::GetOriginalOp(MIPSOpcode op) {
|
||||
|
||||
@@ -215,6 +215,8 @@ private:
|
||||
void WriteExit(u32 destination, int exit_num);
|
||||
void WriteExitDestInR(ArmGen::ARMReg Reg);
|
||||
void WriteSyscallExit();
|
||||
bool CheckJitBreakpoint(u32 addr, int downcountOffset);
|
||||
bool CheckMemoryBreakpoint(int instructionOffset = 0);
|
||||
|
||||
// Utility compilation functions
|
||||
void BranchFPFlag(MIPSOpcode op, CCFlags cc, bool likely);
|
||||
@@ -311,8 +313,6 @@ public:
|
||||
|
||||
const u8 *restoreRoundingMode;
|
||||
const u8 *applyRoundingMode;
|
||||
|
||||
const u8 *breakpointBailout;
|
||||
};
|
||||
|
||||
} // namespace MIPSComp
|
||||
|
||||
@@ -275,7 +275,6 @@ void Arm64Jit::GenerateFixedCode(const JitOptions &jo) {
|
||||
B(CC_EQ, outerLoop);
|
||||
|
||||
SetJumpTarget(badCoreState);
|
||||
breakpointBailout = GetCodePtr();
|
||||
|
||||
SaveStaticRegisters();
|
||||
RestoreRoundingMode(true);
|
||||
|
||||
@@ -81,6 +81,7 @@ void Arm64Jit::Comp_FPU3op(MIPSOpcode op) {
|
||||
void Arm64Jit::Comp_FPULS(MIPSOpcode op)
|
||||
{
|
||||
CONDITIONAL_DISABLE;
|
||||
CheckMemoryBreakpoint();
|
||||
|
||||
// Surprisingly, these work fine alraedy.
|
||||
|
||||
@@ -88,7 +89,6 @@ void Arm64Jit::Comp_FPULS(MIPSOpcode op)
|
||||
int ft = _FT;
|
||||
MIPSGPReg rs = _RS;
|
||||
// u32 addr = R(rs) + offset;
|
||||
// logBlocks = 1;
|
||||
std::vector<FixupBranch> skips;
|
||||
switch (op >> 26) {
|
||||
case 49: //FI(ft) = Memory::Read_U32(addr); break; //lwc1
|
||||
|
||||
@@ -111,6 +111,7 @@ namespace MIPSComp {
|
||||
|
||||
void Arm64Jit::Comp_ITypeMemLR(MIPSOpcode op, bool load) {
|
||||
CONDITIONAL_DISABLE;
|
||||
CheckMemoryBreakpoint();
|
||||
int offset = (signed short)(op & 0xFFFF);
|
||||
MIPSGPReg rt = _RT;
|
||||
MIPSGPReg rs = _RS;
|
||||
@@ -119,6 +120,7 @@ namespace MIPSComp {
|
||||
if (!js.inDelaySlot) {
|
||||
// Optimisation: Combine to single unaligned load/store
|
||||
bool isLeft = (o == 34 || o == 42);
|
||||
CheckMemoryBreakpoint(1);
|
||||
MIPSOpcode nextOp = GetOffsetInstruction(1);
|
||||
// Find a matching shift in opposite direction with opposite offset.
|
||||
if (nextOp == (isLeft ? (op.encoding + (4 << 26) - 3) : (op.encoding - (4 << 26) + 3))) {
|
||||
@@ -255,6 +257,7 @@ namespace MIPSComp {
|
||||
|
||||
void Arm64Jit::Comp_ITypeMem(MIPSOpcode op) {
|
||||
CONDITIONAL_DISABLE;
|
||||
CheckMemoryBreakpoint();
|
||||
|
||||
int offset = (signed short)(op & 0xFFFF);
|
||||
bool load = false;
|
||||
|
||||
@@ -202,6 +202,7 @@ namespace MIPSComp {
|
||||
|
||||
void Arm64Jit::Comp_SV(MIPSOpcode op) {
|
||||
CONDITIONAL_DISABLE;
|
||||
CheckMemoryBreakpoint();
|
||||
|
||||
s32 offset = (signed short)(op & 0xFFFC);
|
||||
int vt = ((op >> 16) & 0x1f) | ((op & 3) << 5);
|
||||
@@ -275,6 +276,7 @@ namespace MIPSComp {
|
||||
|
||||
void Arm64Jit::Comp_SVQ(MIPSOpcode op) {
|
||||
CONDITIONAL_DISABLE;
|
||||
CheckMemoryBreakpoint();
|
||||
|
||||
int imm = (signed short)(op&0xFFFC);
|
||||
int vt = (((op >> 16) & 0x1f)) | ((op&1) << 5);
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "Core/Config.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/CoreTiming.h"
|
||||
#include "Core/Debugger/Breakpoints.h"
|
||||
#include "Core/Debugger/SymbolMap.h"
|
||||
#include "Core/MemMap.h"
|
||||
|
||||
@@ -44,7 +45,7 @@
|
||||
|
||||
using namespace Arm64JitConstants;
|
||||
|
||||
void DisassembleArm64Print(const u8 *data, int size) {
|
||||
static void DisassembleArm64Print(const u8 *data, int size) {
|
||||
std::vector<std::string> lines = DisassembleArm64(data, size);
|
||||
for (auto s : lines) {
|
||||
ILOG("%s", s.c_str());
|
||||
@@ -60,6 +61,33 @@ void DisassembleArm64Print(const u8 *data, int size) {
|
||||
ILOG("===");*/
|
||||
}
|
||||
|
||||
static u32 JitBreakpoint() {
|
||||
// Should we skip this breakpoint?
|
||||
if (CBreakPoints::CheckSkipFirst() == currentMIPS->pc)
|
||||
return 0;
|
||||
|
||||
BreakAction result = CBreakPoints::ExecBreakPoint(currentMIPS->pc);
|
||||
if ((result & BREAK_ACTION_PAUSE) == 0)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static u32 JitMemCheck(u32 pc) {
|
||||
if (CBreakPoints::CheckSkipFirst() == currentMIPS->pc)
|
||||
return 0;
|
||||
|
||||
// Note: pc may be the delay slot.
|
||||
const auto op = Memory::Read_Instruction(pc, true);
|
||||
s32 offset = (s16)(op & 0xFFFF);
|
||||
if (MIPSGetInfo(op) & IS_VFPU)
|
||||
offset &= 0xFFFC;
|
||||
u32 addr = currentMIPS->r[MIPS_GET_RS(op)] + offset;
|
||||
|
||||
CBreakPoints::ExecOpMemCheck(addr, pc);
|
||||
return coreState == CORE_RUNNING || coreState == CORE_NEXTFRAME ? 0 : 1;
|
||||
}
|
||||
|
||||
namespace MIPSComp
|
||||
{
|
||||
using namespace Arm64Gen;
|
||||
@@ -80,6 +108,10 @@ Arm64Jit::Arm64Jit(MIPSState *mips) : blocks(mips, this), gpr(mips, &js, &jo), f
|
||||
GenerateFixedCode(jo);
|
||||
js.startDefaultPrefix = mips_->HasDefaultPrefix();
|
||||
js.currentRoundingFunc = convertS0ToSCRATCH1[mips_->fcr31 & 3];
|
||||
|
||||
// The debugger sets this so that "go" on a breakpoint will actually... go.
|
||||
// But if they reset, we can end up hitting it by mistake, since it's based on PC and ticks.
|
||||
CBreakPoints::SetSkipFirst(0);
|
||||
}
|
||||
|
||||
Arm64Jit::~Arm64Jit() {
|
||||
@@ -99,6 +131,10 @@ void Arm64Jit::DoState(PointerWrap &p) {
|
||||
}
|
||||
|
||||
// Note: we can't update the currentRoundingFunc here because fcr31 wasn't loaded yet.
|
||||
|
||||
// The debugger sets this so that "go" on a breakpoint will actually... go.
|
||||
// But if they reset, we can end up hitting it by mistake, since it's based on PC and ticks.
|
||||
CBreakPoints::SetSkipFirst(0);
|
||||
}
|
||||
|
||||
void Arm64Jit::UpdateFCR31() {
|
||||
@@ -151,12 +187,16 @@ void Arm64Jit::EatInstruction(MIPSOpcode op) {
|
||||
ERROR_LOG_REPORT_ONCE(ateInDelaySlot, JIT, "Ate an instruction inside a delay slot.");
|
||||
}
|
||||
|
||||
CheckJitBreakpoint(GetCompilerPC() + 4, 0);
|
||||
js.numInstructions++;
|
||||
js.compilerPC += 4;
|
||||
js.downcountAmount += MIPSGetInstructionCycleEstimate(op);
|
||||
}
|
||||
|
||||
void Arm64Jit::CompileDelaySlot(int flags) {
|
||||
// Need to offset the downcount which was already incremented for the branch + delay slot.
|
||||
CheckJitBreakpoint(GetCompilerPC() + 4, -2);
|
||||
|
||||
// preserve flag around the delay slot! Maybe this is not always necessary on ARM where
|
||||
// we can (mostly) control whether we set the flag or not. Of course, if someone puts an slt in to the
|
||||
// delay slot, we're screwed.
|
||||
@@ -285,8 +325,10 @@ const u8 *Arm64Jit::DoJit(u32 em_address, JitBlock *b) {
|
||||
js.numInstructions = 0;
|
||||
while (js.compiling) {
|
||||
gpr.SetCompilerPC(GetCompilerPC()); // Let it know for log messages
|
||||
// Jit breakpoints are quite fast, so let's do them in release too.
|
||||
CheckJitBreakpoint(GetCompilerPC(), 0);
|
||||
|
||||
MIPSOpcode inst = Memory::Read_Opcode_JIT(GetCompilerPC());
|
||||
|
||||
js.downcountAmount += MIPSGetInstructionCycleEstimate(inst);
|
||||
|
||||
MIPSCompileOp(inst, this);
|
||||
@@ -473,7 +515,18 @@ void Arm64Jit::Comp_ReplacementFunc(MIPSOpcode op)
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry->flags & REPFLAG_DISABLED) {
|
||||
u32 funcSize = g_symbolMap->GetFunctionSize(GetCompilerPC());
|
||||
bool disabled = (entry->flags & REPFLAG_DISABLED) != 0;
|
||||
if (!disabled && funcSize != SymbolMap::INVALID_ADDRESS && funcSize > sizeof(u32)) {
|
||||
// We don't need to disable hooks, the code will still run.
|
||||
if ((entry->flags & (REPFLAG_HOOKENTER | REPFLAG_HOOKEXIT)) == 0) {
|
||||
// Any breakpoint at the func entry was already tripped, so we can still run the replacement.
|
||||
// That's a common case - just to see how often the replacement hits.
|
||||
disabled = CBreakPoints::RangeContainsBreakPoint(GetCompilerPC() + sizeof(u32), funcSize - sizeof(u32));
|
||||
}
|
||||
}
|
||||
|
||||
if (disabled) {
|
||||
MIPSCompileOp(Memory::Read_Instruction(GetCompilerPC(), true), this);
|
||||
} else if (entry->jitReplaceFunc) {
|
||||
MIPSReplaceFunc repl = entry->jitReplaceFunc;
|
||||
@@ -646,6 +699,60 @@ void Arm64Jit::WriteSyscallExit() {
|
||||
B((const void *)dispatcherCheckCoreState);
|
||||
}
|
||||
|
||||
bool Arm64Jit::CheckJitBreakpoint(u32 addr, int downcountOffset) {
|
||||
if (CBreakPoints::IsAddressBreakPoint(addr)) {
|
||||
MRS(FLAGTEMPREG, FIELD_NZCV);
|
||||
FlushAll();
|
||||
MOVI2R(SCRATCH1, GetCompilerPC());
|
||||
MovToPC(SCRATCH1);
|
||||
RestoreRoundingMode();
|
||||
QuickCallFunction(SCRATCH1_64, &JitBreakpoint);
|
||||
|
||||
// If 0, the conditional breakpoint wasn't taken.
|
||||
CMPI2R(W0, 0);
|
||||
FixupBranch skip = B(CC_EQ);
|
||||
WriteDownCount(downcountOffset);
|
||||
ApplyRoundingMode();
|
||||
B((const void *)dispatcherCheckCoreState);
|
||||
SetJumpTarget(skip);
|
||||
|
||||
ApplyRoundingMode();
|
||||
_MSR(FIELD_NZCV, FLAGTEMPREG);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Arm64Jit::CheckMemoryBreakpoint(int instructionOffset) {
|
||||
if (CBreakPoints::HasMemChecks()) {
|
||||
int off = instructionOffset + (js.inDelaySlot ? 1 : 0);
|
||||
|
||||
MRS(FLAGTEMPREG, FIELD_NZCV);
|
||||
FlushAll();
|
||||
RestoreRoundingMode();
|
||||
MOVI2R(W0, GetCompilerPC());
|
||||
MovToPC(W0);
|
||||
if (off != 0)
|
||||
ADDI2R(W0, W0, off * 4);
|
||||
QuickCallFunction(SCRATCH2_64, &JitMemCheck);
|
||||
|
||||
// If 0, the breakpoint wasn't tripped.
|
||||
CMPI2R(W0, 0);
|
||||
FixupBranch skip = B(CC_EQ);
|
||||
WriteDownCount(-1 - off);
|
||||
ApplyRoundingMode();
|
||||
B((const void *)dispatcherCheckCoreState);
|
||||
SetJumpTarget(skip);
|
||||
|
||||
ApplyRoundingMode();
|
||||
_MSR(FIELD_NZCV, FLAGTEMPREG);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Arm64Jit::Comp_DoNothing(MIPSOpcode op) { }
|
||||
|
||||
MIPSOpcode Arm64Jit::GetOriginalOp(MIPSOpcode op) {
|
||||
|
||||
@@ -214,6 +214,8 @@ private:
|
||||
void WriteExit(u32 destination, int exit_num);
|
||||
void WriteExitDestInR(Arm64Gen::ARM64Reg Reg);
|
||||
void WriteSyscallExit();
|
||||
bool CheckJitBreakpoint(u32 addr, int downcountOffset);
|
||||
bool CheckMemoryBreakpoint(int instructionOffset = 0);
|
||||
|
||||
// Utility compilation functions
|
||||
void BranchFPFlag(MIPSOpcode op, CCFlags cc, bool likely);
|
||||
@@ -272,8 +274,6 @@ public:
|
||||
const u8 *dispatcher;
|
||||
const u8 *dispatcherNoCheck;
|
||||
|
||||
const u8 *breakpointBailout;
|
||||
|
||||
const u8 *saveStaticRegisters;
|
||||
const u8 *loadStaticRegisters;
|
||||
|
||||
|
||||
@@ -181,8 +181,6 @@ public:
|
||||
const u8 *dispatcherPCInR0;
|
||||
const u8 *dispatcher;
|
||||
const u8 *dispatcherNoCheck;
|
||||
|
||||
const u8 *breakpointBailout;
|
||||
};
|
||||
|
||||
typedef void (MipsJit::*MIPSCompileFunc)(MIPSOpcode opcode);
|
||||
|
||||
@@ -211,11 +211,6 @@ void Jit::GenerateFixedCode(JitOptions &jo) {
|
||||
ABI_PopAllCalleeSavedRegsAndAdjustStack();
|
||||
RET();
|
||||
|
||||
breakpointBailout = GetCodePtr();
|
||||
RestoreRoundingMode(true);
|
||||
ABI_PopAllCalleeSavedRegsAndAdjustStack();
|
||||
RET();
|
||||
|
||||
// Let's spare the pre-generated code from unprotect-reprotect.
|
||||
endOfPregeneratedCode = AlignCodePage();
|
||||
EndWrite();
|
||||
|
||||
@@ -320,8 +320,6 @@ private:
|
||||
const u8 *dispatcherNoCheck;
|
||||
const u8 *dispatcherInEAXNoCheck;
|
||||
|
||||
const u8 *breakpointBailout;
|
||||
|
||||
const u8 *restoreRoundingMode;
|
||||
const u8 *applyRoundingMode;
|
||||
|
||||
|
||||
@@ -376,17 +376,10 @@ void JitSafeMem::MemCheckImm(MemoryOpType type)
|
||||
|
||||
void JitSafeMem::MemCheckAsm(MemoryOpType type)
|
||||
{
|
||||
const auto memchecks = CBreakPoints::GetMemCheckRanges();
|
||||
bool possible = false;
|
||||
const auto memchecks = CBreakPoints::GetMemCheckRanges(type == MEM_WRITE);
|
||||
bool possible = !memchecks.empty();
|
||||
for (auto it = memchecks.begin(), end = memchecks.end(); it != end; ++it)
|
||||
{
|
||||
if (!(it->cond & MEMCHECK_READ) && type == MEM_READ)
|
||||
continue;
|
||||
if (!(it->cond & MEMCHECK_WRITE) && type == MEM_WRITE)
|
||||
continue;
|
||||
|
||||
possible = true;
|
||||
|
||||
FixupBranch skipNext, skipNextRange;
|
||||
if (it->end != 0)
|
||||
{
|
||||
|
||||
+1
-2
@@ -446,9 +446,8 @@ void Memset(const u32 _Address, const u8 _iValue, const u32 _iLength) {
|
||||
for (size_t i = 0; i < _iLength; i++)
|
||||
Write_U8(_iValue, (u32)(_Address + i));
|
||||
}
|
||||
#ifndef MOBILE_DEVICE
|
||||
|
||||
CBreakPoints::ExecMemCheck(_Address, true, _iLength, currentMIPS->pc);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -33,9 +33,7 @@ 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);
|
||||
#ifndef MOBILE_DEVICE
|
||||
CBreakPoints::ExecMemCheck(to_address, true, len, currentMIPS->pc);
|
||||
#endif
|
||||
}
|
||||
// if not, GetPointer will log.
|
||||
}
|
||||
@@ -45,9 +43,7 @@ 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);
|
||||
#ifndef MOBILE_DEVICE
|
||||
CBreakPoints::ExecMemCheck(from_address, false, len, currentMIPS->pc);
|
||||
#endif
|
||||
}
|
||||
// if not, GetPointer will log.
|
||||
}
|
||||
@@ -55,9 +51,7 @@ inline void Memcpy(void *to_data, const u32 from_address, const u32 len)
|
||||
inline void Memcpy(const u32 to_address, const u32 from_address, const u32 len)
|
||||
{
|
||||
Memcpy(GetPointer(to_address), from_address, len);
|
||||
#ifndef MOBILE_DEVICE
|
||||
CBreakPoints::ExecMemCheck(to_address, true, len, currentMIPS->pc);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Memset(const u32 _Address, const u8 _Data, const u32 _iLength);
|
||||
|
||||
@@ -430,10 +430,24 @@ void PSP_EndHostFrame() {
|
||||
}
|
||||
}
|
||||
|
||||
void PSP_RunLoopWhileState() {
|
||||
// We just run the CPU until we get to vblank. This will quickly sync up pretty nicely.
|
||||
// The actual number of cycles doesn't matter so much here as we will break due to CORE_NEXTFRAME, most of the time hopefully...
|
||||
int blockTicks = usToCycles(1000000 / 10);
|
||||
|
||||
// Run until CORE_NEXTFRAME
|
||||
while (coreState == CORE_RUNNING || coreState == CORE_STEPPING) {
|
||||
PSP_RunLoopFor(blockTicks);
|
||||
}
|
||||
}
|
||||
|
||||
void PSP_RunLoopUntil(u64 globalticks) {
|
||||
SaveState::Process();
|
||||
if (coreState == CORE_POWERDOWN || coreState == CORE_ERROR) {
|
||||
return;
|
||||
} else if (coreState == CORE_STEPPING) {
|
||||
Core_ProcessStepping();
|
||||
return;
|
||||
}
|
||||
|
||||
mipsr4k.RunLoopUntil(globalticks);
|
||||
|
||||
@@ -70,6 +70,7 @@ void PSP_Shutdown();
|
||||
|
||||
void PSP_BeginHostFrame();
|
||||
void PSP_EndHostFrame();
|
||||
void PSP_RunLoopWhileState();
|
||||
void PSP_RunLoopUntil(u64 globalticks);
|
||||
void PSP_RunLoopFor(int cycles);
|
||||
|
||||
|
||||
@@ -2576,10 +2576,8 @@ void GPUCommon::DoBlockTransfer(u32 skipDrawReason) {
|
||||
framebufferManager_->NotifyBlockTransferAfter(dstBasePtr, dstStride, dstX, dstY, srcBasePtr, srcStride, srcX, srcY, width, height, bpp, skipDrawReason);
|
||||
}
|
||||
|
||||
#ifndef MOBILE_DEVICE
|
||||
CBreakPoints::ExecMemCheck(srcBasePtr + (srcY * srcStride + srcX) * bpp, false, height * srcStride * bpp, currentMIPS->pc);
|
||||
CBreakPoints::ExecMemCheck(dstBasePtr + (dstY * dstStride + dstX) * bpp, true, height * dstStride * bpp, currentMIPS->pc);
|
||||
#endif
|
||||
|
||||
// TODO: Correct timing appears to be 1.9, but erring a bit low since some of our other timing is inaccurate.
|
||||
cyclesExecuted += ((height * width * bpp) * 16) / 10;
|
||||
|
||||
@@ -344,10 +344,8 @@ void NullGPU::ExecuteOp(u32 op, u32 diff) {
|
||||
memcpy(dst, src, width * bpp);
|
||||
}
|
||||
|
||||
#ifndef MOBILE_DEVICE
|
||||
CBreakPoints::ExecMemCheck(srcBasePtr + (srcY * srcStride + srcX) * bpp, false, height * srcStride * bpp, currentMIPS->pc);
|
||||
CBreakPoints::ExecMemCheck(dstBasePtr + (srcY * dstStride + srcX) * bpp, true, height * dstStride * bpp, currentMIPS->pc);
|
||||
#endif
|
||||
|
||||
// TODO: Correct timing appears to be 1.9, but erring a bit low since some of our other timing is inaccurate.
|
||||
cyclesExecuted += ((height * width * bpp) * 16) / 10;
|
||||
|
||||
@@ -648,10 +648,8 @@ void SoftGPU::ExecuteOp(u32 op, u32 diff) {
|
||||
memcpy(dst, src, width * bpp);
|
||||
}
|
||||
|
||||
#ifndef MOBILE_DEVICE
|
||||
CBreakPoints::ExecMemCheck(srcBasePtr + (srcY * srcStride + srcX) * bpp, false, height * srcStride * bpp, currentMIPS->pc);
|
||||
CBreakPoints::ExecMemCheck(dstBasePtr + (srcY * dstStride + srcX) * bpp, true, height * dstStride * bpp, currentMIPS->pc);
|
||||
#endif
|
||||
|
||||
// TODO: Correct timing appears to be 1.9, but erring a bit low since some of our other timing is inaccurate.
|
||||
cyclesExecuted += ((height * width * bpp) * 16) / 10;
|
||||
|
||||
+1
-8
@@ -1193,14 +1193,7 @@ void EmuScreen::render() {
|
||||
|
||||
PSP_BeginHostFrame();
|
||||
|
||||
// We just run the CPU until we get to vblank. This will quickly sync up pretty nicely.
|
||||
// The actual number of cycles doesn't matter so much here as we will break due to CORE_NEXTFRAME, most of the time hopefully...
|
||||
int blockTicks = usToCycles(1000000 / 10);
|
||||
|
||||
// Run until CORE_NEXTFRAME
|
||||
while (coreState == CORE_RUNNING) {
|
||||
PSP_RunLoopFor(blockTicks);
|
||||
}
|
||||
PSP_RunLoopWhileState();
|
||||
|
||||
// Hopefully coreState is now CORE_NEXTFRAME
|
||||
if (coreState == CORE_NEXTFRAME) {
|
||||
|
||||
@@ -809,7 +809,6 @@ void CDisasm::SetDebugMode(bool _bDebug, bool switchPC)
|
||||
if (_bDebug && GetUIState() == UISTATE_INGAME && PSP_IsInited())
|
||||
{
|
||||
Core_WaitInactive(TEMP_BREAKPOINT_WAIT_MS);
|
||||
CBreakPoints::ClearTemporaryBreakPoints();
|
||||
breakpointList->reloadBreakpoints();
|
||||
threadList->reloadThreads();
|
||||
stackTraceView->loadStackTrace();
|
||||
@@ -913,4 +912,4 @@ void CDisasm::UpdateDialog(bool _bComplete)
|
||||
// redraw. all others are updated manually
|
||||
InvalidateRect (GetDlgItem(m_hDlg, IDC_DEBUGMEMVIEW), NULL, TRUE);
|
||||
UpdateWindow (GetDlgItem(m_hDlg, IDC_DEBUGMEMVIEW));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user