all repos — mgba @ 6560db2ef58e1a192a2356fd8ae1de65379b838c

mGBA Game Boy Advance Emulator

Merge branch 'master' into qt

Conflicts:
	CMakeLists.txt
Jeffrey Pfau jeffrey@endrift.com
Tue, 07 Oct 2014 02:48:38 -0700
commit

6560db2ef58e1a192a2356fd8ae1de65379b838c

parent

94c077703d9b56680f6bcaede01734b6a0ed1760

M CMakeLists.txtCMakeLists.txt

@@ -6,9 +6,11 @@ set(CMAKE_C_FLAGS_RELEASE "-O3 -Wall -Wextra -std=gnu99")

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(USE_FFMPEG ON CACHE BOOL "Whether or not to enable FFmpeg support") +set(USE_PNG ON CACHE BOOL "Whether or not to enable PNG support") +set(USE_LIBZIP ON CACHE BOOL "Whether or not to enable ZIP support") 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") +set(BUILD_PERF OFF CACHE BOOL "Build performance profiling tool") file(GLOB ARM_SRC ${CMAKE_SOURCE_DIR}/src/arm/*.c) file(GLOB GBA_SRC ${CMAKE_SOURCE_DIR}/src/gba/*.c) file(GLOB UTIL_SRC ${CMAKE_SOURCE_DIR}/src/util/*.[cS])

@@ -22,12 +24,28 @@ include_directories(${CMAKE_SOURCE_DIR}/src/arm)

include_directories(${CMAKE_SOURCE_DIR}/src/gba) include_directories(${CMAKE_SOURCE_DIR}/src) +# Function definitions +include(FindPkgConfig) +function(find_feature FEATURE_NAME FEATURE_REQUIRES) + foreach(REQUIRE ${FEATURE_REQUIRES}) + find_package(${REQUIRE} QUIET) + pkg_search_module(${REQUIRE} ${REQUIRE}) + if (NOT ${REQUIRE}_FOUND) + message(WARNING "Requested module ${REQUIRE} missing for feature ${FEATURE_NAME}. Feature disabled.") + set(${FEATURE_NAME} OFF PARENT_SCOPE) + return() + endif() + endforeach() +endfunction() + +# Version information set(LIB_VERSION_MAJOR 0) set(LIB_VERSION_MINOR 1) set(LIB_VERSION_PATCH 0) set(LIB_VERSION_ABI 0.1) set(LIB_VERSION_STRING ${LIB_VERSION_MAJOR}.${LIB_VERSION_MINOR}.${LIB_VERSION_PATCH}) +# Advanced settings set(BUILD_PGO CACHE BOOL "Build with profiling-guided optimization") set(PGO_STAGE_2 CACHE BOOL "Rebuild for profiling-guided optimization after profiles have been generated") set(PGO_DIR "/tmp/gba-pgo/" CACHE PATH "Profiling-guided optimization profiles path")

@@ -47,16 +65,13 @@ endif()

add_definitions(-DBINARY_NAME="${BINARY_NAME}" -DPROJECT_NAME="${PROJECT_NAME}") -include(FindPkgConfig) -pkg_search_module(LIBZIP libzip) -if(LIBZIP_FOUND) - include_directories(${LIBZIP_INCLUDE_DIRS}) - list(APPEND DEPENDENCY_LIB ${LIBZIP_LIBRARIES}) - add_definitions(-DENABLE_LIBZIP) -else() - message(WARNING "Could not find libzip for archive support") -endif() +# Feature dependencies +find_feature(USE_CLI_DEBUGGER "libedit") +find_feature(USE_FFMPEG "libavcodec;libavformat;libavutil") +find_feature(USE_PNG "PNG;ZLIB") +find_feature(USE_LIBZIP "libzip") +# Platform support if(WIN32) add_definitions(-D_WIN32_WINNT=0x0600) list(APPEND OS_LIB Ws2_32)

@@ -76,19 +91,14 @@ add_definitions(-DCOLOR_16_BIT -DCOLOR_5_6_5)

endif() endif() +# Features set(DEBUGGER_SRC ${CMAKE_SOURCE_DIR}/src/debugger/debugger.c ${CMAKE_SOURCE_DIR}/src/debugger/memory-debugger.c) if(USE_CLI_DEBUGGER) - pkg_search_module(EDIT libedit) - if(EDIT_FOUND) - add_definitions(-DUSE_CLI_DEBUGGER) - list(APPEND DEBUGGER_SRC ${CMAKE_SOURCE_DIR}/src/debugger/cli-debugger.c) - list(APPEND DEBUGGER_SRC ${CMAKE_SOURCE_DIR}/src/debugger/parser.c) - set(DEBUGGER_LIB ${EDIT_LIBRARIES}) - else() - message(WARNING "Could not find libedit for CLI debugger support") - set(USE_CLI_DEBUGGER OFF) - endif() + add_definitions(-DUSE_CLI_DEBUGGER) + list(APPEND DEBUGGER_SRC ${CMAKE_SOURCE_DIR}/src/debugger/cli-debugger.c) + list(APPEND DEBUGGER_SRC ${CMAKE_SOURCE_DIR}/src/debugger/parser.c) + set(DEBUGGER_LIB ${EDIT_LIBRARIES}) else() set(DEBUGGER_LIB "") endif()

@@ -100,18 +110,29 @@ endif()

source_group("ARM debugger" FILES ${DEBUGGER_SRC}) if(USE_FFMPEG) - pkg_search_module(LIBAVCODEC libavcodec REQUIRED) - pkg_search_module(LIBAVFORMAT libavformat REQUIRED) - pkg_search_module(LIBAVUTIL libavutil REQUIRED) + pkg_search_module(LIBAVCODEC libavcodec) + pkg_search_module(LIBAVFORMAT libavcodec;libavformat;libavutil) + pkg_search_module(LIBAVUTIL libavutil) add_definitions(-DUSE_FFMPEG) list(APPEND UTIL_SRC "${CMAKE_SOURCE_DIR}/src/platform/ffmpeg/ffmpeg-encoder.c") list(APPEND DEPENDENCY_LIB ${LIBAVCODEC_LIBRARIES} ${LIBAVFORMAT_LIBRARIES} ${LIBAVUTIL_LIBRARIES}) endif() -find_package(PNG REQUIRED) -find_package(ZLIB REQUIRED) -list(APPEND DEPENDENCY_LIB ${PNG_LIBRARIES} ${ZLIB_LIBRARIES}) +if(USE_PNG) + find_package(PNG) + find_package(ZLIB) + add_definitions(-DUSE_PNG) + include_directories(${PNG_PNG_INCLUDE_DIR}) + list(APPEND DEPENDENCY_LIB ${PNG_LIBRARIES} ${ZLIB_LIBRARIES}) +endif() + +if(USE_LIBZIP) + include_directories(${LIBZIP_INCLUDE_DIRS}) + list(APPEND DEPENDENCY_LIB ${LIBZIP_LIBRARIES}) + add_definitions(-DENABLE_LIBZIP) +endif() +# Binaries add_library(${BINARY_NAME} SHARED ${ARM_SRC} ${GBA_SRC} ${DEBUGGER_SRC} ${RENDERER_SRC} ${UTIL_SRC} ${VFS_SRC} ${OS_SRC}) target_link_libraries(${BINARY_NAME} m ${DEBUGGER_LIB} ${OS_LIB} ${DEPENDENCY_LIB}) install(TARGETS ${BINARY_NAME} DESTINATION lib)

@@ -122,6 +143,10 @@ add_definitions(-DBUILD_SDL)

add_subdirectory(${CMAKE_SOURCE_DIR}/src/platform/sdl ${CMAKE_BINARY_DIR}/sdl) endif() +if(BUILD_QT) + add_subdirectory(${CMAKE_SOURCE_DIR}/src/platform/qt ${CMAKE_BINARY_DIR}/qt) +endif() + if(BUILD_PERF) set(PERF_SRC ${CMAKE_SOURCE_DIR}/src/platform/perf-main.c) if(UNIX AND NOT APPLE)

@@ -133,6 +158,14 @@ target_link_libraries(${BINARY_NAME}-perf ${BINARY_NAME} ${PERF_LIB})

install(TARGETS ${BINARY_NAME}-perf DESTINATION bin) endif() -if(BUILD_QT) - add_subdirectory(${CMAKE_SOURCE_DIR}/src/platform/qt ${CMAKE_BINARY_DIR}/qt) -endif() +# Summaries +message(STATUS "Feature summary:") +message(STATUS " CLI debugger: ${USE_CLI_DEBUGGER}") +message(STATUS " GDB stub: ${USE_GDB_STUB}") +message(STATUS " Video recording: ${USE_FFMPEG}") +message(STATUS " Screenshot/advanced savestate support: ${USE_PNG}") +message(STATUS " ZIP support: ${USE_LIBZIP}") +message(STATUS "Frontend summary:") +message(STATUS " Qt: ${BUILD_QT}") +message(STATUS " SDL (${SDL_VERSION}): ${BUILD_SDL}") +message(STATUS " Profiling: ${BUILD_PERF}")
M src/arm/arm.csrc/arm/arm.c

@@ -117,7 +117,6 @@

cpu->executionMode = MODE_THUMB; _ARMSetMode(cpu, MODE_ARM); - cpu->currentPC = 0; int currentCycles = 0; ARM_WRITE_PC;

@@ -142,7 +141,9 @@ }

ARMSetPrivilegeMode(cpu, MODE_IRQ); cpu->cpsr.priv = MODE_IRQ; cpu->gprs[ARM_LR] = cpu->gprs[ARM_PC] - instructionWidth + WORD_SIZE_ARM; - cpu->gprs[ARM_PC] = BASE_IRQ + WORD_SIZE_ARM; + cpu->gprs[ARM_PC] = BASE_IRQ; + int currentCycles = 0; + ARM_WRITE_PC; cpu->memory.setActiveRegion(cpu, cpu->gprs[ARM_PC]); _ARMSetMode(cpu, MODE_ARM); cpu->spsr = cpsr;

@@ -160,7 +161,9 @@ }

ARMSetPrivilegeMode(cpu, MODE_SUPERVISOR); cpu->cpsr.priv = MODE_SUPERVISOR; cpu->gprs[ARM_LR] = cpu->gprs[ARM_PC] - instructionWidth; - cpu->gprs[ARM_PC] = BASE_SWI + WORD_SIZE_ARM; + cpu->gprs[ARM_PC] = BASE_SWI; + int currentCycles = 0; + ARM_WRITE_PC; cpu->memory.setActiveRegion(cpu, cpu->gprs[ARM_PC]); _ARMSetMode(cpu, MODE_ARM); cpu->spsr = cpsr;

@@ -168,17 +171,12 @@ cpu->cpsr.i = 1;

} static inline void ARMStep(struct ARMCore* cpu) { - uint32_t opcode; - cpu->currentPC = cpu->gprs[ARM_PC] - WORD_SIZE_ARM; - LOAD_32(opcode, cpu->currentPC & cpu->memory.activeMask, cpu->memory.activeRegion); + uint32_t opcode = cpu->prefetch; + LOAD_32(cpu->prefetch, cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion); cpu->gprs[ARM_PC] += WORD_SIZE_ARM; - int condition = opcode >> 28; - if (condition == 0xE) { - ARMInstruction instruction = _armTable[((opcode >> 16) & 0xFF0) | ((opcode >> 4) & 0x00F)]; - instruction(cpu, opcode); - return; - } else { + unsigned condition = opcode >> 28; + if (condition != 0xE) { switch (condition) { case 0x0: if (!ARM_COND_EQ) {

@@ -273,10 +271,9 @@ instruction(cpu, opcode);

} static inline void ThumbStep(struct ARMCore* cpu) { - cpu->currentPC = cpu->gprs[ARM_PC] - WORD_SIZE_THUMB; + uint32_t opcode = cpu->prefetch; + LOAD_16(cpu->prefetch, cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion); cpu->gprs[ARM_PC] += WORD_SIZE_THUMB; - uint16_t opcode; - LOAD_16(opcode, cpu->currentPC & cpu->memory.activeMask, cpu->memory.activeRegion); ThumbInstruction instruction = _thumbTable[opcode >> 6]; instruction(cpu, opcode); }

@@ -291,3 +288,14 @@ if (cpu->cycles >= cpu->nextEvent) {

cpu->irqh.processEvents(cpu); } } + +void ARMRunLoop(struct ARMCore* cpu) { + while (cpu->cycles < cpu->nextEvent) { + if (cpu->executionMode == MODE_THUMB) { + ThumbStep(cpu); + } else { + ARMStep(cpu); + } + } + cpu->irqh.processEvents(cpu); +}
M src/arm/arm.hsrc/arm/arm.h

@@ -52,7 +52,7 @@ struct ARMCore;

union PSR { struct { -#ifdef __POWERPC__ +#if defined(__POWERPC__) || defined(__PPC__) unsigned n : 1; unsigned z : 1; unsigned c : 1;

@@ -133,7 +133,7 @@

int32_t shifterOperand; int32_t shifterCarryOut; - uint32_t currentPC; + uint32_t prefetch; enum ExecutionMode executionMode; enum PrivilegeMode privilegeMode;

@@ -156,5 +156,6 @@ void ARMRaiseIRQ(struct ARMCore*);

void ARMRaiseSWI(struct ARMCore*); void ARMRun(struct ARMCore* cpu); +void ARMRunLoop(struct ARMCore* cpu); #endif
M src/arm/common.hsrc/arm/common.h

@@ -16,11 +16,30 @@ #include <unistd.h>

#define UNUSED(V) (void)(V) -#ifdef __POWERPC__ -#define LOAD_32(DEST, ADDR, ARR) asm("lwbrx %0, %1, %2" : "=r"(DEST) : "r"(ADDR), "r"(ARR)) -#define LOAD_16(DEST, ADDR, ARR) asm("lhbrx %0, %1, %2" : "=r"(DEST) : "r"(ADDR), "r"(ARR)) -#define STORE_32(SRC, ADDR, ARR) asm("stwbrx %0, %1, %2" : : "r"(SRC), "r"(ADDR), "r"(ARR)) -#define STORE_16(SRC, ADDR, ARR) asm("sthbrx %0, %1, %2" : : "r"(SRC), "r"(ADDR), "r"(ARR)) +#if defined(__PPC__) || defined(__POWERPC__) +#define LOAD_32(DEST, ADDR, ARR) { \ + uint32_t _addr = (ADDR); \ + void* _ptr = (ARR); \ + asm("lwbrx %0, %1, %2" : "=r"(DEST) : "b"(_ptr), "r"(_addr)); \ +} + +#define LOAD_16(DEST, ADDR, ARR) { \ + uint32_t _addr = (ADDR); \ + void* _ptr = (ARR); \ + asm("lhbrx %0, %1, %2" : "=r"(DEST) : "b"(_ptr), "r"(_addr)); \ +} + +#define STORE_32(SRC, ADDR, ARR) { \ + uint32_t _addr = (ADDR); \ + void* _ptr = (ARR); \ + asm("stwbrx %0, %1, %2" : : "r"(SRC), "b"(_ptr), "r"(_addr)); \ +} + +#define STORE_16(SRC, ADDR, ARR) { \ + uint32_t _addr = (ADDR); \ + void* _ptr = (ARR); \ + asm("sthbrx %0, %1, %2" : : "r"(SRC), "b"(_ptr), "r"(_addr)); \ +} #else #define LOAD_32(DEST, ADDR, ARR) DEST = ((uint32_t*) ARR)[(ADDR) >> 2] #define LOAD_16(DEST, ADDR, ARR) DEST = ((uint16_t*) ARR)[(ADDR) >> 1]

@@ -28,4 +47,31 @@ #define STORE_32(SRC, ADDR, ARR) ((uint32_t*) ARR)[(ADDR) >> 2] = SRC

#define STORE_16(SRC, ADDR, ARR) ((uint16_t*) ARR)[(ADDR) >> 1] = SRC #endif +#define MAKE_MASK(START, END) (((1 << ((END) - (START))) - 1) << (START)) +#define CHECK_BITS(SRC, START, END) ((SRC) & MAKE_MASK(START, END)) +#define EXT_BITS(SRC, START, END) (((SRC) >> (START)) & ((1 << ((END) - (START))) - 1)) +#define INS_BITS(SRC, START, END, BITS) (CLEAR_BITS(SRC, START, END) | (((BITS) << (START)) & MAKE_MASK(START, END))) +#define CLEAR_BITS(SRC, START, END) ((SRC) & ~MAKE_MASK(START, END)) +#define FILL_BITS(SRC, START, END) ((SRC) | MAKE_MASK(START, END)) + +#define DECL_BITFIELD(NAME, TYPE) typedef TYPE NAME + +#define DECL_BITS(TYPE, FIELD, START, SIZE) \ + __attribute__((unused)) static inline TYPE TYPE ## Is ## FIELD (TYPE src) { \ + return CHECK_BITS(src, (START), (START) + (SIZE)); \ + } \ + __attribute__((unused)) static inline TYPE TYPE ## Get ## FIELD (TYPE src) { \ + return EXT_BITS(src, (START), (START) + (SIZE)); \ + } \ + __attribute__((unused)) static inline TYPE TYPE ## Clear ## FIELD (TYPE src) { \ + return CLEAR_BITS(src, (START), (START) + (SIZE)); \ + } \ + __attribute__((unused)) static inline TYPE TYPE ## Fill ## FIELD (TYPE src) { \ + return FILL_BITS(src, (START), (START) + (SIZE)); \ + } \ + __attribute__((unused)) static inline TYPE TYPE ## Set ## FIELD (TYPE src, TYPE bits) { \ + return INS_BITS(src, (START), (START) + (SIZE), bits); \ + } + +#define DECL_BIT(TYPE, FIELD, BIT) DECL_BITS(TYPE, FIELD, BIT, 1) #endif
M src/arm/decoder.hsrc/arm/decoder.h

@@ -89,7 +89,7 @@

union ARMOperand { struct { uint8_t reg; - enum ARMShifterOperation shifterOp; + uint8_t shifterOp; union { uint8_t shifterReg; uint8_t shifterImm;

@@ -110,9 +110,9 @@ };

struct ARMMemoryAccess { uint8_t baseReg; + uint8_t width; uint16_t format; union ARMOperand offset; - enum ARMMemoryAccessType width; }; enum ARMMnemonic {

@@ -167,25 +167,25 @@ ARM_SPSR = 17

}; struct ARMInstructionInfo { - enum ExecutionMode execMode; uint32_t opcode; - enum ARMMnemonic mnemonic; union ARMOperand op1; union ARMOperand op2; union ARMOperand op3; union ARMOperand op4; struct ARMMemoryAccess memory; int operandFormat; - int branches; - int traps; - int affectsCPSR; - int condition; - int sDataCycles; - int nDataCycles; - int sInstructionCycles; - int nInstructionCycles; - int iCycles; - int cCycles; + unsigned execMode : 1; + bool branches : 1; + bool traps : 1; + bool affectsCPSR : 1; + unsigned condition : 4; + unsigned mnemonic : 6; + unsigned iCycles : 2; + unsigned cCycles : 4; + unsigned sDataCycles : 10; + unsigned nDataCycles : 10; + unsigned sInstructionCycles : 4; + unsigned nInstructionCycles : 4; }; void ARMDecodeARM(uint32_t opcode, struct ARMInstructionInfo* info);
M src/arm/isa-inlines.hsrc/arm/isa-inlines.h

@@ -44,13 +44,17 @@ #define ARM_STUB cpu->irqh.hitStub(cpu, opcode)

#define ARM_ILL cpu->irqh.hitIllegal(cpu, opcode) #define ARM_WRITE_PC \ - cpu->gprs[ARM_PC] = (cpu->gprs[ARM_PC] & -WORD_SIZE_ARM) + WORD_SIZE_ARM; \ - cpu->memory.setActiveRegion(cpu, cpu->gprs[ARM_PC] - WORD_SIZE_ARM); \ + cpu->gprs[ARM_PC] = (cpu->gprs[ARM_PC] & -WORD_SIZE_ARM); \ + cpu->memory.setActiveRegion(cpu, cpu->gprs[ARM_PC]); \ + LOAD_32(cpu->prefetch, cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion); \ + cpu->gprs[ARM_PC] += WORD_SIZE_ARM; \ currentCycles += 2 + cpu->memory.activeUncachedCycles32 + cpu->memory.activeSeqCycles32; #define THUMB_WRITE_PC \ - cpu->gprs[ARM_PC] = (cpu->gprs[ARM_PC] & -WORD_SIZE_THUMB) + WORD_SIZE_THUMB; \ - cpu->memory.setActiveRegion(cpu, cpu->gprs[ARM_PC] - WORD_SIZE_THUMB); \ + cpu->gprs[ARM_PC] = (cpu->gprs[ARM_PC] & -WORD_SIZE_THUMB); \ + cpu->memory.setActiveRegion(cpu, cpu->gprs[ARM_PC]); \ + LOAD_16(cpu->prefetch, cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion); \ + cpu->gprs[ARM_PC] += WORD_SIZE_THUMB; \ currentCycles += 2 + cpu->memory.activeUncachedCycles16 + cpu->memory.activeSeqCycles16; static inline int _ARMModeHasSPSR(enum PrivilegeMode mode) {
M src/debugger/cli-debugger.csrc/debugger/cli-debugger.c

@@ -17,7 +17,7 @@ DV_CHAR_TYPE

} type; union { int32_t intValue; - const char* charValue; + char* charValue; }; };

@@ -25,15 +25,23 @@ static const char* ERROR_MISSING_ARGS = "Arguments missing";

static struct CLIDebugger* _activeDebugger; -typedef void (DebuggerCommand)(struct CLIDebugger*, struct DebugVector*); +typedef void (*DebuggerCommand)(struct CLIDebugger*, struct DebugVector*); +typedef struct DebugVector* (*DVParser)(struct CLIDebugger* debugger, const char* string, size_t length); + +static struct DebugVector* _DVParse(struct CLIDebugger* debugger, const char* string, size_t length); +static struct DebugVector* _DVStringParse(struct CLIDebugger* debugger, const char* string, size_t length); static void _breakInto(struct CLIDebugger*, struct DebugVector*); static void _continue(struct CLIDebugger*, struct DebugVector*); static void _disassemble(struct CLIDebugger*, struct DebugVector*); +static void _disassembleArm(struct CLIDebugger*, struct DebugVector*); +static void _disassembleThumb(struct CLIDebugger*, struct DebugVector*); static void _next(struct CLIDebugger*, struct DebugVector*); static void _print(struct CLIDebugger*, struct DebugVector*); +static void _printBin(struct CLIDebugger*, struct DebugVector*); static void _printHex(struct CLIDebugger*, struct DebugVector*); static void _printStatus(struct CLIDebugger*, struct DebugVector*); +static void _printHelp(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*);

@@ -43,38 +51,49 @@ static void _clearBreakpoint(struct CLIDebugger*, struct DebugVector*);

static void _setWatchpoint(struct CLIDebugger*, struct DebugVector*); static void _breakIntoDefault(int signal); +static void _disassembleMode(struct CLIDebugger*, struct DebugVector*, enum ExecutionMode mode); static void _printLine(struct CLIDebugger* debugger, uint32_t address, enum ExecutionMode mode); static struct { const char* name; - DebuggerCommand* command; + DebuggerCommand command; + DVParser parser; + const char* summary; } _debuggerCommands[] = { - { "b", _setBreakpoint }, - { "break", _setBreakpoint }, - { "c", _continue }, - { "continue", _continue }, - { "d", _clearBreakpoint }, - { "delete", _clearBreakpoint }, - { "dis", _disassemble }, - { "disasm", _disassemble }, - { "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 } + { "b", _setBreakpoint, _DVParse, "Set a breakpoint" }, + { "break", _setBreakpoint, _DVParse, "Set a breakpoint" }, + { "c", _continue, 0, "Continue execution" }, + { "continue", _continue, 0, "Continue execution" }, + { "d", _clearBreakpoint, _DVParse, "Delete a breakpoint" }, + { "delete", _clearBreakpoint, _DVParse, "Delete a breakpoint" }, + { "dis", _disassemble, _DVParse, "Disassemble instructions" }, + { "dis/a", _disassembleArm, _DVParse, "Disassemble instructions as ARM" }, + { "dis/t", _disassembleThumb, _DVParse, "Disassemble instructions as Thumb" }, + { "disasm", _disassemble, _DVParse, "Disassemble instructions" }, + { "disasm/a", _disassembleArm, _DVParse, "Disassemble instructions as ARM" }, + { "disasm/t", _disassembleThumb, _DVParse, "Disassemble instructions as Thumb" }, + { "h", _printHelp, _DVStringParse, "Print help" }, + { "help", _printHelp, _DVStringParse, "Print help" }, + { "i", _printStatus, 0, "Print the current status" }, + { "info", _printStatus, 0, "Print the current status" }, + { "n", _next, 0, "Execute next instruction" }, + { "next", _next, 0, "Execute next instruction" }, + { "p", _print, _DVParse, "Print a value" }, + { "p/t", _printBin, _DVParse, "Print a value as binary" }, + { "p/x", _printHex, _DVParse, "Print a value as hexadecimal" }, + { "print", _print, _DVParse, "Print a value" }, + { "print/t", _printBin, _DVParse, "Print a value as binary" }, + { "print/x", _printHex, _DVParse, "Print a value as hexadecimal" }, + { "q", _quit, 0, "Quit the emulator" }, + { "quit", _quit, 0, "Quit the emulator" }, + { "rb", _readByte, _DVParse, "Read a byte from a specified offset" }, + { "rh", _readHalfword, _DVParse, "Read a halfword from a specified offset" }, + { "rw", _readWord, _DVParse, "Read a word from a specified offset" }, + { "status", _printStatus, 0, "Print the current status" }, + { "w", _setWatchpoint, _DVParse, "Set a watchpoint" }, + { "watch", _setWatchpoint, _DVParse, "Set a watchpoint" }, + { "x", _breakInto, 0, "Break into attached debugger (for developers)" }, + { 0, 0, 0, 0 } }; static inline void _printPSR(union PSR psr) {

@@ -122,10 +141,21 @@ _printStatus(debugger, 0);

} static void _disassemble(struct CLIDebugger* debugger, struct DebugVector* dv) { + _disassembleMode(debugger, dv, debugger->d.cpu->executionMode); +} + +static void _disassembleArm(struct CLIDebugger* debugger, struct DebugVector* dv) { + _disassembleMode(debugger, dv, MODE_ARM); +} + +static void _disassembleThumb(struct CLIDebugger* debugger, struct DebugVector* dv) { + _disassembleMode(debugger, dv, MODE_THUMB); +} + +static void _disassembleMode(struct CLIDebugger* debugger, struct DebugVector* dv, enum ExecutionMode mode) { uint32_t address; int size; int wordSize; - enum ExecutionMode mode = debugger->d.cpu->executionMode; if (mode == MODE_ARM) { wordSize = WORD_SIZE_ARM;

@@ -162,12 +192,42 @@ }

printf("\n"); } +static void _printBin(struct CLIDebugger* debugger, struct DebugVector* dv) { + UNUSED(debugger); + for ( ; dv; dv = dv->next) { + printf(" 0b"); + int i = 32; + while (i--) { + printf("%u", (dv->intValue >> i) & 1); + } + } + printf("\n"); +} + static void _printHex(struct CLIDebugger* debugger, struct DebugVector* dv) { UNUSED(debugger); for ( ; dv; dv = dv->next) { printf(" 0x%08X", dv->intValue); } printf("\n"); +} + +static void _printHelp(struct CLIDebugger* debugger, struct DebugVector* dv) { + UNUSED(debugger); + UNUSED(dv); + if (!dv) { + int i; + for (i = 0; _debuggerCommands[i].name; ++i) { + printf("%-10s %s\n", _debuggerCommands[i].name, _debuggerCommands[i].summary); + } + } else { + int i; + for (i = 0; _debuggerCommands[i].name; ++i) { + if (strcmp(_debuggerCommands[i].name, dv->charValue) == 0) { + printf(" %s\n", _debuggerCommands[i].summary); + } + } + } } static inline void _printLine(struct CLIDebugger* debugger, uint32_t address, enum ExecutionMode mode) {

@@ -386,10 +446,44 @@ }

return dv; } +static struct DebugVector* _DVStringParse(struct CLIDebugger* debugger, const char* string, size_t length) { + if (!string || length < 1) { + return 0; + } + + struct DebugVector dvTemp = { .type = DV_CHAR_TYPE }; + + size_t adjusted; + const char* next = strchr(string, ' '); + if (next) { + adjusted = next - string; + } else { + adjusted = length; + } + dvTemp.charValue = malloc(adjusted); + strncpy(dvTemp.charValue, string, adjusted); + + length -= adjusted; + string += adjusted; + + struct DebugVector* dv = malloc(sizeof(struct DebugVector)); + *dv = dvTemp; + if (string[0] == ' ') { + dv->next = _DVStringParse(debugger, string + 1, length - 1); + if (dv->next && dv->next->type == DV_ERROR_TYPE) { + dv->type = DV_ERROR_TYPE; + } + } + return dv; +} + static void _DVFree(struct DebugVector* dv) { struct DebugVector* next; while (dv) { next = dv->next; + if (dv->type == DV_CHAR_TYPE) { + free(dv->charValue); + } free(dv); dv = next; }

@@ -401,12 +495,6 @@ size_t cmdLength;

struct DebugVector* dv = 0; if (firstSpace) { cmdLength = firstSpace - line; - dv = _DVParse(debugger, firstSpace + 1, count - cmdLength - 1); - if (dv && dv->type == DV_ERROR_TYPE) { - printf("Parse error\n"); - _DVFree(dv); - return false; - } } else { cmdLength = count; }

@@ -418,6 +506,20 @@ if (strlen(name) != cmdLength) {

continue; } if (strncasecmp(name, line, cmdLength) == 0) { + if (_debuggerCommands[i].parser) { + if (firstSpace) { + dv = _debuggerCommands[i].parser(debugger, firstSpace + 1, count - cmdLength - 1); + if (dv && dv->type == DV_ERROR_TYPE) { + printf("Parse error\n"); + _DVFree(dv); + return false; + } + } else { + printf("Wrong number of arguments"); + } + } else if (firstSpace) { + printf("Wrong number of arguments"); + } _debuggerCommands[i].command(debugger, dv); _DVFree(dv); return true;

@@ -450,9 +552,8 @@ 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); - } + _parse(cliDebugger, line, count - 1); + history(cliDebugger->histate, &ev, H_ENTER, line); } } }
M src/debugger/debugger.csrc/debugger/debugger.c

@@ -115,3 +115,17 @@ watchpoint->address = address;

watchpoint->next = debugger->watchpoints; debugger->watchpoints = watchpoint; } + +void ARMDebuggerClearWatchpoint(struct ARMDebugger* debugger, uint32_t address) { + struct DebugBreakpoint** previous = &debugger->watchpoints; + struct DebugBreakpoint* breakpoint; + for (; (breakpoint = *previous); previous = &breakpoint->next) { + if (breakpoint->address == address) { + *previous = breakpoint->next; + free(breakpoint); + } + } + if (!debugger->watchpoints) { + ARMDebuggerRemoveMemoryShim(debugger); + } +}
M src/debugger/debugger.hsrc/debugger/debugger.h

@@ -58,5 +58,6 @@ 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); +void ARMDebuggerClearWatchpoint(struct ARMDebugger* debugger, uint32_t address); #endif
M src/debugger/gdb-stub.csrc/debugger/gdb-stub.c

@@ -177,7 +177,7 @@ }

static void _step(struct GDBStub* stub, const char* message) { ARMRun(stub->d.cpu); - snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGINT); + snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGTRAP); _sendMessage(stub); // TODO: parse message UNUSED(message);

@@ -279,46 +279,53 @@ _sendMessage(stub);

} static void _setBreakpoint(struct GDBStub* stub, const char* message) { + 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 + UNUSED(kind); + 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 - UNUSED(kind); + case '1': ARMDebuggerSetBreakpoint(&stub->d, address); strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4); _sendMessage(stub); break; - } case '2': case '3': - // TODO: Watchpoints + case '4': + ARMDebuggerSetWatchpoint(&stub->d, address); + strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4); + _sendMessage(stub); + break; default: + stub->outgoing[0] = '\0'; + _sendMessage(stub); break; } } static void _clearBreakpoint(struct GDBStub* stub, const char* message) { + const char* readAddress = &message[2]; + unsigned i = 0; + uint32_t address = _readHex(readAddress, &i); 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); + case '1': ARMDebuggerClearBreakpoint(&stub->d, address); - strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4); - _sendMessage(stub); break; - } case '2': case '3': - // TODO: Watchpoints + case '4': + ARMDebuggerClearWatchpoint(&stub->d, address); + break; default: break; } + strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4); + _sendMessage(stub); } size_t _parseGDBMessage(struct GDBStub* stub, const char* message) {
M src/debugger/memory-debugger.csrc/debugger/memory-debugger.c

@@ -69,3 +69,16 @@ debugger->cpu->memory.loadU8 = ARMDebuggerShim_loadU8;

debugger->cpu->memory.setActiveRegion = ARMDebuggerShim_setActiveRegion; debugger->cpu->memory.waitMultiple = ARMDebuggerShim_waitMultiple; } + +void ARMDebuggerRemoveMemoryShim(struct ARMDebugger* debugger) { + debugger->cpu->memory.store32 = debugger->originalMemory.store32; + debugger->cpu->memory.store16 = debugger->originalMemory.store16; + debugger->cpu->memory.store8 = debugger->originalMemory.store8; + debugger->cpu->memory.load32 = debugger->originalMemory.load32; + debugger->cpu->memory.load16 = debugger->originalMemory.load16; + debugger->cpu->memory.loadU16 = debugger->originalMemory.loadU16; + debugger->cpu->memory.load8 = debugger->originalMemory.load8; + debugger->cpu->memory.loadU8 = debugger->originalMemory.loadU8; + debugger->cpu->memory.setActiveRegion = debugger->originalMemory.setActiveRegion; + debugger->cpu->memory.waitMultiple = debugger->originalMemory.waitMultiple; +}
M src/debugger/memory-debugger.hsrc/debugger/memory-debugger.h

@@ -8,5 +8,6 @@

struct ARMDebugger; void ARMDebuggerInstallMemoryShim(struct ARMDebugger* debugger); +void ARMDebuggerRemoveMemoryShim(struct ARMDebugger* debugger); #endif
M src/debugger/parser.csrc/debugger/parser.c

@@ -1,5 +1,17 @@

#include "parser.h" +static inline char* _strndup(const char* start, size_t len) { +#ifdef HAVE_STRNDUP + return strndup(start, len); +#else + // This is suboptimal, but anything recent should have strndup + char* out = malloc((len + 1) * sizeof(char)); + strncpy(out, start, len); + out[len] = '\0'; + return out; +#endif +} + static struct LexVector* _lexOperator(struct LexVector* lv, char operator) { struct LexVector* lvNext = malloc(sizeof(struct LexVector)); lvNext->token.type = TOKEN_OPERATOR_TYPE;

@@ -95,13 +107,13 @@ case '-':

case '*': case '/': lv->token.type = TOKEN_IDENTIFIER_TYPE; - lv->token.identifierValue = strndup(tokenStart, string - tokenStart - 1); + lv->token.identifierValue = _strndup(tokenStart, string - tokenStart - 1); lv = _lexOperator(lv, token); state = LEX_ROOT; break; case ')': lv->token.type = TOKEN_IDENTIFIER_TYPE; - lv->token.identifierValue = strndup(tokenStart, string - tokenStart - 1); + lv->token.identifierValue = _strndup(tokenStart, string - tokenStart - 1); state = LEX_EXPECT_OPERATOR; break; default:

@@ -240,7 +252,7 @@ lv->token.uintValue = next;

break; case LEX_EXPECT_IDENTIFIER: lv->token.type = TOKEN_IDENTIFIER_TYPE; - lv->token.identifierValue = strndup(tokenStart, string - tokenStart); + lv->token.identifierValue = _strndup(tokenStart, string - tokenStart); break; case LEX_EXPECT_OPERATOR: lvNext = malloc(sizeof(struct LexVector));
M src/gba/gba-audio.csrc/gba/gba-audio.c

@@ -10,6 +10,7 @@ const unsigned GBA_AUDIO_SAMPLES = 2048;

const unsigned GBA_AUDIO_FIFO_SIZE = 8 * sizeof(int32_t); #define SWEEP_CYCLES (GBA_ARM7TDMI_FREQUENCY / 128) +static bool _writeEnvelope(struct GBAAudioEnvelope* envelope, uint16_t value); static int32_t _updateSquareChannel(struct GBAAudioSquareControl* envelope, int duty); static void _updateEnvelope(struct GBAAudioEnvelope* envelope); static bool _updateSweep(struct GBAAudioChannel1* ch);

@@ -33,28 +34,40 @@ audio->nextCh1 = 0;

audio->nextCh2 = 0; audio->nextCh3 = 0; audio->nextCh4 = 0; - audio->ch1.sweep.time = 0; - audio->ch1.envelope.nextStep = INT_MAX; - audio->ch1.control.nextStep = 0; - audio->ch1.control.endTime = 0; - audio->ch1.nextSweep = INT_MAX; - audio->ch1.sample = 0; - audio->ch2.envelope.nextStep = INT_MAX; - audio->ch2.control.nextStep = 0; - audio->ch2.control.endTime = 0; - audio->ch2.sample = 0; - audio->ch3.bank.packed = 0; - audio->ch3.control.endTime = 0; - audio->ch3.sample = 0; - audio->ch4.sample = 0; - audio->ch4.envelope.nextStep = INT_MAX; + audio->ch1 = (struct GBAAudioChannel1) { .envelope = { .nextStep = INT_MAX }, .nextSweep = INT_MAX }; + audio->ch2 = (struct GBAAudioChannel2) { .envelope = { .nextStep = INT_MAX } }; + audio->ch3 = (struct GBAAudioChannel3) { .bank = { .bank = 0 } }; + audio->ch4 = (struct GBAAudioChannel4) { .envelope = { .nextStep = INT_MAX } }; + audio->chA.dmaSource = 0; + audio->chB.dmaSource = 0; audio->eventDiff = 0; audio->nextSample = 0; audio->sampleRate = 0x8000; audio->soundbias = 0x200; - audio->soundcntLo = 0; - audio->soundcntHi = 0; - audio->soundcntX = 0; + audio->volumeRight = 0; + audio->volumeLeft = 0; + audio->ch1Right = false; + audio->ch2Right = false; + audio->ch3Right = false; + audio->ch4Right = false; + audio->ch1Left = false; + audio->ch2Left = false; + audio->ch3Left = false; + audio->ch4Left = false; + audio->volume = 0; + audio->volumeChA = false; + audio->volumeChB = false; + audio->chARight = false; + audio->chALeft = false; + audio->chATimer = false; + audio->chBRight = false; + audio->chBLeft = false; + audio->chBTimer = false; + audio->playingCh1 = false; + audio->playingCh2 = false; + audio->playingCh3 = false; + audio->playingCh4 = false; + audio->enable = false; audio->sampleInterval = GBA_ARM7TDMI_FREQUENCY / audio->sampleRate; CircleBufferClear(&audio->left);

@@ -251,11 +264,13 @@ default:

GBALog(audio->p, GBA_LOG_GAME_ERROR, "Invalid FIFO destination: 0x%08X", info->dest); return; } - info->dstControl = DMA_FIXED; + info->reg = GBADMARegisterSetDestControl(info->reg, DMA_FIXED); } void GBAAudioWriteSOUND1CNT_LO(struct GBAAudio* audio, uint16_t value) { - audio->ch1.sweep.packed = value; + audio->ch1.sweep.shift = GBAAudioRegisterSquareSweepGetShift(value); + audio->ch1.sweep.direction = GBAAudioRegisterSquareSweepGetDirection(value); + audio->ch1.sweep.time = GBAAudioRegisterSquareSweepGetTime(value); if (audio->ch1.sweep.time) { audio->ch1.nextSweep = audio->ch1.sweep.time * SWEEP_CYCLES; } else {

@@ -264,23 +279,16 @@ }

} void GBAAudioWriteSOUND1CNT_HI(struct GBAAudio* audio, uint16_t value) { - audio->ch1.envelope.packed = value; - audio->ch1.envelope.dead = 0; - if (audio->ch1.envelope.stepTime) { - audio->ch1.envelope.nextStep = 0; - } else { - audio->ch1.envelope.nextStep = INT_MAX; - if (audio->ch1.envelope.initialVolume == 0) { - audio->ch1.envelope.dead = 1; - audio->ch1.sample = 0; - } + if (!_writeEnvelope(&audio->ch1.envelope, value)) { + audio->ch1.sample = 0; } } void GBAAudioWriteSOUND1CNT_X(struct GBAAudio* audio, uint16_t value) { - audio->ch1.control.packed = value; + audio->ch1.control.frequency = GBAAudioRegisterControlGetFrequency(value); + audio->ch1.control.stop = GBAAudioRegisterControlGetStop(value); audio->ch1.control.endTime = (GBA_ARM7TDMI_FREQUENCY * (64 - audio->ch1.envelope.length)) >> 8; - if (audio->ch1.control.restart) { + if (GBAAudioRegisterControlIsRestart(value)) { if (audio->ch1.sweep.time) { audio->ch1.nextSweep = audio->ch1.sweep.time * SWEEP_CYCLES; } else {

@@ -305,23 +313,16 @@ }

} void GBAAudioWriteSOUND2CNT_LO(struct GBAAudio* audio, uint16_t value) { - audio->ch2.envelope.packed = value; - audio->ch2.envelope.dead = 0; - if (audio->ch2.envelope.stepTime) { - audio->ch2.envelope.nextStep = 0; - } else { - audio->ch2.envelope.nextStep = INT_MAX; - if (audio->ch2.envelope.initialVolume == 0) { - audio->ch2.envelope.dead = 1; - audio->ch2.sample = 0; - } + if (!_writeEnvelope(&audio->ch2.envelope, value)) { + audio->ch2.sample = 0; } } void GBAAudioWriteSOUND2CNT_HI(struct GBAAudio* audio, uint16_t value) { - audio->ch2.control.packed = value; - audio->ch1.control.endTime = (GBA_ARM7TDMI_FREQUENCY * (64 - audio->ch2.envelope.length)) >> 8; - if (audio->ch2.control.restart) { + audio->ch2.control.frequency = GBAAudioRegisterControlGetFrequency(value); + audio->ch2.control.stop = GBAAudioRegisterControlGetStop(value); + audio->ch2.control.endTime = (GBA_ARM7TDMI_FREQUENCY * (64 - audio->ch2.envelope.length)) >> 8; + if (GBAAudioRegisterControlIsRestart(value)) { audio->playingCh2 = 1; audio->ch2.envelope.currentVolume = audio->ch2.envelope.initialVolume; if (audio->ch2.envelope.stepTime) {

@@ -334,42 +335,41 @@ }

} void GBAAudioWriteSOUND3CNT_LO(struct GBAAudio* audio, uint16_t value) { - audio->ch3.bank.packed = value; + audio->ch3.bank.size = GBAAudioRegisterBankGetSize(value); + audio->ch3.bank.bank = GBAAudioRegisterBankGetBank(value); + audio->ch3.bank.enable = GBAAudioRegisterBankGetEnable(value); if (audio->ch3.control.endTime >= 0) { audio->playingCh3 = audio->ch3.bank.enable; } } void GBAAudioWriteSOUND3CNT_HI(struct GBAAudio* audio, uint16_t value) { - audio->ch3.wave.packed = value; + audio->ch3.wave.length = GBAAudioRegisterBankWaveGetLength(value); + audio->ch3.wave.volume = GBAAudioRegisterBankWaveGetVolume(value); } void GBAAudioWriteSOUND3CNT_X(struct GBAAudio* audio, uint16_t value) { - audio->ch3.control.packed = value; + audio->ch3.control.rate = GBAAudioRegisterControlGetRate(value); + audio->ch3.control.stop = GBAAudioRegisterControlGetStop(value); audio->ch3.control.endTime = (GBA_ARM7TDMI_FREQUENCY * (256 - audio->ch3.wave.length)) >> 8; - if (audio->ch3.control.restart) { + if (GBAAudioRegisterControlIsRestart(value)) { audio->playingCh3 = audio->ch3.bank.enable; } } void GBAAudioWriteSOUND4CNT_LO(struct GBAAudio* audio, uint16_t value) { - audio->ch4.envelope.packed = value; - audio->ch4.envelope.dead = 0; - if (audio->ch4.envelope.stepTime) { - audio->ch4.envelope.nextStep = 0; - } else { - audio->ch4.envelope.nextStep = INT_MAX; - if (audio->ch4.envelope.initialVolume == 0) { - audio->ch4.envelope.dead = 1; - audio->ch4.sample = 0; - } + if (!_writeEnvelope(&audio->ch4.envelope, value)) { + audio->ch4.sample = 0; } } void GBAAudioWriteSOUND4CNT_HI(struct GBAAudio* audio, uint16_t value) { - audio->ch4.control.packed = value; + audio->ch4.control.ratio = GBAAudioRegisterCh4ControlGetRatio(value); + audio->ch4.control.frequency = GBAAudioRegisterCh4ControlGetFrequency(value); + audio->ch4.control.power = GBAAudioRegisterCh4ControlGetPower(value); + audio->ch4.control.stop = GBAAudioRegisterCh4ControlGetStop(value); audio->ch4.control.endTime = (GBA_ARM7TDMI_FREQUENCY * (64 - audio->ch4.envelope.length)) >> 8; - if (audio->ch4.control.restart) { + if (GBAAudioRegisterCh4ControlIsRestart(value)) { audio->playingCh4 = 1; audio->ch4.envelope.currentVolume = audio->ch4.envelope.initialVolume; if (audio->ch4.envelope.stepTime) {

@@ -387,15 +387,33 @@ }

} void GBAAudioWriteSOUNDCNT_LO(struct GBAAudio* audio, uint16_t value) { - audio->soundcntLo = value; + audio->volumeRight = GBARegisterSOUNDCNT_LOGetVolumeRight(value); + audio->volumeLeft = GBARegisterSOUNDCNT_LOGetVolumeLeft(value); + audio->ch1Right = GBARegisterSOUNDCNT_LOGetCh1Right(value); + audio->ch2Right = GBARegisterSOUNDCNT_LOGetCh2Right(value); + audio->ch3Right = GBARegisterSOUNDCNT_LOGetCh3Right(value); + audio->ch4Right = GBARegisterSOUNDCNT_LOGetCh4Right(value); + audio->ch1Left = GBARegisterSOUNDCNT_LOGetCh1Left(value); + audio->ch2Left = GBARegisterSOUNDCNT_LOGetCh2Left(value); + audio->ch3Left = GBARegisterSOUNDCNT_LOGetCh3Left(value); + audio->ch4Left = GBARegisterSOUNDCNT_LOGetCh4Left(value); } void GBAAudioWriteSOUNDCNT_HI(struct GBAAudio* audio, uint16_t value) { - audio->soundcntHi = value; + audio->volume = GBARegisterSOUNDCNT_HIGetVolume(value); + audio->volumeChA = GBARegisterSOUNDCNT_HIGetVolumeChA(value); + audio->volumeChB = GBARegisterSOUNDCNT_HIGetVolumeChB(value); + audio->chARight = GBARegisterSOUNDCNT_HIGetChARight(value); + audio->chALeft = GBARegisterSOUNDCNT_HIGetChALeft(value); + audio->chATimer = GBARegisterSOUNDCNT_HIGetChATimer(value); + audio->chBRight = GBARegisterSOUNDCNT_HIGetChBRight(value); + audio->chBLeft = GBARegisterSOUNDCNT_HIGetChBLeft(value); + audio->chBTimer = GBARegisterSOUNDCNT_HIGetChBTimer(value); + // TODO: Implement channel reset } void GBAAudioWriteSOUNDCNT_X(struct GBAAudio* audio, uint16_t value) { - audio->soundcntX = (value & 0x80) | (audio->soundcntX & 0x0F); + audio->enable = GBARegisterSOUNDCNT_XGetEnable(value); } void GBAAudioWriteSOUNDBIAS(struct GBAAudio* audio, uint16_t value) {

@@ -419,9 +437,12 @@ default:

GBALog(audio->p, GBA_LOG_ERROR, "Bad FIFO write to address 0x%03x", address); return; } - while (!CircleBufferWrite32(fifo, value)) { - int32_t dummy; - CircleBufferRead32(fifo, &dummy); + int i; + for (i = 0; i < 4; ++i) { + while (!CircleBufferWrite8(fifo, value >> (8 * i))) { + int8_t dummy; + CircleBufferRead8(fifo, &dummy); + } } }

@@ -501,6 +522,25 @@ break;

} } return totalRead; +} + +bool _writeEnvelope(struct GBAAudioEnvelope* envelope, uint16_t value) { + envelope->length = GBAAudioRegisterEnvelopeGetLength(value); + envelope->duty = GBAAudioRegisterEnvelopeGetDuty(value); + envelope->stepTime = GBAAudioRegisterEnvelopeGetStepTime(value); + envelope->direction = GBAAudioRegisterEnvelopeGetDirection(value); + envelope->initialVolume = GBAAudioRegisterEnvelopeGetInitialVolume(value); + envelope->dead = 0; + if (envelope->stepTime) { + envelope->nextStep = 0; + } else { + envelope->nextStep = INT_MAX; + if (envelope->initialVolume == 0) { + envelope->dead = 1; + return false; + } + } + return true; } static int32_t _updateSquareChannel(struct GBAAudioSquareControl* control, int duty) {
M src/gba/gba-audio.hsrc/gba/gba-audio.h

@@ -9,46 +9,64 @@ struct GBADMA;

extern const unsigned GBA_AUDIO_SAMPLES; +DECL_BITFIELD(GBAAudioRegisterEnvelope, uint16_t); +DECL_BITS(GBAAudioRegisterEnvelope, Length, 0, 6); +DECL_BITS(GBAAudioRegisterEnvelope, Duty, 6, 2); +DECL_BITS(GBAAudioRegisterEnvelope, StepTime, 8, 3); +DECL_BIT(GBAAudioRegisterEnvelope, Direction, 11); +DECL_BITS(GBAAudioRegisterEnvelope, InitialVolume, 12, 4); + +DECL_BITFIELD(GBAAudioRegisterControl, uint16_t); +DECL_BITS(GBAAudioRegisterControl, Rate, 0, 11); +DECL_BITS(GBAAudioRegisterControl, Frequency, 0, 11); +DECL_BIT(GBAAudioRegisterControl, Stop, 14); +DECL_BIT(GBAAudioRegisterControl, Restart, 15); + +DECL_BITFIELD(GBAAudioRegisterSquareSweep, uint16_t); +DECL_BITS(GBAAudioRegisterSquareSweep, Shift, 0, 3); +DECL_BIT(GBAAudioRegisterSquareSweep, Direction, 3); +DECL_BITS(GBAAudioRegisterSquareSweep, Time, 4, 3); + +DECL_BITFIELD(GBAAudioRegisterBank, uint16_t); +DECL_BIT(GBAAudioRegisterBank, Size, 5); +DECL_BIT(GBAAudioRegisterBank, Bank, 6); +DECL_BIT(GBAAudioRegisterBank, Enable, 7); + +DECL_BITFIELD(GBAAudioRegisterBankWave, uint16_t); +DECL_BITS(GBAAudioRegisterBankWave, Length, 0, 8); +DECL_BITS(GBAAudioRegisterBankWave, Volume, 13, 3); + +DECL_BITFIELD(GBAAudioRegisterCh4Control, uint16_t); +DECL_BITS(GBAAudioRegisterCh4Control, Ratio, 0, 3); +DECL_BIT(GBAAudioRegisterCh4Control, Power, 3); +DECL_BITS(GBAAudioRegisterCh4Control, Frequency, 4, 4); +DECL_BIT(GBAAudioRegisterCh4Control, Stop, 14); +DECL_BIT(GBAAudioRegisterCh4Control, Restart, 15); + struct GBAAudioEnvelope { - union { - struct { - unsigned length : 6; - unsigned duty : 2; - unsigned stepTime : 3; - unsigned direction : 1; - unsigned initialVolume : 4; - }; - uint16_t packed; - }; + uint8_t length; + uint8_t duty; + uint8_t stepTime; + uint8_t initialVolume; + bool direction; int currentVolume; int dead; int32_t nextStep; }; struct GBAAudioSquareControl { - union { - struct { - unsigned frequency : 11; - unsigned : 3; - unsigned stop : 1; - unsigned restart : 1; - }; - uint16_t packed; - }; + uint16_t frequency; + bool stop; int hi; int32_t nextStep; int32_t endTime; }; struct GBAAudioChannel1 { - union GBAAudioSquareSweep { - struct { - unsigned shift : 3; - unsigned direction : 1; - unsigned time : 3; - unsigned : 9; - }; - uint16_t packed; + struct GBAAudioSquareSweep { + uint8_t shift; + uint8_t time; + bool direction; } sweep; int32_t nextSweep;

@@ -64,36 +82,20 @@ int8_t sample;

}; struct GBAAudioChannel3 { - union { - struct { - unsigned : 5; - unsigned size : 1; - unsigned bank : 1; - unsigned enable : 1; - unsigned : 7; - }; - uint16_t packed; + struct { + bool size; + bool bank; + bool enable; } bank; - union { - struct { - unsigned length : 8; - unsigned : 5; - unsigned volume : 3; - }; - uint16_t packed; + struct { + uint8_t length; + uint8_t volume; } wave; struct { - union { - struct { - unsigned rate : 11; - unsigned : 3; - unsigned stop : 1; - unsigned restart : 1; - }; - uint16_t packed; - }; + uint16_t rate; + bool stop; int32_t endTime; } control;

@@ -103,18 +105,12 @@ };

struct GBAAudioChannel4 { struct GBAAudioEnvelope envelope; + struct { - union { - struct { - unsigned ratio : 3; - unsigned power : 1; - unsigned frequency : 4; - unsigned : 6; - unsigned stop : 1; - unsigned restart : 1; - }; - uint16_t packed; - }; + uint8_t ratio; + uint8_t frequency; + bool power; + bool stop; int32_t endTime; } control;

@@ -128,6 +124,38 @@ int dmaSource;

int8_t sample; }; +DECL_BITFIELD(GBARegisterSOUNDCNT_LO, uint16_t); +DECL_BITS(GBARegisterSOUNDCNT_LO, VolumeRight, 0, 3); +DECL_BITS(GBARegisterSOUNDCNT_LO, VolumeLeft, 4, 3); +DECL_BIT(GBARegisterSOUNDCNT_LO, Ch1Right, 8); +DECL_BIT(GBARegisterSOUNDCNT_LO, Ch2Right, 9); +DECL_BIT(GBARegisterSOUNDCNT_LO, Ch3Right, 10); +DECL_BIT(GBARegisterSOUNDCNT_LO, Ch4Right, 11); +DECL_BIT(GBARegisterSOUNDCNT_LO, Ch1Left, 12); +DECL_BIT(GBARegisterSOUNDCNT_LO, Ch2Left, 13); +DECL_BIT(GBARegisterSOUNDCNT_LO, Ch3Left, 14); +DECL_BIT(GBARegisterSOUNDCNT_LO, Ch4Left, 15); + +DECL_BITFIELD(GBARegisterSOUNDCNT_HI, uint16_t); +DECL_BITS(GBARegisterSOUNDCNT_HI, Volume, 0, 2); +DECL_BIT(GBARegisterSOUNDCNT_HI, VolumeChA, 2); +DECL_BIT(GBARegisterSOUNDCNT_HI, VolumeChB, 3); +DECL_BIT(GBARegisterSOUNDCNT_HI, ChARight, 8); +DECL_BIT(GBARegisterSOUNDCNT_HI, ChALeft, 9); +DECL_BIT(GBARegisterSOUNDCNT_HI, ChATimer, 10); +DECL_BIT(GBARegisterSOUNDCNT_HI, ChAReset, 11); +DECL_BIT(GBARegisterSOUNDCNT_HI, ChBRight, 12); +DECL_BIT(GBARegisterSOUNDCNT_HI, ChBLeft, 13); +DECL_BIT(GBARegisterSOUNDCNT_HI, ChBTimer, 14); +DECL_BIT(GBARegisterSOUNDCNT_HI, ChBReset, 15); + +DECL_BITFIELD(GBARegisterSOUNDCNT_X, uint16_t); +DECL_BIT(GBARegisterSOUNDCNT_X, PlayingCh1, 0); +DECL_BIT(GBARegisterSOUNDCNT_X, PlayingCh2, 1); +DECL_BIT(GBARegisterSOUNDCNT_X, PlayingCh3, 2); +DECL_BIT(GBARegisterSOUNDCNT_X, PlayingCh4, 3); +DECL_BIT(GBARegisterSOUNDCNT_X, Enable, 7); + struct GBAAudio { struct GBA* p;

@@ -142,54 +170,32 @@

struct CircleBuffer left; struct CircleBuffer right; - union { - struct { - unsigned volumeRight : 3; - unsigned : 1; - unsigned volumeLeft : 3; - unsigned : 1; - unsigned ch1Right : 1; - unsigned ch2Right : 1; - unsigned ch3Right : 1; - unsigned ch4Right : 1; - unsigned ch1Left : 1; - unsigned ch2Left : 1; - unsigned ch3Left : 1; - unsigned ch4Left : 1; - }; - uint16_t soundcntLo; - }; + uint8_t volumeRight; + uint8_t volumeLeft; + bool ch1Right; + bool ch2Right; + bool ch3Right; + bool ch4Right; + bool ch1Left; + bool ch2Left; + bool ch3Left; + bool ch4Left; - union { - struct { - unsigned volume : 2; - unsigned volumeChA : 1; - unsigned volumeChB : 1; - unsigned : 4; - unsigned chARight : 1; - unsigned chALeft : 1; - unsigned chATimer : 1; - unsigned chAReset : 1; - unsigned chBRight : 1; - unsigned chBLeft : 1; - unsigned chBTimer : 1; - unsigned chBReset : 1; - }; - uint16_t soundcntHi; - }; + uint8_t volume; + bool volumeChA; + bool volumeChB; + bool chARight; + bool chALeft; + bool chATimer; + bool chBRight; + bool chBLeft; + bool chBTimer; - union { - struct { - unsigned playingCh1 : 1; - unsigned playingCh2 : 1; - unsigned playingCh3 : 1; - unsigned playingCh4 : 1; - unsigned : 3; - unsigned enable : 1; - unsigned : 8; - }; - uint16_t soundcntX; - }; + bool playingCh1; + bool playingCh2; + bool playingCh3; + bool playingCh4; + bool enable; unsigned sampleRate;
M src/gba/gba-io.csrc/gba/gba-io.c

@@ -482,7 +482,7 @@ gba->memory.dma[i].nextSource = state->dma[i].nextSource;

gba->memory.dma[i].nextDest = state->dma[i].nextDest; gba->memory.dma[i].nextCount = state->dma[i].nextCount; gba->memory.dma[i].nextEvent = state->dma[i].nextEvent; - if (gba->memory.dma[i].timing != DMA_TIMING_NOW) { + if (GBADMARegisterGetTiming(gba->memory.dma[i].reg) != DMA_TIMING_NOW) { GBAMemoryScheduleDMA(gba, i, &gba->memory.dma[i]); }
M src/gba/gba-memory.csrc/gba/gba-memory.c

@@ -109,7 +109,7 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) {

struct GBA* gba = (struct GBA*) cpu->master; struct GBAMemory* memory = &gba->memory; - if (cpu->currentPC == gba->busyLoop) { + if (address == gba->busyLoop && memory->activeRegion != REGION_BIOS) { GBAHalt(gba); }

@@ -118,7 +118,7 @@ if (newRegion == memory->activeRegion) {

return; } if (memory->activeRegion == REGION_BIOS) { - memory->biosPrefetch = cpu->memory.load32(cpu, cpu->currentPC + WORD_SIZE_ARM * 2, 0); + memory->biosPrefetch = cpu->prefetch; } memory->activeRegion = newRegion; switch (address & ~OFFSET_MASK) {

@@ -163,9 +163,9 @@ struct GBAMemory* memory = &gba->memory;

uint32_t value = 0; int wait = 0; - switch (address & ~OFFSET_MASK) { - case BASE_BIOS: - if (cpu->currentPC >> BASE_OFFSET == REGION_BIOS) { + switch (address >> BASE_OFFSET) { + case REGION_BIOS: + if (memory->activeRegion == REGION_BIOS) { if (address < SIZE_BIOS) { LOAD_32(value, address, memory->bios); } else {

@@ -175,51 +175,48 @@ } else {

value = memory->biosPrefetch; } break; - case BASE_WORKING_RAM: + case REGION_WORKING_RAM: LOAD_32(value, address & (SIZE_WORKING_RAM - 1), memory->wram); wait = memory->waitstatesNonseq32[REGION_WORKING_RAM]; break; - case BASE_WORKING_IRAM: + case REGION_WORKING_IRAM: LOAD_32(value, address & (SIZE_WORKING_IRAM - 1), memory->iwram); break; - case BASE_IO: + case REGION_IO: value = GBAIORead(gba, (address & (SIZE_IO - 1)) & ~2) | (GBAIORead(gba, (address & (SIZE_IO - 1)) | 2) << 16); break; - case BASE_PALETTE_RAM: + case REGION_PALETTE_RAM: LOAD_32(value, address & (SIZE_PALETTE_RAM - 1), gba->video.palette); break; - case BASE_VRAM: + case REGION_VRAM: LOAD_32(value, address & 0x0001FFFF, gba->video.renderer->vram); break; - case BASE_OAM: + case REGION_OAM: LOAD_32(value, address & (SIZE_OAM - 1), gba->video.oam.raw); break; - case BASE_CART0: - case BASE_CART0_EX: - case BASE_CART1: - case BASE_CART1_EX: - case BASE_CART2: - case BASE_CART2_EX: + case REGION_CART0: + case REGION_CART0_EX: + case REGION_CART1: + case REGION_CART1_EX: + case REGION_CART2: + case REGION_CART2_EX: wait = memory->waitstatesNonseq32[address >> BASE_OFFSET]; if ((address & (SIZE_CART0 - 1)) < memory->romSize) { LOAD_32(value, address & (SIZE_CART0 - 1), memory->rom); } break; - case BASE_CART_SRAM: - case BASE_CART_SRAM_MIRROR: + case REGION_CART_SRAM: + case REGION_CART_SRAM_MIRROR: GBALog(gba, GBA_LOG_STUB, "Unimplemented memory Load32: 0x%08X", address); break; default: GBALog(gba, GBA_LOG_GAME_ERROR, "Bad memory Load32: 0x%08X", address); - if (cpu->executionMode == MODE_ARM) { - value = cpu->memory.load32(cpu, cpu->currentPC + WORD_SIZE_ARM * 2, 0); - } else { - value = cpu->memory.load16(cpu, cpu->currentPC + WORD_SIZE_THUMB * 2, 0); + value = cpu->prefetch; + if (cpu->executionMode == MODE_THUMB) { value |= value << 16; } break; } - if (cycleCounter) { *cycleCounter += 2 + wait;

@@ -239,9 +236,9 @@ struct GBAMemory* memory = &gba->memory;

uint16_t value = 0; int wait = 0; - switch (address & ~OFFSET_MASK) { - case BASE_BIOS: - if (cpu->currentPC >> BASE_OFFSET == REGION_BIOS) { + switch (address >> BASE_OFFSET) { + case REGION_BIOS: + if (memory->activeRegion == REGION_BIOS) { if (address < SIZE_BIOS) { LOAD_16(value, address, memory->bios); } else {

@@ -251,36 +248,36 @@ } else {

value = memory->biosPrefetch; } break; - case BASE_WORKING_RAM: + case REGION_WORKING_RAM: LOAD_16(value, address & (SIZE_WORKING_RAM - 1), memory->wram); wait = memory->waitstatesNonseq16[REGION_WORKING_RAM]; break; - case BASE_WORKING_IRAM: + case REGION_WORKING_IRAM: LOAD_16(value, address & (SIZE_WORKING_IRAM - 1), memory->iwram); break; - case BASE_IO: + case REGION_IO: value = GBAIORead(gba, address & (SIZE_IO - 1)); break; - case BASE_PALETTE_RAM: + case REGION_PALETTE_RAM: LOAD_16(value, address & (SIZE_PALETTE_RAM - 1), gba->video.palette); break; - case BASE_VRAM: + case REGION_VRAM: LOAD_16(value, address & 0x0001FFFF, gba->video.renderer->vram); break; - case BASE_OAM: + case REGION_OAM: LOAD_16(value, address & (SIZE_OAM - 1), gba->video.oam.raw); break; - case BASE_CART0: - case BASE_CART0_EX: - case BASE_CART1: - case BASE_CART1_EX: - case BASE_CART2: + case REGION_CART0: + case REGION_CART0_EX: + case REGION_CART1: + case REGION_CART1_EX: + case REGION_CART2: wait = memory->waitstatesNonseq16[address >> BASE_OFFSET]; if ((address & (SIZE_CART0 - 1)) < memory->romSize) { LOAD_16(value, address & (SIZE_CART0 - 1), memory->rom); } break; - case BASE_CART2_EX: + case REGION_CART2_EX: wait = memory->waitstatesNonseq16[address >> BASE_OFFSET]; if (memory->savedata.type == SAVEDATA_EEPROM) { value = GBASavedataReadEEPROM(&memory->savedata);

@@ -288,13 +285,13 @@ } else if ((address & (SIZE_CART0 - 1)) < memory->romSize) {

LOAD_16(value, address & (SIZE_CART0 - 1), memory->rom); } break; - case BASE_CART_SRAM: - case BASE_CART_SRAM_MIRROR: + case REGION_CART_SRAM: + case REGION_CART_SRAM_MIRROR: GBALog(gba, GBA_LOG_STUB, "Unimplemented memory Load16: 0x%08X", address); break; default: GBALog(gba, GBA_LOG_GAME_ERROR, "Bad memory Load16: 0x%08X", address); - value = cpu->memory.load16(cpu, cpu->currentPC + (cpu->executionMode == MODE_ARM ? WORD_SIZE_ARM : WORD_SIZE_THUMB) * 2, 0); + value = cpu->prefetch; break; }

@@ -316,9 +313,9 @@ struct GBAMemory* memory = &gba->memory;

int8_t value = 0; int wait = 0; - switch (address & ~OFFSET_MASK) { - case BASE_BIOS: - if (cpu->currentPC >> BASE_OFFSET == REGION_BIOS) { + switch (address >> BASE_OFFSET) { + case REGION_BIOS: + if (memory->activeRegion == REGION_BIOS) { if (address < SIZE_BIOS) { value = ((int8_t*) memory->bios)[address]; } else {

@@ -328,38 +325,38 @@ } else {

value = memory->biosPrefetch; } break; - case BASE_WORKING_RAM: + case REGION_WORKING_RAM: value = ((int8_t*) memory->wram)[address & (SIZE_WORKING_RAM - 1)]; wait = memory->waitstatesNonseq16[REGION_WORKING_RAM]; break; - case BASE_WORKING_IRAM: + case REGION_WORKING_IRAM: value = ((int8_t*) memory->iwram)[address & (SIZE_WORKING_IRAM - 1)]; break; - case BASE_IO: + case REGION_IO: value = (GBAIORead(gba, address & 0xFFFE) >> ((address & 0x0001) << 3)) & 0xFF; break; - case BASE_PALETTE_RAM: - value = ((int8_t*) gba->video.renderer->palette)[address & (SIZE_PALETTE_RAM - 1)]; + case REGION_PALETTE_RAM: + value = ((int8_t*) gba->video.palette)[address & (SIZE_PALETTE_RAM - 1)]; break; - case BASE_VRAM: + case REGION_VRAM: value = ((int8_t*) gba->video.renderer->vram)[address & 0x0001FFFF]; break; - case BASE_OAM: + case REGION_OAM: GBALog(gba, GBA_LOG_STUB, "Unimplemented memory Load8: 0x%08X", address); break; - case BASE_CART0: - case BASE_CART0_EX: - case BASE_CART1: - case BASE_CART1_EX: - case BASE_CART2: - case BASE_CART2_EX: + case REGION_CART0: + case REGION_CART0_EX: + case REGION_CART1: + case REGION_CART1_EX: + case REGION_CART2: + case REGION_CART2_EX: wait = memory->waitstatesNonseq16[address >> BASE_OFFSET]; if ((address & (SIZE_CART0 - 1)) < memory->romSize) { value = ((int8_t*) memory->rom)[address & (SIZE_CART0 - 1)]; } break; - case BASE_CART_SRAM: - case BASE_CART_SRAM_MIRROR: + case REGION_CART_SRAM: + case REGION_CART_SRAM_MIRROR: wait = memory->waitstatesNonseq16[address >> BASE_OFFSET]; if (memory->savedata.type == SAVEDATA_NONE) { GBASavedataInitSRAM(&memory->savedata);

@@ -372,7 +369,7 @@ }

break; default: GBALog(gba, GBA_LOG_GAME_ERROR, "Bad memory Load8: 0x%08x", address); - value = cpu->memory.load16(cpu, cpu->currentPC + (cpu->executionMode == MODE_ARM ? WORD_SIZE_ARM : WORD_SIZE_THUMB) * 2, 0) >> ((address & 1) << 3); + value = cpu->prefetch & 0xFF; break; }

@@ -387,39 +384,39 @@ struct GBA* gba = (struct GBA*) cpu->master;

struct GBAMemory* memory = &gba->memory; int wait = 0; - switch (address & ~OFFSET_MASK) { - case BASE_WORKING_RAM: + switch (address >> BASE_OFFSET) { + case REGION_WORKING_RAM: STORE_32(value, address & (SIZE_WORKING_RAM - 1), memory->wram); wait = memory->waitstatesNonseq32[REGION_WORKING_RAM]; break; - case BASE_WORKING_IRAM: + case REGION_WORKING_IRAM: STORE_32(value, address & (SIZE_WORKING_IRAM - 1), memory->iwram); break; - case BASE_IO: + case REGION_IO: GBAIOWrite32(gba, address & (SIZE_IO - 1), value); break; - case BASE_PALETTE_RAM: + case REGION_PALETTE_RAM: STORE_32(value, address & (SIZE_PALETTE_RAM - 1), gba->video.palette); gba->video.renderer->writePalette(gba->video.renderer, (address & (SIZE_PALETTE_RAM - 1)) + 2, value >> 16); gba->video.renderer->writePalette(gba->video.renderer, address & (SIZE_PALETTE_RAM - 1), value); break; - case BASE_VRAM: + case REGION_VRAM: if ((address & OFFSET_MASK) < SIZE_VRAM) { STORE_32(value, address & 0x0001FFFF, gba->video.renderer->vram); } else if ((address & OFFSET_MASK) < 0x00020000) { STORE_32(value, address & 0x00017FFF, gba->video.renderer->vram); } break; - case BASE_OAM: + case REGION_OAM: STORE_32(value, address & (SIZE_OAM - 1), gba->video.oam.raw); gba->video.renderer->writeOAM(gba->video.renderer, (address & (SIZE_OAM - 4)) >> 1); gba->video.renderer->writeOAM(gba->video.renderer, ((address & (SIZE_OAM - 4)) >> 1) + 1); break; - case BASE_CART0: + case REGION_CART0: GBALog(gba, GBA_LOG_STUB, "Unimplemented memory Store32: 0x%08X", address); break; - case BASE_CART_SRAM: - case BASE_CART_SRAM_MIRROR: + case REGION_CART_SRAM: + case REGION_CART_SRAM_MIRROR: GBALog(gba, GBA_LOG_STUB, "Unimplemented memory Store32: 0x%08X", address); break; default:

@@ -437,33 +434,33 @@ struct GBA* gba = (struct GBA*) cpu->master;

struct GBAMemory* memory = &gba->memory; int wait = 0; - switch (address & ~OFFSET_MASK) { - case BASE_WORKING_RAM: + switch (address >> BASE_OFFSET) { + case REGION_WORKING_RAM: STORE_16(value, address & (SIZE_WORKING_RAM - 1), memory->wram); wait = memory->waitstatesNonseq16[REGION_WORKING_RAM]; break; - case BASE_WORKING_IRAM: + case REGION_WORKING_IRAM: STORE_16(value, address & (SIZE_WORKING_IRAM - 1), memory->iwram); break; - case BASE_IO: + case REGION_IO: GBAIOWrite(gba, address & (SIZE_IO - 1), value); break; - case BASE_PALETTE_RAM: + case REGION_PALETTE_RAM: STORE_16(value, address & (SIZE_PALETTE_RAM - 1), gba->video.palette); gba->video.renderer->writePalette(gba->video.renderer, address & (SIZE_PALETTE_RAM - 1), value); break; - case BASE_VRAM: + case REGION_VRAM: if ((address & OFFSET_MASK) < SIZE_VRAM) { STORE_16(value, address & 0x0001FFFF, gba->video.renderer->vram); } else if ((address & OFFSET_MASK) < 0x00020000) { STORE_16(value, address & 0x00017FFF, gba->video.renderer->vram); } break; - case BASE_OAM: + case REGION_OAM: STORE_16(value, address & (SIZE_OAM - 1), gba->video.oam.raw); gba->video.renderer->writeOAM(gba->video.renderer, (address & (SIZE_OAM - 1)) >> 1); break; - case BASE_CART0: + case REGION_CART0: if (IS_GPIO_REGISTER(address & 0xFFFFFF)) { uint32_t reg = address & 0xFFFFFF; GBAGPIOWrite(&memory->gpio, reg, value);

@@ -471,14 +468,14 @@ } else {

GBALog(gba, GBA_LOG_GAME_ERROR, "Bad cartridge Store16: 0x%08X", address); } break; - case BASE_CART2_EX: + case REGION_CART2_EX: if (memory->savedata.type == SAVEDATA_NONE) { GBASavedataInitEEPROM(&memory->savedata); } GBASavedataWriteEEPROM(&memory->savedata, value, 1); break; - case BASE_CART_SRAM: - case BASE_CART_SRAM_MIRROR: + case REGION_CART_SRAM: + case REGION_CART_SRAM_MIRROR: GBALog(gba, GBA_LOG_STUB, "Unimplemented memory Store16: 0x%08X", address); break; default:

@@ -496,21 +493,21 @@ struct GBA* gba = (struct GBA*) cpu->master;

struct GBAMemory* memory = &gba->memory; int wait = 0; - switch (address & ~OFFSET_MASK) { - case BASE_WORKING_RAM: + switch (address >> BASE_OFFSET) { + case REGION_WORKING_RAM: ((int8_t*) memory->wram)[address & (SIZE_WORKING_RAM - 1)] = value; wait = memory->waitstatesNonseq16[REGION_WORKING_RAM]; break; - case BASE_WORKING_IRAM: + case REGION_WORKING_IRAM: ((int8_t*) memory->iwram)[address & (SIZE_WORKING_IRAM - 1)] = value; break; - case BASE_IO: + case REGION_IO: GBAIOWrite8(gba, address & (SIZE_IO - 1), value); break; - case BASE_PALETTE_RAM: + case REGION_PALETTE_RAM: GBALog(gba, GBA_LOG_STUB, "Unimplemented memory Store8: 0x%08X", address); break; - case BASE_VRAM: + case REGION_VRAM: if (address >= 0x06018000) { // TODO: check BG mode GBALog(gba, GBA_LOG_GAME_ERROR, "Cannot Store8 to OBJ: 0x%08X", address);

@@ -519,14 +516,14 @@ }

((int8_t*) gba->video.renderer->vram)[address & 0x1FFFE] = value; ((int8_t*) gba->video.renderer->vram)[(address & 0x1FFFE) | 1] = value; break; - case BASE_OAM: + case REGION_OAM: GBALog(gba, GBA_LOG_GAME_ERROR, "Cannot Store8 to OAM: 0x%08X", address); break; - case BASE_CART0: + case REGION_CART0: GBALog(gba, GBA_LOG_STUB, "Unimplemented memory Store8: 0x%08X", address); break; - case BASE_CART_SRAM: - case BASE_CART_SRAM_MIRROR: + case REGION_CART_SRAM: + case REGION_CART_SRAM_MIRROR: if (memory->savedata.type == SAVEDATA_NONE) { if (address == SAVEDATA_FLASH_BASE) { GBASavedataInitFlash(&memory->savedata);

@@ -654,26 +651,26 @@

uint16_t GBAMemoryWriteDMACNT_HI(struct GBA* gba, int dma, uint16_t control) { struct GBAMemory* memory = &gba->memory; struct GBADMA* currentDma = &memory->dma[dma]; - int wasEnabled = currentDma->enable; - currentDma->packed = control; + int wasEnabled = GBADMARegisterIsEnable(currentDma->reg); + currentDma->reg = control; - if (currentDma->drq) { + if (GBADMARegisterIsDRQ(currentDma->reg)) { GBALog(gba, GBA_LOG_STUB, "DRQ not implemented"); } - if (!wasEnabled && currentDma->enable) { + if (!wasEnabled && GBADMARegisterIsEnable(currentDma->reg)) { currentDma->nextSource = currentDma->source; currentDma->nextDest = currentDma->dest; currentDma->nextCount = currentDma->count; GBAMemoryScheduleDMA(gba, dma, currentDma); } // If the DMA has already occurred, this value might have changed since the function started - return currentDma->packed; + return currentDma->reg; }; void GBAMemoryScheduleDMA(struct GBA* gba, int number, struct GBADMA* info) { struct ARMCore* cpu = gba->cpu; - switch (info->timing) { + switch (GBADMARegisterGetTiming(info->reg)) { case DMA_TIMING_NOW: info->nextEvent = cpu->cycles; GBAMemoryUpdateDMAs(gba, 0);

@@ -709,7 +706,7 @@ struct GBADMA* dma;

int i; for (i = 0; i < 4; ++i) { dma = &memory->dma[i]; - if (dma->enable && dma->timing == DMA_TIMING_HBLANK) { + if (GBADMARegisterIsEnable(dma->reg) && GBADMARegisterGetTiming(dma->reg) == DMA_TIMING_HBLANK) { dma->nextEvent = cycles; } }

@@ -722,7 +719,7 @@ struct GBADMA* dma;

int i; for (i = 0; i < 4; ++i) { dma = &memory->dma[i]; - if (dma->enable && dma->timing == DMA_TIMING_VBLANK) { + if (GBADMARegisterIsEnable(dma->reg) && GBADMARegisterGetTiming(dma->reg) == DMA_TIMING_VBLANK) { dma->nextEvent = cycles; } }

@@ -755,7 +752,7 @@ for (i = 3; i >= 0; --i) {

struct GBADMA* dma = &memory->dma[i]; if (dma->nextEvent != INT_MAX) { dma->nextEvent -= cycles; - if (dma->enable) { + if (GBADMARegisterIsEnable(dma->reg)) { memory->activeDMA = i; memory->nextDMA = dma->nextEvent; }

@@ -769,9 +766,9 @@

void GBAMemoryServiceDMA(struct GBA* gba, int number, struct GBADMA* info) { struct GBAMemory* memory = &gba->memory; struct ARMCore* cpu = gba->cpu; - uint32_t width = info->width ? 4 : 2; - int sourceOffset = DMA_OFFSET[info->srcControl] * width; - int destOffset = DMA_OFFSET[info->dstControl] * width; + uint32_t width = GBADMARegisterGetWidth(info->reg) ? 4 : 2; + int sourceOffset = DMA_OFFSET[GBADMARegisterGetSrcControl(info->reg)] * width; + int destOffset = DMA_OFFSET[GBADMARegisterGetDestControl(info->reg)] * width; int32_t wordsRemaining = info->nextCount; uint32_t source = info->nextSource; uint32_t dest = info->nextDest;

@@ -831,20 +828,20 @@ }

} if (!wordsRemaining) { - if (!info->repeat) { - info->enable = 0; + if (!GBADMARegisterIsRepeat(info->reg)) { + info->reg = GBADMARegisterClearEnable(info->reg); info->nextEvent = INT_MAX; // Clear the enable bit in memory memory->io[(REG_DMA0CNT_HI + number * (REG_DMA1CNT_HI - REG_DMA0CNT_HI)) >> 1] &= 0x7FE0; } else { info->nextCount = info->count; - if (info->dstControl == DMA_INCREMENT_RELOAD) { + if (GBADMARegisterGetDestControl(info->reg) == DMA_INCREMENT_RELOAD) { info->nextDest = info->dest; } GBAMemoryScheduleDMA(gba, number, info); } - if (info->doIrq) { + if (GBADMARegisterIsDoIRQ(info->reg)) { GBARaiseIRQ(gba, IRQ_DMA0 + number); } } else {
M src/gba/gba-memory.hsrc/gba/gba-memory.h

@@ -80,21 +80,19 @@ DMA_TIMING_HBLANK = 2,

DMA_TIMING_CUSTOM = 3 }; + +DECL_BITFIELD(GBADMARegister, uint16_t); +DECL_BITS(GBADMARegister, DestControl, 5, 2); +DECL_BITS(GBADMARegister, SrcControl, 7, 2); +DECL_BIT(GBADMARegister, Repeat, 9); +DECL_BIT(GBADMARegister, Width, 10); +DECL_BIT(GBADMARegister, DRQ, 11); +DECL_BITS(GBADMARegister, Timing, 12, 2); +DECL_BIT(GBADMARegister, DoIRQ, 14); +DECL_BIT(GBADMARegister, Enable, 15); + struct GBADMA { - union { - struct { - int : 5; - enum DMAControl dstControl : 2; - enum DMAControl srcControl : 2; - unsigned repeat : 1; - unsigned width : 1; - unsigned drq : 1; - enum DMATiming timing : 2; - unsigned doIrq : 1; - unsigned enable : 1; - }; - uint16_t packed; - }; + GBADMARegister reg; uint32_t source; uint32_t dest;
M src/gba/gba-serialize.csrc/gba/gba-serialize.c

@@ -7,12 +7,15 @@ #include "gba-thread.h"

#include "gba-video.h" #include "util/memory.h" -#include "util/png-io.h" #include "util/vfs.h" #include <fcntl.h> + +#ifdef USE_PNG +#include "util/png-io.h" #include <png.h> #include <zlib.h> +#endif const uint32_t GBA_SAVESTATE_MAGIC = 0x01000000;

@@ -70,9 +73,15 @@ gba->cpu->cycles = state->cpu.cycles;

gba->cpu->nextEvent = state->cpu.nextEvent; memcpy(gba->cpu->bankedRegisters, state->cpu.bankedRegisters, 6 * 7 * sizeof(int32_t)); memcpy(gba->cpu->bankedSPSRs, state->cpu.bankedSPSRs, 6 * sizeof(int32_t)); - gba->cpu->executionMode = gba->cpu->cpsr.t ? MODE_THUMB : MODE_ARM; gba->cpu->privilegeMode = gba->cpu->cpsr.priv; gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]); + if (gba->cpu->cpsr.t) { + gba->cpu->executionMode = MODE_THUMB; + LOAD_16(gba->cpu->prefetch, (gba->cpu->gprs[ARM_PC] - WORD_SIZE_THUMB) & gba->cpu->memory.activeMask, gba->cpu->memory.activeRegion); + } else { + gba->cpu->executionMode = MODE_ARM; + LOAD_32(gba->cpu->prefetch, (gba->cpu->gprs[ARM_PC] - WORD_SIZE_ARM) & gba->cpu->memory.activeMask, gba->cpu->memory.activeRegion); + } GBAMemoryDeserialize(&gba->memory, state); GBAIODeserialize(gba, state);

@@ -107,6 +116,7 @@ }

return vf; } +#ifdef USE_PNG static bool _savePNGState(struct GBA* gba, struct VFile* vf) { unsigned stride; void* pixels = 0;

@@ -162,6 +172,7 @@ gba->video.renderer->putPixels(gba->video.renderer, VIDEO_HORIZONTAL_PIXELS, pixels);

GBASyncPostFrame(gba->sync); return true; } +#endif bool GBASaveState(struct GBA* gba, struct VDir* dir, int slot, bool screenshot) { struct VFile* vf = _getStateVf(gba, dir, slot, true);

@@ -192,23 +203,28 @@ return false;

} GBASerialize(gba, state); vf->unmap(vf, state, sizeof(struct GBASerializedState)); - } else { + return true; + } + #ifdef USE_PNG + else { return _savePNGState(gba, vf); } - return true; + #endif + return false; } bool GBALoadStateNamed(struct GBA* gba, struct VFile* vf) { - if (!isPNG(vf)) { - struct GBASerializedState* state = vf->map(vf, sizeof(struct GBASerializedState), MAP_READ); - if (!state) { - return false; - } - GBADeserialize(gba, state); - vf->unmap(vf, state, sizeof(struct GBASerializedState)); - } else { + #ifdef USE_PNG + if (isPNG(vf)) { return _loadPNGState(gba, vf); } + #endif + struct GBASerializedState* state = vf->map(vf, sizeof(struct GBASerializedState), MAP_READ); + if (!state) { + return false; + } + GBADeserialize(gba, state); + vf->unmap(vf, state, sizeof(struct GBASerializedState)); return true; }
M src/gba/gba-thread.csrc/gba/gba-thread.c

@@ -144,6 +144,7 @@ }

} if (threadContext->debugger) { + threadContext->debugger->log = GBADebuggerLogShim; GBAAttachDebugger(&gba, threadContext->debugger); ARMDebuggerEnter(threadContext->debugger, DEBUGGER_ENTER_ATTACHED); }

@@ -167,7 +168,7 @@ _changeState(threadContext, THREAD_EXITING, false);

} } else { while (threadContext->state == THREAD_RUNNING) { - ARMRun(&cpu); + ARMRunLoop(&cpu); } }

@@ -519,6 +520,7 @@ return TlsGetValue(_contextKey);

} #endif +#ifdef USE_PNG void GBAThreadTakeScreenshot(struct GBAThread* threadContext) { unsigned stride; void* pixels = 0;

@@ -530,6 +532,7 @@ PNGWritePixels(png, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, stride, pixels);

PNGWriteClose(png, info); vf->close(vf); } +#endif void GBASyncPostFrame(struct GBASync* sync) { if (!sync) {

@@ -577,9 +580,7 @@ ConditionWake(&sync->videoFrameRequiredCond);

if (!sync->videoFrameOn && !sync->videoFramePending) { return false; } - if (!sync->videoFramePending) { - ConditionWait(&sync->videoFrameAvailableCond, &sync->videoFrameMutex); - } + ConditionWait(&sync->videoFrameAvailableCond, &sync->videoFrameMutex); sync->videoFramePending = 0; sync->videoFrameSkip = frameskip; return true;
M src/gba/gba-thread.hsrc/gba/gba-thread.h

@@ -112,7 +112,9 @@ void GBAThreadTogglePause(struct GBAThread* threadContext);

void GBAThreadPauseFromThread(struct GBAThread* threadContext); struct GBAThread* GBAThreadGetContext(void); +#ifdef USE_PNG void GBAThreadTakeScreenshot(struct GBAThread* threadContext); +#endif void GBASyncPostFrame(struct GBASync* sync); bool GBASyncWaitFrameStart(struct GBASync* sync, int frameskip);
M src/gba/gba-video.csrc/gba/gba-video.c

@@ -36,14 +36,7 @@ video->vram = 0;

} void GBAVideoReset(struct GBAVideo* video) { - video->inHblank = 0; - video->inVblank = 0; - video->vcounter = 0; - video->vblankIRQ = 0; - video->hblankIRQ = 0; - video->vcounterIRQ = 0; - video->vcountSetting = 0; - + video->dispstat = 0; video->vcount = 0; video->lastHblank = 0;

@@ -94,9 +87,9 @@ video->nextHblank -= video->eventDiff;

video->nextHblankIRQ -= video->eventDiff; video->nextVcounterIRQ -= video->eventDiff; - if (video->inHblank) { + if (GBARegisterDISPSTATIsInHblank(video->dispstat)) { // End Hblank - video->inHblank = 0; + video->dispstat = GBARegisterDISPSTATClearInHblank(video->dispstat); video->nextEvent = video->nextHblank; ++video->vcount;

@@ -104,13 +97,13 @@ video->p->memory.io[REG_VCOUNT >> 1] = video->vcount;

switch (video->vcount) { case VIDEO_VERTICAL_PIXELS: - video->inVblank = 1; + video->dispstat = GBARegisterDISPSTATFillInVblank(video->dispstat); if (GBASyncDrawingFrame(video->p->sync)) { video->renderer->finishFrame(video->renderer); } video->nextVblankIRQ = video->nextEvent + VIDEO_TOTAL_LENGTH; GBAMemoryRunVblankDMAs(video->p, lastEvent); - if (video->vblankIRQ) { + if (GBARegisterDISPSTATIsVblankIRQ(video->dispstat)) { GBARaiseIRQ(video->p, IRQ_VBLANK); } GBASyncPostFrame(video->p->sync);

@@ -119,7 +112,7 @@ case VIDEO_VERTICAL_TOTAL_PIXELS - 1:

if (video->p->rr) { GBARRNextFrame(video->p->rr); } - video->inVblank = 0; + video->dispstat = GBARegisterDISPSTATClearInVblank(video->dispstat); break; case VIDEO_VERTICAL_TOTAL_PIXELS: video->vcount = 0;

@@ -127,14 +120,18 @@ video->p->memory.io[REG_VCOUNT >> 1] = 0;

break; } - video->vcounter = video->vcount == video->vcountSetting; - if (video->vcounter && video->vcounterIRQ) { - GBARaiseIRQ(video->p, IRQ_VCOUNTER); - video->nextVcounterIRQ += VIDEO_TOTAL_LENGTH; + if (video->vcount == GBARegisterDISPSTATGetVcountSetting(video->dispstat)) { + video->dispstat = GBARegisterDISPSTATFillVcounter(video->dispstat); + if (GBARegisterDISPSTATIsVcounterIRQ(video->dispstat)) { + GBARaiseIRQ(video->p, IRQ_VCOUNTER); + video->nextVcounterIRQ += VIDEO_TOTAL_LENGTH; + } + } else { + video->dispstat = GBARegisterDISPSTATClearVcounter(video->dispstat); } } else { // Begin Hblank - video->inHblank = 1; + video->dispstat = GBARegisterDISPSTATFillInHblank(video->dispstat); video->lastHblank = video->nextHblank; video->nextEvent = video->lastHblank + VIDEO_HBLANK_LENGTH; video->nextHblank = video->nextEvent + VIDEO_HDRAW_LENGTH;

@@ -147,7 +144,7 @@

if (video->vcount < VIDEO_VERTICAL_PIXELS) { GBAMemoryRunHblankDMAs(video->p, lastEvent); } - if (video->hblankIRQ) { + if (GBARegisterDISPSTATIsHblankIRQ(video->dispstat)) { GBARaiseIRQ(video->p, IRQ_HBLANK); } }

@@ -155,21 +152,17 @@

video->eventDiff = 0; } video->p->memory.io[REG_DISPSTAT >> 1] &= 0xFFF8; - video->p->memory.io[REG_DISPSTAT >> 1] |= (video->inVblank) | (video->inHblank << 1) | (video->vcounter << 2); + video->p->memory.io[REG_DISPSTAT >> 1] |= video->dispstat & 0x7; return video->nextEvent; } void GBAVideoWriteDISPSTAT(struct GBAVideo* video, uint16_t value) { - union GBARegisterDISPSTAT dispstat; - dispstat.packed = value; - video->vblankIRQ = dispstat.vblankIRQ; - video->hblankIRQ = dispstat.hblankIRQ; - video->vcounterIRQ = dispstat.vcounterIRQ; - video->vcountSetting = dispstat.vcountSetting; + video->dispstat &= 0x7; + video->dispstat |= value & 0xFFF8; - if (video->vcounterIRQ) { + if (GBARegisterDISPSTATIsVcounterIRQ(video->dispstat)) { // FIXME: this can be too late if we're in the middle of an Hblank - video->nextVcounterIRQ = video->nextHblank + VIDEO_HBLANK_LENGTH + (video->vcountSetting - video->vcount) * VIDEO_HORIZONTAL_LENGTH; + video->nextVcounterIRQ = video->nextHblank + VIDEO_HBLANK_LENGTH + (GBARegisterDISPSTATGetVcountSetting(video->dispstat) - video->vcount) * VIDEO_HORIZONTAL_LENGTH; if (video->nextVcounterIRQ < video->nextEvent) { video->nextVcounterIRQ += VIDEO_TOTAL_LENGTH; }

@@ -233,15 +226,7 @@ void GBAVideoSerialize(struct GBAVideo* video, struct GBASerializedState* state) {

memcpy(state->vram, video->renderer->vram, SIZE_VRAM); memcpy(state->oam, video->oam.raw, SIZE_OAM); memcpy(state->pram, video->palette, SIZE_PALETTE_RAM); - union GBARegisterDISPSTAT dispstat; - dispstat.inVblank = video->inVblank; - dispstat.inHblank = video->inHblank; - dispstat.vcounter = video->vcounter; - dispstat.vblankIRQ = video->vblankIRQ; - dispstat.hblankIRQ = video->hblankIRQ; - dispstat.vcounterIRQ = video->vcounterIRQ; - dispstat.vcountSetting = video->vcountSetting; - state->io[REG_DISPSTAT >> 1] = dispstat.packed; + state->io[REG_DISPSTAT >> 1] = video->dispstat; state->video.nextEvent = video->nextEvent; state->video.eventDiff = video->eventDiff; state->video.lastHblank = video->lastHblank;

@@ -260,15 +245,7 @@ }

for (i = 0; i < SIZE_PALETTE_RAM; i += 2) { GBAStore16(video->p->cpu, BASE_PALETTE_RAM | i, state->pram[i >> 1], 0); } - union GBARegisterDISPSTAT dispstat; - dispstat.packed = state->io[REG_DISPSTAT >> 1]; - video->inVblank = dispstat.inVblank; - video->inHblank = dispstat.inHblank; - video->vcounter = dispstat.vcounter; - video->vblankIRQ = dispstat.vblankIRQ; - video->hblankIRQ = dispstat.hblankIRQ; - video->vcounterIRQ = dispstat.vcounterIRQ; - video->vcountSetting = dispstat.vcountSetting; + video->dispstat = state->io[REG_DISPSTAT >> 1]; video->nextEvent = state->video.nextEvent; video->eventDiff = state->video.eventDiff; video->lastHblank = state->video.lastHblank;
M src/gba/gba-video.hsrc/gba/gba-video.h

@@ -43,78 +43,48 @@ OBJ_SHAPE_HORIZONTAL = 1,

OBJ_SHAPE_VERTICAL = 2 }; -union GBAColor { - struct { - unsigned r : 5; - unsigned g : 5; - unsigned b : 5; - }; - uint16_t packed; -}; +DECL_BITFIELD(GBAObjAttributesA, uint16_t); +DECL_BITS(GBAObjAttributesA, Y, 0, 8); +DECL_BIT(GBAObjAttributesA, Transformed, 8); +DECL_BIT(GBAObjAttributesA, Disable, 9); +DECL_BIT(GBAObjAttributesA, DoubleSize, 9); +DECL_BITS(GBAObjAttributesA, Mode, 10, 2); +DECL_BIT(GBAObjAttributesA, Mosaic, 12); +DECL_BIT(GBAObjAttributesA, 256Color, 13); +DECL_BITS(GBAObjAttributesA, Shape, 14, 2); -struct GBAObj { - unsigned y : 8; - unsigned transformed : 1; - unsigned disable : 1; - enum ObjMode mode : 2; - unsigned mosaic : 1; - unsigned multipalette : 1; - enum ObjShape shape : 2; - int x : 9; - int : 3; - unsigned hflip : 1; - unsigned vflip : 1; - unsigned size : 2; +DECL_BITFIELD(GBAObjAttributesB, uint16_t); +DECL_BITS(GBAObjAttributesB, X, 0, 9); +DECL_BITS(GBAObjAttributesB, MatIndex, 9, 5); +DECL_BIT(GBAObjAttributesB, HFlip, 12); +DECL_BIT(GBAObjAttributesB, VFlip, 13); +DECL_BITS(GBAObjAttributesB, Size, 14, 2); - unsigned tile : 10; - unsigned priority : 2; - unsigned palette : 4; +DECL_BITFIELD(GBAObjAttributesC, uint16_t); +DECL_BITS(GBAObjAttributesC, Tile, 0, 10); +DECL_BITS(GBAObjAttributesC, Priority, 10, 2); +DECL_BITS(GBAObjAttributesC, Palette, 12, 4); - int : 16; -}; - -struct GBATransformedObj { - unsigned y : 8; - unsigned transformed : 1; - unsigned doublesize : 1; - enum ObjMode mode : 2; - unsigned mosaic : 1; - unsigned multipalette : 1; - enum ObjShape shape : 2; - - int x : 9; - unsigned matIndex : 5; - unsigned size : 2; - - unsigned tile : 10; - unsigned priority : 2; - unsigned palette : 4; - - int : 16; +struct GBAObj { + GBAObjAttributesA a; + GBAObjAttributesB b; + GBAObjAttributesC c; + uint16_t d; }; union GBAOAM { struct GBAObj obj[128]; - struct GBATransformedObj tobj[128]; struct GBAOAMMatrix { - int : 16; - int : 16; - int : 16; - int a : 16; - int : 16; - int : 16; - int : 16; - int b : 16; - int : 16; - int : 16; - int : 16; - int c : 16; - int : 16; - int : 16; - int : 16; - int d : 16; + int16_t padding0[3]; + int16_t a; + int16_t padding1[3]; + int16_t b; + int16_t padding2[3]; + int16_t c; + int16_t padding3[3]; + int16_t d; } mat[32]; uint16_t raw[512];

@@ -125,53 +95,54 @@ #define GBA_TEXT_MAP_HFLIP(MAP) ((MAP) & 0x0400)

#define GBA_TEXT_MAP_VFLIP(MAP) ((MAP) & 0x0800) #define GBA_TEXT_MAP_PALETTE(MAP) (((MAP) & 0xF000) >> 12) -union GBARegisterDISPCNT { - struct { - unsigned mode : 3; - unsigned cgb : 1; - unsigned frameSelect : 1; - unsigned hblankIntervalFree : 1; - unsigned objCharacterMapping : 1; - unsigned forcedBlank : 1; - unsigned bg0Enable : 1; - unsigned bg1Enable : 1; - unsigned bg2Enable : 1; - unsigned bg3Enable : 1; - unsigned objEnable : 1; - unsigned win0Enable : 1; - unsigned win1Enable : 1; - unsigned objwinEnable : 1; - }; - uint16_t packed; -}; +DECL_BITFIELD(GBARegisterDISPCNT, uint16_t); +DECL_BITS(GBARegisterDISPCNT, Mode, 0, 3); +DECL_BIT(GBARegisterDISPCNT, Cgb, 3); +DECL_BIT(GBARegisterDISPCNT, FrameSelect, 4); +DECL_BIT(GBARegisterDISPCNT, HblankIntervalFree, 5); +DECL_BIT(GBARegisterDISPCNT, ObjCharacterMapping, 6); +DECL_BIT(GBARegisterDISPCNT, ForcedBlank, 7); +DECL_BIT(GBARegisterDISPCNT, Bg0Enable, 8); +DECL_BIT(GBARegisterDISPCNT, Bg1Enable, 9); +DECL_BIT(GBARegisterDISPCNT, Bg2Enable, 10); +DECL_BIT(GBARegisterDISPCNT, Bg3Enable, 11); +DECL_BIT(GBARegisterDISPCNT, ObjEnable, 12); +DECL_BIT(GBARegisterDISPCNT, Win0Enable, 13); +DECL_BIT(GBARegisterDISPCNT, Win1Enable, 14); +DECL_BIT(GBARegisterDISPCNT, ObjwinEnable, 15); + +DECL_BITFIELD(GBARegisterDISPSTAT, uint16_t); +DECL_BIT(GBARegisterDISPSTAT, InVblank, 0); +DECL_BIT(GBARegisterDISPSTAT, InHblank, 1); +DECL_BIT(GBARegisterDISPSTAT, Vcounter, 2); +DECL_BIT(GBARegisterDISPSTAT, VblankIRQ, 3); +DECL_BIT(GBARegisterDISPSTAT, HblankIRQ, 4); +DECL_BIT(GBARegisterDISPSTAT, VcounterIRQ, 5); +DECL_BITS(GBARegisterDISPSTAT, VcountSetting, 8, 8); -union GBARegisterDISPSTAT { - struct { - unsigned inVblank : 1; - unsigned inHblank : 1; - unsigned vcounter : 1; - unsigned vblankIRQ : 1; - unsigned hblankIRQ : 1; - unsigned vcounterIRQ : 1; - unsigned : 2; - unsigned vcountSetting : 8; - }; - uint32_t packed; -}; +DECL_BITFIELD(GBARegisterBGCNT, uint16_t); +DECL_BITS(GBARegisterBGCNT, Priority, 0, 2); +DECL_BITS(GBARegisterBGCNT, CharBase, 2, 2); +DECL_BIT(GBARegisterBGCNT, Mosaic, 6); +DECL_BIT(GBARegisterBGCNT, 256Color, 7); +DECL_BITS(GBARegisterBGCNT, ScreenBase, 8, 5); +DECL_BIT(GBARegisterBGCNT, Overflow, 13); +DECL_BITS(GBARegisterBGCNT, Size, 14, 2); -union GBARegisterBGCNT { - struct { - unsigned priority : 2; - unsigned charBase : 2; - unsigned : 2; - unsigned mosaic : 1; - unsigned multipalette : 1; - unsigned screenBase : 5; - unsigned overflow : 1; - unsigned size : 2; - }; - uint16_t packed; -}; +DECL_BITFIELD(GBARegisterBLDCNT, uint16_t); +DECL_BIT(GBARegisterBLDCNT, Target1Bg0, 0); +DECL_BIT(GBARegisterBLDCNT, Target1Bg1, 1); +DECL_BIT(GBARegisterBLDCNT, Target1Bg2, 2); +DECL_BIT(GBARegisterBLDCNT, Target1Bg3, 3); +DECL_BIT(GBARegisterBLDCNT, Target1Obj, 4); +DECL_BIT(GBARegisterBLDCNT, Target1Bd, 5); +DECL_BITS(GBARegisterBLDCNT, Effect, 6, 2); +DECL_BIT(GBARegisterBLDCNT, Target2Bg0, 8); +DECL_BIT(GBARegisterBLDCNT, Target2Bg1, 9); +DECL_BIT(GBARegisterBLDCNT, Target2Bg2, 10); +DECL_BIT(GBARegisterBLDCNT, Target2Bg3, 11); +DECL_BIT(GBARegisterBLDCNT, Target2Obj, 12); +DECL_BIT(GBARegisterBLDCNT, Target2Bd, 13); struct GBAVideoRenderer { void (*init)(struct GBAVideoRenderer* renderer);

@@ -196,14 +167,7 @@ struct GBAVideo {

struct GBA* p; struct GBAVideoRenderer* renderer; - // DISPSTAT - int inHblank; - int inVblank; - int vcounter; - int vblankIRQ; - int hblankIRQ; - int vcounterIRQ; - int vcountSetting; + GBARegisterDISPSTAT dispstat; // VCOUNT int vcount;
M src/gba/gba.csrc/gba/gba.c

@@ -14,7 +14,8 @@

const uint32_t GBA_ARM7TDMI_FREQUENCY = 0x1000000; const uint32_t GBA_COMPONENT_MAGIC = 0x1000000; -static const uint64_t GBA_ROM_MAGIC = 0x21A29A6951AEFF24; +static const size_t GBA_ROM_MAGIC_OFFSET = 4; +static const uint8_t GBA_ROM_MAGIC[] = { 0x24, 0xFF, 0xAE, 0x51, 0x69, 0x9A, 0xA2, 0x21 }; enum { SP_BASE_SYSTEM = 0x03FFFF00,

@@ -42,8 +43,11 @@ // Drill Dozer

{ "V49J", SAVEDATA_SRAM, GPIO_RUMBLE, -1 }, { "V49E", SAVEDATA_SRAM, GPIO_RUMBLE, -1 }, + // Final Fantasy Tactics Advance + { "AFXE", SAVEDATA_FLASH512, GPIO_NONE, 0x8000418 }, + // Mega Man Battle Network - { "AREE", SAVEDATA_SRAM, GPIO_NONE, 0x8000338 }, + { "AREE", SAVEDATA_SRAM, GPIO_NONE, 0x800032E }, // Pokemon Ruby { "AXVJ", SAVEDATA_FLASH1M, GPIO_RTC, -1 },

@@ -604,14 +608,18 @@ va_end(args);

} bool GBAIsROM(struct VFile* vf) { - if (vf->seek(vf, 4, SEEK_SET) < 0) { + if (vf->seek(vf, GBA_ROM_MAGIC_OFFSET, SEEK_SET) < 0) { return false; } - uint64_t signature; + uint8_t signature[sizeof(GBA_ROM_MAGIC)]; if (vf->read(vf, &signature, sizeof(signature)) != sizeof(signature)) { return false; } - return signature == GBA_ROM_MAGIC; + return memcmp(signature, GBA_ROM_MAGIC, sizeof(signature)) == 0; +} + +void GBAGetGameCode(struct GBA* gba, char* out) { + memcpy(out, &((struct GBACartridge*) gba->memory.rom)->id, 4); } void GBAHitStub(struct ARMCore* cpu, uint32_t opcode) {
M src/gba/gba.hsrc/gba/gba.h

@@ -148,6 +148,7 @@ void GBALoadBIOS(struct GBA* gba, struct VFile* vf);

void GBAApplyPatch(struct GBA* gba, struct Patch* patch); bool GBAIsROM(struct VFile* vf); +void GBAGetGameCode(struct GBA* gba, char* out); __attribute__((format (printf, 3, 4))) void GBALog(struct GBA* gba, enum GBALogLevel level, const char* format, ...);
M src/gba/hle-bios.csrc/gba/hle-bios.c

@@ -7,12 +7,12 @@ 0x06, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x05, 0x00, 0x00, 0xea,

0xfe, 0xff, 0xff, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x00, 0x00, 0xa0, 0xe1, 0x24, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x02, 0xf3, 0xa0, 0xe3, 0x00, 0x00, 0x5d, 0xe3, 0x01, 0xd3, 0xa0, 0x03, 0x20, 0xd0, 0x4d, 0x02, - 0x30, 0x40, 0x2d, 0xe9, 0x02, 0x40, 0x5e, 0xe5, 0x7c, 0x50, 0xa0, 0xe3, - 0x04, 0x41, 0x95, 0xe7, 0x00, 0x00, 0x54, 0xe3, 0x00, 0x50, 0x4f, 0xe1, - 0x20, 0x00, 0x2d, 0xe9, 0x80, 0x50, 0x05, 0xe2, 0x1f, 0x50, 0x85, 0xe3, - 0x05, 0xf0, 0x29, 0xe1, 0x00, 0x40, 0x2d, 0xe9, 0x0f, 0xe0, 0xa0, 0xe1, - 0x14, 0xff, 0x2f, 0x11, 0x00, 0x40, 0xbd, 0xe8, 0x93, 0xf0, 0x29, 0xe3, - 0x20, 0x00, 0xbd, 0xe8, 0x05, 0xf0, 0x69, 0xe1, 0x30, 0x40, 0xbd, 0xe8, + 0x00, 0x58, 0x2d, 0xe9, 0x02, 0xb0, 0x5e, 0xe5, 0x7c, 0xc0, 0xa0, 0xe3, + 0x0b, 0xb1, 0x9c, 0xe7, 0x00, 0x00, 0x5b, 0xe3, 0x00, 0xc0, 0x4f, 0xe1, + 0x00, 0x10, 0x2d, 0xe9, 0x80, 0xc0, 0x0c, 0xe2, 0x1f, 0xc0, 0x8c, 0xe3, + 0x0c, 0xf0, 0x29, 0xe1, 0x00, 0x40, 0x2d, 0xe9, 0x0f, 0xe0, 0xa0, 0xe1, + 0x1b, 0xff, 0x2f, 0x11, 0x00, 0x40, 0xbd, 0xe8, 0x93, 0xf0, 0x29, 0xe3, + 0x00, 0x10, 0xbd, 0xe8, 0x0c, 0xf0, 0x69, 0xe1, 0x00, 0x58, 0xbd, 0xe8, 0x0e, 0xf0, 0xb0, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x00, 0x00, 0x00, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

@@ -20,11 +20,11 @@ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x0c, 0x01, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x0f, 0x50, 0x2d, 0xe9, 0x01, 0x03, 0xa0, 0xe3, 0x00, 0xe0, 0x8f, 0xe2, 0x04, 0xf0, 0x10, 0xe5, 0x0f, 0x50, 0xbd, 0xe8, 0x04, 0xf0, 0x5e, 0xe2, 0x01, 0x00, 0xa0, 0xe3, - 0x01, 0x10, 0xa0, 0xe3, 0x0c, 0x40, 0x2d, 0xe9, 0x01, 0x43, 0xa0, 0xe3, + 0x01, 0x10, 0xa0, 0xe3, 0x0c, 0x40, 0x2d, 0xe9, 0x01, 0xc3, 0xa0, 0xe3, 0x00, 0x00, 0x50, 0xe3, 0x00, 0x00, 0xa0, 0xe3, 0x01, 0x20, 0xa0, 0xe3, - 0x00, 0x00, 0x00, 0x0a, 0x01, 0x03, 0xc4, 0xe5, 0x08, 0x02, 0xc4, 0xe5, - 0xb8, 0x30, 0x54, 0xe1, 0x01, 0x30, 0x13, 0xe0, 0x01, 0x30, 0x23, 0x10, - 0xb8, 0x30, 0x44, 0x11, 0x08, 0x22, 0xc4, 0xe5, 0xf7, 0xff, 0xff, 0x0a, + 0x00, 0x00, 0x00, 0x0a, 0x01, 0x03, 0xcc, 0xe5, 0x08, 0x02, 0xcc, 0xe5, + 0xb8, 0x30, 0x5c, 0xe1, 0x01, 0x30, 0x13, 0xe0, 0x01, 0x30, 0x23, 0x10, + 0xb8, 0x30, 0x4c, 0x11, 0x08, 0x22, 0xcc, 0xe5, 0xf7, 0xff, 0xff, 0x0a, 0x0c, 0x80, 0xbd, 0xe8, 0x00, 0x40, 0x2d, 0xe9, 0x02, 0x36, 0xa0, 0xe1, 0x01, 0x04, 0x12, 0xe3, 0x0f, 0x00, 0x00, 0x0a, 0x01, 0x03, 0x12, 0xe3, 0x05, 0x00, 0x00, 0x0a, 0x23, 0x35, 0x81, 0xe0, 0x04, 0x00, 0xb0, 0xe8,
A src/gba/hle-bios.make

@@ -0,0 +1,18 @@

+PREFIX := $(DEVKITARM)/bin/arm-none-eabi- +AS := $(PREFIX)as +OBJCOPY := $(PREFIX)objcopy + +all: hle-bios.c + +hle-bios.o: hle-bios.s + $(AS) -o $@ $< + +hle-bios.bin: hle-bios.o + $(OBJCOPY) -O binary $< $@ + +hle-bios.c: hle-bios.bin + echo '#include "hle-bios.h"' > $@ + echo >> $@ + echo '#include "gba-memory.h"' >> $@ + echo >> $@ + xxd -i $< | sed -e 's/unsigned char hle_bios_bin\[\]/const uint8_t hleBios[SIZE_BIOS]/' | grep -v hle_bios_bin_len >> $@
M src/gba/hle-bios.ssrc/gba/hle-bios.s

@@ -18,24 +18,24 @@ swiBase:

cmp sp, #0 moveq sp, #0x04000000 subeq sp, #0x20 -stmfd sp!, {r4-r5, lr} -ldrb r4, [lr, #-2] -mov r5, #swiTable -ldr r4, [r5, r4, lsl #2] -cmp r4, #0 -mrs r5, spsr -stmfd sp!, {r5} -and r5, #0x80 -orr r5, #0x1F -msr cpsr, r5 +stmfd sp!, {r11-r12, lr} +ldrb r11, [lr, #-2] +mov r12, #swiTable +ldr r11, [r12, r11, lsl #2] +cmp r11, #0 +mrs r12, spsr +stmfd sp!, {r12} +and r12, #0x80 +orr r12, #0x1F +msr cpsr, r12 stmfd sp!, {lr} mov lr, pc -bxne r4 +bxne r11 ldmfd sp!, {lr} msr cpsr, #0x93 -ldmfd sp!, {r5} -msr spsr, r5 -ldmfd sp!, {r4-r5, lr} +ldmfd sp!, {r12} +msr spsr, r12 +ldmfd sp!, {r11-r12, lr} movs pc, lr swiTable:

@@ -68,7 +68,7 @@ mov r1, #1

IntrWait: stmfd sp!, {r2-r3, lr} # Pull current interrupts enabled and add the ones we need -mov r4, #0x04000000 +mov r12, #0x04000000 # See if we want to return immediately cmp r0, #0 mov r0, #0

@@ -76,15 +76,15 @@ mov r2, #1

beq 1f # Halt 0: -strb r0, [r4, #0x301] +strb r0, [r12, #0x301] 1: # Check which interrupts were acknowledged -strb r0, [r4, #0x208] -ldrh r3, [r4, #-8] +strb r0, [r12, #0x208] +ldrh r3, [r12, #-8] ands r3, r1 eorne r3, r1 -strneh r3, [r4, #-8] -strb r2, [r4, #0x208] +strneh r3, [r12, #-8] +strb r2, [r12, #0x208] beq 0b ldmfd sp!, {r2-r3, pc}
M src/gba/renderers/video-software.csrc/gba/renderers/video-software.c

@@ -51,7 +51,6 @@ static void _drawBackgroundMode3(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);

static void _drawBackgroundMode4(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y); static void _drawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y); static void _cleanOAM(struct GBAVideoSoftwareRenderer* renderer); -static int _preprocessTransformedSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBATransformedObj* sprite, int y); static int _preprocessSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y); static void _postprocessSprite(struct GBAVideoSoftwareRenderer* renderer, unsigned priority);

@@ -77,7 +76,7 @@ static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer) {

struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer; int i; - softwareRenderer->dispcnt.packed = 0x0080; + softwareRenderer->dispcnt = 0x0080; softwareRenderer->target1Obj = 0; softwareRenderer->target1Bd = 0;

@@ -91,20 +90,13 @@ softwareRenderer->blda = 0;

softwareRenderer->bldb = 0; softwareRenderer->bldy = 0; - softwareRenderer->winN[0].h.packed = 0; - softwareRenderer->winN[0].v.packed = 0; - softwareRenderer->winN[0].control.packed = 0; - softwareRenderer->winN[0].control.priority = 0; - softwareRenderer->winN[1].h.packed = 0; - softwareRenderer->winN[1].v.packed = 0; - softwareRenderer->winN[1].control.packed = 0; - softwareRenderer->winN[1].control.priority = 1; - softwareRenderer->objwin.packed = 0; - softwareRenderer->objwin.priority = 2; - softwareRenderer->winout.packed = 0; - softwareRenderer->winout.priority = 3; + softwareRenderer->winN[0] = (struct WindowN) { .control = { .priority = 0 } }; + softwareRenderer->winN[1] = (struct WindowN) { .control = { .priority = 1 } }; + softwareRenderer->objwin = (struct WindowControl) { .priority = 2 }; + softwareRenderer->winout = (struct WindowControl) { .priority = 3 }; + softwareRenderer->oamMax = 0; - softwareRenderer->mosaic.packed = 0; + softwareRenderer->mosaic = 0; for (i = 0; i < 4; ++i) { struct GBAVideoSoftwareBackground* bg = &softwareRenderer->bg[i];

@@ -141,7 +133,7 @@ static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {

struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer; switch (address) { case REG_DISPCNT: - softwareRenderer->dispcnt.packed = value; + softwareRenderer->dispcnt = value; GBAVideoSoftwareRendererUpdateDISPCNT(softwareRenderer); break; case REG_BG0CNT:

@@ -261,7 +253,8 @@ }

_updatePalettes(softwareRenderer); break; case REG_WIN0H: - softwareRenderer->winN[0].h.packed = value; + softwareRenderer->winN[0].h.end = value; + softwareRenderer->winN[0].h.start = value >> 8; if (softwareRenderer->winN[0].h.start > VIDEO_HORIZONTAL_PIXELS && softwareRenderer->winN[0].h.start > softwareRenderer->winN[0].h.end) { softwareRenderer->winN[0].h.start = 0; }

@@ -270,7 +263,8 @@ softwareRenderer->winN[0].h.end = VIDEO_HORIZONTAL_PIXELS;

} break; case REG_WIN1H: - softwareRenderer->winN[1].h.packed = value; + softwareRenderer->winN[1].h.end = value; + softwareRenderer->winN[1].h.start = value >> 8; if (softwareRenderer->winN[1].h.start > VIDEO_HORIZONTAL_PIXELS && softwareRenderer->winN[1].h.start > softwareRenderer->winN[1].h.end) { softwareRenderer->winN[1].h.start = 0; }

@@ -279,7 +273,8 @@ softwareRenderer->winN[1].h.end = VIDEO_HORIZONTAL_PIXELS;

} break; case REG_WIN0V: - softwareRenderer->winN[0].v.packed = value; + softwareRenderer->winN[0].v.end = value; + softwareRenderer->winN[0].v.start = value >> 8; if (softwareRenderer->winN[0].v.start > VIDEO_VERTICAL_PIXELS && softwareRenderer->winN[0].v.start > softwareRenderer->winN[0].v.end) { softwareRenderer->winN[0].v.start = 0; }

@@ -288,7 +283,8 @@ softwareRenderer->winN[0].v.end = VIDEO_VERTICAL_PIXELS;

} break; case REG_WIN1V: - softwareRenderer->winN[1].v.packed = value; + softwareRenderer->winN[1].v.end = value; + softwareRenderer->winN[1].v.start = value >> 8; if (softwareRenderer->winN[1].v.start > VIDEO_VERTICAL_PIXELS && softwareRenderer->winN[1].v.start > softwareRenderer->winN[1].v.end) { softwareRenderer->winN[1].v.start = 0; }

@@ -305,7 +301,7 @@ softwareRenderer->winout.packed = value;

softwareRenderer->objwin.packed = value >> 8; break; case REG_MOSAIC: - softwareRenderer->mosaic.packed = value; + softwareRenderer->mosaic = value; break; case REG_GREENSWP: GBALog(0, GBA_LOG_STUB, "Stub video register write: 0x%03X", address);

@@ -393,16 +389,19 @@ static void _cleanOAM(struct GBAVideoSoftwareRenderer* renderer) {

int i; int oamMax = 0; for (i = 0; i < 128; ++i) { - struct GBAObj* obj = &renderer->d.oam->obj[i]; - if (obj->transformed || !obj->disable) { - int height = _objSizes[obj->shape * 8 + obj->size * 2 + 1]; - if (obj->transformed) { - height <<= ((struct GBATransformedObj*) obj)->doublesize; + struct GBAObj obj; + LOAD_16(obj.a, 0, &renderer->d.oam->obj[i].a); + LOAD_16(obj.b, 0, &renderer->d.oam->obj[i].b); + LOAD_16(obj.c, 0, &renderer->d.oam->obj[i].c); + if (GBAObjAttributesAIsTransformed(obj.a) || !GBAObjAttributesAIsDisable(obj.a)) { + int height = _objSizes[GBAObjAttributesAGetShape(obj.a) * 8 + GBAObjAttributesBGetSize(obj.b) * 2 + 1]; + if (GBAObjAttributesAIsTransformed(obj.a)) { + height <<= GBAObjAttributesAGetDoubleSize(obj.a); } - 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; + if (GBAObjAttributesAGetY(obj.a) < VIDEO_VERTICAL_PIXELS || GBAObjAttributesAGetY(obj.a) + height >= VIDEO_VERTICAL_TOTAL_PIXELS) { + renderer->sprites[oamMax].y = GBAObjAttributesAGetY(obj.a); + renderer->sprites[oamMax].endY = GBAObjAttributesAGetY(obj.a) + height; + renderer->sprites[oamMax].obj = obj; ++oamMax; } }

@@ -416,7 +415,7 @@ static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {

struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer; color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y]; - if (softwareRenderer->dispcnt.forcedBlank) { + if (GBARegisterDISPCNTIsForcedBlank(softwareRenderer->dispcnt)) { int x; for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) { row[x] = GBA_COLOR_WHITE;

@@ -434,12 +433,12 @@ }

softwareRenderer->windows[0].endX = VIDEO_HORIZONTAL_PIXELS; softwareRenderer->nWindows = 1; - if (softwareRenderer->dispcnt.win0Enable || softwareRenderer->dispcnt.win1Enable || softwareRenderer->dispcnt.objwinEnable) { + if (GBARegisterDISPCNTIsWin0Enable(softwareRenderer->dispcnt) || GBARegisterDISPCNTIsWin1Enable(softwareRenderer->dispcnt) || GBARegisterDISPCNTIsObjwinEnable(softwareRenderer->dispcnt)) { softwareRenderer->windows[0].control = softwareRenderer->winout; - if (softwareRenderer->dispcnt.win1Enable && y < softwareRenderer->winN[1].v.end && y >= softwareRenderer->winN[1].v.start) { + if (GBARegisterDISPCNTIsWin1Enable(softwareRenderer->dispcnt) && y < softwareRenderer->winN[1].v.end && y >= softwareRenderer->winN[1].v.start) { _breakWindow(softwareRenderer, &softwareRenderer->winN[1]); } - if (softwareRenderer->dispcnt.win0Enable && y < softwareRenderer->winN[0].v.end && y >= softwareRenderer->winN[0].v.start) { + if (GBARegisterDISPCNTIsWin0Enable(softwareRenderer->dispcnt) && y < softwareRenderer->winN[0].v.end && y >= softwareRenderer->winN[0].v.start) { _breakWindow(softwareRenderer, &softwareRenderer->winN[0]); } } else {

@@ -451,7 +450,7 @@ x = 0;

for (w = 0; w < softwareRenderer->nWindows; ++w) { // TOOD: handle objwin on backdrop uint32_t backdrop = FLAG_UNWRITTEN | FLAG_PRIORITY | FLAG_IS_BACKGROUND; - if (!softwareRenderer->target1Bd || softwareRenderer->blendEffect == BLEND_NONE || softwareRenderer->blendEffect == BLEND_ALPHA || !softwareRenderer->windows[w].control.blendEnable) { + if (!softwareRenderer->target1Bd || softwareRenderer->blendEffect == BLEND_NONE || softwareRenderer->blendEffect == BLEND_ALPHA || !GBAWindowControlIsBlendEnable(softwareRenderer->windows[w].control.packed)) { backdrop |= softwareRenderer->normalPalette[0]; } else { backdrop |= softwareRenderer->variantPalette[0];

@@ -468,7 +467,7 @@ if (softwareRenderer->target2Bd) {

x = 0; for (w = 0; w < softwareRenderer->nWindows; ++w) { uint32_t backdrop = FLAG_UNWRITTEN; - if (!softwareRenderer->target1Bd || softwareRenderer->blendEffect == BLEND_NONE || softwareRenderer->blendEffect == BLEND_ALPHA || !softwareRenderer->windows[w].control.blendEnable) { + if (!softwareRenderer->target1Bd || softwareRenderer->blendEffect == BLEND_NONE || softwareRenderer->blendEffect == BLEND_ALPHA || !GBAWindowControlIsBlendEnable(softwareRenderer->windows[w].control.packed)) { backdrop |= softwareRenderer->normalPalette[0]; } else { backdrop |= softwareRenderer->variantPalette[0];

@@ -523,22 +522,21 @@ }

} static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer) { - renderer->bg[0].enabled = renderer->dispcnt.bg0Enable; - renderer->bg[1].enabled = renderer->dispcnt.bg1Enable; - renderer->bg[2].enabled = renderer->dispcnt.bg2Enable; - renderer->bg[3].enabled = renderer->dispcnt.bg3Enable; + renderer->bg[0].enabled = GBARegisterDISPCNTGetBg0Enable(renderer->dispcnt); + renderer->bg[1].enabled = GBARegisterDISPCNTGetBg1Enable(renderer->dispcnt); + renderer->bg[2].enabled = GBARegisterDISPCNTGetBg2Enable(renderer->dispcnt); + renderer->bg[3].enabled = GBARegisterDISPCNTGetBg3Enable(renderer->dispcnt); } static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value) { UNUSED(renderer); - union GBARegisterBGCNT reg = { .packed = value }; - bg->priority = reg.priority; - bg->charBase = reg.charBase << 14; - bg->mosaic = reg.mosaic; - bg->multipalette = reg.multipalette; - bg->screenBase = reg.screenBase << 11; - bg->overflow = reg.overflow; - bg->size = reg.size; + bg->priority = GBARegisterBGCNTGetPriority(value); + bg->charBase = GBARegisterBGCNTGetCharBase(value) << 14; + bg->mosaic = GBARegisterBGCNTGetMosaic(value); + bg->multipalette = GBARegisterBGCNTGet256Color(value); + bg->screenBase = GBARegisterBGCNTGetScreenBase(value) << 11; + bg->overflow = GBARegisterBGCNTGetOverflow(value); + bg->size = GBARegisterBGCNTGetSize(value); } static void GBAVideoSoftwareRendererWriteBGPA(struct GBAVideoSoftwareBackground* bg, uint16_t value) {

@@ -582,43 +580,24 @@ bg->sy = bg->refy;

} static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer, uint16_t value) { - union { - struct { - unsigned target1Bg0 : 1; - unsigned target1Bg1 : 1; - unsigned target1Bg2 : 1; - unsigned target1Bg3 : 1; - unsigned target1Obj : 1; - unsigned target1Bd : 1; - enum BlendEffect effect : 2; - unsigned target2Bg0 : 1; - unsigned target2Bg1 : 1; - unsigned target2Bg2 : 1; - unsigned target2Bg3 : 1; - unsigned target2Obj : 1; - unsigned target2Bd : 1; - }; - uint16_t packed; - } bldcnt = { .packed = value }; - enum BlendEffect oldEffect = renderer->blendEffect; - renderer->bg[0].target1 = bldcnt.target1Bg0; - renderer->bg[1].target1 = bldcnt.target1Bg1; - renderer->bg[2].target1 = bldcnt.target1Bg2; - renderer->bg[3].target1 = bldcnt.target1Bg3; - renderer->bg[0].target2 = bldcnt.target2Bg0; - renderer->bg[1].target2 = bldcnt.target2Bg1; - renderer->bg[2].target2 = bldcnt.target2Bg2; - renderer->bg[3].target2 = bldcnt.target2Bg3; + renderer->bg[0].target1 = GBARegisterBLDCNTGetTarget1Bg0(value); + renderer->bg[1].target1 = GBARegisterBLDCNTGetTarget1Bg1(value); + renderer->bg[2].target1 = GBARegisterBLDCNTGetTarget1Bg2(value); + renderer->bg[3].target1 = GBARegisterBLDCNTGetTarget1Bg3(value); + renderer->bg[0].target2 = GBARegisterBLDCNTGetTarget2Bg0(value); + renderer->bg[1].target2 = GBARegisterBLDCNTGetTarget2Bg1(value); + renderer->bg[2].target2 = GBARegisterBLDCNTGetTarget2Bg2(value); + renderer->bg[3].target2 = GBARegisterBLDCNTGetTarget2Bg3(value); - renderer->blendEffect = bldcnt.effect; - renderer->target1Obj = bldcnt.target1Obj; - renderer->target1Bd = bldcnt.target1Bd; - renderer->target2Obj = bldcnt.target2Obj; - renderer->target2Bd = bldcnt.target2Bd; + renderer->blendEffect = GBARegisterBLDCNTGetEffect(value); + renderer->target1Obj = GBARegisterBLDCNTGetTarget1Obj(value); + renderer->target1Bd = GBARegisterBLDCNTGetTarget1Bd(value); + renderer->target2Obj = GBARegisterBLDCNTGetTarget2Obj(value); + renderer->target2Bd = GBARegisterBLDCNTGetTarget2Bd(value); - renderer->anyTarget2 = bldcnt.packed & 0x3F00; + renderer->anyTarget2 = value & 0x3F00; if (oldEffect != renderer->blendEffect) { _updatePalettes(renderer);

@@ -627,25 +606,25 @@ }

#define TEST_LAYER_ENABLED(X) \ (renderer->bg[X].enabled && \ - (renderer->currentWindow.bg ## X ## Enable || \ - (renderer->dispcnt.objwinEnable && renderer->objwin.bg ## X ## Enable)) && \ + (GBAWindowControlIsBg ## X ## Enable(renderer->currentWindow.packed) || \ + (GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt) && GBAWindowControlIsBg ## X ## Enable (renderer->objwin.packed))) && \ renderer->bg[X].priority == priority) static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y) { int w; renderer->end = 0; int spriteLayers = 0; - if (renderer->dispcnt.objEnable) { + if (GBARegisterDISPCNTIsObjEnable(renderer->dispcnt)) { if (renderer->oamDirty) { _cleanOAM(renderer); } - int mosaicV = renderer->mosaic.objV + 1; + int mosaicV = GBAMosaicControlGetObjV(renderer->mosaic) + 1; int mosaicY = y - (y % mosaicV); for (w = 0; w < renderer->nWindows; ++w) { renderer->start = renderer->end; renderer->end = renderer->windows[w].endX; renderer->currentWindow = renderer->windows[w].control; - if (!renderer->currentWindow.objEnable) { + if (!GBAWindowControlIsObjEnable(renderer->currentWindow.packed)) { continue; } int i;

@@ -653,18 +632,14 @@ int drawn;

for (i = 0; i < renderer->oamMax; ++i) { int localY = y; struct GBAVideoSoftwareSprite* sprite = &renderer->sprites[i]; - if (sprite->obj.mosaic) { + if (GBAObjAttributesAIsMosaic(sprite->obj.a)) { localY = mosaicY; } if ((localY < sprite->y && (sprite->endY - 256 < 0 || localY >= sprite->endY - 256)) || localY >= sprite->endY) { continue; } - if (sprite->obj.transformed) { - drawn = _preprocessTransformedSprite(renderer, &sprite->tobj, localY); - } else { - drawn = _preprocessSprite(renderer, &sprite->obj, localY); - } - spriteLayers |= drawn << sprite->obj.priority; + drawn = _preprocessSprite(renderer, &sprite->obj, localY); + spriteLayers |= drawn << GBAObjAttributesCGetPriority(sprite->obj.c); } } }

@@ -679,14 +654,14 @@ for (w = 0; w < renderer->nWindows; ++w) {

renderer->start = renderer->end; renderer->end = renderer->windows[w].endX; renderer->currentWindow = renderer->windows[w].control; - if (TEST_LAYER_ENABLED(0) && renderer->dispcnt.mode < 2) { + if (TEST_LAYER_ENABLED(0) && GBARegisterDISPCNTGetMode(renderer->dispcnt) < 2) { _drawBackgroundMode0(renderer, &renderer->bg[0], y); } - if (TEST_LAYER_ENABLED(1) && renderer->dispcnt.mode < 2) { + if (TEST_LAYER_ENABLED(1) && GBARegisterDISPCNTGetMode(renderer->dispcnt) < 2) { _drawBackgroundMode0(renderer, &renderer->bg[1], y); } if (TEST_LAYER_ENABLED(2)) { - switch (renderer->dispcnt.mode) { + switch (GBARegisterDISPCNTGetMode(renderer->dispcnt)) { case 0: _drawBackgroundMode0(renderer, &renderer->bg[2], y); break;

@@ -706,7 +681,7 @@ break;

} } if (TEST_LAYER_ENABLED(3)) { - switch (renderer->dispcnt.mode) { + switch (GBARegisterDISPCNTGetMode(renderer->dispcnt)) { case 0: _drawBackgroundMode0(renderer, &renderer->bg[3], y); break;

@@ -811,39 +786,39 @@ if (background->size & 1) { \

xBase += (localX & 0x100) << 5; \ } \ screenBase = yBase + (xBase >> 3); \ - mapData = vram[screenBase]; \ + LOAD_16(mapData, screenBase << 1, vram); \ localY = inY & 0x7; \ if (GBA_TEXT_MAP_VFLIP(mapData)) { \ localY = 7 - localY; \ } #define PREPARE_OBJWIN \ - int objwinSlowPath = renderer->dispcnt.objwinEnable; \ + int objwinSlowPath = GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt); \ int objwinOnly = 0; \ int objwinForceEnable = 0; \ color_t* objwinPalette; \ if (objwinSlowPath) { \ - if (background->target1 && renderer->objwin.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN)) { \ + if (background->target1 && GBAWindowControlIsBlendEnable(renderer->objwin.packed) && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN)) { \ objwinPalette = renderer->variantPalette; \ } else { \ objwinPalette = renderer->normalPalette; \ } \ switch (background->index) { \ case 0: \ - objwinForceEnable = renderer->objwin.bg0Enable && renderer->currentWindow.bg0Enable; \ - objwinOnly = !renderer->objwin.bg0Enable; \ + objwinForceEnable = GBAWindowControlIsBg0Enable(renderer->objwin.packed) && GBAWindowControlIsBg0Enable(renderer->currentWindow.packed); \ + objwinOnly = !GBAWindowControlIsBg0Enable(renderer->objwin.packed); \ break; \ case 1: \ - objwinForceEnable = renderer->objwin.bg1Enable && renderer->currentWindow.bg1Enable; \ - objwinOnly = !renderer->objwin.bg1Enable; \ + objwinForceEnable = GBAWindowControlIsBg1Enable(renderer->objwin.packed) && GBAWindowControlIsBg1Enable(renderer->currentWindow.packed); \ + objwinOnly = !GBAWindowControlIsBg1Enable(renderer->objwin.packed); \ break; \ case 2: \ - objwinForceEnable = renderer->objwin.bg2Enable && renderer->currentWindow.bg2Enable; \ - objwinOnly = !renderer->objwin.bg2Enable; \ + objwinForceEnable = GBAWindowControlIsBg2Enable(renderer->objwin.packed) && GBAWindowControlIsBg2Enable(renderer->currentWindow.packed); \ + objwinOnly = !GBAWindowControlIsBg2Enable(renderer->objwin.packed); \ break; \ case 3: \ - objwinForceEnable = renderer->objwin.bg3Enable && renderer->currentWindow.bg3Enable; \ - objwinOnly = !renderer->objwin.bg3Enable; \ + objwinForceEnable = GBAWindowControlIsBg3Enable(renderer->objwin.packed) && GBAWindowControlIsBg3Enable(renderer->currentWindow.packed); \ + objwinOnly = !GBAWindowControlIsBg3Enable(renderer->objwin.packed); \ break; \ } \ }

@@ -851,8 +826,8 @@

#define DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_16(BLEND, OBJWIN) \ paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \ palette = &mainPalette[paletteData]; \ - charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) >> 2) + localY; \ - tileData = ((uint32_t*) vram)[charBase]; \ + charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \ + LOAD_32(tileData, charBase, vram); \ if (!GBA_TEXT_MAP_HFLIP(mapData)) { \ tileData >>= 4 * mod8; \ for (; outX < end; ++outX) { \

@@ -867,8 +842,8 @@ } \

} #define DRAW_BACKGROUND_MODE_0_TILE_PREFIX_16(BLEND, OBJWIN) \ - charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) >> 2) + localY; \ - tileData = ((uint32_t*) vram)[charBase]; \ + charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \ + LOAD_32(tileData, charBase, vram); \ paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \ palette = &mainPalette[paletteData]; \ if (!GBA_TEXT_MAP_HFLIP(mapData)) { \

@@ -896,13 +871,13 @@

#define DRAW_BACKGROUND_MODE_0_MOSAIC_16(BLEND, OBJWIN) \ for (; tileX < tileEnd; ++tileX) { \ BACKGROUND_TEXT_SELECT_CHARACTER; \ - charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) >> 2) + localY; \ + charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \ tileData = carryData; \ for (x = 0; x < 8; ++x) { \ if (!mosaicWait) { \ paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \ palette = &mainPalette[paletteData]; \ - tileData = ((uint32_t*) vram)[charBase]; \ + LOAD_32(tileData, charBase, vram); \ if (!GBA_TEXT_MAP_HFLIP(mapData)) { \ tileData >>= x * 4; \ } else { \

@@ -930,8 +905,8 @@ for (; tileX < tileEnd; ++tileX) { \

BACKGROUND_TEXT_SELECT_CHARACTER; \ paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \ palette = &mainPalette[paletteData]; \ - charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) >> 2) + localY; \ - tileData = ((uint32_t*) vram)[charBase]; \ + charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \ + LOAD_32(tileData, charBase, vram); \ if (tileData) { \ if (!GBA_TEXT_MAP_HFLIP(mapData)) { \ BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \

@@ -975,53 +950,93 @@ } \

} #define DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_256(BLEND, OBJWIN) \ - /* TODO: hflip */ \ - charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) >> 2) + (localY << 1); \ + charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \ int end2 = end - 4; \ - int shift = inX & 0x3; \ - if (end2 > 0) { \ - tileData = ((uint32_t*) vram)[charBase]; \ + if (!GBA_TEXT_MAP_HFLIP(mapData)) { \ + int shift = inX & 0x3; \ + if (end2 > outX) { \ + LOAD_32(tileData, charBase, vram); \ + tileData >>= 8 * shift; \ + shift = 0; \ + for (; outX < end2; ++outX) { \ + pixel = &renderer->row[outX]; \ + BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \ + } \ + } \ + \ + LOAD_32(tileData, charBase + 4, vram); \ tileData >>= 8 * shift; \ - shift = 0; \ - for (; outX < end2; ++outX) { \ - uint32_t* pixel = &renderer->row[outX]; \ + for (; outX < end; ++outX) { \ + pixel = &renderer->row[outX]; \ + BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \ + } \ + } else { \ + int start = outX; \ + outX = end - 1; \ + if (end2 > start) { \ + LOAD_32(tileData, charBase, vram); \ + for (; outX >= end2; --outX) { \ + pixel = &renderer->row[outX]; \ + BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \ + } \ + charBase += 4; \ + } \ + \ + LOAD_32(tileData, charBase, vram); \ + for (; outX >= renderer->start; --outX) { \ + pixel = &renderer->row[outX]; \ BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \ } \ - } \ - \ - tileData = ((uint32_t*) vram)[charBase + 1]; \ - tileData >>= 8 * shift; \ - for (; outX < end; ++outX) { \ - uint32_t* pixel = &renderer->row[outX]; \ - BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \ + outX = end; \ } #define DRAW_BACKGROUND_MODE_0_TILE_PREFIX_256(BLEND, OBJWIN) \ - /* TODO: hflip */ \ - charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) >> 2) + (localY << 1); \ + charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \ outX = renderer->end - 8 + end; \ int end2 = 4 - end; \ - if (end2 > 0) { \ - tileData = ((uint32_t*) vram)[charBase]; \ - for (; outX < renderer->end - end2; ++outX) { \ - uint32_t* pixel = &renderer->row[outX]; \ + if (!GBA_TEXT_MAP_HFLIP(mapData)) { \ + if (end2 > 0) { \ + LOAD_32(tileData, charBase, vram); \ + for (; outX < renderer->end - end2; ++outX) { \ + pixel = &renderer->row[outX]; \ + BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \ + } \ + charBase += 4; \ + } \ + \ + LOAD_32(tileData, charBase, vram); \ + for (; outX < renderer->end; ++outX) { \ + pixel = &renderer->row[outX]; \ BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \ } \ - ++charBase; \ - } \ - \ - tileData = ((uint32_t*) vram)[charBase]; \ - for (; outX < renderer->end; ++outX) { \ - uint32_t* pixel = &renderer->row[outX]; \ - BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \ + } else { \ + int shift = end & 0x3; \ + int start = outX; \ + outX = renderer->end - 1; \ + if (end2 > 0) { \ + LOAD_32(tileData, charBase, vram); \ + tileData >>= 8 * shift; \ + for (; outX >= start + 4; --outX) { \ + pixel = &renderer->row[outX]; \ + BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \ + } \ + shift = 0; \ + } \ + \ + LOAD_32(tileData, charBase + 4, vram); \ + tileData >>= 8 * shift; \ + for (; outX >= start; --outX) { \ + pixel = &renderer->row[outX]; \ + BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \ + } \ } #define DRAW_BACKGROUND_MODE_0_TILES_256(BLEND, OBJWIN) \ for (; tileX < tileEnd; ++tileX) { \ BACKGROUND_TEXT_SELECT_CHARACTER; \ - charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) >> 2) + (localY << 1); \ + charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \ if (!GBA_TEXT_MAP_HFLIP(mapData)) { \ - tileData = ((uint32_t*) vram)[charBase]; \ + LOAD_32(tileData, charBase, vram); \ if (tileData) { \ BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \ ++pixel; \

@@ -1034,7 +1049,7 @@ ++pixel; \

} else { \ pixel += 4; \ } \ - tileData = ((uint32_t*) vram)[charBase + 1]; \ + LOAD_32(tileData, charBase + 4, vram); \ if (tileData) { \ BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \ ++pixel; \

@@ -1048,7 +1063,7 @@ } else { \

pixel += 4; \ } \ } else { \ - uint32_t tileData = ((uint32_t*) vram)[charBase + 1]; \ + LOAD_32(tileData, charBase + 4, vram); \ if (tileData) { \ pixel += 3; \ BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \

@@ -1060,7 +1075,7 @@ --pixel; \

BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \ } \ pixel += 4; \ - tileData = ((uint32_t*) vram)[charBase]; \ + LOAD_32(tileData, charBase, vram); \ if (tileData) { \ pixel += 3; \ BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \

@@ -1078,24 +1093,24 @@

#define DRAW_BACKGROUND_MODE_0_MOSAIC_256(BLEND, OBJWIN) \ for (; tileX < tileEnd; ++tileX) { \ BACKGROUND_TEXT_SELECT_CHARACTER; \ - charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) >> 2) + (localY << 1); \ + charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \ tileData = carryData; \ for (x = 0; x < 8; ++x) { \ if (!mosaicWait) { \ if (!GBA_TEXT_MAP_HFLIP(mapData)) { \ if (x >= 4) { \ - tileData = ((uint32_t*) vram)[charBase + 1]; \ + LOAD_32(tileData, charBase + 4, vram); \ tileData >>= (x - 4) * 8; \ } else { \ - tileData = ((uint32_t*) vram)[charBase]; \ + LOAD_32(tileData, charBase, vram); \ tileData >>= x * 8; \ } \ } else { \ if (x >= 4) { \ - tileData = ((uint32_t*) vram)[charBase]; \ + LOAD_32(tileData, charBase, vram); \ tileData >>= (7 - x) * 8; \ } else { \ - tileData = ((uint32_t*) vram)[charBase + 1]; \ + LOAD_32(tileData, charBase + 4, vram); \ tileData >>= (3 - x) * 8; \ } \ } \

@@ -1112,8 +1127,8 @@ }

#define DRAW_BACKGROUND_MODE_0(BPP, BLEND, OBJWIN) \ uint32_t* pixel = &renderer->row[outX]; \ - if (background->mosaic && renderer->mosaic.bgH) { \ - int mosaicH = renderer->mosaic.bgH + 1; \ + if (background->mosaic && GBAMosaicControlGetBgH(renderer->mosaic)) { \ + int mosaicH = GBAMosaicControlGetBgH(renderer->mosaic) + 1; \ int x; \ int mosaicWait = outX % mosaicH; \ int carryData = 0; \

@@ -1156,7 +1171,7 @@

static void _drawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y) { int inX = renderer->start + background->x; if (background->mosaic) { - int mosaicV = renderer->mosaic.bgV + 1; + int mosaicV = GBAMosaicControlGetBgV(renderer->mosaic) + 1; y -= y % mosaicV; } int inY = y + background->y;

@@ -1181,7 +1196,7 @@ flags |= FLAG_TARGET_2 * background->target2;

uint32_t screenBase; uint32_t charBase; - int variant = background->target1 && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN); + int variant = background->target1 && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN); color_t* mainPalette = renderer->normalPalette; if (variant) { mainPalette = renderer->variantPalette;

@@ -1240,7 +1255,7 @@ \

int flags = (background->priority << OFFSET_PRIORITY) | (background->index << OFFSET_INDEX) | FLAG_IS_BACKGROUND; \ flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA); \ flags |= FLAG_TARGET_2 * background->target2; \ - int variant = background->target1 && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN); \ + int variant = background->target1 && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN); \ color_t* palette = renderer->normalPalette; \ if (variant) { \ palette = renderer->variantPalette; \

@@ -1308,7 +1323,7 @@ uint32_t* pixel;

for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) { BACKGROUND_BITMAP_ITERATE(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS); - color = ((uint16_t*)renderer->d.vram)[(localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS]; + LOAD_16(color, ((localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS) << 1, renderer->d.vram); #ifndef COLOR_16_BIT unsigned color32; color32 = 0;

@@ -1336,7 +1351,7 @@ BACKGROUND_BITMAP_INIT;

uint16_t color; uint32_t offset = 0; - if (renderer->dispcnt.frameSelect) { + if (GBARegisterDISPCNTIsFrameSelect(renderer->dispcnt)) { offset = 0xA000; }

@@ -1364,7 +1379,7 @@ BACKGROUND_BITMAP_INIT;

uint32_t color; uint32_t offset = 0; - if (renderer->dispcnt.frameSelect) { + if (GBARegisterDISPCNTIsFrameSelect(renderer->dispcnt)) { offset = 0xA000; }

@@ -1373,7 +1388,7 @@ uint32_t* pixel;

for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) { BACKGROUND_BITMAP_ITERATE(160, 128); - color = ((uint16_t*)renderer->d.vram)[offset + (localX >> 8) + (localY >> 8) * 160]; + LOAD_16(color, (offset + (localX >> 8) + (localY >> 8) * 160) << 1, renderer->d.vram); #ifndef COLOR_16_BIT unsigned color32 = 0; color32 |= (color << 9) & 0xF80000;

@@ -1397,6 +1412,7 @@ }

#define SPRITE_NORMAL_LOOP(DEPTH, TYPE) \ SPRITE_YBASE_ ## DEPTH(inY); \ + unsigned tileData; \ for (; outX < condition; ++outX, inX += xOffset) { \ if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \ continue; \

@@ -1407,6 +1423,7 @@ }

#define SPRITE_MOSAIC_LOOP(DEPTH, TYPE) \ SPRITE_YBASE_ ## DEPTH(inY); \ + unsigned tileData; \ if (outX % mosaicH) { \ inX += (mosaicH - (outX % mosaicH)) * xOffset; \ outX += mosaicH - (outX % mosaicH); \

@@ -1421,14 +1438,15 @@ SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \

} #define SPRITE_TRANSFORMED_LOOP(DEPTH, TYPE) \ - int outX; \ - for (outX = x >= start ? x : start; outX < x + totalWidth && outX < end; ++outX) { \ + unsigned tileData; \ + for (; outX < x + totalWidth && outX < end; ++outX, ++inX) { \ if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \ continue; \ } \ - int inX = outX - x; \ - int localX = ((mat->a * (inX - (totalWidth >> 1)) + mat->b * (inY - (totalHeight >> 1))) >> 8) + (width >> 1); \ - int localY = ((mat->c * (inX - (totalWidth >> 1)) + mat->d * (inY - (totalHeight >> 1))) >> 8) + (height >> 1); \ + xAccum += mat.a; \ + yAccum += mat.c; \ + int localX = (xAccum >> 8) + (width >> 1); \ + int localY = (yAccum >> 8) + (height >> 1); \ \ if (localX < 0 || localX >= width || localY < 0 || localY >= height) { \ continue; \

@@ -1440,52 +1458,53 @@ SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \

} #define SPRITE_XBASE_16(localX) unsigned xBase = (localX & ~0x7) * 4 + ((localX >> 1) & 2); -#define SPRITE_YBASE_16(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width >> 1 : 0x80) + (localY & 0x7) * 4; +#define SPRITE_YBASE_16(localY) unsigned yBase = (localY & ~0x7) * (GBARegisterDISPCNTIsObjCharacterMapping(renderer->dispcnt) ? width >> 1 : 0x80) + (localY & 0x7) * 4; #define SPRITE_DRAW_PIXEL_16_NORMAL(localX) \ - unsigned tileData = vramBase[((yBase + charBase + xBase) & 0x7FFF) >> 1]; \ + LOAD_16(tileData, ((yBase + charBase + xBase) & 0x7FFF), vramBase); \ tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \ if (tileData && (renderer->spriteLayer[outX] & FLAG_ORDER_MASK) > flags) { \ renderer->spriteLayer[outX] = palette[tileData] | flags; \ } #define SPRITE_DRAW_PIXEL_16_OBJWIN(localX) \ - unsigned tileData = vramBase[((yBase + charBase + xBase) & 0x7FFF) >> 1]; \ + LOAD_16(tileData, ((yBase + charBase + xBase) & 0x7FFF), vramBase); \ tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \ if (tileData) { \ renderer->row[outX] |= FLAG_OBJWIN; \ } #define SPRITE_XBASE_256(localX) unsigned xBase = (localX & ~0x7) * 8 + (localX & 6); -#define SPRITE_YBASE_256(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width : 0x80) + (localY & 0x7) * 8; +#define SPRITE_YBASE_256(localY) unsigned yBase = (localY & ~0x7) * (GBARegisterDISPCNTIsObjCharacterMapping(renderer->dispcnt) ? width : 0x80) + (localY & 0x7) * 8; #define SPRITE_DRAW_PIXEL_256_NORMAL(localX) \ - unsigned tileData = vramBase[((yBase + charBase + xBase) & 0x7FFF) >> 1]; \ + LOAD_16(tileData, ((yBase + charBase + xBase) & 0x7FFF), vramBase); \ tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \ if (tileData && (renderer->spriteLayer[outX] & FLAG_ORDER_MASK) > flags) { \ renderer->spriteLayer[outX] = palette[tileData] | flags; \ } #define SPRITE_DRAW_PIXEL_256_OBJWIN(localX) \ - unsigned tileData = vramBase[((yBase + charBase + xBase) & 0x7FFF) >> 1]; \ + LOAD_16(tileData, ((yBase + charBase + xBase) & 0x7FFF), vramBase); \ tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \ if (tileData) { \ renderer->row[outX] |= FLAG_OBJWIN; \ } static int _preprocessSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y) { - int width = _objSizes[sprite->shape * 8 + sprite->size * 2]; - int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1]; + int width = _objSizes[GBAObjAttributesAGetShape(sprite->a) * 8 + GBAObjAttributesBGetSize(sprite->b) * 2]; + int height = _objSizes[GBAObjAttributesAGetShape(sprite->a) * 8 + GBAObjAttributesBGetSize(sprite->b) * 2 + 1]; int start = renderer->start; int end = renderer->end; - uint32_t flags = sprite->priority << OFFSET_PRIORITY; - flags |= FLAG_TARGET_1 * ((renderer->currentWindow.blendEnable && renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT); - flags |= FLAG_OBJWIN * (sprite->mode == OBJ_MODE_OBJWIN); - int x = sprite->x; + uint32_t flags = GBAObjAttributesCGetPriority(sprite->c) << OFFSET_PRIORITY; + flags |= FLAG_TARGET_1 * ((GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_SEMITRANSPARENT); + flags |= FLAG_OBJWIN * (GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_OBJWIN); + int32_t x = GBAObjAttributesBGetX(sprite->b) << 23; + x >>= 23; uint16_t* vramBase = &renderer->d.vram[BASE_TILE >> 1]; - unsigned charBase = sprite->tile * 0x20; - int variant = renderer->target1Obj && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN); - if (sprite->mode == OBJ_MODE_SEMITRANSPARENT && renderer->target2Bd) { + unsigned charBase = GBAObjAttributesCGetTile(sprite->c) * 0x20; + int variant = renderer->target1Obj && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN); + if (GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_SEMITRANSPARENT && renderer->target2Bd) { // Hack: if a sprite is blended, then the variant palette is not used, but we don't know if it's blended in advance variant = 0; }

@@ -1494,91 +1513,81 @@ if (variant) {

palette = &renderer->variantPalette[0x100]; } - int outX = x >= start ? x : start; - int condition = x + width; - int mosaicH = 1; - if (sprite->mosaic) { - mosaicH = renderer->mosaic.objH + 1; - if (condition % mosaicH) { - condition += mosaicH - (condition % mosaicH); + int inY = y - (int) GBAObjAttributesAGetY(sprite->a); + + if (GBAObjAttributesAIsTransformed(sprite->a)) { + int totalWidth = width << GBAObjAttributesAGetDoubleSize(sprite->a); + int totalHeight = height << GBAObjAttributesAGetDoubleSize(sprite->a); + struct GBAOAMMatrix mat; + LOAD_16(mat.a, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].a); + LOAD_16(mat.b, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].b); + LOAD_16(mat.c, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].c); + LOAD_16(mat.d, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].d); + + if (inY < 0) { + inY += 256; } - } - int inY = y - sprite->y; - if (sprite->y + height - 256 >= 0) { - inY += 256; - } - if (sprite->vflip) { - inY = height - inY - 1; - } - if (end < condition) { - condition = end; - } - int inX = outX - x; - int xOffset = 1; - if (sprite->hflip) { - inX = width - inX - 1; - xOffset = -1; - } - if (!sprite->multipalette) { - palette = &palette[sprite->palette << 4]; - if (flags & FLAG_OBJWIN) { - SPRITE_NORMAL_LOOP(16, OBJWIN); - } else if (sprite->mosaic) { - SPRITE_MOSAIC_LOOP(16, NORMAL); + int outX = x >= start ? x : start; + int inX = outX - x; + int xAccum = mat.a * (inX - 1 - (totalWidth >> 1)) + mat.b * (inY - (totalHeight >> 1)); + int yAccum = mat.c * (inX - 1 - (totalWidth >> 1)) + mat.d * (inY - (totalHeight >> 1)); + + if (!GBAObjAttributesAIs256Color(sprite->a)) { + palette = &palette[GBAObjAttributesCGetPalette(sprite->c) << 4]; + if (flags & FLAG_OBJWIN) { + SPRITE_TRANSFORMED_LOOP(16, OBJWIN); + } else { + SPRITE_TRANSFORMED_LOOP(16, NORMAL); + } } else { - SPRITE_NORMAL_LOOP(16, NORMAL); + if (flags & FLAG_OBJWIN) { + SPRITE_TRANSFORMED_LOOP(256, OBJWIN); + } else { + SPRITE_TRANSFORMED_LOOP(256, NORMAL); + } } } else { - if (flags & FLAG_OBJWIN) { - SPRITE_NORMAL_LOOP(256, OBJWIN); - } else if (sprite->mosaic) { - SPRITE_MOSAIC_LOOP(256, NORMAL); - } else { - SPRITE_NORMAL_LOOP(256, NORMAL); + int outX = x >= start ? x : start; + int condition = x + width; + int mosaicH = 1; + if (GBAObjAttributesAIsMosaic(sprite->a)) { + mosaicH = GBAMosaicControlGetObjH(renderer->mosaic) + 1; + if (condition % mosaicH) { + condition += mosaicH - (condition % mosaicH); + } + } + if ((int) GBAObjAttributesAGetY(sprite->a) + height - 256 >= 0) { + inY += 256; + } + if (GBAObjAttributesBIsVFlip(sprite->b)) { + inY = height - inY - 1; + } + if (end < condition) { + condition = end; } - } - return 1; -} - -static int _preprocessTransformedSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBATransformedObj* sprite, int y) { - int width = _objSizes[sprite->shape * 8 + sprite->size * 2]; - int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1]; - int totalWidth = width << sprite->doublesize; - int totalHeight = height << sprite->doublesize; - int start = renderer->start; - int end = renderer->end; - uint32_t flags = sprite->priority << OFFSET_PRIORITY; - flags |= FLAG_TARGET_1 * ((renderer->currentWindow.blendEnable && renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT); - flags |= FLAG_OBJWIN * (sprite->mode == OBJ_MODE_OBJWIN); - int x = sprite->x; - uint16_t* vramBase = &renderer->d.vram[BASE_TILE >> 1]; - unsigned charBase = sprite->tile * 0x20; - struct GBAOAMMatrix* mat = &renderer->d.oam->mat[sprite->matIndex]; - int variant = renderer->target1Obj && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN); - if (sprite->mode == OBJ_MODE_SEMITRANSPARENT && renderer->target2Bd) { - // Hack: if a sprite is blended, then the variant palette is not used, but we don't know if it's blended in advance - variant = 0; - } - color_t* palette = &renderer->normalPalette[0x100]; - if (variant) { - palette = &renderer->variantPalette[0x100]; - } - int inY = y - sprite->y; - if (inY < 0) { - inY += 256; - } - if (!sprite->multipalette) { - palette = &palette[sprite->palette << 4]; - if (flags & FLAG_OBJWIN) { - SPRITE_TRANSFORMED_LOOP(16, OBJWIN); - } else { - SPRITE_TRANSFORMED_LOOP(16, NORMAL); + int inX = outX - x; + int xOffset = 1; + if (GBAObjAttributesBIsHFlip(sprite->b)) { + inX = width - inX - 1; + xOffset = -1; } - } else { - if (flags & FLAG_OBJWIN) { - SPRITE_TRANSFORMED_LOOP(256, OBJWIN); + if (!GBAObjAttributesAIs256Color(sprite->a)) { + palette = &palette[GBAObjAttributesCGetPalette(sprite->c) << 4]; + if (flags & FLAG_OBJWIN) { + SPRITE_NORMAL_LOOP(16, OBJWIN); + } else if (GBAObjAttributesAIsMosaic(sprite->a)) { + SPRITE_MOSAIC_LOOP(16, NORMAL); + } else { + SPRITE_NORMAL_LOOP(16, NORMAL); + } } else { - SPRITE_TRANSFORMED_LOOP(256, NORMAL); + if (flags & FLAG_OBJWIN) { + SPRITE_NORMAL_LOOP(256, OBJWIN); + } else if (GBAObjAttributesAIsMosaic(sprite->a)) { + SPRITE_MOSAIC_LOOP(256, NORMAL); + } else { + SPRITE_NORMAL_LOOP(256, NORMAL); + } } } return 1;

@@ -1589,10 +1598,10 @@ int x;

uint32_t* pixel = renderer->row; uint32_t flags = FLAG_TARGET_2 * renderer->target2Obj; - int objwinSlowPath = renderer->dispcnt.objwinEnable; + int objwinSlowPath = GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt); int objwinDisable = 0; if (objwinSlowPath) { - objwinDisable = !renderer->objwin.objEnable; + objwinDisable = !GBAWindowControlIsObjEnable(renderer->objwin.packed); } if (objwinSlowPath && objwinDisable) { for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x, ++pixel) {
M src/gba/renderers/video-software.hsrc/gba/renderers/video-software.h

@@ -12,10 +12,7 @@ typedef uint32_t color_t;

#endif struct GBAVideoSoftwareSprite { - union { - struct GBAObj obj; - struct GBATransformedObj tobj; - }; + struct GBAObj obj; int y; int endY; };

@@ -76,27 +73,27 @@ #define FLAG_ORDER_MASK 0xF8000000

#define IS_WRITABLE(PIXEL) ((PIXEL) & 0xFE000000) -union WindowRegion { - struct { - uint8_t end; - uint8_t start; - }; - uint16_t packed; +struct WindowRegion { + uint8_t end; + uint8_t start; }; +DECL_BITFIELD(GBAWindowControl, uint8_t); +DECL_BIT(GBAWindowControl, Bg0Enable, 0); +DECL_BIT(GBAWindowControl, Bg1Enable, 1); +DECL_BIT(GBAWindowControl, Bg2Enable, 2); +DECL_BIT(GBAWindowControl, Bg3Enable, 3); +DECL_BIT(GBAWindowControl, ObjEnable, 4); +DECL_BIT(GBAWindowControl, BlendEnable, 5); + +DECL_BITFIELD(GBAMosaicControl, uint16_t); +DECL_BITS(GBAMosaicControl, BgH, 0, 4); +DECL_BITS(GBAMosaicControl, BgV, 4, 4); +DECL_BITS(GBAMosaicControl, ObjH, 8, 4); +DECL_BITS(GBAMosaicControl, ObjV, 12, 4); + struct WindowControl { - union { - struct { - unsigned bg0Enable : 1; - unsigned bg1Enable : 1; - unsigned bg2Enable : 1; - unsigned bg3Enable : 1; - unsigned objEnable : 1; - unsigned blendEnable : 1; - unsigned : 2; - }; - uint8_t packed; - }; + GBAWindowControl packed; int8_t priority; };

@@ -113,7 +110,7 @@

color_t* outputBuffer; unsigned outputBufferStride; - union GBARegisterDISPCNT dispcnt; + GBARegisterDISPCNT dispcnt; uint32_t row[VIDEO_HORIZONTAL_PIXELS]; uint32_t spriteLayer[VIDEO_HORIZONTAL_PIXELS];

@@ -132,19 +129,11 @@ uint16_t blda;

uint16_t bldb; uint16_t bldy; - union { - struct { - unsigned bgH : 4; - unsigned bgV : 4; - unsigned objH : 4; - unsigned objV : 4; - }; - uint16_t packed; - } mosaic; + GBAMosaicControl mosaic; struct WindowN { - union WindowRegion h; - union WindowRegion v; + struct WindowRegion h; + struct WindowRegion v; struct WindowControl control; } winN[2];
M src/platform/commandline.csrc/platform/commandline.c

@@ -201,13 +201,15 @@

void usage(const char* arg0, const char* extraOptions) { printf("usage: %s [option ...] file\n", arg0); puts("\nGeneric options:"); - puts(" -b, --bios FILE GBA BIOS file to use"); + puts(" -b, --bios FILE GBA BIOS file to use"); #ifdef USE_CLI_DEBUGGER - puts(" -d, --debug Use command-line debugger"); + puts(" -d, --debug Use command-line debugger"); #endif #ifdef USE_GDB_STUB - puts(" -g, --gdb Start GDB session (default port 2345)"); + puts(" -g, --gdb Start GDB session (default port 2345)"); #endif + puts(" -p, --patch Apply a specified patch file when running"); + puts(" -s, --frameskip N Skip every N frames"); if (extraOptions) { puts(extraOptions); }
M src/platform/perf-main.csrc/platform/perf-main.c

@@ -7,18 +7,22 @@ #include <fcntl.h>

#include <signal.h> #include <sys/time.h> -#define PERF_OPTIONS "NS:" +#define PERF_OPTIONS "F:NPS:" #define PERF_USAGE \ "\nBenchmark options:\n" \ - " -N Disable video rendering entirely" \ + " -F FRAMES Run for the specified number of FRAMES before exiting\n" \ + " -N Disable video rendering entirely\n" \ + " -P CSV output, useful for parsing\n" \ " -S SEC Run for SEC in-game seconds before exiting" struct PerfOpts { bool noVideo; - int duration; + bool csv; + unsigned duration; + unsigned frames; }; -static void _GBAPerfRunloop(struct GBAThread* context, int* frames); +static void _GBAPerfRunloop(struct GBAThread* context, int* frames, bool quiet); static void _GBAPerfShutdown(int signal); static bool _parsePerfOpts(struct SubParser* parser, int option, const char* arg);

@@ -30,7 +34,7 @@

struct GBAVideoSoftwareRenderer renderer; GBAVideoSoftwareRendererCreate(&renderer); - struct PerfOpts perfOpts = { false, 0 }; + struct PerfOpts perfOpts = { false, false, 0, 0 }; struct SubParser subparser = { .usage = PERF_USAGE, .parse = _parsePerfOpts,

@@ -58,16 +62,24 @@ context.renderer = &renderer.d;

} context.debugger = createDebugger(&opts); + char gameCode[5] = { 0 }; GBAMapOptionsToContext(&opts, &context); GBAThreadStart(&context); + GBAGetGameCode(context.gba, gameCode); - int frames = perfOpts.duration; - time_t start = time(0); - _GBAPerfRunloop(&context, &frames); - time_t end = time(0); - int duration = end - start; + int frames = perfOpts.frames; + if (!frames) { + frames = perfOpts.duration * 60; + } + struct timeval tv; + gettimeofday(&tv, 0); + uint64_t start = 1000000 * tv.tv_sec + tv.tv_usec; + _GBAPerfRunloop(&context, &frames, perfOpts.csv); + gettimeofday(&tv, 0); + uint64_t end = 1000000 * tv.tv_sec + tv.tv_usec; + uint64_t duration = end - start; GBAThreadJoin(&context); freeOptions(&opts);

@@ -75,12 +87,24 @@ free(context.debugger);

free(renderer.outputBuffer); - printf("%u frames in %i seconds: %g fps (%gx)\n", frames, duration, frames / (float) duration, frames / (duration * 60.f)); + float scaledFrames = frames * 1000000.f; + if (perfOpts.csv) { + puts("game_code,frames,duration,renderer"); + const char* rendererName; + if (perfOpts.noVideo) { + rendererName = "none"; + } else { + rendererName = "software"; + } + printf("%s,%i,%lli,%s\n", gameCode, frames, duration, rendererName); + } else { + printf("%u frames in %lli microseconds: %g fps (%gx)\n", frames, duration, scaledFrames / duration, scaledFrames / (duration * 60.f)); + } return 0; } -static void _GBAPerfRunloop(struct GBAThread* context, int* frames) { +static void _GBAPerfRunloop(struct GBAThread* context, int* frames, bool quiet) { struct timeval lastEcho; gettimeofday(&lastEcho, 0); int duration = *frames;

@@ -90,25 +114,29 @@ while (context->state < THREAD_EXITING) {

if (GBASyncWaitFrameStart(&context->sync, 0)) { ++*frames; ++lastFrames; - struct timeval currentTime; - long timeDiff; - gettimeofday(&currentTime, 0); - timeDiff = currentTime.tv_sec - lastEcho.tv_sec; - timeDiff *= 1000; - timeDiff += (currentTime.tv_usec - lastEcho.tv_usec) / 1000; - if (timeDiff >= 1000) { - printf("\033[2K\rCurrent FPS: %g (%gx)", lastFrames / (timeDiff / 1000.0f), lastFrames / (float) (60 * (timeDiff / 1000.0f))); - fflush(stdout); - lastEcho = currentTime; - lastFrames = 0; + if (!quiet) { + struct timeval currentTime; + long timeDiff; + gettimeofday(&currentTime, 0); + timeDiff = currentTime.tv_sec - lastEcho.tv_sec; + timeDiff *= 1000; + timeDiff += (currentTime.tv_usec - lastEcho.tv_usec) / 1000; + if (timeDiff >= 1000) { + printf("\033[2K\rCurrent FPS: %g (%gx)", lastFrames / (timeDiff / 1000.0f), lastFrames / (float) (60 * (timeDiff / 1000.0f))); + fflush(stdout); + lastEcho = currentTime; + lastFrames = 0; + } } } GBASyncWaitFrameEnd(&context->sync); - if (*frames == duration * 60) { + if (*frames == duration) { _GBAPerfShutdown(0); } } - printf("\033[2K\r"); + if (!quiet) { + printf("\033[2K\r"); + } } static void _GBAPerfShutdown(int signal) {

@@ -121,11 +149,17 @@

static bool _parsePerfOpts(struct SubParser* parser, int option, const char* arg) { struct PerfOpts* opts = parser->opts; switch (option) { + case 'F': + opts->frames = strtoul(arg, 0, 10); + return !errno; case 'N': opts->noVideo = true; return true; + case 'P': + opts->csv = true; + return true; case 'S': - opts->duration = strtol(arg, 0, 10); + opts->duration = strtoul(arg, 0, 10); return !errno; default: return false;
M src/platform/qt/CMakeLists.txtsrc/platform/qt/CMakeLists.txt

@@ -18,8 +18,20 @@

set(CMAKE_AUTOMOC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) -find_package(Qt5Widgets REQUIRED) -find_package(OpenGL REQUIRED) +set(REQUIRES "Qt5Widgets") + +if(APPLE) + find_package(OpenGL REQUIRED) +else() + list(APPEND REQUIRES "OpenGL") +endif() + +find_feature(BUILD_QT ${REQUIRES}) + +if(NOT BUILD_QT) + set(BUILD_QT OFF PARENT_SCOPE) + return() +endif() set(SOURCE_FILES AudioDevice.cpp AudioProcessor.cpp Display.cpp GameController.cpp Window.cpp)
M src/platform/sdl/CMakeLists.txtsrc/platform/sdl/CMakeLists.txt

@@ -13,7 +13,12 @@ endif()

endif() if(SDL_VERSION EQUAL "1.2" OR NOT SDL2_FOUND) - find_package(SDL 1.2 REQUIRED) + find_package(SDL 1.2) +endif() + +if (NOT SDL2_FOUND AND NOT SDL_FOUND) + set(BUILD_SDL OFF) + return() endif() file(GLOB PLATFORM_SRC ${CMAKE_SOURCE_DIR}/src/platform/sdl/sdl-*.c)
M src/platform/sdl/gl-main.csrc/platform/sdl/gl-main.c

@@ -121,13 +121,6 @@ if (SDL_Init(SDL_INIT_VIDEO) < 0) {

return 0; } - -#if SDL_VERSION_ATLEAST(2, 0, 0) - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); -#else - SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1); -#endif - #ifndef COLOR_16_BIT SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);

@@ -145,9 +138,11 @@

#if SDL_VERSION_ATLEAST(2, 0, 0) renderer->window = SDL_CreateWindow(PROJECT_NAME, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, renderer->viewportWidth, renderer->viewportHeight, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | (SDL_WINDOW_FULLSCREEN_DESKTOP * renderer->events.fullscreen)); SDL_GL_CreateContext(renderer->window); + SDL_GL_SetSwapInterval(1); SDL_GetWindowSize(renderer->window, &renderer->viewportWidth, &renderer->viewportHeight); renderer->events.window = renderer->window; #else + SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1); #ifdef COLOR_16_BIT SDL_SetVideoMode(renderer->viewportWidth, renderer->viewportHeight, 16, SDL_OPENGL); #else
M src/platform/sdl/sdl-events.csrc/platform/sdl/sdl-events.c

@@ -77,6 +77,7 @@ if (event->type == SDL_KEYDOWN && context->debugger) {

ARMDebuggerEnter(context->debugger, DEBUGGER_ENTER_MANUAL); } return; +#ifdef USE_PNG case SDLK_F12: if (event->type == SDL_KEYDOWN) { GBAThreadInterrupt(context);

@@ -84,6 +85,7 @@ GBAThreadTakeScreenshot(context);

GBAThreadContinue(context); } return; +#endif case SDLK_TAB: context->sync.audioWait = event->type != SDL_KEYDOWN; return;
M src/platform/sdl/sw-main.csrc/platform/sdl/sw-main.c

@@ -82,6 +82,9 @@

renderer.audio.samples = context.audioBuffers; GBASDLInitAudio(&renderer.audio); + renderer.events.bindings = &context.inputMap; + GBASDLInitEvents(&renderer.events); + #if SDL_VERSION_ATLEAST(2, 0, 0) renderer.events.fullscreen = graphicsOpts.fullscreen; renderer.window = SDL_CreateWindow(PROJECT_NAME, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, renderer.viewportWidth, renderer.viewportHeight, SDL_WINDOW_OPENGL | (SDL_WINDOW_FULLSCREEN_DESKTOP * renderer.events.fullscreen));

@@ -138,8 +141,6 @@ static int _GBASDLInit(struct SoftwareRenderer* renderer) {

if (SDL_Init(SDL_INIT_VIDEO) < 0) { return 0; } - - GBASDLInitEvents(&renderer->events); #if !SDL_VERSION_ATLEAST(2, 0, 0) #ifdef COLOR_16_BIT

@@ -203,6 +204,7 @@

static void _GBASDLStart(struct GBAThread* threadContext) { struct SoftwareRenderer* renderer = threadContext->userData; renderer->audio.audio = &threadContext->gba->audio; + renderer->audio.thread = threadContext; } static void _GBASDLClean(struct GBAThread* threadContext) {
M src/util/png-io.csrc/util/png-io.c

@@ -1,5 +1,7 @@

#include "util/png-io.h" +#ifdef USE_PNG + #include "vfs.h" static void _pngWrite(png_structp png, png_bytep buffer, png_size_t size) {

@@ -74,7 +76,7 @@ realName[4] = '\0';

if (setjmp(png_jmpbuf(png))) { return false; } - png_write_chunk(png, (png_const_bytep) realName, data, size); + png_write_chunk(png, (const png_bytep) realName, data, size); return true; }

@@ -110,7 +112,7 @@ if (setjmp(png_jmpbuf(png))) {

return false; } png_set_read_user_chunk_fn(png, context, handler); - png_set_keep_unknown_chunks(png, PNG_HANDLE_CHUNK_ALWAYS, (png_const_bytep) chunkName, 1); + png_set_keep_unknown_chunks(png, PNG_HANDLE_CHUNK_ALWAYS, (const png_bytep) chunkName, 1); return true; }

@@ -177,3 +179,5 @@

void PNGReadClose(png_structp png, png_infop info, png_infop end) { png_destroy_read_struct(&png, &info, &end); } + +#endif
M src/util/png-io.hsrc/util/png-io.h

@@ -3,6 +3,8 @@ #define PNG_IO_H

#include "common.h" +#ifdef USE_PNG + #include <png.h> struct VFile;

@@ -29,3 +31,5 @@ bool PNGReadFooter(png_structp png, png_infop end);

void PNGReadClose(png_structp png, png_infop info, png_infop end); #endif + +#endif