2016-01-09 13:15:43 -05:00
|
|
|
#include "stdafx.h"
|
2016-01-10 13:23:19 -05:00
|
|
|
#include <climits>
|
2016-12-11 10:56:23 -05:00
|
|
|
#include <algorithm>
|
2016-01-09 13:15:43 -05:00
|
|
|
#include "ExpressionEvaluator.h"
|
|
|
|
#include "Console.h"
|
|
|
|
#include "Debugger.h"
|
2017-03-04 21:50:19 -05:00
|
|
|
#include "MemoryDumper.h"
|
2019-01-19 20:41:31 -05:00
|
|
|
#include "Disassembler.h"
|
2016-11-22 22:38:14 -05:00
|
|
|
#include "LabelManager.h"
|
2016-11-26 10:42:59 -05:00
|
|
|
#include "../Utilities/HexUtilities.h"
|
2016-01-09 13:15:43 -05:00
|
|
|
|
2017-03-03 21:03:20 -05:00
|
|
|
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 } };
|
2019-11-04 20:16:20 -05:00
|
|
|
const vector<string> ExpressionEvaluator::_unaryOperators = { { "+", "-", "~", "!", ":" } };
|
|
|
|
const vector<int> ExpressionEvaluator::_unaryPrecedence = { { 11, 11, 11, 11, 11 } };
|
|
|
|
const std::unordered_set<string> ExpressionEvaluator::_operators = { { "*", "/", "%", "+", "-", "<<", ">>", "<", "<=", ">", ">=", "==", "!=", "&", "^", "|", "&&", "||", "~", "!", "(", ")", "{", "}", "[", "]", ":" } };
|
2017-03-03 21:03:20 -05:00
|
|
|
|
2016-01-09 13:15:43 -05:00
|
|
|
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) {
|
2018-08-02 20:44:48 -04:00
|
|
|
return (EvalOperators)(EvalOperators::Plus + i);
|
2016-01-09 13:15:43 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for(size_t i = 0, len = _binaryOperators.size(); i < len; i++) {
|
|
|
|
if(token.compare(_binaryOperators[i]) == 0) {
|
2018-08-02 20:44:48 -04:00
|
|
|
return (EvalOperators)(EvalOperators::Multiplication + i);
|
2016-01-09 13:15:43 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return EvalOperators::Addition;
|
|
|
|
}
|
|
|
|
|
2018-08-02 20:44:48 -04:00
|
|
|
bool ExpressionEvaluator::CheckSpecialTokens(string expression, size_t &pos, string &output, ExpressionData &data)
|
2016-01-09 13:15:43 -05:00
|
|
|
{
|
|
|
|
string token;
|
2016-11-22 18:28:59 -05:00
|
|
|
size_t initialPos = pos;
|
2016-01-09 13:15:43 -05:00
|
|
|
size_t len = expression.size();
|
|
|
|
do {
|
|
|
|
char c = std::tolower(expression[pos]);
|
2018-06-25 15:13:32 -04:00
|
|
|
if((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_' || c == '@') {
|
2016-11-22 18:28:59 -05:00
|
|
|
//Only letters, numbers and underscore are allowed in code labels
|
2016-01-10 13:23:19 -05:00
|
|
|
token += c;
|
2016-01-09 13:15:43 -05:00
|
|
|
pos++;
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} while(pos < len);
|
|
|
|
|
2018-08-02 20:44:48 -04:00
|
|
|
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);
|
2020-04-18 18:10:14 -04:00
|
|
|
} else if(token == "pscarry") {
|
|
|
|
output += std::to_string((int64_t)EvalValues::RegPS_Carry);
|
|
|
|
} else if(token == "pszero") {
|
|
|
|
output += std::to_string((int64_t)EvalValues::RegPS_Zero);
|
|
|
|
} else if(token == "psinterrupt") {
|
|
|
|
output += std::to_string((int64_t)EvalValues::RegPS_Interrupt);
|
|
|
|
} else if(token == "psdecimal") {
|
|
|
|
output += std::to_string((int64_t)EvalValues::RegPS_Decimal);
|
|
|
|
} else if(token == "psoverflow") {
|
|
|
|
output += std::to_string((int64_t)EvalValues::RegPS_Overflow);
|
|
|
|
} else if(token == "psnegative") {
|
|
|
|
output += std::to_string((int64_t)EvalValues::RegPS_Negative);
|
2018-08-02 20:44:48 -04:00
|
|
|
} 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);
|
2019-01-19 20:41:31 -05:00
|
|
|
} else if(token == "previousoppc") {
|
|
|
|
output += std::to_string((int64_t)EvalValues::PreviousOpPC);
|
2018-08-02 20:44:48 -04:00
|
|
|
} 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);
|
2019-01-19 20:41:31 -05:00
|
|
|
} 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);
|
2018-08-02 20:44:48 -04:00
|
|
|
} 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 == "iswrite") {
|
|
|
|
output += std::to_string((int64_t)EvalValues::IsWrite);
|
|
|
|
} else if(token == "isread") {
|
|
|
|
output += std::to_string((int64_t)EvalValues::IsRead);
|
2019-01-19 20:41:31 -05:00
|
|
|
} else if(token == "branched") {
|
|
|
|
output += std::to_string((int64_t)EvalValues::Branched);
|
2016-01-09 13:15:43 -05:00
|
|
|
} else {
|
2016-11-22 18:28:59 -05:00
|
|
|
string originalExpression = expression.substr(initialPos, pos - initialPos);
|
2018-08-02 20:44:48 -04:00
|
|
|
bool validLabel = _debugger->GetLabelManager()->ContainsLabel(originalExpression);
|
2019-01-16 19:07:50 -05:00
|
|
|
if(!validLabel) {
|
|
|
|
//Check if a multi-byte label exists for this name
|
|
|
|
string label = originalExpression + "+0";
|
|
|
|
validLabel = _debugger->GetLabelManager()->ContainsLabel(label);
|
|
|
|
}
|
|
|
|
|
2018-08-02 20:44:48 -04:00
|
|
|
if(validLabel) {
|
|
|
|
data.Labels.push_back(originalExpression);
|
|
|
|
output += std::to_string(EvalValues::FirstLabelIndex + data.Labels.size() - 1);
|
2016-11-22 18:28:59 -05:00
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
2016-01-09 13:15:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-05-19 21:55:43 -04:00
|
|
|
string ExpressionEvaluator::GetNextToken(string expression, size_t& pos, ExpressionData& data, bool& success, bool previousTokenIsOp)
|
2016-01-09 13:15:43 -05:00
|
|
|
{
|
|
|
|
string output;
|
2019-01-29 18:32:17 -05:00
|
|
|
success = true;
|
|
|
|
|
2019-01-06 18:41:33 -05:00
|
|
|
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')) {
|
2016-01-09 13:15:43 -05:00
|
|
|
output += c;
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
2019-01-06 18:41:33 -05:00
|
|
|
}
|
2019-01-29 18:32:17 -05:00
|
|
|
if(output.empty()) {
|
|
|
|
//No numbers followed the hex mark, this isn't a valid expression
|
|
|
|
success = false;
|
|
|
|
}
|
2019-01-06 18:41:33 -05:00
|
|
|
output = std::to_string((uint32_t)HexUtilities::FromHex(output));
|
2020-05-19 21:55:43 -04:00
|
|
|
} else if(c == '%' && previousTokenIsOp) {
|
2019-01-29 19:15:57 -05:00
|
|
|
//Binary numbers
|
|
|
|
pos++;
|
|
|
|
for(size_t len = expression.size(); pos < len; pos++) {
|
|
|
|
c = std::tolower(expression[pos]);
|
2020-05-19 21:55:43 -04:00
|
|
|
if(c == '0' || c == '1') {
|
2019-01-29 19:15:57 -05:00
|
|
|
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);
|
2019-01-06 18:41:33 -05:00
|
|
|
} 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') {
|
2016-01-09 13:15:43 -05:00
|
|
|
output += c;
|
2019-01-06 18:41:33 -05:00
|
|
|
} else {
|
2016-01-09 13:15:43 -05:00
|
|
|
break;
|
2019-01-06 18:41:33 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} 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)
|
2016-01-09 13:15:43 -05:00
|
|
|
output += c;
|
|
|
|
} else {
|
2019-01-06 18:41:33 -05:00
|
|
|
//Reached the end of the operator, return
|
|
|
|
break;
|
2016-01-09 13:15:43 -05:00
|
|
|
}
|
|
|
|
}
|
2019-01-06 18:41:33 -05:00
|
|
|
} else {
|
|
|
|
//Special tokens and labels
|
2019-01-29 18:32:17 -05:00
|
|
|
success = CheckSpecialTokens(expression, pos, output, data);
|
2016-01-09 13:15:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
2018-08-02 20:44:48 -04:00
|
|
|
bool ExpressionEvaluator::ProcessSpecialOperator(EvalOperators evalOp, std::stack<EvalOperators> &opStack, std::stack<int> &precedenceStack, vector<int64_t> &outputQueue)
|
2016-12-10 15:18:50 -05:00
|
|
|
{
|
|
|
|
if(opStack.empty()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
while(opStack.top() != evalOp) {
|
|
|
|
outputQueue.push_back(opStack.top());
|
|
|
|
opStack.pop();
|
2018-02-20 20:04:43 -05:00
|
|
|
precedenceStack.pop();
|
2016-12-10 15:18:50 -05:00
|
|
|
|
|
|
|
if(opStack.empty()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(evalOp != EvalOperators::Parenthesis) {
|
|
|
|
outputQueue.push_back(opStack.top());
|
|
|
|
}
|
|
|
|
opStack.pop();
|
2018-02-20 20:04:43 -05:00
|
|
|
precedenceStack.pop();
|
2016-12-10 15:18:50 -05:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-08-02 20:44:48 -04:00
|
|
|
bool ExpressionEvaluator::ToRpn(string expression, ExpressionData &data)
|
2016-01-09 13:15:43 -05:00
|
|
|
{
|
|
|
|
std::stack<EvalOperators> opStack = std::stack<EvalOperators>();
|
|
|
|
std::stack<int> precedenceStack;
|
|
|
|
|
|
|
|
size_t position = 0;
|
2016-12-10 15:18:50 -05:00
|
|
|
int parenthesisCount = 0;
|
|
|
|
int bracketCount = 0;
|
|
|
|
int braceCount = 0;
|
|
|
|
|
2016-01-09 13:15:43 -05:00
|
|
|
bool previousTokenIsOp = true;
|
2019-01-29 18:55:40 -05:00
|
|
|
bool operatorExpected = false;
|
2019-01-29 19:15:57 -05:00
|
|
|
bool operatorOrEndTokenExpected = false;
|
2016-01-09 13:15:43 -05:00
|
|
|
while(true) {
|
2019-01-29 18:32:17 -05:00
|
|
|
bool success = true;
|
2020-05-19 21:55:43 -04:00
|
|
|
string token = GetNextToken(expression, position, data, success, previousTokenIsOp);
|
2019-01-29 18:32:17 -05:00
|
|
|
if(!success) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-01-09 13:15:43 -05:00
|
|
|
if(token.empty()) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-01-29 18:55:40 -05:00
|
|
|
bool requireOperator = operatorExpected;
|
2019-01-29 19:15:57 -05:00
|
|
|
bool requireOperatorOrEndToken = operatorOrEndTokenExpected;
|
2016-01-09 13:15:43 -05:00
|
|
|
bool unaryOperator = previousTokenIsOp;
|
2019-01-29 19:15:57 -05:00
|
|
|
|
|
|
|
operatorExpected = false;
|
|
|
|
operatorOrEndTokenExpected = false;
|
2016-01-09 13:15:43 -05:00
|
|
|
previousTokenIsOp = false;
|
2019-01-29 19:15:57 -05:00
|
|
|
|
2016-01-09 13:15:43 -05:00
|
|
|
int precedence = 0;
|
|
|
|
if(IsOperator(token, precedence, unaryOperator)) {
|
|
|
|
EvalOperators op = GetOperator(token, unaryOperator);
|
2019-01-06 18:41:33 -05:00
|
|
|
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();
|
2016-01-09 13:15:43 -05:00
|
|
|
}
|
|
|
|
opStack.push(op);
|
|
|
|
precedenceStack.push(precedence);
|
2019-01-29 19:15:57 -05:00
|
|
|
|
2016-01-09 13:15:43 -05:00
|
|
|
previousTokenIsOp = true;
|
2019-01-29 18:55:40 -05:00
|
|
|
} 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;
|
2019-01-29 19:15:57 -05:00
|
|
|
} 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;
|
2016-01-09 13:15:43 -05:00
|
|
|
} else if(token[0] == '(') {
|
2016-12-10 15:18:50 -05:00
|
|
|
parenthesisCount++;
|
2016-01-09 13:15:43 -05:00
|
|
|
opStack.push(EvalOperators::Parenthesis);
|
|
|
|
precedenceStack.push(0);
|
2016-11-22 16:56:23 -05:00
|
|
|
previousTokenIsOp = true;
|
2016-01-09 13:15:43 -05:00
|
|
|
} else if(token[0] == ')') {
|
2016-12-10 15:18:50 -05:00
|
|
|
parenthesisCount--;
|
2018-08-02 20:44:48 -04:00
|
|
|
if(!ProcessSpecialOperator(EvalOperators::Parenthesis, opStack, precedenceStack, data.RpnQueue)) {
|
2016-12-10 15:18:50 -05:00
|
|
|
return false;
|
2016-01-09 13:15:43 -05:00
|
|
|
}
|
2020-04-18 17:55:35 -04:00
|
|
|
operatorOrEndTokenExpected = true;
|
2016-06-05 11:51:46 -04:00
|
|
|
} else if(token[0] == '[') {
|
2016-12-10 15:18:50 -05:00
|
|
|
bracketCount++;
|
2016-06-05 11:51:46 -04:00
|
|
|
opStack.push(EvalOperators::Bracket);
|
|
|
|
precedenceStack.push(0);
|
|
|
|
} else if(token[0] == ']') {
|
2016-12-10 15:18:50 -05:00
|
|
|
bracketCount--;
|
2018-08-02 20:44:48 -04:00
|
|
|
if(!ProcessSpecialOperator(EvalOperators::Bracket, opStack, precedenceStack, data.RpnQueue)) {
|
2016-12-10 15:18:50 -05:00
|
|
|
return false;
|
2016-06-05 11:51:46 -04:00
|
|
|
}
|
2020-04-18 17:55:35 -04:00
|
|
|
operatorOrEndTokenExpected = true;
|
2016-11-22 16:56:23 -05:00
|
|
|
} else if(token[0] == '{') {
|
2016-12-10 15:18:50 -05:00
|
|
|
braceCount++;
|
2016-11-22 16:56:23 -05:00
|
|
|
opStack.push(EvalOperators::Braces);
|
|
|
|
precedenceStack.push(0);
|
|
|
|
} else if(token[0] == '}') {
|
2016-12-10 15:18:50 -05:00
|
|
|
braceCount--;
|
2018-08-02 20:44:48 -04:00
|
|
|
if(!ProcessSpecialOperator(EvalOperators::Braces, opStack, precedenceStack, data.RpnQueue)){
|
2016-12-10 15:18:50 -05:00
|
|
|
return false;
|
2016-11-22 16:56:23 -05:00
|
|
|
}
|
2020-04-18 17:55:35 -04:00
|
|
|
operatorOrEndTokenExpected = true;
|
2016-01-09 13:15:43 -05:00
|
|
|
} else {
|
2017-03-03 21:03:20 -05:00
|
|
|
if(token[0] < '0' || token[0] > '9') {
|
|
|
|
return false;
|
|
|
|
} else {
|
2018-08-02 20:44:48 -04:00
|
|
|
data.RpnQueue.push_back(std::stoll(token));
|
2019-01-29 19:15:57 -05:00
|
|
|
operatorOrEndTokenExpected = true;
|
2017-03-03 21:03:20 -05:00
|
|
|
}
|
2016-01-09 13:15:43 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-10 15:18:50 -05:00
|
|
|
if(braceCount || bracketCount || parenthesisCount) {
|
|
|
|
//Mismatching number of brackets/braces/parenthesis
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-01-09 13:15:43 -05:00
|
|
|
while(!opStack.empty()) {
|
2018-08-02 20:44:48 -04:00
|
|
|
data.RpnQueue.push_back(opStack.top());
|
2016-01-09 13:15:43 -05:00
|
|
|
opStack.pop();
|
|
|
|
}
|
2016-12-10 15:18:50 -05:00
|
|
|
|
|
|
|
return true;
|
2016-01-09 13:15:43 -05:00
|
|
|
}
|
|
|
|
|
2018-08-02 20:44:48 -04:00
|
|
|
int32_t ExpressionEvaluator::Evaluate(ExpressionData &data, DebugState &state, EvalResultType &resultType, OperationInfo &operationInfo)
|
2016-01-09 13:15:43 -05:00
|
|
|
{
|
2018-08-02 20:44:48 -04:00
|
|
|
if(data.RpnQueue.empty()) {
|
|
|
|
resultType = EvalResultType::Invalid;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-01-09 13:15:43 -05:00
|
|
|
int pos = 0;
|
2018-08-02 20:44:48 -04:00
|
|
|
int64_t right = 0;
|
|
|
|
int64_t left = 0;
|
2016-01-10 13:23:19 -05:00
|
|
|
resultType = EvalResultType::Numeric;
|
2016-01-09 13:15:43 -05:00
|
|
|
|
2018-08-02 20:44:48 -04:00
|
|
|
for(size_t i = 0, len = data.RpnQueue.size(); i < len; i++) {
|
|
|
|
int64_t token = data.RpnQueue[i];
|
2016-01-09 13:15:43 -05:00
|
|
|
|
|
|
|
if(token >= EvalValues::RegA) {
|
|
|
|
//Replace value with a special value
|
2018-08-02 20:44:48 -04:00
|
|
|
if(token >= EvalValues::FirstLabelIndex) {
|
|
|
|
int64_t labelIndex = token - EvalValues::FirstLabelIndex;
|
|
|
|
if((size_t)labelIndex < data.Labels.size()) {
|
2019-01-11 21:20:26 -05:00
|
|
|
token = _debugger->GetLabelManager()->GetLabelRelativeAddress(data.Labels[(uint32_t)labelIndex]);
|
2019-01-16 19:07:50 -05:00
|
|
|
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);
|
|
|
|
}
|
2018-08-02 20:44:48 -04:00
|
|
|
} else {
|
2019-01-16 19:07:50 -05:00
|
|
|
token = -2;
|
2018-08-02 20:44:48 -04:00
|
|
|
}
|
|
|
|
if(token < 0) {
|
|
|
|
//Label is no longer valid
|
2019-01-16 19:07:50 -05:00
|
|
|
resultType = token == -1 ? EvalResultType::OutOfScope : EvalResultType::Invalid;
|
2018-08-02 20:44:48 -04:00
|
|
|
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;
|
|
|
|
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;
|
2019-01-19 20:41:31 -05:00
|
|
|
case EvalValues::Nmi: token = state.CPU.NMIFlag; resultType = EvalResultType::Boolean; break;
|
|
|
|
case EvalValues::Irq: token = state.CPU.IRQFlag; resultType = EvalResultType::Boolean; break;
|
2018-08-02 20:44:48 -04:00
|
|
|
case EvalValues::Value: token = operationInfo.Value; break;
|
|
|
|
case EvalValues::Address: token = operationInfo.Address; break;
|
2018-12-23 13:12:46 -05:00
|
|
|
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;
|
2019-01-19 20:41:31 -05:00
|
|
|
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;
|
2020-04-18 18:10:14 -04:00
|
|
|
case EvalValues::RegPS_Carry: token = (state.CPU.PS & PSFlags::Carry) != 0; resultType = EvalResultType::Boolean; break;
|
|
|
|
case EvalValues::RegPS_Zero: token = (state.CPU.PS & PSFlags::Zero) != 0; resultType = EvalResultType::Boolean; break;
|
|
|
|
case EvalValues::RegPS_Interrupt: token = (state.CPU.PS & PSFlags::Interrupt) != 0; resultType = EvalResultType::Boolean; break;
|
|
|
|
case EvalValues::RegPS_Decimal: token = (state.CPU.PS & PSFlags::Decimal) != 0; resultType = EvalResultType::Boolean; break;
|
|
|
|
case EvalValues::RegPS_Overflow: token = (state.CPU.PS & PSFlags::Overflow) != 0; resultType = EvalResultType::Boolean; break;
|
|
|
|
case EvalValues::RegPS_Negative: token = (state.CPU.PS & PSFlags::Negative) != 0; resultType = EvalResultType::Boolean; break;
|
2018-08-02 20:44:48 -04:00
|
|
|
}
|
2016-01-10 13:23:19 -05:00
|
|
|
}
|
2016-01-09 13:15:43 -05:00
|
|
|
} else if(token >= EvalOperators::Multiplication) {
|
|
|
|
right = operandStack[--pos];
|
2016-11-22 16:56:23 -05:00
|
|
|
if(pos > 0 && token <= EvalOperators::LogicalOr) {
|
|
|
|
//Only do this for binary operators
|
2016-01-09 13:15:43 -05:00
|
|
|
left = operandStack[--pos];
|
|
|
|
}
|
|
|
|
|
2016-01-10 13:23:19 -05:00
|
|
|
resultType = EvalResultType::Numeric;
|
2016-01-09 13:15:43 -05:00
|
|
|
switch(token) {
|
|
|
|
case EvalOperators::Multiplication: token = left * right; break;
|
2017-12-29 22:08:50 -05:00
|
|
|
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;
|
2016-01-09 13:15:43 -05:00
|
|
|
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;
|
2016-01-10 13:23:19 -05:00
|
|
|
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;
|
2016-01-09 13:15:43 -05:00
|
|
|
case EvalOperators::BinaryAnd: token = left & right; break;
|
2017-12-29 22:11:16 -05:00
|
|
|
case EvalOperators::BinaryXor: token = left ^ right; break;
|
|
|
|
case EvalOperators::BinaryOr: token = left | right; break;
|
2016-01-10 13:23:19 -05:00
|
|
|
case EvalOperators::LogicalAnd: token = left && right; resultType = EvalResultType::Boolean; break;
|
|
|
|
case EvalOperators::LogicalOr: token = left || right; resultType = EvalResultType::Boolean; break;
|
2016-01-09 13:15:43 -05:00
|
|
|
|
|
|
|
//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;
|
2019-11-04 20:16:20 -05:00
|
|
|
case EvalOperators::AbsoluteAddress: {
|
|
|
|
if(right >= 0) {
|
|
|
|
AddressTypeInfo addressInfo;
|
|
|
|
_debugger->GetAbsoluteAddressAndType((uint32_t)right, &addressInfo);
|
|
|
|
token = addressInfo.Address;
|
|
|
|
} else {
|
|
|
|
token = -1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2018-08-02 20:44:48 -04:00
|
|
|
case EvalOperators::Bracket: token = _debugger->GetMemoryDumper()->GetMemoryValue(DebugMemoryType::CpuMemory, (uint32_t)right); break;
|
|
|
|
case EvalOperators::Braces: token = _debugger->GetMemoryDumper()->GetMemoryValueWord(DebugMemoryType::CpuMemory, (uint32_t)right); break;
|
2016-01-09 13:15:43 -05:00
|
|
|
default: throw std::runtime_error("Invalid operator");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
operandStack[pos++] = token;
|
|
|
|
}
|
2018-08-02 20:44:48 -04:00
|
|
|
return (int32_t)operandStack[0];
|
2016-01-09 13:15:43 -05:00
|
|
|
}
|
|
|
|
|
2016-11-22 18:28:59 -05:00
|
|
|
ExpressionEvaluator::ExpressionEvaluator(Debugger* debugger)
|
2016-01-09 13:15:43 -05:00
|
|
|
{
|
2016-11-22 18:28:59 -05:00
|
|
|
_debugger = debugger;
|
2016-01-09 13:15:43 -05:00
|
|
|
}
|
|
|
|
|
2018-08-02 20:44:48 -04:00
|
|
|
ExpressionData ExpressionEvaluator::GetRpnList(string expression, bool &success)
|
2016-01-09 13:15:43 -05:00
|
|
|
{
|
2018-08-02 20:44:48 -04:00
|
|
|
ExpressionData* cachedData = PrivateGetRpnList(expression, success);
|
|
|
|
if(cachedData) {
|
|
|
|
return *cachedData;
|
|
|
|
} else {
|
|
|
|
return ExpressionData();
|
|
|
|
}
|
2017-03-03 21:03:20 -05:00
|
|
|
}
|
2016-01-09 13:15:43 -05:00
|
|
|
|
2018-08-02 20:44:48 -04:00
|
|
|
ExpressionData* ExpressionEvaluator::PrivateGetRpnList(string expression, bool& success)
|
2017-03-03 21:03:20 -05:00
|
|
|
{
|
2018-08-02 20:44:48 -04:00
|
|
|
ExpressionData *cachedData = nullptr;
|
2016-01-09 13:15:43 -05:00
|
|
|
{
|
|
|
|
LockHandler lock = _cacheLock.AcquireSafe();
|
|
|
|
|
2018-08-02 20:44:48 -04:00
|
|
|
auto result = _cache.find(expression);
|
|
|
|
if(result != _cache.end()) {
|
|
|
|
cachedData = &(result->second);
|
2016-01-09 13:15:43 -05:00
|
|
|
}
|
|
|
|
}
|
2017-03-03 21:03:20 -05:00
|
|
|
|
2018-08-02 20:44:48 -04:00
|
|
|
if(cachedData == nullptr) {
|
2016-01-09 13:15:43 -05:00
|
|
|
string fixedExp = expression;
|
|
|
|
fixedExp.erase(std::remove(fixedExp.begin(), fixedExp.end(), ' '), fixedExp.end());
|
2018-08-02 20:44:48 -04:00
|
|
|
ExpressionData data;
|
|
|
|
success = ToRpn(fixedExp, data);
|
|
|
|
if(success) {
|
2017-03-03 21:03:20 -05:00
|
|
|
LockHandler lock = _cacheLock.AcquireSafe();
|
2018-08-02 20:44:48 -04:00
|
|
|
_cache[expression] = data;
|
|
|
|
cachedData = &_cache[expression];
|
2016-11-22 18:28:59 -05:00
|
|
|
}
|
2018-08-02 20:44:48 -04:00
|
|
|
} else {
|
|
|
|
success = true;
|
2016-01-09 13:15:43 -05:00
|
|
|
}
|
|
|
|
|
2018-08-02 20:44:48 -04:00
|
|
|
return cachedData;
|
2016-01-09 13:15:43 -05:00
|
|
|
}
|
|
|
|
|
2017-08-05 12:13:53 -04:00
|
|
|
int32_t ExpressionEvaluator::PrivateEvaluate(string expression, DebugState &state, EvalResultType &resultType, OperationInfo &operationInfo, bool& success)
|
2016-01-10 13:23:19 -05:00
|
|
|
{
|
2017-03-03 21:03:20 -05:00
|
|
|
success = true;
|
2018-08-02 20:44:48 -04:00
|
|
|
ExpressionData *cachedData = PrivateGetRpnList(expression, success);
|
2017-03-03 21:03:20 -05:00
|
|
|
|
|
|
|
if(!success) {
|
2019-01-06 18:41:33 -05:00
|
|
|
resultType = EvalResultType::Invalid;
|
2017-03-03 21:03:20 -05:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-08-02 20:44:48 -04:00
|
|
|
return Evaluate(*cachedData, state, resultType, operationInfo);
|
2016-01-10 13:23:19 -05:00
|
|
|
}
|
|
|
|
|
2017-08-05 12:13:53 -04:00
|
|
|
int32_t ExpressionEvaluator::Evaluate(string expression, DebugState &state, EvalResultType &resultType, OperationInfo &operationInfo)
|
2016-01-09 13:15:43 -05:00
|
|
|
{
|
|
|
|
try {
|
2016-12-10 15:18:50 -05:00
|
|
|
bool success;
|
2017-08-05 12:13:53 -04:00
|
|
|
int32_t result = PrivateEvaluate(expression, state, resultType, operationInfo, success);
|
2016-12-10 15:18:50 -05:00
|
|
|
if(success) {
|
|
|
|
return result;
|
|
|
|
}
|
2019-11-13 18:30:05 -05:00
|
|
|
} catch(std::exception&) {
|
2016-01-09 13:15:43 -05:00
|
|
|
}
|
2016-12-10 15:18:50 -05:00
|
|
|
resultType = EvalResultType::Invalid;
|
|
|
|
return 0;
|
2016-01-09 13:15:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ExpressionEvaluator::Validate(string expression)
|
|
|
|
{
|
|
|
|
try {
|
|
|
|
DebugState state;
|
2016-01-10 13:23:19 -05:00
|
|
|
EvalResultType type;
|
2017-08-05 12:13:53 -04:00
|
|
|
OperationInfo operationInfo;
|
2016-12-10 15:18:50 -05:00
|
|
|
bool success;
|
2017-08-05 12:13:53 -04:00
|
|
|
PrivateEvaluate(expression, state, type, operationInfo, success);
|
2016-12-10 15:18:50 -05:00
|
|
|
return success;
|
2019-11-13 18:30:05 -05:00
|
|
|
} catch(std::exception&) {
|
2016-01-09 13:15:43 -05:00
|
|
|
return false;
|
|
|
|
}
|
2019-01-06 18:41:33 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#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);
|
2019-01-16 19:07:50 -05:00
|
|
|
|
2019-11-23 21:23:35 -05:00
|
|
|
test("{$4100}", EvalResultType::Numeric, 0x4141);
|
|
|
|
test("[$4100]", EvalResultType::Numeric, 0x41);
|
2019-01-29 19:15:57 -05:00
|
|
|
|
|
|
|
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);
|
2019-11-04 20:16:20 -05:00
|
|
|
|
2020-05-19 21:55:43 -04:00
|
|
|
test("10 % 5", EvalResultType::Numeric, 0);
|
|
|
|
test("%100 % 5", EvalResultType::Numeric, 4);
|
|
|
|
test("%100%5", EvalResultType::Numeric, 4);
|
|
|
|
test("%10(10)", EvalResultType::Invalid, 0);
|
|
|
|
test("10%4*10", EvalResultType::Numeric, 20);
|
|
|
|
test("(5+5)%3", EvalResultType::Numeric, 1);
|
|
|
|
test("11%%10", EvalResultType::Numeric, 1); //11 modulo of 2 in binary (%10)
|
|
|
|
|
2019-11-04 20:16:20 -05:00
|
|
|
test(":$00", EvalResultType::Numeric, 0);
|
|
|
|
test(":50", EvalResultType::Numeric, 50);
|
|
|
|
test(":$50", EvalResultType::Numeric, 0x50);
|
|
|
|
test(":($1FFF+1)", EvalResultType::Numeric, -1);
|
2020-02-23 09:47:10 -05:00
|
|
|
test(":$1FFF+1", EvalResultType::Numeric, 0x800);
|
2019-11-04 20:16:20 -05:00
|
|
|
test("1+:$100", EvalResultType::Numeric, 0x101);
|
2020-04-18 17:55:35 -04:00
|
|
|
|
|
|
|
test("[$4100+[$4100]]", EvalResultType::Numeric, 0x41);
|
|
|
|
test("-($10+[$4100])", EvalResultType::Numeric, -0x51);
|
2019-01-06 18:41:33 -05:00
|
|
|
}
|
|
|
|
#endif
|