Merge branch 'master' into qt

Conflicts:
	CMakeLists.txt
	src/gba/gba.c
This commit is contained in:
Jeffrey Pfau 2014-02-01 17:37:18 -08:00
commit 11e3bdc585
17 changed files with 1394 additions and 692 deletions

View File

@ -3,7 +3,8 @@ project(GBAc)
set(BINARY_NAME gbac CACHE INTERNAL "Name of output binaries")
set(CMAKE_C_FLAGS_DEBUG "-g -Wall -Wextra --std=gnu99")
set(CMAKE_C_FLAGS_RELEASE "-O3 -Wall -Wextra --std=gnu99")
set(USE_DEBUGGER ON CACHE BOOL "Whether or not to enable the ARM debugger")
set(USE_CLI_DEBUGGER ON CACHE BOOL "Whether or not to enable the CLI-mode ARM debugger")
set(USE_GDB_STUB ON CACHE BOOL "Whether or not to enable the GDB stub ARM debugger")
set(BUILD_QT ON CACHE BOOL "Build Qt frontend")
set(BUILD_SDL ON CACHE BOOL "Build SDL frontend")
set(BUILD_PERF ON CACHE BOOL "Build performance profiling tool")
@ -25,21 +26,25 @@ if(WIN32)
source_group("Windows-specific code" FILES ${OS_SRC})
else()
add_definitions(-DUSE_PTHREADS)
set(OS_LIBRARY "${OS_LIBRARY};pthread")
set(OS_LIB "${OS_LIB};pthread")
file(GLOB OS_SRC ${CMAKE_SOURCE_DIR}/src/platform/posix/*.c)
source_group("POSIX-specific code" FILES ${OS_SRC})
endif()
if(USE_DEBUGGER)
file(GLOB DEBUGGER_SRC ${CMAKE_SOURCE_DIR}/src/debugger/*.c)
source_group("ARM debugger" FILES ${DEBUGGER_SRC})
set(DEBUGGER_SRC "${CMAKE_SOURCE_DIR}/src/debugger/debugger.c;${CMAKE_SOURCE_DIR}/src/debugger/memory-debugger.c")
if(USE_CLI_DEBUGGER)
set(DEBUGGER_SRC "${DEBUGGER_SRC};${CMAKE_SOURCE_DIR}/src/debugger/cli-debugger.c")
set(DEBUGGER_LIB "edit")
add_definitions(-DUSE_DEBUGGER)
else()
set(DEBUGGER_SRC "")
set(DEBUGGER_LIB "")
endif()
if(USE_GDB_STUB)
set(DEBUGGER_SRC "${DEBUGGER_SRC};${CMAKE_SOURCE_DIR}/src/debugger/gdb-stub.c")
endif()
source_group("ARM debugger" FILES ${DEBUGGER_SRC})
add_library(${BINARY_NAME} SHARED ${ARM_SRC} ${GBA_SRC} ${DEBUGGER_SRC} ${RENDERER_SRC} ${UTIL_SRC} ${OS_SRC})
target_link_libraries(${BINARY_NAME} m ${DEBUGGER_LIB} ${OS_LIB})
@ -50,11 +55,11 @@ endif()
if(BUILD_PERF)
set(PERF_SRC ${CMAKE_SOURCE_DIR}/src/platform/perf-main.c)
if(UNIX AND NOT APPLE)
set(PERF_LIB "$PERF_LIB};rt")
set(PERF_LIB "${PERF_LIB};rt")
endif()
add_executable(${BINARY_NAME}-perf ${PERF_SRC})
target_link_libraries(${BINARY_NAME}-perf ${BINARY_NAME} ${PERF_LIBRARY})
target_link_libraries(${BINARY_NAME}-perf ${BINARY_NAME} ${PERF_LIB})
endif()
if(BUILD_QT)

616
src/debugger/cli-debugger.c Normal file
View File

@ -0,0 +1,616 @@
#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 (DebuggerCommand)(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 _clearBreakpoint(struct CLIDebugger*, struct DebugVector*);
static void _setWatchpoint(struct CLIDebugger*, struct DebugVector*);
static void _breakIntoDefault(int signal);
static struct {
const char* name;
DebuggerCommand* command;
} _debuggerCommands[] = {
{ "b", _setBreakpoint },
{ "break", _setBreakpoint },
{ "c", _continue },
{ "continue", _continue },
{ "d", _clearBreakpoint },
{ "delete", _clearBreakpoint },
{ "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 _clearBreakpoint(struct CLIDebugger* debugger, struct DebugVector* dv) {
if (!dv || dv->type != INT_TYPE) {
printf("%s\n", ERROR_MISSING_ARGS);
return;
}
uint32_t address = dv->intValue;
ARMDebuggerClearBreakpoint(&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:
case DEBUGGER_ENTER_ATTACHED:
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;
}

View File

@ -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

View File

@ -1,235 +1,10 @@
#include "debugger.h"
#include "memory-debugger.h"
#include "arm.h"
#include <signal.h>
#include <stdio.h>
#include <stdarg.h>
#include "memory-debugger.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) {
struct DebugBreakpoint* breakpoint;
@ -241,375 +16,28 @@ static void _checkBreakpoints(struct ARMDebugger* debugger) {
instructionLength = WORD_SIZE_THUMB;
}
for (breakpoint = debugger->breakpoints; breakpoint; breakpoint = breakpoint->next) {
if (breakpoint->address + instructionLength == debugger->cpu->gprs[ARM_PC]) {
debugger->state = DEBUGGER_PAUSED;
printf("Hit breakpoint\n");
if (breakpoint->address + instructionLength == (uint32_t) debugger->cpu->gprs[ARM_PC]) {
ARMDebuggerEnter(debugger, DEBUGGER_ENTER_BREAKPOINT);
break;
}
}
}
static void _breakIntoDefault(int signal) {
(void)(signal);
_activeDebugger->state = DEBUGGER_PAUSED;
}
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 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.original = cpu->memory;
debugger->memoryShim.p = debugger;
debugger->memoryShim.watchpoints = 0;
_activeDebugger = debugger;
signal(SIGINT, _breakIntoDefault);
if (debugger->init) {
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) {
@ -631,7 +59,11 @@ void ARMDebuggerRun(struct ARMDebugger* debugger) {
case DEBUGGER_RUNNING:
break;
case DEBUGGER_PAUSED:
_commandLine(debugger);
if (debugger->paused) {
debugger->paused(debugger);
} else {
debugger->state = DEBUGGER_RUNNING;
}
break;
case DEBUGGER_EXITING:
case DEBUGGER_SHUTDOWN:
@ -640,6 +72,37 @@ void ARMDebuggerRun(struct ARMDebugger* debugger) {
}
}
void ARMDebuggerEnter(struct ARMDebugger* debugger) {
void ARMDebuggerEnter(struct ARMDebugger* debugger, enum DebuggerEntryReason reason) {
debugger->state = DEBUGGER_PAUSED;
if (debugger->entered) {
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 ARMDebuggerClearBreakpoint(struct ARMDebugger* debugger, uint32_t address) {
struct DebugBreakpoint** previous = &debugger->breakpoints;
struct DebugBreakpoint* breakpoint;
for (; (breakpoint = *previous); previous = &breakpoint->next) {
if (breakpoint->address == address) {
*previous = breakpoint->next;
free(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;
}

View File

@ -1,11 +1,7 @@
#ifndef DEBUGGER_H
#define DEBUGGER_H
#ifdef USE_DEBUGGER
#include <histedit.h>
#include "arm.h"
#endif
enum DebuggerState {
DEBUGGER_PAUSED,
@ -14,11 +10,9 @@ enum DebuggerState {
DEBUGGER_SHUTDOWN
};
#ifdef USE_DEBUGGER
struct DebugBreakpoint {
struct DebugBreakpoint* next;
int32_t address;
uint32_t address;
};
struct DebugMemoryShim {
@ -29,28 +23,43 @@ struct DebugMemoryShim {
struct DebugBreakpoint* watchpoints;
};
enum DebuggerEntryReason {
DEBUGGER_ENTER_MANUAL,
DEBUGGER_ENTER_ATTACHED,
DEBUGGER_ENTER_BREAKPOINT,
DEBUGGER_ENTER_WATCHPOINT,
DEBUGGER_ENTER_ILLEGAL_OP
};
enum DebuggerLogLevel {
DEBUGGER_LOG_DEBUG = 0x01,
DEBUGGER_LOG_INFO = 0x02,
DEBUGGER_LOG_WARN = 0x04,
DEBUGGER_LOG_ERROR = 0x08
};
struct ARMDebugger {
enum DebuggerState state;
struct ARMCore* cpu;
EditLine* elstate;
History* histate;
struct DebugBreakpoint* breakpoints;
struct DebugMemoryShim memoryShim;
void (*init)(struct ARMDebugger*);
void (*deinit)(struct ARMDebugger*);
void (*paused)(struct ARMDebugger*);
void (*entered)(struct ARMDebugger*, enum DebuggerEntryReason);
__attribute__((format (printf, 3, 4)))
void (*log)(struct ARMDebugger*, enum DebuggerLogLevel, const char* format, ...);
};
void ARMDebuggerInit(struct ARMDebugger*, struct ARMCore*);
void ARMDebuggerDeinit(struct ARMDebugger*);
void ARMDebuggerRun(struct ARMDebugger*);
void ARMDebuggerEnter(struct ARMDebugger*);
#else
struct ARMDebugger {
enum DebuggerState state;
};
#endif
void ARMDebuggerEnter(struct ARMDebugger*, enum DebuggerEntryReason);
void ARMDebuggerSetBreakpoint(struct ARMDebugger* debugger, uint32_t address);
void ARMDebuggerClearBreakpoint(struct ARMDebugger* debugger, uint32_t address);
void ARMDebuggerSetWatchpoint(struct ARMDebugger* debugger, uint32_t address);
#endif

551
src/debugger/gdb-stub.c Normal file
View File

@ -0,0 +1,551 @@
#include "gdb-stub.h"
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
enum GDBError {
GDB_NO_ERROR = 0x00,
GDB_BAD_ARGUMENTS = 0x06,
GDB_UNSUPPORTED_COMMAND = 0x07
};
enum {
MACH_O_ARM = 12,
MACH_O_ARM_V4T = 5
};
static void _sendMessage(struct GDBStub* stub);
static void _gdbStubDeinit(struct ARMDebugger* debugger) {
struct GDBStub* stub = (struct GDBStub*) debugger;
if (stub->socket >= 0) {
GDBStubShutdown(stub);
}
}
static void _gdbStubEntered(struct ARMDebugger* debugger, enum DebuggerEntryReason reason) {
struct GDBStub* stub = (struct GDBStub*) debugger;
switch (reason) {
case DEBUGGER_ENTER_MANUAL:
snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGINT);
break;
case DEBUGGER_ENTER_BREAKPOINT:
case DEBUGGER_ENTER_WATCHPOINT: // TODO: Make watchpoints raise with address
snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGTRAP);
break;
case DEBUGGER_ENTER_ILLEGAL_OP:
snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGILL);
break;
case DEBUGGER_ENTER_ATTACHED:
return;
}
_sendMessage(stub);
}
static void _gdbStubPoll(struct ARMDebugger* debugger) {
struct GDBStub* stub = (struct GDBStub*) debugger;
int flags;
while (stub->d.state == DEBUGGER_PAUSED) {
if (stub->connection >= 0) {
flags = fcntl(stub->connection, F_GETFL);
if (flags == -1) {
GDBStubHangup(stub);
return;
}
fcntl(stub->connection, F_SETFL, flags & ~O_NONBLOCK);
}
GDBStubUpdate(stub);
}
}
static void _ack(struct GDBStub* stub) {
char ack = '+';
send(stub->connection, &ack, 1, 0);
}
static void _nak(struct GDBStub* stub) {
char nak = '-';
if (stub->d.log) {
stub->d.log(&stub->d, DEBUGGER_LOG_WARN, "Packet error");
}
send(stub->connection, &nak, 1, 0);
}
static uint32_t _hex2int(const char* hex, int maxDigits) {
uint32_t value = 0;
uint8_t letter;
while (maxDigits--) {
letter = *hex - '0';
if (letter > 9) {
letter = *hex - 'a';
if (letter > 5) {
break;
}
value *= 0x10;
value += letter + 10;
} else {
value *= 0x10;
value += letter;
}
++hex;
}
return value;
}
static void _int2hex8(uint8_t value, char* out) {
static const char language[] = "0123456789abcdef";
out[0] = language[value >> 4];
out[1] = language[value & 0xF];
}
static void _int2hex32(uint32_t value, char* out) {
static const char language[] = "0123456789abcdef";
out[6] = language[value >> 28];
out[7] = language[(value >> 24) & 0xF];
out[4] = language[(value >> 20) & 0xF];
out[5] = language[(value >> 16) & 0xF];
out[2] = language[(value >> 12) & 0xF];
out[3] = language[(value >> 8) & 0xF];
out[0] = language[(value >> 4) & 0xF];
out[1] = language[value & 0xF];
}
static uint32_t _readHex(const char* in, unsigned* out) {
unsigned i;
for (i = 0; i < 8; ++i) {
if (in[i] == ',') {
break;
}
}
*out += i;
return _hex2int(in, i);
}
static void _sendMessage(struct GDBStub* stub) {
if (stub->lineAck != GDB_ACK_OFF) {
stub->lineAck = GDB_ACK_PENDING;
}
uint8_t checksum = 0;
int i = 1;
char buffer = stub->outgoing[0];
char swap;
stub->outgoing[0] = '$';
if (buffer) {
for (; i < GDB_STUB_MAX_LINE - 5; ++i) {
checksum += buffer;
swap = stub->outgoing[i];
stub->outgoing[i] = buffer;
buffer = swap;
if (!buffer) {
++i;
break;
}
}
}
stub->outgoing[i] = '#';
_int2hex8(checksum, &stub->outgoing[i + 1]);
stub->outgoing[i + 3] = 0;
if (stub->d.log) {
stub->d.log(&stub->d, DEBUGGER_LOG_DEBUG, "> %s", stub->outgoing);
}
send(stub->connection, stub->outgoing, i + 3, 0);
}
static void _error(struct GDBStub* stub, enum GDBError error) {
snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "E%02x", error);
_sendMessage(stub);
}
static void _writeHostInfo(struct GDBStub* stub) {
snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "cputype:%u;cpusubtype:%u:ostype:none;vendor:none;endian:little;ptrsize:4;", MACH_O_ARM, MACH_O_ARM_V4T);
_sendMessage(stub);
}
static void _continue(struct GDBStub* stub, const char* message) {
stub->d.state = DEBUGGER_RUNNING;
if (stub->connection >= 0) {
int flags = fcntl(stub->connection, F_GETFL);
if (flags == -1) {
GDBStubHangup(stub);
return;
}
fcntl(stub->connection, F_SETFL, flags | O_NONBLOCK);
}
// TODO: parse message
(void) (message);
}
static void _step(struct GDBStub* stub, const char* message) {
ARMRun(stub->d.cpu);
snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGINT);
_sendMessage(stub);
// TODO: parse message
(void) (message);
}
static void _readMemory(struct GDBStub* stub, const char* message) {
const char* readAddress = message;
unsigned i = 0;
uint32_t address = _readHex(readAddress, &i);
readAddress += i + 1;
uint32_t size = _readHex(readAddress, &i);
if (size > 512) {
_error(stub, GDB_BAD_ARGUMENTS);
return;
}
struct ARMMemory* memory = stub->d.memoryShim.original;
int writeAddress = 0;
for (i = 0; i < size; ++i, writeAddress += 2) {
uint8_t byte = memory->load8(memory, address + i, 0);
_int2hex8(byte, &stub->outgoing[writeAddress]);
}
stub->outgoing[writeAddress] = 0;
_sendMessage(stub);
}
static void _readGPRs(struct GDBStub* stub, const char* message) {
(void) (message);
int r;
int i = 0;
for (r = 0; r < 16; ++r) {
_int2hex32(stub->d.cpu->gprs[r], &stub->outgoing[i]);
i += 8;
}
stub->outgoing[i] = 0;
_sendMessage(stub);
}
static void _readRegister(struct GDBStub* stub, const char* message) {
const char* readAddress = message;
unsigned i = 0;
uint32_t reg = _readHex(readAddress, &i);
uint32_t value;
if (reg < 0x10) {
value = stub->d.cpu->gprs[reg];
} else if (reg == 0x19) {
value = stub->d.cpu->cpsr.packed;
} else {
stub->outgoing[0] = '\0';
_sendMessage(stub);
return;
}
_int2hex32(value, stub->outgoing);
stub->outgoing[8] = '\0';
_sendMessage(stub);
}
static void _processQReadCommand(struct GDBStub* stub, const char* message) {
stub->outgoing[0] = '\0';
if (!strncmp("HostInfo#", message, 9)) {
_writeHostInfo(stub);
return;
}
if (!strncmp("Attached#", message, 9)) {
strncpy(stub->outgoing, "1", GDB_STUB_MAX_LINE - 4);
} else if (!strncmp("VAttachOrWaitSupported#", message, 23)) {
strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
} else if (!strncmp("C#", message, 2)) {
strncpy(stub->outgoing, "QC1", GDB_STUB_MAX_LINE - 4);
} else if (!strncmp("fThreadInfo#", message, 12)) {
strncpy(stub->outgoing, "m1", GDB_STUB_MAX_LINE - 4);
} else if (!strncmp("sThreadInfo#", message, 12)) {
strncpy(stub->outgoing, "l", GDB_STUB_MAX_LINE - 4);
}
_sendMessage(stub);
}
static void _processQWriteCommand(struct GDBStub* stub, const char* message) {
stub->outgoing[0] = '\0';
if (!strncmp("StartNoAckMode#", message, 16)) {
stub->lineAck = GDB_ACK_OFF;
strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
}
_sendMessage(stub);
}
static void _processVWriteCommand(struct GDBStub* stub, const char* message) {
(void) (message);
stub->outgoing[0] = '\0';
_sendMessage(stub);
}
static void _processVReadCommand(struct GDBStub* stub, const char* message) {
stub->outgoing[0] = '\0';
if (!strncmp("Attach", message, 6)) {
strncpy(stub->outgoing, "1", GDB_STUB_MAX_LINE - 4);
ARMDebuggerEnter(&stub->d, DEBUGGER_ENTER_MANUAL);
}
_sendMessage(stub);
}
static void _setBreakpoint(struct GDBStub* stub, const char* message) {
switch (message[0]) {
case '0': // Memory breakpoints are not currently supported
case '1': {
const char* readAddress = &message[2];
unsigned i = 0;
uint32_t address = _readHex(readAddress, &i);
readAddress += i + 1;
uint32_t kind = _readHex(readAddress, &i); // We don't use this in hardware watchpoints
(void) (kind);
ARMDebuggerSetBreakpoint(&stub->d, address);
strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
_sendMessage(stub);
break;
}
case '2':
case '3':
// TODO: Watchpoints
default:
break;
}
}
static void _clearBreakpoint(struct GDBStub* stub, const char* message) {
switch (message[0]) {
case '0': // Memory breakpoints are not currently supported
case '1': {
const char* readAddress = &message[2];
unsigned i = 0;
uint32_t address = _readHex(readAddress, &i);
ARMDebuggerClearBreakpoint(&stub->d, address);
strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
_sendMessage(stub);
break;
}
case '2':
case '3':
// TODO: Watchpoints
default:
break;
}
}
size_t _parseGDBMessage(struct GDBStub* stub, const char* message) {
uint8_t checksum = 0;
int parsed = 1;
switch (*message) {
case '+':
stub->lineAck = GDB_ACK_RECEIVED;
return parsed;
case '-':
stub->lineAck = GDB_NAK_RECEIVED;
return parsed;
case '$':
++message;
break;
case '\x03':
ARMDebuggerEnter(&stub->d, DEBUGGER_ENTER_MANUAL);
return parsed;
default:
_nak(stub);
return parsed;
}
int i;
char messageType = message[0];
for (i = 0; message[i] && message[i] != '#'; ++i, ++parsed) {
checksum += message[i];
}
if (!message[i]) {
_nak(stub);
return parsed;
}
++i;
++parsed;
if (!message[i]) {
_nak(stub);
return parsed;
} else if (!message[i + 1]) {
++parsed;
_nak(stub);
return parsed;
}
parsed += 2;
int networkChecksum = _hex2int(&message[i], 2);
if (networkChecksum != checksum) {
if (stub->d.log) {
stub->d.log(&stub->d, DEBUGGER_LOG_WARN, "Checksum error: expected %02x, got %02x", checksum, networkChecksum);
}
_nak(stub);
return parsed;
}
_ack(stub);
++message;
switch (messageType) {
case '?':
snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGINT);
_sendMessage(stub);
break;
case 'c':
_continue(stub, message);
break;
case 'g':
_readGPRs(stub, message);
break;
case 'H':
// This is faked because we only have one thread
strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
_sendMessage(stub);
break;
case 'm':
_readMemory(stub, message);
break;
case 'p':
_readRegister(stub, message);
break;
case 'Q':
_processQWriteCommand(stub, message);
break;
case 'q':
_processQReadCommand(stub, message);
break;
case 's':
_step(stub, message);
break;
case 'V':
_processVWriteCommand(stub, message);
break;
case 'v':
_processVReadCommand(stub, message);
break;
case 'Z':
_setBreakpoint(stub, message);
break;
case 'z':
_clearBreakpoint(stub, message);
break;
default:
_error(stub, GDB_UNSUPPORTED_COMMAND);
break;
}
return parsed;
}
void GDBStubCreate(struct GDBStub* stub) {
stub->socket = -1;
stub->connection = -1;
stub->d.init = 0;
stub->d.deinit = _gdbStubDeinit;
stub->d.paused = _gdbStubPoll;
stub->d.entered = _gdbStubEntered;
stub->d.log = 0;
}
int GDBStubListen(struct GDBStub* stub, int port, uint32_t bindAddress) {
if (stub->socket >= 0) {
GDBStubShutdown(stub);
}
// TODO: support IPv6
stub->socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (stub->socket < 0) {
if (stub->d.log) {
stub->d.log(&stub->d, DEBUGGER_LOG_ERROR, "Couldn't open socket");
}
return 0;
}
struct sockaddr_in bindInfo = {
.sin_family = AF_INET,
.sin_port = htons(port),
.sin_addr = {
.s_addr = htonl(bindAddress)
}
};
int err = bind(stub->socket, (const struct sockaddr*) &bindInfo, sizeof(struct sockaddr_in));
if (err) {
goto cleanup;
}
err = listen(stub->socket, 1);
if (err) {
goto cleanup;
}
int flags = fcntl(stub->socket, F_GETFL);
if (flags == -1) {
goto cleanup;
}
fcntl(stub->socket, F_SETFL, flags | O_NONBLOCK);
return 1;
cleanup:
if (stub->d.log) {
stub->d.log(&stub->d, DEBUGGER_LOG_ERROR, "Couldn't listen on port");
}
close(stub->socket);
stub->socket = -1;
return 0;
}
void GDBStubHangup(struct GDBStub* stub) {
if (stub->connection >= 0) {
close(stub->connection);
stub->connection = -1;
}
if (stub->d.state == DEBUGGER_PAUSED) {
stub->d.state = DEBUGGER_RUNNING;
}
}
void GDBStubShutdown(struct GDBStub* stub) {
GDBStubHangup(stub);
if (stub->socket >= 0) {
close(stub->socket);
stub->socket = -1;
}
}
void GDBStubUpdate(struct GDBStub* stub) {
if (stub->socket == -1) {
return;
}
if (stub->connection == -1) {
stub->connection = accept(stub->socket, 0, 0);
if (stub->connection >= 0) {
int flags = fcntl(stub->connection, F_GETFL);
if (flags == -1) {
goto connectionLost;
}
fcntl(stub->connection, F_SETFL, flags | O_NONBLOCK);
ARMDebuggerEnter(&stub->d, DEBUGGER_ENTER_ATTACHED);
} else if (errno == EWOULDBLOCK || errno == EAGAIN) {
return;
} else {
goto connectionLost;
}
}
while (1) {
ssize_t messageLen = recv(stub->connection, stub->line, GDB_STUB_MAX_LINE - 1, 0);
if (messageLen == 0) {
goto connectionLost;
}
if (messageLen == -1) {
if (errno == EWOULDBLOCK || errno == EAGAIN) {
return;
}
goto connectionLost;
}
stub->line[messageLen] = '\0';
if (stub->d.log) {
stub->d.log(&stub->d, DEBUGGER_LOG_DEBUG, "< %s", stub->line);
}
ssize_t position = 0;
while (position < messageLen) {
position += _parseGDBMessage(stub, &stub->line[position]);
}
}
connectionLost:
if (stub->d.log) {
stub->d.log(&stub->d, DEBUGGER_LOG_INFO, "Connection lost");
}
GDBStubHangup(stub);
}

34
src/debugger/gdb-stub.h Normal file
View File

@ -0,0 +1,34 @@
#ifndef GDB_STUB_H
#define GDB_STUB_H
#include "debugger.h"
#define GDB_STUB_MAX_LINE 1200
enum GDBStubAckState {
GDB_ACK_PENDING = 0,
GDB_ACK_RECEIVED,
GDB_NAK_RECEIVED,
GDB_ACK_OFF
};
struct GDBStub {
struct ARMDebugger d;
char line[GDB_STUB_MAX_LINE];
char outgoing[GDB_STUB_MAX_LINE];
enum GDBStubAckState lineAck;
int socket;
int connection;
};
void GDBStubCreate(struct GDBStub*);
int GDBStubListen(struct GDBStub*, int port, uint32_t bindAddress);
void GDBStubHangup(struct GDBStub*);
void GDBStubShutdown(struct GDBStub*);
void GDBStubUpdate(struct GDBStub*);
#endif

View File

@ -48,35 +48,10 @@ void ARMDebuggerInstallMemoryShim(struct ARMDebugger* debugger) {
debugger->cpu->memory = &debugger->memoryShim.d;
}
int32_t ARMDebuggerLoad32(struct ARMMemory* memory, uint32_t address, int* cycleCounter) {
struct DebugMemoryShim* debugMemory = (struct DebugMemoryShim*) memory;
return debugMemory->original->load32(debugMemory->original, address, cycleCounter);
}
int16_t ARMDebuggerLoad16(struct ARMMemory* memory, uint32_t address, int* cycleCounter) {
struct DebugMemoryShim* debugMemory = (struct DebugMemoryShim*) memory;
return debugMemory->original->load16(debugMemory->original, address, cycleCounter);
}
uint16_t ARMDebuggerLoadU16(struct ARMMemory* memory, uint32_t address, int* cycleCounter) {
struct DebugMemoryShim* debugMemory = (struct DebugMemoryShim*) memory;
return debugMemory->original->loadU16(debugMemory->original, address, cycleCounter);
}
int8_t ARMDebuggerLoad8(struct ARMMemory* memory, uint32_t address, int* cycleCounter) {
struct DebugMemoryShim* debugMemory = (struct DebugMemoryShim*) memory;
return debugMemory->original->load8(debugMemory->original, address, cycleCounter);
}
uint8_t ARMDebuggerLoadU8(struct ARMMemory* memory, uint32_t address, int* cycleCounter) {
struct DebugMemoryShim* debugMemory = (struct DebugMemoryShim*) memory;
return debugMemory->original->loadU8(debugMemory->original, address, cycleCounter);
}
void ARMDebuggerShim_store32(struct ARMMemory* memory, uint32_t address, int32_t value, int* cycleCounter) {
struct DebugMemoryShim* debugMemory = (struct DebugMemoryShim*) memory;
if (_checkWatchpoints(debugMemory->watchpoints, address, 4)) {
ARMDebuggerEnter(debugMemory->p);
ARMDebuggerEnter(debugMemory->p, DEBUGGER_ENTER_WATCHPOINT);
}
debugMemory->original->store32(debugMemory->original, address, value, cycleCounter);
}
@ -84,7 +59,7 @@ void ARMDebuggerShim_store32(struct ARMMemory* memory, uint32_t address, int32_t
void ARMDebuggerShim_store16(struct ARMMemory* memory, uint32_t address, int16_t value, int* cycleCounter) {
struct DebugMemoryShim* debugMemory = (struct DebugMemoryShim*) memory;
if (_checkWatchpoints(debugMemory->watchpoints, address, 2)) {
ARMDebuggerEnter(debugMemory->p);
ARMDebuggerEnter(debugMemory->p, DEBUGGER_ENTER_WATCHPOINT);
}
debugMemory->original->store16(debugMemory->original, address, value, cycleCounter);
}
@ -92,7 +67,7 @@ void ARMDebuggerShim_store16(struct ARMMemory* memory, uint32_t address, int16_t
void ARMDebuggerShim_store8(struct ARMMemory* memory, uint32_t address, int8_t value, int* cycleCounter) {
struct DebugMemoryShim* debugMemory = (struct DebugMemoryShim*) memory;
if (_checkWatchpoints(debugMemory->watchpoints, address, 1)) {
ARMDebuggerEnter(debugMemory->p);
ARMDebuggerEnter(debugMemory->p, DEBUGGER_ENTER_WATCHPOINT);
}
debugMemory->original->store8(debugMemory->original, address, value, cycleCounter);
}

View File

@ -44,9 +44,6 @@ static THREAD_ENTRY _GBAThreadRun(void* context) {
InitOnceExecuteOnce(&_contextOnce, _createTLS, NULL, 0);
#endif
#ifdef USE_DEBUGGER
struct ARMDebugger debugger;
#endif
struct GBA gba;
struct GBAThread* threadContext = context;
char* savedata = 0;
@ -97,16 +94,9 @@ static THREAD_ENTRY _GBAThreadRun(void* context) {
}
}
#ifdef USE_DEBUGGER
if (threadContext->useDebugger) {
threadContext->debugger = &debugger;
GBAAttachDebugger(&gba, &debugger);
} else {
threadContext->debugger = 0;
if (threadContext->debugger) {
GBAAttachDebugger(&gba, threadContext->debugger);
}
#else
threadContext->debugger = 0;
#endif
gba.keySource = &threadContext->activeKeys;
@ -117,20 +107,16 @@ static THREAD_ENTRY _GBAThreadRun(void* context) {
_changeState(threadContext, THREAD_RUNNING, 1);
while (threadContext->state < THREAD_EXITING) {
#ifdef USE_DEBUGGER
if (threadContext->useDebugger) {
ARMDebuggerRun(&debugger);
if (debugger.state == DEBUGGER_SHUTDOWN) {
if (threadContext->debugger) {
ARMDebuggerRun(threadContext->debugger);
if (threadContext->debugger->state == DEBUGGER_SHUTDOWN) {
_changeState(threadContext, THREAD_EXITING, 0);
}
} else {
#endif
while (threadContext->state == THREAD_RUNNING) {
ARMRun(&gba.cpu);
}
#ifdef USE_DEBUGGER
}
#endif
MutexLock(&threadContext->stateMutex);
while (threadContext->state == THREAD_PAUSED) {
ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);

View File

@ -32,12 +32,11 @@ struct GBASync {
struct GBAThread {
// Output
enum ThreadState state;
int useDebugger;
struct GBA* gba;
struct ARMDebugger* debugger;
// Input
struct GBAVideoRenderer* renderer;
struct ARMDebugger* debugger;
int fd;
int biosFd;
const char* fname;

View File

@ -341,12 +341,10 @@ static int32_t GBATimersProcessEvents(struct GBA* gba, int32_t cycles) {
return nextEvent;
}
#ifdef USE_DEBUGGER
void GBAAttachDebugger(struct GBA* gba, struct ARMDebugger* debugger) {
ARMDebuggerInit(debugger, &gba->cpu);
gba->debugger = debugger;
}
#endif
void GBALoadROM(struct GBA* gba, int fd, const char* fname) {
struct stat info;
@ -505,7 +503,7 @@ int GBAHalt(struct GBA* gba) {
return GBAWaitForIRQ(gba);
}
void GBALog(struct GBA* gba, enum GBALogLevel level, const char* format, ...) {
static void _GBAVLog(struct GBA* gba, enum GBALogLevel level, const char* format, va_list args) {
if (!gba) {
struct GBAThread* threadContext = GBAThreadGetContext();
if (threadContext) {
@ -514,10 +512,7 @@ void GBALog(struct GBA* gba, enum GBALogLevel level, const char* format, ...) {
}
if (gba && gba->logHandler) {
va_list args;
va_start(args, format);
gba->logHandler(gba, level, format, args);
va_end(args);
return;
}
@ -525,10 +520,7 @@ void GBALog(struct GBA* gba, enum GBALogLevel level, const char* format, ...) {
return;
}
va_list args;
va_start(args, format);
vprintf(format, args);
va_end(args);
printf("\n");
if (level == GBA_LOG_FATAL) {
@ -536,32 +528,64 @@ void GBALog(struct GBA* gba, enum GBALogLevel level, const char* format, ...) {
}
}
void GBALog(struct GBA* gba, enum GBALogLevel level, const char* format, ...) {
va_list args;
va_start(args, format);
_GBAVLog(gba, level, format, args);
va_end(args);
}
void GBADebuggerLogShim(struct ARMDebugger* debugger, enum DebuggerLogLevel level, const char* format, ...) {
struct GBABoard* gbaBoard = 0;
if (debugger->cpu && debugger->cpu->board) {
gbaBoard = (struct GBABoard*) debugger->cpu->board;
}
enum GBALogLevel gbaLevel;
switch (level) {
case DEBUGGER_LOG_DEBUG:
gbaLevel = GBA_LOG_DEBUG;
break;
case DEBUGGER_LOG_INFO:
gbaLevel = GBA_LOG_INFO;
break;
case DEBUGGER_LOG_WARN:
gbaLevel = GBA_LOG_WARN;
break;
case DEBUGGER_LOG_ERROR:
gbaLevel = GBA_LOG_ERROR;
break;
}
va_list args;
va_start(args, format);
_GBAVLog(gbaBoard ? gbaBoard->p : 0, gbaLevel, format, args);
va_end(args);
}
void GBAHitStub(struct ARMBoard* board, uint32_t opcode) {
struct GBABoard* gbaBoard = (struct GBABoard*) board;
enum GBALogLevel level = GBA_LOG_FATAL;
#ifdef USE_DEBUGGER
if (gbaBoard->p->debugger) {
level = GBA_LOG_STUB;
ARMDebuggerEnter(gbaBoard->p->debugger);
ARMDebuggerEnter(gbaBoard->p->debugger, DEBUGGER_ENTER_ILLEGAL_OP);
}
#endif
GBALog(gbaBoard->p, level, "Stub opcode: %08x", opcode);
}
void GBAIllegal(struct ARMBoard* board, uint32_t opcode) {
struct GBABoard* gbaBoard = (struct GBABoard*) board;
GBALog(gbaBoard->p, GBA_LOG_WARN, "Illegal opcode: %08x", opcode);
#ifdef USE_DEBUGGER
if (gbaBoard->p->debugger) {
ARMDebuggerEnter(gbaBoard->p->debugger);
ARMDebuggerEnter(gbaBoard->p->debugger, DEBUGGER_ENTER_ILLEGAL_OP);
}
#endif
}
void _checkOverrides(struct GBA* gba, uint32_t id) {
int i;
for (i = 0; _overrides[i].id[0]; ++i) {
if (*(uint32_t*) &_overrides[i].id == id) {
const uint32_t* overrideId = (const uint32_t*) _overrides[i].id;
if (*overrideId == id) {
switch (_overrides[i].type) {
case SAVEDATA_FLASH512:
case SAVEDATA_FLASH1M:

View File

@ -2,6 +2,7 @@
#define GBA_H
#include "arm.h"
#include "debugger.h"
#include "gba-memory.h"
#include "gba-video.h"
@ -147,4 +148,7 @@ void GBALoadBIOS(struct GBA* gba, int fd);
__attribute__((format (printf, 3, 4)))
void GBALog(struct GBA* gba, enum GBALogLevel level, const char* format, ...);
__attribute__((format (printf, 3, 4)))
void GBADebuggerLogShim(struct ARMDebugger* debugger, enum DebuggerLogLevel level, const char* format, ...);
#endif

View File

@ -378,10 +378,12 @@ static void _cleanOAM(struct GBAVideoSoftwareRenderer* renderer) {
if (obj->transformed) {
height <<= ((struct GBATransformedObj*) obj)->doublesize;
}
renderer->sprites[oamMax].y = obj->y;
renderer->sprites[oamMax].endY = obj->y + height;
renderer->sprites[oamMax].obj = *obj;
++oamMax;
if (obj->y < VIDEO_VERTICAL_PIXELS || obj->y + height >= VIDEO_VERTICAL_TOTAL_PIXELS) {
renderer->sprites[oamMax].y = obj->y;
renderer->sprites[oamMax].endY = obj->y + height;
renderer->sprites[oamMax].obj = *obj;
++oamMax;
}
}
}
renderer->oamMax = oamMax;
@ -1104,6 +1106,7 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re
int x; \
int mosaicWait = outX % mosaicH; \
int carryData = 0; \
paletteData = 0; /* Quiets compiler warning */ \
DRAW_BACKGROUND_MODE_0_MOSAIC_ ## BPP (BLEND, OBJWIN) \
return; \
} \
@ -1542,11 +1545,27 @@ static void _postprocessSprite(struct GBAVideoSoftwareRenderer* renderer, unsign
int x;
uint32_t* pixel = renderer->row;
uint32_t flags = FLAG_TARGET_2 * renderer->target2Obj;
for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x, ++pixel) {
uint32_t color = renderer->spriteLayer[x] & ~FLAG_OBJWIN;
uint32_t current = *pixel;
if ((color & FLAG_UNWRITTEN) != FLAG_UNWRITTEN && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority) {
_compositeBlendObjwin(renderer, pixel, color | flags, current);
int objwinSlowPath = renderer->dispcnt.objwinEnable;
int objwinDisable = 0;
if (objwinSlowPath) {
objwinDisable = !renderer->objwin.objEnable;
}
if (objwinSlowPath && objwinDisable) {
for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x, ++pixel) {
uint32_t color = renderer->spriteLayer[x] & ~FLAG_OBJWIN;
uint32_t current = *pixel;
if ((color & FLAG_UNWRITTEN) != FLAG_UNWRITTEN && !(current & FLAG_OBJWIN) && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority) {
_compositeBlendObjwin(renderer, pixel, color | flags, current);
}
}
} else {
for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x, ++pixel) {
uint32_t color = renderer->spriteLayer[x] & ~FLAG_OBJWIN;
uint32_t current = *pixel;
if ((color & FLAG_UNWRITTEN) != FLAG_UNWRITTEN && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority) {
_compositeBlendNoObjwin(renderer, pixel, color | flags, current);
}
}
}
}

View File

@ -36,7 +36,6 @@ int main(int argc, char** argv) {
.fd = fd,
.fname = fname,
.biosFd = -1,
.useDebugger = 0,
.renderer = &renderer.d,
.frameskip = 0,
.sync.videoFrameWait = 0,
@ -87,6 +86,7 @@ static void _GBAPerfRunloop(struct GBAThread* context, int* frames) {
}
static void _GBAPerfShutdown(int signal) {
(void) (signal);
pthread_mutex_lock(&_thread->stateMutex);
_thread->state = THREAD_EXITING;
pthread_mutex_unlock(&_thread->stateMutex);

View File

@ -17,7 +17,7 @@ GameController::GameController(QObject* parent)
m_renderer->outputBuffer = (color_t*) m_drawContext;
m_renderer->outputBufferStride = 256;
m_threadContext = {
.useDebugger = 0,
.debugger = 0,
.frameskip = 0,
.biosFd = -1,
.renderer = &m_renderer->d,

View File

@ -1,4 +1,4 @@
#include "debugger.h"
#include "cli-debugger.h"
#include "gba-thread.h"
#include "gba.h"
#include "sdl-audio.h"
@ -68,11 +68,13 @@ int main(int argc, char** argv) {
return 1;
}
struct CLIDebugger debugger;
CLIDebuggerCreate(&debugger);
struct GBAThread context = {
.fd = fd,
.biosFd = -1,
.fname = fname,
.useDebugger = 1,
.debugger = &debugger.d,
.renderer = &renderer.d.d,
.frameskip = 0,
.sync.videoFrameWait = 0,

View File

@ -59,13 +59,11 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, const struct SDL_Ke
case SDLK_RIGHT:
key = GBA_KEY_RIGHT;
break;
#ifdef USE_DEBUGGER
case SDLK_F11:
if (event->type == SDL_KEYDOWN && context->debugger) {
ARMDebuggerEnter(context->debugger);
ARMDebuggerEnter(context->debugger, DEBUGGER_ENTER_MANUAL);
}
break;
#endif
case SDLK_TAB:
context->sync.audioWait = event->type != SDL_KEYDOWN;
return;