mirror of https://github.com/mgba-emu/mgba.git
Debugger: Conditional breakpoints
This commit is contained in:
parent
178017a9e0
commit
0383c82b46
1
CHANGES
1
CHANGES
|
@ -11,6 +11,7 @@ Features:
|
|||
- Automatic cheat loading and saving
|
||||
- GameShark and Action Replay button support
|
||||
- AGBPrint support
|
||||
- Debugger: Conditional breakpoints
|
||||
Bugfixes:
|
||||
- GB Audio: Make audio unsigned with bias (fixes mgba.io/i/749)
|
||||
- GB Serialize: Fix audio state loading
|
||||
|
|
|
@ -70,6 +70,7 @@ struct mDebuggerEntryInfo {
|
|||
};
|
||||
|
||||
struct mDebugger;
|
||||
struct ParseTree;
|
||||
struct mDebuggerPlatform {
|
||||
struct mDebugger* p;
|
||||
|
||||
|
@ -79,6 +80,7 @@ struct mDebuggerPlatform {
|
|||
|
||||
bool (*hasBreakpoints)(struct mDebuggerPlatform*);
|
||||
void (*setBreakpoint)(struct mDebuggerPlatform*, uint32_t address, int segment);
|
||||
void (*setConditionalBreakpoint)(struct mDebuggerPlatform*, uint32_t address, int segment, struct ParseTree* condition);
|
||||
void (*clearBreakpoint)(struct mDebuggerPlatform*, uint32_t address, int segment);
|
||||
void (*setWatchpoint)(struct mDebuggerPlatform*, uint32_t address, int segment, enum mWatchpointType type);
|
||||
void (*clearWatchpoint)(struct mDebuggerPlatform*, uint32_t address, int segment);
|
||||
|
|
|
@ -15,8 +15,10 @@ CXX_GUARD_START
|
|||
#include <mgba/internal/arm/arm.h>
|
||||
#include <mgba-util/vector.h>
|
||||
|
||||
struct ParseTree;
|
||||
struct ARMDebugBreakpoint {
|
||||
uint32_t address;
|
||||
struct ParseTree* condition;
|
||||
bool isSw;
|
||||
struct {
|
||||
uint32_t opcode;
|
||||
|
|
|
@ -12,6 +12,9 @@
|
|||
|
||||
CXX_GUARD_START
|
||||
|
||||
struct Token;
|
||||
DECLARE_VECTOR(LexVector, struct Token);
|
||||
|
||||
enum Operation {
|
||||
OP_ASSIGN,
|
||||
OP_ADD,
|
||||
|
@ -54,15 +57,13 @@ struct Token {
|
|||
};
|
||||
};
|
||||
|
||||
DECLARE_VECTOR(LexVector, struct Token);
|
||||
|
||||
struct ParseTree {
|
||||
struct Token token;
|
||||
struct ParseTree* lhs;
|
||||
struct ParseTree* rhs;
|
||||
};
|
||||
|
||||
size_t lexExpression(struct LexVector* lv, const char* string, size_t length);
|
||||
size_t lexExpression(struct LexVector* lv, const char* string, size_t length, const char* eol);
|
||||
void parseLexedExpression(struct ParseTree* tree, struct LexVector* lv);
|
||||
|
||||
void lexFree(struct LexVector* lv);
|
||||
|
|
|
@ -15,10 +15,11 @@ CXX_GUARD_START
|
|||
#include <mgba/internal/lr35902/lr35902.h>
|
||||
#include <mgba-util/vector.h>
|
||||
|
||||
|
||||
struct ParseTree;
|
||||
struct LR35902DebugBreakpoint {
|
||||
uint16_t address;
|
||||
int segment;
|
||||
struct ParseTree* condition;
|
||||
};
|
||||
|
||||
struct LR35902DebugWatchpoint {
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <mgba/internal/arm/decoder.h>
|
||||
#include <mgba/internal/arm/isa-inlines.h>
|
||||
#include <mgba/internal/arm/debugger/memory-debugger.h>
|
||||
#include <mgba/internal/debugger/parser.h>
|
||||
|
||||
DEFINE_VECTOR(ARMDebugBreakpointList, struct ARMDebugBreakpoint);
|
||||
DEFINE_VECTOR(ARMDebugWatchpointList, struct ARMDebugWatchpoint);
|
||||
|
@ -24,6 +25,13 @@ static struct ARMDebugBreakpoint* _lookupBreakpoint(struct ARMDebugBreakpointLis
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void _destroyBreakpoint(struct ARMDebugBreakpoint* breakpoint) {
|
||||
if (breakpoint->condition) {
|
||||
parseFree(breakpoint->condition);
|
||||
free(breakpoint->condition);
|
||||
}
|
||||
}
|
||||
|
||||
static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform* d) {
|
||||
struct ARMDebugger* debugger = (struct ARMDebugger*) d;
|
||||
int instructionLength;
|
||||
|
@ -37,6 +45,13 @@ static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform* d) {
|
|||
if (!breakpoint) {
|
||||
return;
|
||||
}
|
||||
if (breakpoint->condition) {
|
||||
int32_t value;
|
||||
int segment;
|
||||
if (!mDebuggerEvaluateParseTree(d->p, breakpoint->condition, &value, &segment) || !(value || segment >= 0)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
struct mDebuggerEntryInfo info = {
|
||||
.address = breakpoint->address,
|
||||
.type.bp.breakType = BREAKPOINT_HARDWARE
|
||||
|
@ -50,6 +65,7 @@ static void ARMDebuggerDeinit(struct mDebuggerPlatform* platform);
|
|||
static void ARMDebuggerEnter(struct mDebuggerPlatform* d, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info);
|
||||
|
||||
static void ARMDebuggerSetBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment);
|
||||
static void ARMDebuggerSetConditionalBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment, struct ParseTree* condition);
|
||||
static void ARMDebuggerClearBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment);
|
||||
static void ARMDebuggerSetWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment, enum mWatchpointType type);
|
||||
static void ARMDebuggerClearWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment);
|
||||
|
@ -65,6 +81,7 @@ struct mDebuggerPlatform* ARMDebuggerPlatformCreate(void) {
|
|||
platform->init = ARMDebuggerInit;
|
||||
platform->deinit = ARMDebuggerDeinit;
|
||||
platform->setBreakpoint = ARMDebuggerSetBreakpoint;
|
||||
platform->setConditionalBreakpoint = ARMDebuggerSetConditionalBreakpoint;
|
||||
platform->clearBreakpoint = ARMDebuggerClearBreakpoint;
|
||||
platform->setWatchpoint = ARMDebuggerSetWatchpoint;
|
||||
platform->clearWatchpoint = ARMDebuggerClearWatchpoint;
|
||||
|
@ -97,6 +114,10 @@ void ARMDebuggerDeinit(struct mDebuggerPlatform* platform) {
|
|||
}
|
||||
ARMDebuggerRemoveMemoryShim(debugger);
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < ARMDebugBreakpointListSize(&debugger->breakpoints); ++i) {
|
||||
_destroyBreakpoint(ARMDebugBreakpointListGetPointer(&debugger->breakpoints, i));
|
||||
}
|
||||
ARMDebugBreakpointListDeinit(&debugger->breakpoints);
|
||||
ARMDebugBreakpointListDeinit(&debugger->swBreakpoints);
|
||||
ARMDebugWatchpointListDeinit(&debugger->watchpoints);
|
||||
|
@ -168,6 +189,16 @@ static void ARMDebuggerSetBreakpoint(struct mDebuggerPlatform* d, uint32_t addre
|
|||
UNUSED(segment);
|
||||
struct ARMDebugger* debugger = (struct ARMDebugger*) d;
|
||||
struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListAppend(&debugger->breakpoints);
|
||||
breakpoint->condition = NULL;
|
||||
breakpoint->address = address;
|
||||
breakpoint->isSw = false;
|
||||
}
|
||||
|
||||
static void ARMDebuggerSetConditionalBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, struct ParseTree* condition) {
|
||||
UNUSED(segment);
|
||||
struct ARMDebugger* debugger = (struct ARMDebugger*) d;
|
||||
struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListAppend(&debugger->breakpoints);
|
||||
breakpoint->condition = condition;
|
||||
breakpoint->address = address;
|
||||
breakpoint->isSw = false;
|
||||
}
|
||||
|
@ -179,6 +210,7 @@ static void ARMDebuggerClearBreakpoint(struct mDebuggerPlatform* d, uint32_t add
|
|||
size_t i;
|
||||
for (i = 0; i < ARMDebugBreakpointListSize(breakpoints); ++i) {
|
||||
if (ARMDebugBreakpointListGetPointer(breakpoints, i)->address == address) {
|
||||
_destroyBreakpoint(ARMDebugBreakpointListGetPointer(breakpoints, i));
|
||||
ARMDebugBreakpointListShift(breakpoints, i, 1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,8 +62,8 @@ static void _source(struct CLIDebugger*, struct CLIDebugVector*);
|
|||
#endif
|
||||
|
||||
static struct CLIDebuggerCommandSummary _debuggerCommands[] = {
|
||||
{ "b", _setBreakpoint, "I", "Set a breakpoint" },
|
||||
{ "break", _setBreakpoint, "I", "Set a breakpoint" },
|
||||
{ "b", _setBreakpoint, "Is", "Set a breakpoint" },
|
||||
{ "break", _setBreakpoint, "Is", "Set a breakpoint" },
|
||||
{ "c", _continue, "", "Continue execution" },
|
||||
{ "continue", _continue, "", "Continue execution" },
|
||||
{ "d", _clearBreakpoint, "I", "Delete a breakpoint" },
|
||||
|
@ -458,7 +458,39 @@ static void _setBreakpoint(struct CLIDebugger* debugger, struct CLIDebugVector*
|
|||
return;
|
||||
}
|
||||
uint32_t address = dv->intValue;
|
||||
debugger->d.platform->setBreakpoint(debugger->d.platform, address, dv->segmentValue);
|
||||
if (dv->next && dv->next->type == CLIDV_CHAR_TYPE) {
|
||||
struct LexVector lv;
|
||||
bool error = false;
|
||||
LexVectorInit(&lv, 0);
|
||||
const char* string = dv->next->charValue;
|
||||
size_t length = strlen(dv->next->charValue);
|
||||
size_t adjusted = lexExpression(&lv, string, length, NULL);
|
||||
struct ParseTree* tree = malloc(sizeof(*tree));
|
||||
if (!adjusted) {
|
||||
error = true;
|
||||
} else {
|
||||
parseLexedExpression(tree, &lv);
|
||||
|
||||
if (adjusted > length) {
|
||||
error = true;
|
||||
} else {
|
||||
length -= adjusted;
|
||||
string += adjusted;
|
||||
}
|
||||
}
|
||||
lexFree(&lv);
|
||||
LexVectorClear(&lv);
|
||||
LexVectorDeinit(&lv);
|
||||
if (error) {
|
||||
parseFree(tree);
|
||||
free(tree);
|
||||
debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS);
|
||||
} else {
|
||||
debugger->d.platform->setConditionalBreakpoint(debugger->d.platform, address, dv->segmentValue, tree);
|
||||
}
|
||||
} else {
|
||||
debugger->d.platform->setBreakpoint(debugger->d.platform, address, dv->segmentValue);
|
||||
}
|
||||
}
|
||||
|
||||
static void _setWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
|
@ -547,7 +579,7 @@ struct CLIDebugVector* CLIDVParse(struct CLIDebugger* debugger, const char* stri
|
|||
|
||||
struct LexVector lv;
|
||||
LexVectorInit(&lv, 0);
|
||||
size_t adjusted = lexExpression(&lv, string, length);
|
||||
size_t adjusted = lexExpression(&lv, string, length, " ");
|
||||
if (adjusted > length) {
|
||||
dvTemp.type = CLIDV_ERROR_TYPE;
|
||||
}
|
||||
|
@ -562,8 +594,7 @@ struct CLIDebugVector* CLIDVParse(struct CLIDebugger* debugger, const char* stri
|
|||
}
|
||||
}
|
||||
|
||||
parseFree(tree.lhs);
|
||||
parseFree(tree.rhs);
|
||||
parseFree(&tree);
|
||||
|
||||
lexFree(&lv);
|
||||
LexVectorDeinit(&lv);
|
||||
|
|
|
@ -166,13 +166,20 @@ static void _lexValue(struct LexVector* lv, char token, uint32_t next, enum LexS
|
|||
lvNext->type = TOKEN_CLOSE_PAREN_TYPE;
|
||||
*state = LEX_EXPECT_OPERATOR;
|
||||
break;
|
||||
case ' ':
|
||||
case '\t':
|
||||
lvNext = LexVectorAppend(lv);
|
||||
lvNext->type = TOKEN_UINT_TYPE;
|
||||
lvNext->uintValue = next;
|
||||
*state = LEX_EXPECT_OPERATOR;
|
||||
break;
|
||||
default:
|
||||
*state = LEX_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
size_t lexExpression(struct LexVector* lv, const char* string, size_t length) {
|
||||
size_t lexExpression(struct LexVector* lv, const char* string, size_t length, const char* eol) {
|
||||
if (!string || length < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -184,7 +191,11 @@ size_t lexExpression(struct LexVector* lv, const char* string, size_t length) {
|
|||
const char* tokenStart = 0;
|
||||
struct Token* lvNext;
|
||||
|
||||
while (length > 0 && string[0] && string[0] != ' ' && state != LEX_ERROR) {
|
||||
if (!eol) {
|
||||
eol = " \r\n";
|
||||
}
|
||||
|
||||
while (length > 0 && string[0] && !strchr(eol, string[0]) && state != LEX_ERROR) {
|
||||
char token = string[0];
|
||||
++string;
|
||||
++adjusted;
|
||||
|
@ -236,6 +247,9 @@ size_t lexExpression(struct LexVector* lv, const char* string, size_t length) {
|
|||
lvNext = LexVectorAppend(lv);
|
||||
lvNext->type = TOKEN_OPEN_PAREN_TYPE;
|
||||
break;
|
||||
case ' ':
|
||||
case '\t':
|
||||
break;
|
||||
default:
|
||||
if (tolower(token) >= 'a' && tolower(token <= 'z')) {
|
||||
state = LEX_EXPECT_IDENTIFIER;
|
||||
|
@ -272,6 +286,13 @@ size_t lexExpression(struct LexVector* lv, const char* string, size_t length) {
|
|||
lvNext->type = TOKEN_CLOSE_PAREN_TYPE;
|
||||
state = LEX_EXPECT_OPERATOR;
|
||||
break;
|
||||
case ' ':
|
||||
case '\t':
|
||||
lvNext = LexVectorAppend(lv);
|
||||
lvNext->type = TOKEN_IDENTIFIER_TYPE;
|
||||
lvNext->identifierValue = strndup(tokenStart, string - tokenStart - 1);
|
||||
state = LEX_EXPECT_OPERATOR;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -412,7 +433,9 @@ size_t lexExpression(struct LexVector* lv, const char* string, size_t length) {
|
|||
case ')':
|
||||
lvNext = LexVectorAppend(lv);
|
||||
lvNext->type = TOKEN_CLOSE_PAREN_TYPE;
|
||||
state = LEX_EXPECT_OPERATOR;
|
||||
break;
|
||||
case ' ':
|
||||
case '\t':
|
||||
break;
|
||||
default:
|
||||
state = LEX_ERROR;
|
||||
|
@ -585,13 +608,18 @@ void parseFree(struct ParseTree* tree) {
|
|||
return;
|
||||
}
|
||||
|
||||
parseFree(tree->lhs);
|
||||
parseFree(tree->rhs);
|
||||
if (tree->lhs) {
|
||||
parseFree(tree->lhs);
|
||||
free(tree->lhs);
|
||||
}
|
||||
if (tree->rhs) {
|
||||
parseFree(tree->rhs);
|
||||
free(tree->rhs);
|
||||
}
|
||||
|
||||
if (tree->token.type == TOKEN_IDENTIFIER_TYPE) {
|
||||
free(tree->token.identifierValue);
|
||||
}
|
||||
free(tree);
|
||||
}
|
||||
|
||||
static bool _performOperation(enum Operation operation, int32_t current, int32_t next, int32_t* value) {
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
struct LexVector* lv = *state; \
|
||||
lexFree(lv); \
|
||||
LexVectorClear(lv); \
|
||||
size_t adjusted = lexExpression(lv, STR, strlen(STR)); \
|
||||
size_t adjusted = lexExpression(lv, STR, strlen(STR), ""); \
|
||||
assert_false(adjusted > strlen(STR))
|
||||
|
||||
M_TEST_SUITE_SETUP(Lexer) {
|
||||
|
@ -715,6 +715,68 @@ M_TEST_DEFINE(lexNestedParentheticalExpression) {
|
|||
assert_int_equal(LexVectorGetPointer(lv, 8)->type, TOKEN_CLOSE_PAREN_TYPE);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(lexSpaceSimple) {
|
||||
LEX(" 1 ");
|
||||
|
||||
assert_int_equal(LexVectorSize(lv), 1);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(lexSpaceIdentifier) {
|
||||
LEX(" x ");
|
||||
|
||||
assert_int_equal(LexVectorSize(lv), 1);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_IDENTIFIER_TYPE);
|
||||
assert_string_equal(LexVectorGetPointer(lv, 0)->identifierValue, "x");
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(lexSpaceOperator) {
|
||||
LEX("1 + 2");
|
||||
|
||||
assert_int_equal(LexVectorSize(lv), 3);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_ADD);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_UINT_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 2)->uintValue, 2);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(lexSpaceParen) {
|
||||
LEX(" ( 1 + 2 ) ");
|
||||
|
||||
assert_int_equal(LexVectorSize(lv), 5);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_OPEN_PAREN_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_UINT_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 1)->uintValue, 1);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_OPERATOR_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 2)->operatorValue, OP_ADD);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 3)->type, TOKEN_UINT_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 3)->uintValue, 2);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 4)->type, TOKEN_CLOSE_PAREN_TYPE);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(lexSpaceParens) {
|
||||
LEX(" ( 1 + ( 2 + 3 ) ) ");
|
||||
|
||||
assert_int_equal(LexVectorSize(lv), 9);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_OPEN_PAREN_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_UINT_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 1)->uintValue, 1);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_OPERATOR_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 2)->operatorValue, OP_ADD);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 3)->type, TOKEN_OPEN_PAREN_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 4)->type, TOKEN_UINT_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 4)->uintValue, 2);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 5)->type, TOKEN_OPERATOR_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 5)->operatorValue, OP_ADD);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 6)->type, TOKEN_UINT_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 6)->uintValue, 3);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 7)->type, TOKEN_CLOSE_PAREN_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 8)->type, TOKEN_CLOSE_PAREN_TYPE);
|
||||
}
|
||||
|
||||
M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(Lexer,
|
||||
cmocka_unit_test(lexEmpty),
|
||||
cmocka_unit_test(lexInt),
|
||||
|
@ -785,4 +847,9 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(Lexer,
|
|||
cmocka_unit_test(lexCloseParen),
|
||||
cmocka_unit_test(lexIdentifierCloseParen),
|
||||
cmocka_unit_test(lexParentheticalExpression),
|
||||
cmocka_unit_test(lexNestedParentheticalExpression))
|
||||
cmocka_unit_test(lexNestedParentheticalExpression),
|
||||
cmocka_unit_test(lexSpaceSimple),
|
||||
cmocka_unit_test(lexSpaceIdentifier),
|
||||
cmocka_unit_test(lexSpaceOperator),
|
||||
cmocka_unit_test(lexSpaceParen),
|
||||
cmocka_unit_test(lexSpaceParens))
|
||||
|
|
|
@ -16,7 +16,7 @@ struct LPTest {
|
|||
struct LPTest* lp = *state; \
|
||||
lexFree(&lp->lv); \
|
||||
LexVectorClear(&lp->lv); \
|
||||
size_t adjusted = lexExpression(&lp->lv, STR, strlen(STR)); \
|
||||
size_t adjusted = lexExpression(&lp->lv, STR, strlen(STR), ""); \
|
||||
assert_false(adjusted > strlen(STR)); \
|
||||
struct ParseTree* tree = &lp->tree; \
|
||||
parseLexedExpression(tree, &lp->lv)
|
||||
|
@ -30,8 +30,7 @@ M_TEST_SUITE_SETUP(Parser) {
|
|||
|
||||
M_TEST_SUITE_TEARDOWN(Parser) {
|
||||
struct LPTest* lp = *state;
|
||||
parseFree(lp->tree.lhs); \
|
||||
parseFree(lp->tree.rhs); \
|
||||
parseFree(&lp->tree); \
|
||||
lexFree(&lp->lv);
|
||||
LexVectorDeinit(&lp->lv);
|
||||
free(lp);
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <mgba/internal/lr35902/debugger/debugger.h>
|
||||
|
||||
#include <mgba/core/core.h>
|
||||
#include <mgba/internal/debugger/parser.h>
|
||||
#include <mgba/internal/lr35902/decoder.h>
|
||||
#include <mgba/internal/lr35902/lr35902.h>
|
||||
#include <mgba/internal/lr35902/debugger/memory-debugger.h>
|
||||
|
@ -23,6 +24,13 @@ static struct LR35902DebugBreakpoint* _lookupBreakpoint(struct LR35902DebugBreak
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void _destroyBreakpoint(struct LR35902DebugBreakpoint* breakpoint) {
|
||||
if (breakpoint->condition) {
|
||||
parseFree(breakpoint->condition);
|
||||
free(breakpoint->condition);
|
||||
}
|
||||
}
|
||||
|
||||
static void LR35902DebuggerCheckBreakpoints(struct mDebuggerPlatform* d) {
|
||||
struct LR35902Debugger* debugger = (struct LR35902Debugger*) d;
|
||||
struct LR35902DebugBreakpoint* breakpoint = _lookupBreakpoint(&debugger->breakpoints, debugger->cpu->pc);
|
||||
|
@ -32,6 +40,13 @@ static void LR35902DebuggerCheckBreakpoints(struct mDebuggerPlatform* d) {
|
|||
if (breakpoint->segment >= 0 && debugger->cpu->memory.currentSegment(debugger->cpu, breakpoint->address) != breakpoint->segment) {
|
||||
return;
|
||||
}
|
||||
if (breakpoint->condition) {
|
||||
int32_t value;
|
||||
int segment;
|
||||
if (!mDebuggerEvaluateParseTree(d->p, breakpoint->condition, &value, &segment) || !(value || segment >= 0)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
struct mDebuggerEntryInfo info = {
|
||||
.address = breakpoint->address
|
||||
};
|
||||
|
@ -44,6 +59,7 @@ static void LR35902DebuggerDeinit(struct mDebuggerPlatform* platform);
|
|||
static void LR35902DebuggerEnter(struct mDebuggerPlatform* d, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info);
|
||||
|
||||
static void LR35902DebuggerSetBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment);
|
||||
static void LR35902DebuggerSetConditionalBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment, struct ParseTree* condition);
|
||||
static void LR35902DebuggerClearBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment);
|
||||
static void LR35902DebuggerSetWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment, enum mWatchpointType type);
|
||||
static void LR35902DebuggerClearWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment);
|
||||
|
@ -59,6 +75,7 @@ struct mDebuggerPlatform* LR35902DebuggerPlatformCreate(void) {
|
|||
platform->init = LR35902DebuggerInit;
|
||||
platform->deinit = LR35902DebuggerDeinit;
|
||||
platform->setBreakpoint = LR35902DebuggerSetBreakpoint;
|
||||
platform->setConditionalBreakpoint = LR35902DebuggerSetConditionalBreakpoint;
|
||||
platform->clearBreakpoint = LR35902DebuggerClearBreakpoint;
|
||||
platform->setWatchpoint = LR35902DebuggerSetWatchpoint;
|
||||
platform->clearWatchpoint = LR35902DebuggerClearWatchpoint;
|
||||
|
@ -79,6 +96,10 @@ void LR35902DebuggerInit(void* cpu, struct mDebuggerPlatform* platform) {
|
|||
|
||||
void LR35902DebuggerDeinit(struct mDebuggerPlatform* platform) {
|
||||
struct LR35902Debugger* debugger = (struct LR35902Debugger*) platform;
|
||||
size_t i;
|
||||
for (i = 0; i < LR35902DebugBreakpointListSize(&debugger->breakpoints); ++i) {
|
||||
_destroyBreakpoint(LR35902DebugBreakpointListGetPointer(&debugger->breakpoints, i));
|
||||
}
|
||||
LR35902DebugBreakpointListDeinit(&debugger->breakpoints);
|
||||
LR35902DebugWatchpointListDeinit(&debugger->watchpoints);
|
||||
}
|
||||
|
@ -100,6 +121,15 @@ static void LR35902DebuggerSetBreakpoint(struct mDebuggerPlatform* d, uint32_t a
|
|||
struct LR35902DebugBreakpoint* breakpoint = LR35902DebugBreakpointListAppend(&debugger->breakpoints);
|
||||
breakpoint->address = address;
|
||||
breakpoint->segment = segment;
|
||||
breakpoint->condition = NULL;
|
||||
}
|
||||
|
||||
static void LR35902DebuggerSetConditionalBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, struct ParseTree* condition) {
|
||||
struct LR35902Debugger* debugger = (struct LR35902Debugger*) d;
|
||||
struct LR35902DebugBreakpoint* breakpoint = LR35902DebugBreakpointListAppend(&debugger->breakpoints);
|
||||
breakpoint->address = address;
|
||||
breakpoint->segment = segment;
|
||||
breakpoint->condition = condition;
|
||||
}
|
||||
|
||||
static void LR35902DebuggerClearBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) {
|
||||
|
@ -109,6 +139,7 @@ static void LR35902DebuggerClearBreakpoint(struct mDebuggerPlatform* d, uint32_t
|
|||
for (i = 0; i < LR35902DebugBreakpointListSize(breakpoints); ++i) {
|
||||
struct LR35902DebugBreakpoint* breakpoint = LR35902DebugBreakpointListGetPointer(breakpoints, i);
|
||||
if (breakpoint->address == address && breakpoint->segment == segment) {
|
||||
_destroyBreakpoint(LR35902DebugBreakpointListGetPointer(breakpoints, i));
|
||||
LR35902DebugBreakpointListShift(breakpoints, i, 1);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue