mirror of https://github.com/mgba-emu/mgba.git
Separate out CLI debugger from debugging interface
This commit is contained in:
parent
6822a0d770
commit
5bbf7f2308
|
@ -0,0 +1,603 @@
|
||||||
|
#include "cli-debugger.h"
|
||||||
|
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#ifdef USE_PTHREADS
|
||||||
|
#include <pthread.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct DebugVector {
|
||||||
|
struct DebugVector* next;
|
||||||
|
enum DVType {
|
||||||
|
ERROR_TYPE,
|
||||||
|
INT_TYPE,
|
||||||
|
CHAR_TYPE
|
||||||
|
} type;
|
||||||
|
union {
|
||||||
|
int32_t intValue;
|
||||||
|
const char* charValue;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char* ERROR_MISSING_ARGS = "Arguments missing";
|
||||||
|
|
||||||
|
static struct CLIDebugger* _activeDebugger;
|
||||||
|
|
||||||
|
typedef void (DebuggerComamnd)(struct CLIDebugger*, struct DebugVector*);
|
||||||
|
|
||||||
|
static void _breakInto(struct CLIDebugger*, struct DebugVector*);
|
||||||
|
static void _continue(struct CLIDebugger*, struct DebugVector*);
|
||||||
|
static void _next(struct CLIDebugger*, struct DebugVector*);
|
||||||
|
static void _print(struct CLIDebugger*, struct DebugVector*);
|
||||||
|
static void _printHex(struct CLIDebugger*, struct DebugVector*);
|
||||||
|
static void _printStatus(struct CLIDebugger*, struct DebugVector*);
|
||||||
|
static void _quit(struct CLIDebugger*, struct DebugVector*);
|
||||||
|
static void _readByte(struct CLIDebugger*, struct DebugVector*);
|
||||||
|
static void _readHalfword(struct CLIDebugger*, struct DebugVector*);
|
||||||
|
static void _readWord(struct CLIDebugger*, struct DebugVector*);
|
||||||
|
static void _setBreakpoint(struct CLIDebugger*, struct DebugVector*);
|
||||||
|
static void _setWatchpoint(struct CLIDebugger*, struct DebugVector*);
|
||||||
|
|
||||||
|
static void _breakIntoDefault(int signal);
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
const char* name;
|
||||||
|
DebuggerComamnd* command;
|
||||||
|
} _debuggerCommands[] = {
|
||||||
|
{ "b", _setBreakpoint },
|
||||||
|
{ "break", _setBreakpoint },
|
||||||
|
{ "c", _continue },
|
||||||
|
{ "continue", _continue },
|
||||||
|
{ "i", _printStatus },
|
||||||
|
{ "info", _printStatus },
|
||||||
|
{ "n", _next },
|
||||||
|
{ "next", _next },
|
||||||
|
{ "p", _print },
|
||||||
|
{ "p/x", _printHex },
|
||||||
|
{ "print", _print },
|
||||||
|
{ "print/x", _printHex },
|
||||||
|
{ "q", _quit },
|
||||||
|
{ "quit", _quit },
|
||||||
|
{ "rb", _readByte },
|
||||||
|
{ "rh", _readHalfword },
|
||||||
|
{ "rw", _readWord },
|
||||||
|
{ "status", _printStatus },
|
||||||
|
{ "w", _setWatchpoint },
|
||||||
|
{ "watch", _setWatchpoint },
|
||||||
|
{ "x", _breakInto },
|
||||||
|
{ 0, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline void _printPSR(union PSR psr) {
|
||||||
|
printf("%08X [%c%c%c%c%c%c%c]\n", psr.packed,
|
||||||
|
psr.n ? 'N' : '-',
|
||||||
|
psr.z ? 'Z' : '-',
|
||||||
|
psr.c ? 'C' : '-',
|
||||||
|
psr.v ? 'V' : '-',
|
||||||
|
psr.i ? 'I' : '-',
|
||||||
|
psr.f ? 'F' : '-',
|
||||||
|
psr.t ? 'T' : '-');
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _handleDeath(int sig) {
|
||||||
|
(void)(sig);
|
||||||
|
printf("No debugger attached!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _breakInto(struct CLIDebugger* debugger, struct DebugVector* dv) {
|
||||||
|
(void)(debugger);
|
||||||
|
(void)(dv);
|
||||||
|
struct sigaction sa, osa;
|
||||||
|
sa.sa_handler = _handleDeath;
|
||||||
|
sigemptyset(&sa.sa_mask);
|
||||||
|
sigaddset(&sa.sa_mask, SIGTRAP);
|
||||||
|
sa.sa_flags = SA_RESTART;
|
||||||
|
sigaction(SIGTRAP, &sa, &osa);
|
||||||
|
#ifdef USE_PTHREADS
|
||||||
|
pthread_kill(pthread_self(), SIGTRAP);
|
||||||
|
#else
|
||||||
|
kill(getpid(), SIGTRAP);
|
||||||
|
#endif
|
||||||
|
sigaction(SIGTRAP, &osa, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _continue(struct CLIDebugger* debugger, struct DebugVector* dv) {
|
||||||
|
(void)(dv);
|
||||||
|
debugger->d.state = DEBUGGER_RUNNING;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _next(struct CLIDebugger* debugger, struct DebugVector* dv) {
|
||||||
|
(void)(dv);
|
||||||
|
ARMRun(debugger->d.cpu);
|
||||||
|
_printStatus(debugger, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _print(struct CLIDebugger* debugger, struct DebugVector* dv) {
|
||||||
|
(void)(debugger);
|
||||||
|
for ( ; dv; dv = dv->next) {
|
||||||
|
printf(" %u", dv->intValue);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _printHex(struct CLIDebugger* debugger, struct DebugVector* dv) {
|
||||||
|
(void)(debugger);
|
||||||
|
for ( ; dv; dv = dv->next) {
|
||||||
|
printf(" 0x%08X", dv->intValue);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void _printLine(struct CLIDebugger* debugger, uint32_t address, enum ExecutionMode mode) {
|
||||||
|
// TODO: write a disassembler
|
||||||
|
if (mode == MODE_ARM) {
|
||||||
|
uint32_t instruction = debugger->d.cpu->memory->load32(debugger->d.cpu->memory, address, 0);
|
||||||
|
printf("%08X\n", instruction);
|
||||||
|
} else {
|
||||||
|
uint16_t instruction = debugger->d.cpu->memory->loadU16(debugger->d.cpu->memory, address, 0);
|
||||||
|
printf("%04X\n", instruction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _printStatus(struct CLIDebugger* debugger, struct DebugVector* dv) {
|
||||||
|
(void)(dv);
|
||||||
|
int r;
|
||||||
|
for (r = 0; r < 4; ++r) {
|
||||||
|
printf("%08X %08X %08X %08X\n",
|
||||||
|
debugger->d.cpu->gprs[r << 2],
|
||||||
|
debugger->d.cpu->gprs[(r << 2) + 1],
|
||||||
|
debugger->d.cpu->gprs[(r << 2) + 2],
|
||||||
|
debugger->d.cpu->gprs[(r << 2) + 3]);
|
||||||
|
}
|
||||||
|
_printPSR(debugger->d.cpu->cpsr);
|
||||||
|
int instructionLength;
|
||||||
|
enum ExecutionMode mode = debugger->d.cpu->cpsr.t;
|
||||||
|
if (mode == MODE_ARM) {
|
||||||
|
instructionLength = WORD_SIZE_ARM;
|
||||||
|
} else {
|
||||||
|
instructionLength = WORD_SIZE_THUMB;
|
||||||
|
}
|
||||||
|
_printLine(debugger, debugger->d.cpu->gprs[ARM_PC] - instructionLength, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _quit(struct CLIDebugger* debugger, struct DebugVector* dv) {
|
||||||
|
(void)(dv);
|
||||||
|
debugger->d.state = DEBUGGER_SHUTDOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _readByte(struct CLIDebugger* debugger, struct DebugVector* dv) {
|
||||||
|
if (!dv || dv->type != INT_TYPE) {
|
||||||
|
printf("%s\n", ERROR_MISSING_ARGS);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint32_t address = dv->intValue;
|
||||||
|
uint8_t value = debugger->d.cpu->memory->loadU8(debugger->d.cpu->memory, address, 0);
|
||||||
|
printf(" 0x%02X\n", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _readHalfword(struct CLIDebugger* debugger, struct DebugVector* dv) {
|
||||||
|
if (!dv || dv->type != INT_TYPE) {
|
||||||
|
printf("%s\n", ERROR_MISSING_ARGS);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint32_t address = dv->intValue;
|
||||||
|
uint16_t value = debugger->d.cpu->memory->loadU16(debugger->d.cpu->memory, address, 0);
|
||||||
|
printf(" 0x%04X\n", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _readWord(struct CLIDebugger* debugger, struct DebugVector* dv) {
|
||||||
|
if (!dv || dv->type != INT_TYPE) {
|
||||||
|
printf("%s\n", ERROR_MISSING_ARGS);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint32_t address = dv->intValue;
|
||||||
|
uint32_t value = debugger->d.cpu->memory->load32(debugger->d.cpu->memory, address, 0);
|
||||||
|
printf(" 0x%08X\n", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _setBreakpoint(struct CLIDebugger* debugger, struct DebugVector* dv) {
|
||||||
|
if (!dv || dv->type != INT_TYPE) {
|
||||||
|
printf("%s\n", ERROR_MISSING_ARGS);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint32_t address = dv->intValue;
|
||||||
|
ARMDebuggerSetBreakpoint(&debugger->d, address);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _setWatchpoint(struct CLIDebugger* debugger, struct DebugVector* dv) {
|
||||||
|
if (!dv || dv->type != INT_TYPE) {
|
||||||
|
printf("%s\n", ERROR_MISSING_ARGS);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint32_t address = dv->intValue;
|
||||||
|
ARMDebuggerSetWatchpoint(&debugger->d, address);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _breakIntoDefault(int signal) {
|
||||||
|
(void)(signal);
|
||||||
|
ARMDebuggerEnter(&_activeDebugger->d, DEBUGGER_ENTER_MANUAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 CLIDebugger* debugger, const char* string, size_t length) {
|
||||||
|
if (!string || length < 1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum _DVParseState state = PARSE_ROOT;
|
||||||
|
struct DebugVector dvTemp = { .type = INT_TYPE };
|
||||||
|
uint32_t current = 0;
|
||||||
|
|
||||||
|
while (length > 0 && string[0] && string[0] != ' ' && state != PARSE_ERROR) {
|
||||||
|
char token = string[0];
|
||||||
|
++string;
|
||||||
|
--length;
|
||||||
|
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->d.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->d.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->d.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->d.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->d.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, length - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dv;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _DVFree(struct DebugVector* dv) {
|
||||||
|
struct DebugVector* next;
|
||||||
|
while (dv) {
|
||||||
|
next = dv->next;
|
||||||
|
free(dv);
|
||||||
|
dv = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _parse(struct CLIDebugger* debugger, const char* line, size_t count) {
|
||||||
|
const char* firstSpace = strchr(line, ' ');
|
||||||
|
size_t cmdLength;
|
||||||
|
struct DebugVector* dv = 0;
|
||||||
|
if (firstSpace) {
|
||||||
|
cmdLength = firstSpace - line;
|
||||||
|
dv = _DVParse(debugger, firstSpace + 1, count - cmdLength - 1);
|
||||||
|
if (dv && dv->type == ERROR_TYPE) {
|
||||||
|
printf("Parse error\n");
|
||||||
|
_DVFree(dv);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cmdLength = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i;
|
||||||
|
const char* name;
|
||||||
|
for (i = 0; (name = _debuggerCommands[i].name); ++i) {
|
||||||
|
if (strlen(name) != cmdLength) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (strncasecmp(name, line, cmdLength) == 0) {
|
||||||
|
_debuggerCommands[i].command(debugger, dv);
|
||||||
|
_DVFree(dv);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_DVFree(dv);
|
||||||
|
printf("Command not found\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char* _prompt(EditLine* el) {
|
||||||
|
(void)(el);
|
||||||
|
return "> ";
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _commandLine(struct ARMDebugger* debugger) {
|
||||||
|
struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
|
||||||
|
const char* line;
|
||||||
|
_printStatus(cliDebugger, 0);
|
||||||
|
int count = 0;
|
||||||
|
HistEvent ev;
|
||||||
|
while (debugger->state == DEBUGGER_PAUSED) {
|
||||||
|
line = el_gets(cliDebugger->elstate, &count);
|
||||||
|
if (!line) {
|
||||||
|
debugger->state = DEBUGGER_EXITING;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (line[0] == '\n') {
|
||||||
|
if (history(cliDebugger->histate, &ev, H_FIRST) >= 0) {
|
||||||
|
_parse(cliDebugger, ev.str, strlen(ev.str) - 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (_parse(cliDebugger, line, count - 1)) {
|
||||||
|
history(cliDebugger->histate, &ev, H_ENTER, line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _reportEntry(struct ARMDebugger* debugger, enum DebuggerEntryReason reason) {
|
||||||
|
(void) (debugger);
|
||||||
|
switch (reason) {
|
||||||
|
case DEBUGGER_ENTER_MANUAL:
|
||||||
|
break;
|
||||||
|
case DEBUGGER_ENTER_BREAKPOINT:
|
||||||
|
printf("Hit breakpoint\n");
|
||||||
|
break;
|
||||||
|
case DEBUGGER_ENTER_WATCHPOINT:
|
||||||
|
printf("Hit watchpoint\n");
|
||||||
|
break;
|
||||||
|
case DEBUGGER_ENTER_ILLEGAL_OP:
|
||||||
|
printf("Hit illegal opcode\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned char _tabComplete(EditLine* elstate, int ch) {
|
||||||
|
(void)(ch);
|
||||||
|
const LineInfo* li = el_line(elstate);
|
||||||
|
const char* commandPtr;
|
||||||
|
int cmd = 0, len = 0;
|
||||||
|
const char* name = 0;
|
||||||
|
for (commandPtr = li->buffer; commandPtr <= li->cursor; ++commandPtr, ++len) {
|
||||||
|
for (; (name = _debuggerCommands[cmd].name); ++cmd) {
|
||||||
|
int cmp = strncasecmp(name, li->buffer, len);
|
||||||
|
if (cmp > 0) {
|
||||||
|
return CC_ERROR;
|
||||||
|
}
|
||||||
|
if (cmp == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_debuggerCommands[cmd + 1].name && strncasecmp(_debuggerCommands[cmd + 1].name, li->buffer, len - 1) == 0) {
|
||||||
|
return CC_ERROR;
|
||||||
|
}
|
||||||
|
name += len - 1;
|
||||||
|
el_insertstr(elstate, name);
|
||||||
|
el_insertstr(elstate, " ");
|
||||||
|
return CC_REDISPLAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _cliDebuggerInit(struct ARMDebugger* debugger) {
|
||||||
|
struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
|
||||||
|
// TODO: get argv[0]
|
||||||
|
cliDebugger->elstate = el_init("gbac", stdin, stdout, stderr);
|
||||||
|
el_set(cliDebugger->elstate, EL_PROMPT, _prompt);
|
||||||
|
el_set(cliDebugger->elstate, EL_EDITOR, "emacs");
|
||||||
|
|
||||||
|
el_set(cliDebugger->elstate, EL_CLIENTDATA, cliDebugger);
|
||||||
|
el_set(cliDebugger->elstate, EL_ADDFN, "tab-complete", "Tab completion", _tabComplete);
|
||||||
|
el_set(cliDebugger->elstate, EL_BIND, "\t", "tab-complete", 0);
|
||||||
|
cliDebugger->histate = history_init();
|
||||||
|
HistEvent ev;
|
||||||
|
history(cliDebugger->histate, &ev, H_SETSIZE, 200);
|
||||||
|
el_set(cliDebugger->elstate, EL_HIST, history, cliDebugger->histate);
|
||||||
|
_activeDebugger = cliDebugger;
|
||||||
|
signal(SIGINT, _breakIntoDefault);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _cliDebuggerDeinit(struct ARMDebugger* debugger) {
|
||||||
|
struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
|
||||||
|
history_end(cliDebugger->histate);
|
||||||
|
el_end(cliDebugger->elstate);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLIDebuggerCreate(struct CLIDebugger* debugger) {
|
||||||
|
debugger->d.init = _cliDebuggerInit;
|
||||||
|
debugger->d.deinit = _cliDebuggerDeinit;
|
||||||
|
debugger->d.paused = _commandLine;
|
||||||
|
debugger->d.entered = _reportEntry;
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
#ifndef CLI_DEBUGGER_H
|
||||||
|
#define CLI_DEBUGGER_H
|
||||||
|
|
||||||
|
#include "debugger.h"
|
||||||
|
|
||||||
|
#include <histedit.h>
|
||||||
|
|
||||||
|
struct CLIDebugger {
|
||||||
|
struct ARMDebugger d;
|
||||||
|
|
||||||
|
EditLine* elstate;
|
||||||
|
History* histate;
|
||||||
|
};
|
||||||
|
|
||||||
|
void CLIDebuggerCreate(struct CLIDebugger*);
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,235 +1,10 @@
|
||||||
#include "debugger.h"
|
#include "debugger.h"
|
||||||
|
|
||||||
#include "memory-debugger.h"
|
|
||||||
|
|
||||||
#include "arm.h"
|
#include "arm.h"
|
||||||
|
|
||||||
#include <signal.h>
|
#include "memory-debugger.h"
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#ifdef USE_PTHREADS
|
|
||||||
#include <pthread.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct DebugVector {
|
|
||||||
struct DebugVector* next;
|
|
||||||
enum DVType {
|
|
||||||
ERROR_TYPE,
|
|
||||||
INT_TYPE,
|
|
||||||
CHAR_TYPE
|
|
||||||
} type;
|
|
||||||
union {
|
|
||||||
int32_t intValue;
|
|
||||||
const char* charValue;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char* ERROR_MISSING_ARGS = "Arguments missing";
|
|
||||||
|
|
||||||
static struct ARMDebugger* _activeDebugger;
|
|
||||||
|
|
||||||
typedef void (DebuggerComamnd)(struct ARMDebugger*, struct DebugVector*);
|
|
||||||
|
|
||||||
static void _breakInto(struct ARMDebugger*, struct DebugVector*);
|
|
||||||
static void _continue(struct ARMDebugger*, struct DebugVector*);
|
|
||||||
static void _next(struct ARMDebugger*, struct DebugVector*);
|
|
||||||
static void _print(struct ARMDebugger*, struct DebugVector*);
|
|
||||||
static void _printHex(struct ARMDebugger*, struct DebugVector*);
|
|
||||||
static void _printStatus(struct ARMDebugger*, struct DebugVector*);
|
|
||||||
static void _quit(struct ARMDebugger*, struct DebugVector*);
|
|
||||||
static void _readByte(struct ARMDebugger*, struct DebugVector*);
|
|
||||||
static void _readHalfword(struct ARMDebugger*, struct DebugVector*);
|
|
||||||
static void _readWord(struct ARMDebugger*, struct DebugVector*);
|
|
||||||
static void _setBreakpoint(struct ARMDebugger*, struct DebugVector*);
|
|
||||||
static void _setWatchpoint(struct ARMDebugger*, struct DebugVector*);
|
|
||||||
|
|
||||||
static void _breakIntoDefault(int signal);
|
|
||||||
|
|
||||||
static struct {
|
|
||||||
const char* name;
|
|
||||||
DebuggerComamnd* command;
|
|
||||||
} _debuggerCommands[] = {
|
|
||||||
{ "b", _setBreakpoint },
|
|
||||||
{ "break", _setBreakpoint },
|
|
||||||
{ "c", _continue },
|
|
||||||
{ "continue", _continue },
|
|
||||||
{ "i", _printStatus },
|
|
||||||
{ "info", _printStatus },
|
|
||||||
{ "n", _next },
|
|
||||||
{ "next", _next },
|
|
||||||
{ "p", _print },
|
|
||||||
{ "p/x", _printHex },
|
|
||||||
{ "print", _print },
|
|
||||||
{ "print/x", _printHex },
|
|
||||||
{ "q", _quit },
|
|
||||||
{ "quit", _quit },
|
|
||||||
{ "rb", _readByte },
|
|
||||||
{ "rh", _readHalfword },
|
|
||||||
{ "rw", _readWord },
|
|
||||||
{ "status", _printStatus },
|
|
||||||
{ "w", _setWatchpoint },
|
|
||||||
{ "watch", _setWatchpoint },
|
|
||||||
{ "x", _breakInto },
|
|
||||||
{ 0, 0 }
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline void _printPSR(union PSR psr) {
|
|
||||||
printf("%08X [%c%c%c%c%c%c%c]\n", psr.packed,
|
|
||||||
psr.n ? 'N' : '-',
|
|
||||||
psr.z ? 'Z' : '-',
|
|
||||||
psr.c ? 'C' : '-',
|
|
||||||
psr.v ? 'V' : '-',
|
|
||||||
psr.i ? 'I' : '-',
|
|
||||||
psr.f ? 'F' : '-',
|
|
||||||
psr.t ? 'T' : '-');
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _handleDeath(int sig) {
|
|
||||||
(void)(sig);
|
|
||||||
printf("No debugger attached!\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _breakInto(struct ARMDebugger* debugger, struct DebugVector* dv) {
|
|
||||||
(void)(debugger);
|
|
||||||
(void)(dv);
|
|
||||||
struct sigaction sa, osa;
|
|
||||||
sa.sa_handler = _handleDeath;
|
|
||||||
sigemptyset(&sa.sa_mask);
|
|
||||||
sigaddset(&sa.sa_mask, SIGTRAP);
|
|
||||||
sa.sa_flags = SA_RESTART;
|
|
||||||
sigaction(SIGTRAP, &sa, &osa);
|
|
||||||
#ifdef USE_PTHREADS
|
|
||||||
pthread_kill(pthread_self(), SIGTRAP);
|
|
||||||
#else
|
|
||||||
kill(getpid(), SIGTRAP);
|
|
||||||
#endif
|
|
||||||
sigaction(SIGTRAP, &osa, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _continue(struct ARMDebugger* debugger, struct DebugVector* dv) {
|
|
||||||
(void)(dv);
|
|
||||||
debugger->state = DEBUGGER_RUNNING;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _next(struct ARMDebugger* debugger, struct DebugVector* dv) {
|
|
||||||
(void)(dv);
|
|
||||||
ARMRun(debugger->cpu);
|
|
||||||
_printStatus(debugger, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _print(struct ARMDebugger* debugger, struct DebugVector* dv) {
|
|
||||||
(void)(debugger);
|
|
||||||
for ( ; dv; dv = dv->next) {
|
|
||||||
printf(" %u", dv->intValue);
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _printHex(struct ARMDebugger* debugger, struct DebugVector* dv) {
|
|
||||||
(void)(debugger);
|
|
||||||
for ( ; dv; dv = dv->next) {
|
|
||||||
printf(" 0x%08X", dv->intValue);
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void _printLine(struct ARMDebugger* debugger, uint32_t address, enum ExecutionMode mode) {
|
|
||||||
// TODO: write a disassembler
|
|
||||||
if (mode == MODE_ARM) {
|
|
||||||
uint32_t instruction = debugger->cpu->memory->load32(debugger->cpu->memory, address, 0);
|
|
||||||
printf("%08X\n", instruction);
|
|
||||||
} else {
|
|
||||||
uint16_t instruction = debugger->cpu->memory->loadU16(debugger->cpu->memory, address, 0);
|
|
||||||
printf("%04X\n", instruction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _printStatus(struct ARMDebugger* debugger, struct DebugVector* dv) {
|
|
||||||
(void)(dv);
|
|
||||||
int r;
|
|
||||||
for (r = 0; r < 4; ++r) {
|
|
||||||
printf("%08X %08X %08X %08X\n",
|
|
||||||
debugger->cpu->gprs[r << 2],
|
|
||||||
debugger->cpu->gprs[(r << 2) + 1],
|
|
||||||
debugger->cpu->gprs[(r << 2) + 2],
|
|
||||||
debugger->cpu->gprs[(r << 2) + 3]);
|
|
||||||
}
|
|
||||||
_printPSR(debugger->cpu->cpsr);
|
|
||||||
int instructionLength;
|
|
||||||
enum ExecutionMode mode = debugger->cpu->cpsr.t;
|
|
||||||
if (mode == MODE_ARM) {
|
|
||||||
instructionLength = WORD_SIZE_ARM;
|
|
||||||
} else {
|
|
||||||
instructionLength = WORD_SIZE_THUMB;
|
|
||||||
}
|
|
||||||
_printLine(debugger, debugger->cpu->gprs[ARM_PC] - instructionLength, mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _quit(struct ARMDebugger* debugger, struct DebugVector* dv) {
|
|
||||||
(void)(dv);
|
|
||||||
debugger->state = DEBUGGER_SHUTDOWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _readByte(struct ARMDebugger* debugger, struct DebugVector* dv) {
|
|
||||||
if (!dv || dv->type != INT_TYPE) {
|
|
||||||
printf("%s\n", ERROR_MISSING_ARGS);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
uint32_t address = dv->intValue;
|
|
||||||
uint8_t value = debugger->cpu->memory->loadU8(debugger->cpu->memory, address, 0);
|
|
||||||
printf(" 0x%02X\n", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _readHalfword(struct ARMDebugger* debugger, struct DebugVector* dv) {
|
|
||||||
if (!dv || dv->type != INT_TYPE) {
|
|
||||||
printf("%s\n", ERROR_MISSING_ARGS);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
uint32_t address = dv->intValue;
|
|
||||||
uint16_t value = debugger->cpu->memory->loadU16(debugger->cpu->memory, address, 0);
|
|
||||||
printf(" 0x%04X\n", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _readWord(struct ARMDebugger* debugger, struct DebugVector* dv) {
|
|
||||||
if (!dv || dv->type != INT_TYPE) {
|
|
||||||
printf("%s\n", ERROR_MISSING_ARGS);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
uint32_t address = dv->intValue;
|
|
||||||
uint32_t value = debugger->cpu->memory->load32(debugger->cpu->memory, address, 0);
|
|
||||||
printf(" 0x%08X\n", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _setBreakpoint(struct ARMDebugger* debugger, struct DebugVector* dv) {
|
|
||||||
if (!dv || dv->type != INT_TYPE) {
|
|
||||||
printf("%s\n", ERROR_MISSING_ARGS);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
uint32_t address = dv->intValue;
|
|
||||||
struct DebugBreakpoint* breakpoint = malloc(sizeof(struct DebugBreakpoint));
|
|
||||||
breakpoint->address = address;
|
|
||||||
breakpoint->next = debugger->breakpoints;
|
|
||||||
debugger->breakpoints = breakpoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _setWatchpoint(struct ARMDebugger* debugger, struct DebugVector* dv) {
|
|
||||||
if (!dv || dv->type != INT_TYPE) {
|
|
||||||
printf("%s\n", ERROR_MISSING_ARGS);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
uint32_t address = dv->intValue;
|
|
||||||
if (debugger->cpu->memory != &debugger->memoryShim.d) {
|
|
||||||
ARMDebuggerInstallMemoryShim(debugger);
|
|
||||||
}
|
|
||||||
struct DebugBreakpoint* watchpoint = malloc(sizeof(struct DebugBreakpoint));
|
|
||||||
watchpoint->address = address;
|
|
||||||
watchpoint->next = debugger->memoryShim.watchpoints;
|
|
||||||
debugger->memoryShim.watchpoints = watchpoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _checkBreakpoints(struct ARMDebugger* debugger) {
|
static void _checkBreakpoints(struct ARMDebugger* debugger) {
|
||||||
struct DebugBreakpoint* breakpoint;
|
struct DebugBreakpoint* breakpoint;
|
||||||
|
@ -248,388 +23,18 @@ static void _checkBreakpoints(struct ARMDebugger* debugger) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _breakIntoDefault(int signal) {
|
|
||||||
(void)(signal);
|
|
||||||
ARMDebuggerEnter(_activeDebugger, DEBUGGER_ENTER_MANUAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
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, size_t length) {
|
|
||||||
if (!string || length < 1) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum _DVParseState state = PARSE_ROOT;
|
|
||||||
struct DebugVector dvTemp = { .type = INT_TYPE };
|
|
||||||
uint32_t current = 0;
|
|
||||||
|
|
||||||
while (length > 0 && string[0] && string[0] != ' ' && state != PARSE_ERROR) {
|
|
||||||
char token = string[0];
|
|
||||||
++string;
|
|
||||||
--length;
|
|
||||||
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, length - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return dv;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _DVFree(struct DebugVector* dv) {
|
|
||||||
struct DebugVector* next;
|
|
||||||
while (dv) {
|
|
||||||
next = dv->next;
|
|
||||||
free(dv);
|
|
||||||
dv = next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _parse(struct ARMDebugger* debugger, const char* line, size_t count) {
|
|
||||||
const char* firstSpace = strchr(line, ' ');
|
|
||||||
size_t cmdLength;
|
|
||||||
struct DebugVector* dv = 0;
|
|
||||||
if (firstSpace) {
|
|
||||||
cmdLength = firstSpace - line;
|
|
||||||
dv = _DVParse(debugger, firstSpace + 1, count - cmdLength - 1);
|
|
||||||
if (dv && dv->type == ERROR_TYPE) {
|
|
||||||
printf("Parse error\n");
|
|
||||||
_DVFree(dv);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cmdLength = count;
|
|
||||||
}
|
|
||||||
|
|
||||||
int i;
|
|
||||||
const char* name;
|
|
||||||
for (i = 0; (name = _debuggerCommands[i].name); ++i) {
|
|
||||||
if (strlen(name) != cmdLength) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (strncasecmp(name, line, cmdLength) == 0) {
|
|
||||||
_debuggerCommands[i].command(debugger, dv);
|
|
||||||
_DVFree(dv);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_DVFree(dv);
|
|
||||||
printf("Command not found\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char* _prompt(EditLine* el) {
|
|
||||||
(void)(el);
|
|
||||||
return "> ";
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _commandLine(struct ARMDebugger* debugger) {
|
|
||||||
const char* line;
|
|
||||||
_printStatus(debugger, 0);
|
|
||||||
int count = 0;
|
|
||||||
HistEvent ev;
|
|
||||||
while (debugger->state == DEBUGGER_PAUSED) {
|
|
||||||
line = el_gets(debugger->elstate, &count);
|
|
||||||
if (!line) {
|
|
||||||
debugger->state = DEBUGGER_EXITING;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (line[0] == '\n') {
|
|
||||||
if (history(debugger->histate, &ev, H_FIRST) >= 0) {
|
|
||||||
_parse(debugger, ev.str, strlen(ev.str) - 1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (_parse(debugger, line, count - 1)) {
|
|
||||||
history(debugger->histate, &ev, H_ENTER, line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _reportEntry(struct ARMDebugger* debugger, enum DebuggerEntryReason reason) {
|
|
||||||
(void) (debugger);
|
|
||||||
switch (reason) {
|
|
||||||
case DEBUGGER_ENTER_MANUAL:
|
|
||||||
break;
|
|
||||||
case DEBUGGER_ENTER_BREAKPOINT:
|
|
||||||
printf("Hit breakpoint\n");
|
|
||||||
break;
|
|
||||||
case DEBUGGER_ENTER_WATCHPOINT:
|
|
||||||
printf("Hit watchpoint\n");
|
|
||||||
break;
|
|
||||||
case DEBUGGER_ENTER_ILLEGAL_OP:
|
|
||||||
printf("Hit illegal opcode\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned char _tabComplete(EditLine* elstate, int ch) {
|
|
||||||
(void)(ch);
|
|
||||||
const LineInfo* li = el_line(elstate);
|
|
||||||
const char* commandPtr;
|
|
||||||
int cmd = 0, len = 0;
|
|
||||||
const char* name = 0;
|
|
||||||
for (commandPtr = li->buffer; commandPtr <= li->cursor; ++commandPtr, ++len) {
|
|
||||||
for (; (name = _debuggerCommands[cmd].name); ++cmd) {
|
|
||||||
int cmp = strncasecmp(name, li->buffer, len);
|
|
||||||
if (cmp > 0) {
|
|
||||||
return CC_ERROR;
|
|
||||||
}
|
|
||||||
if (cmp == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (_debuggerCommands[cmd + 1].name && strncasecmp(_debuggerCommands[cmd + 1].name, li->buffer, len - 1) == 0) {
|
|
||||||
return CC_ERROR;
|
|
||||||
}
|
|
||||||
name += len - 1;
|
|
||||||
el_insertstr(elstate, name);
|
|
||||||
el_insertstr(elstate, " ");
|
|
||||||
return CC_REDISPLAY;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ARMDebuggerInit(struct ARMDebugger* debugger, struct ARMCore* cpu) {
|
void ARMDebuggerInit(struct ARMDebugger* debugger, struct ARMCore* cpu) {
|
||||||
debugger->cpu = cpu;
|
debugger->cpu = cpu;
|
||||||
debugger->state = DEBUGGER_PAUSED;
|
debugger->state = DEBUGGER_PAUSED;
|
||||||
debugger->breakpoints = 0;
|
debugger->breakpoints = 0;
|
||||||
// TODO: get argv[0]
|
|
||||||
debugger->elstate = el_init("gbac", stdin, stdout, stderr);
|
|
||||||
el_set(debugger->elstate, EL_PROMPT, _prompt);
|
|
||||||
el_set(debugger->elstate, EL_EDITOR, "emacs");
|
|
||||||
|
|
||||||
el_set(debugger->elstate, EL_CLIENTDATA, debugger);
|
|
||||||
el_set(debugger->elstate, EL_ADDFN, "tab-complete", "Tab completion", _tabComplete);
|
|
||||||
el_set(debugger->elstate, EL_BIND, "\t", "tab-complete", 0);
|
|
||||||
debugger->histate = history_init();
|
|
||||||
HistEvent ev;
|
|
||||||
history(debugger->histate, &ev, H_SETSIZE, 200);
|
|
||||||
el_set(debugger->elstate, EL_HIST, history, debugger->histate);
|
|
||||||
debugger->memoryShim.p = debugger;
|
debugger->memoryShim.p = debugger;
|
||||||
debugger->memoryShim.watchpoints = 0;
|
debugger->memoryShim.watchpoints = 0;
|
||||||
_activeDebugger = debugger;
|
debugger->init(debugger);
|
||||||
debugger->init = 0;
|
|
||||||
debugger->deinit = 0;
|
|
||||||
debugger->paused = _commandLine;
|
|
||||||
debugger->entered = _reportEntry;
|
|
||||||
signal(SIGINT, _breakIntoDefault);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ARMDebuggerDeinit(struct ARMDebugger* debugger) {
|
void ARMDebuggerDeinit(struct ARMDebugger* debugger) {
|
||||||
// TODO: actually call this
|
// TODO: actually call this
|
||||||
history_end(debugger->histate);
|
debugger->deinit(debugger);
|
||||||
el_end(debugger->elstate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ARMDebuggerRun(struct ARMDebugger* debugger) {
|
void ARMDebuggerRun(struct ARMDebugger* debugger) {
|
||||||
|
@ -666,3 +71,20 @@ void ARMDebuggerEnter(struct ARMDebugger* debugger, enum DebuggerEntryReason rea
|
||||||
debugger->entered(debugger, reason);
|
debugger->entered(debugger, reason);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ARMDebuggerSetBreakpoint(struct ARMDebugger* debugger, uint32_t address) {
|
||||||
|
struct DebugBreakpoint* breakpoint = malloc(sizeof(struct DebugBreakpoint));
|
||||||
|
breakpoint->address = address;
|
||||||
|
breakpoint->next = debugger->breakpoints;
|
||||||
|
debugger->breakpoints = breakpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ARMDebuggerSetWatchpoint(struct ARMDebugger* debugger, uint32_t address) {
|
||||||
|
if (debugger->cpu->memory != &debugger->memoryShim.d) {
|
||||||
|
ARMDebuggerInstallMemoryShim(debugger);
|
||||||
|
}
|
||||||
|
struct DebugBreakpoint* watchpoint = malloc(sizeof(struct DebugBreakpoint));
|
||||||
|
watchpoint->address = address;
|
||||||
|
watchpoint->next = debugger->memoryShim.watchpoints;
|
||||||
|
debugger->memoryShim.watchpoints = watchpoint;
|
||||||
|
}
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
#define DEBUGGER_H
|
#define DEBUGGER_H
|
||||||
|
|
||||||
#ifdef USE_DEBUGGER
|
#ifdef USE_DEBUGGER
|
||||||
#include <histedit.h>
|
|
||||||
|
|
||||||
#include "arm.h"
|
#include "arm.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -40,13 +38,10 @@ struct ARMDebugger {
|
||||||
enum DebuggerState state;
|
enum DebuggerState state;
|
||||||
struct ARMCore* cpu;
|
struct ARMCore* cpu;
|
||||||
|
|
||||||
EditLine* elstate;
|
|
||||||
History* histate;
|
|
||||||
|
|
||||||
struct DebugBreakpoint* breakpoints;
|
struct DebugBreakpoint* breakpoints;
|
||||||
struct DebugMemoryShim memoryShim;
|
struct DebugMemoryShim memoryShim;
|
||||||
|
|
||||||
void (*init)(struct ARMDebugger*, struct ARMCore*);
|
void (*init)(struct ARMDebugger*);
|
||||||
void (*deinit)(struct ARMDebugger*);
|
void (*deinit)(struct ARMDebugger*);
|
||||||
void (*paused)(struct ARMDebugger*);
|
void (*paused)(struct ARMDebugger*);
|
||||||
void (*entered)(struct ARMDebugger*, enum DebuggerEntryReason);
|
void (*entered)(struct ARMDebugger*, enum DebuggerEntryReason);
|
||||||
|
@ -56,6 +51,8 @@ void ARMDebuggerInit(struct ARMDebugger*, struct ARMCore*);
|
||||||
void ARMDebuggerDeinit(struct ARMDebugger*);
|
void ARMDebuggerDeinit(struct ARMDebugger*);
|
||||||
void ARMDebuggerRun(struct ARMDebugger*);
|
void ARMDebuggerRun(struct ARMDebugger*);
|
||||||
void ARMDebuggerEnter(struct ARMDebugger*, enum DebuggerEntryReason);
|
void ARMDebuggerEnter(struct ARMDebugger*, enum DebuggerEntryReason);
|
||||||
|
void ARMDebuggerSetBreakpoint(struct ARMDebugger* debugger, uint32_t address);
|
||||||
|
void ARMDebuggerSetWatchpoint(struct ARMDebugger* debugger, uint32_t address);
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#include "gba-thread.h"
|
#include "gba-thread.h"
|
||||||
|
|
||||||
#include "arm.h"
|
#include "arm.h"
|
||||||
#include "debugger.h"
|
#include "cli-debugger.h"
|
||||||
#include "gba.h"
|
#include "gba.h"
|
||||||
#include "gba-serialize.h"
|
#include "gba-serialize.h"
|
||||||
|
|
||||||
|
@ -45,7 +45,8 @@ static THREAD_ENTRY _GBAThreadRun(void* context) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_DEBUGGER
|
#ifdef USE_DEBUGGER
|
||||||
struct ARMDebugger debugger;
|
struct CLIDebugger debugger;
|
||||||
|
CLIDebuggerCreate(&debugger);
|
||||||
#endif
|
#endif
|
||||||
struct GBA gba;
|
struct GBA gba;
|
||||||
struct GBAThread* threadContext = context;
|
struct GBAThread* threadContext = context;
|
||||||
|
@ -99,8 +100,8 @@ static THREAD_ENTRY _GBAThreadRun(void* context) {
|
||||||
|
|
||||||
#ifdef USE_DEBUGGER
|
#ifdef USE_DEBUGGER
|
||||||
if (threadContext->useDebugger) {
|
if (threadContext->useDebugger) {
|
||||||
threadContext->debugger = &debugger;
|
threadContext->debugger = &debugger.d;
|
||||||
GBAAttachDebugger(&gba, &debugger);
|
GBAAttachDebugger(&gba, &debugger.d);
|
||||||
} else {
|
} else {
|
||||||
threadContext->debugger = 0;
|
threadContext->debugger = 0;
|
||||||
}
|
}
|
||||||
|
@ -119,8 +120,8 @@ static THREAD_ENTRY _GBAThreadRun(void* context) {
|
||||||
while (threadContext->state < THREAD_EXITING) {
|
while (threadContext->state < THREAD_EXITING) {
|
||||||
#ifdef USE_DEBUGGER
|
#ifdef USE_DEBUGGER
|
||||||
if (threadContext->useDebugger) {
|
if (threadContext->useDebugger) {
|
||||||
ARMDebuggerRun(&debugger);
|
ARMDebuggerRun(&debugger.d);
|
||||||
if (debugger.state == DEBUGGER_SHUTDOWN) {
|
if (debugger.d.state == DEBUGGER_SHUTDOWN) {
|
||||||
_changeState(threadContext, THREAD_EXITING, 0);
|
_changeState(threadContext, THREAD_EXITING, 0);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in New Issue