Debugger: Initial debugger implementation

This commit is contained in:
Ty Lamontagne 2022-12-24 01:51:44 -05:00 committed by refractionpcsx2
parent 78c9b7f33e
commit 2f0b244f48
57 changed files with 30812 additions and 5 deletions

6
3rdparty/demangler/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,6 @@
cmake_minimum_required(VERSION 3.6)
project(demangler CXX)
add_subdirectory(src)

59
3rdparty/demangler/demangler.vcxproj vendored Normal file
View File

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(SolutionDir)common\vsprops\BaseProjectConfig.props" />
<Import Project="$(SolutionDir)common\vsprops\WinSDK.props" />
<PropertyGroup Label="Globals">
<ProjectGuid>{1E3D706C-1D95-4E1B-BDF2-CA3D0007DF7F}</ProjectGuid>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
<WholeProgramOptimization Condition="$(Configuration.Contains(Release))">true</WholeProgramOptimization>
<UseDebugLibraries Condition="$(Configuration.Contains(Debug))">true</UseDebugLibraries>
<UseDebugLibraries Condition="!$(Configuration.Contains(Debug))">false</UseDebugLibraries>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings" />
<ImportGroup Label="PropertySheets">
<Import Project="..\DefaultProjectRootDir.props" />
<Import Project="..\3rdparty.props" />
<Import Condition="$(Configuration.Contains(Debug))" Project="..\..\common\vsprops\CodeGen_Debug.props" />
<Import Condition="$(Configuration.Contains(Devel))" Project="..\..\common\vsprops\CodeGen_Devel.props" />
<Import Condition="$(Configuration.Contains(Release))" Project="..\..\common\vsprops\CodeGen_Release.props" />
<Import Condition="!$(Configuration.Contains(Release))" Project="..\..\common\vsprops\IncrementalLinking.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="src\demangler.cpp" />
<ClCompile Include="src\demtools.cpp" />
<ClCompile Include="src\gparser.cpp" />
<ClCompile Include="src\igrams.cpp" />
<ClCompile Include="src\stgrammars\borlandll.cpp" />
<ClCompile Include="src\stgrammars\gccll.cpp" />
<ClCompile Include="src\stgrammars\msll.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\demangler\demangler.h" />
<ClInclude Include="include\demangler\demglobal.h" />
<ClInclude Include="include\demangler\demtools.h" />
<ClInclude Include="include\demangler\gparser.h" />
<ClInclude Include="include\demangler\igrams.h" />
<ClInclude Include="include\demangler\stgrammars\borlandll.h" />
<ClInclude Include="include\demangler\stgrammars\gccll.h" />
<ClInclude Include="include\demangler\stgrammars\msll.h" />
</ItemGroup>
<ItemDefinitionGroup>
<ClCompile>
<PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions>
<WarningLevel>TurnOffAllWarnings</WarningLevel>
<AdditionalIncludeDirectories>$(ProjectDir)include;$(ProjectDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" />
</Project>

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="src\demangler.cpp" />
<ClCompile Include="src\demtools.cpp" />
<ClCompile Include="src\gparser.cpp" />
<ClCompile Include="src\igrams.cpp" />
<ClCompile Include="src\stgrammars\borlandll.cpp" />
<ClCompile Include="src\stgrammars\gccll.cpp" />
<ClCompile Include="src\stgrammars\msll.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\demangler\demangler.h" />
<ClInclude Include="include\demangler\demglobal.h" />
<ClInclude Include="include\demangler\demtools.h" />
<ClInclude Include="include\demangler\gparser.h" />
<ClInclude Include="include\demangler\igrams.h" />
<ClInclude Include="include\demangler\stgrammars\borlandll.h" />
<ClInclude Include="include\demangler\stgrammars\gccll.h" />
<ClInclude Include="include\demangler\stgrammars\msll.h" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,45 @@
/**
* @file include/demangler/demangler.h
* @brief Demangler library.
* @copyright (c) 2017 Avast Software, licensed under the MIT license
*/
#ifndef DEMANGLER_DEMANGLERL_H
#define DEMANGLER_DEMANGLERL_H
#include <memory>
#include <string>
#include "demangler/gparser.h"
namespace demangler {
/**
* The grammar parser class - the core of the demangler.
*/
class CDemangler {
cGram *pGram;
cName *pName;
std::string compiler = "gcc";
cGram::errcode errState; /// error state; 0 = everyting is ok
public:
CDemangler(std::string gname, bool i = true);
static std::unique_ptr<CDemangler> createGcc(bool i = true);
static std::unique_ptr<CDemangler> createMs(bool i = true);
static std::unique_ptr<CDemangler> createBorland(bool i = true);
virtual ~CDemangler();
bool isOk();
std::string printError();
void resetError();
void createGrammar(std::string inputfilename, std::string outputname);
cName *demangleToClass(std::string inputName);
std::string demangleToString(std::string inputName);
void setSubAnalyze(bool x);
};
} // namespace demangler
#endif

View File

@ -0,0 +1,20 @@
/**
* @file include/demangler/demglobal.h
* @brief Global variables in demangler namespace.
* @copyright (c) 2017 Avast Software, licensed under the MIT license
*/
#ifndef DEMANGLER_DEMGLOBAL_H
#define DEMANGLER_DEMGLOBAL_H
#include "demangler/igrams.h"
namespace demangler {
extern cGram::igram_t internalGrammarStruct;
extern cIgram_msll* igram_msll;
extern cIgram_gccll* igram_gccll;
} // namespace demangler
#endif

View File

@ -0,0 +1,36 @@
/**
* @file include/demangler/demtools.h
* @brief Tools and extra functions for demangler.
* @copyright (c) 2017 Avast Software, licensed under the MIT license
*/
#ifndef DEMANGLER_DEMTOOLS_H
#define DEMANGLER_DEMTOOLS_H
#include <string>
namespace demangler {
/**
* @brief Structure for date and time.
*/
struct sdate_t {
unsigned int y = 0;
unsigned int m = 0;
unsigned int d = 0;
unsigned int h = 0;
unsigned int min = 0;
unsigned int s = 0;
};
bool fileExists(const std::string &filename);
void initSdate_t(sdate_t &x);
sdate_t genTimeStruct();
void xreplace(std::string &source, const std::string &tobereplaced, const std::string &replacement);
} // namespace demangler
#endif

View File

@ -0,0 +1,830 @@
/**
* @file include/demangler/gparser.h
* @brief Parser of LL grammar.
* @copyright (c) 2017 Avast Software, licensed under the MIT license
*/
#ifndef DEMANGLER_GPARSER_H
#define DEMANGLER_GPARSER_H
#include <map>
#include <set>
#include <stack>
#include <string>
#include <utility>
#include <vector>
//internal grammars
namespace demangler {
/**
* @brief Class which holds a demangled name.
*/
class cName {
public:
/**
* @brief Enum of types of the mangled types (Determines whether a type is built-in or named).
*/
enum ttype { //type of type
TT_UNKNOWN = 0, //can be used for unknown return type
TT_BUILTIN,
TT_NAME,
TT_NUM,
TT_PEXPR
};
/**
* @brief Enum of types of the mangled names.
*/
enum ntype { //type of the mangled name
NT_FUNCTION = 0,
NT_TEMPLFUNCTION,
NT_OPERATOR,
NT_CONSTRUCTOR,
NT_DESTRUCTOR,
NT_DATA,
NT_VTABLE,
NT_R0,
NT_R1,
NT_R2,
NT_R3,
NT_R4,
NT__A,
NT__B,
NT__C,
NT__D,
NT__E,
NT__F,
NT__G,
NT__H,
NT__I,
NT__J,
NT__K,
NT__L,
NT__M,
NT__N,
NT__O,
NT__P,
NT__Q,
NT__R,
NT__S,
NT__T,
NT__U,
NT__V,
NT__W,
NT__X,
NT__Y,
NT__Z,
NT_CLASS
};
/**
* @brief Enum of operator types.
*/
enum optype { //type of operator
OT_NULL = 0,
OT_NEW,
OT_NEWARR,
OT_DEL,
OT_DELARR,
OT_UPLUS,
OT_UMINUS,
OT_UAND,
OT_UAST,
OT_TILDA,
OT_PLUS,
OT_MINUS,
OT_AST,
OT_DIV,
OT_MOD,
OT_AND,
OT_OR,
OT_EXP,
OT_ASSIGN,
OT_PLUSASS,
OT_MINUSASS,
OT_ASTASS,
OT_DIVASS,
OT_MODASS,
OT_ANDASS,
OT_ORASS,
OT_EXPASS,
OT_LSHIFT,
OT_RSHIFT,
OT_LSHIFTASS,
OT_RSHIFTASS,
OT_EQ,
OT_NEQ,
OT_LT,
OT_GT,
OT_LE,
OT_GE,
OT_NOT,
OT_ANDAND,
OT_OROR,
OT_PLUSPLUS,
OT_MINUSMINUS,
OT_COMMA,
OT_PTAST,
OT_PT,
OT_BRACKETS,
OT_ARR,
OT_QUESTION,
OT_SIZEOFT,
OT_SIZEOFE,
OT_ALIGNOFT,
OT_ALIGNOFE,
OT_CAST
};
/**
* @brief Enum of built-in types.
*/
enum st_type { //standard built-in types
T_VOID = 0,
T_WCHAR,
T_BOOL,
T_CHAR,
T_SCHAR,
T_UCHAR,
T_SHORT,
T_USHORT,
T_INT,
T_UINT,
T_LONG,
T_ULONG,
T_LONGLONG,
T_ULONGLONG,
T_INT128,
T_UINT128,
T_FLOAT,
T_DOUBLE,
T_LONGDOUBLE,
T_FLOAT128,
T_ELLIPSIS,
T_DD,
T_DE,
T_DF,
T_DH,
T_CHAR32,
T_CHAR16,
T_AUTO,
T_NULLPTR
};
/**
* @brief Enum of member function accessibility.
*/
enum memfacc_t { //standard built-in types
MFM_NULL = 0,
MFM_PRIVATE,
MFM_PUBLIC,
MFM_PROTECTED
};
/**
* @brief Enum of function call conventions.
*/
enum fcall_t { //standard built-in types
FCC_NULL = 0,
FCC_CDECL,
FCC_PASCAL,
FCC_FORTRAN,
FCC_THISCALL,
FCC_STDCALL,
FCC_FASTCALL,
FCC_INTERRUPT
};
/**
* @brief Enum of named type classifications. Union, Struct, Class, Enum.
*/
enum mstype_t { //standard built-in types
MST_NULL = 0,
MST_UNION,
MST_STRUCT,
MST_CLASS,
MST_ENUM
};
/**
* @brief Structure of an unqualified name.
* @param un String which holds the name.
* @param tpl Pointer to the the template (vector of types) of this unqualified name. If nullptr, the unqualified name consists only of the string.
*/
struct name_t {
std::string un; //unqualified name
void *tpl = nullptr; //std::vector<type_t>
bool op = false; //is it operator name element?
};
/**
* @brief Structure of a type.
* @param type Type of the type.
* @param b Built-in type. This value is defined only if 'type' is TT_BUILTIN.
* @param n Qualified name of the type. This value is defined only if 'type' is TT_NAME.
* @param is_const Bool value which determines whether the type is const.
* @param is_restrict Bool value which determines whether the type is restrict.
* @param is_volatile Bool value which determines whether the type is volatile.
* @param is_pointer Integer value determining the pointer level of the type.
* @param is_reference Bool value which determines whether the type is a reference.
* @param is_rvalue Bool value which determines whether the type is an R-value.
* @param is_cpair Bool value which determines whether the type is a complex pair.
* @param is_imaginary Bool value which determines whether the type is imaginary.
*/
struct type_t {
ttype type = TT_UNKNOWN; //type of type... builtin or named type
st_type b = T_VOID; //builtin type
void *value = nullptr; //expression value
std::vector<name_t> n; //qualified name of named type
std::string modifiers;
mstype_t mst = MST_NULL;
int num = 0;
bool is_array = false;
std::vector<unsigned int> array_dimensions;
bool is_const = false;
bool is_restrict = false;
bool is_volatile = false;
unsigned int is_pointer = 0;
bool is_reference = false;
bool is_rvalue = false; //r-value reference
bool is_cpair = false; //complex pair
bool is_imaginary = false;
std::string getLlvmType();
private:
std::string llvmIr;
std::string getLlvmTypePrivate();
};
ntype name_type = NT_FUNCTION; //name type
optype operator_type = OT_NULL; //type of operator. it is OT_NULL if function is not an operator
type_t return_type;
type_t special_type; //return value for function or conversion type for operator OT_CAST
std::string modifiers;
memfacc_t member_function_access = MFM_NULL;
fcall_t function_call = FCC_NULL;
bool is_static = false;
bool is_virtual = false;
std::string storage_class;
std::vector<long int> rttibcd;
std::vector<type_t> parameters; //function parameters
std::vector<name_t> name; //qualified name composed of unqualified names
void *tf_tpl = nullptr;
bool last_shown_endtpl = false; //an auxiliary variable which helps to add space between multiple '>' at the end of templates
void deleteparams(std::vector<type_t> & vec);
cName(); //constructor
virtual ~cName(); //mass destruction
void type_t_clear(type_t &x);
void addname(const std::vector<name_t> & inname); //set the function name
void addpar(const std::vector<type_t> & inpar); //set the parameters of the name
void setnametype(ntype x); //set type of the mangled name
void setfcall(fcall_t x); //set type of the function call convention
void setmfacc(memfacc_t x); //set type of the member function access level
ntype getnametype(void); //get type of the mangled name
void setop(optype x); //set operator type
void setret(type_t x); //set return type
void setspec(type_t x); //set special type
void setstatic(); //set name's static flag
void setvirtual(); //set name's virtual flag
void addmodifier(char x); //add a modifier to the name modifier string
void addstcl(char x); //add a modifier to the storage class string
void setmodifiers(std::string x); //set modifiers
void settftpl(void* x); //set template function template
void addrttinum(long int x); //add a RTTI Base Class descriptor num
std::string optypetostr(optype x); //operator type to string
std::string printmodifiers(std::string x, bool space);
std::string printpremodifiers(std::string x, bool space);
std::string printpostmodifiers(std::string x, bool space);
std::string printname(std::vector<name_t> & vec, std::string compiler = "gcc");
std::string printparams(std::vector<type_t> & vec, bool ignorevoid = false, std::string compiler = "gcc");
std::string printpexpr(type_t & x);
/**
* @brief Print the calling convention to a string.
* @param callconv The calling convention to be printed.
* @return String containing calling convention.
*/
std::string printcallingconvention(fcall_t callconv);
std::string printall(std::string compiler = "gcc");
std::string printall_old(bool msvcpp = false);
}; //class cName
/**
* @brief Grammar class. It's member functions allow loading an external grammar and demangling a mangled name using the grammar.
*/
class cGram {
public:
/**
* @brief Global array of semantic action names. Used when building internal LL table from external grammar.
*/
static const char *semactname[];
/**
* @brief Enum of error codes.
*/
enum errcode {
ERROR_OK = 0,
ERROR_FILE,
ERROR_FSM,
ERROR_SYN,
ERROR_MEM,
ERROR_GRAM,
ERROR_LL,
ERROR_UNK
};
/**
* @brief An array of error messages.
*/
static const char *errmsg[];
/**
* @brief Type of a grammar element. It can be either a terminal or a non-terminal.
*/
enum gelemtype {
GE_TERM = 0,
GE_NONTERM
};
/**
* @brief Structure of a grammar element.
* @param type Type of the element (terminal or non-terminal).
* @param nt The name of the non-terminal. Only vylid if type is GE_NONTERM.
* @param t The byte value of the terminal. Only valid if type is GE_TERM.
*/
struct gelem_t {
gelem_t(gelemtype t, char* n, unsigned int i, char c) :
type(t),
nt(n),
ntst(i),
t(c)
{}
gelem_t() {}
gelemtype type = GE_TERM;
char* nt = nullptr;
unsigned int ntst = 0;
char t = 0;
};
/**
* @brief Enum of semantic actions.
*/
enum semact {
//do nothing
SA_NULL = 0,
//set type of name (function, operator, constructor, destructor, data)
SA_SETNAMEF,
SA_SETNAMETF,
SA_SETNAMEO,
SA_SETNAMEC,
SA_SETNAMED,
SA_SETNAMEX,
SA_SETNAMEVT,
//set operator type
SA_SETOPXX,
SA_SETOPNW,
SA_SETOPNA,
SA_SETOPDL,
SA_SETOPDA,
SA_SETOPPS,
SA_SETOPNG,
SA_SETOPAD,
SA_SETOPDE,
SA_SETOPCO,
SA_SETOPPL,
SA_SETOPMI,
SA_SETOPML,
SA_SETOPDV,
SA_SETOPRM,
SA_SETOPAN,
SA_SETOPOR,
SA_SETOPEO,
SA_SETOPASS,
SA_SETOPPLL,
SA_SETOPMII,
SA_SETOPMLL,
SA_SETOPDVV,
SA_SETOPRMM,
SA_SETOPANN,
SA_SETOPORR,
SA_SETOPEOO,
SA_SETOPLS,
SA_SETOPRS,
SA_SETOPLSS,
SA_SETOPRSS,
SA_SETOPEQ,
SA_SETOPNE,
SA_SETOPLT,
SA_SETOPGT,
SA_SETOPLE,
SA_SETOPGE,
SA_SETOPNT,
SA_SETOPAA,
SA_SETOPOO,
SA_SETOPPP,
SA_SETOPMM,
SA_SETOPCM,
SA_SETOPPM,
SA_SETOPPT,
SA_SETOPCL,
SA_SETOPIX,
SA_SETOPQU,
SA_SETOPST,
SA_SETOPSZ,
SA_SETOPAT,
SA_SETOPAZ,
SA_SETOPCV,
//builtin types
SA_SETTYPEV,
SA_SETTYPEW,
SA_SETTYPEB,
SA_SETTYPEC,
SA_SETTYPEA,
SA_SETTYPEH,
SA_SETTYPES,
SA_SETTYPET,
SA_SETTYPEI,
SA_SETTYPEJ,
SA_SETTYPEL,
SA_SETTYPEM,
SA_SETTYPEX,
SA_SETTYPEY,
SA_SETTYPEN,
SA_SETTYPEO,
SA_SETTYPEF,
SA_SETTYPED,
SA_SETTYPEE,
SA_SETTYPEG,
SA_SETTYPEZ,
//parameter modifiers
SA_SETCONST,
SA_SETRESTRICT,
SA_SETVOLATILE,
SA_SETPTR,
SA_SETREF,
SA_SETRVAL,
SA_SETCPAIR,
SA_SETIM,
//substitutions
SA_SUBSTD, //::std::
SA_SUBALC, //::std::allocator
SA_SUBSTR, //::std::basic_string
SA_SUBSTRS, //::std::basic_string<char,::std::char_traits<char>,::std::allocator<char>>
SA_SUBISTR, //::std::basic_istream<char, std::char_traits<char>>
SA_SUBOSTR, //::std::basic_ostream<char, std::char_traits<char>>
SA_SUBIOSTR, //::std::basic_iostream<char, std::char_traits<char>>
//other very important semantic actions
SA_LOADID, //load an unqualified name into the qualified vector of names
SA_LOADSUB, //load a substitution
SA_LOADTSUB, //load a template sub
SA_LOADARR, //load an array dimension
SA_STOREPAR, //store current parameter to vector of parameters
SA_STORETEMPARG, //store current parameter to current vector of template arguments
SA_STORETEMPLATE, //store the whole template into the last name element of last name vector
SA_BEGINTEMPL, //begin a template
SA_SKIPTEMPL, //skip a template
SA_PAR2F, //store current vector of parameters into the function
SA_PAR2RET, //store current parameter to the return value
SA_PAR2SPEC, //store current parameter to the special value
SA_UNQ2F, //future identifiers are added to the function name
SA_UNQ2P, //function identifiers are added to parameter name
//substitution expansion modifiers
SA_SSNEST, //nested sub
SA_STUNQ, //unqualified std:: sub
SA_SSNO, //other sub derived from <name>
SA_TYPE2EXPR, //builtin type is converted to primary expression
SA_EXPRVAL, //load expression value
SA_BEGINPEXPR, //begin a primary expression
SA_STOREPEXPR, //end a primary expression
SA_COPYTERM, //copy the terminal on the input into current_name in substitution analyzer
SA_ADDCHARTONAME, //add current character to current unqualified name
SA_STORENAME, //move current unqualified name into current name vector
SA_REVERSENAME,
SA_SETPRIVATE,
SA_SETPUBLIC,
SA_SETPROTECTED,
SA_SETFCDECL,
SA_SETFPASCAL,
SA_SETFFORTRAN,
SA_SETFTHISCALL,
SA_SETFSTDCALL,
SA_SETFFASTCALL,
SA_SETFINTERRUPT,
SA_SETUNION,
SA_SETSTRUCT,
SA_SETCLASS,
SA_SETENUM,
SA_SETSTATIC,
SA_SETVIRTUAL,
SA_STCLCONST,
SA_STCLVOL,
SA_STCLFAR,
SA_STCLHUGE,
SA_SAVENAMESUB,
SA_LOADNAMESUB,
SA_MSTEMPLPSUB,
SA_SETNAMER0,
SA_SETNAMER1,
SA_SETNAMER2,
SA_SETNAMER3,
SA_SETNAMER4,
SA_SETNAME_A,
SA_SETNAME_B,
SA_SETNAME_C,
SA_SETNAME_D,
SA_SETNAME_E,
SA_SETNAME_F,
SA_SETNAME_G,
SA_SETNAME_H,
SA_SETNAME_I,
SA_SETNAME_J,
SA_SETNAME_K,
SA_SETNAME_L,
SA_SETNAME_M,
SA_SETNAME_N,
SA_SETNAME_O,
SA_SETNAME_P,
SA_SETNAME_Q,
SA_SETNAME_R,
SA_SETNAME_S,
SA_SETNAME_T,
SA_SETNAME_U,
SA_SETNAME_V,
SA_SETNAME_W,
SA_SETNAME_X,
SA_SETNAME_Y,
SA_SETNAME_Z,
SA_TEMPL2TFTPL,
SA_BEGINBSUB,
SA_LOADBSUB,
SA_ADDMCONST,
SA_ADDMVOL,
SA_ADDMFAR,
SA_ADDMHUGE,
SA_LOADMSNUM,
SA_NUMTORTTIBCD,
SA_NUMTOTYPE,
SA_BORLANDNORMALIZEPARNAME,
SA_BORLANDID,
SA_LOADBORLANDSUB,
SA_BORLANDARR,
SA_END
};
/**
* @brief Structure of an element in an internal LL table.
* @param n Rule number. Numbered from 1. 0 is reserved for "no rule", which indicates a syntax error.
* @param s Semantic action to be done when this LL element is used.
*/
struct llelem_t {
llelem_t(unsigned int i, semact ss) :
n(i),
s(ss)
{}
llelem_t() {}
unsigned int n = 0;
semact s = SA_NULL;
};
/**
* @brief Struct used to describe a rule boundaries in an internal LL table.
* @param offset Offset from the start of ruleelements array.
* @param size Number of elements in the current rule.
*/
struct ruleaddr_t {
ruleaddr_t(unsigned int o, unsigned int s) :
offset(o),
size(s)
{}
unsigned int offset = 0;
unsigned int size = 0;
};
/**
* @brief Structure of a grammar rule.
* @param n Number of the rule. Numbered from 1. 0 is reserved for "no rule", which indicates a syntax error.
* @param left The left side of the rule, consisting of only one non-terminal.
* @param right The right side of the rule, which is a sequence of terminals or non-terminals. May be empty.
*/
struct rule_t {
unsigned int n = 0;
gelem_t left;
std::vector<gelem_t> right;
};
/**
* @brief Types of substitution expansion.
*/
enum subtype {
ST_NULL = 0,
ST_STUNQ,
ST_SSNEST,
ST_SSNO
};
/**
* @brief States of the FSM for parsing grammar rules from a file.
*/
enum fsmstate {
S_START = 0, //beginning of a line
S_NT_LEFT, //non-terminal on the left side
S_OP1, //:
S_OP2, //:
S_OP3, //=
S_RIGHT, //right side
S_NT_RIGHT, //non-terminal on the right side
S_T, //terminal on the right side
S_QT, //quoted terminal on the right side
S_QT_ESC, //escape sequence of a quoted terminal
S_IGNORE, //ignore the rest of the line
S_ERROR, //error ocurred
S_NULL //just a NULL terminator for the array of final states
};
/**
* @brief Class for comparing two grammar element structures. Used in std::set of grammar elements.
*/
class comparegelem_c {
public:
/**
* @brief Comparison function for two grammar element structures
* @param t1 First grammar element.
* @param t2 Second grammar element.
*/
bool operator() (const gelem_t t1, const gelem_t t2) const {
//if types don't match, terminal is less than non-terminal
if (t1.type!= t2.type) {
return (t1.type == GE_TERM)?true:false;
}
//for two terminals, compare their byte values
if (t1.type == GE_TERM) {
return t1.t < t2.t;
}
//for two non-terminals, compare the non-terminal name strings
else {
return t1.nt < t2.nt;
}
}
};
/**
* @brief Struct for internal grammar.
*/
struct igram_t {
igram_t(unsigned int tsx, unsigned int rax, unsigned int rex, unsigned int lx, unsigned int ly,
gelem_t r, unsigned char* ts, ruleaddr_t* ra, gelem_t* re, llelem_t** lt) :
terminal_static_x(tsx),
ruleaddrs_x(rax),
ruleelements_x(rex),
llst_x(lx),
llst_y(ly),
root(r),
terminal_static(ts),
ruleaddrs(ra),
ruleelements(re),
llst(lt)
{}
igram_t() {}
//dimensions of the arrays
unsigned int terminal_static_x = 0;
unsigned int ruleaddrs_x = 0;
unsigned int ruleelements_x = 0;
unsigned int llst_x = 0; //first one
unsigned int llst_y = 0; //second one
//root element
gelem_t root;
//the arrays
unsigned char* terminal_static = nullptr; //array of used terminals
ruleaddr_t* ruleaddrs = nullptr; //structures defining offset and size of each rule in the ruleelements table
gelem_t* ruleelements = nullptr; //all elements of all rules
llelem_t** llst = nullptr; //the LL table
};
/**
* @brief The struct variable containing pointers to internal grammar data.
*/
cGram::igram_t internalGrammarStruct;
//FSM for parsing external rules
static const fsmstate fsm_final[];
static const gelem_t T_EOF;
bool internalGrammar = false;
std::string compiler;
/*
* Variables used for generation of new internal grammars
*/
std::string createIGrammar; //if this thing is on, new internal grammar will be generated from external grammar file
unsigned int newIG_terminal_static_x = 0;
std::size_t newIG_ruleaddrs_x = 0;
std::size_t newIG_ruleelements_x = 0;
std::size_t newIG_llst_x = 0;
std::size_t newIG_llst_y = 0;
std::string newIG_root;
std::string newIG_terminal_static;
std::string newIG_ruleaddrs;
std::string newIG_ruleelements;
std::string newIG_llst;
/*
* Variables for parsed external grammar
*/
std::vector<rule_t> rules;
std::map<std::string,bool> empty;
std::map<std::string,std::set<gelem_t,comparegelem_c>> first;
std::map<std::string,std::set<gelem_t,comparegelem_c>> follow;
std::map<unsigned int,std::set<gelem_t,comparegelem_c>> predict;
std::map<std::string,std::map<char,std::pair<unsigned int, semact>>> ll;
std::vector<unsigned char> terminals;
std::vector<std::string> nonterminals;
size_t lex_position = 0; //position in the source file
std::fstream *source = nullptr; //pointer to the input filestream
/*
* methods
*/
errcode loadfile(const std::string filename);
bool is_final(fsmstate s); //is s a final state of fsm?
char getc();
bool eof();
bool lf();
errcode getgrammar(const std::string filename);
bool copyset(std::set<gelem_t,comparegelem_c> & src, std::set<gelem_t,comparegelem_c> & dst);
void genempty();
void genfirst();
bool getempty(std::vector<gelem_t> & src);
std::set<gelem_t,comparegelem_c> getfirst(std::vector<gelem_t> & src);
llelem_t getllpair(std::string nt, unsigned int ntst, unsigned char t);
void genfollow();
void genpredict();
errcode genll();
errcode genconstll();
void genllsem();
errcode analyze(std::string input, cName & pName);
std::string subanalyze(const std::string input, cGram::errcode *err);
semact getsem(const std::string input);
void *getbstpl(cName & pName);
void *getstrtpl(cName & pName);
bool issub(std::string candidate,std::vector<std::string> & vec);
void showsubs(std::vector<std::string> & vec);
long int b36toint(std::string x);
void * copynametpl(void * src);
public:
//constructor
cGram();
//destructor
virtual ~cGram();
errcode initialize(std::string gname, bool i = true);
errcode parse(const std::string filename);
cName *perform(const std::string input, errcode *err);
void demangleClassName(const std::string& input, cName* retvalue, cGram::errcode& err_i);
void showrules();
void showempty();
void showfirst();
void showfollow();
void showpredict();
void showll();
unsigned int isnt(std::vector<std::string> & nonterminals, std::string nonterminal);
unsigned int ist(std::vector<unsigned char> & terminals, unsigned char terminal);
void resetError() {errString = "";}
std::string errString; //string containing last error message
bool errValid = false; //is the gParser valid? false if it has not been properly initialized yet
bool SubAnalyzeEnabled = false; //enable substitution analysis for GCC demangler?
void setSubAnalyze(bool x);
errcode generateIgrammar(const std::string inputfilename, const std::string outputname);
std::string generatedTerminalStatic;
}; //class cGram
} // namespace demangler
#endif

View File

@ -0,0 +1,23 @@
/**
* @file include/demangler/igrams.h
* @brief Internal grammar list.
* @copyright (c) 2017 Avast Software, licensed under the MIT license
*/
#ifndef DEMANGLER_IGRAMS_H
#define DEMANGLER_IGRAMS_H
//[igram] add internal grammar headers here
#include "demangler/stgrammars/borlandll.h"
#include "demangler/stgrammars/gccll.h"
#include "demangler/stgrammars/msll.h"
namespace demangler {
bool initIgram(std::string gname, cGram* gParser);
void deleteIgrams(cGram* gParser);
} // namespace demangler
#endif

View File

@ -0,0 +1,25 @@
/**
* @file include/demangler/stgrammars/borlandll.h
* @brief Internal LL grammar for demangler.
* @copyright (c) 2017 Avast Software, licensed under the MIT license
*/
#ifndef DEMANGLER_STGRAMMARS_BORLANDLL_H
#define DEMANGLER_STGRAMMARS_BORLANDLL_H
#include "demangler/gparser.h"
namespace demangler {
class cIgram_borlandll {
public:
static unsigned char terminal_static[256];
static cGram::llelem_t llst[280][69];
static cGram::ruleaddr_t ruleaddrs[467];
static cGram::gelem_t ruleelements[603];
static cGram::gelem_t root;
cGram::igram_t getInternalGrammar();
};
} // namespace demangler
#endif

View File

@ -0,0 +1,25 @@
/**
* @file include/demangler/stgrammars/gccll.h
* @brief Internal LL grammar for demangler.
* @copyright (c) 2017 Avast Software, licensed under the MIT license
*/
#ifndef DEMANGLER_STGRAMMARS_GCCLL_H
#define DEMANGLER_STGRAMMARS_GCCLL_H
#include "demangler/gparser.h"
namespace demangler {
class cIgram_gccll {
public:
static unsigned char terminal_static[256];
static cGram::llelem_t llst[254][64];
static cGram::ruleaddr_t ruleaddrs[423];
static cGram::gelem_t ruleelements[445];
static cGram::gelem_t root;
cGram::igram_t getInternalGrammar();
};
} // namespace demangler
#endif

View File

@ -0,0 +1,26 @@
/**
* @file include/demangler/stgrammars/msll.h
* @brief Internal LL grammar for demangler.
* @copyright (c) 2017 Avast Software, licensed under the MIT license
*/
#ifndef DEMANGLER_STGRAMMARS_MSLL_H
#define DEMANGLER_STGRAMMARS_MSLL_H
#include "demangler/gparser.h"
namespace demangler {
class cIgram_msll {
public:
static unsigned char terminal_static[256];
static cGram::llelem_t llst[249][68];
static cGram::ruleaddr_t ruleaddrs[534];
static cGram::gelem_t ruleelements[796];
static cGram::gelem_t root;
cGram::igram_t getInternalGrammar();
};
} // namespace demangler
#endif

16
3rdparty/demangler/src/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,16 @@
set(DEMANGLER_SOURCES
demangler.cpp
demtools.cpp
gparser.cpp
igrams.cpp
stgrammars/borlandll.cpp
stgrammars/gccll.cpp
stgrammars/msll.cpp
)
add_library(demangler STATIC ${DEMANGLER_SOURCES})
target_include_directories(demangler PUBLIC ${PROJECT_SOURCE_DIR}/include/)
set_property(TARGET demangler PROPERTY CXX_STANDARD 17)
set_property(TARGET demangler PROPERTY CXX_STANDARD_REQUIRED ON)

131
3rdparty/demangler/src/demangler.cpp vendored Normal file
View File

@ -0,0 +1,131 @@
/**
* @file src/demangler/demangler.cpp
* @brief Demangler library.
* @copyright (c) 2017 Avast Software, licensed under the MIT license
*/
#include <iostream>
#include <string>
#include "demangler/demangler.h"
namespace demangler {
/**
* @brief Constructor of CDemangler class.
* @param gname Grammar name. If internal grammar is used, internal grammar of this name must exist.
* If external grammar is used file with this name must exist and it used as external grammar.
* @param i Use internal grammar? Default setting is true. If set to false, external grammar is used.
*/
CDemangler::CDemangler(std::string gname, bool i):
pGram(new cGram()),
pName(nullptr),
errState(cGram::ERROR_OK) {
//if gname is empty, pGram will not be initialized (may be used for creating new internal grammars)
if (!gname.empty()) {
errState = pGram->initialize(gname, i);
compiler = gname;
}
}
std::unique_ptr<CDemangler> CDemangler::createGcc(bool i)
{
return std::make_unique<CDemangler>("gcc", i);
}
std::unique_ptr<CDemangler> CDemangler::createMs(bool i)
{
return std::make_unique<CDemangler>("ms", i);
}
std::unique_ptr<CDemangler> CDemangler::createBorland(bool i)
{
return std::make_unique<CDemangler>("borland", i);
}
/**
* @brief Destructor of CDemangler class.
*/
CDemangler::~CDemangler() {
delete pGram;
}
/**
* @brief Check if the error state of demangler is ok. Returns false if an error has ocurred during the last action.
* @return Boolean value determining whether everything is ok.
*/
bool CDemangler::isOk() {
if (errState == cGram::ERROR_OK) {
return true;
}
else {
return false;
}
}
/**
* @brief Returns string describing the last error.
*/
std::string CDemangler::printError() {
if (pGram != nullptr) {
return pGram->errString;
}
else {
return "No grammar class allocated. Cannot get error state.";
}
}
/**
* @brief Reset error state.
*/
void CDemangler::resetError() {
if (pGram != nullptr) {
pGram->resetError();
errState = cGram::ERROR_OK;
}
}
/**
* @brief Function which converts external grammar into internal grammar.
* After using this function the demangler object must not be used for demangling.S
* errState may be set if an error occurs.
* @param inputfilename The name of the file which contains grammar rules.
* @param outputname The name of the output grammar.
*/
void CDemangler::createGrammar(std::string inputfilename, std::string outputname) {
errState = pGram->generateIgrammar(inputfilename, outputname);
}
/**
* @brief Demangle the input string and return the demangled name class. errState may be set if an error occurs.
* @param inputName The name to be demangled.
* @return Pointer to a cName object containing all info anout the demangled name.
*/
cName *CDemangler::demangleToClass(std::string inputName) {
return pGram->perform(inputName,&errState);
}
/**
* @brief Demangle the input string and return the demangled name as a string. errState may be set if an error occurs.
* @param inputName The name to be demangled.
* @return String containing the declaration of the demangled name.
*/
std::string CDemangler::demangleToString(std::string inputName) {
std::string retvalue;
resetError();
pName = pGram->perform(inputName,&errState);
retvalue = pName->printall(compiler);
delete pName;
return retvalue;
}
/**
* @brief Set substitution analysis manually to enabled or disabled.
* @param x Boolean value. True means enable, false means disable.
*/
void CDemangler::setSubAnalyze(bool x) {
pGram->setSubAnalyze(x);
}
} // namespace demangler

82
3rdparty/demangler/src/demtools.cpp vendored Normal file
View File

@ -0,0 +1,82 @@
/**
* @file src/demangler/demtools.cpp
* @brief Tools and extra functions for demangler.
* @copyright (c) 2017 Avast Software, licensed under the MIT license
*/
#include <ctime>
#include <fstream>
#include <sstream>
#include <string>
#include "demangler/demtools.h"
using namespace std;
namespace demangler {
/**
* @brief Function which finds out whether a file exists.
* @param filename Name of the file to be checked.
* @return Boolean value determining whether the file exists or not.
*/
bool fileExists(const std::string &filename) {
ifstream ifile(filename);
return ifile.is_open();
}
/**
* @brief Initializes a sdate_t to default values.
* @param x sdate_t to be initialized..
*/
void initSdate_t(sdate_t &x) {
x.y = 0;
x.m = 0;
x.d = 0;
x.h = 0;
x.min = 0;
x.s = 0;
}
/**
* @brief Get stuct with current date and time.
* @return Struct with current date and time.
*/
sdate_t genTimeStruct() {
sdate_t retvalue;
initSdate_t(retvalue);
time_t t = time(nullptr); // get time now
struct tm * now = localtime( & t );
//year
retvalue.y = now->tm_year + 1900;
//month
retvalue.m = (now->tm_mon + 1);
//day
retvalue.d = now->tm_mday;
//hour
retvalue.h = now->tm_hour;
//minute
retvalue.min = now->tm_min;
//second
retvalue.s = now->tm_sec;
return retvalue;
}
/**
* @brief Replaces strings "tobereplaced" in source with "replacement".
* @param source Source string.
* @param tobereplaced Substring which will be searched for and all of its instances will be replaced.
* @param replacement The replacement string.
*/
void xreplace(string &source, const string &tobereplaced, const string &replacement) {
std::size_t lastfound = 0;
if (tobereplaced != "") {
while (source.find(tobereplaced,lastfound) != source.npos) {
lastfound = source.find(tobereplaced,lastfound);
source.replace(source.find(tobereplaced,lastfound),tobereplaced.length(),replacement);
lastfound += replacement.length();
}
}
}
} // namespace demangler

View File

@ -0,0 +1,467 @@
<mangled-name> ::= <template-prefix> @ <qualified-name> <sem-unq2f> <function-section> <sem-end>
<template-prefix> ::=
<template-section> ::= % <name-element> $ t <sem-begintempl> <sem-beginbsub> <type> <type-more-template> %
<qualified-name> ::= <name-element> <name-element-more>
<name-element-more> ::=
<name-element-more> ::= @ <name-element> <name-element-more>
<name-element> ::= @ <name-element> #double @ for __linksys__ - ignore that for now and just use qualified name
<name-element> ::= $ <op>
<name-element> ::= <template-section>
<name-element> ::= <name-char> <name-char-more>
<name-char-more> ::= <sem-storename> #no more characters in name, store the name
<name-char-more> ::= <name-char> <name-char-more>
<name-char> ::= <sem-addchartoname> _
<name-char> ::= <sem-addchartoname> a
<name-char> ::= <sem-addchartoname> b
<name-char> ::= <sem-addchartoname> c
<name-char> ::= <sem-addchartoname> d
<name-char> ::= <sem-addchartoname> e
<name-char> ::= <sem-addchartoname> f
<name-char> ::= <sem-addchartoname> g
<name-char> ::= <sem-addchartoname> h
<name-char> ::= <sem-addchartoname> i
<name-char> ::= <sem-addchartoname> j
<name-char> ::= <sem-addchartoname> k
<name-char> ::= <sem-addchartoname> l
<name-char> ::= <sem-addchartoname> m
<name-char> ::= <sem-addchartoname> n
<name-char> ::= <sem-addchartoname> o
<name-char> ::= <sem-addchartoname> p
<name-char> ::= <sem-addchartoname> q
<name-char> ::= <sem-addchartoname> r
<name-char> ::= <sem-addchartoname> s
<name-char> ::= <sem-addchartoname> t
<name-char> ::= <sem-addchartoname> u
<name-char> ::= <sem-addchartoname> v
<name-char> ::= <sem-addchartoname> w
<name-char> ::= <sem-addchartoname> x
<name-char> ::= <sem-addchartoname> y
<name-char> ::= <sem-addchartoname> z
<name-char> ::= <sem-addchartoname> A
<name-char> ::= <sem-addchartoname> B
<name-char> ::= <sem-addchartoname> C
<name-char> ::= <sem-addchartoname> D
<name-char> ::= <sem-addchartoname> E
<name-char> ::= <sem-addchartoname> F
<name-char> ::= <sem-addchartoname> G
<name-char> ::= <sem-addchartoname> H
<name-char> ::= <sem-addchartoname> I
<name-char> ::= <sem-addchartoname> J
<name-char> ::= <sem-addchartoname> K
<name-char> ::= <sem-addchartoname> L
<name-char> ::= <sem-addchartoname> M
<name-char> ::= <sem-addchartoname> N
<name-char> ::= <sem-addchartoname> O
<name-char> ::= <sem-addchartoname> P
<name-char> ::= <sem-addchartoname> Q
<name-char> ::= <sem-addchartoname> R
<name-char> ::= <sem-addchartoname> S
<name-char> ::= <sem-addchartoname> T
<name-char> ::= <sem-addchartoname> U
<name-char> ::= <sem-addchartoname> V
<name-char> ::= <sem-addchartoname> W
<name-char> ::= <sem-addchartoname> X
<name-char> ::= <sem-addchartoname> Y
<name-char> ::= <sem-addchartoname> Z
<name-char> ::= <sem-addchartoname> 0
<name-char> ::= <sem-addchartoname> 1
<name-char> ::= <sem-addchartoname> 2
<name-char> ::= <sem-addchartoname> 3
<name-char> ::= <sem-addchartoname> 4
<name-char> ::= <sem-addchartoname> 5
<name-char> ::= <sem-addchartoname> 6
<name-char> ::= <sem-addchartoname> 7
<name-char> ::= <sem-addchartoname> 8
<name-char> ::= <sem-addchartoname> 9
<function-section> ::= <sem-setnamex>
<function-section> ::= $ <const-vol> q <calling-convention> <parameters>
<const-vol> ::=
<const-vol> ::= <sem-addmconst> x <const-vol>
<const-vol> ::= <sem-addmvol> w <const-vol>
<calling-convention> ::=
<calling-convention> ::= <sem-setfpascal> Q
<calling-convention> ::= q <calling-convention-q>
<calling-convention-q> ::= <sem-setffortran> f
<calling-convention-q> ::= <sem-setfstdcall> s
<calling-convention-q> ::= <sem-setffastcall> r
<calling-convention-q> ::= <sem-setfinterrupt> i
<parameters> ::= <sem-beginbsub> <type> <type-more>
<type-more> ::= <sem-par2f>
<type-more> ::= <sem-storepar> <sem-beginbsub> <type> <type-more>
<type-more-template> ::= <sem-storetemplate>
<type-more-template> ::= <sem-storetemparg> <sem-beginbsub> <type> <type-more-template>
<type> ::= <sem-settypev> v #void
<type> ::= <sem-settypec> c #char
<type> ::= <sem-settypes> s #short int
<type> ::= <sem-settypei> i #int
<type> ::= <sem-settypex> j #__int64
<type> ::= <sem-settypel> l #long int
<type> ::= <sem-settypew> b #wchar_t
<type> ::= <sem-settypef> f #float
<type> ::= <sem-settyped> d #double
<type> ::= <sem-settypee> g #long double
<type> ::= <sem-settypeb> o #bool
<type> ::= z <type-z>
<type> ::= u <type-u>
<type> ::= x <sem-setconst> <type>
<type> ::= p <sem-setptr> <type>
<type> ::= w <sem-setvolatile> <type>
<type> ::= r <sem-setref> <type>
<type> ::= t <typesub>
<type> ::= a <array-number> $ <type>
<array-number> ::= <sem-borlandarr> <number>
<type> ::= <source-name>
<type-z> ::= <sem-settypea> c #signed char
<type-u> ::= <sem-settypeh> c #unsigned char
<type-u> ::= <sem-settypet> s #unsigned short int
<type-u> ::= <sem-settypej> i #unsigned int
<type-u> ::= <sem-settypem> l #unsigned long int
<typesub> ::= <sem-loadborlandsub> 1
<typesub> ::= <sem-loadborlandsub> 2
<typesub> ::= <sem-loadborlandsub> 3
<typesub> ::= <sem-loadborlandsub> 4
<typesub> ::= <sem-loadborlandsub> 5
<typesub> ::= <sem-loadborlandsub> 6
<typesub> ::= <sem-loadborlandsub> 7
<typesub> ::= <sem-loadborlandsub> 8
<typesub> ::= <sem-loadborlandsub> 9
<typesub> ::= <sem-loadborlandsub> a
<typesub> ::= <sem-loadborlandsub> b
<typesub> ::= <sem-loadborlandsub> c
<typesub> ::= <sem-loadborlandsub> d
<typesub> ::= <sem-loadborlandsub> e
<typesub> ::= <sem-loadborlandsub> f
<typesub> ::= <sem-loadborlandsub> g
<typesub> ::= <sem-loadborlandsub> h
<typesub> ::= <sem-loadborlandsub> i
<typesub> ::= <sem-loadborlandsub> j
<typesub> ::= <sem-loadborlandsub> k
<typesub> ::= <sem-loadborlandsub> l
<typesub> ::= <sem-loadborlandsub> m
<typesub> ::= <sem-loadborlandsub> n
<typesub> ::= <sem-loadborlandsub> o
<typesub> ::= <sem-loadborlandsub> p
<typesub> ::= <sem-loadborlandsub> q
<typesub> ::= <sem-loadborlandsub> r
<typesub> ::= <sem-loadborlandsub> s
<typesub> ::= <sem-loadborlandsub> t
<typesub> ::= <sem-loadborlandsub> u
<typesub> ::= <sem-loadborlandsub> v
<typesub> ::= <sem-loadborlandsub> w
<typesub> ::= <sem-loadborlandsub> x
<typesub> ::= <sem-loadborlandsub> y
<typesub> ::= <sem-loadborlandsub> z
<source-name> ::= <source-number>
<source-number> ::= <sem-borlandid> <number> <qualified-name> <sem-unq2p> |
<number> ::= 0
<number> ::= 1
<number> ::= 2
<number> ::= 3
<number> ::= 4
<number> ::= 5
<number> ::= 6
<number> ::= 7
<number> ::= 8
<number> ::= 9
<op> ::= b <op-b>
<op-b> ::= a <op-ba>
<op-ba> ::= d <op-bad>
<op-bad> ::= <sem-setnameo> <sem-setoppl> d #operator +
<op-bad> ::= <sem-setnameo> <sem-setopad> r #operator & (unary)
<op-ba> ::= n <op-ban>
<op-ban> ::= <sem-setnameo> <sem-setopan> d #operator &
<op-ba> ::= r <op-bar>
<op-bar> ::= o <op-baro>
<op-baro> ::= <sem-setnameo> <sem-setoppt> w #operator ->
<op-bar> ::= w <op-barw>
<op-barw> ::= <sem-setnameo> <sem-setoppm> m #operator ->*
<op-ba> ::= s <op-bas>
<op-bas> ::= <sem-setnameo> <sem-setopass> g #operator =
<op-b> ::= c <op-bc>
<op-bc> ::= a <op-bca>
<op-bca> ::= l <op-bcal>
<op-bcal> ::= <sem-setnameo> <sem-setopcl> l #operator ()
<op-bc> ::= m <op-bcm>
<op-bcm> ::= <sem-setnameo> <sem-setopco> p #operator ~
<op-bc> ::= o <op-bco>
<op-bco> ::= m <op-bcom>
<op-bcom> ::= <sem-setnameo> <sem-setopcm> a #operator ,
<op-bc> ::= t <op-bct>
<op-bct> ::= <sem-setnamec> r #constructor
<op-b> ::= d <op-bd>
<op-bd> ::= e <op-bde>
<op-bde> ::= <sem-setnameo> <sem-setopmm> c #operator --
<op-bde> ::= l <op-bdel>
<op-bdel> ::= <sem-setnameo> <sem-setopdl> e #operator delete
<op-bd> ::= i <op-bdi>
<op-bdi> ::= <sem-setnameo> <sem-setopdv> v #operator /
<op-bd> ::= l <op-bdl>
<op-bdl> ::= <sem-setnameo> <sem-setopda> a #operator delete[]
<op-bd> ::= t <op-bdt>
<op-bdt> ::= <sem-setnamed> r #destructor
<op-b> ::= e <op-be>
<op-be> ::= q <op-beq>
<op-beq> ::= <sem-setnameo> <sem-setopeq> l #operator ==
<op-b> ::= g <op-bg>
<op-bg> ::= e <op-bge>
<op-bge> ::= <sem-setnameo> <sem-setopge> q #operator >=
<op-bg> ::= t <op-bgt>
<op-bgt> ::= <sem-setnameo> <sem-setopgt> r #operator >
<op-b> ::= i <op-bi>
<op-bi> ::= n <op-bin>
<op-bin> ::= <sem-setnameo> <sem-setoppp> c #operator ++
<op-bin> ::= <sem-setnameo> <sem-setopde> d #operator * (unary)
<op-b> ::= l <op-bl>
<op-bl> ::= a <op-bla>
<op-bla> ::= n <op-blan>
<op-blan> ::= <sem-setnameo> <sem-setopaa> d #operator &&
<op-bl> ::= e <op-ble>
<op-ble> ::= <sem-setnameo> <sem-setople> q #operator <=
<op-bl> ::= o <op-blo>
<op-blo> ::= <sem-setnameo> <sem-setopoo> r #operator ||
<op-bl> ::= s <op-bls>
<op-bls> ::= <sem-setnameo> <sem-setopls> h #operator <<
<op-bls> ::= <sem-setnameo> <sem-setoplt> s #operator <
<op-b> ::= m <op-bm>
<op-bm> ::= o <op-bmo>
<op-bmo> ::= <sem-setnameo> <sem-setoprm> d #operator %
<op-bm> ::= u <op-bmu>
<op-bmu> ::= <sem-setnameo> <sem-setopml> l #operator *
<op-b> ::= n <op-bn>
<op-bn> ::= e <op-bne>
<op-bne> ::= <sem-setnameo> <sem-setopne> q #operator !=
<op-bne> ::= <sem-setnameo> <sem-setopnw> w #operator new
<op-bn> ::= o <op-bno>
<op-bno> ::= <sem-setnameo> <sem-setopnt> t #operator !
<op-bn> ::= w <op-bnw>
<op-bnw> ::= <sem-setnameo> <sem-setopna> a #operator new[]
<op-b> ::= o <op-bo>
<op-bo> ::= <sem-setnameo> <sem-setopor> r #operator |
<op-b> ::= r <op-br>
<op-br> ::= a <op-bra>
<op-bra> ::= n <op-bran>
<op-bran> ::= <sem-setnameo> <sem-setopann> d #operator &=
<op-br> ::= d <op-brd>
<op-brd> ::= i <op-brdi>
<op-brdi> ::= <sem-setnameo> <sem-setopdvv> v #operator /=
<op-br> ::= l <op-brl>
<op-brl> ::= s <op-brls>
<op-brls> ::= <sem-setnameo> <sem-setoplss> h #operator <<=
<op-br> ::= m <op-brm>
<op-brm> ::= i <op-brmi>
<op-brmi> ::= <sem-setnameo> <sem-setopmii> n #operator -=
<op-brm> ::= o <op-brmo>
<op-brmo> ::= <sem-setnameo> <sem-setoprmm> d #operator %=
<op-brm> ::= u <op-brmu>
<op-brmu> ::= <sem-setnameo> <sem-setopmll> l #operator *=
<op-br> ::= o <op-bro>
<op-bro> ::= <sem-setnameo> <sem-setoporr> r #operator |=
<op-br> ::= p <op-brp>
<op-brp> ::= l <op-brpl>
<op-brpl> ::= <sem-setnameo> <sem-setoppll> u #operator +=
<op-br> ::= r <op-brr>
<op-brr> ::= s <op-brrs>
<op-brrs> ::= <sem-setnameo> <sem-setoprss> h #operator >>=
<op-br> ::= s <op-brs>
<op-brs> ::= <sem-setnameo> <sem-setoprs> h #operator >>
<op-br> ::= x <op-brx>
<op-brx> ::= o <op-brxo>
<op-brxo> ::= <sem-setnameo> <sem-setopeoo> r #operator ^=
<op-b> ::= s <op-bs>
<op-bs> ::= u <op-bsu>
<op-bsu> ::= b <op-bsub>
<op-bsub> ::= <sem-setnameo> <sem-setopmi> #operator -
<op-bsub> ::= <sem-setnameo> <sem-setopix> s #operator []
<op-b> ::= x <op-bx>
<op-bx> ::= o <op-bxo>
<op-bxo> ::= <sem-setnameo> <sem-setopeo> r #operator ^
<sem-addchartoname> ::=
<sem-storename> ::=
<sem-loadid> ::=
<sem-loadarr> ::=
<sem-ssno> ::=
<sem-ssnest> ::=
<sem-stunq> ::=
<sem-loadtsub> ::=
<sem-type2expr> ::=
<sem-exprval> ::=
<sem-beginexpr> ::=
<sem-storeexpr> ::=
<sem-copyexpr> ::=
<sem-storepar> ::=
<sem-begintempl> ::=
<sem-skiptempl> ::=
<sem-storetemparg> ::=
<sem-storetemplate> ::=
<sem-setnametf> ::=
<sem-par2f> ::=
<sem-unq2f> ::=
<sem-unq2p> ::=
<sem-setnamex> ::=
<sem-setnameo> ::=
<sem-par2spec> ::=
<sem-par2ret> ::=
<sem-settypev> ::=
<sem-settypew> ::=
<sem-settypeb> ::=
<sem-settypec> ::=
<sem-settypea> ::=
<sem-settypeh> ::=
<sem-settypes> ::=
<sem-settypet> ::=
<sem-settypei> ::=
<sem-settypej> ::=
<sem-settypel> ::=
<sem-settypem> ::=
<sem-settypex> ::=
<sem-settypey> ::=
<sem-settypen> ::=
<sem-settypeo> ::=
<sem-settypef> ::=
<sem-settyped> ::=
<sem-settypee> ::=
<sem-settypeg> ::=
<sem-settypez> ::=
<sem-setrestrict> ::=
<sem-setvolatile> ::=
<sem-setconst> ::=
<sem-setptr> ::=
<sem-setref> ::=
<sem-setrval> ::=
<sem-setcpair> ::=
<sem-setim> ::=
<sem-substd> ::=
<sem-subalc> ::=
<sem-substr> ::=
<sem-substrs> ::=
<sem-subistr> ::=
<sem-subostr> ::=
<sem-subiostr> ::=
<sem-loadsub> ::=
<sem-setnamec> ::=
<sem-setnamed> ::=
<sem-setopnw> ::=
<sem-setopna> ::=
<sem-setopdl> ::=
<sem-setopda> ::=
<sem-setopps> ::=
<sem-setopng> ::=
<sem-setopad> ::=
<sem-setopde> ::=
<sem-setopco> ::=
<sem-setoppl> ::=
<sem-setopmi> ::=
<sem-setopml> ::=
<sem-setopdv> ::=
<sem-setoprm> ::=
<sem-setopan> ::=
<sem-setopor> ::=
<sem-setopeo> ::=
<sem-setopass> ::=
<sem-setoppll> ::=
<sem-setopmii> ::=
<sem-setopmll> ::=
<sem-setopdvv> ::=
<sem-setoprmm> ::=
<sem-setopann> ::=
<sem-setoporr> ::=
<sem-setopeoo> ::=
<sem-setopls> ::=
<sem-setoprs> ::=
<sem-setoplss> ::=
<sem-setoprss> ::=
<sem-setopeq> ::=
<sem-setopne> ::=
<sem-setoplt> ::=
<sem-setopgt> ::=
<sem-setople> ::=
<sem-setopnt> ::=
<sem-setopaa> ::=
<sem-setopoo> ::=
<sem-setoppp> ::=
<sem-setopmm> ::=
<sem-setopcm> ::=
<sem-setoppm> ::=
<sem-setoppt> ::=
<sem-setopcl> ::=
<sem-setopix> ::=
<sem-setopge> ::=
<sem-setopqu> ::=
<sem-setopst> ::=
<sem-setopsz> ::=
<sem-setopat> ::=
<sem-setopaz> ::=
<sem-setopcv> ::=
<sem-setopxx> ::=
<sem-setnamef> ::=
<sem-setnamevt> ::=
<sem-reversename> ::=
<sem-setprivate> ::=
<sem-setpublic> ::=
<sem-setprotected> ::=
<sem-setfcdecl> ::=
<sem-setfpascal> ::=
<sem-setffortran> ::=
<sem-setfthiscall> ::=
<sem-setfstdcall> ::=
<sem-setffastcall> ::=
<sem-setfinterrupt> ::=
<sem-setunion> ::=
<sem-setstruct> ::=
<sem-setclass> ::=
<sem-setenum> ::=
<sem-setstatic> ::=
<sem-setvirtual> ::=
<sem-stclconst> ::=
<sem-stclvol> ::=
<sem-stclfar> ::=
<sem-stclhuge> ::=
<sem-savenamesub> ::=
<sem-loadnamesub> ::=
<sem-mstemplsub> ::=
<sem-setnamer0> ::=
<sem-setnamer1> ::=
<sem-setnamer2> ::=
<sem-setnamer3> ::=
<sem-setnamer4> ::=
<sem-setname_a> ::=
<sem-setname_b> ::=
<sem-setname_c> ::=
<sem-setname_d> ::=
<sem-setname_e> ::=
<sem-setname_f> ::=
<sem-setname_g> ::=
<sem-setname_h> ::=
<sem-setname_i> ::=
<sem-setname_j> ::=
<sem-setname_k> ::=
<sem-setname_l> ::=
<sem-setname_m> ::=
<sem-setname_n> ::=
<sem-setname_o> ::=
<sem-setname_p> ::=
<sem-setname_q> ::=
<sem-setname_r> ::=
<sem-setname_s> ::=
<sem-setname_t> ::=
<sem-setname_u> ::=
<sem-setname_v> ::=
<sem-setname_w> ::=
<sem-setname_x> ::=
<sem-setname_y> ::=
<sem-setname_z> ::=
<sem-templ2tftpl> ::=
<sem-beginbsub> ::= #begin built-in substitution
<sem-loadbsub> ::=
<sem-addmconst> ::=
<sem-addmvol> ::=
<sem-addmfar> ::=
<sem-addmhuge> ::=
<sem-loadmsnum> ::=
<sem-numtorttibcd> ::=
<sem-numtotype> ::=
<sem-normalizeparname> ::=
<sem-borlandid> ::=
<sem-loadborlandsub> ::=
<sem-borlandarr> ::=
<sem-end> ::=

View File

@ -0,0 +1,423 @@
<mangled-name> ::= _ <mangled-name2>
<mangled-name2> ::= <mangled-name3> <sem-end>
<mangled-name2> ::= _ <mangled-name3> <sem-end>
<mangled-name3> ::= Z <encoding>
<encoding> ::= <name> <bare-function-type>
<name> ::= <nested-name>
<name> ::= <source-name> <template-args>
<name> ::= S <name-sub> #deciding rule
<name-sub> ::= <sem-ssno> <substitution2-t> <unqualified-name>
<name-sub> ::= <sem-ssno> <substitution2> <template-args>
<template-args> ::= <sem-skiptempl>
<template-args> ::= <sem-begintempl> I <template-arg> <template-arg-more> E
<template-args-nostore> ::= <sem-skiptempl>
<template-args-nostore> ::= <sem-begintempl> I <template-arg-nostore> <template-arg-nostore-more> E
<template-arg-more> ::= <sem-storetemparg> <template-arg> <template-arg-more>
<template-arg-more> ::= <sem-storetemplate>
<template-arg> ::= <type>
<template-arg> ::= <expr-primary>
<template-arg-nostore> ::= <type>
<template-arg-nostore> ::= <expr-primary>
<template-arg-nostore-more> ::= <sem-storetemparg> <template-arg> <template-arg-more>
<template-arg-nostore-more> ::= <sem-templ2tftpl>
<template-param> ::= T <template-param2>
<template-param2> ::= <sem-loadtsub> _
<template-param2> ::= <sem-loadtsub> <number> _ #<unscoped-name> ::= <unqualified-name> #<unscoped-name> ::= St <unqualified-name> # unused, but may be useful in the future
<nested-name> ::= <sem-ssnest> N <CV-qualifiers> <unqualified-name> <unqualified-name-more> E
<prefix> ::=
<prefix> ::= <substitution>
<prefix> ::= <template-param>
<unqualified-function-name> ::= <unqualified-name>
<unqualified-function-name> ::= <ctor-dtor-name> <template-args-nostore>
<unqualified-function-name> ::= <operator-name-setname>
<operator-name-setname> ::= <sem-setnameo> <operator-name> <template-args>
<operator-name-setname> ::= <sem-setnameo> <operator-name-cv>
<unqualified-name> ::= <source-name> <template-args>
<unqualified-name> ::= <substitution> <template-args>
<unqualified-name-more> ::= <unqualified-function-name> <unqualified-name-more>
<unqualified-name-more> ::=
<source-name> ::= <source-number>
<source-number> ::= <sem-loadid> <number>
<number> ::= 0
<number> ::= 1
<number> ::= 2
<number> ::= 3
<number> ::= 4
<number> ::= 5
<number> ::= 6
<number> ::= 7
<number> ::= 8
<number> ::= 9
<ctor-dtor-name> ::= <sem-setnamec> C <ctor>
<ctor> ::= 1 # complete object constructor
<ctor> ::= 2 # base object constructor
<ctor> ::= 3 # complete object allocating constructor
<ctor-dtor-name> ::= <sem-setnamed> D <dtor>
<dtor> ::= 0 # deleting destructor
<dtor> ::= 1 # complete object destructor
<dtor> ::= 2 # base object destructor
<CV-qualifiers> ::= <r> <V> <K>
<r> ::= <sem-setrestrict> r # restrict
<r> ::=
<V> ::= <sem-setvolatile> V # volatile
<V> ::=
<K> ::= <sem-setconst> K # const
<K> ::=
<bare-function-type> ::= <sem-unq2f> <bare-function-type2>
<bare-function-type2> ::= <type> <another-type> <sem-par2f>
<bare-function-type2> ::= <sem-setnamex>
<another-type> ::= <sem-storepar> <type> <another-type>
<another-type> ::=
<type> ::= <type-qualifier> <type2>
<type-qualifier> ::=
<type-qualifier> ::= <sem-setrestrict> r <type-qualifier>
<type-qualifier> ::= <sem-setvolatile> V <type-qualifier>
<type-qualifier> ::= <sem-setconst> K <type-qualifier>
<type-qualifier> ::= <sem-setptr> P <type-qualifier>
<type-qualifier> ::= <sem-setref> R <type-qualifier>
<type-qualifier> ::= <sem-setrval> O <type-qualifier>
<type-qualifier> ::= <sem-setcpair> C <type-qualifier>
<type-qualifier> ::= <sem-setim> G <type-qualifier>
<type2> ::= <template-param>
<type2> ::= <builtin-type>
<type2> ::= <class-enum-type>
<type2> ::= <array-type>
<array-type> ::= A <array-number> _ <type>
<array-number> ::= <sem-loadarr> <number>
<class-enum-type> ::= <name>
<builtin-type> ::= <sem-settypev> v # void
<builtin-type> ::= <sem-settypew> w # wchar_t
<builtin-type> ::= <sem-settypeb> b # bool
<builtin-type> ::= <sem-settypec> c # char
<builtin-type> ::= <sem-settypea> a # signed char
<builtin-type> ::= <sem-settypeh> h # unsigned char
<builtin-type> ::= <sem-settypes> s # short
<builtin-type> ::= <sem-settypet> t # unsigned short
<builtin-type> ::= <sem-settypei> i # int
<builtin-type> ::= <sem-settypej> j # unsigned int
<builtin-type> ::= <sem-settypel> l # long
<builtin-type> ::= <sem-settypem> m # unsigned long
<builtin-type> ::= <sem-settypex> x # long long, __int64
<builtin-type> ::= <sem-settypey> y # unsigned long long, __int64
<builtin-type> ::= <sem-settypen> n # __int128
<builtin-type> ::= <sem-settypeo> o # unsigned __int128
<builtin-type> ::= <sem-settypef> f # float
<builtin-type> ::= <sem-settyped> d # double
<builtin-type> ::= <sem-settypee> e # long double, __float80
<builtin-type> ::= <sem-settypeg> g # __float128
<builtin-type> ::= <sem-settypez> z # ellipsis
<builtin-type> ::= D <builtin-type2>
<builtin-type2> ::= d # IEEE 754r decimal floating point (64 bits)
<builtin-type2> ::= e # IEEE 754r decimal floating point (128 bits)
<builtin-type2> ::= f # IEEE 754r decimal floating point (32 bits)
<builtin-type2> ::= h # IEEE 754r half-precision floating point (16 bits)
<builtin-type2> ::= i # char32_t
<builtin-type2> ::= s # char16_t
<builtin-type2> ::= a # auto (in dependent new-expressions)
<builtin-type2> ::= n # std::nullptr_t (i.e., decltype(nullptr))
<substitution> ::= S <subst>
<subst> ::= <substitution2-t> # separating St
<subst> ::= <substitution2>
<substitution2> ::= <seq-id> _
<substitution2> ::= <sem-loadsub> _
<seq-id> ::= <sem-loadsub> 0
<seq-id> ::= <sem-loadsub> 1
<seq-id> ::= <sem-loadsub> 2
<seq-id> ::= <sem-loadsub> 3
<seq-id> ::= <sem-loadsub> 4
<seq-id> ::= <sem-loadsub> 5
<seq-id> ::= <sem-loadsub> 6
<seq-id> ::= <sem-loadsub> 7
<seq-id> ::= <sem-loadsub> 8
<seq-id> ::= <sem-loadsub> 9
<seq-id> ::= <sem-loadsub> A
<seq-id> ::= <sem-loadsub> B
<seq-id> ::= <sem-loadsub> C
<seq-id> ::= <sem-loadsub> D
<seq-id> ::= <sem-loadsub> E
<seq-id> ::= <sem-loadsub> F
<seq-id> ::= <sem-loadsub> G
<seq-id> ::= <sem-loadsub> H
<seq-id> ::= <sem-loadsub> I
<seq-id> ::= <sem-loadsub> J
<seq-id> ::= <sem-loadsub> K
<seq-id> ::= <sem-loadsub> L
<seq-id> ::= <sem-loadsub> M
<seq-id> ::= <sem-loadsub> N
<seq-id> ::= <sem-loadsub> O
<seq-id> ::= <sem-loadsub> P
<seq-id> ::= <sem-loadsub> Q
<seq-id> ::= <sem-loadsub> R
<seq-id> ::= <sem-loadsub> S
<seq-id> ::= <sem-loadsub> T
<seq-id> ::= <sem-loadsub> U
<seq-id> ::= <sem-loadsub> V
<seq-id> ::= <sem-loadsub> W
<seq-id> ::= <sem-loadsub> X
<seq-id> ::= <sem-loadsub> Y
<seq-id> ::= <sem-loadsub> Z
<substitution2-t> ::= <sem-substd> t # ::std::
<substitution2> ::= <sem-subalc> a # ::std::allocator
<substitution2> ::= <sem-substr> b # ::std::basic_string
<substitution2> ::= <sem-substrs> s # ::std::basic_string<char,::std::char_traits<char>,::std::allocator<char>>
<substitution2> ::= <sem-subistr> i # ::std::basic_istream<char, std::char_traits<char> >
<substitution2> ::= <sem-subostr> o # ::std::basic_ostream<char, std::char_traits<char> >
<substitution2> ::= <sem-subiostr> d # ::std::basic_iostream<char, std::char_traits<char> >
<operator-name> ::= a <operator-name-a>
<operator-name> ::= c <operator-name-c>
<operator-name> ::= d <operator-name-d>
<operator-name> ::= e <operator-name-e>
<operator-name> ::= g <operator-name-g>
<operator-name> ::= i <operator-name-i>
<operator-name> ::= l <operator-name-l>
<operator-name> ::= m <operator-name-m>
<operator-name> ::= n <operator-name-n>
<operator-name> ::= o <operator-name-o>
<operator-name> ::= p <operator-name-p>
<operator-name> ::= q <operator-name-q>
<operator-name> ::= r <operator-name-r>
<operator-name> ::= s <operator-name-s>
<operator-name-a> ::= <sem-setopaa> a # &&
<operator-name-a> ::= <sem-setopad> d # & (unary)
<operator-name-a> ::= <sem-setopan> n # &
<operator-name-a> ::= <sem-setopat> t # alignof (a type)
<operator-name-a> ::= <sem-setopaz> z # alignof (an expression)
<operator-name-a> ::= <sem-setopass> S # =
<operator-name-a> ::= <sem-setopann> N # &=
<operator-name-c> ::= <sem-setopcm> m # ,
<operator-name-c> ::= <sem-setopcl> l # ()
<operator-name-c> ::= <sem-setopco> o # ~
<operator-name-cv> ::= <sem-setopcv> v <type> <sem-par2spec> # (cast)
<operator-name-d> ::= <sem-setopdl> l # delete
<operator-name-d> ::= <sem-setopda> a # delete[]
<operator-name-d> ::= <sem-setopde> e # * (unary)
<operator-name-d> ::= <sem-setopdv> v # /
<operator-name-d> ::= <sem-setopdvv> V # /=
<operator-name-e> ::= <sem-setopeo> o # ^
<operator-name-e> ::= <sem-setopeoo> O # ^=
<operator-name-e> ::= <sem-setopeq> q # ==
<operator-name-g> ::= <sem-setopgt> t # >
<operator-name-g> ::= <sem-setopge> e # >=
<operator-name-i> ::= <sem-setopix> x # []
<operator-name-l> ::= <sem-setopls> s # <<
<operator-name-l> ::= <sem-setoplss> S # <<=
<operator-name-l> ::= <sem-setoplt> t # <
<operator-name-l> ::= <sem-setople> e # <=
<operator-name-m> ::= <sem-setopmi> i # -
<operator-name-m> ::= <sem-setopml> l # *
<operator-name-m> ::= <sem-setopmii> I # -=
<operator-name-m> ::= <sem-setopmll> L # *=
<operator-name-m> ::= <sem-setopmm> m # -- (postfix in <expression> context)
<operator-name-n> ::= <sem-setopnw> w # new
<operator-name-n> ::= <sem-setopna> a # new[]
<operator-name-n> ::= <sem-setopng> g # - (unary)
<operator-name-n> ::= <sem-setopne> e # !=
<operator-name-n> ::= <sem-setopnt> t # !
<operator-name-o> ::= <sem-setopor> r # |
<operator-name-o> ::= <sem-setoporr> R # |=
<operator-name-o> ::= <sem-setopoo> o # ||
<operator-name-p> ::= <sem-setopps> s # + (unary)
<operator-name-p> ::= <sem-setoppl> l # +
<operator-name-p> ::= <sem-setoppll> L # +=
<operator-name-p> ::= <sem-setoppp> p # ++ (postfix in <expression> context)
<operator-name-p> ::= <sem-setoppm> m # ->*
<operator-name-p> ::= <sem-setoppt> t # ->
<operator-name-q> ::= <sem-setopqu> u # ?
<operator-name-r> ::= <sem-setoprm> m # %
<operator-name-r> ::= <sem-setoprmm> M # %=
<operator-name-r> ::= <sem-setoprs> s # >>
<operator-name-r> ::= <sem-setoprss> S # >>=
<operator-name-s> ::= <sem-setopst> t # sizeof (a type)
<operator-name-s> ::= <sem-setopsz> z # sizeof (an expression)
<expr-primary> ::= <sem-beginexpr> L <builtin-type> <sem-type2expr> <expr-number> E <sem-storeexpr>
<expr-number> ::= <sem-exprval> <number>
<sem-addchartoname> ::=
<sem-storename> ::=
<sem-loadid> ::=
<sem-loadarr> ::=
<sem-ssno> ::=
<sem-ssnest> ::=
<sem-stunq> ::=
<sem-loadtsub> ::=
<sem-type2expr> ::=
<sem-exprval> ::=
<sem-beginexpr> ::=
<sem-storeexpr> ::=
<sem-copyexpr> ::=
<sem-storepar> ::=
<sem-begintempl> ::=
<sem-skiptempl> ::=
<sem-storetemparg> ::=
<sem-storetemplate> ::=
<sem-setnametf> ::=
<sem-par2f> ::=
<sem-unq2f> ::=
<sem-unq2p> ::=
<sem-setnamex> ::=
<sem-setnameo> ::=
<sem-par2spec> ::=
<sem-par2ret> ::=
<sem-settypev> ::=
<sem-settypew> ::=
<sem-settypeb> ::=
<sem-settypec> ::=
<sem-settypea> ::=
<sem-settypeh> ::=
<sem-settypes> ::=
<sem-settypet> ::=
<sem-settypei> ::=
<sem-settypej> ::=
<sem-settypel> ::=
<sem-settypem> ::=
<sem-settypex> ::=
<sem-settypey> ::=
<sem-settypen> ::=
<sem-settypeo> ::=
<sem-settypef> ::=
<sem-settyped> ::=
<sem-settypee> ::=
<sem-settypeg> ::=
<sem-settypez> ::=
<sem-setrestrict> ::=
<sem-setvolatile> ::=
<sem-setconst> ::=
<sem-setptr> ::=
<sem-setref> ::=
<sem-setrval> ::=
<sem-setcpair> ::=
<sem-setim> ::=
<sem-substd> ::=
<sem-subalc> ::=
<sem-substr> ::=
<sem-substrs> ::=
<sem-subistr> ::=
<sem-subostr> ::=
<sem-subiostr> ::=
<sem-loadsub> ::=
<sem-setnamec> ::=
<sem-setnamed> ::=
<sem-setopnw> ::=
<sem-setopna> ::=
<sem-setopdl> ::=
<sem-setopda> ::=
<sem-setopps> ::=
<sem-setopng> ::=
<sem-setopad> ::=
<sem-setopde> ::=
<sem-setopco> ::=
<sem-setoppl> ::=
<sem-setopmi> ::=
<sem-setopml> ::=
<sem-setopdv> ::=
<sem-setoprm> ::=
<sem-setopan> ::=
<sem-setopor> ::=
<sem-setopeo> ::=
<sem-setopass> ::=
<sem-setoppll> ::=
<sem-setopmii> ::=
<sem-setopmll> ::=
<sem-setopdvv> ::=
<sem-setoprmm> ::=
<sem-setopann> ::=
<sem-setoporr> ::=
<sem-setopeoo> ::=
<sem-setopls> ::=
<sem-setoprs> ::=
<sem-setoplss> ::=
<sem-setoprss> ::=
<sem-setopeq> ::=
<sem-setopne> ::=
<sem-setoplt> ::=
<sem-setopgt> ::=
<sem-setople> ::=
<sem-setopnt> ::=
<sem-setopaa> ::=
<sem-setopoo> ::=
<sem-setoppp> ::=
<sem-setopmm> ::=
<sem-setopcm> ::=
<sem-setoppm> ::=
<sem-setoppt> ::=
<sem-setopcl> ::=
<sem-setopix> ::=
<sem-setopge> ::=
<sem-setopqu> ::=
<sem-setopst> ::=
<sem-setopsz> ::=
<sem-setopat> ::=
<sem-setopaz> ::=
<sem-setopcv> ::=
<sem-setopxx> ::=
<sem-setnamef> ::=
<sem-setnamevt> ::=
<sem-reversename> ::=
<sem-setprivate> ::=
<sem-setpublic> ::=
<sem-setprotected> ::=
<sem-setfcdecl> ::=
<sem-setfpascal> ::=
<sem-setffortran> ::=
<sem-setfthiscall> ::=
<sem-setfstdcall> ::=
<sem-setffastcall> ::=
<sem-setfinterrupt> ::=
<sem-setunion> ::=
<sem-setstruct> ::=
<sem-setclass> ::=
<sem-setenum> ::=
<sem-setstatic> ::=
<sem-setvirtual> ::=
<sem-stclconst> ::=
<sem-stclvol> ::=
<sem-stclfar> ::=
<sem-stclhuge> ::=
<sem-savenamesub> ::=
<sem-loadnamesub> ::=
<sem-mstemplsub> ::=
<sem-setnamer0> ::=
<sem-setnamer1> ::=
<sem-setnamer2> ::=
<sem-setnamer3> ::=
<sem-setnamer4> ::=
<sem-setname_a> ::=
<sem-setname_b> ::=
<sem-setname_c> ::=
<sem-setname_d> ::=
<sem-setname_e> ::=
<sem-setname_f> ::=
<sem-setname_g> ::=
<sem-setname_h> ::=
<sem-setname_i> ::=
<sem-setname_j> ::=
<sem-setname_k> ::=
<sem-setname_l> ::=
<sem-setname_m> ::=
<sem-setname_n> ::=
<sem-setname_o> ::=
<sem-setname_p> ::=
<sem-setname_q> ::=
<sem-setname_r> ::=
<sem-setname_s> ::=
<sem-setname_t> ::=
<sem-setname_u> ::=
<sem-setname_v> ::=
<sem-setname_w> ::=
<sem-setname_x> ::=
<sem-setname_y> ::=
<sem-setname_z> ::=
<sem-templ2tftpl> ::=
<sem-beginbsub> ::= #begin built-in substitution
<sem-loadbsub> ::=
<sem-addmconst> ::=
<sem-addmvol> ::=
<sem-addmfar> ::=
<sem-addmhuge> ::=
<sem-loadmsnum> ::=
<sem-numtorttibcd> ::=
<sem-numtotype> ::=
<sem-normalizeparname> ::=
<sem-borlandid> ::=
<sem-loadborlandsub> ::=
<sem-borlandarr> ::=
<sem-end> ::=

View File

@ -0,0 +1,534 @@
<mangled-name> ::= ? <mangled-name-2>
<mangled-name-2> ::= ? <mangled-name-qs>
<mangled-name-qs> ::= _ <mangled-name-qssub>
<mangled-name-qs> ::= 0 <name-cover> <sem-setnamec> <sem-unq2f> <modif> <const-vol> <calling-conv> @ <parameters> <terma> Z <sem-end>
<mangled-name-qs> ::= 1 <name-cover> <sem-setnamed> <sem-unq2f> <modif> <const-vol> <calling-conv> @ <parameters> <terma>Z <sem-end>
<mangled-name-qs> ::= <sem-setnameo> <optype> <name-cover> <sem-unq2f> <mangled-name-data>
<optype> ::= <sem-setopdl> 3
<optype> ::= <sem-setopix> A
<optype> ::= <sem-setopcl> R
<optype> ::= <sem-setoppt> C
<optype> ::= <sem-setoppp> E
<optype> ::= <sem-setopmm> F
<optype> ::= <sem-setopnw> 2
<optype> ::= <sem-setopde> D
<optype> ::= <sem-setopad> I
<optype> ::= <sem-setopps> H
<optype> ::= <sem-setopng> G
<optype> ::= <sem-setopnt> 7
<optype> ::= <sem-setopco> S
<optype> ::= <sem-setoppm> J
<optype> ::= <sem-setopdv> K
<optype> ::= <sem-setoprm> L
<optype> ::= <sem-setopls> 6
<optype> ::= <sem-setoprs> 5
<optype> ::= <sem-setoplt> M
<optype> ::= <sem-setopgt> O
<optype> ::= <sem-setople> N
<optype> ::= <sem-setopge> P
<optype> ::= <sem-setopeq> 8
<optype> ::= <sem-setopne> 9
<optype> ::= <sem-setopor> U
<optype> ::= <sem-setopeo> T
<optype> ::= <sem-setopaa> V
<optype> ::= <sem-setopoo> W
<optype> ::= <sem-setopass> 4
<optype> ::= <sem-setopmll> X
<optype> ::= <sem-setoppll> Y
<optype> ::= <sem-setopmii> Z
<optype> ::= <sem-setopcm> Q
<optype> ::= <sem-setopcv> B
<optype_> ::= <sem-setopna> U
<optype_> ::= <sem-setopda> V
<optype_> ::= <sem-setopdvv> 0
<optype_> ::= <sem-setoprmm> 1
<optype_> ::= <sem-setoprss> 2
<optype_> ::= <sem-setoplss> 3
<optype_> ::= <sem-setopann> 4
<optype_> ::= <sem-setoporr> 5
<optype_> ::= <sem-setopeoo> 6
<terma> ::=
<terma> ::= @
<mangled-name-qssub> ::= 7 <name-cover> <sem-setnamevt> <sem-unq2f> <mangled-name-data>
<mangled-name-qssub> ::= R <mangled-name-qssub-r>
<mangled-name-qssub-r> ::= <r-num> <name-cover> <sem-unq2f> 8 <sem-end>
<mangled-name-qssub-r> ::= <sem-setnamer4> 4 <name-cover> <sem-unq2f> <mangled-name-data>
<mangled-name-qssub-r> ::= <sem-setnamer1> 1 <sem-loadmsnum> <msnum> <sem-numtorttibcd> <sem-loadmsnum> <msnum> <sem-numtorttibcd> <sem-loadmsnum> <msnum> <sem-numtorttibcd> <sem-loadmsnum> <msnum> <sem-numtorttibcd> <name-cover> <sem-unq2f> <terma> 8 <sem-end>
<mangled-name-qssub-r> ::= <sem-setnamer0> 0 <storage-return> <type> <sem-par2ret> @ 8 <sem-end>
<r-num> ::= <sem-setnamer2> 2
<r-num> ::= <sem-setnamer3> 3
<mangled-name-qssub> ::= <sem-setnameo> <optype_> <name-cover> <sem-unq2f> <mangled-name-data>
<mangled-name-qssub> ::= <qssub> <name-cover> <sem-unq2f> <mangled-name-data>
<qssub> ::= <sem-setname_a> A
<qssub> ::= <sem-setname_b> B
<qssub> ::= <sem-setname_d> D
<qssub> ::= <sem-setname_e> E
<qssub> ::= <sem-setname_f> F
<qssub> ::= <sem-setname_g> G
<qssub> ::= <sem-setname_h> H
<qssub> ::= <sem-setname_i> I
<qssub> ::= <sem-setname_j> J
<qssub> ::= <sem-setname_k> K
<qssub> ::= <sem-setname_l> L
<qssub> ::= <sem-setname_m> M
<qssub> ::= <sem-setname_n> N
<qssub> ::= <sem-setname_o> O
<qssub> ::= <sem-setname_q> Q
<qssub> ::= <sem-setname_s> S
<qssub> ::= <sem-setname_t> T
<qssub> ::= <sem-setname_x> X
<qssub> ::= <sem-setname_y> Y
<storage-return> ::=
<storage-return> ::= ? <storage-return-2>
<storage-return-2> ::= A
<storage-return-2> ::= <sem-setconst> B
<storage-return-2> ::= <sem-setvol> C
<storage-return-2> ::= <sem-setconst> <sem-setvol> D
<mangled-name-qs> ::= <name-cover-qs> <sem-unq2f> <mangled-name-data>
<mangled-name-2> ::= <name-cover-noqs> <sem-unq2f> <mangled-name-data>
<mangled-name-data> ::= <data-object> <sem-end>
<mangled-name-data> ::= <modif> <const-vol> <calling-conv> <storage-return> <type> <sem-par2ret> <parameters> <terma> Z <sem-end>
<mangled-name-data> ::= <near-far> <calling-conv> <storage-return> <type> <sem-par2ret> <parameters> <terma> Z <sem-end>
<data-object> ::= 6 <storage-class> <terma>
<data-object> ::= 3 <sem-setnamex> <type> <sem-par2ret> <storage-class> #global object
<data-object> ::= 2 <sem-setnamex> <sem-setpublic> <sem-setstatic> <type> <sem-par2ret> <storage-class> #static class member object
<data-object> ::= 1 <sem-setnamex> <sem-setprotected> <sem-setstatic> <type> <sem-par2ret> <storage-class> #static class member object
<data-object> ::= 0 <sem-setnamex> <sem-setprivate> <sem-setstatic> <type> <sem-par2ret> <storage-class> #static class member object
<msnum> ::= ?
<msnum> ::= 0
<msnum> ::= 1
<msnum> ::= 2
<msnum> ::= 3
<msnum> ::= 4
<msnum> ::= 5
<msnum> ::= 6
<msnum> ::= 7
<msnum> ::= 8
<msnum> ::= 9
<msnum> ::= A
<msnum> ::= B
<msnum> ::= C
<msnum> ::= D
<msnum> ::= E
<msnum> ::= F
<msnum> ::= G
<msnum> ::= H
<msnum> ::= I
<msnum> ::= J
<msnum> ::= K
<msnum> ::= L
<msnum> ::= M
<msnum> ::= N
<msnum> ::= O
<msnum> ::= P
<storage-class> ::= Q1@ #member pointers, member function pointers
<storage-class> ::= A #near
<storage-class> ::= <sem-stclconst> B #const
<storage-class> ::= <sem-stclvol> C #volatile
<storage-class> ::= <sem-stclconst> <sem-stclvol> D #const volatile
<storage-class> ::= <sem-stclfar> E #far
<storage-class> ::= <sem-stclconst> <sem-stclfar> F #const far
<storage-class> ::= <sem-stclvol> <sem-stclfar> G #volatile far
<storage-class> ::= <sem-stclconst> <sem-stclvol> <sem-stclfar> H #const volatile far
<storage-class> ::= <sem-stclhuge> I #huge
<modif> ::= <sem-setprivate> A #private default
<modif> ::= <sem-setprivate> B #private far
<modif> ::= <sem-setprivate> <sem-setstatic> C #private static
<modif> ::= <sem-setprivate> <sem-setstatic> D #private static far
<modif> ::= <sem-setprivate> <sem-setvirtual> E #private virtual
<modif> ::= <sem-setprivate> <sem-setvirtual> F #private virtual far
<modif> ::= <sem-setprotected> I #protected default
<modif> ::= <sem-setprotected> J #protected far
<modif> ::= <sem-setprotected> <sem-setstatic> K #protected static
<modif> ::= <sem-setprotected> <sem-setstatic> L #protected static far
<modif> ::= <sem-setprotected> <sem-setvirtual> M #protected virtual
<modif> ::= <sem-setprotected> <sem-setvirtual> N #protected virtual far
<modif> ::= <sem-setpublic> Q #public default
<modif> ::= <sem-setpublic> R #public far
<modif> ::= <sem-setpublic> <sem-setstatic> S #public static
<modif> ::= <sem-setpublic> <sem-setstatic> T #public static far
<modif> ::= <sem-setpublic> <sem-setvirtual> U #public virtual
<modif> ::= <sem-setpublic> <sem-setvirtual> V #public virtual far
<const-vol-e> ::=
<const-vol-e> ::= <const-vol>
<const-vol> ::= A #default
<const-vol> ::= <sem-addmconst> B #const
<const-vol> ::= <sem-addmvol> C #volatile
<const-vol> ::= <sem-addmconst> <sem-addmvol> D #const volatile
<calling-conv> ::= <sem-setfcdecl> A #__cdecl
<calling-conv> ::= <sem-setfpascal> C #__pascal
<calling-conv> ::= <sem-setfthiscall> E #__thiscall
<calling-conv> ::= <sem-setfstdcall> G #__stdcall
<calling-conv> ::= <sem-setffastcall> I #__fastcall
<name-cover-qs> ::= <name-qs> @ <namespace-x> @
<name-cover-noqs> ::= <name-element-noqs> <namespace-x> @
<name-cover> ::= <name-element> <namespace-x> @
<name-cover> ::= @
<name-element> ::= <name> @
<name-element> ::= <name-sub>
<name-element-noqs> ::= <name-noqs> @
<name-element-noqs> ::= <name-sub>
<name-sub> ::= <sem-loadnamesub> 0
<name-sub> ::= <sem-loadnamesub> 1
<name-sub> ::= <sem-loadnamesub> 2
<name-sub> ::= <sem-loadnamesub> 3
<name-sub> ::= <sem-loadnamesub> 4
<name-sub> ::= <sem-loadnamesub> 5
<name-sub> ::= <sem-loadnamesub> 6
<name-sub> ::= <sem-loadnamesub> 7
<name-sub> ::= <sem-loadnamesub> 8
<name-sub> ::= <sem-loadnamesub> 9
<name-elemental> ::= <name-char-first> <name-char-more>
<template-header> ::= <name-elemental> @
<template-header> ::= ? <template-op>
<template-op> ::= <sem-setnameo> <optype>
<template-op> ::= <sem-setnameo> _ <optype_>
<name-qs> ::= $ <template-header> <sem-begintempl> <sem-mstemplsub> <template-arg> <template-arg-more> <sem-savenamesub>
<name-noqs> ::= <name-elemental> <sem-savenamesub>
<name> ::= ? <name-qs>
<name> ::= <name-noqs>
<name-char-more> ::= <name-char> <name-char-more>
<name-char-more> ::= <name-char-first> <name-char-more>
<name-char-more> ::= <sem-storename>
<namespace-x> ::= <sem-reversename>
<namespace-x> ::= <name-element> <namespace-x>
<template-arg-more> ::= <sem-storetemplate>
<template-arg-more> ::= <sem-storetemparg> <template-arg> <template-arg-more>
<template-arg> ::= <type>
<parameters> ::= <parameter> <parameter-more>
<parameter> ::= <type>
<parameter-more> ::= <sem-storepar> <parameter> <parameter-more>
<parameter-more> ::= <sem-par2f>
<near-far> ::= Y #near
<near-far> ::= Z #far
<name-char-first> ::= <sem-addchartoname> _
<name-char-first> ::= <sem-addchartoname> a
<name-char-first> ::= <sem-addchartoname> b
<name-char-first> ::= <sem-addchartoname> c
<name-char-first> ::= <sem-addchartoname> d
<name-char-first> ::= <sem-addchartoname> e
<name-char-first> ::= <sem-addchartoname> f
<name-char-first> ::= <sem-addchartoname> g
<name-char-first> ::= <sem-addchartoname> h
<name-char-first> ::= <sem-addchartoname> i
<name-char-first> ::= <sem-addchartoname> j
<name-char-first> ::= <sem-addchartoname> k
<name-char-first> ::= <sem-addchartoname> l
<name-char-first> ::= <sem-addchartoname> m
<name-char-first> ::= <sem-addchartoname> n
<name-char-first> ::= <sem-addchartoname> o
<name-char-first> ::= <sem-addchartoname> p
<name-char-first> ::= <sem-addchartoname> q
<name-char-first> ::= <sem-addchartoname> r
<name-char-first> ::= <sem-addchartoname> s
<name-char-first> ::= <sem-addchartoname> t
<name-char-first> ::= <sem-addchartoname> u
<name-char-first> ::= <sem-addchartoname> v
<name-char-first> ::= <sem-addchartoname> w
<name-char-first> ::= <sem-addchartoname> x
<name-char-first> ::= <sem-addchartoname> y
<name-char-first> ::= <sem-addchartoname> z
<name-char-first> ::= <sem-addchartoname> A
<name-char-first> ::= <sem-addchartoname> B
<name-char-first> ::= <sem-addchartoname> C
<name-char-first> ::= <sem-addchartoname> D
<name-char-first> ::= <sem-addchartoname> E
<name-char-first> ::= <sem-addchartoname> F
<name-char-first> ::= <sem-addchartoname> G
<name-char-first> ::= <sem-addchartoname> H
<name-char-first> ::= <sem-addchartoname> I
<name-char-first> ::= <sem-addchartoname> J
<name-char-first> ::= <sem-addchartoname> K
<name-char-first> ::= <sem-addchartoname> L
<name-char-first> ::= <sem-addchartoname> M
<name-char-first> ::= <sem-addchartoname> N
<name-char-first> ::= <sem-addchartoname> O
<name-char-first> ::= <sem-addchartoname> P
<name-char-first> ::= <sem-addchartoname> Q
<name-char-first> ::= <sem-addchartoname> R
<name-char-first> ::= <sem-addchartoname> S
<name-char-first> ::= <sem-addchartoname> T
<name-char-first> ::= <sem-addchartoname> U
<name-char-first> ::= <sem-addchartoname> V
<name-char-first> ::= <sem-addchartoname> W
<name-char-first> ::= <sem-addchartoname> X
<name-char-first> ::= <sem-addchartoname> Y
<name-char-first> ::= <sem-addchartoname> Z
<name-char> ::= <sem-addchartoname> 0
<name-char> ::= <sem-addchartoname> 1
<name-char> ::= <sem-addchartoname> 2
<name-char> ::= <sem-addchartoname> 3
<name-char> ::= <sem-addchartoname> 4
<name-char> ::= <sem-addchartoname> 5
<name-char> ::= <sem-addchartoname> 6
<name-char> ::= <sem-addchartoname> 7
<name-char> ::= <sem-addchartoname> 8
<name-char> ::= <sem-addchartoname> 9
<name-char> ::= <sem-addchartoname> $
<type> ::= <typesub>
<type> ::= _ <sem-beginbsub> <type_>
<type> ::= $ <type-ds>
<type-ds> ::= $ <type-dsds>
<type-ds> ::= 0 <sem-loadmsnum> <msnum> <sem-numtotype>
<type-dsds> ::= C <dsds-modifier> <type>
<dsds-modifier> ::= A
<dsds-modifier> ::= <sem-setconst> B
<dsds-modifier> ::= <sem-setvolatile> C
<dsds-modifier> ::= <sem-setvolatile> <sem-setconst> D
<dsds-modifier> ::= E
<dsds-modifier> ::= F
<dsds-modifier> ::= <sem-setvolatile> G
<dsds-modifier> ::= <sem-setvolatile> <sem-setconst> H
<dsds-modifier> ::= I
<dsds-modifier> ::= <sem-setconst> J
<dsds-modifier> ::= <sem-setvolatile> K
<dsds-modifier> ::= <sem-setvolatile> <sem-setconst> L
<typesub> ::= <sem-loadbsub> 0
<typesub> ::= <sem-loadbsub> 1
<typesub> ::= <sem-loadbsub> 2
<typesub> ::= <sem-loadbsub> 3
<typesub> ::= <sem-loadbsub> 4
<typesub> ::= <sem-loadbsub> 5
<typesub> ::= <sem-loadbsub> 6
<typesub> ::= <sem-loadbsub> 7
<typesub> ::= <sem-loadbsub> 8
<typesub> ::= <sem-loadbsub> 9
<type> ::= <sem-settypev> X #void
<type> ::= <sem-settypec> D #char
<type> ::= <sem-settypea> C #signed char
<type> ::= <sem-settypeh> E #unsigned char
<type> ::= <sem-settypes> F #short int
<type> ::= <sem-settypet> G #unsigned short int
<type> ::= <sem-settypei> H #int
<type> ::= <sem-settypej> I #unsigned int
<type> ::= <sem-settypel> J #long int
<type> ::= <sem-settypem> K #unsigned long int
<type> ::= <sem-settypef> M #float
<type> ::= <sem-settyped> N #double
<type> ::= <sem-settypee> O #long double (64-bit precision)
<type> ::= T <sem-beginbsub> <type-t>
<type> ::= U <sem-beginbsub> <type-u>
<type> ::= V <sem-beginbsub> <sem-setclass> <name-cover> <sem-unq2p> #class
<type> ::= W <sem-beginbsub> <type-w>
<type-t> ::= <sem-setunion> <name-cover> <sem-unq2p> #union
<type-u> ::= <sem-setstruct> <name-cover> <sem-unq2p> #struct
<type-w> ::= 4 <sem-setenum> <name-cover> <sem-unq2p> #enum
<type> ::= P <e> <sem-beginbsub> <type-pe> <type>
<type> ::= Q <e> <sem-beginbsub> <type-qe> <type>
<type> ::= R <e> <sem-beginbsub> <type-re> <type>
<type> ::= S <e> <sem-beginbsub> <type-se> <type>
<type> ::= A <e> <sem-beginbsub> <type-ae> <type>
<type-pe> ::= <sem-setptr> A #X *
<type-pe> ::= <sem-setptr> <sem-setconst> B #const X *
<type-pe> ::= <sem-setptr> <sem-setvolatile> C #volatile X *
<type-pe> ::= <sem-setptr> <sem-setvolatile> <sem-setconst> D #const volatile X *
<type-qe> ::= <sem-setptr> A #X * const
<type-qe> ::= <sem-setptr> <sem-setconst> B #const X * const
<type-qe> ::= <sem-setptr> <sem-setvolatile> C #volatile X *
<type-qe> ::= <sem-setptr> <sem-setvolatile> <sem-setconst> D #const volatile X *
<type-re> ::= A #X * volatile
<type-se> ::= A #X * const volatile
<type-pe> ::= IA #X * __restrict
<type-ae> ::= <sem-setref> A #X &
<type-ae> ::= <sem-setref> <sem-setconst> B #const X &
<type-ae> ::= <sem-setref> <sem-setvolatile> C #volatile X &
<type-ae> ::= <sem-setref> <sem-setvolatile> <sem-setconst> D #const volatile X &
<type-pa> ::= #X[] (as global object)
<type_> ::= <sem-settypeb> N #bool
<type_> ::= <sem-settypex> J #long long (__int64)
<type_> ::= <sem-settypey> K #unsigned long long (unsigned __int64)
<type_> ::= <sem-settypew> W #wchar_t
<type_> ::= T #long double (80-bit precision Intel compiler)
<type_> ::= Z #long double (80-bit precision Symantec/Digital Mars compiler)
<e> ::= E
<e> ::=
<sem-addchartoname> ::=
<sem-storename> ::=
<sem-loadid> ::=
<sem-loadarr> ::=
<sem-ssno> ::=
<sem-ssnest> ::=
<sem-stunq> ::=
<sem-loadtsub> ::=
<sem-type2expr> ::=
<sem-exprval> ::=
<sem-beginexpr> ::=
<sem-storeexpr> ::=
<sem-copyexpr> ::=
<sem-storepar> ::=
<sem-begintempl> ::=
<sem-skiptempl> ::=
<sem-storetemparg> ::=
<sem-storetemplate> ::=
<sem-setnametf> ::=
<sem-par2f> ::=
<sem-unq2f> ::=
<sem-unq2p> ::=
<sem-setnamex> ::=
<sem-setnameo> ::=
<sem-par2spec> ::=
<sem-par2ret> ::=
<sem-settypev> ::=
<sem-settypew> ::=
<sem-settypeb> ::=
<sem-settypec> ::=
<sem-settypea> ::=
<sem-settypeh> ::=
<sem-settypes> ::=
<sem-settypet> ::=
<sem-settypei> ::=
<sem-settypej> ::=
<sem-settypel> ::=
<sem-settypem> ::=
<sem-settypex> ::=
<sem-settypey> ::=
<sem-settypen> ::=
<sem-settypeo> ::=
<sem-settypef> ::=
<sem-settyped> ::=
<sem-settypee> ::=
<sem-settypeg> ::=
<sem-settypez> ::=
<sem-setrestrict> ::=
<sem-setvolatile> ::=
<sem-setconst> ::=
<sem-setptr> ::=
<sem-setref> ::=
<sem-setrval> ::=
<sem-setcpair> ::=
<sem-setim> ::=
<sem-substd> ::=
<sem-subalc> ::=
<sem-substr> ::=
<sem-substrs> ::=
<sem-subistr> ::=
<sem-subostr> ::=
<sem-subiostr> ::=
<sem-loadsub> ::=
<sem-setnamec> ::=
<sem-setnamed> ::=
<sem-setopnw> ::=
<sem-setopna> ::=
<sem-setopdl> ::=
<sem-setopda> ::=
<sem-setopps> ::=
<sem-setopng> ::=
<sem-setopad> ::=
<sem-setopde> ::=
<sem-setopco> ::=
<sem-setoppl> ::=
<sem-setopmi> ::=
<sem-setopml> ::=
<sem-setopdv> ::=
<sem-setoprm> ::=
<sem-setopan> ::=
<sem-setopor> ::=
<sem-setopeo> ::=
<sem-setopass> ::=
<sem-setoppll> ::=
<sem-setopmii> ::=
<sem-setopmll> ::=
<sem-setopdvv> ::=
<sem-setoprmm> ::=
<sem-setopann> ::=
<sem-setoporr> ::=
<sem-setopeoo> ::=
<sem-setopls> ::=
<sem-setoprs> ::=
<sem-setoplss> ::=
<sem-setoprss> ::=
<sem-setopeq> ::=
<sem-setopne> ::=
<sem-setoplt> ::=
<sem-setopgt> ::=
<sem-setople> ::=
<sem-setopnt> ::=
<sem-setopaa> ::=
<sem-setopoo> ::=
<sem-setoppp> ::=
<sem-setopmm> ::=
<sem-setopcm> ::=
<sem-setoppm> ::=
<sem-setoppt> ::=
<sem-setopcl> ::=
<sem-setopix> ::=
<sem-setopge> ::=
<sem-setopqu> ::=
<sem-setopst> ::=
<sem-setopsz> ::=
<sem-setopat> ::=
<sem-setopaz> ::=
<sem-setopcv> ::=
<sem-setopxx> ::=
<sem-setnamef> ::=
<sem-setnamevt> ::=
<sem-reversename> ::=
<sem-setprivate> ::=
<sem-setpublic> ::=
<sem-setprotected> ::=
<sem-setfcdecl> ::=
<sem-setfpascal> ::=
<sem-setffortran> ::=
<sem-setfthiscall> ::=
<sem-setfstdcall> ::=
<sem-setffastcall> ::=
<sem-setfinterrupt> ::=
<sem-setunion> ::=
<sem-setstruct> ::=
<sem-setclass> ::=
<sem-setenum> ::=
<sem-setstatic> ::=
<sem-setvirtual> ::=
<sem-stclconst> ::=
<sem-stclvol> ::=
<sem-stclfar> ::=
<sem-stclhuge> ::=
<sem-savenamesub> ::=
<sem-loadnamesub> ::=
<sem-mstemplsub> ::=
<sem-setnamer0> ::=
<sem-setnamer1> ::=
<sem-setnamer2> ::=
<sem-setnamer3> ::=
<sem-setnamer4> ::=
<sem-setname_a> ::=
<sem-setname_b> ::=
<sem-setname_c> ::=
<sem-setname_d> ::=
<sem-setname_e> ::=
<sem-setname_f> ::=
<sem-setname_g> ::=
<sem-setname_h> ::=
<sem-setname_i> ::=
<sem-setname_j> ::=
<sem-setname_k> ::=
<sem-setname_l> ::=
<sem-setname_m> ::=
<sem-setname_n> ::=
<sem-setname_o> ::=
<sem-setname_p> ::=
<sem-setname_q> ::=
<sem-setname_r> ::=
<sem-setname_s> ::=
<sem-setname_t> ::=
<sem-setname_u> ::=
<sem-setname_v> ::=
<sem-setname_w> ::=
<sem-setname_x> ::=
<sem-setname_y> ::=
<sem-setname_z> ::=
<sem-templ2tftpl> ::=
<sem-beginbsub> ::= #begin built-in substitution
<sem-loadbsub> ::=
<sem-addmconst> ::=
<sem-addmvol> ::=
<sem-addmfar> ::=
<sem-addmhuge> ::=
<sem-loadmsnum> ::=
<sem-numtorttibcd> ::=
<sem-numtotype> ::=
<sem-normalizeparname> ::=
<sem-borlandid> ::=
<sem-loadborlandsub> ::=
<sem-borlandarr> ::=
<sem-end> ::=

6145
3rdparty/demangler/src/gparser.cpp vendored Normal file

File diff suppressed because it is too large Load Diff

91
3rdparty/demangler/src/igrams.cpp vendored Normal file
View File

@ -0,0 +1,91 @@
/**
* @file src/demangler/igrams.cpp
* @brief Internal grammar list.
* @copyright (c) 2017 Avast Software, licensed under the MIT license
*/
#include <cstdlib>
#include "demangler/demglobal.h"
#include "demangler/igrams.h"
using namespace std;
namespace demangler {
/**
* @brief Classes containing internal grammar.
*/
cIgram_msll* igram_msll = nullptr; //Microsoft Visual C++
cIgram_gccll* igram_gccll = nullptr; //GCC
cIgram_borlandll* igram_borlandll = nullptr; //Borland
//[igram] add pointers to internal grammars here
/**
* @brief Function which allocates an internal grammar class and sets the internal grammar structure.
* @param gname Grammar name. The particular internal grammar is selected using this name.
* @param gParser Pointer to a cGram to send pointers to newly allocated grammar to.
* @return Was the initialisation successful?
* @retval false Grammar with the specified name was not found.
*/
bool initIgram(string gname, cGram* gParser) {
bool retvalue = false;
//Microsoft Visual C++ (msll)
if (gname == "ms") {
igram_msll = new cIgram_msll;
gParser->internalGrammarStruct = igram_msll->getInternalGrammar();
return true;
}
//GCC (gccll)
else if (gname == "gcc") {
igram_gccll = new cIgram_gccll;
gParser->internalGrammarStruct = igram_gccll->getInternalGrammar();
return true;
}
//Borland (borlandll)
else if (gname == "borland") {
igram_borlandll = new cIgram_borlandll;
gParser->internalGrammarStruct = igram_borlandll->getInternalGrammar();
return true;
}
//[igram] add allocation of internal grammars here
return retvalue;
}
/**
* @brief Function which deallocates all used internal grammar classes.
* @param gParser Pointer to a cGram to clean internal grammars from.
*/
void deleteIgrams(cGram* gParser) {
//first, delete the dynamically allocated internal llst if there is any
if (gParser->internalGrammarStruct.llst != nullptr) {
free(gParser->internalGrammarStruct.llst);
gParser->internalGrammarStruct.llst = nullptr;
}
//dealocate all internal grammars here...
if (igram_msll != nullptr) {
delete igram_msll;
igram_msll = nullptr;
}
if (igram_gccll != nullptr) {
delete igram_gccll;
igram_gccll = nullptr;
}
if (igram_borlandll != nullptr) {
delete igram_borlandll;
igram_borlandll = nullptr;
}
//[igram] add deallocation of internal grammars here
}
} // namespace demangler

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

5449
3rdparty/demangler/src/stgrammars/msll.cpp vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -63,6 +63,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pcsx2-gsrunner", "pcsx2-gsr
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zydis", "3rdparty\zydis\zydis.vcxproj", "{67D0160C-0FE4-44B9-AC2E-82BBCF4104DF}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "demangler", "3rdparty\demangler\demangler.vcxproj", "{1E3D706C-1D95-4E1B-BDF2-CA3D0007DF7F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug AVX2|x64 = Debug AVX2|x64
@ -415,6 +417,18 @@ Global
{67D0160C-0FE4-44B9-AC2E-82BBCF4104DF}.Release AVX2|x64.Build.0 = Release|x64
{67D0160C-0FE4-44B9-AC2E-82BBCF4104DF}.Release|x64.ActiveCfg = Release|x64
{67D0160C-0FE4-44B9-AC2E-82BBCF4104DF}.Release|x64.Build.0 = Release|x64
{1E3D706C-1D95-4E1B-BDF2-CA3D0007DF7F}.Debug AVX2|x64.ActiveCfg = Debug|x64
{1E3D706C-1D95-4E1B-BDF2-CA3D0007DF7F}.Debug AVX2|x64.Build.0 = Debug|x64
{1E3D706C-1D95-4E1B-BDF2-CA3D0007DF7F}.Debug|x64.ActiveCfg = Debug|x64
{1E3D706C-1D95-4E1B-BDF2-CA3D0007DF7F}.Debug|x64.Build.0 = Debug|x64
{1E3D706C-1D95-4E1B-BDF2-CA3D0007DF7F}.Devel AVX2|x64.ActiveCfg = Devel|x64
{1E3D706C-1D95-4E1B-BDF2-CA3D0007DF7F}.Devel AVX2|x64.Build.0 = Devel|x64
{1E3D706C-1D95-4E1B-BDF2-CA3D0007DF7F}.Devel|x64.ActiveCfg = Devel|x64
{1E3D706C-1D95-4E1B-BDF2-CA3D0007DF7F}.Devel|x64.Build.0 = Devel|x64
{1E3D706C-1D95-4E1B-BDF2-CA3D0007DF7F}.Release AVX2|x64.ActiveCfg = Release|x64
{1E3D706C-1D95-4E1B-BDF2-CA3D0007DF7F}.Release AVX2|x64.Build.0 = Release|x64
{1E3D706C-1D95-4E1B-BDF2-CA3D0007DF7F}.Release|x64.ActiveCfg = Release|x64
{1E3D706C-1D95-4E1B-BDF2-CA3D0007DF7F}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -444,6 +458,7 @@ Global
{95DD0A0C-D14D-4CFF-A593-820EF26EFCC8} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38}
{E960DFDF-1BD3-4C29-B251-D1A0919C9B09} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38}
{67D0160C-0FE4-44B9-AC2E-82BBCF4104DF} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38}
{1E3D706C-1D95-4E1B-BDF2-CA3D0007DF7F} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0BC474EA-3628-45D3-9DBC-E22D0B7E0F77}

View File

@ -254,6 +254,9 @@ if(QT_BUILD)
add_subdirectory(3rdparty/rapidjson EXCLUDE_FROM_ALL)
add_subdirectory(3rdparty/discord-rpc EXCLUDE_FROM_ALL)
endif()
# Demangler for the debugger
add_subdirectory(3rdparty/demangler EXCLUDE_FROM_ALL)
endif()
if(NOT WIN32 AND QT_BUILD)

View File

@ -122,6 +122,24 @@ target_sources(pcsx2-qt PRIVATE
Settings/SettingsDialog.ui
Settings/USBBindingWidget_DrivingForce.ui
Settings/USBBindingWidget_GTForce.ui
Debugger/CpuWidget.cpp
Debugger/CpuWidget.h
Debugger/CpuWidget.ui
Debugger/DebuggerWindow.cpp
Debugger/DebuggerWindow.h
Debugger/DebuggerWindow.ui
Debugger/DisassemblyWidget.cpp
Debugger/DisassemblyWidget.h
Debugger/DisassemblyWidget.ui
Debugger/MemoryViewWidget.cpp
Debugger/MemoryViewWidget.h
Debugger/MemoryViewWidget.ui
Debugger/RegisterWidget.cpp
Debugger/RegisterWidget.h
Debugger/RegisterWidget.ui
Debugger/BreakpointDialog.cpp
Debugger/BreakpointDialog.h
Debugger/BreakpointDialog.ui
Tools/InputRecording/NewInputRecordingDlg.cpp
Tools/InputRecording/NewInputRecordingDlg.h
Tools/InputRecording/NewInputRecordingDlg.ui

View File

@ -0,0 +1,206 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2022 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 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 PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "BreakpointDialog.h"
#include "DebugTools/Breakpoints.h"
#include "QtUtils.h"
#include "QtHost.h"
#include <QtWidgets/QDialog>
#include <QtWidgets/QMessageBox>
BreakpointDialog::BreakpointDialog(QWidget* parent, DebugInterface* cpu)
: QDialog(parent)
, m_cpu(cpu)
, m_purpose(BPDIALOG_PURPOSE::CREATE)
{
m_ui.setupUi(this);
m_ui.grpType->setEnabled(true);
m_ui.txtAddress->setEnabled(true);
connect(m_ui.rdoExecute, &QRadioButton::toggled, this, &BreakpointDialog::onRdoButtonToggled);
connect(m_ui.rdoMemory, &QRadioButton::toggled, this, &BreakpointDialog::onRdoButtonToggled);
}
BreakpointDialog::BreakpointDialog(QWidget* parent, DebugInterface* cpu, BreakPoint* bp)
: QDialog(parent)
, m_cpu(cpu)
, m_purpose(BPDIALOG_PURPOSE::EDIT_BP)
, m_bp(bp)
{
m_ui.setupUi(this);
m_ui.txtAddress->setText(QtUtils::FilledQStringFromValue(m_bp->addr, 16));
if (m_bp->hasCond)
m_ui.txtCondition->setText(QString::fromLocal8Bit(&m_bp->cond.expressionString[0]));
m_ui.chkEnable->setChecked(m_bp->enabled);
connect(m_ui.rdoExecute, &QRadioButton::toggled, this, &BreakpointDialog::onRdoButtonToggled);
connect(m_ui.rdoMemory, &QRadioButton::toggled, this, &BreakpointDialog::onRdoButtonToggled);
m_ui.rdoExecute->toggle();
}
BreakpointDialog::BreakpointDialog(QWidget* parent, DebugInterface* cpu, MemCheck* mc)
: QDialog(parent)
, m_cpu(cpu)
, m_purpose(BPDIALOG_PURPOSE::EDIT_MC)
, m_mc(mc)
{
m_ui.setupUi(this);
m_ui.txtAddress->setText(QtUtils::FilledQStringFromValue(m_mc->start, 16));
m_ui.txtSize->setText(QString::number(m_mc->end - m_mc->start, 16).toUpper());
m_ui.chkLog->setChecked(m_mc->result & MEMCHECK_LOG);
m_ui.chkEnable->setChecked(m_mc->result & MEMCHECK_BREAK);
m_ui.chkChange->setChecked(m_mc->cond & MEMCHECK_WRITE_ONCHANGE);
m_ui.chkRead->setChecked(m_mc->cond & MEMCHECK_READ);
m_ui.chkWrite->setChecked(m_mc->cond & MEMCHECK_WRITE);
connect(m_ui.rdoExecute, &QRadioButton::toggled, this, &BreakpointDialog::onRdoButtonToggled);
connect(m_ui.rdoMemory, &QRadioButton::toggled, this, &BreakpointDialog::onRdoButtonToggled);
m_ui.rdoMemory->toggle();
}
BreakpointDialog::~BreakpointDialog()
{
}
void BreakpointDialog::onRdoButtonToggled()
{
const bool isExecute = m_ui.rdoExecute->isChecked();
m_ui.grpExecute->setEnabled(isExecute);
m_ui.grpMemory->setEnabled(!isExecute);
m_ui.chkLog->setEnabled(!isExecute);
}
// When we are creating a breakpoint, use m_bp or m_mc only as a place to store information for the new breakpoint
// When we are modifying a breakpoint, m_bp or m_mc is a pointer to the breakpoint we are updating
void BreakpointDialog::accept()
{
if (m_purpose == BPDIALOG_PURPOSE::CREATE)
{
if (m_ui.rdoExecute->isChecked())
{
m_bp = new BreakPoint;
m_bp->cpu = m_cpu->getCpuType();
m_bp->cond.debug = m_cpu;
}
else
{
m_mc = new MemCheck;
m_mc->cpu = m_cpu->getCpuType();
}
}
PostfixExpression expression;
// Validate the address
u64 address;
if (!m_cpu->initExpression(m_ui.txtAddress->text().toLocal8Bit(), expression) || !m_cpu->parseExpression(expression, address))
{
QMessageBox::warning(this, "Error", "Invalid address \"" + m_ui.txtAddress->text() + "\"");
return;
}
u64 size;
if (m_ui.rdoMemory->isChecked())
{
if (!m_cpu->initExpression(m_ui.txtSize->text().toLocal8Bit(), expression) || !m_cpu->parseExpression(expression, size) || !size)
{
QMessageBox::warning(this, "Error", "Invalid size \"" + m_ui.txtSize->text() + "\"");
return;
}
m_mc->start = address;
const bool changedSize = m_mc->end != (m_mc->start + size);
const u32 prevEnd = m_mc->end;
m_mc->end = m_mc->start + size;
int condition = 0;
if (m_ui.chkRead->isChecked())
condition |= MEMCHECK_READ;
if (m_ui.chkWrite->isChecked())
condition |= MEMCHECK_WRITE;
if (m_ui.chkChange->isChecked())
condition |= MEMCHECK_WRITE_ONCHANGE;
int result = 0;
if (m_ui.chkEnable->isChecked())
result |= MEMCHECK_BREAK;
if (m_ui.chkLog->isChecked())
result |= MEMCHECK_LOG;
if (m_purpose == BPDIALOG_PURPOSE::CREATE)
{
Host::RunOnCPUThread([this, condition, result] {
CBreakPoints::AddMemCheck(m_cpu->getCpuType(), m_mc->start, m_mc->end, (MemCheckCondition)condition, (MemCheckResult)result);
delete m_mc;
});
}
else
{
Host::RunOnCPUThread([this, mc = *m_mc, condition, result, prevEnd, changedSize] {
if (changedSize)
{
CBreakPoints::RemoveMemCheck(m_cpu->getCpuType(), mc.start, prevEnd);
CBreakPoints::AddMemCheck(m_cpu->getCpuType(), mc.start, mc.end, static_cast<MemCheckCondition>(condition), static_cast<MemCheckResult>(result));
}
else
{
CBreakPoints::ChangeMemCheck(m_cpu->getCpuType(), mc.start, mc.end, static_cast<MemCheckCondition>(condition), static_cast<MemCheckResult>(result));
}
});
}
}
else
{
if (m_purpose == BPDIALOG_PURPOSE::CREATE)
Host::RunOnCPUThread([this, address] {
CBreakPoints::AddBreakPoint(m_cpu->getCpuType(), address);
});
// Validate the condition
// TODO: Expression management in the core should be updated to make this prettier
if (!m_ui.txtCondition->text().isEmpty())
{
auto strData = m_ui.txtCondition->text().toLocal8Bit();
expression.clear();
if (!m_cpu->initExpression(strData.constData(), expression))
{
QMessageBox::warning(this, "Error", "Invalid condition \"" + strData + "\"");
return;
}
m_bp->cond.expression = expression;
strncpy(&m_bp->cond.expressionString[0], strData.constData(), sizeof(m_bp->cond.expressionString));
Host::RunOnCPUThread([this, address] {
CBreakPoints::ChangeBreakPointAddCond(m_cpu->getCpuType(), address, m_bp->cond);
});
}
else
{
Host::RunOnCPUThread([this, address] { CBreakPoints::ChangeBreakPointRemoveCond(m_cpu->getCpuType(), address); }, true);
}
m_bp->addr = address;
}
QDialog::accept();
}

View File

@ -0,0 +1,53 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2022 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 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 PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "ui_BreakpointDialog.h"
#include "DebugTools/Breakpoints.h"
#include <QtWidgets/QDialog>
class BreakpointDialog final : public QDialog
{
Q_OBJECT
public:
BreakpointDialog(QWidget* parent, DebugInterface* cpu);
BreakpointDialog(QWidget* parent, DebugInterface* cpu, BreakPoint* bp);
BreakpointDialog(QWidget* parent, DebugInterface* cpu, MemCheck* mc);
~BreakpointDialog();
public slots:
void onRdoButtonToggled();
void accept() override;
private:
enum class BPDIALOG_PURPOSE
{
CREATE,
EDIT_BP,
EDIT_MC
};
Ui::BreakpointDialog m_ui;
DebugInterface* m_cpu;
const BPDIALOG_PURPOSE m_purpose;
BreakPoint* m_bp = nullptr;
MemCheck* m_mc = nullptr;
};

View File

@ -0,0 +1,325 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>BreakpointDialog</class>
<widget class="QDialog" name="BreakpointDialog">
<property name="windowModality">
<enum>Qt::ApplicationModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>375</width>
<height>250</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>375</width>
<height>250</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>375</width>
<height>250</height>
</size>
</property>
<property name="baseSize">
<size>
<width>375</width>
<height>250</height>
</size>
</property>
<property name="windowTitle">
<string>Create / Modify Breakpoint</string>
</property>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="geometry">
<rect>
<x>20</x>
<y>210</y>
<width>341</width>
<height>32</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
<widget class="QGroupBox" name="grpType">
<property name="enabled">
<bool>false</bool>
</property>
<property name="geometry">
<rect>
<x>10</x>
<y>0</y>
<width>91</width>
<height>81</height>
</rect>
</property>
<property name="title">
<string>Type</string>
</property>
<layout class="QFormLayout" name="formLayout_2">
<item row="1" column="0">
<widget class="QRadioButton" name="rdoExecute">
<property name="text">
<string>Execute</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QRadioButton" name="rdoMemory">
<property name="text">
<string>Memory</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QGroupBox" name="groupBox_2">
<property name="geometry">
<rect>
<x>110</x>
<y>10</y>
<width>251</width>
<height>41</height>
</rect>
</property>
<property name="title">
<string/>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Address</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="txtAddress">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>19</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>19</height>
</size>
</property>
<property name="text">
<string>0</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QGroupBox" name="grpMemory">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>110</x>
<y>50</y>
<width>251</width>
<height>91</height>
</rect>
</property>
<property name="title">
<string>Memory</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QCheckBox" name="chkRead">
<property name="text">
<string>Read</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="chkWrite">
<property name="text">
<string>Write</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="chkChange">
<property name="text">
<string>Change</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QFormLayout" name="formLayout_4">
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Size</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="txtSize">
<property name="minimumSize">
<size>
<width>0</width>
<height>19</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>19</height>
</size>
</property>
<property name="text">
<string>1</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QGroupBox" name="grpExecute">
<property name="enabled">
<bool>false</bool>
</property>
<property name="geometry">
<rect>
<x>110</x>
<y>140</y>
<width>251</width>
<height>61</height>
</rect>
</property>
<property name="title">
<string>Execute</string>
</property>
<layout class="QFormLayout" name="formLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Condition</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="txtCondition"/>
</item>
</layout>
</widget>
<widget class="QGroupBox" name="groupBox_5">
<property name="geometry">
<rect>
<x>10</x>
<y>90</y>
<width>91</width>
<height>71</height>
</rect>
</property>
<property name="title">
<string/>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QCheckBox" name="chkLog">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Log</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="chkEnable">
<property name="text">
<string>Enable</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>BreakpointDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>BreakpointDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,120 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2022 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 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 PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "ui_CpuWidget.h"
#include "DebugTools/DebugInterface.h"
#include "DebugTools/Breakpoints.h"
#include "DebugTools/BiosDebugData.h"
#include "DebugTools/MipsStackWalk.h"
#include "QtHost.h"
#include <QtWidgets/QWidget>
#include <QtWidgets/QTableWidget>
#include <vector>
using namespace MipsStackWalk;
class CpuWidget final : public QWidget
{
Q_OBJECT
public:
CpuWidget(QWidget* parent, DebugInterface& cpu);
~CpuWidget();
public slots:
void resizeEvent(QResizeEvent* event);
void paintEvent(QPaintEvent* event);
void onStepInto();
void onStepOver();
void onStepOut();
void onVMPaused();
void updateBreakpoints();
void fixBPListColumnSize();
void onBPListContextMenu(QPoint pos);
void onBPListItemChange(QTableWidgetItem* item);
void contextBPListCopy();
void contextBPListDelete();
void contextBPListNew();
void contextBPListEdit();
void updateThreads();
void onThreadListContextMenu(QPoint pos);
void onThreadListDoubleClick(int row, int column);
void updateStackFrames();
void onStackListContextMenu(QPoint pos);
void onStackListDoubleClick(int row, int column);
void updateFunctionList(bool whenEmpty = false);
void onFuncListContextMenu(QPoint pos);
void onFuncListDoubleClick(QListWidgetItem* item);
void reloadCPUWidgets()
{
if (!QtHost::IsOnUIThread())
{
QtHost::RunOnUIThread(CBreakPoints::GetUpdateHandler());
return;
}
updateBreakpoints();
updateThreads();
updateStackFrames();
updateFunctionList();
m_ui.registerWidget->update();
m_ui.disassemblyWidget->update();
m_ui.memoryviewWidget->update();
};
void onSearchButtonClicked();
private:
std::vector<QTableWidget*> m_registerTableViews;
QMenu* m_bplistContextMenu = 0;
QMenu* m_threadlistContextMenu = 0;
QMenu* m_stacklistContextMenu = 0;
QMenu* m_funclistContextMenu = 0;
Ui::CpuWidget m_ui;
DebugInterface& m_cpu;
// Poor mans variant
// Allows us to map row index to breakpoint / memcheck objects
struct BreakpointObject
{
std::shared_ptr<BreakPoint> bp;
std::shared_ptr<MemCheck> mc;
};
std::vector<BreakpointObject> m_bplistObjects;
std::vector<EEThread> m_threadlistObjects;
EEThread m_activeThread;
std::vector<StackFrame> m_stacklistObjects;
bool m_demangleFunctions = true;
};

View File

@ -0,0 +1,667 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CpuWidget</class>
<widget class="QWidget" name="CpuWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>668</width>
<height>563</height>
</rect>
</property>
<property name="font">
<font>
<family>Monospace</family>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="windowTitle">
<string/>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0">
<item>
<widget class="QTabWidget" name="tabWidgetRegFunc">
<property name="minimumSize">
<size>
<width>320</width>
<height>350</height>
</size>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tabRegisters">
<attribute name="title">
<string>Registers</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="RegisterWidget" name="registerWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>320</width>
<height>300</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>595</height>
</size>
</property>
<property name="font">
<font>
<family>Monospace</family>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="autoFillBackground">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tabFunctions">
<attribute name="title">
<string>Functions</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QListWidget" name="listFunctions">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QPushButton" name="btnRefreshFunctions">
<property name="text">
<string>Refresh</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="txtFuncSearch">
<property name="placeholderText">
<string>Filter</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Memory Search</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QGridLayout" name="gridLayout_2">
<item row="5" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>End</string>
</property>
</widget>
</item>
<item row="4" column="1" colspan="3">
<widget class="QLineEdit" name="txtSearchStart">
<property name="text">
<string>0x00</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Value</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="txtSearchValue"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Start</string>
</property>
</widget>
</item>
<item row="5" column="1" colspan="3">
<widget class="QLineEdit" name="txtSearchEnd">
<property name="text">
<string>0x2000000</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Type</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="cmbSearchType">
<item>
<property name="text">
<string>1 Byte (8 bits)</string>
</property>
</item>
<item>
<property name="text">
<string>2 Bytes (16 bits)</string>
</property>
</item>
<item>
<property name="text">
<string>4 Bytes (32 bits)</string>
</property>
</item>
<item>
<property name="text">
<string>8 Bytes (64 bits)</string>
</property>
</item>
<item>
<property name="text">
<string>Float</string>
</property>
</item>
<item>
<property name="text">
<string>Double</string>
</property>
</item>
<item>
<property name="text">
<string>String</string>
</property>
</item>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Hex</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QCheckBox" name="chkSearchHex">
<property name="text">
<string/>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="2" colspan="2">
<widget class="QPushButton" name="btnSearch">
<property name="text">
<string>Search</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QListWidget" name="listSearchResults"/>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="DisassemblyWidget" name="disassemblyWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>320</width>
<height>350</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>595</height>
</size>
</property>
<property name="font">
<font>
<family>Monospace</family>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="autoFillBackground">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>215</height>
</size>
</property>
<property name="tabPosition">
<enum>QTabWidget::South</enum>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab_memory">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<attribute name="title">
<string>Memory</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="MemoryViewWidget" name="memoryviewWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>250</width>
<height>145</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<family>Monospace</family>
</font>
</property>
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="autoFillBackground">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_breakpoints">
<attribute name="title">
<string>Breakpoints</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTableWidget" name="breakpointList">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="gridStyle">
<enum>Qt::NoPen</enum>
</property>
<property name="rowCount">
<number>0</number>
</property>
<attribute name="horizontalHeaderCascadingSectionResizes">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderDefaultSectionSize">
<number>75</number>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderCascadingSectionResizes">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string>Type</string>
</property>
</column>
<column>
<property name="text">
<string>Offset</string>
</property>
</column>
<column>
<property name="text">
<string>Size / Label</string>
</property>
</column>
<column>
<property name="text">
<string>Opcode</string>
</property>
</column>
<column>
<property name="text">
<string>Condition</string>
</property>
</column>
<column>
<property name="text">
<string>Hits</string>
</property>
</column>
<column>
<property name="text">
<string>Enabled</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_threads">
<attribute name="title">
<string>Threads</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTableWidget" name="threadList">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="gridStyle">
<enum>Qt::NoPen</enum>
</property>
<property name="cornerButtonEnabled">
<bool>false</bool>
</property>
<property name="rowCount">
<number>0</number>
</property>
<property name="columnCount">
<number>6</number>
</property>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string>ID</string>
</property>
</column>
<column>
<property name="text">
<string>PC</string>
</property>
</column>
<column>
<property name="text">
<string>Entry Point</string>
</property>
</column>
<column>
<property name="text">
<string>Priority</string>
</property>
</column>
<column>
<property name="text">
<string>State</string>
</property>
</column>
<column>
<property name="text">
<string>Wait Type</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_callstack">
<attribute name="title">
<string>Call Stack</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTableWidget" name="stackframeList">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContentsOnFirstShow</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="gridStyle">
<enum>Qt::NoPen</enum>
</property>
<property name="cornerButtonEnabled">
<bool>false</bool>
</property>
<property name="rowCount">
<number>0</number>
</property>
<property name="columnCount">
<number>6</number>
</property>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string>Entry</string>
</property>
</column>
<column>
<property name="text">
<string>Name</string>
</property>
</column>
<column>
<property name="text">
<string>PC</string>
</property>
</column>
<column>
<property name="text">
<string>Opcode</string>
</property>
</column>
<column>
<property name="text">
<string>SP</string>
</property>
</column>
<column>
<property name="text">
<string>Frame Size</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>DisassemblyWidget</class>
<extends>QWidget</extends>
<header>pcsx2-qt/Debugger/DisassemblyWidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>RegisterWidget</class>
<extends>QWidget</extends>
<header>pcsx2-qt/Debugger/RegisterWidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>MemoryViewWidget</class>
<extends>QWidget</extends>
<header>pcsx2-qt/Debugger/MemoryViewWidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,123 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2022 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 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 PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "DebuggerWindow.h"
#include "DebugTools/DebugInterface.h"
#include "DebugTools/Breakpoints.h"
#include "VMManager.h"
#include "QtHost.h"
#include "MainWindow.h"
DebuggerWindow::DebuggerWindow(QWidget* parent)
: QMainWindow(parent)
{
m_ui.setupUi(this);
// Easiest way to handle cross platform monospace fonts
// There are issues related to TabWidget -> Children font inheritance otherwise
#ifdef WIN32
this->setStyleSheet("font: 8pt 'Lucida Console'");
#else
this->setStyleSheet("font: 8pt 'Monospace'");
#endif
m_actionRunPause = new QAction("Run", this);
m_actionStepInto = new QAction("Step Into", this);
m_actionStepOver = new QAction("Step Over", this);
m_actionStepOut = new QAction("Step Out", this);
m_ui.menubar->addAction(m_actionRunPause);
m_ui.menubar->addAction(m_actionStepInto);
m_ui.menubar->addAction(m_actionStepOver);
m_ui.menubar->addAction(m_actionStepOut);
connect(m_actionRunPause, &QAction::triggered, this, &DebuggerWindow::onRunPause);
connect(m_actionStepInto, &QAction::triggered, this, &DebuggerWindow::onStepInto);
connect(m_actionStepOver, &QAction::triggered, this, &DebuggerWindow::onStepOver);
connect(m_actionStepOut, &QAction::triggered, this, &DebuggerWindow::onStepOut);
connect(g_emu_thread, &EmuThread::onVMPaused, this, &DebuggerWindow::onVMStateChanged);
connect(g_emu_thread, &EmuThread::onVMResumed, this, &DebuggerWindow::onVMStateChanged);
onVMStateChanged(); // If we missed a state change while we weren't loaded
m_cpuWidget_r5900 = new CpuWidget(this, r5900Debug);
m_cpuWidget_r3000 = new CpuWidget(this, r3000Debug);
m_ui.cpuTabs->addTab(m_cpuWidget_r5900, "R5900");
m_ui.cpuTabs->addTab(m_cpuWidget_r3000, "R3000");
return;
}
DebuggerWindow::~DebuggerWindow() = default;
// TODO: not this
bool nextStatePaused = true;
void DebuggerWindow::onVMStateChanged()
{
if (!QtHost::IsVMPaused())
{
nextStatePaused = true;
m_actionRunPause->setText("Pause");
m_actionStepInto->setEnabled(false);
m_actionStepOver->setEnabled(false);
m_actionStepOut->setEnabled(false);
}
else
{
nextStatePaused = false;
m_actionRunPause->setText("Run");
m_actionStepInto->setEnabled(true);
m_actionStepOver->setEnabled(true);
m_actionStepOut->setEnabled(true);
CBreakPoints::ClearTemporaryBreakPoints();
if (CBreakPoints::GetBreakpointTriggered())
{
CBreakPoints::SetBreakpointTriggered(false);
// Our current PC is on a breakpoint.
// When we run the core again, we want to skip this breakpoint and run
CBreakPoints::SetSkipFirst(BREAKPOINT_EE, r5900Debug.getPC());
CBreakPoints::SetSkipFirst(BREAKPOINT_IOP, r3000Debug.getPC());
}
}
return;
}
void DebuggerWindow::onRunPause()
{
g_emu_thread->setVMPaused(nextStatePaused);
}
void DebuggerWindow::onStepInto()
{
CpuWidget* currentCpu = static_cast<CpuWidget*>(m_ui.cpuTabs->currentWidget());
currentCpu->onStepInto();
}
void DebuggerWindow::onStepOver()
{
CpuWidget* currentCpu = static_cast<CpuWidget*>(m_ui.cpuTabs->currentWidget());
currentCpu->onStepOver();
}
void DebuggerWindow::onStepOut()
{
CpuWidget* currentCpu = static_cast<CpuWidget*>(m_ui.cpuTabs->currentWidget());
currentCpu->onStepOut();
}

View File

@ -0,0 +1,46 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2022 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 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 PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "CpuWidget.h"
#include "ui_DebuggerWindow.h"
class DebuggerWindow : public QMainWindow
{
Q_OBJECT
public:
DebuggerWindow(QWidget* parent);
~DebuggerWindow();
public slots:
void onVMStateChanged();
void onRunPause();
void onStepInto();
void onStepOver();
void onStepOut();
private:
Ui::DebuggerWindow m_ui;
QAction* m_actionRunPause;
QAction* m_actionStepInto;
QAction* m_actionStepOver;
QAction* m_actionStepOut;
CpuWidget* m_cpuWidget_r5900;
CpuWidget* m_cpuWidget_r3000;
};

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DebuggerWindow</class>
<widget class="QMainWindow" name="DebuggerWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>PCSX2 Debugger</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QTabWidget" name="cpuTabs"/>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>22</height>
</rect>
</property>
</widget>
<action name="actionRun">
<property name="text">
<string>Run</string>
</property>
</action>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,702 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2022 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 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 PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "DisassemblyWidget.h"
#include "DebugTools/DebugInterface.h"
#include "DebugTools/DisassemblyManager.h"
#include "DebugTools/Breakpoints.h"
#include "DebugTools/MipsAssembler.h"
#include "QtUtils.h"
#include "QtHost.h"
#include <QtGui/QMouseEvent>
#include <QtWidgets/QMenu>
#include <QtGui/QClipboard>
#include <QtWidgets/QInputDialog>
#include <QtWidgets/QMessageBox>
using namespace QtUtils;
DisassemblyWidget::DisassemblyWidget(QWidget* parent)
: QWidget(parent)
{
ui.setupUi(this);
CreateCustomContextMenu();
connect(this, &DisassemblyWidget::customContextMenuRequested, this, &DisassemblyWidget::customMenuRequested);
}
DisassemblyWidget::~DisassemblyWidget() = default;
void DisassemblyWidget::CreateCustomContextMenu()
{
if (m_contextMenu)
return; // ???
m_contextMenu = new QMenu(this);
QAction* action = 0;
m_contextMenu->addAction(action = new QAction("Copy Address", this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextCopyAddress);
m_contextMenu->addAction(action = new QAction("Copy Instruction Hex", this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextCopyInstructionHex);
m_contextMenu->addAction(action = new QAction("Copy Instruction Text", this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextCopyInstructionText);
// TODO: Disassemble to file. Do people use that?
m_contextMenu->addSeparator();
m_contextMenu->addAction(action = new QAction("Assemble new Instruction(s)", this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextAssembleInstruction);
m_contextMenu->addSeparator();
m_contextMenu->addAction(action = new QAction("Run to Cursor", this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextRunToCursor);
m_contextMenu->addAction(action = new QAction("Jump to Cursor", this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextJumpToCursor);
m_contextMenu->addAction(action = new QAction("Toggle Breakpoint", this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextToggleBreakpoint);
m_contextMenu->addAction(action = new QAction("Follow Branch", this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextFollowBranch);
m_contextMenu->addSeparator();
m_contextMenu->addAction(action = new QAction("Go to Address", this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextGoToAddress);
m_contextMenu->addAction(action = new QAction("Go to in Memory View", this));
connect(action, &QAction::triggered, this, [this]() { gotoInMemory(m_selectedAddressStart); });
m_contextMenu->addSeparator();
m_contextMenu->addAction(action = new QAction("Add Function", this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextAddFunction);
m_contextMenu->addAction(action = new QAction("Rename Function", this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextRenameFunction);
m_contextMenu->addAction(action = new QAction("Remove Function", this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextRemoveFunction);
}
void DisassemblyWidget::contextCopyAddress()
{
QGuiApplication::clipboard()->setText(FetchSelectionInfo(SelectionInfo::ADDRESS));
}
void DisassemblyWidget::contextCopyInstructionHex()
{
QGuiApplication::clipboard()->setText(FetchSelectionInfo(SelectionInfo::INSTRUCTIONHEX));
}
void DisassemblyWidget::contextCopyInstructionText()
{
QGuiApplication::clipboard()->setText(FetchSelectionInfo(SelectionInfo::INSTRUCTIONTEXT));
}
void DisassemblyWidget::contextAssembleInstruction()
{
if (!m_cpu->isCpuPaused())
{
QMessageBox::warning(this, "Assemble Error", "Unable to change assembly while core is running");
return;
}
DisassemblyLineInfo line;
bool ok;
m_disassemblyManager.getLine(m_selectedAddressStart, false, line);
QString instruction = QInputDialog::getText(this, "Assemble Instruction", "",
QLineEdit::Normal, QString("%1 %2").arg(line.name.c_str()).arg(line.params.c_str()), &ok);
if (!ok)
return;
u32 encodedInstruction;
std::string errorText;
bool valid = MipsAssembleOpcode(instruction.toLocal8Bit().constData(), m_cpu, m_selectedAddressStart, encodedInstruction, errorText);
if (!valid)
{
QMessageBox::warning(this, "Assemble Error", QString::fromStdString(errorText));
return;
}
else
{
Host::RunOnCPUThread([this, start = m_selectedAddressStart, end = m_selectedAddressEnd, cpu = m_cpu, val = encodedInstruction] {
for (u32 i = start; i <= end; i += 4)
{
cpu->write32(i, val);
}
QtHost::RunOnUIThread([this] { VMUpdate(); });
});
}
}
void DisassemblyWidget::contextRunToCursor()
{
Host::RunOnCPUThread([&] { CBreakPoints::AddBreakPoint(m_cpu->getCpuType(), m_selectedAddressStart); });
m_cpu->resumeCpu();
}
void DisassemblyWidget::contextJumpToCursor()
{
m_cpu->setPc(m_selectedAddressStart);
this->repaint();
}
void DisassemblyWidget::contextToggleBreakpoint()
{
if (CBreakPoints::IsAddressBreakPoint(m_cpu->getCpuType(), m_selectedAddressStart))
{
Host::RunOnCPUThread([&] { CBreakPoints::RemoveBreakPoint(m_cpu->getCpuType(), m_selectedAddressStart); });
}
else
{
Host::RunOnCPUThread([&] { CBreakPoints::AddBreakPoint(m_cpu->getCpuType(), m_selectedAddressStart); });
}
breakpointsChanged();
this->repaint();
}
void DisassemblyWidget::contextFollowBranch()
{
DisassemblyLineInfo line;
m_disassemblyManager.getLine(m_selectedAddressStart, true, line);
if (line.type == DISTYPE_OPCODE || line.type == DISTYPE_MACRO)
{
if (line.info.isBranch)
gotoAddress(line.info.branchTarget);
else if (line.info.hasRelevantAddress)
gotoAddress(line.info.releventAddress);
}
}
void DisassemblyWidget::contextGoToAddress()
{
bool ok;
const QString targetString = QInputDialog::getText(this, "Go to address", "",
QLineEdit::Normal, "", &ok);
if (!ok)
return;
const u32 targetAddress = targetString.toUInt(&ok, 16) & ~3;
if (!ok)
{
QMessageBox::warning(this, "Go to address error", "Invalid address");
return;
}
gotoAddress(targetAddress);
}
void DisassemblyWidget::contextAddFunction()
{
// Get current function
const u32 curAddress = m_selectedAddressStart;
const u32 curFuncAddr = m_cpu->GetSymbolMap().GetFunctionStart(m_selectedAddressStart);
QString optionaldlgText;
if (curFuncAddr != SymbolMap::INVALID_ADDRESS)
{
if (curFuncAddr == curAddress) // There is already a function here
{
QMessageBox::warning(this, "Add Function Error", "A function entry point already exists here. Consider renaming instead.");
}
else
{
const u32 prevSize = m_cpu->GetSymbolMap().GetFunctionSize(curFuncAddr);
u32 newSize = curAddress - curFuncAddr;
bool ok;
QString funcName = QInputDialog::getText(this, "Add Function",
QString("Function will be (0x%1) instructions long.\nEnter function name").arg(prevSize - newSize, 0, 16), QLineEdit::Normal, "", &ok);
if (!ok)
return;
m_cpu->GetSymbolMap().SetFunctionSize(curFuncAddr, newSize); // End the current function to where we selected
newSize = prevSize - newSize;
m_cpu->GetSymbolMap().AddFunction(funcName.toLocal8Bit().constData(), curAddress, newSize);
m_cpu->GetSymbolMap().SortSymbols();
m_cpu->GetSymbolMap().UpdateActiveSymbols();
}
}
else
{
bool ok;
QString funcName = QInputDialog::getText(this, "Add Function",
QString("Function will be (0x%1) instructions long.\nEnter function name").arg(m_selectedAddressEnd + 4 - m_selectedAddressStart, 0, 16), QLineEdit::Normal, "", &ok);
if (!ok)
return;
m_cpu->GetSymbolMap().AddFunction(funcName.toLocal8Bit().constData(), m_selectedAddressStart, m_selectedAddressEnd + 4 - m_selectedAddressStart);
m_cpu->GetSymbolMap().SortSymbols();
m_cpu->GetSymbolMap().UpdateActiveSymbols();
}
}
void DisassemblyWidget::contextRemoveFunction()
{
u32 curFuncAddr = m_cpu->GetSymbolMap().GetFunctionStart(m_selectedAddressStart);
if (curFuncAddr != SymbolMap::INVALID_ADDRESS)
{
u32 previousFuncAddr = m_cpu->GetSymbolMap().GetFunctionStart(curFuncAddr - 4);
if (previousFuncAddr != SymbolMap::INVALID_ADDRESS)
{
// Extend the previous function to replace the spot of the function that is going to be removed
u32 expandedSize = m_cpu->GetSymbolMap().GetFunctionSize(previousFuncAddr) + m_cpu->GetSymbolMap().GetFunctionSize(curFuncAddr);
m_cpu->GetSymbolMap().SetFunctionSize(previousFuncAddr, expandedSize);
}
m_cpu->GetSymbolMap().RemoveFunction(curFuncAddr, true);
m_cpu->GetSymbolMap().SortSymbols();
m_cpu->GetSymbolMap().UpdateActiveSymbols();
}
}
void DisassemblyWidget::contextRenameFunction()
{
const u32 curFuncAddress = m_cpu->GetSymbolMap().GetFunctionStart(m_selectedAddressStart);
if (curFuncAddress != SymbolMap::INVALID_ADDRESS)
{
bool ok;
QString funcName = QInputDialog::getText(this, "Rename Function", "Function name", QLineEdit::Normal, m_cpu->GetSymbolMap().GetLabelString(curFuncAddress).c_str(), &ok);
if (!ok)
return;
if (funcName.isEmpty())
{
QMessageBox::warning(this, "Rename Function Error", "Function name cannot be nothing.");
}
else
{
m_cpu->GetSymbolMap().SetLabelName(funcName.toLocal8Bit().constData(), curFuncAddress);
m_cpu->GetSymbolMap().SortSymbols();
m_cpu->GetSymbolMap().UpdateActiveSymbols();
this->repaint();
}
}
else
{
QMessageBox::warning(this, "Rename Function Error", "No function / symbol is currently selected.");
}
}
void DisassemblyWidget::SetCpu(DebugInterface* cpu)
{
m_cpu = cpu;
m_disassemblyManager.setCpu(cpu);
}
QString DisassemblyWidget::GetLineDisasm(u32 address)
{
DisassemblyLineInfo lineInfo;
m_disassemblyManager.getLine(address, true, lineInfo);
return QString("%1 %2").arg(lineInfo.name.c_str()).arg(lineInfo.params.c_str());
};
// Here we go!
void DisassemblyWidget::paintEvent(QPaintEvent* event)
{
QPainter painter(this);
const u32 w = painter.device()->width() - 1;
const u32 h = painter.device()->height() - 1;
// Get the current font size
const QFontMetrics fm = painter.fontMetrics();
// Get the row height
m_rowHeight = fm.height() + 2;
// Find the amount of visible rows
m_visibleRows = h / m_rowHeight;
m_disassemblyManager.analyze(m_visibleStart, m_disassemblyManager.getNthNextAddress(m_visibleStart, m_visibleRows) - m_visibleStart);
// Draw the rows
bool inSelectionBlock = false;
bool alternate = m_visibleStart % 8;
const u32 curPC = m_cpu->getPC(); // Get the PC here, because it'll change when we are drawing and make it seem like there are two PCs
for (u32 i = 0; i <= m_visibleRows; i++)
{
const u32 rowAddress = (i * 4) + m_visibleStart;
// Row backgrounds
if (inSelectionBlock || (m_selectedAddressStart <= rowAddress && rowAddress <= m_selectedAddressEnd))
{
painter.fillRect(0, i * m_rowHeight, w, m_rowHeight, this->palette().highlight());
inSelectionBlock = m_selectedAddressEnd != rowAddress;
}
else
{
painter.fillRect(0, i * m_rowHeight, w, m_rowHeight, alternate ? this->palette().base() : this->palette().alternateBase());
}
// Row text
painter.setPen(GetAddressFunctionColor(rowAddress));
QString lineString = DisassemblyStringFromAddress(rowAddress, painter.font(), curPC);
painter.drawText(2, i * m_rowHeight, w, m_rowHeight, Qt::AlignLeft, lineString);
alternate = !alternate;
}
// Draw the branch lines
// This is where it gets a little scary
// It's been mostly copied from the wx implementation
u32 visibleEnd = m_disassemblyManager.getNthNextAddress(m_visibleStart, m_visibleRows);
std::vector<BranchLine> branchLines = m_disassemblyManager.getBranchLines(m_visibleStart, visibleEnd - m_visibleStart);
s32 branchCount = 0;
s32 skippedBranches = 0;
for (const auto& branchLine : branchLines)
{
if (branchCount == 5)
break;
const int winBottom = this->height();
const int x = this->width() - 10 - ((std::max(0, branchLine.laneIndex - skippedBranches)) * 10);
int top, bottom;
// If the start is technically 'above' our address view
if (branchLine.first < m_visibleStart)
{
top = -1;
}
// If the start is technically 'below' our address view
else if (branchLine.first >= visibleEnd)
{
top = winBottom + 1;
}
else
{
// Explaination
// ((branchLine.first - m_visibleStart) -> Find the amount of bytes from the top of the view
// / 4 -> Convert that into rowss in instructions
// * m_rowHeight -> convert that into rows in pixels
// + (m_rowHeight / 2) -> Add half a row in pixels to center the arrow
top = (((branchLine.first - m_visibleStart) / 4) * m_rowHeight) + (m_rowHeight / 2);
}
if (branchLine.second < m_visibleStart)
{
bottom = -1;
}
else if (branchLine.second >= visibleEnd)
{
bottom = winBottom + 1;
}
else
{
bottom = (((branchLine.second - m_visibleStart) / 4) * m_rowHeight) + (m_rowHeight / 2);
}
if ((top < 0 && bottom < 0) || (top > winBottom && bottom > winBottom) || (top < 0 && bottom > winBottom) || (top > winBottom && bottom < 0))
{
skippedBranches++;
continue;
}
branchCount++;
if (branchLine.first == m_selectedAddressStart || branchLine.second == m_selectedAddressStart)
{
painter.setPen(QColor(0xFF257AFA));
}
else
{
painter.setPen(QColor(0xFFFF3020));
}
if (top < 0) // first is not visible, but second is
{
painter.drawLine(x - 2, bottom, x + 2, bottom);
painter.drawLine(x + 2, bottom, x + 2, 0);
if (branchLine.type == LINE_DOWN)
{
painter.drawLine(x, bottom - 4, x - 4, bottom);
painter.drawLine(x - 4, bottom, x + 1, bottom + 5);
}
}
else if (bottom > winBottom) // second is not visible, but first is
{
painter.drawLine(x - 2, top, x + 2, top);
painter.drawLine(x + 2, top, x + 2, winBottom);
if (branchLine.type == LINE_UP)
{
painter.drawLine(x, top - 4, x - 4, top);
painter.drawLine(x - 4, top, x + 1, top + 5);
}
}
else
{ // both are visible
if (branchLine.type == LINE_UP)
{
painter.drawLine(x - 2, bottom, x + 2, bottom);
painter.drawLine(x + 2, bottom, x + 2, top);
painter.drawLine(x + 2, top, x - 4, top);
painter.drawLine(x, top - 4, x - 4, top);
painter.drawLine(x - 4, top, x + 1, top + 5);
}
else
{
painter.drawLine(x - 2, top, x + 2, top);
painter.drawLine(x + 2, top, x + 2, bottom);
painter.drawLine(x + 2, bottom, x - 4, bottom);
painter.drawLine(x, bottom - 4, x - 4, bottom);
painter.drawLine(x - 4, bottom, x + 1, bottom + 5);
}
}
}
// Draw a border
painter.setPen(this->palette().shadow().color());
painter.drawRect(0, 0, w, h);
}
void DisassemblyWidget::mousePressEvent(QMouseEvent* event)
{
const u32 selectedAddress = (event->y() / m_rowHeight * 4) + m_visibleStart;
if (event->buttons() & Qt::LeftButton)
{
if (event->modifiers() & Qt::ShiftModifier)
{
if (selectedAddress < m_selectedAddressStart)
{
m_selectedAddressStart = selectedAddress;
}
else if (selectedAddress > m_visibleStart)
{
m_selectedAddressEnd = selectedAddress;
}
}
else
{
m_selectedAddressStart = selectedAddress;
m_selectedAddressEnd = selectedAddress;
}
}
else if (event->buttons() & Qt::RightButton)
{
if (m_selectedAddressStart == m_selectedAddressEnd)
{
m_selectedAddressStart = selectedAddress;
m_selectedAddressEnd = selectedAddress;
}
}
this->repaint();
}
void DisassemblyWidget::mouseDoubleClickEvent(QMouseEvent* event)
{
const u32 selectedAddress = (event->y() / m_rowHeight * 4) + m_visibleStart;
if (CBreakPoints::IsAddressBreakPoint(m_cpu->getCpuType(), selectedAddress))
{
Host::RunOnCPUThread([&] { CBreakPoints::RemoveBreakPoint(m_cpu->getCpuType(), selectedAddress); });
}
else
{
Host::RunOnCPUThread([&] { CBreakPoints::AddBreakPoint(m_cpu->getCpuType(), selectedAddress); });
}
breakpointsChanged();
this->repaint();
}
void DisassemblyWidget::wheelEvent(QWheelEvent* event)
{
if (event->angleDelta().y() < 0) // todo: max address bounds check?
{
m_visibleStart += 4;
}
else if (event->angleDelta().y() && m_visibleStart > 0)
{
m_visibleStart -= 4;
}
this->repaint();
}
void DisassemblyWidget::keyPressEvent(QKeyEvent* event)
{
switch (event->key())
{
case Qt::Key_Up:
{
m_selectedAddressStart -= 4;
if (!(event->modifiers() & Qt::ShiftModifier))
m_selectedAddressEnd = m_selectedAddressStart;
// Auto scroll
if (m_visibleStart > m_selectedAddressStart)
m_visibleStart -= 4;
}
break;
case Qt::Key_Down:
{
m_selectedAddressEnd += 4;
if (!(event->modifiers() & Qt::ShiftModifier))
m_selectedAddressStart = m_selectedAddressEnd;
// Purposely scroll on the second to last row. It's possible to
// size the window so part of a row is visible and we don't want to have half a row selected and cut off!
if (m_visibleStart + ((m_visibleRows - 1) * 4) < m_selectedAddressEnd)
m_visibleStart += 4;
}
break;
case Qt::Key_G:
contextGoToAddress();
break;
case Qt::Key_C:
contextCopyInstructionText();
break;
case Qt::Key_B:
contextToggleBreakpoint();
break;
case Qt::Key_M:
contextAssembleInstruction();
break;
case Qt::Key_Right:
contextFollowBranch();
break;
case Qt::Key_Left:
gotoAddress(m_cpu->getPC());
break;
}
this->repaint();
}
void DisassemblyWidget::customMenuRequested(QPoint pos)
{
// m_selectedAddressStart will be properly set as the mouse click handler is called _before_ us
// yay :)
m_contextMenu->popup(this->mapToGlobal(pos));
}
inline QString DisassemblyWidget::DisassemblyStringFromAddress(u32 address, QFont font, u32 pc)
{
DisassemblyLineInfo line;
if (!m_cpu->isValidAddress(address))
return QString("%1 NOT VALID ADDRESS").arg(address, 8, 16, QChar('0')).toUpper();
// Todo? support non symbol view?
m_disassemblyManager.getLine(address, true, line);
const bool isConditional = line.info.isConditional && m_cpu->getPC() == address;
const bool isConditionalMet = line.info.conditionMet;
const bool isCurrentPC = m_cpu->getPC() == address;
const bool isBreakpoint = CBreakPoints::IsAddressBreakPoint(m_cpu->getCpuType(), address) && !CBreakPoints::IsTempBreakPoint(m_cpu->getCpuType(), address);
const std::string addressSymbol = m_cpu->GetSymbolMap().GetLabelString(address);
QString lineString("%1 %2 %3 %4 %5 %6");
lineString = lineString.arg(isBreakpoint ? "\u25A0" : " "); // Bp block ( ■ )
if (addressSymbol.empty()) // The address wont have symbol text if it's the start of a function for example
lineString = lineString.arg(address, 8, 16, QChar('0')).toUpper();
else
{
// We want this text elided
QFontMetrics metric(font);
lineString = lineString.arg(metric.elidedText(QString::fromStdString(addressSymbol), Qt::ElideRight, 8 * font.pointSize()));
}
lineString = lineString.leftJustified(4, ' ') // Address / symbol
.arg(line.name.c_str())
.arg(line.params.c_str()) // opcode + arguments
.arg(isConditional ? (isConditionalMet ? "# true" : "# false") : "")
.arg(isCurrentPC ? "<--" : "");
return lineString;
}
QColor DisassemblyWidget::GetAddressFunctionColor(u32 address)
{
// This is an attempt to figure out if the current palette is dark or light
// We calculate the luminescence of the alternateBase colour
// and swap between our darker and lighter function colours
std::array<QColor, 6> colors;
const QColor base = this->palette().alternateBase().color();
auto Y = (base.redF() * 0.33) + (0.5 * base.greenF()) + (0.16 * base.blueF());
if (Y > 0.5)
{
colors = {
QColor::fromRgba(0xFFFA3434),
QColor::fromRgba(0xFF206b6b),
QColor::fromRgba(0xFF858534),
QColor::fromRgba(0xFF378c37),
QColor::fromRgba(0xFF783278),
QColor::fromRgba(0xFF21214a),
};
}
else
{
colors = {
QColor::fromRgba(0xFFe05555),
QColor::fromRgba(0xFF55e0e0),
QColor::fromRgba(0xFFe8e855),
QColor::fromRgba(0xFF55e055),
QColor::fromRgba(0xFFe055e0),
QColor::fromRgba(0xFFC2C2F5),
};
}
auto funNum = m_cpu->GetSymbolMap().GetFunctionNum(address);
if (funNum == -1)
return this->palette().text().color();
return colors[funNum % 6];
}
QString DisassemblyWidget::FetchSelectionInfo(SelectionInfo selInfo)
{
QString infoBlock;
for (u32 i = m_selectedAddressStart; i <= m_selectedAddressEnd; i += 4)
{
if (i != m_selectedAddressStart)
infoBlock += '\n';
if (selInfo == SelectionInfo::ADDRESS)
{
infoBlock += FilledQStringFromValue(i, 16);
}
else if (selInfo == SelectionInfo::INSTRUCTIONTEXT)
{
DisassemblyLineInfo line;
m_disassemblyManager.getLine(i, true, line);
infoBlock += QString("%1 %2").arg(line.name.c_str()).arg(line.params.c_str());
}
else // INSTRUCTIONHEX
{
infoBlock += FilledQStringFromValue(m_cpu->read32(i), 16);
}
}
return infoBlock;
}
void DisassemblyWidget::gotoAddress(u32 address)
{
const u32 destAddress = address & ~3;
// Center the address
m_visibleStart = (destAddress - (m_visibleRows * 4 / 2)) & ~3;
m_selectedAddressStart = destAddress;
m_selectedAddressEnd = destAddress;
this->repaint();
}

View File

@ -0,0 +1,97 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2022 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 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 PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "ui_DisassemblyWidget.h"
#include "pcsx2/DebugTools/DebugInterface.h"
#include "pcsx2/DebugTools/DisassemblyManager.h"
#include <QtWidgets/QWidget>
#include <QtWidgets/QMenu>
#include <QtGui/QPainter>
class DisassemblyWidget final : public QWidget
{
Q_OBJECT
public:
DisassemblyWidget(QWidget* parent);
~DisassemblyWidget();
// Required because our constructor needs to take no extra arguments.
void SetCpu(DebugInterface* cpu);
// Required for the breakpoint list (ugh wtf)
QString GetLineDisasm(u32 address);
protected:
void paintEvent(QPaintEvent* event);
void mousePressEvent(QMouseEvent* event);
void mouseDoubleClickEvent(QMouseEvent* event);
void wheelEvent(QWheelEvent* event);
void keyPressEvent(QKeyEvent* event);
public slots:
void customMenuRequested(QPoint pos);
// Context menu actions
// When called, m_selectedAddressStart will be the 'selected' instruction
// Of course, m_selectedAddressEnd will be the end of the selection when required
void contextCopyAddress();
void contextCopyInstructionHex();
void contextCopyInstructionText();
void contextAssembleInstruction();
void contextRunToCursor();
void contextJumpToCursor();
void contextToggleBreakpoint();
void contextFollowBranch();
void contextGoToAddress();
void contextAddFunction();
void contextRenameFunction();
void contextRemoveFunction();
void gotoAddress(u32 address);
signals:
void gotoInMemory(u32 address);
void breakpointsChanged();
void VMUpdate();
private:
Ui::DisassemblyWidget ui;
QMenu* m_contextMenu = 0x0;
void CreateCustomContextMenu();
DebugInterface* m_cpu;
u32 m_visibleStart = 0x00336318; // The address of the first opcode shown(row 0)
u32 m_visibleRows;
u32 m_selectedAddressStart = 0;
u32 m_selectedAddressEnd = 0;
u32 m_rowHeight = 0;
DisassemblyManager m_disassemblyManager;
inline QString DisassemblyStringFromAddress(u32 address, QFont font, u32 pc);
QColor GetAddressFunctionColor(u32 address);
enum class SelectionInfo
{
ADDRESS,
INSTRUCTIONHEX,
INSTRUCTIONTEXT,
};
QString FetchSelectionInfo(SelectionInfo selInfo);
};

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DisassemblyWidget</class>
<widget class="QWidget" name="DisassemblyWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Disassembly</string>
</property>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,497 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2022 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 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 PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "MemoryViewWidget.h"
#include "common/BitCast.h"
#include "QtHost.h"
#include "QtUtils.h"
#include <QtGui/QMouseEvent>
#include <QtCore/QObject>
#include <QtWidgets/QInputDialog>
#include <QtWidgets/QMessageBox>
#include <QtGui/QClipboard>
#include <QtCore/QtEndian>
using namespace QtUtils;
/*
MemoryViewTable
*/
void MemoryViewTable::UpdateStartAddress(u32 start)
{
startAddress = start & ~0xF;
}
void MemoryViewTable::UpdateSelectedAddress(u32 selected, bool page)
{
selectedAddress = selected;
if (startAddress > selectedAddress)
{
if (page)
startAddress -= 0x10 * rowVisible;
else
startAddress -= 0x10;
}
else if (startAddress + ((rowVisible - 1) * 0x10) < selectedAddress)
{
if (page)
startAddress += 0x10 * rowVisible;
else
startAddress += 0x10;
}
}
void MemoryViewTable::DrawTable(QPainter& painter, const QPalette& palette, s32 height)
{
rowHeight = painter.fontMetrics().height() + 2;
const s32 charWidth = painter.fontMetrics().averageCharWidth();
const s32 x = charWidth; // Left padding
const s32 y = rowHeight;
rowVisible = (height / rowHeight);
rowCount = rowVisible + 1;
row1YAxis = 0;
// Draw the row addresses
painter.setPen(palette.text().color());
for (u32 i = 0; i < rowCount; i++)
{
painter.drawText(x, y + (rowHeight * i), FilledQStringFromValue(startAddress + (i * 0x10), 16));
}
valuexAxis = x + (charWidth * 8);
// Draw the row values
for (u32 i = 0; i < rowCount; i++)
{
const u32 currentRowAddress = startAddress + (i * 0x10);
s32 valX = valuexAxis;
segmentXAxis[0] = valX;
u32 currentSegmentAddress = currentRowAddress;
for (int j = 0; j < 16; j++)
{
const u32 currentByteAddress = currentRowAddress + j;
if (!(j % (s32)displayType))
{
valX += charWidth;
currentSegmentAddress = currentByteAddress;
}
segmentXAxis[j] = valX;
if ((selectedAddress & ~0xF) == currentRowAddress)
{
if (selectedAddress == currentByteAddress)
{ // If the current byte and row we are drawing is selected
if (!selectedText)
{
painter.setPen(QColor::fromRgb(205, 165, 0)); // SELECTED NIBBLE LINE COLOUR
const QPoint lineStart(valX + (selectedNibbleHI ? 0 : charWidth) + 1, y + (rowHeight * i));
painter.drawLine(lineStart, lineStart + QPoint(charWidth - 3, 0));
}
painter.setPen(QColor::fromRgb(0xaa, 0x22, 0x22)); // SELECTED BYTE COLOUR
}
// If the current selected byte is in our current segment, highlight the entire segment
else if (displayType != MemoryViewType::BYTE &&
currentSegmentAddress <= selectedAddress && (selectedAddress <= (currentSegmentAddress + (s32)displayType - 1)))
{
painter.setPen(palette.highlight().color()); // SELECTED SEGMENT COLOUR
}
else
painter.setPen(palette.text().color()); // Default colour
}
else
painter.setPen(palette.text().color()); // Default colour
bool valid;
const u8 val = static_cast<u8>(m_cpu->read8(currentByteAddress, valid));
painter.drawText(valX, y + (rowHeight * i), valid ? FilledQStringFromValue(val, 16) : "??");
valX += charWidth * 2;
}
// valX is our new X position after the hex values
valX = valX + 6;
textXAxis = valX;
// Print the string representation
for (s32 j = 0; j < 16; j++)
{
if (selectedAddress == j + currentRowAddress)
painter.setPen(palette.highlight().color());
else
painter.setPen(palette.text().color());
bool valid;
const u8 value = m_cpu->read8(currentRowAddress + j, valid);
if (valid)
{
QChar curChar = QChar::fromLatin1(value);
if (!curChar.isPrint() && curChar != ' ') // Default to '.' for unprintable characters
curChar = '.';
painter.drawText(valX, y + (rowHeight * i), curChar);
}
else
{
painter.drawText(valX, y + (rowHeight * i), "?");
}
valX += charWidth + 1;
}
}
}
void MemoryViewTable::SelectAt(QPoint pos)
{
const u32 selectedRow = (pos.y() - 2) / (rowHeight);
const s32 x = pos.x();
const s32 avgSegmentWidth = segmentXAxis[1] - segmentXAxis[0];
selectedAddress = (selectedRow * 0x10) + startAddress;
if (x <= segmentXAxis[0])
{
selectedText = false;
// The user clicked before the first segment
selectedNibbleHI = true;
}
else if (x > valuexAxis && x < textXAxis)
{
selectedText = false;
// The user clicked inside of the hexadecimal area
for (s32 i = 0; i < 16; i++)
{
if (i == 15 || (x >= segmentXAxis[i] && x < (segmentXAxis[i + 1])))
{
selectedAddress = selectedAddress + i;
selectedNibbleHI = ((x - segmentXAxis[i]) < ((avgSegmentWidth / 2) - 2)); // Subtract 2 units, makes selecting nibbles feel more natural
break;
}
}
}
else if (x >= textXAxis)
{
selectedText = true;
// The user clicked the text area
selectedAddress += std::min((x - textXAxis) / 8, 15);
}
}
u128 MemoryViewTable::GetSelectedSegment()
{
u128 val;
switch (displayType)
{
case MemoryViewType::BYTE:
val.lo = m_cpu->read8(selectedAddress);
break;
case MemoryViewType::BYTEHW:
val.lo = qToBigEndian((u16)m_cpu->read16(selectedAddress & ~1));
break;
case MemoryViewType::WORD:
val.lo = qToBigEndian(m_cpu->read32(selectedAddress & ~3));
break;
case MemoryViewType::DWORD:
val._u64[0] = qToBigEndian(m_cpu->read64(selectedAddress & ~7));
break;
}
return val;
}
void MemoryViewTable::InsertIntoSelectedHexView(u8 value)
{
const u8 mask = selectedNibbleHI ? 0x0f : 0xf0;
u8 curVal = m_cpu->read8(selectedAddress) & mask;
u8 newVal = value << (selectedNibbleHI ? 4 : 0);
curVal |= newVal;
Host::RunOnCPUThread([this, address = selectedAddress, cpu = m_cpu, val = curVal] {
cpu->write8(address, val);
QtHost::RunOnUIThread([this] { parent->update(); });
});
}
// We need both key and keychar because `key` is easy to use, but is case insensitive
void MemoryViewTable::KeyPress(int key, QChar keychar)
{
if (!m_cpu->isValidAddress(selectedAddress))
return;
const bool keyCharIsText = keychar.isLetterOrNumber() || keychar.isSpace();
if (selectedText)
{
if (keyCharIsText || (!keychar.isNonCharacter() && keychar.category() != QChar::Other_Control))
{
Host::RunOnCPUThread([this, address = selectedAddress, cpu = m_cpu, val = keychar.toLatin1()] {
cpu->write8(address, val);
QtHost::RunOnUIThread([this] { UpdateSelectedAddress(selectedAddress + 1); parent->update(); });
});
}
switch (key)
{
case Qt::Key::Key_Backspace:
case Qt::Key::Key_Escape:
Host::RunOnCPUThread([this, address = selectedAddress, cpu = m_cpu, key] {
cpu->write8(address, 0);
QtHost::RunOnUIThread([this, key] { UpdateSelectedAddress(selectedAddress - 1); parent->update(); });
});
break;
case Qt::Key::Key_Right:
UpdateSelectedAddress(selectedAddress + 1);
break;
case Qt::Key::Key_Left:
UpdateSelectedAddress(selectedAddress - 1);
break;
}
}
else
{
// Hex view is selected
if (keyCharIsText)
{
InsertIntoSelectedHexView(((u8)QString(QChar(key)).toInt(nullptr, 16)));
// Increment to the next nibble or byte
if (selectedNibbleHI = !selectedNibbleHI)
UpdateSelectedAddress(selectedAddress + 1);
}
switch (key)
{
case Qt::Key::Key_Backspace:
case Qt::Key::Key_Escape:
InsertIntoSelectedHexView(0);
// Move back a byte or nibble if it's backspace being pressed
if (!(selectedNibbleHI = !selectedNibbleHI))
UpdateSelectedAddress(selectedAddress - 1);
break;
case Qt::Key::Key_Right:
if (selectedNibbleHI = !selectedNibbleHI)
UpdateSelectedAddress(selectedAddress + 1);
break;
case Qt::Key::Key_Left:
if (!(selectedNibbleHI = !selectedNibbleHI))
UpdateSelectedAddress(selectedAddress - 1);
break;
}
}
// Keybinds that are the same for the text and hex view
switch (key)
{
case Qt::Key::Key_Up:
UpdateSelectedAddress(selectedAddress - 0x10);
break;
case Qt::Key::Key_PageUp:
UpdateSelectedAddress(selectedAddress - (0x10 * rowVisible), true);
break;
case Qt::Key::Key_Down:
UpdateSelectedAddress(selectedAddress + 0x10);
break;
case Qt::Key::Key_PageDown:
UpdateSelectedAddress(selectedAddress + (0x10 * rowVisible), true);
break;
}
}
/*
MemoryViewWidget
*/
MemoryViewWidget::MemoryViewWidget(QWidget* parent)
: QWidget(parent)
, m_table(this)
{
ui.setupUi(this);
this->setFocusPolicy(Qt::FocusPolicy::ClickFocus);
connect(this, &MemoryViewWidget::customContextMenuRequested, this, &MemoryViewWidget::customMenuRequested);
}
MemoryViewWidget::~MemoryViewWidget() = default;
void MemoryViewWidget::SetCpu(DebugInterface* cpu)
{
m_cpu = cpu;
m_table.SetCpu(cpu);
m_table.UpdateStartAddress(0x480000);
}
void MemoryViewWidget::paintEvent(QPaintEvent* event)
{
if (!m_cpu->isAlive())
return;
QPainter painter(this);
m_table.DrawTable(painter, this->palette(), this->height());
}
void MemoryViewWidget::mousePressEvent(QMouseEvent* event)
{
m_table.SelectAt(event->pos());
repaint();
}
void MemoryViewWidget::customMenuRequested(QPoint pos)
{
if (!m_contextMenu)
{
m_contextMenu = new QMenu(this);
QAction* action = new QAction("Go to in disassembly");
m_contextMenu->addAction(action);
connect(action, &QAction::triggered, this, [this]() { emit gotoInDisasm(m_table.selectedAddress); });
action = new QAction("Go to address");
m_contextMenu->addAction(action);
connect(action, &QAction::triggered, this, [this]() { contextGoToAddress(); });
m_contextMenu->addSeparator();
// View Types
m_actionBYTE = new QAction("Show as 1 byte");
m_actionBYTE->setCheckable(true);
m_contextMenu->addAction(m_actionBYTE);
connect(m_actionBYTE, &QAction::triggered, this, [this]() { m_table.SetViewType(MemoryViewType::BYTE); });
m_actionBYTEHW = new QAction("Show as 2 bytes");
m_actionBYTEHW->setCheckable(true);
m_contextMenu->addAction(m_actionBYTEHW);
connect(m_actionBYTEHW, &QAction::triggered, this, [this]() { m_table.SetViewType(MemoryViewType::BYTEHW); });
m_actionWORD = new QAction("Show as 4 bytes");
m_actionWORD->setCheckable(true);
m_contextMenu->addAction(m_actionWORD);
connect(m_actionWORD, &QAction::triggered, this, [this]() { m_table.SetViewType(MemoryViewType::WORD); });
m_actionDWORD = new QAction("Show as 8 bytes");
m_actionDWORD->setCheckable(true);
m_contextMenu->addAction(m_actionDWORD);
connect(m_actionDWORD, &QAction::triggered, this, [this]() { m_table.SetViewType(MemoryViewType::DWORD); });
m_contextMenu->addSeparator();
action = new QAction("Copy Byte");
m_contextMenu->addAction(action);
connect(action, &QAction::triggered, this, [this]() { contextCopyByte(); });
action = new QAction("Copy Segment");
m_contextMenu->addAction(action);
connect(action, &QAction::triggered, this, [this]() { contextCopySegment(); });
action = new QAction("Copy Character");
m_contextMenu->addAction(action);
connect(action, &QAction::triggered, this, [this]() { contextCopyCharacter(); });
}
const MemoryViewType currentViewType = m_table.GetViewType();
m_actionBYTE->setChecked(currentViewType == MemoryViewType::BYTE);
m_actionBYTEHW->setChecked(currentViewType == MemoryViewType::BYTEHW);
m_actionWORD->setChecked(currentViewType == MemoryViewType::WORD);
m_actionDWORD->setChecked(currentViewType == MemoryViewType::DWORD);
m_contextMenu->popup(this->mapToGlobal(pos));
this->repaint();
return;
}
void MemoryViewWidget::contextCopyByte()
{
QApplication::clipboard()->setText(QString::number(m_cpu->read8(m_table.selectedAddress), 16).toUpper());
}
void MemoryViewWidget::contextCopySegment()
{
QApplication::clipboard()->setText(QString::number(m_table.GetSelectedSegment().lo, 16).toUpper());
}
void MemoryViewWidget::contextCopyCharacter()
{
QApplication::clipboard()->setText(QChar::fromLatin1(m_cpu->read8(m_table.selectedAddress)).toUpper());
}
void MemoryViewWidget::contextGoToAddress()
{
bool ok;
QString targetString = QInputDialog::getText(this, "Go to address", "",
QLineEdit::Normal, "", &ok);
if (!ok)
return;
const u32 targetAddress = targetString.toUInt(&ok, 16);
if (!ok)
{
QMessageBox::warning(this, "Go to address error", "Invalid address");
return;
}
gotoAddress(targetAddress);
}
void MemoryViewWidget::mouseDoubleClickEvent(QMouseEvent* event)
{
}
void MemoryViewWidget::wheelEvent(QWheelEvent* event)
{
if (event->angleDelta().y() < 0)
{
m_table.UpdateStartAddress(m_table.startAddress + 0x10);
}
else if (event->angleDelta().y() > 0)
{
m_table.UpdateStartAddress(m_table.startAddress - 0x10);
}
this->repaint();
}
void MemoryViewWidget::keyPressEvent(QKeyEvent* event)
{
bool handledByWidget = true;
switch (event->key())
{
case Qt::Key_G:
contextGoToAddress();
break;
case Qt::Key_C:
if (event->modifiers() & Qt::ControlModifier)
contextCopySegment();
else
handledByWidget = false;
break;
default:
handledByWidget = false;
break;
}
if (!handledByWidget)
m_table.KeyPress(event->key(), event->text().size() ? event->text()[0] : '\0');
this->repaint();
VMUpdate();
}
void MemoryViewWidget::gotoAddress(u32 address)
{
m_table.UpdateStartAddress(address & ~0xF);
m_table.selectedAddress = address;
this->repaint();
}

View File

@ -0,0 +1,129 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2022 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 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 PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "ui_RegisterWidget.h"
#include "DebugTools/DebugInterface.h"
#include "DebugTools/DisassemblyManager.h"
#include <QtWidgets/QWidget>
#include <QtWidgets/QMenu>
#include <QtWidgets/QTabBar>
#include <QtGui/QPainter>
#include <vector>
enum class MemoryViewType
{
BYTE = 1,
BYTEHW = 2,
WORD = 4,
DWORD = 8,
};
class MemoryViewTable
{
QWidget* parent;
DebugInterface* m_cpu;
MemoryViewType displayType = MemoryViewType::BYTE;
u32 rowCount;
u32 rowVisible;
s32 rowHeight;
// Stuff used for selection handling
// This gets set every paint and depends on the window size / current display mode (1byte,2byte,etc)
s32 valuexAxis; // Where the hexadecimal view begins
s32 textXAxis; // Where the text view begins
s32 row1YAxis; // Where the first row starts
s32 segmentXAxis[16]; // Where the segments begin
bool selectedText = false; // Whether the user has clicked on text or hex
bool selectedNibbleHI = false;
void InsertIntoSelectedHexView(u8 value);
public:
MemoryViewTable(QWidget* parent)
: parent(parent){};
u32 startAddress;
u32 selectedAddress;
void SetCpu(DebugInterface* cpu)
{
m_cpu = cpu;
}
void UpdateStartAddress(u32 start);
void UpdateSelectedAddress(u32 selected, bool page = false);
void DrawTable(QPainter& painter, const QPalette& palette, s32 height);
void SelectAt(QPoint pos);
u128 GetSelectedSegment();
void KeyPress(int key, QChar keychar);
MemoryViewType GetViewType()
{
return displayType;
}
void SetViewType(MemoryViewType viewType)
{
displayType = viewType;
}
};
class MemoryViewWidget final : public QWidget
{
Q_OBJECT
public:
MemoryViewWidget(QWidget* parent);
~MemoryViewWidget();
void SetCpu(DebugInterface* cpu);
protected:
void paintEvent(QPaintEvent* event);
void mousePressEvent(QMouseEvent* event);
void mouseDoubleClickEvent(QMouseEvent* event);
void wheelEvent(QWheelEvent* event);
void keyPressEvent(QKeyEvent* event);
public slots:
void customMenuRequested(QPoint pos);
void contextGoToAddress();
void contextCopyByte();
void contextCopySegment();
void contextCopyCharacter();
void gotoAddress(u32 address);
signals:
void gotoInDisasm(u32 address);
void VMUpdate();
private:
Ui::RegisterWidget ui;
QMenu* m_contextMenu = 0x0;
QAction* m_actionBYTE;
QAction* m_actionBYTEHW;
QAction* m_actionWORD;
QAction* m_actionDWORD;
DebugInterface* m_cpu;
MemoryViewTable m_table;
};

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MemoryViewWidget</class>
<widget class="QWidget" name="MemoryViewWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Memory</string>
</property>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,446 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2022 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 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 PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "RegisterWidget.h"
#include "common/BitCast.h"
#include "QtUtils.h"
#include <QtGui/QMouseEvent>
#include <QtWidgets/QTabBar>
#include <QtWidgets/QStylePainter>
#include <QtWidgets/QStyleOptionTab>
#include <QtGui/QClipboard>
#include <QtWidgets/QInputDialog>
#include <QtWidgets/QProxyStyle>
#include <QtWidgets/QMessageBox>
#include <algorithm>
#define CAT_SHOW_FLOAT (categoryIndex == EECAT_FPR && m_showFPRFloat) || (categoryIndex == EECAT_VU0F && m_showVU0FFloat)
using namespace QtUtils;
RegisterWidget::RegisterWidget(QWidget* parent)
: QWidget(parent)
{
this->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);
ui.setupUi(this);
ui.registerTabs->setDrawBase(false);
connect(this, &RegisterWidget::customContextMenuRequested, this, &RegisterWidget::customMenuRequested);
connect(ui.registerTabs, &QTabBar::currentChanged, this, &RegisterWidget::tabCurrentChanged);
};
RegisterWidget::~RegisterWidget()
{
}
void RegisterWidget::SetCpu(DebugInterface* cpu)
{
m_cpu = cpu;
for (int i = 0; i < m_cpu->getRegisterCategoryCount(); i++)
{
ui.registerTabs->addTab(m_cpu->getRegisterCategoryName(i));
}
connect(ui.registerTabs, &QTabBar::currentChanged, [this]() { this->repaint(); });
}
void RegisterWidget::tabCurrentChanged(int cur)
{
m_rowStart = 0;
}
void RegisterWidget::paintEvent(QPaintEvent* event)
{
if (!m_cpu)
return;
QPainter painter(this);
painter.setPen(this->palette().text().color());
m_renderStart = QPoint(0, ui.registerTabs->pos().y() + ui.registerTabs->size().height());
const QSize renderSize = QSize(this->size().width(), this->size().height() - ui.registerTabs->size().height());
m_rowHeight = painter.fontMetrics().height() + 2;
m_rowEnd = m_rowStart + (renderSize.height() / m_rowHeight) - 1; // Maybe move this to a onsize event
bool alternate = m_rowStart % 2;
const int categoryIndex = ui.registerTabs->currentIndex();
// Used for 128 bit and VU0f registers
const int titleStartX = m_renderStart.x() + (painter.fontMetrics().averageCharWidth() * 6);
m_fieldWidth = ((renderSize.width() - (painter.fontMetrics().averageCharWidth() * 6)) / 4);
m_fieldStartX[0] = titleStartX;
m_fieldStartX[1] = titleStartX + m_fieldWidth;
m_fieldStartX[2] = titleStartX + (m_fieldWidth * 2);
m_fieldStartX[3] = titleStartX + (m_fieldWidth * 3);
if (categoryIndex == EECAT_VU0F)
{
painter.fillRect(m_renderStart.x(), m_renderStart.y(), renderSize.width(), m_rowHeight, this->palette().highlight());
painter.drawText(m_fieldStartX[0], m_renderStart.y(), m_fieldWidth, m_rowHeight, Qt::AlignLeft, "W");
painter.drawText(m_fieldStartX[1], m_renderStart.y(), m_fieldWidth, m_rowHeight, Qt::AlignLeft, "Z");
painter.drawText(m_fieldStartX[2], m_renderStart.y(), m_fieldWidth, m_rowHeight, Qt::AlignLeft, "Y");
painter.drawText(m_fieldStartX[3], m_renderStart.y(), m_fieldWidth, m_rowHeight, Qt::AlignLeft, "X");
m_renderStart += QPoint(0, m_rowHeight); // Make room for VU0f titles
}
// Find the longest register name and calculate where to place our values
// off of that.
// Can probably constexpr the loop out as register names are known during runtime
int safeValueStartX = 0;
for (int i = 0; i < m_cpu->getRegisterCount(categoryIndex); i++)
{
const int registerNameWidth = strlen(m_cpu->getRegisterName(categoryIndex, i));
if (safeValueStartX < registerNameWidth)
{
safeValueStartX = registerNameWidth;
}
}
// Add a space between the value and name
safeValueStartX += 2;
// Convert to width in pixels
safeValueStartX *= painter.fontMetrics().averageCharWidth();
// Make it relative to where we start rendering
safeValueStartX += m_renderStart.x();
for (s32 i = 0; i < m_cpu->getRegisterCount(categoryIndex) - m_rowStart; i++)
{
const s32 registerIndex = i + m_rowStart;
const int yStart = (i * m_rowHeight) + m_renderStart.y();
painter.fillRect(m_renderStart.x(), yStart, renderSize.width(), m_rowHeight, alternate ? this->palette().base() : this->palette().alternateBase());
alternate = !alternate;
// Draw register name
painter.setPen(this->palette().text().color());
painter.drawText(m_renderStart.x() + painter.fontMetrics().averageCharWidth(), yStart, renderSize.width(), m_rowHeight, Qt::AlignLeft, m_cpu->getRegisterName(categoryIndex, registerIndex));
if (m_cpu->getRegisterSize(categoryIndex) == 128)
{
const u128 curRegister = m_cpu->getRegister(categoryIndex, registerIndex);
int regIndex = 3;
for (int j = 0; j < 4; j++)
{
if (m_selectedRow == registerIndex && m_selected128Field == j)
painter.setPen(this->palette().highlight().color());
else
painter.setPen(this->palette().text().color());
if (categoryIndex == EECAT_VU0F && m_showVU0FFloat)
painter.drawText(m_fieldStartX[j], yStart, m_fieldWidth, m_rowHeight, Qt::AlignLeft,
painter.fontMetrics().elidedText(QString::number(bit_cast<float>(m_cpu->getRegister(categoryIndex, registerIndex)._u32[regIndex])), Qt::ElideRight, m_fieldWidth - painter.fontMetrics().averageCharWidth()));
else
painter.drawText(m_fieldStartX[j], yStart, m_fieldWidth, m_rowHeight,
Qt::AlignLeft, FilledQStringFromValue(curRegister._u32[regIndex], 16));
regIndex--;
}
painter.setPen(this->palette().text().color());
}
else
{
if (m_selectedRow == registerIndex)
painter.setPen(this->palette().highlight().color());
else
painter.setPen(this->palette().text().color());
if (categoryIndex == EECAT_FPR && m_showFPRFloat)
painter.drawText(safeValueStartX, yStart, renderSize.width(), m_rowHeight, Qt::AlignLeft,
QString("%1").arg(QString::number(bit_cast<float>(m_cpu->getRegister(categoryIndex, registerIndex)._u32[0]))).toUpper());
else if (m_cpu->getRegisterSize(categoryIndex) == 64)
painter.drawText(safeValueStartX, yStart, renderSize.width(), m_rowHeight, Qt::AlignLeft,
FilledQStringFromValue(m_cpu->getRegister(categoryIndex, registerIndex).lo, 16));
else
painter.drawText(safeValueStartX, yStart, renderSize.width(), m_rowHeight, Qt::AlignLeft,
FilledQStringFromValue(m_cpu->getRegister(categoryIndex, registerIndex)._u32[0], 16));
}
}
painter.end();
}
void RegisterWidget::mousePressEvent(QMouseEvent* event)
{
const int categoryIndex = ui.registerTabs->currentIndex();
m_selectedRow = ((event->y() - m_renderStart.y()) / m_rowHeight) + m_rowStart;
// For 128 bit types, support selecting segments
if (m_cpu->getRegisterSize(categoryIndex) == 128)
{
constexpr auto inRange = [](u32 low, u32 high, u32 val) {
return (low <= val && val <= high);
};
for (int i = 0; i < 4; i++)
{
if (inRange(m_fieldStartX[i], m_fieldStartX[i] + m_fieldWidth, event->x()))
{
m_selected128Field = i;
}
}
}
this->repaint();
}
void RegisterWidget::wheelEvent(QWheelEvent* event)
{
if (event->angleDelta().y() < 0 && m_rowEnd < m_cpu->getRegisterCount(ui.registerTabs->currentIndex()))
{
m_rowStart += 1;
}
else if (event->angleDelta().y() > 0 && m_rowStart > 0)
{
m_rowStart -= 1;
}
this->repaint();
}
void RegisterWidget::customMenuRequested(QPoint pos)
{
if (m_selectedRow > m_rowEnd) // Unsigned underflow; selectedRow will be > m_rowEnd (technically negative)
return;
// Unlike the disassembly widget, we need to create a new context menu every time
// we show it. Because some register groups are special
if (!m_contextMenu)
m_contextMenu = new QMenu(this);
else
m_contextMenu->clear();
int categoryIndex = ui.registerTabs->currentIndex();
QAction* action = 0;
if (categoryIndex == EECAT_FPR)
{
m_contextMenu->addAction(action = new QAction(m_showFPRFloat ? "View as hex" : "View as float"));
connect(action, &QAction::triggered, this, [this]() { m_showFPRFloat = !m_showFPRFloat; });
m_contextMenu->addSeparator();
}
if (categoryIndex == EECAT_VU0F)
{
m_contextMenu->addAction(action = new QAction(m_showVU0FFloat ? "View as hex" : "View as float"));
connect(action, &QAction::triggered, this, [this]() { m_showVU0FFloat = !m_showVU0FFloat; });
m_contextMenu->addSeparator();
}
if (m_cpu->getRegisterSize(categoryIndex) == 128)
{
m_contextMenu->addAction(action = new QAction("Copy Top Half", this));
connect(action, &QAction::triggered, this, &RegisterWidget::contextCopyTop);
m_contextMenu->addAction(action = new QAction("Copy Bottom Half", this));
connect(action, &QAction::triggered, this, &RegisterWidget::contextCopyBottom);
m_contextMenu->addAction(action = new QAction("Copy Segment", this));
connect(action, &QAction::triggered, this, &RegisterWidget::contextCopySegment);
}
else
{
m_contextMenu->addAction(action = new QAction("Copy Value", this));
connect(action, &QAction::triggered, this, &RegisterWidget::contextCopyValue);
}
m_contextMenu->addSeparator();
if (m_cpu->getRegisterSize(categoryIndex) == 128)
{
m_contextMenu->addAction(action = new QAction("Change Top Half", this));
connect(action, &QAction::triggered, this, &RegisterWidget::contextChangeTop);
m_contextMenu->addAction(action = new QAction("Change Bottom Half", this));
connect(action, &QAction::triggered, this, &RegisterWidget::contextChangeBottom);
m_contextMenu->addAction(action = new QAction("Change Segment", this));
connect(action, &QAction::triggered, this, &RegisterWidget::contextChangeSegment);
}
else
{
m_contextMenu->addAction(action = new QAction("Change Value", this));
connect(action, &QAction::triggered, this, &RegisterWidget::contextChangeValue);
}
m_contextMenu->addSeparator();
m_contextMenu->addAction(action = new QAction("Go to in Disassembly", this));
connect(action, &QAction::triggered, this, &RegisterWidget::contextGotoDisasm);
m_contextMenu->addAction(action = new QAction("Go to in Memory", this));
connect(action, &QAction::triggered, this, &RegisterWidget::contextGotoMemory);
m_contextMenu->popup(this->mapToGlobal(pos));
}
void RegisterWidget::contextCopyValue()
{
const int categoryIndex = ui.registerTabs->currentIndex();
const u128 val = m_cpu->getRegister(categoryIndex, m_selectedRow);
if (CAT_SHOW_FLOAT)
QApplication::clipboard()->setText(QString("%1").arg(QString::number(bit_cast<float>(val._u32[0])).toUpper(), 16));
else
QApplication::clipboard()->setText(QString("%1").arg(QString::number(val._u64[0], 16).toUpper(), 16));
}
void RegisterWidget::contextCopyTop()
{
const int categoryIndex = ui.registerTabs->currentIndex();
const u128 val = m_cpu->getRegister(categoryIndex, m_selectedRow);
QApplication::clipboard()->setText(FilledQStringFromValue(val.hi, 16));
}
void RegisterWidget::contextCopyBottom()
{
const int categoryIndex = ui.registerTabs->currentIndex();
const u128 val = m_cpu->getRegister(categoryIndex, m_selectedRow);
QApplication::clipboard()->setText(FilledQStringFromValue(val.lo, 16));
}
void RegisterWidget::contextCopySegment()
{
const int categoryIndex = ui.registerTabs->currentIndex();
const u128 val = m_cpu->getRegister(categoryIndex, m_selectedRow);
if (CAT_SHOW_FLOAT)
QApplication::clipboard()->setText(FilledQStringFromValue(bit_cast<float>(val._u32[3 - m_selected128Field]), 10));
else
QApplication::clipboard()->setText(FilledQStringFromValue(val._u32[3 - m_selected128Field], 16));
}
bool RegisterWidget::contextFetchNewValue(u64& out, u64 currentValue, bool segment)
{
const int categoryIndex = ui.registerTabs->currentIndex();
const bool floatingPoint = CAT_SHOW_FLOAT && segment;
const int regSize = m_cpu->getRegisterSize(categoryIndex);
bool ok = false;
QString existingValue("%1");
if (!floatingPoint)
existingValue = existingValue.arg(currentValue, regSize == 64 ? 16 : 8, 16, QChar('0'));
else
existingValue = existingValue.arg(bit_cast<float>((u32)currentValue));
QString input = QInputDialog::getText(this, QString("Change %1").arg(m_cpu->getRegisterName(categoryIndex, m_selectedRow)), "",
QLineEdit::Normal, existingValue, &ok);
if (!ok)
return false;
if (!floatingPoint) // Get input as hexadecimal
{
out = input.toULongLong(&ok, 16);
if (!ok)
{
QMessageBox::warning(this, "Invalid register value", "Invalid hexadecimal register value.");
return false;
}
}
else
{
out = bit_cast<u32>(input.toFloat(&ok));
if (!ok)
{
QMessageBox::warning(this, "Invalid register value", "Invalid floating-point register value.");
return false;
}
}
return true;
}
void RegisterWidget::contextChangeValue()
{
const int categoryIndex = ui.registerTabs->currentIndex();
u64 newVal;
if (contextFetchNewValue(newVal, m_cpu->getRegister(categoryIndex, m_selectedRow).lo))
{
m_cpu->setRegister(categoryIndex, m_selectedRow, u128::From64(newVal));
VMUpdate();
}
}
void RegisterWidget::contextChangeTop()
{
u64 newVal;
u128 oldVal = m_cpu->getRegister(ui.registerTabs->currentIndex(), m_selectedRow);
if (contextFetchNewValue(newVal, oldVal.hi))
{
oldVal.hi = newVal;
m_cpu->setRegister(ui.registerTabs->currentIndex(), m_selectedRow, oldVal);
VMUpdate();
}
}
void RegisterWidget::contextChangeBottom()
{
u64 newVal;
u128 oldVal = m_cpu->getRegister(ui.registerTabs->currentIndex(), m_selectedRow);
if (contextFetchNewValue(newVal, oldVal.lo))
{
oldVal.lo = newVal;
m_cpu->setRegister(ui.registerTabs->currentIndex(), m_selectedRow, oldVal);
VMUpdate();
}
}
void RegisterWidget::contextChangeSegment()
{
u64 newVal;
u128 oldVal = m_cpu->getRegister(ui.registerTabs->currentIndex(), m_selectedRow);
if (contextFetchNewValue(newVal, oldVal._u32[3 - m_selected128Field], true))
{
oldVal._u32[3 - m_selected128Field] = (u32)newVal;
m_cpu->setRegister(ui.registerTabs->currentIndex(), m_selectedRow, oldVal);
VMUpdate();
}
}
void RegisterWidget::contextGotoDisasm()
{
const int categoryIndex = ui.registerTabs->currentIndex();
u128 regVal = m_cpu->getRegister(categoryIndex, m_selectedRow);
u32 addr = 0;
if (m_cpu->getRegisterSize(categoryIndex) == 128)
addr = regVal._u32[3 - m_selected128Field];
else
addr = regVal._u32[0];
if (m_cpu->isValidAddress(addr))
gotoInDisasm(addr);
else
QMessageBox::warning(this, "Invalid target address", "This register holds an invalid address.");
}
void RegisterWidget::contextGotoMemory()
{
const int categoryIndex = ui.registerTabs->currentIndex();
u128 regVal = m_cpu->getRegister(categoryIndex, m_selectedRow);
u32 addr = 0;
if (m_cpu->getRegisterSize(categoryIndex) == 128)
addr = regVal._u32[3 - m_selected128Field];
else
addr = regVal._u32[0];
gotoInMemory(addr);
}

View File

@ -0,0 +1,91 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2022 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 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 PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "ui_RegisterWidget.h"
#include "DebugTools/DebugInterface.h"
#include "DebugTools/DisassemblyManager.h"
#include <QtWidgets/QWidget>
#include <QtWidgets/QMenu>
#include <QtWidgets/QTabBar>
#include <QtGui/QPainter>
class RegisterWidget final : public QWidget
{
Q_OBJECT
public:
RegisterWidget(QWidget* parent);
~RegisterWidget();
void SetCpu(DebugInterface* cpu);
protected:
void paintEvent(QPaintEvent* event);
void mousePressEvent(QMouseEvent* event);
void wheelEvent(QWheelEvent* event);
public slots:
void customMenuRequested(QPoint pos);
void contextCopyValue();
void contextCopyTop();
void contextCopyBottom();
void contextCopySegment();
void contextChangeValue();
void contextChangeTop();
void contextChangeBottom();
void contextChangeSegment();
void contextGotoDisasm();
void contextGotoMemory();
void tabCurrentChanged(int cur);
signals:
void gotoInDisasm(u32 address);
void gotoInMemory(u32 address);
void VMUpdate();
private:
Ui::RegisterWidget ui;
QMenu* m_contextMenu = 0x0;
// Returns true on success
bool contextFetchNewValue(u64& out, u64 currentValue, bool segment = false);
DebugInterface* m_cpu;
// Used for the height offset the tab bar creates
// because we share a widget
QPoint m_renderStart;
s32 m_rowStart = 0; // Index, 0 -> VF00, 1 -> VF01 etc
s32 m_rowEnd; // Index, what register is the last one drawn
s32 m_rowHeight; // The height of each register row
// Used for mouse clicks
s32 m_fieldStartX[4]; // Where the register segments start
s32 m_fieldWidth; // How wide the register segments are
s32 m_selectedRow = 0; // Index
s32 m_selected128Field = 0; // Values are from 0 to 3
// TODO: Save this configuration ??
bool m_showVU0FFloat;
bool m_showFPRFloat;
};

View File

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>RegisterWidget</class>
<widget class="QWidget" name="RegisterWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>1</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>325</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string>Register View</string>
</property>
<widget class="QWidget" name="horizontalLayoutWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>411</width>
<height>301</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<item alignment="Qt::AlignLeft|Qt::AlignTop">
<widget class="QTabBar" name="registerTabs" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>QTabBar</class>
<extends>QWidget</extends>
<header>qtabbar.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -426,6 +426,7 @@ void MainWindow::connectVMThreadSignals(EmuThread* thread)
connect(m_ui.actionPause, &QAction::toggled, thread, &EmuThread::setVMPaused);
connect(m_ui.actionFullscreen, &QAction::triggered, thread, &EmuThread::toggleFullscreen);
connect(m_ui.actionToggleSoftwareRendering, &QAction::triggered, thread, &EmuThread::toggleSoftwareRendering);
connect(m_ui.actionDebugger, &QAction::triggered, this, &MainWindow::openDebugger);
connect(m_ui.actionReloadPatches, &QAction::triggered, thread, &EmuThread::reloadPatches);
static constexpr GSRendererType renderers[] = {
@ -2414,6 +2415,20 @@ void MainWindow::doSettings(const char* category /* = nullptr */)
dlg->setCategory(category);
}
DebuggerWindow* MainWindow::getDebuggerWindow()
{
if (!m_debugger_window)
m_debugger_window = new DebuggerWindow(this);
return m_debugger_window;
}
void MainWindow::openDebugger()
{
DebuggerWindow* dwnd = getDebuggerWindow();
dwnd->isVisible() ? dwnd->hide() : dwnd->show();
}
ControllerSettingsDialog* MainWindow::getControllerSettingsDialog()
{
if (!m_controller_settings_dialog)

View File

@ -25,6 +25,7 @@
#include "Tools/InputRecording/InputRecordingViewer.h"
#include "Settings/ControllerSettingsDialog.h"
#include "Settings/SettingsDialog.h"
#include "Debugger/DebuggerWindow.h"
#include "ui_MainWindow.h"
class QProgressBar;
@ -242,6 +243,9 @@ private:
InputRecordingViewer* getInputRecordingViewer();
void updateInputRecordingActions(bool started);
DebuggerWindow* getDebuggerWindow();
void openDebugger();
ControllerSettingsDialog* getControllerSettingsDialog();
void doControllerSettings(ControllerSettingsDialog::Category category = ControllerSettingsDialog::Category::Count);
@ -272,6 +276,8 @@ private:
ControllerSettingsDialog* m_controller_settings_dialog = nullptr;
AutoUpdaterDialog* m_auto_updater_dialog = nullptr;
DebuggerWindow* m_debugger_window = nullptr;
QProgressBar* m_status_progress_widget = nullptr;
QLabel* m_status_verbose_widget = nullptr;
QLabel* m_status_renderer_widget = nullptr;

View File

@ -127,6 +127,8 @@
</widget>
<addaction name="menuDebugSwitchRenderer"/>
<addaction name="separator"/>
<addaction name="actionDebugger"/>
<addaction name="separator"/>
<addaction name="actionEnableLogTimestamps"/>
<addaction name="separator"/>
<addaction name="actionEnableEEConsoleLogging"/>
@ -720,6 +722,11 @@
<string>Toggle Software Rendering</string>
</property>
</action>
<action name="actionDebugger">
<property name="text">
<string>Open Debugger</string>
</property>
</action>
<action name="actionReloadPatches">
<property name="text">
<string>Reload Cheats/Patches</string>

View File

@ -86,4 +86,11 @@ namespace QtUtils
/// Returns the common window info structure for a Qt widget.
std::optional<WindowInfo> GetWindowInfoForWidget(QWidget* widget);
/// Converts a value to a QString of said value with a proper fixed width
template <typename T>
QString FilledQStringFromValue(T val, u32 base)
{
return QString("%1").arg(QString::number(val, base), sizeof(val) * 2, '0').toUpper();
};
} // namespace QtUtils

View File

@ -43,10 +43,11 @@
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\glad\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\simpleini\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\lzma\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\demangler\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\rapidyaml\rapidyaml\ext\c4core\src\c4\ext\fast_float\include;%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ProjectDir);$(SolutionDir)pcsx2;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<!-- Needed for moc pch -->
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(ProjectDir)\Settings;$(ProjectDir)\GameList;$(ProjectDir)\Tools\InputRecording</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(ProjectDir)\Settings;$(ProjectDir)\GameList;$(ProjectDir)\Tools\InputRecording;$(ProjectDir)\Debugger</AdditionalIncludeDirectories>
<ExceptionHandling>Async</ExceptionHandling>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>PrecompiledHeader.h</PrecompiledHeaderFile>
@ -125,6 +126,9 @@
<ProjectReference Include="..\3rdparty\zydis\zydis.vcxproj">
<Project>{67d0160c-0fe4-44b9-ac2e-82bbcf4104df}</Project>
</ProjectReference>
<ProjectReference Include="..\3rdparty\demangler\demangler.vcxproj">
<Project>{1e3d706c-1d95-4e1b-bdf2-ca3d0007df7f}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Manifest Include="..\pcsx2\windows\PCSX2.manifest" />
@ -137,6 +141,12 @@
<ClCompile Include="Settings\MemoryCardConvertWorker.cpp" />
<ClCompile Include="Tools\InputRecording\InputRecordingViewer.cpp" />
<ClCompile Include="Tools\InputRecording\NewInputRecordingDlg.cpp" />
<ClCompile Include="Debugger\CpuWidget.cpp" />
<ClCompile Include="Debugger\DebuggerWindow.cpp" />
<ClCompile Include="Debugger\DisassemblyWidget.cpp" />
<ClCompile Include="Debugger\MemoryViewWidget.cpp" />
<ClCompile Include="Debugger\RegisterWidget.cpp" />
<ClCompile Include="Debugger\BreakpointDialog.cpp" />
<ClCompile Include="Settings\BIOSSettingsWidget.cpp" />
<ClCompile Include="Settings\ControllerBindingWidgets.cpp" />
<ClCompile Include="Settings\ControllerGlobalSettingsWidget.cpp" />
@ -211,6 +221,12 @@
<QtMoc Include="Tools\InputRecording\NewInputRecordingDlg.h" />
<QtMoc Include="Tools\InputRecording\InputRecordingViewer.h" />
<ClInclude Include="QtUtils.h" />
<QtMoc Include="Debugger\CpuWidget.h" />
<QtMoc Include="Debugger\DebuggerWindow.h" />
<QtMoc Include="Debugger\DisassemblyWidget.h" />
<QtMoc Include="Debugger\MemoryViewWidget.h" />
<QtMoc Include="Debugger\RegisterWidget.h" />
<QtMoc Include="Debugger\BreakpointDialog.h" />
<QtMoc Include="Settings\ControllerBindingWidgets.h" />
<QtMoc Include="Settings\ControllerGlobalSettingsWidget.h" />
<ClInclude Include="Settings\MemoryCardConvertWorker.h" />
@ -255,6 +271,12 @@
<ClCompile Include="$(IntDir)Settings\moc_AchievementLoginDialog.cpp" />
<ClCompile Include="$(IntDir)Settings\moc_AchievementSettingsWidget.cpp" />
<ClCompile Include="$(IntDir)Settings\moc_DebugSettingsWidget.cpp" />
<ClCompile Include="$(IntDir)Debugger\moc_DebuggerWindow.cpp" />
<ClCompile Include="$(IntDir)Debugger\moc_CpuWidget.cpp" />
<ClCompile Include="$(IntDir)Debugger\moc_DisassemblyWidget.cpp" />
<ClCompile Include="$(IntDir)Debugger\moc_RegisterWidget.cpp" />
<ClCompile Include="$(IntDir)Debugger\moc_MemoryViewWidget.cpp" />
<ClCompile Include="$(IntDir)Debugger\moc_BreakpointDialog.cpp" />
<ClCompile Include="$(IntDir)GameList\moc_GameListModel.cpp" />
<ClCompile Include="$(IntDir)GameList\moc_GameListRefreshThread.cpp" />
<ClCompile Include="$(IntDir)GameList\moc_GameListWidget.cpp" />
@ -359,6 +381,24 @@
<QtUi Include="Tools\InputRecording\InputRecordingViewer.ui">
<FileType>Document</FileType>
</QtUi>
<QtUi Include="Debugger\DebuggerWindow.ui">
<FileType>Document</FileType>
</QtUi>
<QtUi Include="Debugger\DisassemblyWidget.ui">
<FileType>Document</FileType>
</QtUi>
<QtUi Include="Debugger\CpuWidget.ui">
<FileType>Document</FileType>
</QtUi>
<QtUi Include="Debugger\RegisterWidget.ui">
<FileType>Document</FileType>
</QtUi>
<QtUi Include="Debugger\MemoryViewWidget.ui">
<FileType>Document</FileType>
</QtUi>
<QtUi Include="Debugger\BreakpointDialog.ui">
<FileType>Document</FileType>
</QtUi>
</ItemGroup>
<ItemGroup>
<QtUi Include="Settings\DebugSettingsWidget.ui">

View File

@ -19,6 +19,9 @@
<Filter Include="Tools\Input Recording">
<UniqueIdentifier>{d5c14016-9690-4e49-bcb9-a634a937951a}</UniqueIdentifier>
</Filter>
<Filter Include="Debugger">
<UniqueIdentifier>{ddb40cc4-9996-4ade-b647-eb549063553c}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\pcsx2\PCSX2.rc">
@ -256,6 +259,42 @@
<ClCompile Include="$(IntDir)Settings\moc_DebugSettingsWidget.cpp">
<Filter>moc</Filter>
</ClCompile>
<ClCompile Include="Debugger\DebuggerWindow.cpp">
<Filter>Debugger</Filter>
</ClCompile>
<ClCompile Include="Debugger\DisassemblyWidget.cpp">
<Filter>Debugger</Filter>
</ClCompile>
<ClCompile Include="Debugger\CpuWidget.cpp">
<Filter>Debugger</Filter>
</ClCompile>
<ClCompile Include="Debugger\RegisterWidget.cpp">
<Filter>Debugger</Filter>
</ClCompile>
<ClCompile Include="Debugger\MemoryViewWidget.cpp">
<Filter>Debugger</Filter>
</ClCompile>
<ClCompile Include="Debugger\BreakpointDialog.cpp">
<Filter>Debugger</Filter>
</ClCompile>
<ClCompile Include="$(IntDir)Debugger\moc_CpuWidget.cpp">
<Filter>moc</Filter>
</ClCompile>
<ClCompile Include="$(IntDir)Debugger\moc_DebuggerWindow.cpp">
<Filter>moc</Filter>
</ClCompile>
<ClCompile Include="$(IntDir)Debugger\moc_DisassemblyWidget.cpp">
<Filter>moc</Filter>
</ClCompile>
<ClCompile Include="$(IntDir)Debugger\moc_RegisterWidget.cpp">
<Filter>moc</Filter>
</ClCompile>
<ClCompile Include="$(IntDir)Debugger\moc_MemoryViewWidget.cpp">
<Filter>moc</Filter>
</ClCompile>
<ClCompile Include="$(IntDir)Debugger\moc_BreakpointDialog.cpp">
<Filter>moc</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Manifest Include="..\pcsx2\windows\PCSX2.manifest">
@ -375,6 +414,24 @@
<QtMoc Include="Settings\DebugSettingsWidget.h">
<Filter>Settings</Filter>
</QtMoc>
<QtMoc Include="Debugger\DebuggerWindow.h">
<Filter>Debugger</Filter>
</QtMoc>
<QtMoc Include="Debugger\DisassemblyWidget.h">
<Filter>Debugger</Filter>
</QtMoc>
<QtMoc Include="Debugger\CpuWidget.h">
<Filter>Debugger</Filter>
</QtMoc>
<QtMoc Include="Debugger\RegisterWidget.h">
<Filter>Debugger</Filter>
</QtMoc>
<QtMoc Include="Debugger\MemoryViewWidget.h">
<Filter>Debugger</Filter>
</QtMoc>
<QtMoc Include="Debugger\BreakpointDialog.h">
<Filter>Debugger</Filter>
</QtMoc>
</ItemGroup>
<ItemGroup>
<QtResource Include="resources\resources.qrc">
@ -475,6 +532,24 @@
<QtUi Include="Settings\DebugSettingsWidget.ui">
<Filter>Settings</Filter>
</QtUi>
<QtUi Include="Debugger\DebuggerWindow.ui">
<Filter>Debugger</Filter>
</QtUi>
<QtUi Include="Debugger\DisassemblyWidget.ui">
<Filter>Debugger</Filter>
</QtUi>
<QtUi Include="Debugger\CpuWidget.ui">
<Filter>Debugger</Filter>
</QtUi>
<QtUi Include="Debugger\RegisterWidget.ui">
<Filter>Debugger</Filter>
</QtUi>
<QtUi Include="Debugger\MemoryViewWidget.ui">
<Filter>Debugger</Filter>
</QtUi>
<QtUi Include="Debugger\BreakpointDialog.ui">
<Filter>Debugger</Filter>
</QtUi>
</ItemGroup>
<ItemGroup>
<None Include="Settings\FolderSettingsWidget.ui">

View File

@ -1625,6 +1625,7 @@ target_link_libraries(PCSX2_FLAGS INTERFACE
if(PCSX2_CORE)
target_link_libraries(PCSX2_FLAGS INTERFACE
simpleini
demangler
)
else()
target_link_libraries(PCSX2_FLAGS INTERFACE

View File

@ -29,6 +29,8 @@ u64 CBreakPoints::breakSkipFirstTicksIop_ = 0;
std::vector<MemCheck> CBreakPoints::memChecks_;
std::vector<MemCheck *> CBreakPoints::cleanupMemChecks_;
bool CBreakPoints::breakpointTriggered_ = false;
bool CBreakPoints::corePaused = false;
std::function<void()> CBreakPoints::cb_bpUpdated_;
// called from the dynarec
u32 standardizeBreakpointAddress(u32 addr)
@ -408,6 +410,7 @@ void CBreakPoints::Update(BreakPointCpu cpu, u32 addr)
bool resume = false;
if (!r5900Debug.isCpuPaused())
{
corePaused = true; // This will be set to false in whatever handles the VM pause event
r5900Debug.pauseCpu();
resume = true;
}
@ -424,5 +427,8 @@ void CBreakPoints::Update(BreakPointCpu cpu, u32 addr)
auto disassembly_window = wxGetApp().GetDisassemblyPtr();
if (disassembly_window) // make sure that valid pointer is recieved to prevent potential NULL dereference.
disassembly_window->update();
#else
if(cb_bpUpdated_)
cb_bpUpdated_();
#endif
}

View File

@ -150,6 +150,13 @@ public:
static void SetBreakpointTriggered(bool b) { breakpointTriggered_ = b; };
static bool GetBreakpointTriggered() { return breakpointTriggered_; };
static bool GetCorePaused() { return corePaused; };
static void SetCorePaused(bool b) { corePaused = b; };
// This will have to do until a full fledged debugger host interface is made
static void SetUpdateHandler(std::function<void()> f) {cb_bpUpdated_ = f; };
static std::function<void()> GetUpdateHandler() { return cb_bpUpdated_; };
private:
static size_t FindBreakpoint(BreakPointCpu cpu, u32 addr, bool matchTemp = false, bool temp = false);
// Finds exactly, not using a range check.
@ -162,6 +169,9 @@ private:
static u64 breakSkipFirstTicksIop_;
static bool breakpointTriggered_;
static bool corePaused;
static std::function<void()> cb_bpUpdated_;
static std::vector<MemCheck> memChecks_;
static std::vector<MemCheck *> cleanupMemChecks_;

View File

@ -278,6 +278,15 @@ u32 R5900DebugInterface::read8(u32 address)
return memRead8(address);
}
u32 R5900DebugInterface::read8(u32 address, bool& valid)
{
if (!(valid = isValidAddress(address)))
return -1;
return memRead8(address);
}
u32 R5900DebugInterface::read16(u32 address)
{
if (!isValidAddress(address) || address % 2)
@ -286,6 +295,14 @@ u32 R5900DebugInterface::read16(u32 address)
return memRead16(address);
}
u32 R5900DebugInterface::read16(u32 address, bool& valid)
{
if (!(valid = (isValidAddress(address) || address % 2)))
return -1;
return memRead16(address);
}
u32 R5900DebugInterface::read32(u32 address)
{
if (!isValidAddress(address) || address % 4)
@ -294,6 +311,14 @@ u32 R5900DebugInterface::read32(u32 address)
return memRead32(address);
}
u32 R5900DebugInterface::read32(u32 address, bool& valid)
{
if (!(valid = (isValidAddress(address) || address % 4)))
return -1;
return memRead32(address);
}
u64 R5900DebugInterface::read64(u32 address)
{
if (!isValidAddress(address) || address % 8)
@ -302,6 +327,14 @@ u64 R5900DebugInterface::read64(u32 address)
return memRead64(address);
}
u64 R5900DebugInterface::read64(u32 address, bool& valid)
{
if (!(valid = (isValidAddress(address) || address % 8)))
return -1;
return memRead64(address);
}
u128 R5900DebugInterface::read128(u32 address)
{
alignas(16) u128 result;
@ -690,6 +723,13 @@ u32 R3000DebugInterface::read8(u32 address)
return iopMemRead8(address);
}
u32 R3000DebugInterface::read8(u32 address, bool& valid)
{
if (!(valid = isValidAddress(address)))
return -1;
return iopMemRead8(address);
}
u32 R3000DebugInterface::read16(u32 address)
{
if (!isValidAddress(address))
@ -697,6 +737,13 @@ u32 R3000DebugInterface::read16(u32 address)
return iopMemRead16(address);
}
u32 R3000DebugInterface::read16(u32 address, bool& valid)
{
if (!(valid = isValidAddress(address)))
return -1;
return iopMemRead16(address);
}
u32 R3000DebugInterface::read32(u32 address)
{
if (!isValidAddress(address))
@ -704,11 +751,25 @@ u32 R3000DebugInterface::read32(u32 address)
return iopMemRead32(address);
}
u32 R3000DebugInterface::read32(u32 address, bool& valid)
{
if (!(valid = isValidAddress(address)))
return -1;
return iopMemRead32(address);
}
u64 R3000DebugInterface::read64(u32 address)
{
return 0;
}
u64 R3000DebugInterface::read64(u32 address, bool& valid)
{
return 0;
}
u128 R3000DebugInterface::read128(u32 address)
{
return u128::From32(0);

View File

@ -53,9 +53,13 @@ public:
};
virtual u32 read8(u32 address) = 0;
virtual u32 read8(u32 address, bool& valid) = 0;
virtual u32 read16(u32 address) = 0;
virtual u32 read16(u32 address, bool& valid) = 0;
virtual u32 read32(u32 address) = 0;
virtual u32 read32(u32 address, bool& valid) = 0;
virtual u64 read64(u32 address) = 0;
virtual u64 read64(u32 address, bool& valid) = 0;
virtual u128 read128(u32 address) = 0;
virtual void write8(u32 address, u8 value) = 0;
virtual void write32(u32 address, u32 value) = 0;
@ -94,9 +98,13 @@ class R5900DebugInterface : public DebugInterface
{
public:
u32 read8(u32 address) override;
u32 read8(u32 address, bool& valid) override;
u32 read16(u32 address) override;
u32 read16(u32 address, bool& valid) override;
u32 read32(u32 address) override;
u32 read32(u32 address, bool& valid) override;
u64 read64(u32 address) override;
u64 read64(u32 address, bool& valid) override;
u128 read128(u32 address) override;
void write8(u32 address, u8 value) override;
void write32(u32 address, u32 value) override;
@ -128,9 +136,13 @@ class R3000DebugInterface : public DebugInterface
{
public:
u32 read8(u32 address) override;
u32 read8(u32 address, bool& valid) override;
u32 read16(u32 address) override;
u32 read16(u32 address, bool& valid) override;
u32 read32(u32 address) override;
u32 read32(u32 address, bool& valid) override;
u64 read64(u32 address) override;
u64 read64(u32 address, bool& valid) override;
u128 read128(u32 address) override;
void write8(u32 address, u8 value) override;
void write32(u32 address, u32 value) override;

View File

@ -141,7 +141,7 @@ std::map<u32,DisassemblyEntry*>::iterator findDisassemblyEntry(std::map<u32,Disa
void DisassemblyManager::analyze(u32 address, u32 size = 1024)
{
if (!cpu->isAlive())
if (!cpu->isAlive() || !cpu->isValidAddress(address))
return;
u32 end = address+size;
@ -333,6 +333,9 @@ u32 DisassemblyManager::getNthNextAddress(u32 address, int n)
it = findDisassemblyEntry(entries,address,false);
}
if ((address + 0x400) < address) // Check for unsigned overflow, we are analysing too high!
break;
analyze(address);
}

View File

@ -301,6 +301,7 @@ void ElfObject::loadSectionHeaders()
eS = (Elf32_Sym*)data.GetPtr(secthead[i_st].sh_offset);
Console.WriteLn("found %d symbols", secthead[i_st].sh_size / sizeof(Elf32_Sym));
R5900SymbolMap.Clear();
for(uint i = 1; i < (secthead[i_st].sh_size / sizeof(Elf32_Sym)); i++) {
if ((eS[i].st_value != 0) && (ELF32_ST_TYPE(eS[i].st_info) == 2))
{

View File

@ -1581,8 +1581,8 @@ void psxRecompileNextInstruction(bool delayslot, bool swapped_delayslot)
if (!delayslot)
{
// Broken on x64
// psxEncodeBreakpoint();
// psxEncodeMemcheck();
psxEncodeBreakpoint();
psxEncodeMemcheck();
}
else
{