mirror of https://github.com/mgba-emu/mgba.git
Debugger: Redo argument handling
This commit is contained in:
parent
b9ae986016
commit
a2447d09e3
|
@ -34,12 +34,11 @@ struct CLIDebugVector {
|
|||
};
|
||||
|
||||
typedef void (*CLIDebuggerCommand)(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
typedef struct CLIDebugVector* (*CLIDVParser)(struct CLIDebugger* debugger, const char* string, size_t length);
|
||||
|
||||
struct CLIDebuggerCommandSummary {
|
||||
const char* name;
|
||||
CLIDebuggerCommand command;
|
||||
CLIDVParser parser;
|
||||
const char* format;
|
||||
const char* summary;
|
||||
};
|
||||
|
||||
|
@ -82,9 +81,6 @@ struct CLIDebugger {
|
|||
struct CLIDebuggerBackend* backend;
|
||||
};
|
||||
|
||||
struct CLIDebugVector* CLIDVParse(struct CLIDebugger* debugger, const char* string, size_t length);
|
||||
struct CLIDebugVector* CLIDVStringParse(struct CLIDebugger* debugger, const char* string, size_t length);
|
||||
|
||||
void CLIDebuggerCreate(struct CLIDebugger*);
|
||||
void CLIDebuggerAttachSystem(struct CLIDebugger*, struct CLIDebuggerSystem*);
|
||||
void CLIDebuggerAttachBackend(struct CLIDebugger*, struct CLIDebuggerBackend*);
|
||||
|
|
|
@ -23,17 +23,17 @@ static void _disassembleMode(struct CLIDebugger*, struct CLIDebugVector*, enum E
|
|||
static uint32_t _printLine(struct CLIDebugger* debugger, uint32_t address, enum ExecutionMode mode);
|
||||
|
||||
static struct CLIDebuggerCommandSummary _armCommands[] = {
|
||||
{ "b/a", _setBreakpointARM, CLIDVParse, "Set a software breakpoint as ARM" },
|
||||
{ "b/t", _setBreakpointThumb, CLIDVParse, "Set a software breakpoint as Thumb" },
|
||||
{ "break/a", _setBreakpointARM, CLIDVParse, "Set a software breakpoint as ARM" },
|
||||
{ "break/t", _setBreakpointThumb, CLIDVParse, "Set a software breakpoint as Thumb" },
|
||||
{ "dis/a", _disassembleArm, CLIDVParse, "Disassemble instructions as ARM" },
|
||||
{ "dis/t", _disassembleThumb, CLIDVParse, "Disassemble instructions as Thumb" },
|
||||
{ "disasm/a", _disassembleArm, CLIDVParse, "Disassemble instructions as ARM" },
|
||||
{ "disasm/t", _disassembleThumb, CLIDVParse, "Disassemble instructions as Thumb" },
|
||||
{ "disassemble/a", _disassembleArm, CLIDVParse, "Disassemble instructions as ARM" },
|
||||
{ "disassemble/t", _disassembleThumb, CLIDVParse, "Disassemble instructions as Thumb" },
|
||||
{ "w/r", _writeRegister, CLIDVParse, "Write a register" },
|
||||
{ "b/a", _setBreakpointARM, "I", "Set a software breakpoint as ARM" },
|
||||
{ "b/t", _setBreakpointThumb, "I", "Set a software breakpoint as Thumb" },
|
||||
{ "break/a", _setBreakpointARM, "I", "Set a software breakpoint as ARM" },
|
||||
{ "break/t", _setBreakpointThumb, "I", "Set a software breakpoint as Thumb" },
|
||||
{ "dis/a", _disassembleArm, "Ii", "Disassemble instructions as ARM" },
|
||||
{ "dis/t", _disassembleThumb, "Ii", "Disassemble instructions as Thumb" },
|
||||
{ "disasm/a", _disassembleArm, "Ii", "Disassemble instructions as ARM" },
|
||||
{ "disasm/t", _disassembleThumb, "Ii", "Disassemble instructions as Thumb" },
|
||||
{ "disassemble/a", _disassembleArm, "Ii", "Disassemble instructions as ARM" },
|
||||
{ "disassemble/t", _disassembleThumb, "Ii", "Disassemble instructions as Thumb" },
|
||||
{ "w/r", _writeRegister, "SI", "Write a register" },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
|
@ -148,7 +148,7 @@ static void _printStatus(struct CLIDebuggerSystem* debugger) {
|
|||
static void _writeRegister(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
struct CLIDebuggerBackend* be = debugger->backend;
|
||||
struct ARMCore* cpu = debugger->d.core->cpu;
|
||||
if (!dv || dv->type != CLIDV_INT_TYPE) {
|
||||
if (!dv || dv->type != CLIDV_CHAR_TYPE) {
|
||||
be->printf(be, "%s\n", ERROR_MISSING_ARGS);
|
||||
return;
|
||||
}
|
||||
|
@ -156,11 +156,17 @@ static void _writeRegister(struct CLIDebugger* debugger, struct CLIDebugVector*
|
|||
be->printf(be, "%s\n", ERROR_MISSING_ARGS);
|
||||
return;
|
||||
}
|
||||
uint32_t regid = dv->intValue;
|
||||
uint32_t value = dv->next->intValue;
|
||||
if (regid >= ARM_PC) {
|
||||
char* end;
|
||||
uint32_t regid = strtoul(&dv->charValue[1], &end, 10);
|
||||
if (dv->charValue[0] != 'r' || (*end && !isspace(*end)) || regid > ARM_PC) {
|
||||
be->printf(be, "%s\n", "Unknown register name");
|
||||
return;
|
||||
}
|
||||
if (regid == ARM_PC) {
|
||||
be->printf(be, "%s\n", "Cannot write to program counter");
|
||||
return;
|
||||
}
|
||||
uint32_t value = dv->next->intValue;
|
||||
cpu->gprs[regid] = value;
|
||||
}
|
||||
|
||||
|
|
|
@ -60,50 +60,50 @@ static void _source(struct CLIDebugger*, struct CLIDebugVector*);
|
|||
#endif
|
||||
|
||||
static struct CLIDebuggerCommandSummary _debuggerCommands[] = {
|
||||
{ "b", _setBreakpoint, CLIDVParse, "Set a breakpoint" },
|
||||
{ "break", _setBreakpoint, CLIDVParse, "Set a breakpoint" },
|
||||
{ "c", _continue, 0, "Continue execution" },
|
||||
{ "continue", _continue, 0, "Continue execution" },
|
||||
{ "d", _clearBreakpoint, CLIDVParse, "Delete a breakpoint" },
|
||||
{ "delete", _clearBreakpoint, CLIDVParse, "Delete a breakpoint" },
|
||||
{ "dis", _disassemble, CLIDVParse, "Disassemble instructions" },
|
||||
{ "disasm", _disassemble, CLIDVParse, "Disassemble instructions" },
|
||||
{ "disassemble", _disassemble, CLIDVParse, "Disassemble instructions" },
|
||||
{ "h", _printHelp, CLIDVStringParse, "Print help" },
|
||||
{ "help", _printHelp, CLIDVStringParse, "Print help" },
|
||||
{ "i", _printStatus, 0, "Print the current status" },
|
||||
{ "info", _printStatus, 0, "Print the current status" },
|
||||
{ "n", _next, 0, "Execute next instruction" },
|
||||
{ "next", _next, 0, "Execute next instruction" },
|
||||
{ "p", _print, CLIDVParse, "Print a value" },
|
||||
{ "p/t", _printBin, CLIDVParse, "Print a value as binary" },
|
||||
{ "p/x", _printHex, CLIDVParse, "Print a value as hexadecimal" },
|
||||
{ "print", _print, CLIDVParse, "Print a value" },
|
||||
{ "print/t", _printBin, CLIDVParse, "Print a value as binary" },
|
||||
{ "print/x", _printHex, CLIDVParse, "Print a value as hexadecimal" },
|
||||
{ "q", _quit, 0, "Quit the emulator" },
|
||||
{ "quit", _quit, 0, "Quit the emulator" },
|
||||
{ "reset", _reset, 0, "Reset the emulation" },
|
||||
{ "r/1", _readByte, CLIDVParse, "Read a byte from a specified offset" },
|
||||
{ "r/2", _readHalfword, CLIDVParse, "Read a halfword from a specified offset" },
|
||||
{ "r/4", _readWord, CLIDVParse, "Read a word from a specified offset" },
|
||||
{ "status", _printStatus, 0, "Print the current status" },
|
||||
{ "trace", _trace, CLIDVParse, "Trace a fixed number of instructions" },
|
||||
{ "w", _setWatchpoint, CLIDVParse, "Set a watchpoint" },
|
||||
{ "w/1", _writeByte, CLIDVParse, "Write a byte at a specified offset" },
|
||||
{ "w/2", _writeHalfword, CLIDVParse, "Write a halfword at a specified offset" },
|
||||
{ "w/4", _writeWord, CLIDVParse, "Write a word at a specified offset" },
|
||||
{ "watch", _setWatchpoint, CLIDVParse, "Set a watchpoint" },
|
||||
{ "watch/r", _setReadWatchpoint, CLIDVParse, "Set a read watchpoint" },
|
||||
{ "watch/w", _setWriteWatchpoint, CLIDVParse, "Set a write watchpoint" },
|
||||
{ "x/1", _dumpByte, CLIDVParse, "Examine bytes at a specified offset" },
|
||||
{ "x/2", _dumpHalfword, CLIDVParse, "Examine halfwords at a specified offset" },
|
||||
{ "x/4", _dumpWord, CLIDVParse, "Examine words at a specified offset" },
|
||||
{ "b", _setBreakpoint, "I", "Set a breakpoint" },
|
||||
{ "break", _setBreakpoint, "I", "Set a breakpoint" },
|
||||
{ "c", _continue, "", "Continue execution" },
|
||||
{ "continue", _continue, "", "Continue execution" },
|
||||
{ "d", _clearBreakpoint, "I", "Delete a breakpoint" },
|
||||
{ "delete", _clearBreakpoint, "I", "Delete a breakpoint" },
|
||||
{ "dis", _disassemble, "Ii", "Disassemble instructions" },
|
||||
{ "disasm", _disassemble, "Ii", "Disassemble instructions" },
|
||||
{ "disassemble", _disassemble, "Ii", "Disassemble instructions" },
|
||||
{ "h", _printHelp, "S", "Print help" },
|
||||
{ "help", _printHelp, "S", "Print help" },
|
||||
{ "i", _printStatus, "", "Print the current status" },
|
||||
{ "info", _printStatus, "", "Print the current status" },
|
||||
{ "n", _next, "", "Execute next instruction" },
|
||||
{ "next", _next, "", "Execute next instruction" },
|
||||
{ "p", _print, "I", "Print a value" },
|
||||
{ "p/t", _printBin, "I", "Print a value as binary" },
|
||||
{ "p/x", _printHex, "I", "Print a value as hexadecimal" },
|
||||
{ "print", _print, "I", "Print a value" },
|
||||
{ "print/t", _printBin, "I", "Print a value as binary" },
|
||||
{ "print/x", _printHex, "I", "Print a value as hexadecimal" },
|
||||
{ "q", _quit, "", "Quit the emulator" },
|
||||
{ "quit", _quit, "", "Quit the emulator" },
|
||||
{ "reset", _reset, "", "Reset the emulation" },
|
||||
{ "r/1", _readByte, "I", "Read a byte from a specified offset" },
|
||||
{ "r/2", _readHalfword, "I", "Read a halfword from a specified offset" },
|
||||
{ "r/4", _readWord, "I", "Read a word from a specified offset" },
|
||||
{ "status", _printStatus, "", "Print the current status" },
|
||||
{ "trace", _trace, "I", "Trace a fixed number of instructions" },
|
||||
{ "w", _setWatchpoint, "I", "Set a watchpoint" },
|
||||
{ "w/1", _writeByte, "II", "Write a byte at a specified offset" },
|
||||
{ "w/2", _writeHalfword, "II", "Write a halfword at a specified offset" },
|
||||
{ "w/4", _writeWord, "II", "Write a word at a specified offset" },
|
||||
{ "watch", _setWatchpoint, "I", "Set a watchpoint" },
|
||||
{ "watch/r", _setReadWatchpoint, "I", "Set a read watchpoint" },
|
||||
{ "watch/w", _setWriteWatchpoint, "I", "Set a write watchpoint" },
|
||||
{ "x/1", _dumpByte, "Ii", "Examine bytes at a specified offset" },
|
||||
{ "x/2", _dumpHalfword, "Ii", "Examine halfwords at a specified offset" },
|
||||
{ "x/4", _dumpWord, "Ii", "Examine words at a specified offset" },
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
{ "source", _source, CLIDVStringParse, "Load a script" },
|
||||
{ "source", _source, "S", "Load a script" },
|
||||
#endif
|
||||
#if !defined(NDEBUG) && !defined(_WIN32)
|
||||
{ "!", _breakInto, 0, "Break into attached debugger (for developers)" },
|
||||
{ "!", _breakInto, "", "Break into attached debugger (for developers)" },
|
||||
#endif
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
@ -626,52 +626,28 @@ struct CLIDebugVector* CLIDVParse(struct CLIDebugger* debugger, const char* stri
|
|||
parseFree(tree.lhs);
|
||||
parseFree(tree.rhs);
|
||||
|
||||
length -= adjusted;
|
||||
string += adjusted;
|
||||
|
||||
struct CLIDebugVector* dv = malloc(sizeof(struct CLIDebugVector));
|
||||
if (dvTemp.type == CLIDV_ERROR_TYPE) {
|
||||
dv->type = CLIDV_ERROR_TYPE;
|
||||
dv->next = 0;
|
||||
} else {
|
||||
*dv = dvTemp;
|
||||
if (string[0] == ' ') {
|
||||
dv->next = CLIDVParse(debugger, string + 1, length - 1);
|
||||
if (dv->next && dv->next->type == CLIDV_ERROR_TYPE) {
|
||||
dv->type = CLIDV_ERROR_TYPE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return dv;
|
||||
}
|
||||
|
||||
struct CLIDebugVector* CLIDVStringParse(struct CLIDebugger* debugger, const char* string, size_t length) {
|
||||
UNUSED(debugger);
|
||||
if (!string || length < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct CLIDebugVector dvTemp = { .type = CLIDV_CHAR_TYPE };
|
||||
|
||||
size_t adjusted;
|
||||
const char* next = strchr(string, ' ');
|
||||
if (next) {
|
||||
adjusted = next - string;
|
||||
} else {
|
||||
adjusted = length;
|
||||
}
|
||||
dvTemp.charValue = strndup(string, adjusted);
|
||||
|
||||
length -= adjusted;
|
||||
string += adjusted;
|
||||
dvTemp.charValue = strndup(string, length);
|
||||
|
||||
struct CLIDebugVector* dv = malloc(sizeof(struct CLIDebugVector));
|
||||
*dv = dvTemp;
|
||||
if (string[0] == ' ') {
|
||||
dv->next = CLIDVStringParse(debugger, string + 1, length - 1);
|
||||
if (dv->next && dv->next->type == CLIDV_ERROR_TYPE) {
|
||||
dv->type = CLIDV_ERROR_TYPE;
|
||||
}
|
||||
}
|
||||
return dv;
|
||||
}
|
||||
|
||||
|
@ -687,8 +663,28 @@ static void _DVFree(struct CLIDebugVector* dv) {
|
|||
}
|
||||
}
|
||||
|
||||
static struct CLIDebugVector* _parseArg(struct CLIDebugger* debugger, const char* args, size_t argsLen, char type) {
|
||||
struct CLIDebugVector* dv = NULL;
|
||||
switch (type) {
|
||||
case 'I':
|
||||
case 'i':
|
||||
return CLIDVParse(debugger, args, argsLen);
|
||||
case 'S':
|
||||
case 's':
|
||||
return CLIDVStringParse(debugger, args, argsLen);
|
||||
case '*':
|
||||
dv = _parseArg(debugger, args, argsLen, 'I');
|
||||
if (!dv) {
|
||||
dv = _parseArg(debugger, args, argsLen, 'S');
|
||||
}
|
||||
break;
|
||||
}
|
||||
return dv;
|
||||
}
|
||||
|
||||
static int _tryCommands(struct CLIDebugger* debugger, struct CLIDebuggerCommandSummary* commands, const char* command, size_t commandLen, const char* args, size_t argsLen) {
|
||||
struct CLIDebugVector* dv = 0;
|
||||
struct CLIDebugVector* dv = NULL;
|
||||
struct CLIDebugVector* dvLast = NULL;
|
||||
int i;
|
||||
const char* name;
|
||||
for (i = 0; (name = commands[i].name); ++i) {
|
||||
|
@ -696,22 +692,78 @@ static int _tryCommands(struct CLIDebugger* debugger, struct CLIDebuggerCommandS
|
|||
continue;
|
||||
}
|
||||
if (strncasecmp(name, command, commandLen) == 0) {
|
||||
if (commands[i].parser) {
|
||||
if (args) {
|
||||
dv = commands[i].parser(debugger, args, argsLen);
|
||||
if (dv && dv->type == CLIDV_ERROR_TYPE) {
|
||||
if (commands[i].format && args) {
|
||||
char lastArg = '\0';
|
||||
int arg;
|
||||
for (arg = 0; commands[i].format[arg] && argsLen; ++arg) {
|
||||
while (isspace(args[0]) && argsLen) {
|
||||
++args;
|
||||
--argsLen;
|
||||
}
|
||||
if (!args[0] || !argsLen) {
|
||||
debugger->backend->printf(debugger->backend, "Wrong number of arguments\n");
|
||||
_DVFree(dv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t adjusted;
|
||||
const char* next = strchr(args, ' ');
|
||||
if (next) {
|
||||
adjusted = next - args;
|
||||
} else {
|
||||
adjusted = argsLen;
|
||||
}
|
||||
|
||||
struct CLIDebugVector* dvNext = NULL;
|
||||
bool nextArgMandatory = false;
|
||||
|
||||
if (commands[i].format[arg] == '+') {
|
||||
dvNext = _parseArg(debugger, args, adjusted, lastArg);
|
||||
--args;
|
||||
} else {
|
||||
nextArgMandatory = isupper(commands[i].format[arg]) || (commands[i].format[arg] == '*');
|
||||
dvNext = _parseArg(debugger, args, adjusted, commands[i].format[arg]);
|
||||
}
|
||||
|
||||
args += adjusted;
|
||||
argsLen -= adjusted;
|
||||
|
||||
if (!dvNext) {
|
||||
if (!nextArgMandatory) {
|
||||
args = NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (dvNext->type == CLIDV_ERROR_TYPE) {
|
||||
debugger->backend->printf(debugger->backend, "Parse error\n");
|
||||
_DVFree(dv);
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (dvLast) {
|
||||
dvLast->next = dvNext;
|
||||
dvLast = dvNext;
|
||||
} else {
|
||||
dv = dvNext;
|
||||
dvLast = dv;
|
||||
}
|
||||
}
|
||||
} else if (args) {
|
||||
}
|
||||
|
||||
if (args) {
|
||||
while (isspace(args[0]) && argsLen) {
|
||||
++args;
|
||||
--argsLen;
|
||||
}
|
||||
}
|
||||
if (args && argsLen) {
|
||||
debugger->backend->printf(debugger->backend, "Wrong number of arguments\n");
|
||||
return false;
|
||||
_DVFree(dv);
|
||||
return 0;
|
||||
}
|
||||
commands[i].command(debugger, dv);
|
||||
_DVFree(dv);
|
||||
return true;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
|
|
|
@ -21,9 +21,9 @@ static void _load(struct CLIDebugger*, struct CLIDebugVector*);
|
|||
static void _save(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
|
||||
struct CLIDebuggerCommandSummary _GBCLIDebuggerCommands[] = {
|
||||
{ "frame", _frame, 0, "Frame advance" },
|
||||
{ "load", _load, CLIDVParse, "Load a savestate" },
|
||||
{ "save", _save, CLIDVParse, "Save a savestate" },
|
||||
{ "frame", _frame, "", "Frame advance" },
|
||||
{ "load", _load, "*", "Load a savestate" },
|
||||
{ "save", _save, "*", "Save a savestate" },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
|
|
|
@ -21,9 +21,9 @@ static void _load(struct CLIDebugger*, struct CLIDebugVector*);
|
|||
static void _save(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
|
||||
struct CLIDebuggerCommandSummary _GBACLIDebuggerCommands[] = {
|
||||
{ "frame", _frame, 0, "Frame advance" },
|
||||
{ "load", _load, CLIDVParse, "Load a savestate" },
|
||||
{ "save", _save, CLIDVParse, "Save a savestate" },
|
||||
{ "frame", _frame, "", "Frame advance" },
|
||||
{ "load", _load, "*", "Load a savestate" },
|
||||
{ "save", _save, "*", "Save a savestate" },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue