Debugger: Watch list
This commit is contained in:
parent
802bd75df1
commit
26e90e90a1
19 changed files with 2144 additions and 12 deletions
|
@ -57,6 +57,7 @@
|
|||
<ClInclude Include="ControlDeviceState.h" />
|
||||
<ClInclude Include="ControlManager.h" />
|
||||
<ClInclude Include="Cpu.h" />
|
||||
<ClInclude Include="ExpressionEvaluator.h" />
|
||||
<ClInclude Include="RegisterHandlerB.h" />
|
||||
<ClInclude Include="CpuTypes.h" />
|
||||
<ClInclude Include="Debugger.h" />
|
||||
|
@ -123,6 +124,7 @@
|
|||
<ClCompile Include="Disassembler.cpp" />
|
||||
<ClCompile Include="DisassemblyInfo.cpp" />
|
||||
<ClCompile Include="DmaController.cpp" />
|
||||
<ClCompile Include="ExpressionEvaluator.cpp" />
|
||||
<ClCompile Include="InternalRegisters.cpp" />
|
||||
<ClCompile Include="KeyManager.cpp" />
|
||||
<ClCompile Include="MemoryDumper.cpp" />
|
||||
|
|
|
@ -185,6 +185,9 @@
|
|||
<ClInclude Include="RegisterHandlerA.h">
|
||||
<Filter>SNES</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ExpressionEvaluator.h">
|
||||
<Filter>Debugger</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="stdafx.cpp" />
|
||||
|
@ -287,6 +290,9 @@
|
|||
<ClCompile Include="MemoryManager.cpp">
|
||||
<Filter>SNES</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ExpressionEvaluator.cpp">
|
||||
<Filter>Debugger</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="SNES">
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "CpuTypes.h"
|
||||
#include "PpuTypes.h"
|
||||
|
||||
struct DebugState
|
||||
{
|
||||
CpuState Cpu;
|
||||
PpuState Ppu;
|
||||
};
|
||||
|
||||
enum class SnesMemoryType
|
||||
{
|
||||
|
@ -18,6 +26,13 @@ struct AddressInfo
|
|||
SnesMemoryType Type;
|
||||
};
|
||||
|
||||
struct MemoryOperationInfo
|
||||
{
|
||||
uint32_t Address;
|
||||
int32_t Value;
|
||||
MemoryOperationType OperationType;
|
||||
};
|
||||
|
||||
namespace CdlFlags
|
||||
{
|
||||
enum CdlFlags : uint8_t
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "stdafx.h"
|
||||
#include "Debugger.h"
|
||||
#include "DebugTypes.h"
|
||||
#include "Console.h"
|
||||
#include "Cpu.h"
|
||||
#include "Ppu.h"
|
||||
|
@ -12,6 +13,7 @@
|
|||
#include "MemoryDumper.h"
|
||||
#include "CodeDataLogger.h"
|
||||
#include "Disassembler.h"
|
||||
#include "ExpressionEvaluator.h"
|
||||
#include "../Utilities/HexUtilities.h"
|
||||
#include "../Utilities/FolderUtilities.h"
|
||||
|
||||
|
@ -22,6 +24,7 @@ Debugger::Debugger(shared_ptr<Console> console)
|
|||
_ppu = console->GetPpu();
|
||||
_memoryManager = console->GetMemoryManager();
|
||||
|
||||
_watchExpEval.reset(new ExpressionEvaluator(this));
|
||||
_codeDataLogger.reset(new CodeDataLogger(console->GetCartridge()->DebugGetPrgRomSize()));
|
||||
_disassembler.reset(new Disassembler(console, _codeDataLogger));
|
||||
_traceLogger.reset(new TraceLogger(this, _memoryManager));
|
||||
|
@ -109,6 +112,19 @@ void Debugger::ProcessCpuWrite(uint32_t addr, uint8_t value, MemoryOperationType
|
|||
}
|
||||
}
|
||||
|
||||
int32_t Debugger::EvaluateExpression(string expression, EvalResultType &resultType, bool useCache)
|
||||
{
|
||||
DebugState state;
|
||||
MemoryOperationInfo operationInfo { 0, 0, MemoryOperationType::DummyRead };
|
||||
GetState(&state);
|
||||
if(useCache) {
|
||||
return _watchExpEval->Evaluate(expression, state, resultType, operationInfo);
|
||||
} else {
|
||||
ExpressionEvaluator expEval(this);
|
||||
return expEval.Evaluate(expression, state, resultType, operationInfo);
|
||||
}
|
||||
}
|
||||
|
||||
void Debugger::Run()
|
||||
{
|
||||
_cpuStepCount = -1;
|
||||
|
|
|
@ -11,16 +11,12 @@ class MemoryManager;
|
|||
class CodeDataLogger;
|
||||
|
||||
enum class MemoryOperationType;
|
||||
enum class EvalResultType : int32_t;
|
||||
class TraceLogger;
|
||||
class ExpressionEvaluator;
|
||||
class MemoryDumper;
|
||||
class Disassembler;
|
||||
|
||||
struct DebugState
|
||||
{
|
||||
CpuState Cpu;
|
||||
PpuState Ppu;
|
||||
//ApuState apuState;
|
||||
};
|
||||
struct DebugState;
|
||||
|
||||
class Debugger
|
||||
{
|
||||
|
@ -36,6 +32,8 @@ private:
|
|||
shared_ptr<CodeDataLogger> _codeDataLogger;
|
||||
shared_ptr<Disassembler> _disassembler;
|
||||
|
||||
unique_ptr<ExpressionEvaluator> _watchExpEval;
|
||||
|
||||
atomic<int32_t> _cpuStepCount;
|
||||
uint8_t _prevOpCode = 0;
|
||||
|
||||
|
@ -46,6 +44,8 @@ public:
|
|||
void ProcessCpuRead(uint32_t addr, uint8_t value, MemoryOperationType type);
|
||||
void ProcessCpuWrite(uint32_t addr, uint8_t value, MemoryOperationType type);
|
||||
|
||||
int32_t EvaluateExpression(string expression, EvalResultType &resultType, bool useCache);
|
||||
|
||||
void Run();
|
||||
void Step(int32_t stepCount);
|
||||
bool IsExecutionStopped();
|
||||
|
|
604
Core/ExpressionEvaluator.cpp
Normal file
604
Core/ExpressionEvaluator.cpp
Normal file
|
@ -0,0 +1,604 @@
|
|||
#include "stdafx.h"
|
||||
#include <climits>
|
||||
#include <algorithm>
|
||||
#include "DebugTypes.h"
|
||||
#include "ExpressionEvaluator.h"
|
||||
#include "Console.h"
|
||||
#include "Debugger.h"
|
||||
#include "MemoryDumper.h"
|
||||
#include "Disassembler.h"
|
||||
#include "../Utilities/HexUtilities.h"
|
||||
|
||||
const vector<string> ExpressionEvaluator::_binaryOperators = { { "*", "/", "%", "+", "-", "<<", ">>", "<", "<=", ">", ">=", "==", "!=", "&", "^", "|", "&&", "||" } };
|
||||
const vector<int> ExpressionEvaluator::_binaryPrecedence = { { 10, 10, 10, 9, 9, 8, 8, 7, 7, 7, 7, 6, 6, 5, 4, 3, 2, 1 } };
|
||||
const vector<string> ExpressionEvaluator::_unaryOperators = { { "+", "-", "~", "!" } };
|
||||
const vector<int> ExpressionEvaluator::_unaryPrecedence = { { 11, 11, 11, 11 } };
|
||||
const std::unordered_set<string> ExpressionEvaluator::_operators = { { "*", "/", "%", "+", "-", "<<", ">>", "<", "<=", ">", ">=", "==", "!=", "&", "^", "|", "&&", "||", "~", "!", "(", ")", "{", "}", "[", "]" } };
|
||||
|
||||
bool ExpressionEvaluator::IsOperator(string token, int &precedence, bool unaryOperator)
|
||||
{
|
||||
if(unaryOperator) {
|
||||
for(size_t i = 0, len = _unaryOperators.size(); i < len; i++) {
|
||||
if(token.compare(_unaryOperators[i]) == 0) {
|
||||
precedence = _unaryPrecedence[i];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(size_t i = 0, len = _binaryOperators.size(); i < len; i++) {
|
||||
if(token.compare(_binaryOperators[i]) == 0) {
|
||||
precedence = _binaryPrecedence[i];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
EvalOperators ExpressionEvaluator::GetOperator(string token, bool unaryOperator)
|
||||
{
|
||||
if(unaryOperator) {
|
||||
for(size_t i = 0, len = _unaryOperators.size(); i < len; i++) {
|
||||
if(token.compare(_unaryOperators[i]) == 0) {
|
||||
return (EvalOperators)(EvalOperators::Plus + i);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(size_t i = 0, len = _binaryOperators.size(); i < len; i++) {
|
||||
if(token.compare(_binaryOperators[i]) == 0) {
|
||||
return (EvalOperators)(EvalOperators::Multiplication + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
return EvalOperators::Addition;
|
||||
}
|
||||
|
||||
bool ExpressionEvaluator::CheckSpecialTokens(string expression, size_t &pos, string &output, ExpressionData &data)
|
||||
{
|
||||
string token;
|
||||
size_t initialPos = pos;
|
||||
size_t len = expression.size();
|
||||
do {
|
||||
char c = std::tolower(expression[pos]);
|
||||
if((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_' || c == '@') {
|
||||
//Only letters, numbers and underscore are allowed in code labels
|
||||
token += c;
|
||||
pos++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} while(pos < len);
|
||||
|
||||
if(token == "a") {
|
||||
output += std::to_string((int64_t)EvalValues::RegA);
|
||||
} else if(token == "x") {
|
||||
output += std::to_string((int64_t)EvalValues::RegX);
|
||||
} else if(token == "y") {
|
||||
output += std::to_string((int64_t)EvalValues::RegY);
|
||||
} else if(token == "ps") {
|
||||
output += std::to_string((int64_t)EvalValues::RegPS);
|
||||
} else if(token == "sp") {
|
||||
output += std::to_string((int64_t)EvalValues::RegSP);
|
||||
} else if(token == "pc") {
|
||||
output += std::to_string((int64_t)EvalValues::RegPC);
|
||||
} else if(token == "oppc") {
|
||||
output += std::to_string((int64_t)EvalValues::RegOpPC);
|
||||
} else if(token == "previousoppc") {
|
||||
output += std::to_string((int64_t)EvalValues::PreviousOpPC);
|
||||
} else if(token == "frame") {
|
||||
output += std::to_string((int64_t)EvalValues::PpuFrameCount);
|
||||
} else if(token == "cycle") {
|
||||
output += std::to_string((int64_t)EvalValues::PpuCycle);
|
||||
} else if(token == "scanline") {
|
||||
output += std::to_string((int64_t)EvalValues::PpuScanline);
|
||||
} else if(token == "irq") {
|
||||
output += std::to_string((int64_t)EvalValues::Irq);
|
||||
} else if(token == "nmi") {
|
||||
output += std::to_string((int64_t)EvalValues::Nmi);
|
||||
} else if(token == "verticalblank") {
|
||||
output += std::to_string((int64_t)EvalValues::VerticalBlank);
|
||||
} else if(token == "sprite0hit") {
|
||||
output += std::to_string((int64_t)EvalValues::Sprite0Hit);
|
||||
} else if(token == "spriteoverflow") {
|
||||
output += std::to_string((int64_t)EvalValues::SpriteOverflow);
|
||||
} else if(token == "value") {
|
||||
output += std::to_string((int64_t)EvalValues::Value);
|
||||
} else if(token == "address") {
|
||||
output += std::to_string((int64_t)EvalValues::Address);
|
||||
} else if(token == "romaddress") {
|
||||
output += std::to_string((int64_t)EvalValues::AbsoluteAddress);
|
||||
} else if(token == "iswrite") {
|
||||
output += std::to_string((int64_t)EvalValues::IsWrite);
|
||||
} else if(token == "isread") {
|
||||
output += std::to_string((int64_t)EvalValues::IsRead);
|
||||
} else if(token == "branched") {
|
||||
output += std::to_string((int64_t)EvalValues::Branched);
|
||||
} else {
|
||||
return false;
|
||||
//TODO LABELS
|
||||
/*string originalExpression = expression.substr(initialPos, pos - initialPos);
|
||||
bool validLabel = _debugger->GetLabelManager()->ContainsLabel(originalExpression);
|
||||
if(!validLabel) {
|
||||
//Check if a multi-byte label exists for this name
|
||||
string label = originalExpression + "+0";
|
||||
validLabel = _debugger->GetLabelManager()->ContainsLabel(label);
|
||||
}
|
||||
|
||||
if(validLabel) {
|
||||
data.Labels.push_back(originalExpression);
|
||||
output += std::to_string(EvalValues::FirstLabelIndex + data.Labels.size() - 1);
|
||||
} else {
|
||||
return false;
|
||||
}*/
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
string ExpressionEvaluator::GetNextToken(string expression, size_t &pos, ExpressionData &data, bool &success)
|
||||
{
|
||||
string output;
|
||||
success = true;
|
||||
|
||||
char c = std::tolower(expression[pos]);
|
||||
if(c == '$') {
|
||||
//Hex numbers
|
||||
pos++;
|
||||
for(size_t len = expression.size(); pos < len; pos++) {
|
||||
c = std::tolower(expression[pos]);
|
||||
if((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')) {
|
||||
output += c;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(output.empty()) {
|
||||
//No numbers followed the hex mark, this isn't a valid expression
|
||||
success = false;
|
||||
}
|
||||
output = std::to_string((uint32_t)HexUtilities::FromHex(output));
|
||||
} else if(c == '%') {
|
||||
//Binary numbers
|
||||
pos++;
|
||||
for(size_t len = expression.size(); pos < len; pos++) {
|
||||
c = std::tolower(expression[pos]);
|
||||
if(c == '0' || c <= '1') {
|
||||
output += c;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(output.empty()) {
|
||||
//No numbers followed the binary mark, this isn't a valid expression
|
||||
success = false;
|
||||
}
|
||||
|
||||
uint32_t value = 0;
|
||||
for(size_t i = 0; i < output.size(); i++) {
|
||||
value <<= 1;
|
||||
value |= output[i] == '1' ? 1 : 0;
|
||||
}
|
||||
output = std::to_string(value);
|
||||
} else if(c >= '0' && c <= '9') {
|
||||
//Regular numbers
|
||||
for(size_t len = expression.size(); pos < len; pos++) {
|
||||
c = std::tolower(expression[pos]);
|
||||
if(c >= '0' && c <= '9') {
|
||||
output += c;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if((c < 'a' || c > 'z') && c != '_' && c != '@') {
|
||||
//Operators
|
||||
string operatorToken;
|
||||
for(size_t len = expression.size(); pos < len; pos++) {
|
||||
c = std::tolower(expression[pos]);
|
||||
operatorToken += c;
|
||||
if(output.empty() || _operators.find(operatorToken) != _operators.end()) {
|
||||
//If appending the next char results in a valid operator, append it (or if this is the first character)
|
||||
output += c;
|
||||
} else {
|
||||
//Reached the end of the operator, return
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//Special tokens and labels
|
||||
success = CheckSpecialTokens(expression, pos, output, data);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
bool ExpressionEvaluator::ProcessSpecialOperator(EvalOperators evalOp, std::stack<EvalOperators> &opStack, std::stack<int> &precedenceStack, vector<int64_t> &outputQueue)
|
||||
{
|
||||
if(opStack.empty()) {
|
||||
return false;
|
||||
}
|
||||
while(opStack.top() != evalOp) {
|
||||
outputQueue.push_back(opStack.top());
|
||||
opStack.pop();
|
||||
precedenceStack.pop();
|
||||
|
||||
if(opStack.empty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if(evalOp != EvalOperators::Parenthesis) {
|
||||
outputQueue.push_back(opStack.top());
|
||||
}
|
||||
opStack.pop();
|
||||
precedenceStack.pop();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ExpressionEvaluator::ToRpn(string expression, ExpressionData &data)
|
||||
{
|
||||
std::stack<EvalOperators> opStack = std::stack<EvalOperators>();
|
||||
std::stack<int> precedenceStack;
|
||||
|
||||
size_t position = 0;
|
||||
int parenthesisCount = 0;
|
||||
int bracketCount = 0;
|
||||
int braceCount = 0;
|
||||
|
||||
bool previousTokenIsOp = true;
|
||||
bool operatorExpected = false;
|
||||
bool operatorOrEndTokenExpected = false;
|
||||
while(true) {
|
||||
bool success = true;
|
||||
string token = GetNextToken(expression, position, data, success);
|
||||
if(!success) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(token.empty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
bool requireOperator = operatorExpected;
|
||||
bool requireOperatorOrEndToken = operatorOrEndTokenExpected;
|
||||
bool unaryOperator = previousTokenIsOp;
|
||||
|
||||
operatorExpected = false;
|
||||
operatorOrEndTokenExpected = false;
|
||||
previousTokenIsOp = false;
|
||||
|
||||
int precedence = 0;
|
||||
if(IsOperator(token, precedence, unaryOperator)) {
|
||||
EvalOperators op = GetOperator(token, unaryOperator);
|
||||
bool rightAssociative = unaryOperator;
|
||||
while(!opStack.empty() && ((rightAssociative && precedence < precedenceStack.top()) || (!rightAssociative && precedence <= precedenceStack.top()))) {
|
||||
//Pop operators from the stack until we find something with higher priority (or empty the stack)
|
||||
data.RpnQueue.push_back(opStack.top());
|
||||
opStack.pop();
|
||||
precedenceStack.pop();
|
||||
}
|
||||
opStack.push(op);
|
||||
precedenceStack.push(precedence);
|
||||
|
||||
previousTokenIsOp = true;
|
||||
} else if(requireOperator) {
|
||||
//We needed an operator, and got something else, this isn't a valid expression (e.g "(3)4" or "[$00]22")
|
||||
return false;
|
||||
} else if(requireOperatorOrEndToken && token[0] != ')' && token[0] != ']' && token[0] != '}') {
|
||||
//We needed an operator or close token - this isn't a valid expression (e.g "%1134")
|
||||
return false;
|
||||
} else if(token[0] == '(') {
|
||||
parenthesisCount++;
|
||||
opStack.push(EvalOperators::Parenthesis);
|
||||
precedenceStack.push(0);
|
||||
previousTokenIsOp = true;
|
||||
} else if(token[0] == ')') {
|
||||
parenthesisCount--;
|
||||
if(!ProcessSpecialOperator(EvalOperators::Parenthesis, opStack, precedenceStack, data.RpnQueue)) {
|
||||
return false;
|
||||
}
|
||||
operatorExpected = true;
|
||||
} else if(token[0] == '[') {
|
||||
bracketCount++;
|
||||
opStack.push(EvalOperators::Bracket);
|
||||
precedenceStack.push(0);
|
||||
} else if(token[0] == ']') {
|
||||
bracketCount--;
|
||||
if(!ProcessSpecialOperator(EvalOperators::Bracket, opStack, precedenceStack, data.RpnQueue)) {
|
||||
return false;
|
||||
}
|
||||
operatorExpected = true;
|
||||
} else if(token[0] == '{') {
|
||||
braceCount++;
|
||||
opStack.push(EvalOperators::Braces);
|
||||
precedenceStack.push(0);
|
||||
} else if(token[0] == '}') {
|
||||
braceCount--;
|
||||
if(!ProcessSpecialOperator(EvalOperators::Braces, opStack, precedenceStack, data.RpnQueue)){
|
||||
return false;
|
||||
}
|
||||
operatorExpected = true;
|
||||
} else {
|
||||
if(token[0] < '0' || token[0] > '9') {
|
||||
return false;
|
||||
} else {
|
||||
data.RpnQueue.push_back(std::stoll(token));
|
||||
operatorOrEndTokenExpected = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(braceCount || bracketCount || parenthesisCount) {
|
||||
//Mismatching number of brackets/braces/parenthesis
|
||||
return false;
|
||||
}
|
||||
|
||||
while(!opStack.empty()) {
|
||||
data.RpnQueue.push_back(opStack.top());
|
||||
opStack.pop();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t ExpressionEvaluator::Evaluate(ExpressionData &data, DebugState &state, EvalResultType &resultType, MemoryOperationInfo &operationInfo)
|
||||
{
|
||||
if(data.RpnQueue.empty()) {
|
||||
resultType = EvalResultType::Invalid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pos = 0;
|
||||
int64_t right = 0;
|
||||
int64_t left = 0;
|
||||
resultType = EvalResultType::Numeric;
|
||||
|
||||
for(size_t i = 0, len = data.RpnQueue.size(); i < len; i++) {
|
||||
int64_t token = data.RpnQueue[i];
|
||||
|
||||
if(token >= EvalValues::RegA) {
|
||||
//Replace value with a special value
|
||||
if(token >= EvalValues::FirstLabelIndex) {
|
||||
resultType = EvalResultType::Invalid;
|
||||
return 0;
|
||||
|
||||
//TODO
|
||||
/*int64_t labelIndex = token - EvalValues::FirstLabelIndex;
|
||||
if((size_t)labelIndex < data.Labels.size()) {
|
||||
token = _debugger->GetLabelManager()->GetLabelRelativeAddress(data.Labels[(uint32_t)labelIndex]);
|
||||
if(token < -1) {
|
||||
//Label doesn't exist, try to find a matching multi-byte label
|
||||
string label = data.Labels[(uint32_t)labelIndex] + "+0";
|
||||
token = _debugger->GetLabelManager()->GetLabelRelativeAddress(label);
|
||||
}
|
||||
} else {
|
||||
token = -2;
|
||||
}
|
||||
if(token < 0) {
|
||||
//Label is no longer valid
|
||||
resultType = token == -1 ? EvalResultType::OutOfScope : EvalResultType::Invalid;
|
||||
return 0;
|
||||
}*/
|
||||
} else {
|
||||
switch(token) {
|
||||
case EvalValues::RegA: token = state.Cpu.A; break;
|
||||
case EvalValues::RegX: token = state.Cpu.X; break;
|
||||
case EvalValues::RegY: token = state.Cpu.Y; break;
|
||||
case EvalValues::RegSP: token = state.Cpu.SP; break;
|
||||
case EvalValues::RegPS: token = state.Cpu.PS; break;
|
||||
case EvalValues::RegPC: token = state.Cpu.PC; break;
|
||||
|
||||
//TODO
|
||||
/*case EvalValues::RegOpPC: token = state.Cpu.DebugPC; break;*/
|
||||
case EvalValues::PpuFrameCount: token = state.Ppu.FrameCount; break;
|
||||
case EvalValues::PpuCycle: token = state.Ppu.Cycle; break;
|
||||
case EvalValues::PpuScanline: token = state.Ppu.Scanline; break;
|
||||
//TODO
|
||||
/*case EvalValues::Nmi: token = state.CPU.NMIFlag; resultType = EvalResultType::Boolean; break;
|
||||
case EvalValues::Irq: token = state.CPU.IRQFlag; resultType = EvalResultType::Boolean; break;
|
||||
case EvalValues::Value: token = operationInfo.Value; break;
|
||||
case EvalValues::Address: token = operationInfo.Address; break;
|
||||
case EvalValues::AbsoluteAddress: token = _debugger->GetAbsoluteAddress(operationInfo.Address); break;
|
||||
case EvalValues::IsWrite: token = operationInfo.OperationType == MemoryOperationType::Write || operationInfo.OperationType == MemoryOperationType::DummyWrite; break;
|
||||
case EvalValues::IsRead: token = operationInfo.OperationType == MemoryOperationType::Read || operationInfo.OperationType == MemoryOperationType::DummyRead; break;
|
||||
case EvalValues::PreviousOpPC: token = state.CPU.PreviousDebugPC; break;
|
||||
case EvalValues::Sprite0Hit: token = state.PPU.StatusFlags.Sprite0Hit; resultType = EvalResultType::Boolean; break;
|
||||
case EvalValues::SpriteOverflow: token = state.PPU.StatusFlags.SpriteOverflow; resultType = EvalResultType::Boolean; break;
|
||||
case EvalValues::VerticalBlank: token = state.PPU.StatusFlags.VerticalBlank; resultType = EvalResultType::Boolean; break;
|
||||
case EvalValues::Branched: token = Disassembler::IsJump(_debugger->GetMemoryDumper()->GetMemoryValue(DebugMemoryType::CpuMemory, state.CPU.PreviousDebugPC, true)); resultType = EvalResultType::Boolean; break;*/
|
||||
}
|
||||
}
|
||||
} else if(token >= EvalOperators::Multiplication) {
|
||||
right = operandStack[--pos];
|
||||
if(pos > 0 && token <= EvalOperators::LogicalOr) {
|
||||
//Only do this for binary operators
|
||||
left = operandStack[--pos];
|
||||
}
|
||||
|
||||
resultType = EvalResultType::Numeric;
|
||||
switch(token) {
|
||||
case EvalOperators::Multiplication: token = left * right; break;
|
||||
case EvalOperators::Division:
|
||||
if(right == 0) {
|
||||
resultType = EvalResultType::DivideBy0;
|
||||
return 0;
|
||||
}
|
||||
token = left / right; break;
|
||||
case EvalOperators::Modulo:
|
||||
if(right == 0) {
|
||||
resultType = EvalResultType::DivideBy0;
|
||||
return 0;
|
||||
}
|
||||
token = left % right;
|
||||
break;
|
||||
case EvalOperators::Addition: token = left + right; break;
|
||||
case EvalOperators::Substration: token = left - right; break;
|
||||
case EvalOperators::ShiftLeft: token = left << right; break;
|
||||
case EvalOperators::ShiftRight: token = left >> right; break;
|
||||
case EvalOperators::SmallerThan: token = left < right; resultType = EvalResultType::Boolean; break;
|
||||
case EvalOperators::SmallerOrEqual: token = left <= right; resultType = EvalResultType::Boolean; break;
|
||||
case EvalOperators::GreaterThan: token = left > right; resultType = EvalResultType::Boolean; break;
|
||||
case EvalOperators::GreaterOrEqual: token = left >= right; resultType = EvalResultType::Boolean; break;
|
||||
case EvalOperators::Equal: token = left == right; resultType = EvalResultType::Boolean; break;
|
||||
case EvalOperators::NotEqual: token = left != right; resultType = EvalResultType::Boolean; break;
|
||||
case EvalOperators::BinaryAnd: token = left & right; break;
|
||||
case EvalOperators::BinaryXor: token = left ^ right; break;
|
||||
case EvalOperators::BinaryOr: token = left | right; break;
|
||||
case EvalOperators::LogicalAnd: token = left && right; resultType = EvalResultType::Boolean; break;
|
||||
case EvalOperators::LogicalOr: token = left || right; resultType = EvalResultType::Boolean; break;
|
||||
|
||||
//Unary operators
|
||||
case EvalOperators::Plus: token = right; break;
|
||||
case EvalOperators::Minus: token = -right; break;
|
||||
case EvalOperators::BinaryNot: token = ~right; break;
|
||||
case EvalOperators::LogicalNot: token = !right; break;
|
||||
case EvalOperators::Bracket: token = _debugger->GetMemoryDumper()->GetMemoryValue(SnesMemoryType::CpuMemory, (uint32_t)right); break;
|
||||
case EvalOperators::Braces: token = _debugger->GetMemoryDumper()->GetMemoryValueWord(SnesMemoryType::CpuMemory, (uint32_t)right); break;
|
||||
default: throw std::runtime_error("Invalid operator");
|
||||
}
|
||||
}
|
||||
operandStack[pos++] = token;
|
||||
}
|
||||
return (int32_t)operandStack[0];
|
||||
}
|
||||
|
||||
ExpressionEvaluator::ExpressionEvaluator(Debugger* debugger)
|
||||
{
|
||||
_debugger = debugger;
|
||||
}
|
||||
|
||||
ExpressionData ExpressionEvaluator::GetRpnList(string expression, bool &success)
|
||||
{
|
||||
ExpressionData* cachedData = PrivateGetRpnList(expression, success);
|
||||
if(cachedData) {
|
||||
return *cachedData;
|
||||
} else {
|
||||
return ExpressionData();
|
||||
}
|
||||
}
|
||||
|
||||
ExpressionData* ExpressionEvaluator::PrivateGetRpnList(string expression, bool& success)
|
||||
{
|
||||
ExpressionData *cachedData = nullptr;
|
||||
{
|
||||
LockHandler lock = _cacheLock.AcquireSafe();
|
||||
|
||||
auto result = _cache.find(expression);
|
||||
if(result != _cache.end()) {
|
||||
cachedData = &(result->second);
|
||||
}
|
||||
}
|
||||
|
||||
if(cachedData == nullptr) {
|
||||
string fixedExp = expression;
|
||||
fixedExp.erase(std::remove(fixedExp.begin(), fixedExp.end(), ' '), fixedExp.end());
|
||||
ExpressionData data;
|
||||
success = ToRpn(fixedExp, data);
|
||||
if(success) {
|
||||
LockHandler lock = _cacheLock.AcquireSafe();
|
||||
_cache[expression] = data;
|
||||
cachedData = &_cache[expression];
|
||||
}
|
||||
} else {
|
||||
success = true;
|
||||
}
|
||||
|
||||
return cachedData;
|
||||
}
|
||||
|
||||
int32_t ExpressionEvaluator::PrivateEvaluate(string expression, DebugState &state, EvalResultType &resultType, MemoryOperationInfo &operationInfo, bool& success)
|
||||
{
|
||||
success = true;
|
||||
ExpressionData *cachedData = PrivateGetRpnList(expression, success);
|
||||
|
||||
if(!success) {
|
||||
resultType = EvalResultType::Invalid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Evaluate(*cachedData, state, resultType, operationInfo);
|
||||
}
|
||||
|
||||
int32_t ExpressionEvaluator::Evaluate(string expression, DebugState &state, EvalResultType &resultType, MemoryOperationInfo &operationInfo)
|
||||
{
|
||||
try {
|
||||
bool success;
|
||||
int32_t result = PrivateEvaluate(expression, state, resultType, operationInfo, success);
|
||||
if(success) {
|
||||
return result;
|
||||
}
|
||||
} catch(std::exception e) {
|
||||
}
|
||||
resultType = EvalResultType::Invalid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ExpressionEvaluator::Validate(string expression)
|
||||
{
|
||||
try {
|
||||
DebugState state;
|
||||
EvalResultType type;
|
||||
MemoryOperationInfo operationInfo;
|
||||
bool success;
|
||||
PrivateEvaluate(expression, state, type, operationInfo, success);
|
||||
return success;
|
||||
} catch(std::exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#if _DEBUG
|
||||
#include <assert.h>
|
||||
void ExpressionEvaluator::RunTests()
|
||||
{
|
||||
//Some basic unit tests to run in debug mode
|
||||
auto test = [=](string expr, EvalResultType expectedType, int expectedResult) {
|
||||
DebugState state = { 0 };
|
||||
OperationInfo opInfo = { 0 };
|
||||
EvalResultType type;
|
||||
int32_t result = Evaluate(expr, state, type, opInfo);
|
||||
|
||||
assert(type == expectedType);
|
||||
assert(result == expectedResult);
|
||||
};
|
||||
|
||||
test("1 - -1", EvalResultType::Numeric, 2);
|
||||
test("1 - (-1)", EvalResultType::Numeric, 2);
|
||||
test("1 - -(-1)", EvalResultType::Numeric, 0);
|
||||
test("(0 - 1) == -1 && 5 < 10", EvalResultType::Boolean, true);
|
||||
test("(0 - 1) == 0 || 5 < 10", EvalResultType::Boolean, true);
|
||||
test("(0 - 1) == 0 || 5 < -10", EvalResultType::Boolean, false);
|
||||
test("(0 - 1) == 0 || 15 < 10", EvalResultType::Boolean, false);
|
||||
|
||||
test("10 != $10", EvalResultType::Boolean, true);
|
||||
test("10 == $A", EvalResultType::Boolean, true);
|
||||
test("10 == $0A", EvalResultType::Boolean, true);
|
||||
|
||||
test("(0 - 1 == 0 || 15 < 10", EvalResultType::Invalid, 0);
|
||||
test("10 / 0", EvalResultType::DivideBy0, 0);
|
||||
|
||||
test("x + 5", EvalResultType::Numeric, 5);
|
||||
test("x == 0", EvalResultType::Boolean, true);
|
||||
test("x == y", EvalResultType::Boolean, true);
|
||||
test("x == y == scanline", EvalResultType::Boolean, false); //because (x == y) is true, and true != scanline
|
||||
test("x == y && !(a == x)", EvalResultType::Boolean, false);
|
||||
|
||||
test("(~0 & ~1) & $FFF == $FFE", EvalResultType::Numeric, 0); //because of operator priority (& is done after ==)
|
||||
test("((~0 & ~1) & $FFF) == $FFE", EvalResultType::Boolean, true);
|
||||
|
||||
test("1+3*3+10/(3+4)", EvalResultType::Numeric, 11);
|
||||
test("(1+3*3+10)/(3+4)", EvalResultType::Numeric, 2);
|
||||
test("(1+3*3+10)/3+4", EvalResultType::Numeric, 10);
|
||||
|
||||
test("{$4500}", EvalResultType::Numeric, 0x4545);
|
||||
test("[$4500]", EvalResultType::Numeric, 0x45);
|
||||
|
||||
test("[$45]3", EvalResultType::Invalid, 0);
|
||||
test("($45)3", EvalResultType::Invalid, 0);
|
||||
test("($45]", EvalResultType::Invalid, 0);
|
||||
|
||||
test("%11", EvalResultType::Numeric, 3);
|
||||
test("%011", EvalResultType::Numeric, 3);
|
||||
test("%1011", EvalResultType::Numeric, 11);
|
||||
test("%12", EvalResultType::Invalid, 0);
|
||||
}
|
||||
#endif
|
137
Core/ExpressionEvaluator.h
Normal file
137
Core/ExpressionEvaluator.h
Normal file
|
@ -0,0 +1,137 @@
|
|||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include <stack>
|
||||
#include <deque>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include "DebugTypes.h"
|
||||
#include "../Utilities/SimpleLock.h"
|
||||
|
||||
class Debugger;
|
||||
|
||||
enum EvalOperators : int64_t
|
||||
{
|
||||
//Binary operators
|
||||
Multiplication = 20000000000,
|
||||
Division = 20000000001,
|
||||
Modulo = 20000000002,
|
||||
Addition = 20000000003,
|
||||
Substration = 20000000004,
|
||||
ShiftLeft = 20000000005,
|
||||
ShiftRight = 20000000006,
|
||||
SmallerThan = 20000000007,
|
||||
SmallerOrEqual = 20000000008,
|
||||
GreaterThan = 20000000009,
|
||||
GreaterOrEqual = 20000000010,
|
||||
Equal = 20000000011,
|
||||
NotEqual = 20000000012,
|
||||
BinaryAnd = 20000000013,
|
||||
BinaryXor = 20000000014,
|
||||
BinaryOr = 20000000015,
|
||||
LogicalAnd = 20000000016,
|
||||
LogicalOr = 20000000017,
|
||||
|
||||
//Unary operators
|
||||
Plus = 20000000050,
|
||||
Minus = 20000000051,
|
||||
BinaryNot = 20000000052,
|
||||
LogicalNot = 20000000053,
|
||||
|
||||
//Used to read ram address
|
||||
Bracket = 20000000054, //Read byte
|
||||
Braces = 20000000055, //Read word
|
||||
|
||||
//Special value, not used as an operator
|
||||
Parenthesis = 20000000100,
|
||||
};
|
||||
|
||||
enum EvalValues : int64_t
|
||||
{
|
||||
RegA = 20000000100,
|
||||
RegX = 20000000101,
|
||||
RegY = 20000000102,
|
||||
RegSP = 20000000103,
|
||||
RegPS = 20000000104,
|
||||
RegPC = 20000000105,
|
||||
RegOpPC = 20000000106,
|
||||
PpuFrameCount = 20000000107,
|
||||
PpuCycle = 20000000108,
|
||||
PpuScanline = 20000000109,
|
||||
Nmi = 20000000110,
|
||||
Irq = 20000000111,
|
||||
Value = 20000000112,
|
||||
Address = 20000000113,
|
||||
AbsoluteAddress = 20000000114,
|
||||
IsWrite = 20000000115,
|
||||
IsRead = 20000000116,
|
||||
PreviousOpPC = 20000000117,
|
||||
Sprite0Hit = 20000000118,
|
||||
SpriteOverflow = 20000000119,
|
||||
VerticalBlank = 20000000120,
|
||||
Branched = 20000000121,
|
||||
|
||||
FirstLabelIndex = 20000002000,
|
||||
};
|
||||
|
||||
enum class EvalResultType : int32_t
|
||||
{
|
||||
Numeric = 0,
|
||||
Boolean = 1,
|
||||
Invalid = 2,
|
||||
DivideBy0 = 3,
|
||||
OutOfScope = 4
|
||||
};
|
||||
|
||||
class StringHasher
|
||||
{
|
||||
public:
|
||||
size_t operator()(const std::string& t) const
|
||||
{
|
||||
//Quick hash for expressions - most are likely to have different lengths, and not expecting dozens of breakpoints, either, so this should be fine.
|
||||
return t.size();
|
||||
}
|
||||
};
|
||||
|
||||
struct ExpressionData
|
||||
{
|
||||
std::vector<int64_t> RpnQueue;
|
||||
std::vector<string> Labels;
|
||||
};
|
||||
|
||||
class ExpressionEvaluator
|
||||
{
|
||||
private:
|
||||
static const vector<string> _binaryOperators;
|
||||
static const vector<int> _binaryPrecedence;
|
||||
static const vector<string> _unaryOperators;
|
||||
static const vector<int> _unaryPrecedence;
|
||||
static const std::unordered_set<string> _operators;
|
||||
|
||||
std::unordered_map<string, ExpressionData, StringHasher> _cache;
|
||||
SimpleLock _cacheLock;
|
||||
|
||||
int64_t operandStack[1000];
|
||||
Debugger* _debugger;
|
||||
|
||||
bool IsOperator(string token, int &precedence, bool unaryOperator);
|
||||
EvalOperators GetOperator(string token, bool unaryOperator);
|
||||
bool CheckSpecialTokens(string expression, size_t &pos, string &output, ExpressionData &data);
|
||||
string GetNextToken(string expression, size_t &pos, ExpressionData &data, bool &success);
|
||||
bool ProcessSpecialOperator(EvalOperators evalOp, std::stack<EvalOperators> &opStack, std::stack<int> &precedenceStack, vector<int64_t> &outputQueue);
|
||||
bool ToRpn(string expression, ExpressionData &data);
|
||||
int32_t PrivateEvaluate(string expression, DebugState &state, EvalResultType &resultType, MemoryOperationInfo &operationInfo, bool &success);
|
||||
ExpressionData* PrivateGetRpnList(string expression, bool& success);
|
||||
|
||||
public:
|
||||
ExpressionEvaluator(Debugger* debugger);
|
||||
|
||||
int32_t Evaluate(ExpressionData &data, DebugState &state, EvalResultType &resultType, MemoryOperationInfo &operationInfo);
|
||||
int32_t Evaluate(string expression, DebugState &state, EvalResultType &resultType, MemoryOperationInfo &operationInfo);
|
||||
ExpressionData GetRpnList(string expression, bool &success);
|
||||
|
||||
bool Validate(string expression);
|
||||
|
||||
#if _DEBUG
|
||||
void RunTests();
|
||||
#endif
|
||||
};
|
|
@ -111,3 +111,11 @@ uint8_t MemoryDumper::GetMemoryValue(SnesMemoryType memoryType, uint32_t address
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t MemoryDumper::GetMemoryValueWord(SnesMemoryType memoryType, uint32_t address)
|
||||
{
|
||||
uint32_t memorySize = GetMemorySize(memoryType);
|
||||
uint8_t lsb = GetMemoryValue(memoryType, address);
|
||||
uint8_t msb = GetMemoryValue(memoryType, (address + 1) & (memorySize - 1));
|
||||
return (msb << 8) | lsb;
|
||||
}
|
|
@ -22,6 +22,7 @@ public:
|
|||
void GetMemoryState(SnesMemoryType type, uint8_t *buffer);
|
||||
|
||||
uint8_t GetMemoryValue(SnesMemoryType memoryType, uint32_t address, bool disableSideEffects = true);
|
||||
uint8_t GetMemoryValueWord(SnesMemoryType memoryType, uint32_t address);
|
||||
void SetMemoryValue(SnesMemoryType memoryType, uint32_t address, uint8_t value, bool disableSideEffects = true);
|
||||
void SetMemoryValues(SnesMemoryType memoryType, uint32_t address, uint8_t* data, uint32_t length);
|
||||
void SetMemoryState(SnesMemoryType type, uint8_t *buffer, uint32_t length);
|
||||
|
|
|
@ -46,6 +46,8 @@ extern "C"
|
|||
DllExport void __stdcall StopTraceLogger() { GetDebugger()->GetTraceLogger()->StopLogging(); }
|
||||
DllExport const char* GetExecutionTrace(uint32_t lineCount) { return GetDebugger()->GetTraceLogger()->GetExecutionTrace(lineCount); }
|
||||
|
||||
DllExport int32_t __stdcall EvaluateExpression(char* expression, EvalResultType *resultType, bool useCache) { return GetDebugger()->EvaluateExpression(expression, *resultType, useCache); }
|
||||
|
||||
DllExport void __stdcall GetState(DebugState *state) { GetDebugger()->GetState(state); }
|
||||
|
||||
DllExport void __stdcall SetMemoryState(SnesMemoryType type, uint8_t *buffer, int32_t length) { GetDebugger()->GetMemoryDumper()->SetMemoryState(type, buffer, length); }
|
||||
|
|
|
@ -39,6 +39,8 @@ namespace Mesen.GUI.Config
|
|||
public XmlColor CodeReadBreakpointColor = Color.FromArgb(40, 40, 200);
|
||||
public XmlColor CodeActiveStatementColor = Color.Yellow;
|
||||
|
||||
public WatchFormatStyle WatchFormat = WatchFormatStyle.Hex;
|
||||
|
||||
public DebugInfo()
|
||||
{
|
||||
}
|
||||
|
|
515
UI/Debugger/Controls/ctrlWatch.cs
Normal file
515
UI/Debugger/Controls/ctrlWatch.cs
Normal file
|
@ -0,0 +1,515 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Mesen.GUI.Config;
|
||||
using Mesen.GUI.Controls;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Globalization;
|
||||
using Mesen.GUI.Forms;
|
||||
|
||||
namespace Mesen.GUI.Debugger
|
||||
{
|
||||
public partial class ctrlWatch : BaseControl
|
||||
{
|
||||
private Color _updatedColor = Color.Red;
|
||||
private Color _normalColor = SystemColors.ControlText;
|
||||
|
||||
private static Regex _watchAddressOrLabel = new Regex(@"^(\[|{)(\s*((\$[0-9A-Fa-f]+)|(\d+)|([@_a-zA-Z0-9]+)))\s*[,]{0,1}\d*\s*(\]|})$", RegexOptions.Compiled);
|
||||
|
||||
private int _previousMaxLength = -1;
|
||||
private int _selectedAddress = -1;
|
||||
//private CodeLabel _selectedLabel = null;
|
||||
private List<WatchValueInfo> _previousValues = new List<WatchValueInfo>();
|
||||
|
||||
private bool _isEditing = false;
|
||||
ListViewItem _keyDownItem = null;
|
||||
|
||||
public ctrlWatch()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
this.DoubleBuffered = true;
|
||||
}
|
||||
|
||||
protected override void OnLoad(EventArgs e)
|
||||
{
|
||||
base.OnLoad(e);
|
||||
if(!IsDesignMode) {
|
||||
WatchManager.WatchChanged += WatchManager_WatchChanged;
|
||||
mnuRemoveWatch.InitShortcut(this, nameof(DebuggerShortcutsConfig.WatchList_Delete));
|
||||
mnuEditInMemoryViewer.InitShortcut(this, nameof(DebuggerShortcutsConfig.CodeWindow_EditInMemoryViewer));
|
||||
mnuViewInDisassembly.InitShortcut(this, nameof(DebuggerShortcutsConfig.MemoryViewer_ViewInDisassembly));
|
||||
mnuMoveUp.InitShortcut(this, nameof(DebuggerShortcutsConfig.WatchList_MoveUp));
|
||||
mnuMoveDown.InitShortcut(this, nameof(DebuggerShortcutsConfig.WatchList_MoveDown));
|
||||
}
|
||||
}
|
||||
|
||||
public string GetTooltipText()
|
||||
{
|
||||
return "";
|
||||
/*return (
|
||||
frmBreakpoint.GetConditionTooltip(true) + Environment.NewLine + Environment.NewLine +
|
||||
"Additionally, the watch window supports a syntax to display X bytes starting from a specific address. e.g:" + Environment.NewLine +
|
||||
"[$10, 16]: Display 16 bytes starting from address $10" + Environment.NewLine +
|
||||
"[MyLabel, 4]: Display 4 bytes starting from the address the specified label (MyLabel) refers to"
|
||||
);*/
|
||||
}
|
||||
|
||||
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
|
||||
{
|
||||
if(lstWatch.SelectedItems.Count > 0) {
|
||||
//Used to prevent a Mono issue where pressing a key will change the selected item before we get a chance to edit it
|
||||
_keyDownItem = lstWatch.SelectedItems[0];
|
||||
|
||||
if((_isEditing && keyData == Keys.Escape) || keyData == Keys.Enter) {
|
||||
if(keyData == Keys.Enter) {
|
||||
if(_isEditing) {
|
||||
ApplyEdit();
|
||||
} else {
|
||||
StartEdit(lstWatch.SelectedItems[0].Text);
|
||||
}
|
||||
} else if(keyData == Keys.Escape) {
|
||||
CancelEdit();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
_keyDownItem = null;
|
||||
}
|
||||
|
||||
UpdateActions();
|
||||
return base.ProcessCmdKey(ref msg, keyData);
|
||||
}
|
||||
|
||||
private void contextMenuWatch_Opening(object sender, CancelEventArgs e)
|
||||
{
|
||||
UpdateActions();
|
||||
}
|
||||
|
||||
private void WatchManager_WatchChanged(object sender, EventArgs e)
|
||||
{
|
||||
if(this.InvokeRequired) {
|
||||
this.BeginInvoke((Action)(() => this.UpdateWatch()));
|
||||
} else {
|
||||
this.UpdateWatch();
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateWatch(bool autoResizeColumns = true)
|
||||
{
|
||||
List<WatchValueInfo> watchContent = WatchManager.GetWatchContent(_previousValues);
|
||||
_previousValues = watchContent;
|
||||
|
||||
bool updating = false;
|
||||
if(watchContent.Count != lstWatch.Items.Count - 1) {
|
||||
int currentFocus = lstWatch.FocusedItem?.Selected == true ? (lstWatch.FocusedItem?.Index ?? -1) : -1;
|
||||
lstWatch.BeginUpdate();
|
||||
lstWatch.Items.Clear();
|
||||
|
||||
List<ListViewItem> itemsToAdd = new List<ListViewItem>();
|
||||
foreach(WatchValueInfo watch in watchContent) {
|
||||
ListViewItem item = new ListViewItem(watch.Expression);
|
||||
item.UseItemStyleForSubItems = false;
|
||||
item.SubItems.Add(watch.Value).ForeColor = watch.HasChanged ? _updatedColor : _normalColor;
|
||||
itemsToAdd.Add(item);
|
||||
}
|
||||
var lastItem = new ListViewItem("");
|
||||
lastItem.SubItems.Add("");
|
||||
itemsToAdd.Add(lastItem);
|
||||
lstWatch.Items.AddRange(itemsToAdd.ToArray());
|
||||
if(currentFocus >= 0 && currentFocus < lstWatch.Items.Count) {
|
||||
SetSelectedItem(currentFocus);
|
||||
}
|
||||
updating = true;
|
||||
} else {
|
||||
for(int i = 0; i < watchContent.Count; i++) {
|
||||
ListViewItem item = lstWatch.Items[i];
|
||||
bool needUpdate = (
|
||||
item.SubItems[0].Text != watchContent[i].Expression ||
|
||||
item.SubItems[1].Text != watchContent[i].Value ||
|
||||
item.SubItems[1].ForeColor != (watchContent[i].HasChanged ? _updatedColor : _normalColor)
|
||||
);
|
||||
if(needUpdate) {
|
||||
updating = true;
|
||||
item.SubItems[0].Text = watchContent[i].Expression;
|
||||
item.SubItems[1].Text = watchContent[i].Value;
|
||||
item.SubItems[1].ForeColor = watchContent[i].HasChanged ? _updatedColor : _normalColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(updating) {
|
||||
if(watchContent.Count > 0) {
|
||||
int maxLength = watchContent.Select(info => info.Value.Length).Max();
|
||||
if(_previousMaxLength != maxLength) {
|
||||
if(autoResizeColumns) {
|
||||
lstWatch.AutoResizeColumn(1, ColumnHeaderAutoResizeStyle.ColumnContent);
|
||||
}
|
||||
if(colValue.Width < 100) {
|
||||
colValue.Width = 100;
|
||||
}
|
||||
_previousMaxLength = maxLength;
|
||||
}
|
||||
}
|
||||
lstWatch.EndUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
private void lstWatch_SelectedIndexChanged(object sender, EventArgs e)
|
||||
{
|
||||
mnuRemoveWatch.Enabled = lstWatch.SelectedItems.Count >= 1;
|
||||
UpdateActions();
|
||||
}
|
||||
|
||||
private void UpdateActions()
|
||||
{
|
||||
mnuHexDisplay.Checked = ConfigManager.Config.Debug.WatchFormat == WatchFormatStyle.Hex;
|
||||
mnuDecimalDisplay.Checked = ConfigManager.Config.Debug.WatchFormat == WatchFormatStyle.Signed;
|
||||
mnuBinaryDisplay.Checked = ConfigManager.Config.Debug.WatchFormat == WatchFormatStyle.Binary;
|
||||
mnuRowDisplayFormat.Enabled = lstWatch.SelectedItems.Count > 0;
|
||||
|
||||
mnuEditInMemoryViewer.Enabled = false;
|
||||
mnuViewInDisassembly.Enabled = false;
|
||||
mnuMoveUp.Enabled = false;
|
||||
mnuMoveDown.Enabled = false;
|
||||
|
||||
if(lstWatch.SelectedItems.Count == 1) {
|
||||
Match match = _watchAddressOrLabel.Match(lstWatch.SelectedItems[0].Text);
|
||||
if(match.Success) {
|
||||
string address = match.Groups[3].Value;
|
||||
|
||||
if(address[0] >= '0' && address[0] <= '9' || address[0] == '$') {
|
||||
//CPU Address
|
||||
_selectedAddress = Int32.Parse(address[0] == '$' ? address.Substring(1) : address, address[0] == '$' ? NumberStyles.AllowHexSpecifier : NumberStyles.None);
|
||||
//TODO
|
||||
//_selectedLabel = null;
|
||||
mnuEditInMemoryViewer.Enabled = true;
|
||||
mnuViewInDisassembly.Enabled = true;
|
||||
} else {
|
||||
//Label
|
||||
_selectedAddress = -1;
|
||||
//TODO
|
||||
/*_selectedLabel = LabelManager.GetLabel(address);
|
||||
if(_selectedLabel != null) {
|
||||
mnuEditInMemoryViewer.Enabled = true;
|
||||
mnuViewInDisassembly.Enabled = true;
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
mnuMoveUp.Enabled = lstWatch.SelectedIndices[0] > 0 && lstWatch.SelectedIndices[0] < lstWatch.Items.Count - 1;
|
||||
mnuMoveDown.Enabled = lstWatch.SelectedIndices[0] < lstWatch.Items.Count - 2;
|
||||
}
|
||||
}
|
||||
|
||||
private void mnuRemoveWatch_Click(object sender, EventArgs e)
|
||||
{
|
||||
if(lstWatch.SelectedItems.Count >= 1) {
|
||||
var itemsToRemove = new List<int>();
|
||||
foreach(ListViewItem item in lstWatch.SelectedItems) {
|
||||
itemsToRemove.Add(item.Index);
|
||||
}
|
||||
WatchManager.RemoveWatch(itemsToRemove.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
private void mnuViewInDisassembly_Click(object sender, EventArgs e)
|
||||
{
|
||||
//TODO
|
||||
/*if(lstWatch.SelectedItems.Count != 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(_selectedAddress >= 0) {
|
||||
DebugWindowManager.GetDebugger().ScrollToAddress(_selectedAddress);
|
||||
} else if(_selectedLabel != null) {
|
||||
int relAddress = _selectedLabel.GetRelativeAddress();
|
||||
if(relAddress >= 0) {
|
||||
DebugWindowManager.GetDebugger().ScrollToAddress(relAddress);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
private void mnuEditInMemoryViewer_Click(object sender, EventArgs e)
|
||||
{
|
||||
//TODO
|
||||
/*if(lstWatch.SelectedItems.Count != 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(_selectedAddress >= 0) {
|
||||
DebugWindowManager.OpenMemoryViewer(_selectedAddress, DebugMemoryType.CpuMemory);
|
||||
} else if(_selectedLabel != null) {
|
||||
DebugWindowManager.OpenMemoryViewer((int)_selectedLabel.Address, _selectedLabel.AddressType.ToMemoryType());
|
||||
}*/
|
||||
}
|
||||
|
||||
private void StartEdit(string text, ListViewItem selectedItem = null)
|
||||
{
|
||||
if(selectedItem == null) {
|
||||
selectedItem = lstWatch.SelectedItems[0];
|
||||
}
|
||||
|
||||
SetSelectedItem(selectedItem.Index);
|
||||
|
||||
txtEdit.Location = selectedItem.Position;
|
||||
txtEdit.Width = selectedItem.Bounds.Width;
|
||||
txtEdit.Text = text;
|
||||
txtEdit.SelectionLength = 0;
|
||||
txtEdit.SelectionStart = text.Length;
|
||||
txtEdit.Visible = true;
|
||||
txtEdit.Focus();
|
||||
_isEditing = true;
|
||||
}
|
||||
|
||||
private void lstWatch_Click(object sender, EventArgs e)
|
||||
{
|
||||
if(lstWatch.SelectedItems.Count == 1 && string.IsNullOrWhiteSpace(lstWatch.SelectedItems[0].Text)) {
|
||||
StartEdit("");
|
||||
}
|
||||
}
|
||||
|
||||
private void lstWatch_DoubleClick(object sender, EventArgs e)
|
||||
{
|
||||
if(lstWatch.SelectedItems.Count == 1) {
|
||||
StartEdit(lstWatch.SelectedItems[0].Text);
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyEdit()
|
||||
{
|
||||
if(lstWatch.SelectedItems.Count > 0) {
|
||||
lstWatch.SelectedItems[0].Text = txtEdit.Text;
|
||||
WatchManager.UpdateWatch(lstWatch.SelectedIndices[0], txtEdit.Text);
|
||||
}
|
||||
lstWatch.Focus();
|
||||
}
|
||||
|
||||
private void CancelEdit()
|
||||
{
|
||||
if(lstWatch.SelectedItems.Count > 0) {
|
||||
txtEdit.Text = lstWatch.SelectedItems[0].Text;
|
||||
}
|
||||
lstWatch.Focus();
|
||||
}
|
||||
|
||||
private void lstWatch_KeyPress(object sender, KeyPressEventArgs e)
|
||||
{
|
||||
if(lstWatch.SelectedItems.Count > 0) {
|
||||
if(e.KeyChar >= ' ' && e.KeyChar < 128) {
|
||||
e.Handled = true;
|
||||
StartEdit(e.KeyChar.ToString(), _keyDownItem);
|
||||
_keyDownItem = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void txtEdit_Leave(object sender, EventArgs e)
|
||||
{
|
||||
_isEditing = false;
|
||||
txtEdit.Visible = false;
|
||||
lstWatch.Focus();
|
||||
ApplyEdit();
|
||||
}
|
||||
|
||||
private void mnuMoveUp_Click(object sender, EventArgs e)
|
||||
{
|
||||
MoveUp(false);
|
||||
}
|
||||
|
||||
private void mnuMoveDown_Click(object sender, EventArgs e)
|
||||
{
|
||||
MoveDown();
|
||||
}
|
||||
|
||||
private void SetSelectedItem(int index)
|
||||
{
|
||||
if(index < lstWatch.Items.Count) {
|
||||
lstWatch.FocusedItem = lstWatch.Items[index];
|
||||
foreach(ListViewItem item in lstWatch.Items) {
|
||||
item.Selected = lstWatch.FocusedItem == item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void MoveUp(bool fromUpDownArrow)
|
||||
{
|
||||
if(lstWatch.SelectedIndices.Count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int index = lstWatch.SelectedIndices[0];
|
||||
if(Program.IsMono && fromUpDownArrow) {
|
||||
//Mono appears to move the selection up before processing this
|
||||
index++;
|
||||
}
|
||||
|
||||
if(index > 0 && index < lstWatch.Items.Count - 1) {
|
||||
string currentEntry = lstWatch.Items[index].SubItems[0].Text;
|
||||
string entryAbove = lstWatch.Items[index - 1].SubItems[0].Text;
|
||||
SetSelectedItem(index - 1);
|
||||
WatchManager.UpdateWatch(index - 1, currentEntry);
|
||||
WatchManager.UpdateWatch(index, entryAbove);
|
||||
} else {
|
||||
SetSelectedItem(index);
|
||||
}
|
||||
}
|
||||
|
||||
private void MoveDown()
|
||||
{
|
||||
if(lstWatch.SelectedIndices.Count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int index = lstWatch.SelectedIndices[0];
|
||||
if(index < lstWatch.Items.Count - 2) {
|
||||
string currentEntry = lstWatch.Items[index].SubItems[0].Text;
|
||||
string entryBelow = lstWatch.Items[index + 1].SubItems[0].Text;
|
||||
SetSelectedItem(index + 1);
|
||||
WatchManager.UpdateWatch(index + 1, currentEntry);
|
||||
WatchManager.UpdateWatch(index, entryBelow);
|
||||
} else {
|
||||
SetSelectedItem(index);
|
||||
}
|
||||
}
|
||||
|
||||
private void lstWatch_OnMoveUpDown(Keys keyData, ref bool processed)
|
||||
{
|
||||
if(keyData == ConfigManager.Config.Debug.Shortcuts.WatchList_MoveUp) {
|
||||
MoveUp(true);
|
||||
processed = true;
|
||||
} else if(keyData == ConfigManager.Config.Debug.Shortcuts.WatchList_MoveDown) {
|
||||
MoveDown();
|
||||
processed = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void mnuImport_Click(object sender, EventArgs e)
|
||||
{
|
||||
using(OpenFileDialog ofd = new OpenFileDialog()) {
|
||||
ofd.SetFilter("Watch files (*.mwf)|*.mwf");
|
||||
if(ofd.ShowDialog() == DialogResult.OK) {
|
||||
WatchManager.Import(ofd.FileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void mnuExport_Click(object sender, EventArgs e)
|
||||
{
|
||||
using(SaveFileDialog sfd = new SaveFileDialog()) {
|
||||
sfd.SetFilter("Watch files (*.mwf)|*.mwf");
|
||||
if(sfd.ShowDialog() == DialogResult.OK) {
|
||||
WatchManager.Export(sfd.FileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void mnuHexDisplay_Click(object sender, EventArgs e)
|
||||
{
|
||||
ConfigManager.Config.Debug.WatchFormat = WatchFormatStyle.Hex;
|
||||
ConfigManager.ApplyChanges();
|
||||
UpdateWatch();
|
||||
}
|
||||
|
||||
private void mnuDecimalDisplay_Click(object sender, EventArgs e)
|
||||
{
|
||||
ConfigManager.Config.Debug.WatchFormat = WatchFormatStyle.Signed;
|
||||
ConfigManager.ApplyChanges();
|
||||
UpdateWatch();
|
||||
}
|
||||
|
||||
private void mnuBinaryDisplay_Click(object sender, EventArgs e)
|
||||
{
|
||||
ConfigManager.Config.Debug.WatchFormat = WatchFormatStyle.Binary;
|
||||
ConfigManager.ApplyChanges();
|
||||
UpdateWatch();
|
||||
}
|
||||
|
||||
private string GetFormatString(WatchFormatStyle format, int byteLength)
|
||||
{
|
||||
string formatString = ", ";
|
||||
switch(format) {
|
||||
case WatchFormatStyle.Binary: formatString += "B"; break;
|
||||
case WatchFormatStyle.Hex: formatString += "H"; break;
|
||||
case WatchFormatStyle.Signed: formatString += "S"; break;
|
||||
case WatchFormatStyle.Unsigned: formatString += "U"; break;
|
||||
default: throw new Exception("Unsupported type");
|
||||
}
|
||||
if(byteLength > 1) {
|
||||
formatString += byteLength.ToString();
|
||||
}
|
||||
return formatString;
|
||||
}
|
||||
|
||||
private void SetSelectionFormat(WatchFormatStyle format, int byteLength)
|
||||
{
|
||||
SetSelectionFormat(GetFormatString(format, byteLength));
|
||||
}
|
||||
|
||||
private void SetSelectionFormat(string formatString)
|
||||
{
|
||||
List<string> entries = WatchManager.WatchEntries;
|
||||
foreach(int i in lstWatch.SelectedIndices) {
|
||||
if(i < entries.Count) {
|
||||
Match match = WatchManager.FormatSuffixRegex.Match(entries[i]);
|
||||
if(match.Success) {
|
||||
WatchManager.UpdateWatch(i, match.Groups[1].Value + formatString);
|
||||
} else {
|
||||
WatchManager.UpdateWatch(i, entries[i] + formatString);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void mnuRowBinary_Click(object sender, EventArgs e)
|
||||
{
|
||||
SetSelectionFormat(WatchFormatStyle.Binary, 1);
|
||||
}
|
||||
|
||||
private void mnuRowHex1_Click(object sender, EventArgs e)
|
||||
{
|
||||
SetSelectionFormat(WatchFormatStyle.Hex, 1);
|
||||
}
|
||||
|
||||
private void mnuRowHex2_Click(object sender, EventArgs e)
|
||||
{
|
||||
SetSelectionFormat(WatchFormatStyle.Hex, 2);
|
||||
}
|
||||
|
||||
private void mnuRowHex3_Click(object sender, EventArgs e)
|
||||
{
|
||||
SetSelectionFormat(WatchFormatStyle.Hex, 3);
|
||||
}
|
||||
|
||||
private void mnuRowSigned1_Click(object sender, EventArgs e)
|
||||
{
|
||||
SetSelectionFormat(WatchFormatStyle.Signed, 1);
|
||||
}
|
||||
|
||||
private void mnuRowSigned2_Click(object sender, EventArgs e)
|
||||
{
|
||||
SetSelectionFormat(WatchFormatStyle.Signed, 2);
|
||||
}
|
||||
|
||||
private void mnuRowSigned3_Click(object sender, EventArgs e)
|
||||
{
|
||||
SetSelectionFormat(WatchFormatStyle.Unsigned, 1);
|
||||
}
|
||||
|
||||
private void mnuRowUnsigned_Click(object sender, EventArgs e)
|
||||
{
|
||||
SetSelectionFormat(WatchFormatStyle.Unsigned, 1);
|
||||
}
|
||||
|
||||
private void mnuRowClearFormat_Click(object sender, EventArgs e)
|
||||
{
|
||||
SetSelectionFormat("");
|
||||
}
|
||||
}
|
||||
}
|
396
UI/Debugger/Controls/ctrlWatch.designer.cs
generated
Normal file
396
UI/Debugger/Controls/ctrlWatch.designer.cs
generated
Normal file
|
@ -0,0 +1,396 @@
|
|||
namespace Mesen.GUI.Debugger
|
||||
{
|
||||
partial class ctrlWatch
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if(disposing && (components != null)) {
|
||||
components.Dispose();
|
||||
}
|
||||
WatchManager.WatchChanged -= WatchManager_WatchChanged;
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Component Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.components = new System.ComponentModel.Container();
|
||||
System.Windows.Forms.ListViewItem listViewItem1 = new System.Windows.Forms.ListViewItem("");
|
||||
this.lstWatch = new Mesen.GUI.Controls.WatchListView();
|
||||
this.colName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
|
||||
this.colValue = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
|
||||
this.contextMenuWatch = new System.Windows.Forms.ContextMenuStrip(this.components);
|
||||
this.mnuRemoveWatch = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator();
|
||||
this.mnuEditInMemoryViewer = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.mnuViewInDisassembly = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.toolStripMenuItem4 = new System.Windows.Forms.ToolStripSeparator();
|
||||
this.mnuMoveUp = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.mnuMoveDown = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator();
|
||||
this.mnuDecimalDisplay = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.mnuHexDisplay = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.mnuBinaryDisplay = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.toolStripMenuItem5 = new System.Windows.Forms.ToolStripSeparator();
|
||||
this.mnuRowDisplayFormat = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.mnuRowBinary = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.toolStripMenuItem8 = new System.Windows.Forms.ToolStripSeparator();
|
||||
this.mnuRowHex1 = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.mnuRowHex2 = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.mnuRowHex3 = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.toolStripMenuItem6 = new System.Windows.Forms.ToolStripSeparator();
|
||||
this.mnuRowSigned1 = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.mnuRowSigned2 = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.mnuRowSigned3 = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.toolStripMenuItem7 = new System.Windows.Forms.ToolStripSeparator();
|
||||
this.mnuRowUnsigned = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.toolStripMenuItem9 = new System.Windows.Forms.ToolStripSeparator();
|
||||
this.mnuRowClearFormat = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.toolStripMenuItem3 = new System.Windows.Forms.ToolStripSeparator();
|
||||
this.mnuImport = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.mnuExport = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.txtEdit = new System.Windows.Forms.TextBox();
|
||||
this.contextMenuWatch.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// lstWatch
|
||||
//
|
||||
this.lstWatch.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
|
||||
this.colName,
|
||||
this.colValue});
|
||||
this.lstWatch.ContextMenuStrip = this.contextMenuWatch;
|
||||
this.lstWatch.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.lstWatch.FullRowSelect = true;
|
||||
this.lstWatch.GridLines = true;
|
||||
this.lstWatch.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable;
|
||||
this.lstWatch.Items.AddRange(new System.Windows.Forms.ListViewItem[] {
|
||||
listViewItem1});
|
||||
this.lstWatch.Location = new System.Drawing.Point(0, 0);
|
||||
this.lstWatch.Name = "lstWatch";
|
||||
this.lstWatch.Size = new System.Drawing.Size(378, 112);
|
||||
this.lstWatch.TabIndex = 6;
|
||||
this.lstWatch.UseCompatibleStateImageBehavior = false;
|
||||
this.lstWatch.View = System.Windows.Forms.View.Details;
|
||||
this.lstWatch.OnMoveUpDown += new Mesen.GUI.Controls.WatchListView.MoveUpDownHandler(this.lstWatch_OnMoveUpDown);
|
||||
this.lstWatch.SelectedIndexChanged += new System.EventHandler(this.lstWatch_SelectedIndexChanged);
|
||||
this.lstWatch.Click += new System.EventHandler(this.lstWatch_Click);
|
||||
this.lstWatch.DoubleClick += new System.EventHandler(this.lstWatch_DoubleClick);
|
||||
this.lstWatch.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.lstWatch_KeyPress);
|
||||
//
|
||||
// colName
|
||||
//
|
||||
this.colName.Text = "Name";
|
||||
this.colName.Width = 180;
|
||||
//
|
||||
// colValue
|
||||
//
|
||||
this.colValue.Text = "Value";
|
||||
this.colValue.Width = 110;
|
||||
//
|
||||
// contextMenuWatch
|
||||
//
|
||||
this.contextMenuWatch.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.mnuRemoveWatch,
|
||||
this.toolStripMenuItem1,
|
||||
this.mnuEditInMemoryViewer,
|
||||
this.mnuViewInDisassembly,
|
||||
this.toolStripMenuItem4,
|
||||
this.mnuMoveUp,
|
||||
this.mnuMoveDown,
|
||||
this.toolStripMenuItem2,
|
||||
this.mnuRowDisplayFormat,
|
||||
this.toolStripMenuItem3,
|
||||
this.mnuDecimalDisplay,
|
||||
this.mnuHexDisplay,
|
||||
this.mnuBinaryDisplay,
|
||||
this.toolStripMenuItem5,
|
||||
this.mnuImport,
|
||||
this.mnuExport});
|
||||
this.contextMenuWatch.Name = "contextMenuWatch";
|
||||
this.contextMenuWatch.Size = new System.Drawing.Size(194, 298);
|
||||
this.contextMenuWatch.Opening += new System.ComponentModel.CancelEventHandler(this.contextMenuWatch_Opening);
|
||||
//
|
||||
// mnuRemoveWatch
|
||||
//
|
||||
this.mnuRemoveWatch.Image = global::Mesen.GUI.Properties.Resources.Close;
|
||||
this.mnuRemoveWatch.Name = "mnuRemoveWatch";
|
||||
this.mnuRemoveWatch.Size = new System.Drawing.Size(193, 22);
|
||||
this.mnuRemoveWatch.Text = "Remove";
|
||||
this.mnuRemoveWatch.Click += new System.EventHandler(this.mnuRemoveWatch_Click);
|
||||
//
|
||||
// toolStripMenuItem1
|
||||
//
|
||||
this.toolStripMenuItem1.Name = "toolStripMenuItem1";
|
||||
this.toolStripMenuItem1.Size = new System.Drawing.Size(190, 6);
|
||||
//
|
||||
// mnuEditInMemoryViewer
|
||||
//
|
||||
this.mnuEditInMemoryViewer.Image = global::Mesen.GUI.Properties.Resources.CheatCode;
|
||||
this.mnuEditInMemoryViewer.Name = "mnuEditInMemoryViewer";
|
||||
this.mnuEditInMemoryViewer.Size = new System.Drawing.Size(193, 22);
|
||||
this.mnuEditInMemoryViewer.Text = "Edit in Memory Viewer";
|
||||
this.mnuEditInMemoryViewer.Click += new System.EventHandler(this.mnuEditInMemoryViewer_Click);
|
||||
//
|
||||
// mnuViewInDisassembly
|
||||
//
|
||||
this.mnuViewInDisassembly.Image = global::Mesen.GUI.Properties.Resources.Debugger;
|
||||
this.mnuViewInDisassembly.Name = "mnuViewInDisassembly";
|
||||
this.mnuViewInDisassembly.Size = new System.Drawing.Size(193, 22);
|
||||
this.mnuViewInDisassembly.Text = "View in disassembly";
|
||||
this.mnuViewInDisassembly.Click += new System.EventHandler(this.mnuViewInDisassembly_Click);
|
||||
//
|
||||
// toolStripMenuItem4
|
||||
//
|
||||
this.toolStripMenuItem4.Name = "toolStripMenuItem4";
|
||||
this.toolStripMenuItem4.Size = new System.Drawing.Size(190, 6);
|
||||
//
|
||||
// mnuMoveUp
|
||||
//
|
||||
this.mnuMoveUp.Image = global::Mesen.GUI.Properties.Resources.MoveUp;
|
||||
this.mnuMoveUp.Name = "mnuMoveUp";
|
||||
this.mnuMoveUp.Size = new System.Drawing.Size(193, 22);
|
||||
this.mnuMoveUp.Text = "Move up";
|
||||
this.mnuMoveUp.Click += new System.EventHandler(this.mnuMoveUp_Click);
|
||||
//
|
||||
// mnuMoveDown
|
||||
//
|
||||
this.mnuMoveDown.Image = global::Mesen.GUI.Properties.Resources.MoveDown;
|
||||
this.mnuMoveDown.Name = "mnuMoveDown";
|
||||
this.mnuMoveDown.Size = new System.Drawing.Size(193, 22);
|
||||
this.mnuMoveDown.Text = "Move down";
|
||||
this.mnuMoveDown.Click += new System.EventHandler(this.mnuMoveDown_Click);
|
||||
//
|
||||
// toolStripMenuItem2
|
||||
//
|
||||
this.toolStripMenuItem2.Name = "toolStripMenuItem2";
|
||||
this.toolStripMenuItem2.Size = new System.Drawing.Size(190, 6);
|
||||
//
|
||||
// mnuDecimalDisplay
|
||||
//
|
||||
this.mnuDecimalDisplay.Name = "mnuDecimalDisplay";
|
||||
this.mnuDecimalDisplay.Size = new System.Drawing.Size(193, 22);
|
||||
this.mnuDecimalDisplay.Text = "Decimal Display";
|
||||
this.mnuDecimalDisplay.Click += new System.EventHandler(this.mnuDecimalDisplay_Click);
|
||||
//
|
||||
// mnuHexDisplay
|
||||
//
|
||||
this.mnuHexDisplay.Checked = true;
|
||||
this.mnuHexDisplay.CheckState = System.Windows.Forms.CheckState.Checked;
|
||||
this.mnuHexDisplay.Name = "mnuHexDisplay";
|
||||
this.mnuHexDisplay.Size = new System.Drawing.Size(193, 22);
|
||||
this.mnuHexDisplay.Text = "Hexadecimal Display";
|
||||
this.mnuHexDisplay.Click += new System.EventHandler(this.mnuHexDisplay_Click);
|
||||
//
|
||||
// mnuBinaryDisplay
|
||||
//
|
||||
this.mnuBinaryDisplay.Name = "mnuBinaryDisplay";
|
||||
this.mnuBinaryDisplay.Size = new System.Drawing.Size(193, 22);
|
||||
this.mnuBinaryDisplay.Text = "Binary Display";
|
||||
this.mnuBinaryDisplay.Click += new System.EventHandler(this.mnuBinaryDisplay_Click);
|
||||
//
|
||||
// toolStripMenuItem5
|
||||
//
|
||||
this.toolStripMenuItem5.Name = "toolStripMenuItem5";
|
||||
this.toolStripMenuItem5.Size = new System.Drawing.Size(190, 6);
|
||||
//
|
||||
// mnuRowDisplayFormat
|
||||
//
|
||||
this.mnuRowDisplayFormat.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.mnuRowBinary,
|
||||
this.toolStripMenuItem8,
|
||||
this.mnuRowHex1,
|
||||
this.mnuRowHex2,
|
||||
this.mnuRowHex3,
|
||||
this.toolStripMenuItem6,
|
||||
this.mnuRowSigned1,
|
||||
this.mnuRowSigned2,
|
||||
this.mnuRowSigned3,
|
||||
this.toolStripMenuItem7,
|
||||
this.mnuRowUnsigned,
|
||||
this.toolStripMenuItem9,
|
||||
this.mnuRowClearFormat});
|
||||
this.mnuRowDisplayFormat.Name = "mnuRowDisplayFormat";
|
||||
this.mnuRowDisplayFormat.Size = new System.Drawing.Size(193, 22);
|
||||
this.mnuRowDisplayFormat.Text = "Row Display Format";
|
||||
//
|
||||
// mnuRowBinary
|
||||
//
|
||||
this.mnuRowBinary.Name = "mnuRowBinary";
|
||||
this.mnuRowBinary.Size = new System.Drawing.Size(197, 22);
|
||||
this.mnuRowBinary.Text = "Binary";
|
||||
this.mnuRowBinary.Click += new System.EventHandler(this.mnuRowBinary_Click);
|
||||
//
|
||||
// toolStripMenuItem8
|
||||
//
|
||||
this.toolStripMenuItem8.Name = "toolStripMenuItem8";
|
||||
this.toolStripMenuItem8.Size = new System.Drawing.Size(194, 6);
|
||||
//
|
||||
// mnuRowHex1
|
||||
//
|
||||
this.mnuRowHex1.Name = "mnuRowHex1";
|
||||
this.mnuRowHex1.Size = new System.Drawing.Size(197, 22);
|
||||
this.mnuRowHex1.Text = "Hexadecimal (8-bit)";
|
||||
this.mnuRowHex1.Click += new System.EventHandler(this.mnuRowHex1_Click);
|
||||
//
|
||||
// mnuRowHex2
|
||||
//
|
||||
this.mnuRowHex2.Name = "mnuRowHex2";
|
||||
this.mnuRowHex2.Size = new System.Drawing.Size(197, 22);
|
||||
this.mnuRowHex2.Text = "Hexadecimal (16-bit)";
|
||||
this.mnuRowHex2.Click += new System.EventHandler(this.mnuRowHex2_Click);
|
||||
//
|
||||
// mnuRowHex3
|
||||
//
|
||||
this.mnuRowHex3.Name = "mnuRowHex3";
|
||||
this.mnuRowHex3.Size = new System.Drawing.Size(197, 22);
|
||||
this.mnuRowHex3.Text = "Hexadecimal (24-bit)";
|
||||
this.mnuRowHex3.Click += new System.EventHandler(this.mnuRowHex3_Click);
|
||||
//
|
||||
// toolStripMenuItem6
|
||||
//
|
||||
this.toolStripMenuItem6.Name = "toolStripMenuItem6";
|
||||
this.toolStripMenuItem6.Size = new System.Drawing.Size(194, 6);
|
||||
//
|
||||
// mnuRowSigned1
|
||||
//
|
||||
this.mnuRowSigned1.Name = "mnuRowSigned1";
|
||||
this.mnuRowSigned1.Size = new System.Drawing.Size(197, 22);
|
||||
this.mnuRowSigned1.Text = "Signed decimal (8-bit)";
|
||||
this.mnuRowSigned1.Click += new System.EventHandler(this.mnuRowSigned1_Click);
|
||||
//
|
||||
// mnuRowSigned2
|
||||
//
|
||||
this.mnuRowSigned2.Name = "mnuRowSigned2";
|
||||
this.mnuRowSigned2.Size = new System.Drawing.Size(197, 22);
|
||||
this.mnuRowSigned2.Text = "Signed decimal (16-bit)";
|
||||
this.mnuRowSigned2.Click += new System.EventHandler(this.mnuRowSigned2_Click);
|
||||
//
|
||||
// mnuRowSigned3
|
||||
//
|
||||
this.mnuRowSigned3.Name = "mnuRowSigned3";
|
||||
this.mnuRowSigned3.Size = new System.Drawing.Size(197, 22);
|
||||
this.mnuRowSigned3.Text = "Signed decimal (24-bit)";
|
||||
this.mnuRowSigned3.Click += new System.EventHandler(this.mnuRowSigned3_Click);
|
||||
//
|
||||
// toolStripMenuItem7
|
||||
//
|
||||
this.toolStripMenuItem7.Name = "toolStripMenuItem7";
|
||||
this.toolStripMenuItem7.Size = new System.Drawing.Size(194, 6);
|
||||
//
|
||||
// mnuRowUnsigned
|
||||
//
|
||||
this.mnuRowUnsigned.Name = "mnuRowUnsigned";
|
||||
this.mnuRowUnsigned.Size = new System.Drawing.Size(197, 22);
|
||||
this.mnuRowUnsigned.Text = "Unsigned decimal";
|
||||
this.mnuRowUnsigned.Click += new System.EventHandler(this.mnuRowUnsigned_Click);
|
||||
//
|
||||
// toolStripMenuItem9
|
||||
//
|
||||
this.toolStripMenuItem9.Name = "toolStripMenuItem9";
|
||||
this.toolStripMenuItem9.Size = new System.Drawing.Size(194, 6);
|
||||
//
|
||||
// mnuRowClearFormat
|
||||
//
|
||||
this.mnuRowClearFormat.Image = global::Mesen.GUI.Properties.Resources.Close;
|
||||
this.mnuRowClearFormat.Name = "mnuRowClearFormat";
|
||||
this.mnuRowClearFormat.Size = new System.Drawing.Size(197, 22);
|
||||
this.mnuRowClearFormat.Text = "Clear";
|
||||
this.mnuRowClearFormat.Click += new System.EventHandler(this.mnuRowClearFormat_Click);
|
||||
//
|
||||
// toolStripMenuItem3
|
||||
//
|
||||
this.toolStripMenuItem3.Name = "toolStripMenuItem3";
|
||||
this.toolStripMenuItem3.Size = new System.Drawing.Size(190, 6);
|
||||
//
|
||||
// mnuImport
|
||||
//
|
||||
this.mnuImport.Image = global::Mesen.GUI.Properties.Resources.Import;
|
||||
this.mnuImport.Name = "mnuImport";
|
||||
this.mnuImport.Size = new System.Drawing.Size(193, 22);
|
||||
this.mnuImport.Text = "Import...";
|
||||
this.mnuImport.Click += new System.EventHandler(this.mnuImport_Click);
|
||||
//
|
||||
// mnuExport
|
||||
//
|
||||
this.mnuExport.Image = global::Mesen.GUI.Properties.Resources.Export;
|
||||
this.mnuExport.Name = "mnuExport";
|
||||
this.mnuExport.Size = new System.Drawing.Size(193, 22);
|
||||
this.mnuExport.Text = "Export...";
|
||||
this.mnuExport.Click += new System.EventHandler(this.mnuExport_Click);
|
||||
//
|
||||
// txtEdit
|
||||
//
|
||||
this.txtEdit.AcceptsReturn = true;
|
||||
this.txtEdit.Location = new System.Drawing.Point(3, 24);
|
||||
this.txtEdit.Name = "txtEdit";
|
||||
this.txtEdit.Size = new System.Drawing.Size(177, 20);
|
||||
this.txtEdit.TabIndex = 7;
|
||||
this.txtEdit.Visible = false;
|
||||
this.txtEdit.Leave += new System.EventHandler(this.txtEdit_Leave);
|
||||
//
|
||||
// ctrlWatch
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.Controls.Add(this.txtEdit);
|
||||
this.Controls.Add(this.lstWatch);
|
||||
this.Name = "ctrlWatch";
|
||||
this.Size = new System.Drawing.Size(378, 112);
|
||||
this.contextMenuWatch.ResumeLayout(false);
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private Mesen.GUI.Controls.WatchListView lstWatch;
|
||||
private System.Windows.Forms.ColumnHeader colName;
|
||||
private System.Windows.Forms.ColumnHeader colValue;
|
||||
private System.Windows.Forms.ContextMenuStrip contextMenuWatch;
|
||||
private System.Windows.Forms.ToolStripMenuItem mnuRemoveWatch;
|
||||
private System.Windows.Forms.ToolStripMenuItem mnuHexDisplay;
|
||||
private System.Windows.Forms.ToolStripSeparator toolStripMenuItem1;
|
||||
private System.Windows.Forms.ToolStripMenuItem mnuEditInMemoryViewer;
|
||||
private System.Windows.Forms.ToolStripMenuItem mnuViewInDisassembly;
|
||||
private System.Windows.Forms.ToolStripSeparator toolStripMenuItem2;
|
||||
private System.Windows.Forms.TextBox txtEdit;
|
||||
private System.Windows.Forms.ToolStripSeparator toolStripMenuItem4;
|
||||
private System.Windows.Forms.ToolStripMenuItem mnuMoveUp;
|
||||
private System.Windows.Forms.ToolStripMenuItem mnuMoveDown;
|
||||
private System.Windows.Forms.ToolStripSeparator toolStripMenuItem3;
|
||||
private System.Windows.Forms.ToolStripMenuItem mnuImport;
|
||||
private System.Windows.Forms.ToolStripMenuItem mnuExport;
|
||||
private System.Windows.Forms.ToolStripMenuItem mnuDecimalDisplay;
|
||||
private System.Windows.Forms.ToolStripMenuItem mnuBinaryDisplay;
|
||||
private System.Windows.Forms.ToolStripSeparator toolStripMenuItem5;
|
||||
private System.Windows.Forms.ToolStripMenuItem mnuRowDisplayFormat;
|
||||
private System.Windows.Forms.ToolStripMenuItem mnuRowSigned1;
|
||||
private System.Windows.Forms.ToolStripMenuItem mnuRowSigned2;
|
||||
private System.Windows.Forms.ToolStripMenuItem mnuRowSigned3;
|
||||
private System.Windows.Forms.ToolStripSeparator toolStripMenuItem6;
|
||||
private System.Windows.Forms.ToolStripMenuItem mnuRowHex1;
|
||||
private System.Windows.Forms.ToolStripMenuItem mnuRowHex2;
|
||||
private System.Windows.Forms.ToolStripSeparator toolStripMenuItem7;
|
||||
private System.Windows.Forms.ToolStripMenuItem mnuRowUnsigned;
|
||||
private System.Windows.Forms.ToolStripMenuItem mnuRowBinary;
|
||||
private System.Windows.Forms.ToolStripSeparator toolStripMenuItem8;
|
||||
private System.Windows.Forms.ToolStripMenuItem mnuRowHex3;
|
||||
private System.Windows.Forms.ToolStripSeparator toolStripMenuItem9;
|
||||
private System.Windows.Forms.ToolStripMenuItem mnuRowClearFormat;
|
||||
}
|
||||
}
|
123
UI/Debugger/Controls/ctrlWatch.resx
Normal file
123
UI/Debugger/Controls/ctrlWatch.resx
Normal file
|
@ -0,0 +1,123 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<metadata name="contextMenuWatch.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
</root>
|
226
UI/Debugger/WatchManager.cs
Normal file
226
UI/Debugger/WatchManager.cs
Normal file
|
@ -0,0 +1,226 @@
|
|||
using Mesen.GUI.Config;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Mesen.GUI.Debugger
|
||||
{
|
||||
class WatchManager
|
||||
{
|
||||
public static event EventHandler WatchChanged;
|
||||
private static List<string> _watchEntries = new List<string>();
|
||||
private static Regex _arrayWatchRegex = new Regex(@"\[((\$[0-9A-Fa-f]+)|(\d+)|([@_a-zA-Z0-9]+))\s*,\s*(\d+)\]", RegexOptions.Compiled);
|
||||
public static Regex FormatSuffixRegex = new Regex(@"^(.*),\s*([B|H|S|U])([\d]){0,1}$", RegexOptions.Compiled);
|
||||
|
||||
public static List<string> WatchEntries
|
||||
{
|
||||
get { return _watchEntries; }
|
||||
set
|
||||
{
|
||||
_watchEntries = new List<string>(value);
|
||||
WatchChanged?.Invoke(null, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
public static List<WatchValueInfo> GetWatchContent(List<WatchValueInfo> previousValues)
|
||||
{
|
||||
WatchFormatStyle defaultStyle = ConfigManager.Config.Debug.WatchFormat;
|
||||
int defaultByteLength = 1;
|
||||
if(defaultStyle == WatchFormatStyle.Signed) {
|
||||
defaultByteLength = 4;
|
||||
}
|
||||
|
||||
var list = new List<WatchValueInfo>();
|
||||
for(int i = 0; i < _watchEntries.Count; i++) {
|
||||
string expression = _watchEntries[i].Trim();
|
||||
string newValue = "";
|
||||
EvalResultType resultType;
|
||||
|
||||
string exprToEvaluate = expression;
|
||||
WatchFormatStyle style = defaultStyle;
|
||||
int byteLength = defaultByteLength;
|
||||
if(expression.StartsWith("{") && expression.EndsWith("}")) {
|
||||
//Default to 2-byte values when using {} syntax
|
||||
byteLength = 2;
|
||||
}
|
||||
|
||||
ProcessFormatSpecifier(ref exprToEvaluate, ref style, ref byteLength);
|
||||
|
||||
bool forceHasChanged = false;
|
||||
Match match = _arrayWatchRegex.Match(expression);
|
||||
if(match.Success) {
|
||||
//Watch expression matches the array display syntax (e.g: [$300,10] = display 10 bytes starting from $300)
|
||||
newValue = ProcessArrayDisplaySyntax(style, ref forceHasChanged, match);
|
||||
} else {
|
||||
Int32 result = DebugApi.EvaluateExpression(exprToEvaluate, out resultType, true);
|
||||
switch(resultType) {
|
||||
case EvalResultType.Numeric: newValue = FormatValue(result, style, byteLength); break;
|
||||
case EvalResultType.Boolean: newValue = result == 0 ? "false" : "true"; break;
|
||||
case EvalResultType.Invalid: newValue = "<invalid expression>"; forceHasChanged = true; break;
|
||||
case EvalResultType.DivideBy0: newValue = "<division by zero>"; forceHasChanged = true; break;
|
||||
case EvalResultType.OutOfScope: newValue = "<label out of scope>"; forceHasChanged = true; break;
|
||||
}
|
||||
}
|
||||
|
||||
list.Add(new WatchValueInfo() { Expression = expression, Value = newValue, HasChanged = forceHasChanged || (i < previousValues.Count ? (previousValues[i].Value != newValue) : false) });
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private static string FormatValue(int value, WatchFormatStyle style, int byteLength)
|
||||
{
|
||||
switch(style) {
|
||||
case WatchFormatStyle.Unsigned: return ((UInt32)value).ToString();
|
||||
case WatchFormatStyle.Hex: return "$" + value.ToString("X" + byteLength * 2);
|
||||
case WatchFormatStyle.Binary:
|
||||
string binary = Convert.ToString(value, 2).PadLeft(byteLength * 8, '0');
|
||||
for(int i = binary.Length - 4; i > 0; i -= 4) {
|
||||
binary = binary.Insert(i, ".");
|
||||
}
|
||||
return "%" + binary;
|
||||
case WatchFormatStyle.Signed:
|
||||
int bitCount = byteLength * 8;
|
||||
if(bitCount < 32) {
|
||||
if(((value >> (bitCount - 1)) & 0x01) == 0x01) {
|
||||
//Negative value
|
||||
return (value | (-(1 << bitCount))).ToString();
|
||||
} else {
|
||||
//Position value
|
||||
return value.ToString();
|
||||
}
|
||||
} else {
|
||||
return value.ToString();
|
||||
}
|
||||
|
||||
default: throw new Exception("Unsupported format");
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsArraySyntax(string expression)
|
||||
{
|
||||
return _arrayWatchRegex.IsMatch(expression);
|
||||
}
|
||||
|
||||
private static bool ProcessFormatSpecifier(ref string expression, ref WatchFormatStyle style, ref int byteLength)
|
||||
{
|
||||
Match match = WatchManager.FormatSuffixRegex.Match(expression);
|
||||
if(!match.Success) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string format = match.Groups[2].Value.ToUpperInvariant();
|
||||
switch(format[0]) {
|
||||
case 'S': style = WatchFormatStyle.Signed; break;
|
||||
case 'H': style = WatchFormatStyle.Hex; break;
|
||||
case 'B': style = WatchFormatStyle.Binary; break;
|
||||
case 'U': style = WatchFormatStyle.Unsigned; break;
|
||||
default: throw new Exception("Invalid format");
|
||||
}
|
||||
|
||||
if(match.Groups[3].Success) {
|
||||
byteLength = Math.Max(Math.Min(Int32.Parse(match.Groups[3].Value), 4), 1);
|
||||
} else {
|
||||
byteLength = 1;
|
||||
}
|
||||
|
||||
expression = match.Groups[1].Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static string ProcessArrayDisplaySyntax(WatchFormatStyle style, ref bool forceHasChanged, Match match)
|
||||
{
|
||||
string newValue;
|
||||
int address;
|
||||
if(match.Groups[2].Value.Length > 0) {
|
||||
address = int.Parse(match.Groups[2].Value.Substring(1), System.Globalization.NumberStyles.HexNumber);
|
||||
} else if(match.Groups[3].Value.Length > 0) {
|
||||
address = int.Parse(match.Groups[3].Value);
|
||||
} else {
|
||||
return "<invalid expression>";
|
||||
/*CodeLabel label = LabelManager.GetLabel(match.Groups[4].Value);
|
||||
if(label == null) {
|
||||
forceHasChanged = true;
|
||||
return "<invalid label>";
|
||||
}
|
||||
address = label.GetRelativeAddress();*/
|
||||
}
|
||||
int elemCount = int.Parse(match.Groups[5].Value);
|
||||
|
||||
if(address >= 0) {
|
||||
List<string> values = new List<string>(elemCount);
|
||||
for(int j = address, end = address + elemCount; j < end; j++) {
|
||||
int memValue = DebugApi.GetMemoryValue(SnesMemoryType.CpuMemory, (uint)j);
|
||||
values.Add(FormatValue(memValue, style, 1));
|
||||
}
|
||||
newValue = string.Join(" ", values);
|
||||
} else {
|
||||
newValue = "<label out of scope>";
|
||||
forceHasChanged = true;
|
||||
}
|
||||
|
||||
return newValue;
|
||||
}
|
||||
|
||||
public static void AddWatch(params string[] expressions)
|
||||
{
|
||||
foreach(string expression in expressions) {
|
||||
_watchEntries.Add(expression);
|
||||
}
|
||||
WatchChanged?.Invoke(null, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public static void UpdateWatch(int index, string expression)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(expression)) {
|
||||
RemoveWatch(index);
|
||||
} else {
|
||||
if(index >= _watchEntries.Count) {
|
||||
_watchEntries.Add(expression);
|
||||
} else {
|
||||
_watchEntries[index] = expression;
|
||||
}
|
||||
WatchChanged?.Invoke(null, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
public static void RemoveWatch(params int[] indexes)
|
||||
{
|
||||
HashSet<int> set = new HashSet<int>(indexes);
|
||||
_watchEntries = _watchEntries.Where((el, index) => !set.Contains(index)).ToList();
|
||||
//_previousValues = _previousValues.Where((el, index) => !set.Contains(index)).ToList();
|
||||
WatchChanged?.Invoke(null, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public static void Import(string filename)
|
||||
{
|
||||
if(File.Exists(filename)) {
|
||||
WatchManager.WatchEntries = new List<string>(File.ReadAllLines(filename));
|
||||
}
|
||||
}
|
||||
|
||||
public static void Export(string filename)
|
||||
{
|
||||
File.WriteAllLines(filename, WatchManager.WatchEntries);
|
||||
}
|
||||
}
|
||||
|
||||
public class WatchValueInfo
|
||||
{
|
||||
public string Expression { get; set; }
|
||||
public string Value { get; set; }
|
||||
public bool HasChanged { get; set; }
|
||||
}
|
||||
|
||||
public enum WatchFormatStyle
|
||||
{
|
||||
Unsigned,
|
||||
Signed,
|
||||
Hex,
|
||||
Binary
|
||||
}
|
||||
}
|
67
UI/Debugger/frmDebugger.Designer.cs
generated
67
UI/Debugger/frmDebugger.Designer.cs
generated
|
@ -55,15 +55,23 @@
|
|||
this.toolStripMenuItem8 = new System.Windows.Forms.ToolStripSeparator();
|
||||
this.mnuBreakIn = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.mnuBreakOn = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.ctrlSplitContainer = new Mesen.GUI.Controls.ctrlSplitContainer();
|
||||
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
|
||||
this.ctrlWatch = new Mesen.GUI.Debugger.ctrlWatch();
|
||||
this.ctrlMesenMenuStrip1.SuspendLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.ctrlSplitContainer)).BeginInit();
|
||||
this.ctrlSplitContainer.Panel1.SuspendLayout();
|
||||
this.ctrlSplitContainer.Panel2.SuspendLayout();
|
||||
this.ctrlSplitContainer.SuspendLayout();
|
||||
this.tableLayoutPanel1.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// ctrlDisassemblyView
|
||||
//
|
||||
this.ctrlDisassemblyView.Dock = System.Windows.Forms.DockStyle.Left;
|
||||
this.ctrlDisassemblyView.Location = new System.Drawing.Point(0, 24);
|
||||
this.ctrlDisassemblyView.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.ctrlDisassemblyView.Location = new System.Drawing.Point(0, 0);
|
||||
this.ctrlDisassemblyView.Name = "ctrlDisassemblyView";
|
||||
this.ctrlDisassemblyView.Size = new System.Drawing.Size(559, 620);
|
||||
this.ctrlDisassemblyView.Size = new System.Drawing.Size(852, 439);
|
||||
this.ctrlDisassemblyView.TabIndex = 0;
|
||||
//
|
||||
// ctrlMesenMenuStrip1
|
||||
|
@ -243,17 +251,63 @@
|
|||
this.mnuBreakOn.Size = new System.Drawing.Size(212, 22);
|
||||
this.mnuBreakOn.Text = "Break on...";
|
||||
//
|
||||
// ctrlSplitContainer
|
||||
//
|
||||
this.ctrlSplitContainer.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.ctrlSplitContainer.HidePanel2 = false;
|
||||
this.ctrlSplitContainer.Location = new System.Drawing.Point(0, 24);
|
||||
this.ctrlSplitContainer.Name = "ctrlSplitContainer";
|
||||
this.ctrlSplitContainer.Orientation = System.Windows.Forms.Orientation.Horizontal;
|
||||
//
|
||||
// ctrlSplitContainer.Panel1
|
||||
//
|
||||
this.ctrlSplitContainer.Panel1.Controls.Add(this.ctrlDisassemblyView);
|
||||
//
|
||||
// ctrlSplitContainer.Panel2
|
||||
//
|
||||
this.ctrlSplitContainer.Panel2.Controls.Add(this.tableLayoutPanel1);
|
||||
this.ctrlSplitContainer.Size = new System.Drawing.Size(852, 620);
|
||||
this.ctrlSplitContainer.SplitterDistance = 439;
|
||||
this.ctrlSplitContainer.TabIndex = 2;
|
||||
//
|
||||
// tableLayoutPanel1
|
||||
//
|
||||
this.tableLayoutPanel1.ColumnCount = 2;
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
|
||||
this.tableLayoutPanel1.Controls.Add(this.ctrlWatch, 0, 0);
|
||||
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
|
||||
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
|
||||
this.tableLayoutPanel1.RowCount = 1;
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
||||
this.tableLayoutPanel1.Size = new System.Drawing.Size(852, 177);
|
||||
this.tableLayoutPanel1.TabIndex = 0;
|
||||
//
|
||||
// ctrlWatch
|
||||
//
|
||||
this.ctrlWatch.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.ctrlWatch.Location = new System.Drawing.Point(3, 3);
|
||||
this.ctrlWatch.Name = "ctrlWatch";
|
||||
this.ctrlWatch.Size = new System.Drawing.Size(420, 171);
|
||||
this.ctrlWatch.TabIndex = 0;
|
||||
//
|
||||
// frmDebugger
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(852, 644);
|
||||
this.Controls.Add(this.ctrlDisassemblyView);
|
||||
this.Controls.Add(this.ctrlSplitContainer);
|
||||
this.Controls.Add(this.ctrlMesenMenuStrip1);
|
||||
this.Name = "frmDebugger";
|
||||
this.Text = "frmDebugger";
|
||||
this.Text = "Debugger";
|
||||
this.ctrlMesenMenuStrip1.ResumeLayout(false);
|
||||
this.ctrlMesenMenuStrip1.PerformLayout();
|
||||
this.ctrlSplitContainer.Panel1.ResumeLayout(false);
|
||||
this.ctrlSplitContainer.Panel2.ResumeLayout(false);
|
||||
((System.ComponentModel.ISupportInitialize)(this.ctrlSplitContainer)).EndInit();
|
||||
this.ctrlSplitContainer.ResumeLayout(false);
|
||||
this.tableLayoutPanel1.ResumeLayout(false);
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
|
@ -285,5 +339,8 @@
|
|||
private System.Windows.Forms.ToolStripSeparator toolStripMenuItem8;
|
||||
private System.Windows.Forms.ToolStripMenuItem mnuBreakIn;
|
||||
private System.Windows.Forms.ToolStripMenuItem mnuBreakOn;
|
||||
private GUI.Controls.ctrlSplitContainer ctrlSplitContainer;
|
||||
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
|
||||
private ctrlWatch ctrlWatch;
|
||||
}
|
||||
}
|
|
@ -76,6 +76,7 @@ namespace Mesen.GUI.Debugger
|
|||
|
||||
this.BeginInvoke((MethodInvoker)(() => {
|
||||
ctrlDisassemblyView.SetActiveAddress(activeAddress);
|
||||
ctrlWatch.UpdateWatch(true);
|
||||
}));
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -53,6 +53,8 @@ namespace Mesen.GUI
|
|||
return state;
|
||||
}
|
||||
|
||||
[DllImport(DllPath)] public static extern Int32 EvaluateExpression([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))]string expression, out EvalResultType resultType, [MarshalAs(UnmanagedType.I1)]bool useCache);
|
||||
|
||||
[DllImport(DllPath)] public static extern Int32 GetMemorySize(SnesMemoryType type);
|
||||
[DllImport(DllPath)] public static extern Byte GetMemoryValue(SnesMemoryType type, UInt32 address);
|
||||
[DllImport(DllPath)] public static extern void SetMemoryValue(SnesMemoryType type, UInt32 address, byte value);
|
||||
|
@ -131,4 +133,13 @@ namespace Mesen.GUI
|
|||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1000)]
|
||||
public byte[] Format;
|
||||
}
|
||||
|
||||
public enum EvalResultType
|
||||
{
|
||||
Numeric = 0,
|
||||
Boolean = 1,
|
||||
Invalid = 2,
|
||||
DivideBy0 = 3,
|
||||
OutOfScope = 4
|
||||
}
|
||||
}
|
||||
|
|
10
UI/UI.csproj
10
UI/UI.csproj
|
@ -301,6 +301,12 @@
|
|||
<Compile Include="Debugger\Controls\ctrlHexViewer.designer.cs">
|
||||
<DependentUpon>ctrlHexViewer.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Debugger\Controls\ctrlWatch.cs">
|
||||
<SubType>UserControl</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Debugger\Controls\ctrlWatch.designer.cs">
|
||||
<DependentUpon>ctrlWatch.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Debugger\frmDbgPreferences.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
|
@ -384,6 +390,7 @@
|
|||
<Compile Include="Debugger\HexBox\Util.cs" />
|
||||
<Compile Include="Debugger\TblLoader.cs" />
|
||||
<Compile Include="Debugger\DebugWindowManager.cs" />
|
||||
<Compile Include="Debugger\WatchManager.cs" />
|
||||
<Compile Include="Forms\BaseConfigForm.Designer.cs">
|
||||
<DependentUpon>BaseConfigForm.cs</DependentUpon>
|
||||
</Compile>
|
||||
|
@ -456,6 +463,9 @@
|
|||
<EmbeddedResource Include="Debugger\Controls\ctrlHexViewer.resx">
|
||||
<DependentUpon>ctrlHexViewer.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Debugger\Controls\ctrlWatch.resx">
|
||||
<DependentUpon>ctrlWatch.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Debugger\frmDbgPreferences.resx">
|
||||
<DependentUpon>frmDbgPreferences.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
|
|
Loading…
Add table
Reference in a new issue