mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2024-11-27 08:05:41 +00:00
738 lines
21 KiB
C++
738 lines
21 KiB
C++
/**
|
|
** Supermodel
|
|
** A Sega Model 3 Arcade Emulator.
|
|
** Copyright 2011 Bart Trzynadlowski, Nik Henson
|
|
**
|
|
** This file is part of Supermodel.
|
|
**
|
|
** Supermodel 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, either version 3 of the License, or (at your option)
|
|
** any later version.
|
|
**
|
|
** Supermodel 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 for
|
|
** more details.
|
|
**
|
|
** You should have received a copy of the GNU General Public License along
|
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
|
**/
|
|
|
|
/*
|
|
* CodeAnalyser.cpp
|
|
*/
|
|
|
|
#ifdef SUPERMODEL_DEBUGGER
|
|
|
|
#include "CodeAnalyser.h"
|
|
#include "CPUDebug.h"
|
|
#include "Label.h"
|
|
|
|
#include <cctype>
|
|
#include <string>
|
|
|
|
namespace Debugger
|
|
{
|
|
CEntryPoint::CEntryPoint(const CEntryPoint &other) : addr(other.addr), autoFlag(other.autoFlag)
|
|
{
|
|
if (other.autoLabel != NULL)
|
|
{
|
|
strncpy(autoLabelStr, other.autoLabel, 254);
|
|
autoLabelStr[254] = '\0';
|
|
autoLabel = autoLabelStr;
|
|
}
|
|
else
|
|
autoLabel = NULL;
|
|
}
|
|
|
|
CEntryPoint::CEntryPoint(UINT32 eAddr, ELabelFlags eAutoFlag, const char *eAutoLabel) :
|
|
addr(eAddr), autoFlag(eAutoFlag)
|
|
{
|
|
if (eAutoLabel != NULL)
|
|
{
|
|
strncpy(autoLabelStr, eAutoLabel, 254);
|
|
autoLabelStr[254] = '\0';
|
|
autoLabel = autoLabelStr;
|
|
}
|
|
else
|
|
autoLabel = NULL;
|
|
}
|
|
|
|
CEntryPoint &CEntryPoint::operator=(const CEntryPoint &other)
|
|
{
|
|
addr = other.addr;
|
|
autoFlag = other.autoFlag;
|
|
if (other.autoLabel != NULL)
|
|
{
|
|
strncpy(autoLabelStr, other.autoLabel, 254);
|
|
autoLabelStr[254] = '\0';
|
|
autoLabel = autoLabelStr;
|
|
}
|
|
else
|
|
autoLabel = NULL;
|
|
return *this;
|
|
}
|
|
|
|
bool CEntryPoint::operator==(const CEntryPoint &other)
|
|
{
|
|
return addr == other.addr && autoFlag == other.autoFlag;
|
|
}
|
|
|
|
bool CEntryPoint::operator!=(const CEntryPoint &other)
|
|
{
|
|
return addr != other.addr || autoFlag != other.autoFlag;
|
|
}
|
|
|
|
const char *CAutoLabel::s_defaultLabelFmts[] = { "Entry%s", "Ex%s", "Int%s", "Jmp%s", "Loop%s", "Sub%s", NULL };
|
|
|
|
const unsigned CAutoLabel::numLabelFlags = sizeof(s_defaultLabelFmts) / sizeof(char*);
|
|
|
|
ELabelFlags CAutoLabel::GetLabelFlag(int index)
|
|
{
|
|
if (index < 0 || index >= numLabelFlags)
|
|
return LFNone;
|
|
return (ELabelFlags)(1 << index);
|
|
}
|
|
|
|
int CAutoLabel::GetFlagIndex(ELabelFlags flag)
|
|
{
|
|
switch (flag)
|
|
{
|
|
case LFEntryPoint: return 0;
|
|
case LFExcepHandler: return 1;
|
|
case LFInterHandler: return 2;
|
|
case LFJumpTarget: return 3;
|
|
case LFLoopPoint: return 4;
|
|
case LFSubroutine: return 5;
|
|
case LFUnseenCode: return 6;
|
|
default: return -1;
|
|
}
|
|
}
|
|
|
|
const char *CAutoLabel::GetFlagString(ELabelFlags flag)
|
|
{
|
|
switch (flag)
|
|
{
|
|
case LFEntryPoint: return "Entry Point";
|
|
case LFExcepHandler: return "Exception Handler";
|
|
case LFInterHandler: return "Interrupt Handler";
|
|
case LFJumpTarget: return "Jump Target";
|
|
case LFLoopPoint: return "Loop Point";
|
|
case LFSubroutine: return "Subroutine";
|
|
case LFUnseenCode: return "Unseen Code";
|
|
default: return "";
|
|
}
|
|
}
|
|
|
|
CAutoLabel::CAutoLabel(CCPUDebug *lCPU, UINT32 lAddr) : CAddressRef(lCPU, lAddr), flags(LFNone), m_acquired(0)
|
|
{
|
|
memset(m_subLabels, NULL, sizeof(m_subLabels));
|
|
}
|
|
|
|
CAutoLabel::~CAutoLabel()
|
|
{
|
|
// Delete all sub-labels
|
|
for (int index = 0; index < numLabelFlags; index++)
|
|
{
|
|
if (m_subLabels[index] != NULL)
|
|
{
|
|
delete[] m_subLabels[index];
|
|
m_subLabels[index] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CAutoLabel::Acquire()
|
|
{
|
|
m_acquired++;
|
|
}
|
|
|
|
void CAutoLabel::Release()
|
|
{
|
|
if (--m_acquired == 0)
|
|
delete this;
|
|
}
|
|
|
|
void CAutoLabel::AddFlag(ELabelFlags flag, const char *subLabel)
|
|
{
|
|
int index = GetFlagIndex(flag);
|
|
if (index == -1)
|
|
return;
|
|
flags = (ELabelFlags)((unsigned)flags | (unsigned)flag);
|
|
if (subLabel != NULL)
|
|
{
|
|
size_t len = strlen(subLabel);
|
|
char *label = new char[len + 1];
|
|
strncpy(label, subLabel, len);
|
|
label[len] = '\0';
|
|
m_subLabels[index] = label;
|
|
}
|
|
else
|
|
m_subLabels[index] = CreateDefaultSubLabel(flag);
|
|
}
|
|
|
|
bool CAutoLabel::GetLabel(char *labelStr, ELabelFlags subFlags)
|
|
{
|
|
char *p = labelStr;
|
|
*p = '\0';
|
|
for (int index = 0; index < numLabelFlags; index++)
|
|
{
|
|
ELabelFlags flag = GetLabelFlag(index);
|
|
if (!(subFlags & flag))
|
|
continue;
|
|
const char *subLabel = m_subLabels[index];
|
|
if (subLabel == NULL)
|
|
continue;
|
|
if (p > labelStr)
|
|
{
|
|
(*p++) = '/';
|
|
*p = '\0';
|
|
}
|
|
strcat(p, subLabel);
|
|
p += strlen(subLabel);
|
|
*p = '\0';
|
|
}
|
|
return p > labelStr;
|
|
}
|
|
|
|
bool CAutoLabel::ContainsSubLabel(const char *subLabel)
|
|
{
|
|
for (int i = 0; i < numLabelFlags; i++)
|
|
{
|
|
if (m_subLabels[i] != NULL && stricmp(subLabel, m_subLabels[i]) == 0)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const char *CAutoLabel::CreateDefaultSubLabel(ELabelFlags flag)
|
|
{
|
|
int index = GetFlagIndex(flag);
|
|
if (index == -1)
|
|
return NULL;
|
|
const char *labelFmt = s_defaultLabelFmts[index];
|
|
if (labelFmt == NULL)
|
|
return NULL;
|
|
char addrStr[50];
|
|
cpu->debugger->FormatData(addrStr, Hex, (unsigned)(cpu->memBusWidth / 8), (UINT64)addr);
|
|
char *label = new char[255];
|
|
sprintf(label, labelFmt, addrStr);
|
|
return label;
|
|
}
|
|
|
|
CCodeAnalysis::CCodeAnalysis(CCodeAnalyser *aAnalyser) : analyser(aAnalyser)
|
|
{
|
|
//
|
|
}
|
|
|
|
CCodeAnalysis::CCodeAnalysis(CCodeAnalyser *aAnalyser, unsigned aTotalIndices, vector<CEntryPoint> &entryPoints, vector<UINT32> &unseenEntryAddrs) :
|
|
analyser(aAnalyser), m_entryPoints(entryPoints), m_unseenEntryAddrs(unseenEntryAddrs),
|
|
m_seenIndices(aTotalIndices), m_validIndices(aTotalIndices), m_acquired(0)
|
|
{
|
|
//
|
|
}
|
|
|
|
CCodeAnalysis::CCodeAnalysis(CCodeAnalysis *oldAnalysis, vector<CEntryPoint> &entryPoints, vector<UINT32> &unseenEntryAddrs) :
|
|
analyser(oldAnalysis->analyser), m_entryPoints(entryPoints), m_unseenEntryAddrs(unseenEntryAddrs),
|
|
m_seenIndices(oldAnalysis->m_seenIndices), m_validIndices(oldAnalysis->m_validIndices),
|
|
m_autoLabelsMap(oldAnalysis->m_autoLabelsMap), m_acquired(0), validIndexSet(oldAnalysis->validIndexSet)
|
|
{
|
|
for (map<unsigned,CAutoLabel*>::iterator it = m_autoLabelsMap.begin(); it != m_autoLabelsMap.end(); it++)
|
|
it->second->Acquire();
|
|
}
|
|
|
|
CCodeAnalysis::~CCodeAnalysis()
|
|
{
|
|
for (vector<CAutoLabel*>::iterator it = autoLabels.begin(); it != autoLabels.end(); it++)
|
|
(*it)->Release();
|
|
autoLabels.clear();
|
|
m_autoLabelsMap.clear();
|
|
}
|
|
|
|
void CCodeAnalysis::Acquire()
|
|
{
|
|
m_acquired++;
|
|
}
|
|
|
|
void CCodeAnalysis::Release()
|
|
{
|
|
if (--m_acquired == 0)
|
|
delete this;
|
|
}
|
|
|
|
void CCodeAnalysis::FinishAnalysis()
|
|
{
|
|
for (map<UINT32,CAutoLabel*>::iterator it = m_autoLabelsMap.begin(); it != m_autoLabelsMap.end(); it++)
|
|
autoLabels.push_back(it->second);
|
|
}
|
|
|
|
bool CCodeAnalysis::IsAddrValid(UINT32 addr)
|
|
{
|
|
unsigned index;
|
|
if (!analyser->GetIndexOfAddr(addr, index))
|
|
return false;
|
|
return IsIndexValid(index);
|
|
}
|
|
|
|
bool CCodeAnalysis::GetNextValidAddr(UINT32 &addr)
|
|
{
|
|
unsigned index;
|
|
if (!analyser->GetIndexOfAddr(addr, index))
|
|
return false;
|
|
if (IsIndexValid(index))
|
|
return true;
|
|
set<unsigned>::iterator it = validIndexSet.lower_bound(index);
|
|
if (it == validIndexSet.end())
|
|
return false;
|
|
return analyser->GetAddrOfIndex(*it, addr);
|
|
}
|
|
|
|
bool CCodeAnalysis::IsIndexValid(unsigned index)
|
|
{
|
|
return index < m_validIndices.size() && m_validIndices[index];
|
|
}
|
|
|
|
bool CCodeAnalysis::GetNextValidIndex(unsigned &index)
|
|
{
|
|
if (IsIndexValid(index))
|
|
return true;
|
|
set<unsigned>::iterator it = validIndexSet.lower_bound(index);
|
|
if (it == validIndexSet.end())
|
|
return false;
|
|
index = *it;
|
|
return true;
|
|
}
|
|
|
|
bool CCodeAnalysis::HasSeenAddr(UINT32 addr)
|
|
{
|
|
unsigned index;
|
|
if (!analyser->GetIndexOfAddr(addr, index))
|
|
return false;
|
|
return HaveSeenIndex(index);
|
|
}
|
|
|
|
bool CCodeAnalysis::HaveSeenIndex(unsigned index)
|
|
{
|
|
return index < m_seenIndices.size() && m_seenIndices[index];
|
|
}
|
|
|
|
CAutoLabel *CCodeAnalysis::GetAutoLabel(UINT32 addr)
|
|
{
|
|
map<UINT32,CAutoLabel*>::iterator it = m_autoLabelsMap.find(addr);
|
|
if (it == m_autoLabelsMap.end())
|
|
return NULL;
|
|
return it->second;
|
|
}
|
|
|
|
CAutoLabel *CCodeAnalysis::GetAutoLabel(const char *subLabel)
|
|
{
|
|
for (vector<CAutoLabel*>::iterator it = autoLabels.begin(); it != autoLabels.end(); it++)
|
|
{
|
|
if ((*it)->ContainsSubLabel(subLabel))
|
|
return *it;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
vector<CAutoLabel*> CCodeAnalysis::GetAutoLabels(ELabelFlags flag)
|
|
{
|
|
vector<CAutoLabel*> matched;
|
|
for (vector<CAutoLabel*>::iterator it = autoLabels.begin(); it != autoLabels.end(); it++)
|
|
{
|
|
if ((*it)->flags & flag)
|
|
matched.push_back(*it);
|
|
}
|
|
return matched;
|
|
}
|
|
|
|
CCodeAnalyser::CCodeAnalyser(CCPUDebug *aCPU) : cpu(aCPU), emptyAnalysis(this), analysis(&emptyAnalysis)
|
|
{
|
|
instrAlign = cpu->minInstrLen;
|
|
|
|
totalIndices = 0;
|
|
for (vector<CRegion*>::iterator it = cpu->regions.begin(); it != cpu->regions.end(); it++)
|
|
{
|
|
if (!(*it)->isCode)
|
|
continue;
|
|
m_codeRegions.push_back(*it);
|
|
totalIndices += (*it)->size / instrAlign;
|
|
m_indexBounds.push_back(totalIndices);
|
|
}
|
|
}
|
|
|
|
CCodeAnalyser::~CCodeAnalyser()
|
|
{
|
|
if (analysis != &emptyAnalysis)
|
|
analysis->Release();
|
|
}
|
|
|
|
void CCodeAnalyser::Reset()
|
|
{
|
|
CCodeAnalysis *oldAnalysis = analysis;
|
|
analysis = &emptyAnalysis;
|
|
if (oldAnalysis != &emptyAnalysis)
|
|
oldAnalysis->Release();
|
|
}
|
|
|
|
bool CCodeAnalyser::GetAddrOfIndex(unsigned index, UINT32 &addr)
|
|
{
|
|
unsigned regIndex = 0;
|
|
unsigned prevBound = 0;
|
|
for (vector<unsigned>::iterator it = m_indexBounds.begin(); it != m_indexBounds.end(); it++)
|
|
{
|
|
if (*it > index)
|
|
{
|
|
addr = m_codeRegions[regIndex]->addr + (UINT32)(index - prevBound) * instrAlign;
|
|
return true;
|
|
}
|
|
prevBound = *it;
|
|
regIndex++;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CCodeAnalyser::GetIndexOfAddr(UINT32 addr, unsigned &index)
|
|
{
|
|
unsigned regIndex = 0;
|
|
for (vector<CRegion*>::iterator it = m_codeRegions.begin(); it != m_codeRegions.end(); it++)
|
|
{
|
|
if ((*it)->addr <= addr && addr <= (*it)->addrEnd)
|
|
{
|
|
unsigned offset = (unsigned)((addr - (*it)->addr) / instrAlign);
|
|
index = (regIndex > 0 ? m_indexBounds[regIndex - 1] + offset : (unsigned)offset);
|
|
return true;
|
|
}
|
|
regIndex++;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CCodeAnalyser::CheckEntryPoints(vector<CEntryPoint> &entryPoints, vector<UINT32> &unseenEntryAddrs, vector<CEntryPoint> &prevPoints,
|
|
bool &needsAnalysis, bool &reanalyse)
|
|
{
|
|
needsAnalysis = false;
|
|
|
|
// Gather entry points
|
|
GatherEntryPoints(entryPoints, unseenEntryAddrs, reanalyse);
|
|
if (reanalyse)
|
|
{
|
|
needsAnalysis = true;
|
|
return;
|
|
}
|
|
|
|
// Compare new entry points with previous ones
|
|
for (size_t i = 0; i < entryPoints.size(); i++)
|
|
{
|
|
// Check if have more than before
|
|
if (i >= prevPoints.size())
|
|
{
|
|
needsAnalysis = true;
|
|
return;
|
|
}
|
|
// Check if any have changed
|
|
if (entryPoints[i] != prevPoints[i])
|
|
{
|
|
// If entry points, exception handlers or interrupt handlers have changed, then force reanalysis
|
|
if (entryPoints[i].autoFlag == LFEntryPoint || prevPoints[i].autoFlag == LFEntryPoint ||
|
|
entryPoints[i].autoFlag == LFExcepHandler || prevPoints[i].autoFlag == LFExcepHandler ||
|
|
entryPoints[i].autoFlag == LFInterHandler || prevPoints[i].autoFlag == LFInterHandler)
|
|
reanalyse = true;
|
|
needsAnalysis = true;
|
|
return;
|
|
}
|
|
}
|
|
// Check if have less than before
|
|
if (entryPoints.size() < prevPoints.size())
|
|
{
|
|
// If so, force reanalysis
|
|
reanalyse = true;
|
|
needsAnalysis = true;
|
|
return;
|
|
}
|
|
}
|
|
|
|
void CCodeAnalyser::GatherEntryPoints(vector<CEntryPoint> &entryPoints, vector<UINT32> &unseenEntryAddrs, bool &reanalyse)
|
|
{
|
|
char labelStr[255];
|
|
UINT32 addr;
|
|
unsigned index;
|
|
|
|
entryPoints.clear();
|
|
reanalyse = false;
|
|
|
|
// Add reset address as main entry point
|
|
AddEntryPoint(entryPoints, cpu->GetResetAddr(), LFEntryPoint, "MainEntry");
|
|
|
|
// Add exception handlers as entry points
|
|
for (vector<CException*>::iterator it = cpu->exceps.begin(); it != cpu->exceps.end(); it++)
|
|
{
|
|
if (!cpu->GetHandlerAddr(*it, addr))
|
|
continue;
|
|
sprintf(labelStr, "Ex%s", (*it)->id);
|
|
AddEntryPoint(entryPoints, addr, LFExcepHandler, labelStr);
|
|
}
|
|
|
|
// Add interrupt handlers as entry points
|
|
for (vector<CInterrupt*>::iterator it = cpu->inters.begin(); it != cpu->inters.end(); it++)
|
|
{
|
|
if (!cpu->GetHandlerAddr(*it, addr))
|
|
continue;
|
|
sprintf(labelStr, "Int%s", (*it)->id);
|
|
AddEntryPoint(entryPoints, addr, LFInterHandler, labelStr);
|
|
}
|
|
|
|
// Add custom entry addresses
|
|
unsigned i = 0;
|
|
for (vector<UINT32>::iterator it = m_customEntryAddrs.begin(); it != m_customEntryAddrs.end(); it++)
|
|
{
|
|
sprintf(labelStr, "Custom%u", i++);
|
|
AddEntryPoint(entryPoints, *it, LFEntryPoint, labelStr);
|
|
}
|
|
|
|
// If current PC address is at an unseen location or at location that was previously invalid, then add address as unseen entry point
|
|
if (cpu->instrCount > 0 && GetIndexOfAddr(cpu->pc, index) && (!analysis->HaveSeenIndex(index) || !analysis->IsIndexValid(index)))
|
|
{
|
|
// If at location that was previously seen and was invalid, then force reanalysis (ie because code may have been modified)
|
|
if (analysis->HaveSeenIndex(index) && !analysis->IsIndexValid(index))
|
|
reanalyse = true;
|
|
|
|
// Check that address not already included in previous entry points
|
|
bool unseen = true;
|
|
for (vector<CEntryPoint>::iterator it = entryPoints.begin(); it != entryPoints.end(); it++)
|
|
{
|
|
if ((*it).addr == cpu->pc)
|
|
{
|
|
unseen = false;
|
|
break;
|
|
}
|
|
}
|
|
if (unseen && find(unseenEntryAddrs.begin(), unseenEntryAddrs.end(), cpu->pc) == unseenEntryAddrs.end())
|
|
unseenEntryAddrs.push_back(cpu->pc);
|
|
}
|
|
|
|
// Add unseen entry points
|
|
for (vector<UINT32>::iterator it = unseenEntryAddrs.begin(); it != unseenEntryAddrs.end(); it++)
|
|
AddEntryPoint(entryPoints, *it, LFUnseenCode, NULL);
|
|
}
|
|
|
|
void CCodeAnalyser::AddEntryPoint(vector<CEntryPoint> &entryPoints, UINT32 addr, ELabelFlags autoFlag, const char *autoLabel)
|
|
{
|
|
CEntryPoint entryPoint(addr, autoFlag, autoLabel);
|
|
if (find(entryPoints.begin(), entryPoints.end(), entryPoint) == entryPoints.end())
|
|
entryPoints.push_back(entryPoint);
|
|
}
|
|
|
|
bool CCodeAnalyser::NeedsAnalysis()
|
|
{
|
|
vector<CEntryPoint> entryPoints;
|
|
vector<UINT32> unseenEntryAddrs(analysis->m_unseenEntryAddrs);
|
|
bool needsAnalysis;
|
|
bool reanalyse;
|
|
CheckEntryPoints(entryPoints, unseenEntryAddrs, analysis->m_entryPoints, needsAnalysis, reanalyse);
|
|
return needsAnalysis;
|
|
}
|
|
|
|
bool CCodeAnalyser::AnalyseCode()
|
|
{
|
|
m_abortAnalysis = false;
|
|
|
|
CCodeAnalysis *oldAnalysis = analysis;
|
|
|
|
vector<CEntryPoint> entryPoints;
|
|
vector<UINT32> unseenEntryAddrs(oldAnalysis->m_unseenEntryAddrs);
|
|
bool needsAnalysis;
|
|
bool reanalyse;
|
|
CheckEntryPoints(entryPoints, unseenEntryAddrs, oldAnalysis->m_entryPoints, needsAnalysis, reanalyse);
|
|
if (!needsAnalysis)
|
|
return false;
|
|
|
|
CCodeAnalysis *newAnalysis;
|
|
if (reanalyse || oldAnalysis == &emptyAnalysis)
|
|
newAnalysis = new CCodeAnalysis(this, totalIndices, entryPoints, unseenEntryAddrs);
|
|
else
|
|
newAnalysis = new CCodeAnalysis(oldAnalysis, entryPoints, unseenEntryAddrs);
|
|
newAnalysis->Acquire();
|
|
|
|
for (vector<CEntryPoint>::iterator it = newAnalysis->m_entryPoints.begin(); it != newAnalysis->m_entryPoints.end(); it++)
|
|
{
|
|
AddFlagToAddr(newAnalysis->m_autoLabelsMap, it->addr, it->autoFlag, it->autoLabel);
|
|
AnalyseCode(newAnalysis->m_seenIndices, newAnalysis->m_validIndices, newAnalysis->validIndexSet, newAnalysis->m_autoLabelsMap, it->addr);
|
|
}
|
|
newAnalysis->FinishAnalysis();
|
|
|
|
if (m_abortAnalysis)
|
|
{
|
|
newAnalysis->Release();
|
|
return false;
|
|
}
|
|
|
|
analysis = newAnalysis;
|
|
if (oldAnalysis != &emptyAnalysis)
|
|
oldAnalysis->Release();
|
|
|
|
cpu->debugger->AnalysisUpdated(this);
|
|
return true;
|
|
}
|
|
|
|
void CCodeAnalyser::AnalyseCode(vector<bool> &seenIndices, vector<bool> &validIndices, set<unsigned> &validIndexSet,
|
|
map<UINT32,CAutoLabel*> &autoLabelsMap, UINT32 addr)
|
|
{
|
|
if (m_abortAnalysis)
|
|
return;
|
|
|
|
unsigned index;
|
|
if (!GetIndexOfAddr(addr, index) || seenIndices[index])
|
|
return;
|
|
|
|
CRegion *region = cpu->GetRegion(addr);
|
|
if (region == NULL || !region->isCode)
|
|
return;
|
|
|
|
set<unsigned>::iterator setIt = validIndexSet.end();
|
|
|
|
unsigned startIndex = index;
|
|
do
|
|
{
|
|
if (m_abortAnalysis)
|
|
return;
|
|
|
|
// Flag that have seen this address index
|
|
seenIndices[index] = true;
|
|
|
|
// If unit is not valid (ie doesn't disassemble) then code block must be invalid (TODO - invalidate whole code block?)
|
|
int codesLen = cpu->GetOpLength(addr);
|
|
if (codesLen <= 0)
|
|
return;
|
|
|
|
validIndices[index] = true;
|
|
if (setIt != validIndexSet.end())
|
|
setIt = validIndexSet.insert(setIt, index);
|
|
else
|
|
setIt = validIndexSet.insert(index).first;
|
|
|
|
UINT32 opcode = cpu->GetOpcode(addr);
|
|
EOpFlags opFlags = cpu->GetOpFlags(addr, opcode);
|
|
|
|
// See if instruction is jump
|
|
if (opFlags & (JumpSimple|JumpLoop|JumpSub))
|
|
{
|
|
// If so, see if address is valid (ie known at disassemble time)
|
|
UINT32 jumpAddr;
|
|
if (cpu->GetJumpAddr(addr, opcode, jumpAddr))
|
|
{
|
|
// If so, add flags to jump address and analyse destination code block too
|
|
if (opFlags & JumpSub) AddFlagToAddr(autoLabelsMap, jumpAddr, LFSubroutine, NULL);
|
|
else if (opFlags & JumpLoop) AddFlagToAddr(autoLabelsMap, jumpAddr, LFLoopPoint, NULL);
|
|
else AddFlagToAddr(autoLabelsMap, jumpAddr, LFJumpTarget, NULL);
|
|
AnalyseCode(seenIndices, validIndices, validIndexSet, autoLabelsMap, jumpAddr);
|
|
}
|
|
}
|
|
|
|
// Finish if instruction terminates code block (ie is not conditional and is either a non-returning jump, a return or some sort
|
|
// of reset/halting instruction)
|
|
if (!(opFlags & Conditional) && (opFlags & (JumpSimple|JumpLoop|ReturnEx|ReturnSub|HaltExec)))
|
|
return;
|
|
|
|
// Move to next index
|
|
index += (unsigned)codesLen / instrAlign;
|
|
|
|
// If reach end of address indices, code block must be invalid (TODO - invalidate whole code block?)
|
|
if (index >= totalIndices)
|
|
return;
|
|
|
|
// Move to next address
|
|
addr += (UINT32)codesLen;
|
|
|
|
// If move between regions, check new region is valid
|
|
if (addr > region->addrEnd)
|
|
{
|
|
region = cpu->GetRegion(addr);
|
|
if (region == NULL || !region->isCode) // (TODO - invalidate whole code block?)
|
|
return;
|
|
}
|
|
}
|
|
while (!seenIndices[index]);
|
|
}
|
|
|
|
void CCodeAnalyser::AddFlagToAddr(map<UINT32,CAutoLabel*> &autoLabelsMap, UINT32 addr, ELabelFlags flag, const char *subLabel)
|
|
{
|
|
if (flag == LFNone)
|
|
return;
|
|
map<UINT32,CAutoLabel*>::iterator it = autoLabelsMap.find(addr);
|
|
CAutoLabel *label;
|
|
if (it == autoLabelsMap.end())
|
|
{
|
|
label = new CAutoLabel(cpu, addr);
|
|
label->Acquire();
|
|
autoLabelsMap[addr] = label;
|
|
}
|
|
else
|
|
label = it->second;
|
|
label->AddFlag(flag, subLabel);
|
|
}
|
|
|
|
void CCodeAnalyser::AbortAnalysis()
|
|
{
|
|
m_abortAnalysis = true;
|
|
}
|
|
|
|
void CCodeAnalyser::ClearCustomEntryAddrs()
|
|
{
|
|
m_customEntryAddrs.clear();
|
|
}
|
|
|
|
void CCodeAnalyser::AddCustomEntryAddr(UINT32 entryAddr)
|
|
{
|
|
if (find(m_customEntryAddrs.begin(), m_customEntryAddrs.end(), entryAddr) == m_customEntryAddrs.end())
|
|
m_customEntryAddrs.push_back(entryAddr);
|
|
}
|
|
|
|
bool CCodeAnalyser::RemoveCustomEntryAddr(UINT32 entryAddr)
|
|
{
|
|
vector<UINT32>::iterator it = find(m_customEntryAddrs.begin(), m_customEntryAddrs.end(), entryAddr);
|
|
if (it == m_customEntryAddrs.end())
|
|
return false;
|
|
m_customEntryAddrs.erase(it);
|
|
return true;
|
|
}
|
|
|
|
#ifdef DEBUGGER_HASBLOCKFILE
|
|
bool CCodeAnalyser::LoadState(CBlockFile *state)
|
|
{
|
|
// Load custom entry addresses
|
|
char blockStr[255];
|
|
sprintf(blockStr, "%s.entryaddrs", cpu->name);
|
|
if (state->FindBlock(blockStr) == OKAY)
|
|
{
|
|
m_customEntryAddrs.clear();
|
|
UINT32 numAddrs;
|
|
state->Read(&numAddrs, sizeof(numAddrs));
|
|
for (UINT32 i = 0; i < numAddrs; i++)
|
|
{
|
|
UINT32 addr;
|
|
state->Read(&addr, sizeof(addr));
|
|
m_customEntryAddrs.push_back(addr);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CCodeAnalyser::SaveState(CBlockFile *state)
|
|
{
|
|
// Save custom entry addresses
|
|
char blockStr[255];
|
|
sprintf(blockStr, "%s.entryaddrs", cpu->name);
|
|
state->NewBlock(blockStr, __FILE__);
|
|
UINT32 numAddrs = m_customEntryAddrs.size();
|
|
state->Write(&numAddrs, sizeof(numAddrs));
|
|
for (UINT32 i = 0; i < numAddrs; i++)
|
|
{
|
|
UINT32 addr = m_customEntryAddrs[i];
|
|
state->Write(&addr, sizeof(addr));
|
|
}
|
|
return true;
|
|
}
|
|
#endif // DEBUGGER_HASBLOCKFILE
|
|
}
|
|
#endif // SUPERMODEL_DEBUGGER
|