all repos — mgba @ 11e3bdc58577540dfe888b68c8b4249ed16e025c

mGBA Game Boy Advance Emulator

Merge branch 'master' into qt

Conflicts:
	CMakeLists.txt
	src/gba/gba.c
Jeffrey Pfau jeffrey@endrift.com
Sat, 01 Feb 2014 17:37:18 -0800
commit

11e3bdc58577540dfe888b68c8b4249ed16e025c

parent

4eb705ca9187bf0115d852640ed37ff2d567adce

M CMakeLists.txtCMakeLists.txt

@@ -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 @@ file(GLOB OS_SRC ${CMAKE_SOURCE_DIR}/src/platform/windows/*.c)

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

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)
A src/debugger/cli-debugger.c

@@ -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; +}
A src/debugger/cli-debugger.h

@@ -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
M src/debugger/debugger.csrc/debugger/debugger.c

@@ -1,235 +1,10 @@

#include "debugger.h" -#include "memory-debugger.h" - #include "arm.h" -#include <signal.h> -#include <stdio.h> -#include <stdarg.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; -} +#include "memory-debugger.h" -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; -} +#include <stdlib.h> static void _checkBreakpoints(struct ARMDebugger* debugger) { struct DebugBreakpoint* breakpoint;

@@ -241,375 +16,28 @@ } else {

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 @@ switch (debugger->state) {

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 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; }
M src/debugger/debugger.hsrc/debugger/debugger.h

@@ -1,11 +1,7 @@

#ifndef DEBUGGER_H #define DEBUGGER_H -#ifdef USE_DEBUGGER -#include <histedit.h> - #include "arm.h" -#endif enum DebuggerState { DEBUGGER_PAUSED,

@@ -13,12 +9,10 @@ DEBUGGER_RUNNING,

DEBUGGER_EXITING, DEBUGGER_SHUTDOWN }; - -#ifdef USE_DEBUGGER struct DebugBreakpoint { struct DebugBreakpoint* next; - int32_t address; + uint32_t address; }; struct DebugMemoryShim {

@@ -29,28 +23,43 @@ struct ARMDebugger* p;

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
A src/debugger/gdb-stub.c

@@ -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); +}
A src/debugger/gdb-stub.h

@@ -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
M src/debugger/memory-debugger.csrc/debugger/memory-debugger.c

@@ -48,35 +48,10 @@ debugger->memoryShim.d.waitMultiple = ARMDebuggerShim_waitMultiple;

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_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_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); }
M src/gba/gba-thread.csrc/gba/gba-thread.c

@@ -44,9 +44,6 @@ #else

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 @@ GBALoadBIOS(&gba, threadContext->biosFd);

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

_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);
M src/gba/gba-thread.hsrc/gba/gba-thread.h

@@ -32,12 +32,11 @@

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;
M src/gba/gba.csrc/gba/gba.c

@@ -341,12 +341,10 @@ }

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

} 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 @@ if (gba && !(level & gba->logLevel) && level != GBA_LOG_FATAL) {

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 @@ abort();

} } +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:
M src/gba/gba.hsrc/gba/gba.h

@@ -2,6 +2,7 @@ #ifndef GBA_H

#define GBA_H #include "arm.h" +#include "debugger.h" #include "gba-memory.h" #include "gba-video.h"

@@ -146,5 +147,8 @@ 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
M src/gba/renderers/video-software.csrc/gba/renderers/video-software.c

@@ -378,10 +378,12 @@ int height = _objSizes[obj->shape * 8 + obj->size * 2 + 1];

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 @@ int mosaicH = renderer->mosaic.bgH + 1; \

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, unsigned priority) {

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); + } } } }
M src/platform/perf-main.csrc/platform/perf-main.c

@@ -36,7 +36,6 @@ struct GBAThread context = {

.fd = fd, .fname = fname, .biosFd = -1, - .useDebugger = 0, .renderer = &renderer.d, .frameskip = 0, .sync.videoFrameWait = 0,

@@ -87,6 +86,7 @@ }

} static void _GBAPerfShutdown(int signal) { + (void) (signal); pthread_mutex_lock(&_thread->stateMutex); _thread->state = THREAD_EXITING; pthread_mutex_unlock(&_thread->stateMutex);
M src/platform/qt/GameController.cppsrc/platform/qt/GameController.cpp

@@ -17,7 +17,7 @@ GBAVideoSoftwareRendererCreate(m_renderer);

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,
M src/platform/sdl/gl-main.csrc/platform/sdl/gl-main.c

@@ -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 @@ if (!_GBASDLInit(&renderer)) {

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,
M src/platform/sdl/sdl-events.csrc/platform/sdl/sdl-events.c

@@ -59,13 +59,11 @@ break;

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;