2012-11-01 15:19:01 +00:00
|
|
|
// 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
|
2012-11-04 22:01:49 +00:00
|
|
|
// the Free Software Foundation, version 2.0 or later versions.
|
2012-11-01 15:19:01 +00:00
|
|
|
|
|
|
|
// 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/.
|
|
|
|
|
2013-06-30 23:22:07 +00:00
|
|
|
#include "Core/Core.h"
|
|
|
|
#include "Core/Debugger/Breakpoints.h"
|
|
|
|
#include "Core/Debugger/SymbolMap.h"
|
2013-03-09 10:39:12 +00:00
|
|
|
#include "Core/Host.h"
|
2014-01-26 19:52:37 +00:00
|
|
|
#include "Core/MIPS/MIPSAnalyst.h"
|
2014-12-12 23:09:37 +00:00
|
|
|
#include "Core/MIPS/JitCommon/NativeJit.h"
|
2013-06-30 23:22:07 +00:00
|
|
|
#include "Core/CoreTiming.h"
|
2012-11-03 02:33:24 +00:00
|
|
|
#include <cstdio>
|
2012-11-01 15:19:01 +00:00
|
|
|
|
2013-06-30 22:16:58 +00:00
|
|
|
std::vector<BreakPoint> CBreakPoints::breakPoints_;
|
2013-06-29 18:22:58 +00:00
|
|
|
u32 CBreakPoints::breakSkipFirstAt_ = 0;
|
2013-06-30 23:22:07 +00:00
|
|
|
u64 CBreakPoints::breakSkipFirstTicks_ = 0;
|
2013-06-30 22:16:58 +00:00
|
|
|
std::vector<MemCheck> CBreakPoints::memChecks_;
|
2014-01-26 19:52:37 +00:00
|
|
|
std::vector<MemCheck *> CBreakPoints::cleanupMemChecks_;
|
2012-11-01 15:19:01 +00:00
|
|
|
|
2014-01-26 19:52:37 +00:00
|
|
|
MemCheck::MemCheck()
|
2012-11-01 15:19:01 +00:00
|
|
|
{
|
2014-01-26 19:52:37 +00:00
|
|
|
numHits = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MemCheck::Log(u32 addr, bool write, int size, u32 pc)
|
|
|
|
{
|
|
|
|
if (result & MEMCHECK_LOG)
|
2014-01-26 22:13:43 +00:00
|
|
|
NOTICE_LOG(MEMMAP, "CHK %s%i at %08x (%s), PC=%08x (%s)", write ? "Write" : "Read", size * 8, addr, symbolMap.GetDescription(addr).c_str(), pc, symbolMap.GetDescription(pc).c_str());
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
|
|
|
|
2013-03-09 08:40:33 +00:00
|
|
|
void MemCheck::Action(u32 addr, bool write, int size, u32 pc)
|
2012-11-01 15:19:01 +00:00
|
|
|
{
|
2013-06-30 22:16:58 +00:00
|
|
|
int mask = write ? MEMCHECK_WRITE : MEMCHECK_READ;
|
|
|
|
if (cond & mask)
|
2012-11-01 15:19:01 +00:00
|
|
|
{
|
2013-03-09 08:35:08 +00:00
|
|
|
++numHits;
|
|
|
|
|
2014-01-26 19:52:37 +00:00
|
|
|
Log(addr, write, size, pc);
|
2013-06-30 22:16:58 +00:00
|
|
|
if (result & MEMCHECK_BREAK)
|
2013-03-09 10:39:12 +00:00
|
|
|
{
|
2013-03-29 21:56:57 +00:00
|
|
|
Core_EnableStepping(true);
|
2013-03-09 10:39:12 +00:00
|
|
|
host->SetDebugMode(true);
|
|
|
|
}
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-26 19:52:37 +00:00
|
|
|
void MemCheck::JitBefore(u32 addr, bool write, int size, u32 pc)
|
|
|
|
{
|
|
|
|
int mask = MEMCHECK_WRITE | MEMCHECK_WRITE_ONCHANGE;
|
|
|
|
if (write && (cond & mask) == mask)
|
|
|
|
{
|
|
|
|
lastAddr = addr;
|
|
|
|
lastPC = pc;
|
|
|
|
lastSize = size;
|
|
|
|
|
|
|
|
// We have to break to find out if it changed.
|
|
|
|
Core_EnableStepping(true);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
lastAddr = 0;
|
|
|
|
Action(addr, write, size, pc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void MemCheck::JitCleanup()
|
|
|
|
{
|
|
|
|
if (lastAddr == 0 || lastPC == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Here's the tricky part: would this have changed memory?
|
|
|
|
// Note that it did not actually get written.
|
2014-06-19 07:48:33 +00:00
|
|
|
bool changed = MIPSAnalyst::OpWouldChangeMemory(lastPC, lastAddr, lastSize);
|
2014-01-26 19:52:37 +00:00
|
|
|
if (changed)
|
|
|
|
{
|
|
|
|
++numHits;
|
|
|
|
Log(lastAddr, true, lastSize, lastPC);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Resume if it should not have gone to stepping, or if it did not change.
|
|
|
|
if ((!(result & MEMCHECK_BREAK) || !changed) && coreState == CORE_STEPPING)
|
|
|
|
{
|
|
|
|
CBreakPoints::SetSkipFirst(lastPC);
|
|
|
|
Core_EnableStepping(false);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
host->SetDebugMode(true);
|
|
|
|
}
|
|
|
|
|
2013-06-30 22:16:58 +00:00
|
|
|
size_t CBreakPoints::FindBreakpoint(u32 addr, bool matchTemp, bool temp)
|
2012-11-01 15:19:01 +00:00
|
|
|
{
|
2014-08-08 15:53:44 +00:00
|
|
|
size_t found = INVALID_BREAKPOINT;
|
2013-06-30 22:16:58 +00:00
|
|
|
for (size_t i = 0; i < breakPoints_.size(); ++i)
|
|
|
|
{
|
2014-08-08 15:53:44 +00:00
|
|
|
const auto &bp = breakPoints_[i];
|
|
|
|
if (bp.addr == addr && (!matchTemp || bp.temporary == temp))
|
|
|
|
{
|
|
|
|
if (bp.enabled)
|
|
|
|
return i;
|
|
|
|
// Hold out until the first enabled one.
|
|
|
|
if (found == INVALID_BREAKPOINT)
|
|
|
|
found = i;
|
|
|
|
}
|
2013-06-30 22:16:58 +00:00
|
|
|
}
|
|
|
|
|
2014-08-08 15:53:44 +00:00
|
|
|
return found;
|
2013-06-30 22:16:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t CBreakPoints::FindMemCheck(u32 start, u32 end)
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < memChecks_.size(); ++i)
|
|
|
|
{
|
|
|
|
if (memChecks_[i].start == start && memChecks_[i].end == end)
|
|
|
|
return i;
|
|
|
|
}
|
2012-11-01 15:19:01 +00:00
|
|
|
|
2013-06-30 22:16:58 +00:00
|
|
|
return INVALID_MEMCHECK;
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
|
|
|
|
2013-06-30 22:16:58 +00:00
|
|
|
bool CBreakPoints::IsAddressBreakPoint(u32 addr)
|
2012-11-01 15:19:01 +00:00
|
|
|
{
|
2013-06-30 22:16:58 +00:00
|
|
|
size_t bp = FindBreakpoint(addr);
|
2013-06-30 22:51:50 +00:00
|
|
|
return bp != INVALID_BREAKPOINT && breakPoints_[bp].enabled;
|
2013-06-30 22:16:58 +00:00
|
|
|
}
|
2012-11-01 15:19:01 +00:00
|
|
|
|
2013-07-10 08:56:03 +00:00
|
|
|
bool CBreakPoints::IsAddressBreakPoint(u32 addr, bool* enabled)
|
|
|
|
{
|
|
|
|
size_t bp = FindBreakpoint(addr);
|
|
|
|
if (bp == INVALID_BREAKPOINT) return false;
|
|
|
|
if (enabled != NULL) *enabled = breakPoints_[bp].enabled;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-06-30 22:16:58 +00:00
|
|
|
bool CBreakPoints::IsTempBreakPoint(u32 addr)
|
|
|
|
{
|
|
|
|
size_t bp = FindBreakpoint(addr, true, true);
|
|
|
|
return bp != INVALID_BREAKPOINT;
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
|
|
|
|
2015-01-19 03:36:42 +00:00
|
|
|
bool CBreakPoints::RangeContainsBreakPoint(u32 addr, u32 size)
|
|
|
|
{
|
|
|
|
const u32 end = addr + size;
|
|
|
|
for (const auto &bp : breakPoints_)
|
|
|
|
{
|
|
|
|
if (bp.addr >= addr && bp.addr < end)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-06-30 22:16:58 +00:00
|
|
|
void CBreakPoints::AddBreakPoint(u32 addr, bool temp)
|
2012-11-01 15:19:01 +00:00
|
|
|
{
|
2013-06-30 22:16:58 +00:00
|
|
|
size_t bp = FindBreakpoint(addr, true, temp);
|
|
|
|
if (bp == INVALID_BREAKPOINT)
|
2012-11-01 15:19:01 +00:00
|
|
|
{
|
2013-06-30 22:16:58 +00:00
|
|
|
BreakPoint pt;
|
2013-06-30 22:51:50 +00:00
|
|
|
pt.enabled = true;
|
|
|
|
pt.temporary = temp;
|
|
|
|
pt.addr = addr;
|
2013-06-30 22:16:58 +00:00
|
|
|
|
|
|
|
breakPoints_.push_back(pt);
|
|
|
|
Update(addr);
|
|
|
|
}
|
2013-06-30 22:51:50 +00:00
|
|
|
else if (!breakPoints_[bp].enabled)
|
2013-06-30 22:16:58 +00:00
|
|
|
{
|
2013-06-30 22:51:50 +00:00
|
|
|
breakPoints_[bp].enabled = true;
|
2013-07-02 09:51:29 +00:00
|
|
|
breakPoints_[bp].hasCond = false;
|
|
|
|
Update(addr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-30 22:16:58 +00:00
|
|
|
void CBreakPoints::RemoveBreakPoint(u32 addr)
|
|
|
|
{
|
|
|
|
size_t bp = FindBreakpoint(addr);
|
|
|
|
if (bp != INVALID_BREAKPOINT)
|
|
|
|
{
|
|
|
|
breakPoints_.erase(breakPoints_.begin() + bp);
|
|
|
|
|
|
|
|
// Check again, there might've been an overlapping temp breakpoint.
|
|
|
|
bp = FindBreakpoint(addr);
|
|
|
|
if (bp != INVALID_BREAKPOINT)
|
|
|
|
breakPoints_.erase(breakPoints_.begin() + bp);
|
|
|
|
|
|
|
|
Update(addr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CBreakPoints::ChangeBreakPoint(u32 addr, bool status)
|
|
|
|
{
|
|
|
|
size_t bp = FindBreakpoint(addr);
|
|
|
|
if (bp != INVALID_BREAKPOINT)
|
|
|
|
{
|
2013-06-30 22:51:50 +00:00
|
|
|
breakPoints_[bp].enabled = status;
|
2013-06-30 22:16:58 +00:00
|
|
|
Update(addr);
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CBreakPoints::ClearAllBreakPoints()
|
|
|
|
{
|
2013-06-30 22:16:58 +00:00
|
|
|
if (!breakPoints_.empty())
|
|
|
|
{
|
|
|
|
breakPoints_.clear();
|
|
|
|
Update();
|
|
|
|
}
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
|
|
|
|
2013-06-26 21:14:15 +00:00
|
|
|
void CBreakPoints::ClearTemporaryBreakPoints()
|
|
|
|
{
|
2013-06-30 22:16:58 +00:00
|
|
|
if (breakPoints_.empty())
|
|
|
|
return;
|
2013-06-26 21:14:15 +00:00
|
|
|
|
|
|
|
bool update = false;
|
2013-06-30 22:16:58 +00:00
|
|
|
for (int i = (int)breakPoints_.size()-1; i >= 0; --i)
|
2013-06-26 21:14:15 +00:00
|
|
|
{
|
2013-06-30 22:51:50 +00:00
|
|
|
if (breakPoints_[i].temporary)
|
2013-06-26 21:14:15 +00:00
|
|
|
{
|
2013-06-30 22:16:58 +00:00
|
|
|
breakPoints_.erase(breakPoints_.begin() + i);
|
2013-06-26 21:14:15 +00:00
|
|
|
update = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-30 22:16:58 +00:00
|
|
|
if (update)
|
|
|
|
Update();
|
|
|
|
}
|
|
|
|
|
2013-06-30 22:35:03 +00:00
|
|
|
void CBreakPoints::ChangeBreakPointAddCond(u32 addr, const BreakPointCond &cond)
|
|
|
|
{
|
2013-06-30 23:14:40 +00:00
|
|
|
size_t bp = FindBreakpoint(addr, true, false);
|
2013-06-30 22:35:03 +00:00
|
|
|
if (bp != INVALID_BREAKPOINT)
|
|
|
|
{
|
|
|
|
breakPoints_[bp].hasCond = true;
|
|
|
|
breakPoints_[bp].cond = cond;
|
|
|
|
Update();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CBreakPoints::ChangeBreakPointRemoveCond(u32 addr)
|
|
|
|
{
|
2013-06-30 23:14:40 +00:00
|
|
|
size_t bp = FindBreakpoint(addr, true, false);
|
2013-06-30 22:35:03 +00:00
|
|
|
if (bp != INVALID_BREAKPOINT)
|
|
|
|
{
|
|
|
|
breakPoints_[bp].hasCond = false;
|
|
|
|
Update();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BreakPointCond *CBreakPoints::GetBreakPointCondition(u32 addr)
|
|
|
|
{
|
2013-06-30 23:14:40 +00:00
|
|
|
size_t bp = FindBreakpoint(addr, true, false);
|
2013-06-30 22:35:03 +00:00
|
|
|
if (bp != INVALID_BREAKPOINT && breakPoints_[bp].hasCond)
|
|
|
|
return &breakPoints_[bp].cond;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-06-30 22:16:58 +00:00
|
|
|
void CBreakPoints::AddMemCheck(u32 start, u32 end, MemCheckCondition cond, MemCheckResult result)
|
|
|
|
{
|
2014-01-26 19:52:37 +00:00
|
|
|
// This will ruin any pending memchecks.
|
|
|
|
cleanupMemChecks_.clear();
|
|
|
|
|
2013-06-30 22:16:58 +00:00
|
|
|
size_t mc = FindMemCheck(start, end);
|
|
|
|
if (mc == INVALID_MEMCHECK)
|
|
|
|
{
|
|
|
|
MemCheck check;
|
|
|
|
check.start = start;
|
|
|
|
check.end = end;
|
|
|
|
check.cond = cond;
|
|
|
|
check.result = result;
|
|
|
|
|
|
|
|
memChecks_.push_back(check);
|
|
|
|
Update();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
memChecks_[mc].cond = (MemCheckCondition)(memChecks_[mc].cond | cond);
|
|
|
|
memChecks_[mc].result = (MemCheckResult)(memChecks_[mc].result | result);
|
|
|
|
Update();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CBreakPoints::RemoveMemCheck(u32 start, u32 end)
|
|
|
|
{
|
2014-01-26 19:52:37 +00:00
|
|
|
// This will ruin any pending memchecks.
|
|
|
|
cleanupMemChecks_.clear();
|
|
|
|
|
2013-06-30 22:16:58 +00:00
|
|
|
size_t mc = FindMemCheck(start, end);
|
|
|
|
if (mc != INVALID_MEMCHECK)
|
|
|
|
{
|
|
|
|
memChecks_.erase(memChecks_.begin() + mc);
|
|
|
|
Update();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CBreakPoints::ChangeMemCheck(u32 start, u32 end, MemCheckCondition cond, MemCheckResult result)
|
|
|
|
{
|
|
|
|
size_t mc = FindMemCheck(start, end);
|
|
|
|
if (mc != INVALID_MEMCHECK)
|
|
|
|
{
|
|
|
|
memChecks_[mc].cond = cond;
|
|
|
|
memChecks_[mc].result = result;
|
|
|
|
Update();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CBreakPoints::ClearAllMemChecks()
|
|
|
|
{
|
2014-01-26 19:52:37 +00:00
|
|
|
// This will ruin any pending memchecks.
|
|
|
|
cleanupMemChecks_.clear();
|
|
|
|
|
2013-06-30 22:16:58 +00:00
|
|
|
if (!memChecks_.empty())
|
|
|
|
{
|
|
|
|
memChecks_.clear();
|
|
|
|
Update();
|
|
|
|
}
|
2013-06-26 21:14:15 +00:00
|
|
|
}
|
|
|
|
|
2013-10-27 20:15:12 +00:00
|
|
|
static inline u32 NotCached(u32 val)
|
|
|
|
{
|
|
|
|
// Remove the cached part of the address.
|
|
|
|
return val & ~0x40000000;
|
|
|
|
}
|
|
|
|
|
2013-03-09 08:35:08 +00:00
|
|
|
MemCheck *CBreakPoints::GetMemCheck(u32 address, int size)
|
2012-11-01 15:19:01 +00:00
|
|
|
{
|
|
|
|
std::vector<MemCheck>::iterator iter;
|
2013-06-30 22:16:58 +00:00
|
|
|
for (iter = memChecks_.begin(); iter != memChecks_.end(); ++iter)
|
2012-11-01 15:19:01 +00:00
|
|
|
{
|
2013-03-09 08:35:08 +00:00
|
|
|
MemCheck &check = *iter;
|
2013-06-30 22:16:58 +00:00
|
|
|
if (check.end != 0)
|
2012-11-01 15:19:01 +00:00
|
|
|
{
|
2013-10-27 20:15:12 +00:00
|
|
|
if (NotCached(address + size) > NotCached(check.start) && NotCached(address) < NotCached(check.end))
|
2013-03-09 08:35:08 +00:00
|
|
|
return ✓
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-10-27 20:15:12 +00:00
|
|
|
if (NotCached(check.start) == NotCached(address))
|
2013-03-09 08:35:08 +00:00
|
|
|
return ✓
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//none found
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-10-27 20:15:12 +00:00
|
|
|
void CBreakPoints::ExecMemCheck(u32 address, bool write, int size, u32 pc)
|
|
|
|
{
|
|
|
|
auto check = GetMemCheck(address, size);
|
|
|
|
if (check)
|
|
|
|
check->Action(address, write, size, pc);
|
|
|
|
}
|
|
|
|
|
2014-01-26 19:52:37 +00:00
|
|
|
void CBreakPoints::ExecMemCheckJitBefore(u32 address, bool write, int size, u32 pc)
|
|
|
|
{
|
|
|
|
auto check = GetMemCheck(address, size);
|
|
|
|
if (check) {
|
|
|
|
check->JitBefore(address, write, size, pc);
|
|
|
|
cleanupMemChecks_.push_back(check);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CBreakPoints::ExecMemCheckJitCleanup()
|
|
|
|
{
|
|
|
|
for (auto it = cleanupMemChecks_.begin(), end = cleanupMemChecks_.end(); it != end; ++it) {
|
|
|
|
auto check = *it;
|
|
|
|
check->JitCleanup();
|
|
|
|
}
|
|
|
|
cleanupMemChecks_.clear();
|
|
|
|
}
|
|
|
|
|
2013-06-30 23:22:07 +00:00
|
|
|
void CBreakPoints::SetSkipFirst(u32 pc)
|
|
|
|
{
|
|
|
|
breakSkipFirstAt_ = pc;
|
|
|
|
breakSkipFirstTicks_ = CoreTiming::GetTicks();
|
|
|
|
}
|
|
|
|
u32 CBreakPoints::CheckSkipFirst()
|
|
|
|
{
|
|
|
|
u32 pc = breakSkipFirstAt_;
|
|
|
|
if (breakSkipFirstTicks_ == CoreTiming::GetTicks())
|
|
|
|
return pc;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-10-27 20:15:12 +00:00
|
|
|
const std::vector<MemCheck> CBreakPoints::GetMemCheckRanges()
|
|
|
|
{
|
|
|
|
std::vector<MemCheck> ranges = memChecks_;
|
|
|
|
for (auto it = memChecks_.begin(), end = memChecks_.end(); it != end; ++it)
|
|
|
|
{
|
|
|
|
MemCheck check = *it;
|
|
|
|
// Toggle the cached part of the address.
|
|
|
|
check.start ^= 0x40000000;
|
|
|
|
if (check.end != 0)
|
|
|
|
check.end ^= 0x40000000;
|
|
|
|
ranges.push_back(check);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ranges;
|
|
|
|
}
|
|
|
|
|
2013-06-30 22:16:58 +00:00
|
|
|
const std::vector<MemCheck> CBreakPoints::GetMemChecks()
|
2013-02-10 15:36:06 +00:00
|
|
|
{
|
2013-06-30 22:16:58 +00:00
|
|
|
return memChecks_;
|
2013-02-10 15:36:06 +00:00
|
|
|
}
|
|
|
|
|
2013-06-30 22:16:58 +00:00
|
|
|
const std::vector<BreakPoint> CBreakPoints::GetBreakpoints()
|
2013-06-27 19:07:49 +00:00
|
|
|
{
|
2013-06-30 22:16:58 +00:00
|
|
|
return breakPoints_;
|
2013-06-27 19:07:49 +00:00
|
|
|
}
|
|
|
|
|
2013-06-30 22:16:58 +00:00
|
|
|
void CBreakPoints::Update(u32 addr)
|
2013-02-10 15:36:06 +00:00
|
|
|
{
|
2013-12-02 09:42:20 +00:00
|
|
|
if (MIPSComp::jit)
|
2013-06-30 22:16:58 +00:00
|
|
|
{
|
2013-12-02 09:42:20 +00:00
|
|
|
bool resume = false;
|
|
|
|
if (Core_IsStepping() == false)
|
|
|
|
{
|
|
|
|
Core_EnableStepping(true);
|
2014-06-19 08:16:24 +00:00
|
|
|
Core_WaitInactive(200);
|
2013-12-02 09:42:20 +00:00
|
|
|
resume = true;
|
|
|
|
}
|
|
|
|
|
2013-09-01 07:21:41 +00:00
|
|
|
// In case this is a delay slot, clear the previous instruction too.
|
2013-06-30 22:16:58 +00:00
|
|
|
if (addr != 0)
|
2014-06-19 08:08:45 +00:00
|
|
|
MIPSComp::jit->InvalidateCacheAt(addr - 4, 8);
|
2013-06-30 22:16:58 +00:00
|
|
|
else
|
2014-06-19 08:08:45 +00:00
|
|
|
MIPSComp::jit->InvalidateCache();
|
2013-12-02 09:42:20 +00:00
|
|
|
|
|
|
|
if (resume)
|
|
|
|
Core_EnableStepping(false);
|
2013-06-30 22:16:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Redraw in order to show the breakpoint.
|
|
|
|
host->UpdateDisassembly();
|
2013-02-10 15:36:06 +00:00
|
|
|
}
|