diff --git a/src/debugger/cli-debugger.c b/src/debugger/cli-debugger.c new file mode 100644 index 000000000..48309482c --- /dev/null +++ b/src/debugger/cli-debugger.c @@ -0,0 +1,603 @@ +#include "cli-debugger.h" + +#include +#include +#include +#include +#include +#include + +#ifdef USE_PTHREADS +#include +#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; +} diff --git a/src/debugger/cli-debugger.h b/src/debugger/cli-debugger.h new file mode 100644 index 000000000..11a63c920 --- /dev/null +++ b/src/debugger/cli-debugger.h @@ -0,0 +1,17 @@ +#ifndef CLI_DEBUGGER_H +#define CLI_DEBUGGER_H + +#include "debugger.h" + +#include + +struct CLIDebugger { + struct ARMDebugger d; + + EditLine* elstate; + History* histate; +}; + +void CLIDebuggerCreate(struct CLIDebugger*); + +#endif diff --git a/src/debugger/debugger.c b/src/debugger/debugger.c index ce67dbd78..e3ffc1338 100644 --- a/src/debugger/debugger.c +++ b/src/debugger/debugger.c @@ -1,235 +1,10 @@ #include "debugger.h" -#include "memory-debugger.h" - #include "arm.h" -#include -#include -#include +#include "memory-debugger.h" + #include -#include -#include - -#ifdef USE_PTHREADS -#include -#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) { 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) { debugger->cpu = cpu; debugger->state = DEBUGGER_PAUSED; 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.watchpoints = 0; - _activeDebugger = debugger; - debugger->init = 0; - debugger->deinit = 0; - debugger->paused = _commandLine; - debugger->entered = _reportEntry; - signal(SIGINT, _breakIntoDefault); + debugger->init(debugger); } void ARMDebuggerDeinit(struct ARMDebugger* debugger) { // TODO: actually call this - history_end(debugger->histate); - el_end(debugger->elstate); + debugger->deinit(debugger); } void ARMDebuggerRun(struct ARMDebugger* debugger) { @@ -666,3 +71,20 @@ void ARMDebuggerEnter(struct ARMDebugger* debugger, enum DebuggerEntryReason rea 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; +} diff --git a/src/debugger/debugger.h b/src/debugger/debugger.h index 59738d47e..90e6aaa8e 100644 --- a/src/debugger/debugger.h +++ b/src/debugger/debugger.h @@ -2,8 +2,6 @@ #define DEBUGGER_H #ifdef USE_DEBUGGER -#include - #include "arm.h" #endif @@ -40,13 +38,10 @@ struct ARMDebugger { enum DebuggerState state; struct ARMCore* cpu; - EditLine* elstate; - History* histate; - struct DebugBreakpoint* breakpoints; struct DebugMemoryShim memoryShim; - void (*init)(struct ARMDebugger*, struct ARMCore*); + void (*init)(struct ARMDebugger*); void (*deinit)(struct ARMDebugger*); void (*paused)(struct ARMDebugger*); void (*entered)(struct ARMDebugger*, enum DebuggerEntryReason); @@ -56,6 +51,8 @@ void ARMDebuggerInit(struct ARMDebugger*, struct ARMCore*); void ARMDebuggerDeinit(struct ARMDebugger*); void ARMDebuggerRun(struct ARMDebugger*); void ARMDebuggerEnter(struct ARMDebugger*, enum DebuggerEntryReason); +void ARMDebuggerSetBreakpoint(struct ARMDebugger* debugger, uint32_t address); +void ARMDebuggerSetWatchpoint(struct ARMDebugger* debugger, uint32_t address); #else diff --git a/src/gba/gba-thread.c b/src/gba/gba-thread.c index 53bc5fe43..9a19698cc 100644 --- a/src/gba/gba-thread.c +++ b/src/gba/gba-thread.c @@ -1,7 +1,7 @@ #include "gba-thread.h" #include "arm.h" -#include "debugger.h" +#include "cli-debugger.h" #include "gba.h" #include "gba-serialize.h" @@ -45,7 +45,8 @@ static THREAD_ENTRY _GBAThreadRun(void* context) { #endif #ifdef USE_DEBUGGER - struct ARMDebugger debugger; + struct CLIDebugger debugger; + CLIDebuggerCreate(&debugger); #endif struct GBA gba; struct GBAThread* threadContext = context; @@ -99,8 +100,8 @@ static THREAD_ENTRY _GBAThreadRun(void* context) { #ifdef USE_DEBUGGER if (threadContext->useDebugger) { - threadContext->debugger = &debugger; - GBAAttachDebugger(&gba, &debugger); + threadContext->debugger = &debugger.d; + GBAAttachDebugger(&gba, &debugger.d); } else { threadContext->debugger = 0; } @@ -119,8 +120,8 @@ static THREAD_ENTRY _GBAThreadRun(void* context) { while (threadContext->state < THREAD_EXITING) { #ifdef USE_DEBUGGER if (threadContext->useDebugger) { - ARMDebuggerRun(&debugger); - if (debugger.state == DEBUGGER_SHUTDOWN) { + ARMDebuggerRun(&debugger.d); + if (debugger.d.state == DEBUGGER_SHUTDOWN) { _changeState(threadContext, THREAD_EXITING, 0); } } else {