mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'master' into qt
Conflicts: CMakeLists.txt src/gba/gba.c
This commit is contained in:
commit
11e3bdc585
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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 "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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue