diff --git a/CHANGES b/CHANGES index 217ec4ddf..962e2e6a2 100644 --- a/CHANGES +++ b/CHANGES @@ -3,6 +3,7 @@ Features: - Improved logging configuration - One-Player BattleChip/Progress/Beast Link Gate support - Add Game Boy Color palettes for original Game Boy games + - Debugger: Add unary operators and memory dereferencing Emulation fixes: - GBA: All IRQs have 7 cycle delay (fixes mgba.io/i/539, mgba.io/i/1208) - GBA: Reset now reloads multiboot ROMs diff --git a/include/mgba/internal/debugger/parser.h b/include/mgba/internal/debugger/parser.h index 6092c1f8c..37f194ddb 100644 --- a/include/mgba/internal/debugger/parser.h +++ b/include/mgba/internal/debugger/parser.h @@ -38,6 +38,7 @@ enum Operation { OP_NOT, OP_SHIFT_L, OP_SHIFT_R, + OP_DEREFERENCE, }; struct Token { diff --git a/src/debugger/parser.c b/src/debugger/parser.c index 41eafc6d4..adad22e21 100644 --- a/src/debugger/parser.c +++ b/src/debugger/parser.c @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include +#include #include #include @@ -89,6 +90,29 @@ static void _lexOperator(struct LexVector* lv, char operator, enum LexState* sta break; } *state = LEX_ERROR; + } + if (*state == LEX_ROOT || *state == LEX_ERROR) { + struct Token* lvNext = LexVectorAppend(lv); + lvNext->type = TOKEN_OPERATOR_TYPE; + *state = LEX_ROOT; + switch (operator) { + case '-': + lvNext->operatorValue = OP_NEGATE; + break; + case '~': + lvNext->operatorValue = OP_FLIP; + break; + case '!': + lvNext->operatorValue = OP_NOT; + break; + case '*': + lvNext->operatorValue = OP_DEREFERENCE; + break; + default: + lvNext->type = TOKEN_ERROR_TYPE; + *state = LEX_ERROR; + break; + } return; } struct Token* lvNext = LexVectorAppend(lv); @@ -247,6 +271,12 @@ size_t lexExpression(struct LexVector* lv, const char* string, size_t length, co lvNext = LexVectorAppend(lv); lvNext->type = TOKEN_OPEN_PAREN_TYPE; break; + case '!': + case '-': + case '~': + case '*': + _lexOperator(lv, token, &state); + break; case ' ': case '\t': break; @@ -499,6 +529,7 @@ static const int _operatorPrecedence[] = { [OP_NOT] = 2, [OP_SHIFT_L] = 5, [OP_SHIFT_R] = 5, + [OP_DEREFERENCE] = 2, }; static struct ParseTree* _parseTreeCreate() { @@ -622,7 +653,7 @@ void parseFree(struct ParseTree* tree) { } } -static bool _performOperation(enum Operation operation, int32_t current, int32_t next, int32_t* value) { +static bool _performOperation(struct mDebugger* debugger, enum Operation operation, int32_t current, int32_t next, int32_t* value, int* segment) { switch (operation) { case OP_ASSIGN: current = next; @@ -683,12 +714,29 @@ static bool _performOperation(enum Operation operation, int32_t current, int32_t case OP_GE: current = current >= next; break; + case OP_NEGATE: + current = -next; + break; + case OP_FLIP: + current = ~next; + break; + case OP_NOT: + current = !next; + break; case OP_SHIFT_L: current <<= next; break; case OP_SHIFT_R: current >>= next; break; + case OP_DEREFERENCE: + if (*segment < 0) { + current = debugger->core->busRead8(debugger->core, next); + } else { + current = debugger->core->rawRead8(debugger->core, next, *segment); + } + *segment = -1; + break; default: return false; } @@ -714,13 +762,37 @@ bool mDebuggerEvaluateParseTree(struct mDebugger* debugger, struct ParseTree* tr } return mDebuggerEvaluateParseTree(debugger, tree->lhs, segment, NULL); case TOKEN_OPERATOR_TYPE: - if (!mDebuggerEvaluateParseTree(debugger, tree->lhs, &lhs, segment)) { - return false; + switch (tree->token.operatorValue) { + case OP_ASSIGN: + case OP_ADD: + case OP_SUBTRACT: + case OP_MULTIPLY: + case OP_DIVIDE: + case OP_MODULO: + case OP_AND: + case OP_OR: + case OP_XOR: + case OP_LESS: + case OP_GREATER: + case OP_EQUAL: + case OP_NOT_EQUAL: + case OP_LOGICAL_AND: + case OP_LOGICAL_OR: + case OP_LE: + case OP_GE: + case OP_SHIFT_L: + case OP_SHIFT_R: + if (!mDebuggerEvaluateParseTree(debugger, tree->lhs, &lhs, segment)) { + return false; + } + // Fall through + default: + if (!mDebuggerEvaluateParseTree(debugger, tree->rhs, &rhs, segment)) { + return false; + } + break; } - if (!mDebuggerEvaluateParseTree(debugger, tree->rhs, &rhs, segment)) { - return false; - } - return _performOperation(tree->token.operatorValue, lhs, rhs, value); + return _performOperation(debugger, tree->token.operatorValue, lhs, rhs, value, segment); case TOKEN_IDENTIFIER_TYPE: return mDebuggerLookupIdentifier(debugger, tree->token.identifierValue, value, segment); case TOKEN_ERROR_TYPE: diff --git a/src/lr35902/debugger/cli-debugger.c b/src/lr35902/debugger/cli-debugger.c index fe85bca27..4fefd77f3 100644 --- a/src/lr35902/debugger/cli-debugger.c +++ b/src/lr35902/debugger/cli-debugger.c @@ -74,7 +74,7 @@ static inline uint16_t _printLine(struct CLIDebugger* debugger, uint16_t address }; disPtr[0] = '\t'; ++disPtr; - LR35902Disassemble(&info, disPtr, sizeof(disassembly) - (disPtr - disassembly)); + LR35902Disassemble(&info, address, disPtr, sizeof(disassembly) - (disPtr - disassembly)); be->printf(be, "%s\n", disassembly); return address; } diff --git a/src/lr35902/debugger/debugger.c b/src/lr35902/debugger/debugger.c index 398131d5d..47ae68533 100644 --- a/src/lr35902/debugger/debugger.c +++ b/src/lr35902/debugger/debugger.c @@ -215,7 +215,7 @@ static void LR35902DebuggerTrace(struct mDebuggerPlatform* d, char* out, size_t* disPtr[0] = ':'; disPtr[1] = ' '; disPtr += 2; - LR35902Disassemble(&info, disPtr, sizeof(disassembly) - (disPtr - disassembly)); + LR35902Disassemble(&info, address, disPtr, sizeof(disassembly) - (disPtr - disassembly)); *length = snprintf(out, *length, "A: %02X F: %02X B: %02X C: %02X D: %02X E: %02X H: %02X L: %02X SP: %04X PC: %02X:%04X | %s", cpu->a, cpu->f.packed, cpu->b, cpu->c,