mirror of
https://github.com/libretro/mgba.git
synced 2024-11-27 18:20:33 +00:00
Begin command line parsing
This commit is contained in:
parent
10884de57a
commit
f715534083
296
src/debugger.c
296
src/debugger.c
@ -10,16 +10,32 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include "linenoise.h"
|
#include "linenoise.h"
|
||||||
|
|
||||||
typedef void (DebuggerComamnd)(struct ARMDebugger*);
|
struct DebugVector {
|
||||||
|
struct DebugVector* next;
|
||||||
|
enum DVType {
|
||||||
|
ERROR_TYPE,
|
||||||
|
INT_TYPE,
|
||||||
|
CHAR_TYPE
|
||||||
|
} type;
|
||||||
|
union {
|
||||||
|
int32_t intValue;
|
||||||
|
const char* charValue;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
static void _breakInto(struct ARMDebugger*);
|
typedef void (DebuggerComamnd)(struct ARMDebugger*, struct DebugVector*);
|
||||||
static void _printStatus(struct ARMDebugger*);
|
|
||||||
static void _quit(struct ARMDebugger*);
|
static void _breakInto(struct ARMDebugger*, struct DebugVector*);
|
||||||
|
static void _print(struct ARMDebugger*, struct DebugVector*);
|
||||||
|
static void _printStatus(struct ARMDebugger*, struct DebugVector*);
|
||||||
|
static void _quit(struct ARMDebugger*, struct DebugVector*);
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
const char* name;
|
const char* name;
|
||||||
DebuggerComamnd* command;
|
DebuggerComamnd* command;
|
||||||
} debuggerCommands[] = {
|
} debuggerCommands[] = {
|
||||||
|
{ "p", _print },
|
||||||
|
{ "print", _print },
|
||||||
{ "i", _printStatus },
|
{ "i", _printStatus },
|
||||||
{ "info", _printStatus },
|
{ "info", _printStatus },
|
||||||
{ "q", _quit },
|
{ "q", _quit },
|
||||||
@ -45,13 +61,22 @@ static void _handleDeath(int sig) {
|
|||||||
printf("No debugger attached!\n");
|
printf("No debugger attached!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _breakInto(struct ARMDebugger* debugger) {
|
static void _breakInto(struct ARMDebugger* debugger, struct DebugVector* dv) {
|
||||||
(void)(debugger);
|
(void)(debugger);
|
||||||
|
(void)(dv);
|
||||||
sig_t oldSignal = signal(SIGTRAP, _handleDeath);
|
sig_t oldSignal = signal(SIGTRAP, _handleDeath);
|
||||||
kill(getpid(), SIGTRAP);
|
kill(getpid(), SIGTRAP);
|
||||||
signal(SIGTRAP, oldSignal);
|
signal(SIGTRAP, oldSignal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _print(struct ARMDebugger* debugger, struct DebugVector* dv) {
|
||||||
|
(void)(debugger);
|
||||||
|
for ( ; dv; dv = dv->next) {
|
||||||
|
printf(" %u", dv->intValue);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
static inline void _printLine(struct ARMDebugger* debugger, uint32_t address, enum ExecutionMode mode) {
|
static inline void _printLine(struct ARMDebugger* debugger, uint32_t address, enum ExecutionMode mode) {
|
||||||
// TODO: write a disassembler
|
// TODO: write a disassembler
|
||||||
if (mode == MODE_ARM) {
|
if (mode == MODE_ARM) {
|
||||||
@ -63,7 +88,8 @@ static inline void _printLine(struct ARMDebugger* debugger, uint32_t address, en
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _printStatus(struct ARMDebugger* debugger) {
|
static void _printStatus(struct ARMDebugger* debugger, struct DebugVector* dv) {
|
||||||
|
(void)(dv);
|
||||||
int r;
|
int r;
|
||||||
for (r = 0; r < 4; ++r) {
|
for (r = 0; r < 4; ++r) {
|
||||||
printf("%08X %08X %08X %08X\n",
|
printf("%08X %08X %08X %08X\n",
|
||||||
@ -83,15 +109,260 @@ static void _printStatus(struct ARMDebugger* debugger) {
|
|||||||
_printLine(debugger, debugger->cpu->gprs[ARM_PC] - instructionLength, mode);
|
_printLine(debugger, debugger->cpu->gprs[ARM_PC] - instructionLength, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _quit(struct ARMDebugger* debugger) {
|
static void _quit(struct ARMDebugger* debugger, struct DebugVector* dv) {
|
||||||
|
(void)(dv);
|
||||||
debugger->state = DEBUGGER_EXITING;
|
debugger->state = DEBUGGER_EXITING;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum _DVParseState {
|
||||||
|
PARSE_ERROR = -1,
|
||||||
|
PARSE_ROOT = 0,
|
||||||
|
PARSE_EXPECT_REGISTER,
|
||||||
|
PARSE_EXPECT_REGISTER_2,
|
||||||
|
PARSE_EXPECT_LR,
|
||||||
|
PARSE_EXPECT_PC,
|
||||||
|
PARSE_EXPECT_SP,
|
||||||
|
PARSE_EXPECT_DECIMAL,
|
||||||
|
PARSE_EXPECT_HEX,
|
||||||
|
PARSE_EXPECT_PREFIX,
|
||||||
|
PARSE_EXPECT_SUFFIX,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct DebugVector* _DVParse(struct ARMDebugger* debugger, const char* string) {
|
||||||
|
if (!string || !string[0]) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum _DVParseState state = PARSE_ROOT;
|
||||||
|
struct DebugVector dvTemp = { .type = INT_TYPE };
|
||||||
|
uint32_t current = 0;
|
||||||
|
|
||||||
|
while (string[0] && string[0] != ' ' && state != PARSE_ERROR) {
|
||||||
|
char token = string[0];
|
||||||
|
++string;
|
||||||
|
switch (state) {
|
||||||
|
case PARSE_ROOT:
|
||||||
|
switch (token) {
|
||||||
|
case 'r':
|
||||||
|
state = PARSE_EXPECT_REGISTER;
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
state = PARSE_EXPECT_PC;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
state = PARSE_EXPECT_SP;
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
state = PARSE_EXPECT_LR;
|
||||||
|
break;
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
case '6':
|
||||||
|
case '7':
|
||||||
|
case '8':
|
||||||
|
case '9':
|
||||||
|
state = PARSE_EXPECT_DECIMAL;
|
||||||
|
current = token - '0';
|
||||||
|
break;
|
||||||
|
case '0':
|
||||||
|
state = PARSE_EXPECT_PREFIX;
|
||||||
|
break;
|
||||||
|
case '$':
|
||||||
|
state = PARSE_EXPECT_HEX;
|
||||||
|
current = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
state = PARSE_ERROR;
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case PARSE_EXPECT_LR:
|
||||||
|
switch (token) {
|
||||||
|
case 'r':
|
||||||
|
current = debugger->cpu->gprs[ARM_LR];
|
||||||
|
state = PARSE_EXPECT_SUFFIX;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
state = PARSE_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PARSE_EXPECT_PC:
|
||||||
|
switch (token) {
|
||||||
|
case 'c':
|
||||||
|
current = debugger->cpu->gprs[ARM_PC];
|
||||||
|
state = PARSE_EXPECT_SUFFIX;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
state = PARSE_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PARSE_EXPECT_SP:
|
||||||
|
switch (token) {
|
||||||
|
case 'p':
|
||||||
|
current = debugger->cpu->gprs[ARM_SP];
|
||||||
|
state = PARSE_EXPECT_SUFFIX;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
state = PARSE_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PARSE_EXPECT_REGISTER:
|
||||||
|
switch (token) {
|
||||||
|
case '0':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
case '6':
|
||||||
|
case '7':
|
||||||
|
case '8':
|
||||||
|
case '9':
|
||||||
|
current = debugger->cpu->gprs[token - '0'];
|
||||||
|
state = PARSE_EXPECT_SUFFIX;
|
||||||
|
break;
|
||||||
|
case '1':
|
||||||
|
state = PARSE_EXPECT_REGISTER_2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
state = PARSE_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PARSE_EXPECT_REGISTER_2:
|
||||||
|
switch (token) {
|
||||||
|
case '0':
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
current = debugger->cpu->gprs[token - '0' + 10];
|
||||||
|
state = PARSE_EXPECT_SUFFIX;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
state = PARSE_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PARSE_EXPECT_DECIMAL:
|
||||||
|
switch (token) {
|
||||||
|
case '0':
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
case '6':
|
||||||
|
case '7':
|
||||||
|
case '8':
|
||||||
|
case '9':
|
||||||
|
// TODO: handle overflow
|
||||||
|
current *= 10;
|
||||||
|
current += token - '0';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
state = PARSE_ERROR;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PARSE_EXPECT_HEX:
|
||||||
|
switch (token) {
|
||||||
|
case '0':
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
case '6':
|
||||||
|
case '7':
|
||||||
|
case '8':
|
||||||
|
case '9':
|
||||||
|
// TODO: handle overflow
|
||||||
|
current *= 16;
|
||||||
|
current += token - '0';
|
||||||
|
break;
|
||||||
|
case 'A':
|
||||||
|
case 'B':
|
||||||
|
case 'C':
|
||||||
|
case 'D':
|
||||||
|
case 'E':
|
||||||
|
case 'F':
|
||||||
|
// TODO: handle overflow
|
||||||
|
current *= 16;
|
||||||
|
current += token - 'A' + 10;
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
case 'b':
|
||||||
|
case 'c':
|
||||||
|
case 'd':
|
||||||
|
case 'e':
|
||||||
|
case 'f':
|
||||||
|
// TODO: handle overflow
|
||||||
|
current *= 16;
|
||||||
|
current += token - 'a' + 10;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
state = PARSE_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PARSE_EXPECT_PREFIX:
|
||||||
|
switch (token) {
|
||||||
|
case 'X':
|
||||||
|
case 'x':
|
||||||
|
current = 0;
|
||||||
|
state = PARSE_EXPECT_HEX;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
state = PARSE_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PARSE_EXPECT_SUFFIX:
|
||||||
|
// TODO
|
||||||
|
state = PARSE_ERROR;
|
||||||
|
break;
|
||||||
|
case PARSE_ERROR:
|
||||||
|
// This shouldn't be reached
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DebugVector* dv = malloc(sizeof(struct DebugVector));
|
||||||
|
if (state == PARSE_ERROR) {
|
||||||
|
dv->type = ERROR_TYPE;
|
||||||
|
dv->next = 0;
|
||||||
|
} else {
|
||||||
|
dvTemp.intValue = current;
|
||||||
|
*dv = dvTemp;
|
||||||
|
if (string[0] == ' ') {
|
||||||
|
dv->next = _DVParse(debugger, string + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dv;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _DVFree(struct DebugVector* dv) {
|
||||||
|
struct DebugVector* next;
|
||||||
|
while (dv) {
|
||||||
|
next = dv->next;
|
||||||
|
free(dv);
|
||||||
|
dv = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void _parse(struct ARMDebugger* debugger, const char* line) {
|
static void _parse(struct ARMDebugger* debugger, const char* line) {
|
||||||
char* firstSpace = strchr(line, ' ');
|
char* firstSpace = strchr(line, ' ');
|
||||||
size_t cmdLength;
|
size_t cmdLength;
|
||||||
|
struct DebugVector* dv = 0;
|
||||||
if (firstSpace) {
|
if (firstSpace) {
|
||||||
cmdLength = line - firstSpace;
|
cmdLength = firstSpace - line;
|
||||||
|
dv = _DVParse(debugger, firstSpace + 1);
|
||||||
} else {
|
} else {
|
||||||
cmdLength = strlen(line);
|
cmdLength = strlen(line);
|
||||||
}
|
}
|
||||||
@ -103,12 +374,15 @@ static void _parse(struct ARMDebugger* debugger, const char* line) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (strncasecmp(name, line, cmdLength) == 0) {
|
if (strncasecmp(name, line, cmdLength) == 0) {
|
||||||
debuggerCommands[i].command(debugger);
|
debuggerCommands[i].command(debugger, dv);
|
||||||
|
_DVFree(dv);
|
||||||
|
linenoiseHistoryAdd(line);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_DVFree(dv);
|
||||||
ARMRun(debugger->cpu);
|
ARMRun(debugger->cpu);
|
||||||
_printStatus(debugger);
|
_printStatus(debugger, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ARMDebuggerInit(struct ARMDebugger* debugger, struct ARMCore* cpu) {
|
void ARMDebuggerInit(struct ARMDebugger* debugger, struct ARMCore* cpu) {
|
||||||
@ -117,7 +391,7 @@ void ARMDebuggerInit(struct ARMDebugger* debugger, struct ARMCore* cpu) {
|
|||||||
|
|
||||||
void ARMDebuggerEnter(struct ARMDebugger* debugger) {
|
void ARMDebuggerEnter(struct ARMDebugger* debugger) {
|
||||||
char* line;
|
char* line;
|
||||||
_printStatus(debugger);
|
_printStatus(debugger, 0);
|
||||||
while ((line = linenoise("> "))) {
|
while ((line = linenoise("> "))) {
|
||||||
_parse(debugger, line);
|
_parse(debugger, line);
|
||||||
free(line);
|
free(line);
|
||||||
|
Loading…
Reference in New Issue
Block a user