all repos — mgba @ f62ccde49d484c65b212ae86299f4720c9dc836d

mGBA Game Boy Advance Emulator

Merge branch 'master' into qt
Jeffrey Pfau jeffrey@endrift.com
Sun, 20 Jul 2014 16:28:12 -0700
commit

f62ccde49d484c65b212ae86299f4720c9dc836d

parent

c5d243fca282cf11d7f2c38010fce18361babdc2

90 files changed, 6136 insertions(+), 2169 deletions(-)

jump to
M .gitignore.gitignore

@@ -1,2 +1,3 @@

/build *~ +*.swp
M CMakeLists.txtCMakeLists.txt

@@ -1,8 +1,8 @@

cmake_minimum_required(VERSION 2.6) project(GBAc) set(BINARY_NAME gbac CACHE INTERNAL "Name of output binaries") -set(CMAKE_C_FLAGS_DEBUG "-g -Wall -Wextra --std=gnu99") -set(CMAKE_C_FLAGS_RELEASE "-O3 -Wall -Wextra --std=gnu99") +set(CMAKE_C_FLAGS_DEBUG "-g -Wall -Wextra -std=c99") +set(CMAKE_C_FLAGS_RELEASE "-O3 -Wall -Wextra -std=c99") set(USE_CLI_DEBUGGER ON CACHE BOOL "Whether or not to enable the CLI-mode ARM debugger") set(USE_GDB_STUB ON CACHE BOOL "Whether or not to enable the GDB stub ARM debugger") set(BUILD_QT ON CACHE BOOL "Build Qt frontend")

@@ -11,17 +11,31 @@ set(BUILD_PERF ON 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]) +file(GLOB VFS_SRC ${CMAKE_SOURCE_DIR}/src/util/vfs/*.c) file(GLOB RENDERER_SRC ${CMAKE_SOURCE_DIR}/src/gba/renderers/video-software.c) +set(UTIL_SRC ${UTIL_SRC};${CMAKE_SOURCE_DIR}/src/platform/commandline.c) source_group("ARM core" FILES ${ARM_SRC}) source_group("GBA board" FILES ${GBA_SRC} ${RENDERER_SRC}) -source_group("Utilities" FILES ${UTIL_SRC}) +source_group("Utilities" FILES ${UTIL_SRC} ${VFS_SRC}}) include_directories(${CMAKE_SOURCE_DIR}/src/arm) include_directories(${CMAKE_SOURCE_DIR}/src/gba) -include_directories(${CMAKE_SOURCE_DIR}/src/debugger) -include_directories(${CMAKE_SOURCE_DIR}/src/util) +include_directories(${CMAKE_SOURCE_DIR}/src) + +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() if(WIN32) add_definitions(-D_WIN32_WINNT=0x0600) + set(OS_LIB "${OS_LIB};Ws2_32") file(GLOB OS_SRC ${CMAKE_SOURCE_DIR}/src/platform/windows/*.c) source_group("Windows-specific code" FILES ${OS_SRC}) else()

@@ -40,8 +54,11 @@ endif()

set(DEBUGGER_SRC "${CMAKE_SOURCE_DIR}/src/debugger/debugger.c;${CMAKE_SOURCE_DIR}/src/debugger/memory-debugger.c") -if(USE_CLI_DEBUGGER) - set(DEBUGGER_SRC "${DEBUGGER_SRC};${CMAKE_SOURCE_DIR}/src/debugger/cli-debugger.c") +if(USE_CLI_DEBUGGER AND NOT WIN32) + # Win32 doesn't have a usable command line, nor libedit, so this is useless on Windows + 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") else() set(DEBUGGER_LIB "")

@@ -53,8 +70,8 @@ set(DEBUGGER_SRC "${DEBUGGER_SRC};${CMAKE_SOURCE_DIR}/src/debugger/gdb-stub.c")

endif() source_group("ARM debugger" FILES ${DEBUGGER_SRC}) -add_library(${BINARY_NAME} SHARED ${ARM_SRC} ${GBA_SRC} ${DEBUGGER_SRC} ${RENDERER_SRC} ${UTIL_SRC} ${OS_SRC}) -target_link_libraries(${BINARY_NAME} m ${DEBUGGER_LIB} ${OS_LIB}) +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}) if(BUILD_SDL) add_definitions(-DBUILD_SDL)
M src/arm/arm.csrc/arm/arm.c

@@ -4,8 +4,6 @@ #include "isa-arm.h"

#include "isa-inlines.h" #include "isa-thumb.h" -#include <limits.h> - static inline enum RegisterBank _ARMSelectBank(enum PrivilegeMode); void ARMSetPrivilegeMode(struct ARMCore* cpu, enum PrivilegeMode mode) {

@@ -67,18 +65,31 @@ }

} void ARMInit(struct ARMCore* cpu) { - cpu->memory = 0; - cpu->board = 0; + cpu->master->init(cpu, cpu->master); + int i; + for (i = 0; i < cpu->numComponents; ++i) { + cpu->components[i]->init(cpu, cpu->components[i]); + } } -void ARMAssociateMemory(struct ARMCore* cpu, struct ARMMemory* memory) { - cpu->memory = memory; +void ARMDeinit(struct ARMCore* cpu) { + if (cpu->master->deinit) { + cpu->master->deinit(cpu->master); + } + int i; + for (i = 0; i < cpu->numComponents; ++i) { + if (cpu->components[i]->deinit) { + cpu->components[i]->deinit(cpu->components[i]); + } + } } -void ARMAssociateBoard(struct ARMCore* cpu, struct ARMBoard* board) { - cpu->board = board; - board->cpu = cpu; +void ARMSetComponents(struct ARMCore* cpu, struct ARMComponent* master, int extra, struct ARMComponent** extras) { + cpu->master = master; + cpu->numComponents = extra; + cpu->components = extras; } + void ARMReset(struct ARMCore* cpu) { int i;

@@ -112,8 +123,9 @@ ARM_WRITE_PC;

cpu->cycles = 0; cpu->nextEvent = 0; + cpu->halted = 0; - cpu->board->reset(cpu->board); + cpu->irqh.reset(cpu); } void ARMRaiseIRQ(struct ARMCore* cpu) {

@@ -131,7 +143,7 @@ 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->memory->setActiveRegion(cpu->memory, cpu->gprs[ARM_PC]); + cpu->memory.setActiveRegion(cpu, cpu->gprs[ARM_PC]); _ARMSetMode(cpu, MODE_ARM); cpu->spsr = cpsr; cpu->cpsr.i = 1;

@@ -149,7 +161,7 @@ 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->memory->setActiveRegion(cpu->memory, cpu->gprs[ARM_PC]); + cpu->memory.setActiveRegion(cpu, cpu->gprs[ARM_PC]); _ARMSetMode(cpu, MODE_ARM); cpu->spsr = cpsr; cpu->cpsr.i = 1;

@@ -158,7 +170,7 @@

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); + LOAD_32(opcode, cpu->currentPC & cpu->memory.activeMask, cpu->memory.activeRegion); cpu->gprs[ARM_PC] += WORD_SIZE_ARM; int condition = opcode >> 28;

@@ -264,7 +276,7 @@ static inline void ThumbStep(struct ARMCore* cpu) {

cpu->currentPC = cpu->gprs[ARM_PC] - WORD_SIZE_THUMB; cpu->gprs[ARM_PC] += WORD_SIZE_THUMB; uint16_t opcode; - LOAD_16(opcode, cpu->currentPC & cpu->memory->activeMask, cpu->memory->activeRegion); + LOAD_16(opcode, cpu->currentPC & cpu->memory.activeMask, cpu->memory.activeRegion); ThumbInstruction instruction = _thumbTable[opcode >> 6]; instruction(cpu, opcode); }

@@ -276,6 +288,6 @@ } else {

ARMStep(cpu); } if (cpu->cycles >= cpu->nextEvent) { - cpu->board->processEvents(cpu->board); + cpu->irqh.processEvents(cpu); } }
M src/arm/arm.hsrc/arm/arm.h

@@ -1,19 +1,7 @@

#ifndef ARM_H #define ARM_H -#include <stdint.h> - -#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)) -#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] -#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 +#include "common.h" enum { ARM_SP = 13,

@@ -91,36 +79,43 @@ int32_t packed;

}; struct ARMMemory { - int32_t (*load32)(struct ARMMemory*, uint32_t address, int* cycleCounter); - int16_t (*load16)(struct ARMMemory*, uint32_t address, int* cycleCounter); - uint16_t (*loadU16)(struct ARMMemory*, uint32_t address, int* cycleCounter); - int8_t (*load8)(struct ARMMemory*, uint32_t address, int* cycleCounter); - uint8_t (*loadU8)(struct ARMMemory*, uint32_t address, int* cycleCounter); + int32_t (*load32)(struct ARMCore*, uint32_t address, int* cycleCounter); + int16_t (*load16)(struct ARMCore*, uint32_t address, int* cycleCounter); + uint16_t (*loadU16)(struct ARMCore*, uint32_t address, int* cycleCounter); + int8_t (*load8)(struct ARMCore*, uint32_t address, int* cycleCounter); + uint8_t (*loadU8)(struct ARMCore*, uint32_t address, int* cycleCounter); - void (*store32)(struct ARMMemory*, uint32_t address, int32_t value, int* cycleCounter); - void (*store16)(struct ARMMemory*, uint32_t address, int16_t value, int* cycleCounter); - void (*store8)(struct ARMMemory*, uint32_t address, int8_t value, int* cycleCounter); + void (*store32)(struct ARMCore*, uint32_t address, int32_t value, int* cycleCounter); + void (*store16)(struct ARMCore*, uint32_t address, int16_t value, int* cycleCounter); + void (*store8)(struct ARMCore*, uint32_t address, int8_t value, int* cycleCounter); uint32_t* activeRegion; uint32_t activeMask; - uint32_t activePrefetchCycles32; - uint32_t activePrefetchCycles16; + uint32_t activeSeqCycles32; + uint32_t activeSeqCycles16; uint32_t activeNonseqCycles32; uint32_t activeNonseqCycles16; - void (*setActiveRegion)(struct ARMMemory*, uint32_t address); - int (*waitMultiple)(struct ARMMemory*, uint32_t startAddress, int count); + uint32_t activeUncachedCycles32; + uint32_t activeUncachedCycles16; + void (*setActiveRegion)(struct ARMCore*, uint32_t address); + int (*waitMultiple)(struct ARMCore*, uint32_t startAddress, int count); }; -struct ARMBoard { - struct ARMCore* cpu; - void (*reset)(struct ARMBoard* board); - void (*processEvents)(struct ARMBoard* board); - void (*swi16)(struct ARMBoard* board, int immediate); - void (*swi32)(struct ARMBoard* board, int immediate); - void (*hitIllegal)(struct ARMBoard* board, uint32_t opcode); - void (*readCPSR)(struct ARMBoard* board); +struct ARMInterruptHandler { + void (*reset)(struct ARMCore* cpu); + void (*processEvents)(struct ARMCore* cpu); + void (*swi16)(struct ARMCore* cpu, int immediate); + void (*swi32)(struct ARMCore* cpu, int immediate); + void (*hitIllegal)(struct ARMCore* cpu, uint32_t opcode); + void (*readCPSR)(struct ARMCore* cpu); - void (*hitStub)(struct ARMBoard* board, uint32_t opcode); + void (*hitStub)(struct ARMCore* cpu, uint32_t opcode); +}; + +struct ARMComponent { + uint32_t id; + void (*init)(struct ARMCore* cpu, struct ARMComponent* component); + void (*deinit)(struct ARMComponent* component); }; struct ARMCore {

@@ -130,6 +125,7 @@ union PSR spsr;

int32_t cycles; int32_t nextEvent; + int halted; int32_t bankedRegisters[6][7]; int32_t bankedSPSRs[6];

@@ -141,16 +137,18 @@ uint32_t currentPC;

enum ExecutionMode executionMode; enum PrivilegeMode privilegeMode; - struct ARMMemory* memory; - struct ARMBoard* board; + struct ARMMemory memory; + struct ARMInterruptHandler irqh; - int64_t absoluteCycles; - int32_t lastCycles; + struct ARMComponent* master; + + int numComponents; + struct ARMComponent** components; }; void ARMInit(struct ARMCore* cpu); -void ARMAssociateMemory(struct ARMCore* cpu, struct ARMMemory* memory); -void ARMAssociateBoard(struct ARMCore* cpu, struct ARMBoard* board); +void ARMDeinit(struct ARMCore* cpu); +void ARMSetComponents(struct ARMCore* cpu, struct ARMComponent* master, int extra, struct ARMComponent** extras); void ARMReset(struct ARMCore* cpu); void ARMSetPrivilegeMode(struct ARMCore*, enum PrivilegeMode);
A src/arm/common.h

@@ -0,0 +1,31 @@

+#ifndef COMMON_H +#define COMMON_H + +#include <ctype.h> +#include <fcntl.h> +#include <limits.h> +#include <math.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#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)) +#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] +#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 + +#endif
A src/arm/decoder-arm.c

@@ -0,0 +1,454 @@

+#include "decoder.h" + +#include "decoder-inlines.h" +#include "emitter-arm.h" +#include "isa-inlines.h" + +#define ADDR_MODE_1_SHIFT(OP) \ + info->op3.reg = opcode & 0x0000000F; \ + info->op3.shifterOp = ARM_SHIFT_ ## OP; \ + info->op3.shifterImm = (opcode >> 7) & 0x1F; \ + info->operandFormat |= ARM_OPERAND_REGISTER_3 | \ + ARM_OPERAND_SHIFT_IMMEDIATE_3; + +#define ADDR_MODE_1_SHIFTR(OP) \ + info->op3.reg = opcode & 0x0000000F; \ + info->op3.shifterOp = ARM_SHIFT_ ## OP; \ + info->op3.shifterReg = (opcode >> 8) & 0xF; \ + ++info->iCycles; \ + info->operandFormat |= ARM_OPERAND_REGISTER_3 | \ + ARM_OPERAND_SHIFT_REGISTER_3; + +#define ADDR_MODE_1_LSL \ + ADDR_MODE_1_SHIFT(LSL) \ + if (!info->op3.shifterImm) { \ + info->operandFormat &= ~ARM_OPERAND_SHIFT_REGISTER_3; \ + info->op3.shifterOp = ARM_SHIFT_NONE; \ + } + +#define ADDR_MODE_1_LSR ADDR_MODE_1_SHIFT(LSR) +#define ADDR_MODE_1_ASR ADDR_MODE_1_SHIFT(ASR) +#define ADDR_MODE_1_ROR \ + ADDR_MODE_1_SHIFT(ROR) \ + if (!info->op3.shifterImm) { \ + info->op3.shifterOp = ARM_SHIFT_RRX; \ + } + +#define ADDR_MODE_1_LSLR ADDR_MODE_1_SHIFTR(LSL) +#define ADDR_MODE_1_LSRR ADDR_MODE_1_SHIFTR(LSR) +#define ADDR_MODE_1_ASRR ADDR_MODE_1_SHIFTR(ASR) +#define ADDR_MODE_1_RORR ADDR_MODE_1_SHIFTR(ROR) + +#define ADDR_MODE_1_IMM \ + int rotate = (opcode & 0x00000F00) >> 7; \ + int immediate = opcode & 0x000000FF; \ + info->op3.immediate = ARM_ROR(immediate, rotate); \ + info->operandFormat |= ARM_OPERAND_IMMEDIATE_3; + +#define ADDR_MODE_2_SHIFT(OP) \ + info->memory.format |= ARM_MEMORY_REGISTER_OFFSET | ARM_MEMORY_SHIFTED_OFFSET; \ + info->memory.offset.shifterOp = ARM_SHIFT_ ## OP; \ + info->memory.offset.shifterImm = (opcode >> 7) & 0x1F; \ + info->memory.offset.reg = opcode & 0x0000000F; + +#define ADDR_MODE_2_LSL \ + ADDR_MODE_2_SHIFT(LSL) \ + if (!info->memory.offset.shifterImm) { \ + info->memory.format &= ~ARM_MEMORY_SHIFTED_OFFSET; \ + info->memory.offset.shifterOp = ARM_SHIFT_NONE; \ + } + +#define ADDR_MODE_2_LSR ADDR_MODE_2_SHIFT(LSR) \ + if (!info->memory.offset.shifterImm) { \ + info->memory.offset.shifterImm = 32; \ + } + +#define ADDR_MODE_2_ASR ADDR_MODE_2_SHIFT(ASR) \ + if (!info->memory.offset.shifterImm) { \ + info->memory.offset.shifterImm = 32; \ + } + +#define ADDR_MODE_2_ROR \ + ADDR_MODE_2_SHIFT(ROR) \ + if (!info->memory.offset.shifterImm) { \ + info->memory.offset.shifterOp = ARM_SHIFT_RRX; \ + } + +#define ADDR_MODE_2_IMM \ + info->memory.format |= ARM_MEMORY_IMMEDIATE_OFFSET; \ + info->memory.offset.immediate = opcode & 0x00000FFF; + +#define ADDR_MODE_3_REG \ + info->memory.format |= ARM_MEMORY_REGISTER_OFFSET; \ + info->memory.offset.reg = opcode & 0x0000000F; + +#define ADDR_MODE_3_IMM \ + info->memory.format |= ARM_MEMORY_IMMEDIATE_OFFSET; \ + info->memory.offset.immediate = (opcode & 0x0000000F) | ((opcode & 0x00000F00) >> 4); + +#define DEFINE_DECODER_ARM(NAME, MNEMONIC, BODY) \ + static void _ARMDecode ## NAME (uint32_t opcode, struct ARMInstructionInfo* info) { \ + UNUSED(opcode); \ + info->mnemonic = ARM_MN_ ## MNEMONIC; \ + BODY; \ + } + +#define DEFINE_ALU_DECODER_EX_ARM(NAME, MNEMONIC, S, SHIFTER, OTHER_AFFECTED, SKIPPED) \ + DEFINE_DECODER_ARM(NAME, MNEMONIC, \ + info->op1.reg = (opcode >> 12) & 0xF; \ + info->op2.reg = (opcode >> 16) & 0xF; \ + info->operandFormat = ARM_OPERAND_REGISTER_1 | \ + OTHER_AFFECTED | \ + ARM_OPERAND_REGISTER_2; \ + info->affectsCPSR = S; \ + SHIFTER; \ + if (SKIPPED == 1) { \ + info->operandFormat &= ~ARM_OPERAND_1; \ + } else if (SKIPPED == 2) { \ + info->operandFormat &= ~ARM_OPERAND_2; \ + } \ + if (info->op1.reg == ARM_PC) { \ + info->branches = 1; \ + }) + +#define DEFINE_ALU_DECODER_ARM(NAME, SKIPPED) \ + DEFINE_ALU_DECODER_EX_ARM(NAME ## _LSL, NAME, 0, ADDR_MODE_1_LSL, ARM_OPERAND_AFFECTED_1, SKIPPED) \ + DEFINE_ALU_DECODER_EX_ARM(NAME ## S_LSL, NAME, 1, ADDR_MODE_1_LSL, ARM_OPERAND_AFFECTED_1, SKIPPED) \ + DEFINE_ALU_DECODER_EX_ARM(NAME ## _LSLR, NAME, 0, ADDR_MODE_1_LSLR, ARM_OPERAND_AFFECTED_1, SKIPPED) \ + DEFINE_ALU_DECODER_EX_ARM(NAME ## S_LSLR, NAME, 1, ADDR_MODE_1_LSLR, ARM_OPERAND_AFFECTED_1, SKIPPED) \ + DEFINE_ALU_DECODER_EX_ARM(NAME ## _LSR, NAME, 0, ADDR_MODE_1_LSR, ARM_OPERAND_AFFECTED_1, SKIPPED) \ + DEFINE_ALU_DECODER_EX_ARM(NAME ## S_LSR, NAME, 1, ADDR_MODE_1_LSR, ARM_OPERAND_AFFECTED_1, SKIPPED) \ + DEFINE_ALU_DECODER_EX_ARM(NAME ## _LSRR, NAME, 0, ADDR_MODE_1_LSRR, ARM_OPERAND_AFFECTED_1, SKIPPED) \ + DEFINE_ALU_DECODER_EX_ARM(NAME ## S_LSRR, NAME, 1, ADDR_MODE_1_LSRR, ARM_OPERAND_AFFECTED_1, SKIPPED) \ + DEFINE_ALU_DECODER_EX_ARM(NAME ## _ASR, NAME, 0, ADDR_MODE_1_ASR, ARM_OPERAND_AFFECTED_1, SKIPPED) \ + DEFINE_ALU_DECODER_EX_ARM(NAME ## S_ASR, NAME, 1, ADDR_MODE_1_ASR, ARM_OPERAND_AFFECTED_1, SKIPPED) \ + DEFINE_ALU_DECODER_EX_ARM(NAME ## _ASRR, NAME, 0, ADDR_MODE_1_ASRR, ARM_OPERAND_AFFECTED_1, SKIPPED) \ + DEFINE_ALU_DECODER_EX_ARM(NAME ## S_ASRR, NAME, 1, ADDR_MODE_1_ASRR, ARM_OPERAND_AFFECTED_1, SKIPPED) \ + DEFINE_ALU_DECODER_EX_ARM(NAME ## _ROR, NAME, 0, ADDR_MODE_1_ROR, ARM_OPERAND_AFFECTED_1, SKIPPED) \ + DEFINE_ALU_DECODER_EX_ARM(NAME ## S_ROR, NAME, 1, ADDR_MODE_1_ROR, ARM_OPERAND_AFFECTED_1, SKIPPED) \ + DEFINE_ALU_DECODER_EX_ARM(NAME ## _RORR, NAME, 0, ADDR_MODE_1_RORR, ARM_OPERAND_AFFECTED_1, SKIPPED) \ + DEFINE_ALU_DECODER_EX_ARM(NAME ## S_RORR, NAME, 1, ADDR_MODE_1_RORR, ARM_OPERAND_AFFECTED_1, SKIPPED) \ + DEFINE_ALU_DECODER_EX_ARM(NAME ## I, NAME, 0, ADDR_MODE_1_IMM, ARM_OPERAND_AFFECTED_1, SKIPPED) \ + DEFINE_ALU_DECODER_EX_ARM(NAME ## SI, NAME, 1, ADDR_MODE_1_IMM, ARM_OPERAND_AFFECTED_1, SKIPPED) + +#define DEFINE_ALU_DECODER_S_ONLY_ARM(NAME) \ + DEFINE_ALU_DECODER_EX_ARM(NAME ## _LSL, NAME, 1, ADDR_MODE_1_LSL, ARM_OPERAND_NONE, 1) \ + DEFINE_ALU_DECODER_EX_ARM(NAME ## _LSLR, NAME, 1, ADDR_MODE_1_LSLR, ARM_OPERAND_NONE, 1) \ + DEFINE_ALU_DECODER_EX_ARM(NAME ## _LSR, NAME, 1, ADDR_MODE_1_LSR, ARM_OPERAND_NONE, 1) \ + DEFINE_ALU_DECODER_EX_ARM(NAME ## _LSRR, NAME, 1, ADDR_MODE_1_LSRR, ARM_OPERAND_NONE, 1) \ + DEFINE_ALU_DECODER_EX_ARM(NAME ## _ASR, NAME, 1, ADDR_MODE_1_ASR, ARM_OPERAND_NONE, 1) \ + DEFINE_ALU_DECODER_EX_ARM(NAME ## _ASRR, NAME, 1, ADDR_MODE_1_ASRR, ARM_OPERAND_NONE, 1) \ + DEFINE_ALU_DECODER_EX_ARM(NAME ## _ROR, NAME, 1, ADDR_MODE_1_ROR, ARM_OPERAND_NONE, 1) \ + DEFINE_ALU_DECODER_EX_ARM(NAME ## _RORR, NAME, 1, ADDR_MODE_1_RORR, ARM_OPERAND_NONE, 1) \ + DEFINE_ALU_DECODER_EX_ARM(NAME ## I, NAME, 1, ADDR_MODE_1_IMM, ARM_OPERAND_NONE, 1) + +#define DEFINE_MULTIPLY_DECODER_EX_ARM(NAME, MNEMONIC, S, OTHER_AFFECTED) \ + DEFINE_DECODER_ARM(NAME, MNEMONIC, \ + info->op1.reg = (opcode >> 16) & 0xF; \ + info->op2.reg = opcode & 0xF; \ + info->op3.reg = (opcode >> 8) & 0xF; \ + info->op4.reg = (opcode >> 12) & 0xF; \ + info->operandFormat = ARM_OPERAND_REGISTER_1 | \ + ARM_OPERAND_AFFECTED_1 | \ + ARM_OPERAND_REGISTER_2 | \ + ARM_OPERAND_REGISTER_3 | \ + OTHER_AFFECTED; \ + info->affectsCPSR = S; \ + if (info->op1.reg == ARM_PC) { \ + info->branches = 1; \ + }) + +#define DEFINE_LONG_MULTIPLY_DECODER_EX_ARM(NAME, MNEMONIC, S) \ + DEFINE_DECODER_ARM(NAME, MNEMONIC, \ + info->op1.reg = (opcode >> 12) & 0xF; \ + info->op2.reg = (opcode >> 16) & 0xF; \ + info->op3.reg = opcode & 0xF; \ + info->op4.reg = (opcode >> 8) & 0xF; \ + info->operandFormat = ARM_OPERAND_REGISTER_1 | \ + ARM_OPERAND_AFFECTED_1 | \ + ARM_OPERAND_REGISTER_2 | \ + ARM_OPERAND_AFFECTED_2 | \ + ARM_OPERAND_REGISTER_3 | \ + ARM_OPERAND_REGISTER_4; \ + info->affectsCPSR = S; \ + if (info->op1.reg == ARM_PC) { \ + info->branches = 1; \ + }) + +#define DEFINE_MULTIPLY_DECODER_ARM(NAME, OTHER_AFFECTED) \ + DEFINE_MULTIPLY_DECODER_EX_ARM(NAME, NAME, 0, OTHER_AFFECTED) \ + DEFINE_MULTIPLY_DECODER_EX_ARM(NAME ## S, NAME, 1, OTHER_AFFECTED) + +#define DEFINE_LONG_MULTIPLY_DECODER_ARM(NAME) \ + DEFINE_LONG_MULTIPLY_DECODER_EX_ARM(NAME, NAME, 0) \ + DEFINE_LONG_MULTIPLY_DECODER_EX_ARM(NAME ## S, NAME, 1) + +#define DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME, MNEMONIC, ADDRESSING_MODE, ADDRESSING_DECODING, CYCLES, TYPE) \ + DEFINE_DECODER_ARM(NAME, MNEMONIC, \ + info->op1.reg = (opcode >> 12) & 0xF; \ + info->memory.baseReg = (opcode >> 16) & 0xF; \ + info->memory.width = TYPE; \ + info->operandFormat = ARM_OPERAND_REGISTER_1 | \ + ARM_OPERAND_AFFECTED_1 | /* TODO: Remove this for STR */ \ + ARM_OPERAND_MEMORY_2; \ + info->memory.format = ARM_MEMORY_REGISTER_BASE | ADDRESSING_MODE; \ + ADDRESSING_DECODING; \ + CYCLES;) + +#define DEFINE_LOAD_STORE_DECODER_SET_ARM(NAME, MNEMONIC, ADDRESSING_MODE, CYCLES, TYPE) \ + DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME, MNEMONIC, \ + ARM_MEMORY_POST_INCREMENT | \ + ARM_MEMORY_WRITEBACK | \ + ARM_MEMORY_OFFSET_SUBTRACT, \ + ADDRESSING_MODE, CYCLES, TYPE) \ + DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME ## U, MNEMONIC, \ + ARM_MEMORY_POST_INCREMENT | \ + ARM_MEMORY_WRITEBACK, \ + ADDRESSING_MODE, CYCLES, TYPE) \ + DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME ## P, MNEMONIC, \ + ARM_MEMORY_PRE_INCREMENT | \ + ARM_MEMORY_OFFSET_SUBTRACT, \ + ADDRESSING_MODE, CYCLES, TYPE) \ + DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME ## PW, MNEMONIC, \ + ARM_MEMORY_PRE_INCREMENT | \ + ARM_MEMORY_WRITEBACK | \ + ARM_MEMORY_OFFSET_SUBTRACT, \ + ADDRESSING_MODE, CYCLES, TYPE) \ + DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME ## PU, MNEMONIC, \ + ARM_MEMORY_PRE_INCREMENT, \ + ADDRESSING_MODE, CYCLES, TYPE) \ + DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME ## PUW, MNEMONIC, \ + ARM_MEMORY_PRE_INCREMENT | \ + ARM_MEMORY_WRITEBACK, \ + ADDRESSING_MODE, CYCLES, TYPE) + +#define DEFINE_LOAD_STORE_MODE_2_DECODER_ARM(NAME, MNEMONIC, CYCLES, TYPE) \ + DEFINE_LOAD_STORE_DECODER_SET_ARM(NAME ## _LSL_, MNEMONIC, ADDR_MODE_2_LSL, CYCLES, TYPE) \ + DEFINE_LOAD_STORE_DECODER_SET_ARM(NAME ## _LSR_, MNEMONIC, ADDR_MODE_2_LSR, CYCLES, TYPE) \ + DEFINE_LOAD_STORE_DECODER_SET_ARM(NAME ## _ASR_, MNEMONIC, ADDR_MODE_2_ASR, CYCLES, TYPE) \ + DEFINE_LOAD_STORE_DECODER_SET_ARM(NAME ## _ROR_, MNEMONIC, ADDR_MODE_2_ROR, CYCLES, TYPE) \ + DEFINE_LOAD_STORE_DECODER_SET_ARM(NAME ## I, MNEMONIC, ADDR_MODE_2_IMM, CYCLES, TYPE) + +#define DEFINE_LOAD_STORE_MODE_3_DECODER_ARM(NAME, MNEMONIC, CYCLES, TYPE) \ + DEFINE_LOAD_STORE_DECODER_SET_ARM(NAME, MNEMONIC, ADDR_MODE_3_REG, CYCLES, TYPE) \ + DEFINE_LOAD_STORE_DECODER_SET_ARM(NAME ## I, MNEMONIC, ADDR_MODE_3_IMM, CYCLES, TYPE) + +#define DEFINE_LOAD_STORE_T_DECODER_SET_ARM(NAME, MNEMONIC, ADDRESSING_MODE, CYCLES, TYPE) \ + DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME, MNEMONIC, \ + ARM_MEMORY_POST_INCREMENT | \ + ARM_MEMORY_OFFSET_SUBTRACT, \ + ADDRESSING_MODE, CYCLES, TYPE) \ + DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME ## U, MNEMONIC, \ + ARM_MEMORY_POST_INCREMENT, \ + ADDRESSING_MODE, CYCLES, TYPE) + +#define DEFINE_LOAD_STORE_T_DECODER_ARM(NAME, MNEMONIC, CYCLES, TYPE) \ + DEFINE_LOAD_STORE_T_DECODER_SET_ARM(NAME ## _LSL_, MNEMONIC, ADDR_MODE_2_LSL, CYCLES, TYPE) \ + DEFINE_LOAD_STORE_T_DECODER_SET_ARM(NAME ## _LSR_, MNEMONIC, ADDR_MODE_2_LSR, CYCLES, TYPE) \ + DEFINE_LOAD_STORE_T_DECODER_SET_ARM(NAME ## _ASR_, MNEMONIC, ADDR_MODE_2_ASR, CYCLES, TYPE) \ + DEFINE_LOAD_STORE_T_DECODER_SET_ARM(NAME ## _ROR_, MNEMONIC, ADDR_MODE_2_ROR, CYCLES, TYPE) \ + DEFINE_LOAD_STORE_T_DECODER_SET_ARM(NAME ## I, MNEMONIC, ADDR_MODE_2_IMM, CYCLES, TYPE) + +#define DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME, MNEMONIC, DIRECTION, WRITEBACK) \ + DEFINE_DECODER_ARM(NAME, MNEMONIC, \ + info->memory.baseReg = (opcode >> 16) & 0xF; \ + info->op1.immediate = opcode & 0x0000FFFF; \ + info->branches = info->op1.immediate & (1 << ARM_PC); \ + info->operandFormat = ARM_OPERAND_MEMORY_1; \ + info->memory.format = ARM_MEMORY_REGISTER_BASE | \ + ARM_MEMORY_WRITEBACK | \ + ARM_MEMORY_ ## DIRECTION;) + + +#define DEFINE_LOAD_STORE_MULTIPLE_DECODER_ARM(NAME) \ + DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## DA, NAME, DECREMENT_AFTER, 0) \ + DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## DAW, NAME, DECREMENT_AFTER, ARM_MEMORY_WRITEBACK) \ + DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## DB, NAME, DECREMENT_BEFORE, 0) \ + DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## DBW, NAME, DECREMENT_BEFORE, ARM_MEMORY_WRITEBACK) \ + DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## IA, NAME, INCREMENT_AFTER, 0) \ + DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## IAW, NAME, INCREMENT_AFTER, ARM_MEMORY_WRITEBACK) \ + DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## IB, NAME, INCREMENT_BEFORE, 0) \ + DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## IBW, NAME, INCREMENT_BEFORE, ARM_MEMORY_WRITEBACK) \ + DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## SDA, NAME, DECREMENT_AFTER, 0) \ + DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## SDAW, NAME, DECREMENT_AFTER, ARM_MEMORY_WRITEBACK) \ + DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## SDB, NAME, DECREMENT_BEFORE, 0) \ + DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## SDBW, NAME, DECREMENT_BEFORE, ARM_MEMORY_WRITEBACK) \ + DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## SIA, NAME, INCREMENT_AFTER, 0) \ + DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## SIAW, NAME, INCREMENT_AFTER, ARM_MEMORY_WRITEBACK) \ + DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## SIB, NAME, INCREMENT_BEFORE, 0) \ + DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## SIBW, NAME, INCREMENT_BEFORE, ARM_MEMORY_WRITEBACK) + +#define DEFINE_SWP_DECODER_ARM(NAME, TYPE) \ + DEFINE_DECODER_ARM(NAME, SWP, \ + info->memory.baseReg = (opcode >> 16) & 0xF; \ + info->op1.reg = (opcode >> 12) & 0xF; \ + info->op2.reg = opcode & 0xF; \ + info->operandFormat = ARM_OPERAND_REGISTER_1 | \ + ARM_OPERAND_AFFECTED_1 | \ + ARM_OPERAND_REGISTER_2 | \ + ARM_OPERAND_MEMORY_3; \ + info->memory.format = ARM_MEMORY_REGISTER_BASE; \ + info->memory.width = TYPE;) + +DEFINE_ALU_DECODER_ARM(ADD, 0) +DEFINE_ALU_DECODER_ARM(ADC, 0) +DEFINE_ALU_DECODER_ARM(AND, 0) +DEFINE_ALU_DECODER_ARM(BIC, 0) +DEFINE_ALU_DECODER_S_ONLY_ARM(CMN) +DEFINE_ALU_DECODER_S_ONLY_ARM(CMP) +DEFINE_ALU_DECODER_ARM(EOR, 0) +DEFINE_ALU_DECODER_ARM(MOV, 2) +DEFINE_ALU_DECODER_ARM(MVN, 2) +DEFINE_ALU_DECODER_ARM(ORR, 0) +DEFINE_ALU_DECODER_ARM(RSB, 0) +DEFINE_ALU_DECODER_ARM(RSC, 0) +DEFINE_ALU_DECODER_ARM(SBC, 0) +DEFINE_ALU_DECODER_ARM(SUB, 0) +DEFINE_ALU_DECODER_S_ONLY_ARM(TEQ) +DEFINE_ALU_DECODER_S_ONLY_ARM(TST) + +// TOOD: Estimate cycles +DEFINE_MULTIPLY_DECODER_ARM(MLA, ARM_OPERAND_REGISTER_4) +DEFINE_MULTIPLY_DECODER_ARM(MUL, ARM_OPERAND_NONE) + +DEFINE_LONG_MULTIPLY_DECODER_ARM(SMLAL) +DEFINE_LONG_MULTIPLY_DECODER_ARM(SMULL) +DEFINE_LONG_MULTIPLY_DECODER_ARM(UMLAL) +DEFINE_LONG_MULTIPLY_DECODER_ARM(UMULL) + +// Begin load/store definitions + +DEFINE_LOAD_STORE_MODE_2_DECODER_ARM(LDR, LDR, LOAD_CYCLES, ARM_ACCESS_WORD) +DEFINE_LOAD_STORE_MODE_2_DECODER_ARM(LDRB, LDR, LOAD_CYCLES, ARM_ACCESS_BYTE) +DEFINE_LOAD_STORE_MODE_3_DECODER_ARM(LDRH, LDR, LOAD_CYCLES, ARM_ACCESS_HALFWORD) +DEFINE_LOAD_STORE_MODE_3_DECODER_ARM(LDRSB, LDR, LOAD_CYCLES, ARM_ACCESS_SIGNED_BYTE) +DEFINE_LOAD_STORE_MODE_3_DECODER_ARM(LDRSH, LDR, LOAD_CYCLES, ARM_ACCESS_SIGNED_HALFWORD) +DEFINE_LOAD_STORE_MODE_2_DECODER_ARM(STR, STR, STORE_CYCLES, ARM_ACCESS_WORD) +DEFINE_LOAD_STORE_MODE_2_DECODER_ARM(STRB, STR, STORE_CYCLES, ARM_ACCESS_BYTE) +DEFINE_LOAD_STORE_MODE_3_DECODER_ARM(STRH, STR, STORE_CYCLES, ARM_ACCESS_HALFWORD) + +DEFINE_LOAD_STORE_T_DECODER_ARM(LDRBT, LDR, LOAD_CYCLES, ARM_ACCESS_TRANSLATED_BYTE) +DEFINE_LOAD_STORE_T_DECODER_ARM(LDRT, LDR, LOAD_CYCLES, ARM_ACCESS_TRANSLATED_WORD) +DEFINE_LOAD_STORE_T_DECODER_ARM(STRBT, STR, STORE_CYCLES, ARM_ACCESS_TRANSLATED_BYTE) +DEFINE_LOAD_STORE_T_DECODER_ARM(STRT, STR, STORE_CYCLES, ARM_ACCESS_TRANSLATED_WORD) + +DEFINE_LOAD_STORE_MULTIPLE_DECODER_ARM(LDM) +DEFINE_LOAD_STORE_MULTIPLE_DECODER_ARM(STM) + +DEFINE_SWP_DECODER_ARM(SWP, ARM_ACCESS_WORD) +DEFINE_SWP_DECODER_ARM(SWPB, ARM_ACCESS_BYTE) + +// End load/store definitions + +// Begin branch definitions + +DEFINE_DECODER_ARM(B, B, + int32_t offset = opcode << 8; + info->op1.immediate = offset >> 6; + info->operandFormat = ARM_OPERAND_IMMEDIATE_1; + info->branches = 1;) + +DEFINE_DECODER_ARM(BL, BL, + int32_t offset = opcode << 8; + info->op1.immediate = offset >> 6; + info->operandFormat = ARM_OPERAND_IMMEDIATE_1; + info->branches = 1;) + +DEFINE_DECODER_ARM(BX, BX, + info->op1.reg = opcode & 0x0000000F; + info->operandFormat = ARM_OPERAND_REGISTER_1; + info->branches = 1;) + +// End branch definitions + +// Begin coprocessor definitions + +DEFINE_DECODER_ARM(CDP, ILL, info->operandFormat = ARM_OPERAND_NONE;) +DEFINE_DECODER_ARM(LDC, ILL, info->operandFormat = ARM_OPERAND_NONE;) +DEFINE_DECODER_ARM(STC, ILL, info->operandFormat = ARM_OPERAND_NONE;) +DEFINE_DECODER_ARM(MCR, ILL, info->operandFormat = ARM_OPERAND_NONE;) +DEFINE_DECODER_ARM(MRC, ILL, info->operandFormat = ARM_OPERAND_NONE;) + +// Begin miscellaneous definitions + +DEFINE_DECODER_ARM(BKPT, BKPT, info->operandFormat = ARM_OPERAND_NONE;) // Not strictly in ARMv4T, but here for convenience +DEFINE_DECODER_ARM(ILL, ILL, info->operandFormat = ARM_OPERAND_NONE;) // Illegal opcode + +DEFINE_DECODER_ARM(MSR, MSR, + info->affectsCPSR = 1; + info->op1.reg = ARM_CPSR; + info->op2.reg = opcode & 0x0000000F; + info->operandFormat = ARM_OPERAND_REGISTER_1 | + ARM_OPERAND_AFFECTED_1 | + ARM_OPERAND_REGISTER_2;) + +DEFINE_DECODER_ARM(MSRR, MSR, + info->op1.reg = ARM_SPSR; + info->op2.reg = opcode & 0x0000000F; + info->operandFormat = ARM_OPERAND_REGISTER_1 | + ARM_OPERAND_AFFECTED_1 | + ARM_OPERAND_REGISTER_2;) + +DEFINE_DECODER_ARM(MRS, MRS, info->affectsCPSR = 1; + info->affectsCPSR = 1; + info->op1.reg = (opcode >> 12) & 0xF; + info->op2.reg = ARM_CPSR; + info->operandFormat = ARM_OPERAND_REGISTER_1 | + ARM_OPERAND_AFFECTED_1 | + ARM_OPERAND_REGISTER_2;) + +DEFINE_DECODER_ARM(MRSR, MRS, info->affectsCPSR = 1; + info->affectsCPSR = 1; + info->op1.reg = (opcode >> 12) & 0xF; + info->op2.reg = ARM_SPSR; + info->operandFormat = ARM_OPERAND_REGISTER_1 | + ARM_OPERAND_AFFECTED_1 | + ARM_OPERAND_REGISTER_2;) + +DEFINE_DECODER_ARM(MSRI, MSR, info->affectsCPSR = 1; + int rotate = (opcode & 0x00000F00) >> 7; + int32_t operand = ARM_ROR(opcode & 0x000000FF, rotate); + info->affectsCPSR = 1; + info->op1.reg = ARM_CPSR; + info->op2.immediate = operand; + info->operandFormat = ARM_OPERAND_REGISTER_1 | + ARM_OPERAND_AFFECTED_1 | + ARM_OPERAND_IMMEDIATE_2;) + +DEFINE_DECODER_ARM(MSRRI, MSR, info->affectsCPSR = 1; + int rotate = (opcode & 0x00000F00) >> 7; + int32_t operand = ARM_ROR(opcode & 0x000000FF, rotate); + info->affectsCPSR = 1; + info->op1.reg = ARM_SPSR; + info->op2.immediate = operand; + info->operandFormat = ARM_OPERAND_REGISTER_1 | + ARM_OPERAND_AFFECTED_1 | + ARM_OPERAND_IMMEDIATE_2;) + +DEFINE_DECODER_ARM(SWI, SWI, + info->op1.immediate = opcode & 0xFFFFFF; + info->operandFormat = ARM_OPERAND_IMMEDIATE_1; + info->traps = 1;) + +typedef void (*ARMDecoder)(uint32_t opcode, struct ARMInstructionInfo* info); + +static const ARMDecoder _armDecoderTable[0x1000] = { + DECLARE_ARM_EMITTER_BLOCK(_ARMDecode) +}; + +void ARMDecodeARM(uint32_t opcode, struct ARMInstructionInfo* info) { + info->execMode = MODE_ARM; + info->opcode = opcode; + info->branches = 0; + info->traps = 0; + info->affectsCPSR = 0; + info->condition = opcode >> 28; + info->sDataCycles = 0; + info->nDataCycles = 0; + info->sInstructionCycles = 1; + info->nInstructionCycles = 0; + info->iCycles = 0; + info->cCycles = 0; + ARMDecoder decoder = _armDecoderTable[((opcode >> 16) & 0xFF0) | ((opcode >> 4) & 0x00F)]; + decoder(opcode, info); +}
A src/arm/decoder-inlines.h

@@ -0,0 +1,20 @@

+#ifndef ARM_DECODER_INLINES_H +#define ARM_DECODER_INLINES_H + +#include "decoder.h" + +#include "arm.h" + +#include <stdio.h> +#include <string.h> + +#define LOAD_CYCLES \ + info->iCycles = 1; \ + info->nDataCycles = 1; + +#define STORE_CYCLES \ + info->sInstructionCycles = 0; \ + info->nInstructionCycles = 1; \ + info->nDataCycles = 1; + +#endif
A src/arm/decoder-thumb.c

@@ -0,0 +1,325 @@

+#include "decoder.h" + +#include "decoder-inlines.h" +#include "emitter-thumb.h" +#include "isa-inlines.h" + +#define DEFINE_THUMB_DECODER(NAME, MNEMONIC, BODY) \ + static void _ThumbDecode ## NAME (uint16_t opcode, struct ARMInstructionInfo* info) { \ + UNUSED(opcode); \ + info->mnemonic = ARM_MN_ ## MNEMONIC; \ + BODY; \ + } + +#define DEFINE_IMMEDIATE_5_DECODER_DATA_THUMB(NAME, IMMEDIATE, MNEMONIC, WIDTH) \ + DEFINE_THUMB_DECODER(NAME, MNEMONIC, \ + info->op3.immediate = IMMEDIATE; \ + info->op1.reg = opcode & 0x0007; \ + info->op2.reg = (opcode >> 3) & 0x0007; \ + info->affectsCPSR = 1; \ + info->operandFormat = ARM_OPERAND_REGISTER_1 | \ + ARM_OPERAND_AFFECTED_1 | \ + ARM_OPERAND_REGISTER_2 | \ + ARM_OPERAND_IMMEDIATE_3;) + +#define DEFINE_IMMEDIATE_5_DECODER_MEM_THUMB(NAME, IMMEDIATE, MNEMONIC, CYCLES, WIDTH) \ + DEFINE_THUMB_DECODER(NAME, MNEMONIC, \ + info->op1.reg = opcode & 0x0007; \ + info->memory.baseReg = (opcode >> 3) & 0x0007; \ + info->memory.offset.immediate = IMMEDIATE * WIDTH; \ + info->memory.width = (enum ARMMemoryAccessType) WIDTH; \ + info->operandFormat = ARM_OPERAND_REGISTER_1 | \ + ARM_OPERAND_AFFECTED_1 | \ + ARM_OPERAND_MEMORY_2; \ + info->memory.format = ARM_MEMORY_REGISTER_BASE | \ + ARM_MEMORY_IMMEDIATE_OFFSET; \ + CYCLES) + +#define DEFINE_IMMEDIATE_5_DECODER_MEM_LOAD_THUMB(NAME, IMMEDIATE, MNEMONIC, WIDTH) \ + DEFINE_IMMEDIATE_5_DECODER_MEM_THUMB(NAME, IMMEDIATE, MNEMONIC, LOAD_CYCLES, WIDTH) + +#define DEFINE_IMMEDIATE_5_DECODER_MEM_STORE_THUMB(NAME, IMMEDIATE, MNEMONIC, WIDTH) \ + DEFINE_IMMEDIATE_5_DECODER_MEM_THUMB(NAME, IMMEDIATE, MNEMONIC, STORE_CYCLES, WIDTH) + +#define DEFINE_IMMEDIATE_5_DECODER_THUMB(NAME, MNEMONIC, TYPE, WIDTH) \ + COUNT_CALL_5(DEFINE_IMMEDIATE_5_DECODER_ ## TYPE ## _THUMB, NAME ## _, MNEMONIC, WIDTH) + +DEFINE_IMMEDIATE_5_DECODER_THUMB(LSL1, LSL, DATA,) +DEFINE_IMMEDIATE_5_DECODER_THUMB(LSR1, LSR, DATA,) +DEFINE_IMMEDIATE_5_DECODER_THUMB(ASR1, ASR, DATA,) +DEFINE_IMMEDIATE_5_DECODER_THUMB(LDR1, LDR, MEM_LOAD, 4) +DEFINE_IMMEDIATE_5_DECODER_THUMB(LDRB1, LDR, MEM_LOAD, 1) +DEFINE_IMMEDIATE_5_DECODER_THUMB(LDRH1, LDR, MEM_LOAD, 2) +DEFINE_IMMEDIATE_5_DECODER_THUMB(STR1, STR, MEM_STORE, 4) +DEFINE_IMMEDIATE_5_DECODER_THUMB(STRB1, STR, MEM_STORE, 1) +DEFINE_IMMEDIATE_5_DECODER_THUMB(STRH1, STR, MEM_STORE, 2) + +#define DEFINE_DATA_FORM_1_DECODER_EX_THUMB(NAME, RM, MNEMONIC) \ + DEFINE_THUMB_DECODER(NAME, MNEMONIC, \ + info->op1.reg = opcode & 0x0007; \ + info->op2.reg = (opcode >> 3) & 0x0007; \ + info->op3.reg = RM; \ + info->affectsCPSR = 1; \ + info->operandFormat = ARM_OPERAND_REGISTER_1 | \ + ARM_OPERAND_AFFECTED_1 | \ + ARM_OPERAND_REGISTER_2 | \ + ARM_OPERAND_REGISTER_3;) + +#define DEFINE_DATA_FORM_1_DECODER_THUMB(NAME) \ + COUNT_CALL_3(DEFINE_DATA_FORM_1_DECODER_EX_THUMB, NAME ## 3_R, NAME) + +DEFINE_DATA_FORM_1_DECODER_THUMB(ADD) +DEFINE_DATA_FORM_1_DECODER_THUMB(SUB) + +#define DEFINE_DATA_FORM_2_DECODER_EX_THUMB(NAME, IMMEDIATE, MNEMONIC) \ + DEFINE_THUMB_DECODER(NAME, MNEMONIC, \ + info->op1.reg = opcode & 0x0007; \ + info->op2.reg = (opcode >> 3) & 0x0007; \ + info->op3.immediate = IMMEDIATE; \ + info->affectsCPSR = 1; \ + info->operandFormat = ARM_OPERAND_REGISTER_1 | \ + ARM_OPERAND_AFFECTED_1 | \ + ARM_OPERAND_REGISTER_2 | \ + ARM_OPERAND_IMMEDIATE_3;) + +#define DEFINE_DATA_FORM_2_DECODER_THUMB(NAME) \ + COUNT_CALL_3(DEFINE_DATA_FORM_2_DECODER_EX_THUMB, NAME ## 1_, NAME) + +DEFINE_DATA_FORM_2_DECODER_THUMB(ADD) +DEFINE_DATA_FORM_2_DECODER_THUMB(SUB) + +#define DEFINE_DATA_FORM_3_DECODER_EX_THUMB(NAME, RD, MNEMONIC, AFFECTED) \ + DEFINE_THUMB_DECODER(NAME, MNEMONIC, \ + info->op1.reg = RD; \ + info->op2.immediate = opcode & 0x00FF; \ + info->affectsCPSR = 1; \ + info->operandFormat = ARM_OPERAND_REGISTER_1 | \ + AFFECTED | \ + ARM_OPERAND_IMMEDIATE_2;) + +#define DEFINE_DATA_FORM_3_DECODER_THUMB(NAME, MNEMONIC, AFFECTED) \ + COUNT_CALL_3(DEFINE_DATA_FORM_3_DECODER_EX_THUMB, NAME ## _R, MNEMONIC, AFFECTED) + +DEFINE_DATA_FORM_3_DECODER_THUMB(ADD2, ADD, ARM_OPERAND_AFFECTED_1) +DEFINE_DATA_FORM_3_DECODER_THUMB(CMP1, CMP, ARM_OPERAND_NONE) +DEFINE_DATA_FORM_3_DECODER_THUMB(MOV1, MOV, ARM_OPERAND_AFFECTED_1) +DEFINE_DATA_FORM_3_DECODER_THUMB(SUB2, SUB, ARM_OPERAND_AFFECTED_1) + +#define DEFINE_DATA_FORM_5_DECODER_THUMB(NAME, MNEMONIC, AFFECTED) \ + DEFINE_THUMB_DECODER(NAME, MNEMONIC, \ + info->op1.reg = opcode & 0x0007; \ + info->op2.reg = (opcode >> 3) & 0x0007; \ + info->affectsCPSR = 1; \ + info->operandFormat = ARM_OPERAND_REGISTER_1 | \ + AFFECTED | \ + ARM_OPERAND_REGISTER_2;) + +DEFINE_DATA_FORM_5_DECODER_THUMB(AND, AND, ARM_OPERAND_AFFECTED_1) +DEFINE_DATA_FORM_5_DECODER_THUMB(EOR, EOR, ARM_OPERAND_AFFECTED_1) +DEFINE_DATA_FORM_5_DECODER_THUMB(LSL2, LSL, ARM_OPERAND_AFFECTED_1) +DEFINE_DATA_FORM_5_DECODER_THUMB(LSR2, LSR, ARM_OPERAND_AFFECTED_1) +DEFINE_DATA_FORM_5_DECODER_THUMB(ASR2, ASR, ARM_OPERAND_AFFECTED_1) +DEFINE_DATA_FORM_5_DECODER_THUMB(ADC, ADC, ARM_OPERAND_AFFECTED_1) +DEFINE_DATA_FORM_5_DECODER_THUMB(SBC, SBC, ARM_OPERAND_AFFECTED_1) +DEFINE_DATA_FORM_5_DECODER_THUMB(ROR, ROR, ARM_OPERAND_AFFECTED_1) +DEFINE_DATA_FORM_5_DECODER_THUMB(TST, TST, ARM_OPERAND_NONE) +DEFINE_DATA_FORM_5_DECODER_THUMB(NEG, NEG, ARM_OPERAND_AFFECTED_1) +DEFINE_DATA_FORM_5_DECODER_THUMB(CMP2, CMP, ARM_OPERAND_NONE) +DEFINE_DATA_FORM_5_DECODER_THUMB(CMN, CMN, ARM_OPERAND_NONE) +DEFINE_DATA_FORM_5_DECODER_THUMB(ORR, ORR, ARM_OPERAND_AFFECTED_1) +DEFINE_DATA_FORM_5_DECODER_THUMB(MUL, MUL, ARM_OPERAND_AFFECTED_1) +DEFINE_DATA_FORM_5_DECODER_THUMB(BIC, BIC, ARM_OPERAND_AFFECTED_1) +DEFINE_DATA_FORM_5_DECODER_THUMB(MVN, MVN, ARM_OPERAND_AFFECTED_1) + +#define DEFINE_DECODER_WITH_HIGH_EX_THUMB(NAME, H1, H2, MNEMONIC, AFFECTED, CPSR) \ + DEFINE_THUMB_DECODER(NAME, MNEMONIC, \ + info->op1.reg = (opcode & 0x0007) | H1; \ + info->op2.reg = ((opcode >> 3) & 0x0007) | H2; \ + info->branches = info->op1.reg == ARM_PC; \ + info->affectsCPSR = CPSR; \ + info->operandFormat = ARM_OPERAND_REGISTER_1 | \ + AFFECTED | \ + ARM_OPERAND_REGISTER_2;) + + +#define DEFINE_DECODER_WITH_HIGH_THUMB(NAME, MNEMONIC, AFFECTED, CPSR) \ + DEFINE_DECODER_WITH_HIGH_EX_THUMB(NAME ## 00, 0, 0, MNEMONIC, AFFECTED, CPSR) \ + DEFINE_DECODER_WITH_HIGH_EX_THUMB(NAME ## 01, 0, 8, MNEMONIC, AFFECTED, CPSR) \ + DEFINE_DECODER_WITH_HIGH_EX_THUMB(NAME ## 10, 8, 0, MNEMONIC, AFFECTED, CPSR) \ + DEFINE_DECODER_WITH_HIGH_EX_THUMB(NAME ## 11, 8, 8, MNEMONIC, AFFECTED, CPSR) + +DEFINE_DECODER_WITH_HIGH_THUMB(ADD4, ADD, ARM_OPERAND_AFFECTED_1, 0) +DEFINE_DECODER_WITH_HIGH_THUMB(CMP3, CMP, ARM_OPERAND_NONE, 1) +DEFINE_DECODER_WITH_HIGH_THUMB(MOV3, MOV, ARM_OPERAND_AFFECTED_1, 0) + +#define DEFINE_IMMEDIATE_WITH_REGISTER_DATA_THUMB(NAME, RD, MNEMONIC, REG) \ + DEFINE_THUMB_DECODER(NAME, MNEMONIC, \ + info->op1.reg = RD; \ + info->op2.reg = REG; \ + info->op3.immediate = (opcode & 0x00FF) << 2; \ + info->operandFormat = ARM_OPERAND_REGISTER_1 | \ + ARM_OPERAND_AFFECTED_1 | \ + ARM_OPERAND_REGISTER_2 | \ + ARM_OPERAND_IMMEDIATE_3;) + +#define DEFINE_IMMEDIATE_WITH_REGISTER_MEM_THUMB(NAME, RD, MNEMONIC, REG, CYCLES) \ + DEFINE_THUMB_DECODER(NAME, MNEMONIC, \ + info->op1.reg = RD; \ + info->memory.baseReg = REG; \ + info->memory.offset.immediate = (opcode & 0x00FF) << 2; \ + info->memory.width = ARM_ACCESS_WORD; \ + info->operandFormat = ARM_OPERAND_REGISTER_1 | \ + ARM_OPERAND_AFFECTED_1 | \ + ARM_OPERAND_MEMORY_2; \ + info->memory.format = ARM_MEMORY_REGISTER_BASE | \ + ARM_MEMORY_IMMEDIATE_OFFSET; \ + CYCLES;) + +#define DEFINE_IMMEDIATE_WITH_REGISTER_MEM_LOAD_THUMB(NAME, RD, MNEMONIC, REG) \ + DEFINE_IMMEDIATE_WITH_REGISTER_MEM_THUMB(NAME, RD, MNEMONIC, REG, LOAD_CYCLES) + +#define DEFINE_IMMEDIATE_WITH_REGISTER_MEM_STORE_THUMB(NAME, RD, MNEMONIC, REG) \ + DEFINE_IMMEDIATE_WITH_REGISTER_MEM_THUMB(NAME, RD, MNEMONIC, REG, STORE_CYCLES) + +#define DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(NAME, MNEMONIC, TYPE, REG) \ + COUNT_CALL_3(DEFINE_IMMEDIATE_WITH_REGISTER_ ## TYPE ## _THUMB, NAME ## _R, MNEMONIC, REG) + +DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(LDR3, LDR, MEM_LOAD, ARM_PC) +DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(LDR4, LDR, MEM_LOAD, ARM_SP) +DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(STR3, STR, MEM_STORE, ARM_SP) + +DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(ADD5, ADD, DATA, ARM_PC) +DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(ADD6, ADD, DATA, ARM_SP) + +#define DEFINE_LOAD_STORE_WITH_REGISTER_EX_THUMB(NAME, RM, MNEMONIC, CYCLES, TYPE) \ + DEFINE_THUMB_DECODER(NAME, MNEMONIC, \ + info->memory.offset.reg = RM; \ + info->op1.reg = opcode & 0x0007; \ + info->memory.baseReg = (opcode >> 3) & 0x0007; \ + info->memory.width = TYPE; \ + info->operandFormat = ARM_OPERAND_REGISTER_1 | \ + ARM_OPERAND_AFFECTED_1 | /* TODO: Remove this for STR */ \ + ARM_OPERAND_MEMORY_2; \ + info->memory.format = ARM_MEMORY_REGISTER_BASE | \ + ARM_MEMORY_REGISTER_OFFSET; \ + CYCLES;) + +#define DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(NAME, MNEMONIC, CYCLES, TYPE) \ + COUNT_CALL_3(DEFINE_LOAD_STORE_WITH_REGISTER_EX_THUMB, NAME ## _R, MNEMONIC, CYCLES, TYPE) + +DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDR2, LDR, LOAD_CYCLES, ARM_ACCESS_WORD) +DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRB2, LDR, LOAD_CYCLES, ARM_ACCESS_BYTE) +DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRH2, LDR, LOAD_CYCLES, ARM_ACCESS_HALFWORD) +DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRSB, LDR, LOAD_CYCLES, ARM_ACCESS_SIGNED_BYTE) +DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRSH, LDR, LOAD_CYCLES, ARM_ACCESS_SIGNED_HALFWORD) +DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(STR2, STR, STORE_CYCLES, ARM_ACCESS_WORD) +DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(STRB2, STR, STORE_CYCLES, ARM_ACCESS_BYTE) +DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(STRH2, STR, STORE_CYCLES, ARM_ACCESS_HALFWORD) + +// TODO: Estimate memory cycles +#define DEFINE_LOAD_STORE_MULTIPLE_EX_THUMB(NAME, RN, MNEMONIC, DIRECTION, ADDITIONAL_REG) \ + DEFINE_THUMB_DECODER(NAME, MNEMONIC, \ + info->memory.baseReg = RN; \ + info->op1.immediate = (opcode & 0xFF) | ADDITIONAL_REG; \ + info->branches = info->op1.immediate & (1 << ARM_PC); \ + info->operandFormat = ARM_OPERAND_MEMORY_1; \ + info->memory.format = ARM_MEMORY_REGISTER_BASE | \ + ARM_MEMORY_WRITEBACK | \ + DIRECTION;) + +#define DEFINE_LOAD_STORE_MULTIPLE_THUMB(NAME) \ + COUNT_CALL_3(DEFINE_LOAD_STORE_MULTIPLE_EX_THUMB, NAME ## IA_R, NAME, ARM_MEMORY_INCREMENT_AFTER, 0) + +DEFINE_LOAD_STORE_MULTIPLE_THUMB(LDM) +DEFINE_LOAD_STORE_MULTIPLE_THUMB(STM) + +#define DEFINE_CONDITIONAL_BRANCH_THUMB(COND) \ + DEFINE_THUMB_DECODER(B ## COND, B, \ + int8_t immediate = opcode; \ + info->op1.immediate = immediate << 1; \ + info->branches = 1; \ + info->condition = ARM_CONDITION_ ## COND; \ + info->operandFormat = ARM_OPERAND_IMMEDIATE_1;) + +DEFINE_CONDITIONAL_BRANCH_THUMB(EQ) +DEFINE_CONDITIONAL_BRANCH_THUMB(NE) +DEFINE_CONDITIONAL_BRANCH_THUMB(CS) +DEFINE_CONDITIONAL_BRANCH_THUMB(CC) +DEFINE_CONDITIONAL_BRANCH_THUMB(MI) +DEFINE_CONDITIONAL_BRANCH_THUMB(PL) +DEFINE_CONDITIONAL_BRANCH_THUMB(VS) +DEFINE_CONDITIONAL_BRANCH_THUMB(VC) +DEFINE_CONDITIONAL_BRANCH_THUMB(LS) +DEFINE_CONDITIONAL_BRANCH_THUMB(HI) +DEFINE_CONDITIONAL_BRANCH_THUMB(GE) +DEFINE_CONDITIONAL_BRANCH_THUMB(LT) +DEFINE_CONDITIONAL_BRANCH_THUMB(GT) +DEFINE_CONDITIONAL_BRANCH_THUMB(LE) + +#define DEFINE_SP_MODIFY_THUMB(NAME, MNEMONIC) \ + DEFINE_THUMB_DECODER(NAME, MNEMONIC, \ + info->op1.reg = ARM_SP; \ + info->op2.immediate = (opcode & 0x7F) << 2; \ + info->operandFormat = ARM_OPERAND_REGISTER_1 | \ + ARM_OPERAND_AFFECTED_1 | \ + ARM_OPERAND_IMMEDIATE_2;) + +DEFINE_SP_MODIFY_THUMB(ADD7, ADD) +DEFINE_SP_MODIFY_THUMB(SUB4, SUB) + +DEFINE_LOAD_STORE_MULTIPLE_EX_THUMB(POP, ARM_SP, LDM, ARM_MEMORY_INCREMENT_AFTER, 0) +DEFINE_LOAD_STORE_MULTIPLE_EX_THUMB(POPR, ARM_SP, LDM, ARM_MEMORY_INCREMENT_AFTER, 1 << ARM_PC) +DEFINE_LOAD_STORE_MULTIPLE_EX_THUMB(PUSH, ARM_SP, STM, ARM_MEMORY_DECREMENT_BEFORE, 0) +DEFINE_LOAD_STORE_MULTIPLE_EX_THUMB(PUSHR, ARM_SP, STM, ARM_MEMORY_DECREMENT_BEFORE, 1 << ARM_LR) + +DEFINE_THUMB_DECODER(ILL, ILL, info->traps = 1;) +DEFINE_THUMB_DECODER(BKPT, BKPT, info->traps = 1;) + +DEFINE_THUMB_DECODER(B, B, + int16_t immediate = (opcode & 0x07FF) << 5; + info->op1.immediate = (((int32_t) immediate) >> 4); + info->operandFormat = ARM_OPERAND_IMMEDIATE_1; + info->branches = 1;) + +DEFINE_THUMB_DECODER(BL1, BLH, + int16_t immediate = (opcode & 0x07FF) << 5; + info->op1.immediate = (((int32_t) immediate) << 7); + info->operandFormat = ARM_OPERAND_IMMEDIATE_1;) + +DEFINE_THUMB_DECODER(BL2, BL, + info->op1.immediate = (opcode & 0x07FF) << 1; + info->operandFormat = ARM_OPERAND_IMMEDIATE_1; + info->branches = 1;) + +DEFINE_THUMB_DECODER(BX, BX, + info->op1.reg = (opcode >> 3) & 0xF; + info->operandFormat = ARM_OPERAND_REGISTER_1; + info->branches = 1;) + +DEFINE_THUMB_DECODER(SWI, SWI, + info->op1.immediate = opcode & 0xFF; + info->operandFormat = ARM_OPERAND_IMMEDIATE_1; + info->traps = 1;) + +typedef void (*ThumbDecoder)(uint16_t opcode, struct ARMInstructionInfo* info); + +static const ThumbDecoder _thumbDecoderTable[0x400] = { + DECLARE_THUMB_EMITTER_BLOCK(_ThumbDecode) +}; + +void ARMDecodeThumb(uint16_t opcode, struct ARMInstructionInfo* info) { + info->execMode = MODE_THUMB; + info->opcode = opcode; + info->branches = 0; + info->traps = 0; + info->affectsCPSR = 0; + info->condition = ARM_CONDITION_AL; + info->sDataCycles = 0; + info->nDataCycles = 0; + info->sInstructionCycles = 1; + info->nInstructionCycles = 0; + info->iCycles = 0; + info->cCycles = 0; + ThumbDecoder decoder = _thumbDecoderTable[opcode >> 6]; + decoder(opcode, info); +}
A src/arm/decoder.c

@@ -0,0 +1,349 @@

+#include "decoder.h" + +#include "decoder-inlines.h" + +#define ADVANCE(AMOUNT) \ + if (AMOUNT > blen) { \ + buffer[blen - 1] = '\0'; \ + return total; \ + } \ + total += AMOUNT; \ + buffer += AMOUNT; \ + blen -= AMOUNT; + +static int _decodeRegister(int reg, char* buffer, int blen); +static int _decodeRegisterList(int list, char* buffer, int blen); +static int _decodePCRelative(uint32_t address, uint32_t pc, char* buffer, int blen); +static int _decodeMemory(struct ARMMemoryAccess memory, int pc, char* buffer, int blen); + +static const char* _armConditions[] = { + "eq", + "ne", + "cs", + "cc", + "mi", + "pl", + "vs", + "vc", + "hi", + "ls", + "ge", + "lt", + "gt", + "le", + "al", + "nv" +}; + +static int _decodeRegister(int reg, char* buffer, int blen) { + switch (reg) { + case ARM_SP: + strncpy(buffer, "sp", blen - 1); + return 2; + case ARM_LR: + strncpy(buffer, "lr", blen - 1); + return 2; + case ARM_PC: + strncpy(buffer, "pc", blen - 1); + return 2; + case ARM_CPSR: + strncpy(buffer, "cpsr", blen - 1); + return 4; + case ARM_SPSR: + strncpy(buffer, "spsr", blen - 1); + return 4; + default: + return snprintf(buffer, blen - 1, "r%i", reg); + } +} + +static int _decodeRegisterList(int list, char* buffer, int blen) { + if (blen <= 0) { + return 0; + } + int total = 0; + strncpy(buffer, "{", blen - 1); + ADVANCE(1); + int i; + int start = -1; + int end = -1; + int written; + for (i = 0; i <= ARM_PC; ++i) { + if (list & 1) { + if (start < 0) { + start = i; + end = i; + } else if (end + 1 == i) { + end = i; + } else { + if (end > start) { + written = _decodeRegister(start, buffer, blen); + ADVANCE(written); + strncpy(buffer, "-", blen - 1); + ADVANCE(1); + } + written = _decodeRegister(end, buffer, blen); + ADVANCE(written); + strncpy(buffer, ",", blen - 1); + ADVANCE(1); + start = i; + end = i; + } + } + list >>= 1; + } + if (start >= 0) { + if (end > start) { + written = _decodeRegister(start, buffer, blen); + ADVANCE(written); + strncpy(buffer, "-", blen - 1); + ADVANCE(1); + } + written = _decodeRegister(end, buffer, blen); + ADVANCE(written); + } + strncpy(buffer, "}", blen - 1); + ADVANCE(1); + return total; +} + +static int _decodePCRelative(uint32_t address, uint32_t pc, char* buffer, int blen) { + return snprintf(buffer, blen - 1, "$%08X", address + pc); +} + +static int _decodeMemory(struct ARMMemoryAccess memory, int pc, char* buffer, int blen) { + if (blen <= 1) { + return 0; + } + int total = 0; + strncpy(buffer, "[", blen - 1); + ADVANCE(1); + int written; + if (memory.format & ARM_MEMORY_REGISTER_BASE) { + if (memory.baseReg == ARM_PC && memory.format & ARM_MEMORY_IMMEDIATE_OFFSET) { + written = _decodePCRelative(memory.offset.immediate, pc, buffer, blen); + ADVANCE(written); + } else { + written = _decodeRegister(memory.baseReg, buffer, blen); + ADVANCE(written); + if (memory.format & (ARM_MEMORY_REGISTER_OFFSET | ARM_MEMORY_IMMEDIATE_OFFSET) && !(memory.format & ARM_MEMORY_POST_INCREMENT)) { + strncpy(buffer, ", ", blen - 1); + ADVANCE(2); + } + } + } + if (memory.format & ARM_MEMORY_POST_INCREMENT) { + strncpy(buffer, "], ", blen - 1); + ADVANCE(3); + } + if (memory.format & ARM_MEMORY_IMMEDIATE_OFFSET && memory.baseReg != ARM_PC) { + if (memory.format & ARM_MEMORY_OFFSET_SUBTRACT) { + written = snprintf(buffer, blen - 1, "#-%i", memory.offset.immediate); + ADVANCE(written); + } else { + written = snprintf(buffer, blen - 1, "#%i", memory.offset.immediate); + ADVANCE(written); + } + } else if (memory.format & ARM_MEMORY_REGISTER_OFFSET) { + if (memory.format & ARM_MEMORY_OFFSET_SUBTRACT) { + strncpy(buffer, "-", blen - 1); + ADVANCE(1); + } + written = _decodeRegister(memory.offset.reg, buffer, blen); + ADVANCE(written); + } + // TODO: shifted registers + + if (!(memory.format & ARM_MEMORY_POST_INCREMENT)) { + strncpy(buffer, "]", blen - 1); + ADVANCE(1); + } + if ((memory.format & (ARM_MEMORY_PRE_INCREMENT | ARM_MEMORY_WRITEBACK)) == (ARM_MEMORY_PRE_INCREMENT | ARM_MEMORY_WRITEBACK)) { + strncpy(buffer, "!", blen - 1); + ADVANCE(1); + } + return total; +} + +static const char* _armMnemonicStrings[] = { + "ill", + "adc", + "add", + "and", + "asr", + "b", + "bic", + "bkpt", + "bl", + "blh", + "bx", + "cmn", + "cmp", + "eor", + "ldm", + "ldr", + "lsl", + "lsr", + "mla", + "mov", + "mrs", + "msr", + "mul", + "mvn", + "neg", + "orr", + "ror", + "rsb", + "rsc", + "sbc", + "smlal", + "smull", + "stm", + "str", + "sub", + "swi", + "swp", + "teq", + "tst", + "umlal", + "umull", + + "ill" +}; + +static const char* _armDirectionStrings[] = { + "da", + "ia", + "db", + "da" +}; + +static const char* _armAccessTypeStrings[] = { + "", + "b", + "h", + "", + "", + "", + "", + "", + "", + "sb", + "sh", + "" + "" +}; + +int ARMDisassemble(struct ARMInstructionInfo* info, uint32_t pc, char* buffer, int blen) { + const char* mnemonic = _armMnemonicStrings[info->mnemonic]; + int written; + int total = 0; + const char* cond = ""; + if (info->condition != ARM_CONDITION_AL && info->condition < ARM_CONDITION_NV) { + cond = _armConditions[info->condition]; + } + const char* flags = ""; + switch (info->mnemonic) { + case ARM_MN_LDM: + case ARM_MN_STM: + flags = _armDirectionStrings[MEMORY_FORMAT_TO_DIRECTION(info->memory.format)]; + break; + case ARM_MN_LDR: + case ARM_MN_STR: + case ARM_MN_SWP: + flags = _armAccessTypeStrings[info->memory.width]; + break; + case ARM_MN_ADD: + case ARM_MN_ADC: + case ARM_MN_AND: + case ARM_MN_BIC: + case ARM_MN_EOR: + case ARM_MN_MOV: + case ARM_MN_MVN: + case ARM_MN_ORR: + case ARM_MN_RSB: + case ARM_MN_RSC: + case ARM_MN_SBC: + case ARM_MN_SUB: + if (info->affectsCPSR && info->execMode == MODE_ARM) { + flags = "s"; + } + break; + default: + break; + } + written = snprintf(buffer, blen - 1, "%s%s%s ", mnemonic, cond, flags); + ADVANCE(written); + + switch (info->mnemonic) { + case ARM_MN_LDM: + case ARM_MN_STM: + written = _decodeRegister(info->memory.baseReg, buffer, blen); + ADVANCE(written); + if (info->memory.format & ARM_MEMORY_WRITEBACK) { + strncpy(buffer, "!", blen - 1); + ADVANCE(1); + } + strncpy(buffer, ", ", blen - 1); + ADVANCE(2); + written = _decodeRegisterList(info->op1.immediate, buffer, blen); + ADVANCE(written); + break; + case ARM_MN_B: + written = _decodePCRelative(info->op1.immediate, pc, buffer, blen); + ADVANCE(written); + break; + default: + if (info->operandFormat & ARM_OPERAND_IMMEDIATE_1) { + written = snprintf(buffer, blen - 1, "#%i", info->op1.immediate); + ADVANCE(written); + } else if (info->operandFormat & ARM_OPERAND_MEMORY_1) { + written = _decodeMemory(info->memory, pc, buffer, blen); + ADVANCE(written); + } else if (info->operandFormat & ARM_OPERAND_REGISTER_1) { + written = _decodeRegister(info->op1.reg, buffer, blen); + ADVANCE(written); + } + if (info->operandFormat & ARM_OPERAND_2) { + strncpy(buffer, ", ", blen); + ADVANCE(2); + } + if (info->operandFormat & ARM_OPERAND_IMMEDIATE_2) { + written = snprintf(buffer, blen - 1, "#%i", info->op2.immediate); + ADVANCE(written); + } else if (info->operandFormat & ARM_OPERAND_MEMORY_2) { + written = _decodeMemory(info->memory, pc, buffer, blen); + ADVANCE(written); + } else if (info->operandFormat & ARM_OPERAND_REGISTER_2) { + written = _decodeRegister(info->op2.reg, buffer, blen); + ADVANCE(written); + } + if (info->operandFormat & ARM_OPERAND_3) { + strncpy(buffer, ", ", blen - 1); + ADVANCE(2); + } + if (info->operandFormat & ARM_OPERAND_IMMEDIATE_3) { + written = snprintf(buffer, blen - 1, "#%i", info->op3.immediate); + ADVANCE(written); + } else if (info->operandFormat & ARM_OPERAND_MEMORY_3) { + written = _decodeMemory(info->memory, pc, buffer, blen); + ADVANCE(written); + } else if (info->operandFormat & ARM_OPERAND_REGISTER_3) { + written = _decodeRegister(info->op3.reg, buffer, blen); + ADVANCE(written); + } + if (info->operandFormat & ARM_OPERAND_IMMEDIATE_4) { + written = snprintf(buffer, blen - 1, "#%i", info->op4.immediate); + ADVANCE(written); + } else if (info->operandFormat & ARM_OPERAND_MEMORY_4) { + written = _decodeMemory(info->memory, pc, buffer, blen); + ADVANCE(written); + } else if (info->operandFormat & ARM_OPERAND_REGISTER_4) { + written = _decodeRegister(info->op4.reg, buffer, blen); + ADVANCE(written); + } + break; + } + buffer[blen - 1] = '\0'; + return total; +}
A src/arm/decoder.h

@@ -0,0 +1,195 @@

+#ifndef ARM_DECODER_H +#define ARM_DECODER_H + +#include "arm.h" + +// Bit 0: a register is involved with this operand +// Bit 1: an immediate is invovled with this operand +// Bit 2: a memory access is invovled with this operand +// Bit 3: the destination of this operand is affected by this opcode +// Bit 4: this operand is shifted by a register +// Bit 5: this operand is shifted by an immediate +#define ARM_OPERAND_NONE 0x00000000 +#define ARM_OPERAND_REGISTER_1 0x00000001 +#define ARM_OPERAND_IMMEDIATE_1 0x00000002 +#define ARM_OPERAND_MEMORY_1 0x00000004 +#define ARM_OPERAND_AFFECTED_1 0x00000008 +#define ARM_OPERAND_SHIFT_REGISTER_1 0x00000010 +#define ARM_OPERAND_SHIFT_IMMEDIATE_1 0x00000020 +#define ARM_OPERAND_1 0x000000FF + +#define ARM_OPERAND_REGISTER_2 0x00000100 +#define ARM_OPERAND_IMMEDIATE_2 0x00000200 +#define ARM_OPERAND_MEMORY_2 0x00000400 +#define ARM_OPERAND_AFFECTED_2 0x00000800 +#define ARM_OPERAND_SHIFT_REGISTER_2 0x00001000 +#define ARM_OPERAND_SHIFT_IMMEDIATE_2 0x00002000 +#define ARM_OPERAND_2 0x0000FF00 + +#define ARM_OPERAND_REGISTER_3 0x00010000 +#define ARM_OPERAND_IMMEDIATE_3 0x00020000 +#define ARM_OPERAND_MEMORY_3 0x00040000 +#define ARM_OPERAND_AFFECTED_3 0x00080000 +#define ARM_OPERAND_SHIFT_REGISTER_3 0x00100000 +#define ARM_OPERAND_SHIFT_IMMEDIATE_3 0x00200000 +#define ARM_OPERAND_3 0x00FF0000 + +#define ARM_OPERAND_REGISTER_4 0x01000000 +#define ARM_OPERAND_IMMEDIATE_4 0x02000000 +#define ARM_OPERAND_MEMORY_4 0x04000000 +#define ARM_OPERAND_AFFECTED_4 0x08000000 +#define ARM_OPERAND_SHIFT_REGISTER_4 0x10000000 +#define ARM_OPERAND_SHIFT_IMMEDIATE_4 0x20000000 +#define ARM_OPERAND_4 0xFF000000 + + +#define ARM_MEMORY_REGISTER_BASE 0x0001 +#define ARM_MEMORY_IMMEDIATE_OFFSET 0x0002 +#define ARM_MEMORY_REGISTER_OFFSET 0x0004 +#define ARM_MEMORY_SHIFTED_OFFSET 0x0008 +#define ARM_MEMORY_PRE_INCREMENT 0x0010 +#define ARM_MEMORY_POST_INCREMENT 0x0020 +#define ARM_MEMORY_OFFSET_SUBTRACT 0x0040 +#define ARM_MEMORY_WRITEBACK 0x0080 +#define ARM_MEMORY_DECREMENT_AFTER 0x0000 +#define ARM_MEMORY_INCREMENT_AFTER 0x0100 +#define ARM_MEMORY_DECREMENT_BEFORE 0x0200 +#define ARM_MEMORY_INCREMENT_BEFORE 0x0300 + +#define MEMORY_FORMAT_TO_DIRECTION(F) (((F) >> 8) & 0x7) + +enum ARMCondition { + ARM_CONDITION_EQ = 0x0, + ARM_CONDITION_NE = 0x1, + ARM_CONDITION_CS = 0x2, + ARM_CONDITION_CC = 0x3, + ARM_CONDITION_MI = 0x4, + ARM_CONDITION_PL = 0x5, + ARM_CONDITION_VS = 0x6, + ARM_CONDITION_VC = 0x7, + ARM_CONDITION_HI = 0x8, + ARM_CONDITION_LS = 0x9, + ARM_CONDITION_GE = 0xA, + ARM_CONDITION_LT = 0xB, + ARM_CONDITION_GT = 0xC, + ARM_CONDITION_LE = 0xD, + ARM_CONDITION_AL = 0xE, + ARM_CONDITION_NV = 0xF +}; + +enum ARMShifterOperation { + ARM_SHIFT_NONE = 0, + ARM_SHIFT_LSL, + ARM_SHIFT_LSR, + ARM_SHIFT_ASR, + ARM_SHIFT_ROR, + ARM_SHIFT_RRX +}; + +union ARMOperand { + struct { + uint8_t reg; + enum ARMShifterOperation shifterOp; + union { + uint8_t shifterReg; + uint8_t shifterImm; + }; + }; + int32_t immediate; +}; + +enum ARMMemoryAccessType { + ARM_ACCESS_WORD = 4, + ARM_ACCESS_HALFWORD = 2, + ARM_ACCESS_SIGNED_HALFWORD = 10, + ARM_ACCESS_BYTE = 1, + ARM_ACCESS_SIGNED_BYTE = 9, + ARM_ACCESS_TRANSLATED_WORD = 20, + ARM_ACCESS_TRANSLATED_BYTE = 17 +}; + +struct ARMMemoryAccess { + uint8_t baseReg; + uint16_t format; + union ARMOperand offset; + enum ARMMemoryAccessType width; +}; + +enum ARMMnemonic { + ARM_MN_ILL = 0, + ARM_MN_ADC, + ARM_MN_ADD, + ARM_MN_AND, + ARM_MN_ASR, + ARM_MN_B, + ARM_MN_BIC, + ARM_MN_BKPT, + ARM_MN_BL, + ARM_MN_BLH, + ARM_MN_BX, + ARM_MN_CMN, + ARM_MN_CMP, + ARM_MN_EOR, + ARM_MN_LDM, + ARM_MN_LDR, + ARM_MN_LSL, + ARM_MN_LSR, + ARM_MN_MLA, + ARM_MN_MOV, + ARM_MN_MRS, + ARM_MN_MSR, + ARM_MN_MUL, + ARM_MN_MVN, + ARM_MN_NEG, + ARM_MN_ORR, + ARM_MN_ROR, + ARM_MN_RSB, + ARM_MN_RSC, + ARM_MN_SBC, + ARM_MN_SMLAL, + ARM_MN_SMULL, + ARM_MN_STM, + ARM_MN_STR, + ARM_MN_SUB, + ARM_MN_SWI, + ARM_MN_SWP, + ARM_MN_TEQ, + ARM_MN_TST, + ARM_MN_UMLAL, + ARM_MN_UMULL, + + ARM_MN_MAX +}; + +enum { + ARM_CPSR = 16, + 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; +}; + +void ARMDecodeARM(uint32_t opcode, struct ARMInstructionInfo* info); +void ARMDecodeThumb(uint16_t opcode, struct ARMInstructionInfo* info); +int ARMDisassemble(struct ARMInstructionInfo* info, uint32_t pc, char* buffer, int blen); + +#endif
A src/arm/emitter-arm.h

@@ -0,0 +1,330 @@

+#ifndef EMITTER_ARM_H +#define EMITTER_ARM_H + +#include "emitter-inlines.h" + +#define DECLARE_INSTRUCTION_ARM(EMITTER, NAME) \ + EMITTER ## NAME + +#define DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, ALU) \ + DO_8(DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## I)), \ + DO_8(DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## I)) + +#define DECLARE_ARM_ALU_BLOCK(EMITTER, ALU, EX1, EX2, EX3, EX4) \ + DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## _LSL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## _LSLR), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## _LSR), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## _LSRR), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## _ASR), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## _ASRR), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## _ROR), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## _RORR), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## _LSL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, EX1), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## _LSR), \ + DECLARE_INSTRUCTION_ARM(EMITTER, EX2), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## _ASR), \ + DECLARE_INSTRUCTION_ARM(EMITTER, EX3), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## _ROR), \ + DECLARE_INSTRUCTION_ARM(EMITTER, EX4) + +#define DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, NAME, P, U, W) \ + DO_8(DECLARE_INSTRUCTION_ARM(EMITTER, NAME ## I ## P ## U ## W)), \ + DO_8(DECLARE_INSTRUCTION_ARM(EMITTER, NAME ## I ## P ## U ## W)) + +#define DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, NAME, P, U, W) \ + DECLARE_INSTRUCTION_ARM(EMITTER, NAME ## _LSL_ ## P ## U ## W), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, NAME ## _LSR_ ## P ## U ## W), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, NAME ## _ASR_ ## P ## U ## W), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, NAME ## _ROR_ ## P ## U ## W), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, NAME ## _LSL_ ## P ## U ## W), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, NAME ## _LSR_ ## P ## U ## W), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, NAME ## _ASR_ ## P ## U ## W), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, NAME ## _ROR_ ## P ## U ## W), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL) + +#define DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, NAME, MODE, W) \ + DO_8(DECLARE_INSTRUCTION_ARM(EMITTER, NAME ## MODE ## W)), \ + DO_8(DECLARE_INSTRUCTION_ARM(EMITTER, NAME ## MODE ## W)) + +#define DECLARE_ARM_BRANCH_BLOCK(EMITTER, NAME) \ + DO_256(DECLARE_INSTRUCTION_ARM(EMITTER, NAME)) + +// TODO: Support coprocessors +#define DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, NAME, P, U, N, W) \ + DO_8(DECLARE_INSTRUCTION_ARM(EMITTER, NAME)), \ + DO_8(DECLARE_INSTRUCTION_ARM(EMITTER, NAME)) + +#define DECLARE_ARM_COPROCESSOR_BLOCK(EMITTER, NAME1, NAME2) \ + DO_8(DO_8(DO_INTERLACE(DECLARE_INSTRUCTION_ARM(EMITTER, NAME1), DECLARE_INSTRUCTION_ARM(EMITTER, NAME2)))) + +#define DECLARE_ARM_SWI_BLOCK(EMITTER) \ + DO_256(DECLARE_INSTRUCTION_ARM(EMITTER, SWI)) + +#define DECLARE_ARM_EMITTER_BLOCK(EMITTER) \ + DECLARE_ARM_ALU_BLOCK(EMITTER, AND, MUL, STRH, ILL, ILL), \ + DECLARE_ARM_ALU_BLOCK(EMITTER, ANDS, MULS, LDRH, LDRSB, LDRSH), \ + DECLARE_ARM_ALU_BLOCK(EMITTER, EOR, MLA, ILL, ILL, ILL), \ + DECLARE_ARM_ALU_BLOCK(EMITTER, EORS, MLAS, ILL, ILL, ILL), \ + DECLARE_ARM_ALU_BLOCK(EMITTER, SUB, ILL, STRHI, ILL, ILL), \ + DECLARE_ARM_ALU_BLOCK(EMITTER, SUBS, ILL, LDRHI, LDRSBI, LDRSHI), \ + DECLARE_ARM_ALU_BLOCK(EMITTER, RSB, ILL, ILL, ILL, ILL), \ + DECLARE_ARM_ALU_BLOCK(EMITTER, RSBS, ILL, ILL, ILL, ILL), \ + DECLARE_ARM_ALU_BLOCK(EMITTER, ADD, UMULL, STRHU, ILL, ILL), \ + DECLARE_ARM_ALU_BLOCK(EMITTER, ADDS, UMULLS, LDRHU, LDRSBU, LDRSHU), \ + DECLARE_ARM_ALU_BLOCK(EMITTER, ADC, UMLAL, ILL, ILL, ILL), \ + DECLARE_ARM_ALU_BLOCK(EMITTER, ADCS, UMLALS, ILL, ILL, ILL), \ + DECLARE_ARM_ALU_BLOCK(EMITTER, SBC, SMULL, STRHIU, ILL, ILL), \ + DECLARE_ARM_ALU_BLOCK(EMITTER, SBCS, SMULLS, LDRHIU, LDRSBIU, LDRSHIU), \ + DECLARE_ARM_ALU_BLOCK(EMITTER, RSC, SMLAL, ILL, ILL, ILL), \ + DECLARE_ARM_ALU_BLOCK(EMITTER, RSCS, SMLALS, ILL, ILL, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, MRS), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, SWP), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, STRHP), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_ARM_ALU_BLOCK(EMITTER, TST, ILL, LDRHP, LDRSBP, LDRSHP), \ + DECLARE_INSTRUCTION_ARM(EMITTER, MSR), \ + DECLARE_INSTRUCTION_ARM(EMITTER, BX), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, BKPT), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, STRHPW), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_ARM_ALU_BLOCK(EMITTER, TEQ, ILL, LDRHPW, LDRSBPW, LDRSHPW), \ + DECLARE_INSTRUCTION_ARM(EMITTER, MRSR), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, SWPB), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, STRHIP), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_ARM_ALU_BLOCK(EMITTER, CMP, ILL, LDRHIP, LDRSBIP, LDRSHIP), \ + DECLARE_INSTRUCTION_ARM(EMITTER, MSRR), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, STRHIPW), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ + DECLARE_ARM_ALU_BLOCK(EMITTER, CMN, ILL, LDRHIPW, LDRSBIPW, LDRSHIPW), \ + DECLARE_ARM_ALU_BLOCK(EMITTER, ORR, SMLAL, STRHPU, ILL, ILL), \ + DECLARE_ARM_ALU_BLOCK(EMITTER, ORRS, SMLALS, LDRHPU, LDRSBPU, LDRSHPU), \ + DECLARE_ARM_ALU_BLOCK(EMITTER, MOV, SMLAL, STRHPUW, ILL, ILL), \ + DECLARE_ARM_ALU_BLOCK(EMITTER, MOVS, SMLALS, LDRHPUW, LDRSBPUW, LDRSHPUW), \ + DECLARE_ARM_ALU_BLOCK(EMITTER, BIC, SMLAL, STRHIPU, ILL, ILL), \ + DECLARE_ARM_ALU_BLOCK(EMITTER, BICS, SMLALS, LDRHIPU, LDRSBIPU, LDRSHIPU), \ + DECLARE_ARM_ALU_BLOCK(EMITTER, MVN, SMLAL, STRHIPUW, ILL, ILL), \ + DECLARE_ARM_ALU_BLOCK(EMITTER, MVNS, SMLALS, LDRHIPUW, LDRSBIPUW, LDRSHIPUW), \ + DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, AND), \ + DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, ANDS), \ + DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, EOR), \ + DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, EORS), \ + DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, SUB), \ + DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, SUBS), \ + DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, RSB), \ + DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, RSBS), \ + DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, ADD), \ + DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, ADDS), \ + DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, ADC), \ + DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, ADCS), \ + DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, SBC), \ + DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, SBCS), \ + DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, RSC), \ + DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, RSCS), \ + DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, TST), \ + DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, TST), \ + DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, MSR), \ + DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, TEQ), \ + DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, CMP), \ + DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, CMP), \ + DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, MSRR), \ + DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, CMN), \ + DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, ORR), \ + DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, ORRS), \ + DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, MOV), \ + DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, MOVS), \ + DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, BIC), \ + DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, BICS), \ + DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, MVN), \ + DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, MVNS), \ + DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STR, , , ), \ + DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDR, , , ), \ + DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STRT, , , ), \ + DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDRT, , , ), \ + DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STRB, , , ), \ + DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDRB, , , ), \ + DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STRBT, , , ), \ + DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDRBT, , , ), \ + DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STR, , U, ), \ + DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDR, , U, ), \ + DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STRT, , U, ), \ + DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDRT, , U, ), \ + DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STRB, , U, ), \ + DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDRB, , U, ), \ + DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STRBT, , U, ), \ + DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDRBT, , U, ), \ + DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STR, P, , ), \ + DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDR, P, , ), \ + DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STR, P, , W), \ + DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDR, P, , W), \ + DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STRB, P, , ), \ + DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDRB, P, , ), \ + DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STRB, P, , W), \ + DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDRB, P, , W), \ + DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STR, P, U, ), \ + DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDR, P, U, ), \ + DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STR, P, U, W), \ + DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDR, P, U, W), \ + DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STRB, P, U, ), \ + DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDRB, P, U, ), \ + DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STRB, P, U, W), \ + DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDRB, P, U, W), \ + DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STR, , , ), \ + DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDR, , , ), \ + DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STRT, , , ), \ + DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDRT, , , ), \ + DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STRB, , , ), \ + DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDRB, , , ), \ + DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STRBT, , , ), \ + DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDRBT, , , ), \ + DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STR, , U, ), \ + DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDR, , U, ), \ + DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STRT, , U, ), \ + DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDRT, , U, ), \ + DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STRB, , U, ), \ + DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDRB, , U, ), \ + DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STRBT, , U, ), \ + DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDRBT, , U, ), \ + DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STR, P, , ), \ + DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDR, P, , ), \ + DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STR, P, , W), \ + DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDR, P, , W), \ + DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STRB, P, , ), \ + DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDRB, P, , ), \ + DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STRB, P, , W), \ + DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDRB, P, , W), \ + DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STR, P, U, ), \ + DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDR, P, U, ), \ + DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STR, P, U, W), \ + DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDR, P, U, W), \ + DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STRB, P, U, ), \ + DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDRB, P, U, ), \ + DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STRB, P, U, W), \ + DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDRB, P, U, W), \ + DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STM, DA, ), \ + DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDM, DA, ), \ + DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STM, DA, W), \ + DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDM, DA, W), \ + DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STMS, DA, ), \ + DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDMS, DA, ), \ + DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STMS, DA, W), \ + DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDMS, DA, W), \ + DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STM, IA, ), \ + DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDM, IA, ), \ + DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STM, IA, W), \ + DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDM, IA, W), \ + DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STMS, IA, ), \ + DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDMS, IA, ), \ + DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STMS, IA, W), \ + DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDMS, IA, W), \ + DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STM, DB, ), \ + DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDM, DB, ), \ + DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STM, DB, W), \ + DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDM, DB, W), \ + DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STMS, DB, ), \ + DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDMS, DB, ), \ + DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STMS, DB, W), \ + DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDMS, DB, W), \ + DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STM, IB, ), \ + DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDM, IB, ), \ + DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STM, IB, W), \ + DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDM, IB, W), \ + DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STMS, IB, ), \ + DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDMS, IB, ), \ + DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STMS, IB, W), \ + DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDMS, IB, W), \ + DECLARE_ARM_BRANCH_BLOCK(EMITTER, B), \ + DECLARE_ARM_BRANCH_BLOCK(EMITTER, BL), \ + DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, , , , ), \ + DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, , , , ), \ + DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, , , , W), \ + DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, , , , W), \ + DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, , , N, ), \ + DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, , , N, ), \ + DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, , , N, W), \ + DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, , , N, W), \ + DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, , U, , ), \ + DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, , U, , ), \ + DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, , U, , W), \ + DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, , U, , W), \ + DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, , U, N, ), \ + DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, , U, N, ), \ + DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, , U, N, W), \ + DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, , U, N, W), \ + DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, P, , , ), \ + DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, P, , , ), \ + DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, P, , , W), \ + DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, P, , , W), \ + DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, P, U, N, ), \ + DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, P, U, N, ), \ + DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, P, U, N, W), \ + DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, P, U, N, W), \ + DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, P, , N, ), \ + DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, P, , N, ), \ + DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, P, , N, W), \ + DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, P, , N, W), \ + DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, P, U, N, ), \ + DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, P, U, N, ), \ + DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, P, U, N, W), \ + DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, P, U, N, W), \ + DECLARE_ARM_COPROCESSOR_BLOCK(EMITTER, CDP, MCR), \ + DECLARE_ARM_COPROCESSOR_BLOCK(EMITTER, CDP, MRC), \ + DECLARE_ARM_SWI_BLOCK(EMITTER) + +#endif
A src/arm/emitter-inlines.h

@@ -0,0 +1,128 @@

+#ifndef EMITTER_INLINES_H +#define EMITTER_INLINES_H + +#define DO_4(DIRECTIVE) \ + DIRECTIVE, \ + DIRECTIVE, \ + DIRECTIVE, \ + DIRECTIVE + +#define DO_8(DIRECTIVE) \ + DIRECTIVE, \ + DIRECTIVE, \ + DIRECTIVE, \ + DIRECTIVE, \ + DIRECTIVE, \ + DIRECTIVE, \ + DIRECTIVE, \ + DIRECTIVE + +#define DO_256(DIRECTIVE) \ + DO_4(DO_8(DO_8(DIRECTIVE))) + +#define DO_INTERLACE(LEFT, RIGHT) \ + LEFT, \ + RIGHT + +#define APPLY(F, ...) F(__VA_ARGS__) + +#define COUNT_CALL_1(EMITTER, PREFIX, ...) \ + EMITTER(PREFIX ## 0, 0, __VA_ARGS__) \ + EMITTER(PREFIX ## 1, 1, __VA_ARGS__) + +#define COUNT_CALL_2(EMITTER, PREFIX, ...) \ + COUNT_CALL_1(EMITTER, PREFIX, __VA_ARGS__) \ + EMITTER(PREFIX ## 2, 2, __VA_ARGS__) \ + EMITTER(PREFIX ## 3, 3, __VA_ARGS__) + +#define COUNT_CALL_3(EMITTER, PREFIX, ...) \ + COUNT_CALL_2(EMITTER, PREFIX, __VA_ARGS__) \ + EMITTER(PREFIX ## 4, 4, __VA_ARGS__) \ + EMITTER(PREFIX ## 5, 5, __VA_ARGS__) \ + EMITTER(PREFIX ## 6, 6, __VA_ARGS__) \ + EMITTER(PREFIX ## 7, 7, __VA_ARGS__) + +#define COUNT_CALL_4(EMITTER, PREFIX, ...) \ + COUNT_CALL_3(EMITTER, PREFIX, __VA_ARGS__) \ + EMITTER(PREFIX ## 8, 8, __VA_ARGS__) \ + EMITTER(PREFIX ## 9, 9, __VA_ARGS__) \ + EMITTER(PREFIX ## A, 10, __VA_ARGS__) \ + EMITTER(PREFIX ## B, 11, __VA_ARGS__) \ + EMITTER(PREFIX ## C, 12, __VA_ARGS__) \ + EMITTER(PREFIX ## D, 13, __VA_ARGS__) \ + EMITTER(PREFIX ## E, 14, __VA_ARGS__) \ + EMITTER(PREFIX ## F, 15, __VA_ARGS__) + +#define COUNT_CALL_5(EMITTER, PREFIX, ...) \ + COUNT_CALL_4(EMITTER, PREFIX ## 0, __VA_ARGS__) \ + EMITTER(PREFIX ## 10, 16, __VA_ARGS__) \ + EMITTER(PREFIX ## 11, 17, __VA_ARGS__) \ + EMITTER(PREFIX ## 12, 18, __VA_ARGS__) \ + EMITTER(PREFIX ## 13, 19, __VA_ARGS__) \ + EMITTER(PREFIX ## 14, 20, __VA_ARGS__) \ + EMITTER(PREFIX ## 15, 21, __VA_ARGS__) \ + EMITTER(PREFIX ## 16, 22, __VA_ARGS__) \ + EMITTER(PREFIX ## 17, 23, __VA_ARGS__) \ + EMITTER(PREFIX ## 18, 24, __VA_ARGS__) \ + EMITTER(PREFIX ## 19, 25, __VA_ARGS__) \ + EMITTER(PREFIX ## 1A, 26, __VA_ARGS__) \ + EMITTER(PREFIX ## 1B, 27, __VA_ARGS__) \ + EMITTER(PREFIX ## 1C, 28, __VA_ARGS__) \ + EMITTER(PREFIX ## 1D, 29, __VA_ARGS__) \ + EMITTER(PREFIX ## 1E, 30, __VA_ARGS__) \ + EMITTER(PREFIX ## 1F, 31, __VA_ARGS__) \ + +#define COUNT_1(EMITTER, PREFIX) \ + EMITTER(PREFIX ## 0) \ + EMITTER(PREFIX ## 1) + +#define COUNT_2(EMITTER, PREFIX) \ + COUNT_1(EMITTER, PREFIX) \ + EMITTER(PREFIX ## 2) \ + EMITTER(PREFIX ## 3) + +#define COUNT_3(EMITTER, PREFIX) \ + COUNT_2(EMITTER, PREFIX) \ + EMITTER(PREFIX ## 4) \ + EMITTER(PREFIX ## 5) \ + EMITTER(PREFIX ## 6) \ + EMITTER(PREFIX ## 7) + +#define COUNT_4(EMITTER, PREFIX) \ + COUNT_3(EMITTER, PREFIX) \ + EMITTER(PREFIX ## 8) \ + EMITTER(PREFIX ## 9) \ + EMITTER(PREFIX ## A) \ + EMITTER(PREFIX ## B) \ + EMITTER(PREFIX ## C) \ + EMITTER(PREFIX ## D) \ + EMITTER(PREFIX ## E) \ + EMITTER(PREFIX ## F) + +#define COUNT_5(EMITTER, PREFIX) \ + COUNT_4(EMITTER, PREFIX ## 0) \ + EMITTER(PREFIX ## 10) \ + EMITTER(PREFIX ## 11) \ + EMITTER(PREFIX ## 12) \ + EMITTER(PREFIX ## 13) \ + EMITTER(PREFIX ## 14) \ + EMITTER(PREFIX ## 15) \ + EMITTER(PREFIX ## 16) \ + EMITTER(PREFIX ## 17) \ + EMITTER(PREFIX ## 18) \ + EMITTER(PREFIX ## 19) \ + EMITTER(PREFIX ## 1A) \ + EMITTER(PREFIX ## 1B) \ + EMITTER(PREFIX ## 1C) \ + EMITTER(PREFIX ## 1D) \ + EMITTER(PREFIX ## 1E) \ + EMITTER(PREFIX ## 1F) \ + +#define ECHO(...) __VA_ARGS__, +#define ECHO_4(...) \ + ECHO(__VA_ARGS__) \ + ECHO(__VA_ARGS__) \ + ECHO(__VA_ARGS__) \ + ECHO(__VA_ARGS__) + +#endif
A src/arm/emitter-thumb.h

@@ -0,0 +1,108 @@

+#ifndef EMITTER_THUMB_H +#define EMITTER_THUMB_H + +#include "emitter-inlines.h" + +#define DECLARE_INSTRUCTION_THUMB(EMITTER, NAME) \ + EMITTER ## NAME + +#define DECLARE_INSTRUCTION_WITH_HIGH_THUMB(EMITTER, NAME) \ + DECLARE_INSTRUCTION_THUMB(EMITTER, NAME ## 00), \ + DECLARE_INSTRUCTION_THUMB(EMITTER, NAME ## 01), \ + DECLARE_INSTRUCTION_THUMB(EMITTER, NAME ## 10), \ + DECLARE_INSTRUCTION_THUMB(EMITTER, NAME ## 11) + +#define DECLARE_THUMB_EMITTER_BLOCK(EMITTER) \ + APPLY(COUNT_5, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, LSL1_)) \ + APPLY(COUNT_5, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, LSR1_)) \ + APPLY(COUNT_5, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, ASR1_)) \ + APPLY(COUNT_3, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, ADD3_R)) \ + APPLY(COUNT_3, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, SUB3_R)) \ + APPLY(COUNT_3, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, ADD1_)) \ + APPLY(COUNT_3, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, SUB1_)) \ + APPLY(COUNT_3, ECHO_4, DECLARE_INSTRUCTION_THUMB(EMITTER, MOV1_R)) \ + APPLY(COUNT_3, ECHO_4, DECLARE_INSTRUCTION_THUMB(EMITTER, CMP1_R)) \ + APPLY(COUNT_3, ECHO_4, DECLARE_INSTRUCTION_THUMB(EMITTER, ADD2_R)) \ + APPLY(COUNT_3, ECHO_4, DECLARE_INSTRUCTION_THUMB(EMITTER, SUB2_R)) \ + DECLARE_INSTRUCTION_THUMB(EMITTER, AND), \ + DECLARE_INSTRUCTION_THUMB(EMITTER, EOR), \ + DECLARE_INSTRUCTION_THUMB(EMITTER, LSL2), \ + DECLARE_INSTRUCTION_THUMB(EMITTER, LSR2), \ + DECLARE_INSTRUCTION_THUMB(EMITTER, ASR2), \ + DECLARE_INSTRUCTION_THUMB(EMITTER, ADC), \ + DECLARE_INSTRUCTION_THUMB(EMITTER, SBC), \ + DECLARE_INSTRUCTION_THUMB(EMITTER, ROR), \ + DECLARE_INSTRUCTION_THUMB(EMITTER, TST), \ + DECLARE_INSTRUCTION_THUMB(EMITTER, NEG), \ + DECLARE_INSTRUCTION_THUMB(EMITTER, CMP2), \ + DECLARE_INSTRUCTION_THUMB(EMITTER, CMN), \ + DECLARE_INSTRUCTION_THUMB(EMITTER, ORR), \ + DECLARE_INSTRUCTION_THUMB(EMITTER, MUL), \ + DECLARE_INSTRUCTION_THUMB(EMITTER, BIC), \ + DECLARE_INSTRUCTION_THUMB(EMITTER, MVN), \ + DECLARE_INSTRUCTION_WITH_HIGH_THUMB(EMITTER, ADD4), \ + DECLARE_INSTRUCTION_WITH_HIGH_THUMB(EMITTER, CMP3), \ + DECLARE_INSTRUCTION_WITH_HIGH_THUMB(EMITTER, MOV3), \ + DECLARE_INSTRUCTION_THUMB(EMITTER, BX), \ + DECLARE_INSTRUCTION_THUMB(EMITTER, BX), \ + DECLARE_INSTRUCTION_THUMB(EMITTER, ILL), \ + DECLARE_INSTRUCTION_THUMB(EMITTER, ILL), \ + APPLY(COUNT_3, ECHO_4, DECLARE_INSTRUCTION_THUMB(EMITTER, LDR3_R)) \ + APPLY(COUNT_3, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, STR2_R)) \ + APPLY(COUNT_3, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, STRH2_R)) \ + APPLY(COUNT_3, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, STRB2_R)) \ + APPLY(COUNT_3, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, LDRSB_R)) \ + APPLY(COUNT_3, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, LDR2_R)) \ + APPLY(COUNT_3, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, LDRH2_R)) \ + APPLY(COUNT_3, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, LDRB2_R)) \ + APPLY(COUNT_3, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, LDRSH_R)) \ + APPLY(COUNT_5, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, STR1_)) \ + APPLY(COUNT_5, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, LDR1_)) \ + APPLY(COUNT_5, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, STRB1_)) \ + APPLY(COUNT_5, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, LDRB1_)) \ + APPLY(COUNT_5, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, STRH1_)) \ + APPLY(COUNT_5, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, LDRH1_)) \ + APPLY(COUNT_3, ECHO_4, DECLARE_INSTRUCTION_THUMB(EMITTER, STR3_R)) \ + APPLY(COUNT_3, ECHO_4, DECLARE_INSTRUCTION_THUMB(EMITTER, LDR4_R)) \ + APPLY(COUNT_3, ECHO_4, DECLARE_INSTRUCTION_THUMB(EMITTER, ADD5_R)) \ + APPLY(COUNT_3, ECHO_4, DECLARE_INSTRUCTION_THUMB(EMITTER, ADD6_R)) \ + DECLARE_INSTRUCTION_THUMB(EMITTER, ADD7), \ + DECLARE_INSTRUCTION_THUMB(EMITTER, ADD7), \ + DECLARE_INSTRUCTION_THUMB(EMITTER, SUB4), \ + DECLARE_INSTRUCTION_THUMB(EMITTER, SUB4), \ + DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, ILL)), \ + DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, ILL)), \ + DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, ILL)), \ + DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, PUSH)), \ + DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, PUSHR)), \ + DO_8(DECLARE_INSTRUCTION_THUMB(EMITTER, ILL)), \ + DO_8(DECLARE_INSTRUCTION_THUMB(EMITTER, ILL)), \ + DO_8(DECLARE_INSTRUCTION_THUMB(EMITTER, ILL)), \ + DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, POP)), \ + DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, POPR)), \ + DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, BKPT)), \ + DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, ILL)), \ + APPLY(COUNT_3, ECHO_4, DECLARE_INSTRUCTION_THUMB(EMITTER, STMIA_R)) \ + APPLY(COUNT_3, ECHO_4, DECLARE_INSTRUCTION_THUMB(EMITTER, LDMIA_R)) \ + DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, BEQ)), \ + DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, BNE)), \ + DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, BCS)), \ + DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, BCC)), \ + DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, BMI)), \ + DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, BPL)), \ + DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, BVS)), \ + DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, BVC)), \ + DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, BHI)), \ + DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, BLS)), \ + DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, BGE)), \ + DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, BLT)), \ + DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, BGT)), \ + DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, BLE)), \ + DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, ILL)), \ + DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, SWI)), \ + DO_8(DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, B))), \ + DO_8(DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, ILL))), \ + DO_8(DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, BL1))), \ + DO_8(DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, BL2))) \ + +#endif
M src/arm/isa-arm.csrc/arm/isa-arm.c

@@ -1,13 +1,12 @@

#include "isa-arm.h" #include "arm.h" +#include "emitter-arm.h" #include "isa-inlines.h" -enum { - PSR_USER_MASK = 0xF0000000, - PSR_PRIV_MASK = 0x000000CF, - PSR_STATE_MASK = 0x00000020 -}; +#define PSR_USER_MASK 0xF0000000 +#define PSR_PRIV_MASK 0x000000CF +#define PSR_STATE_MASK 0x00000020 // Addressing mode 1 static inline void _shiftLSL(struct ARMCore* cpu, uint32_t opcode) {

@@ -243,13 +242,13 @@ #define ADDR_MODE_3_INDEX(U_OP, M) ADDR_MODE_2_INDEX(U_OP, M)

#define ADDR_MODE_3_WRITEBACK(ADDR) ADDR_MODE_2_WRITEBACK(ADDR) #define ARM_LOAD_POST_BODY \ + ++currentCycles; \ if (rd == ARM_PC) { \ ARM_WRITE_PC; \ } #define ARM_STORE_POST_BODY \ - currentCycles -= ARM_PREFETCH_CYCLES; \ - currentCycles += 1 + cpu->memory->activeNonseqCycles32; + currentCycles += cpu->memory.activeNonseqCycles32 - cpu->memory.activeSeqCycles32; #define DEFINE_INSTRUCTION_ARM(NAME, BODY) \ static void _ARMInstruction ## NAME (struct ARMCore* cpu, uint32_t opcode) { \

@@ -424,7 +423,7 @@ ADDRESS; \

S_PRE; \ LOOP(BODY); \ S_POST; \ - currentCycles += cpu->memory->waitMultiple(cpu->memory, addr, total); \ + currentCycles += cpu->memory.waitMultiple(cpu, addr, total); \ POST_BODY; \ WRITEBACK;)

@@ -543,68 +542,68 @@ // End multiply definitions

// Begin load/store definitions -DEFINE_LOAD_STORE_INSTRUCTION_ARM(LDR, cpu->gprs[rd] = cpu->memory->load32(cpu->memory, address, &currentCycles); ARM_LOAD_POST_BODY;) -DEFINE_LOAD_STORE_INSTRUCTION_ARM(LDRB, cpu->gprs[rd] = cpu->memory->loadU8(cpu->memory, address, &currentCycles); ARM_LOAD_POST_BODY;) -DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(LDRH, cpu->gprs[rd] = cpu->memory->loadU16(cpu->memory, address, &currentCycles); ARM_LOAD_POST_BODY;) -DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(LDRSB, cpu->gprs[rd] = cpu->memory->load8(cpu->memory, address, &currentCycles); ARM_LOAD_POST_BODY;) -DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(LDRSH, cpu->gprs[rd] = cpu->memory->load16(cpu->memory, address, &currentCycles); ARM_LOAD_POST_BODY;) -DEFINE_LOAD_STORE_INSTRUCTION_ARM(STR, cpu->memory->store32(cpu->memory, address, cpu->gprs[rd], &currentCycles); ARM_STORE_POST_BODY;) -DEFINE_LOAD_STORE_INSTRUCTION_ARM(STRB, cpu->memory->store8(cpu->memory, address, cpu->gprs[rd], &currentCycles); ARM_STORE_POST_BODY;) -DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(STRH, cpu->memory->store16(cpu->memory, address, cpu->gprs[rd], &currentCycles); ARM_STORE_POST_BODY;) +DEFINE_LOAD_STORE_INSTRUCTION_ARM(LDR, cpu->gprs[rd] = cpu->memory.load32(cpu, address, &currentCycles); ARM_LOAD_POST_BODY;) +DEFINE_LOAD_STORE_INSTRUCTION_ARM(LDRB, cpu->gprs[rd] = cpu->memory.loadU8(cpu, address, &currentCycles); ARM_LOAD_POST_BODY;) +DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(LDRH, cpu->gprs[rd] = cpu->memory.loadU16(cpu, address, &currentCycles); ARM_LOAD_POST_BODY;) +DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(LDRSB, cpu->gprs[rd] = cpu->memory.load8(cpu, address, &currentCycles); ARM_LOAD_POST_BODY;) +DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(LDRSH, cpu->gprs[rd] = cpu->memory.load16(cpu, address, &currentCycles); ARM_LOAD_POST_BODY;) +DEFINE_LOAD_STORE_INSTRUCTION_ARM(STR, cpu->memory.store32(cpu, address, cpu->gprs[rd], &currentCycles); ARM_STORE_POST_BODY;) +DEFINE_LOAD_STORE_INSTRUCTION_ARM(STRB, cpu->memory.store8(cpu, address, cpu->gprs[rd], &currentCycles); ARM_STORE_POST_BODY;) +DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(STRH, cpu->memory.store16(cpu, address, cpu->gprs[rd], &currentCycles); ARM_STORE_POST_BODY;) DEFINE_LOAD_STORE_T_INSTRUCTION_ARM(LDRBT, enum PrivilegeMode priv = cpu->privilegeMode; ARMSetPrivilegeMode(cpu, MODE_USER); - cpu->gprs[rd] = cpu->memory->loadU8(cpu->memory, address, &currentCycles); + cpu->gprs[rd] = cpu->memory.loadU8(cpu, address, &currentCycles); ARMSetPrivilegeMode(cpu, priv); ARM_LOAD_POST_BODY;) DEFINE_LOAD_STORE_T_INSTRUCTION_ARM(LDRT, enum PrivilegeMode priv = cpu->privilegeMode; ARMSetPrivilegeMode(cpu, MODE_USER); - cpu->gprs[rd] = cpu->memory->load32(cpu->memory, address, &currentCycles); + cpu->gprs[rd] = cpu->memory.load32(cpu, address, &currentCycles); ARMSetPrivilegeMode(cpu, priv); ARM_LOAD_POST_BODY;) DEFINE_LOAD_STORE_T_INSTRUCTION_ARM(STRBT, enum PrivilegeMode priv = cpu->privilegeMode; ARMSetPrivilegeMode(cpu, MODE_USER); - cpu->memory->store32(cpu->memory, address, cpu->gprs[rd], &currentCycles); + cpu->memory.store32(cpu, address, cpu->gprs[rd], &currentCycles); ARMSetPrivilegeMode(cpu, priv); ARM_STORE_POST_BODY;) DEFINE_LOAD_STORE_T_INSTRUCTION_ARM(STRT, enum PrivilegeMode priv = cpu->privilegeMode; ARMSetPrivilegeMode(cpu, MODE_USER); - cpu->memory->store8(cpu->memory, address, cpu->gprs[rd], &currentCycles); + cpu->memory.store8(cpu, address, cpu->gprs[rd], &currentCycles); ARMSetPrivilegeMode(cpu, priv); ARM_STORE_POST_BODY;) DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_ARM(LDM, - cpu->gprs[i] = cpu->memory->load32(cpu->memory, addr & 0xFFFFFFFC, 0);, + cpu->gprs[i] = cpu->memory.load32(cpu, addr & 0xFFFFFFFC, 0);, ++currentCycles; if (rs & 0x8000) { ARM_WRITE_PC; }) DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_ARM(STM, - cpu->memory->store32(cpu->memory, addr, cpu->gprs[i], 0);, - currentCycles += cpu->memory->activeNonseqCycles32 - cpu->memory->activePrefetchCycles32) + cpu->memory.store32(cpu, addr, cpu->gprs[i], 0);, + currentCycles += cpu->memory.activeNonseqCycles32 - cpu->memory.activeSeqCycles32) DEFINE_INSTRUCTION_ARM(SWP, int rm = opcode & 0xF; int rd = (opcode >> 12) & 0xF; int rn = (opcode >> 16) & 0xF; - int32_t d = cpu->memory->load32(cpu->memory, cpu->gprs[rn], &currentCycles); - cpu->memory->store32(cpu->memory, cpu->gprs[rn], cpu->gprs[rm], &currentCycles); + int32_t d = cpu->memory.load32(cpu, cpu->gprs[rn], &currentCycles); + cpu->memory.store32(cpu, cpu->gprs[rn], cpu->gprs[rm], &currentCycles); cpu->gprs[rd] = d;) DEFINE_INSTRUCTION_ARM(SWPB, int rm = opcode & 0xF; int rd = (opcode >> 12) & 0xF; int rn = (opcode >> 16) & 0xF; - int32_t d = cpu->memory->loadU8(cpu->memory, cpu->gprs[rn], &currentCycles); - cpu->memory->store8(cpu->memory, cpu->gprs[rn], cpu->gprs[rm], &currentCycles); + int32_t d = cpu->memory.loadU8(cpu, cpu->gprs[rn], &currentCycles); + cpu->memory.store8(cpu, cpu->gprs[rn], cpu->gprs[rm], &currentCycles); cpu->gprs[rd] = d;) // End load/store definitions

@@ -702,331 +701,7 @@ int32_t mask = (c ? 0x000000FF : 0) | (f ? 0xFF000000 : 0);

mask &= PSR_USER_MASK | PSR_PRIV_MASK | PSR_STATE_MASK; cpu->spsr.packed = (cpu->spsr.packed & ~mask) | (operand & mask);) -DEFINE_INSTRUCTION_ARM(SWI, cpu->board->swi32(cpu->board, opcode & 0xFFFFFF)) - -#define DECLARE_INSTRUCTION_ARM(EMITTER, NAME) \ - EMITTER ## NAME - -#define DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, ALU) \ - DO_8(DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## I)), \ - DO_8(DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## I)) - -#define DECLARE_ARM_ALU_BLOCK(EMITTER, ALU, EX1, EX2, EX3, EX4) \ - DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## _LSL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## _LSLR), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## _LSR), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## _LSRR), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## _ASR), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## _ASRR), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## _ROR), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## _RORR), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## _LSL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, EX1), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## _LSR), \ - DECLARE_INSTRUCTION_ARM(EMITTER, EX2), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## _ASR), \ - DECLARE_INSTRUCTION_ARM(EMITTER, EX3), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## _ROR), \ - DECLARE_INSTRUCTION_ARM(EMITTER, EX4) - -#define DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, NAME, P, U, W) \ - DO_8(DECLARE_INSTRUCTION_ARM(EMITTER, NAME ## I ## P ## U ## W)), \ - DO_8(DECLARE_INSTRUCTION_ARM(EMITTER, NAME ## I ## P ## U ## W)) - -#define DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, NAME, P, U, W) \ - DECLARE_INSTRUCTION_ARM(EMITTER, NAME ## _LSL_ ## P ## U ## W), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, NAME ## _LSR_ ## P ## U ## W), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, NAME ## _ASR_ ## P ## U ## W), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, NAME ## _ROR_ ## P ## U ## W), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, NAME ## _LSL_ ## P ## U ## W), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, NAME ## _LSR_ ## P ## U ## W), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, NAME ## _ASR_ ## P ## U ## W), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, NAME ## _ROR_ ## P ## U ## W), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL) - -#define DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, NAME, MODE, W) \ - DO_8(DECLARE_INSTRUCTION_ARM(EMITTER, NAME ## MODE ## W)), \ - DO_8(DECLARE_INSTRUCTION_ARM(EMITTER, NAME ## MODE ## W)) - -#define DECLARE_ARM_BRANCH_BLOCK(EMITTER, NAME) \ - DO_256(DECLARE_INSTRUCTION_ARM(EMITTER, NAME)) - -// TODO: Support coprocessors -#define DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, NAME, P, U, N, W) \ - DO_8(DECLARE_INSTRUCTION_ARM(EMITTER, NAME)), \ - DO_8(DECLARE_INSTRUCTION_ARM(EMITTER, NAME)) - -#define DECLARE_ARM_COPROCESSOR_BLOCK(EMITTER, NAME1, NAME2) \ - DO_8(DO_8(DO_INTERLACE(DECLARE_INSTRUCTION_ARM(EMITTER, NAME1), DECLARE_INSTRUCTION_ARM(EMITTER, NAME2)))) - -#define DECLARE_ARM_SWI_BLOCK(EMITTER) \ - DO_256(DECLARE_INSTRUCTION_ARM(EMITTER, SWI)) - -#define DECLARE_ARM_EMITTER_BLOCK(EMITTER) \ - DECLARE_ARM_ALU_BLOCK(EMITTER, AND, MUL, STRH, ILL, ILL), \ - DECLARE_ARM_ALU_BLOCK(EMITTER, ANDS, MULS, LDRH, LDRSB, LDRSH), \ - DECLARE_ARM_ALU_BLOCK(EMITTER, EOR, MLA, ILL, ILL, ILL), \ - DECLARE_ARM_ALU_BLOCK(EMITTER, EORS, MLAS, ILL, ILL, ILL), \ - DECLARE_ARM_ALU_BLOCK(EMITTER, SUB, ILL, STRHI, ILL, ILL), \ - DECLARE_ARM_ALU_BLOCK(EMITTER, SUBS, ILL, LDRHI, LDRSBI, LDRSHI), \ - DECLARE_ARM_ALU_BLOCK(EMITTER, RSB, ILL, ILL, ILL, ILL), \ - DECLARE_ARM_ALU_BLOCK(EMITTER, RSBS, ILL, ILL, ILL, ILL), \ - DECLARE_ARM_ALU_BLOCK(EMITTER, ADD, UMULL, STRHU, ILL, ILL), \ - DECLARE_ARM_ALU_BLOCK(EMITTER, ADDS, UMULLS, LDRHU, LDRSBU, LDRSHU), \ - DECLARE_ARM_ALU_BLOCK(EMITTER, ADC, UMLAL, ILL, ILL, ILL), \ - DECLARE_ARM_ALU_BLOCK(EMITTER, ADCS, UMLALS, ILL, ILL, ILL), \ - DECLARE_ARM_ALU_BLOCK(EMITTER, SBC, SMULL, STRHIU, ILL, ILL), \ - DECLARE_ARM_ALU_BLOCK(EMITTER, SBCS, SMULLS, LDRHIU, LDRSBIU, LDRSHIU), \ - DECLARE_ARM_ALU_BLOCK(EMITTER, RSC, SMLAL, ILL, ILL, ILL), \ - DECLARE_ARM_ALU_BLOCK(EMITTER, RSCS, SMLALS, ILL, ILL, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, MRS), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, SWP), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, STRHP), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_ARM_ALU_BLOCK(EMITTER, TST, ILL, LDRHP, LDRSBP, LDRSHP), \ - DECLARE_INSTRUCTION_ARM(EMITTER, MSR), \ - DECLARE_INSTRUCTION_ARM(EMITTER, BX), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, BKPT), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, STRHPW), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_ARM_ALU_BLOCK(EMITTER, TEQ, ILL, LDRHPW, LDRSBPW, LDRSHPW), \ - DECLARE_INSTRUCTION_ARM(EMITTER, MRSR), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, SWPB), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, STRHIP), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_ARM_ALU_BLOCK(EMITTER, CMP, ILL, LDRHIP, LDRSBIP, LDRSHIP), \ - DECLARE_INSTRUCTION_ARM(EMITTER, MSRR), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, STRHIPW), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \ - DECLARE_ARM_ALU_BLOCK(EMITTER, CMN, ILL, LDRHIPW, LDRSBIPW, LDRSHIPW), \ - DECLARE_ARM_ALU_BLOCK(EMITTER, ORR, SMLAL, STRHPU, ILL, ILL), \ - DECLARE_ARM_ALU_BLOCK(EMITTER, ORRS, SMLALS, LDRHPU, LDRSBPU, LDRSHPU), \ - DECLARE_ARM_ALU_BLOCK(EMITTER, MOV, SMLAL, STRHPUW, ILL, ILL), \ - DECLARE_ARM_ALU_BLOCK(EMITTER, MOVS, SMLALS, LDRHPUW, LDRSBPUW, LDRSHPUW), \ - DECLARE_ARM_ALU_BLOCK(EMITTER, BIC, SMLAL, STRHIPU, ILL, ILL), \ - DECLARE_ARM_ALU_BLOCK(EMITTER, BICS, SMLALS, LDRHIPU, LDRSBIPU, LDRSHIPU), \ - DECLARE_ARM_ALU_BLOCK(EMITTER, MVN, SMLAL, STRHIPUW, ILL, ILL), \ - DECLARE_ARM_ALU_BLOCK(EMITTER, MVNS, SMLALS, LDRHIPUW, LDRSBIPUW, LDRSHIPUW), \ - DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, AND), \ - DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, ANDS), \ - DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, EOR), \ - DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, EORS), \ - DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, SUB), \ - DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, SUBS), \ - DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, RSB), \ - DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, RSBS), \ - DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, ADD), \ - DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, ADDS), \ - DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, ADC), \ - DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, ADCS), \ - DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, SBC), \ - DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, SBCS), \ - DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, RSC), \ - DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, RSCS), \ - DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, TST), \ - DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, TST), \ - DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, MSR), \ - DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, TEQ), \ - DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, CMP), \ - DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, CMP), \ - DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, MSRR), \ - DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, CMN), \ - DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, ORR), \ - DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, ORRS), \ - DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, MOV), \ - DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, MOVS), \ - DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, BIC), \ - DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, BICS), \ - DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, MVN), \ - DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, MVNS), \ - DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STR, , , ), \ - DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDR, , , ), \ - DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STRT, , , ), \ - DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDRT, , , ), \ - DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STRB, , , ), \ - DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDRB, , , ), \ - DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STRBT, , , ), \ - DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDRBT, , , ), \ - DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STR, , U, ), \ - DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDR, , U, ), \ - DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STRT, , U, ), \ - DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDRT, , U, ), \ - DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STRB, , U, ), \ - DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDRB, , U, ), \ - DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STRBT, , U, ), \ - DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDRBT, , U, ), \ - DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STR, P, , ), \ - DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDR, P, , ), \ - DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STR, P, , W), \ - DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDR, P, , W), \ - DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STRB, P, , ), \ - DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDRB, P, , ), \ - DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STRB, P, , W), \ - DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDRB, P, , W), \ - DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STR, P, U, ), \ - DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDR, P, U, ), \ - DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STR, P, U, W), \ - DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDR, P, U, W), \ - DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STRB, P, U, ), \ - DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDRB, P, U, ), \ - DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STRB, P, U, W), \ - DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDRB, P, U, W), \ - DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STR, , , ), \ - DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDR, , , ), \ - DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STRT, , , ), \ - DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDRT, , , ), \ - DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STRB, , , ), \ - DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDRB, , , ), \ - DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STRBT, , , ), \ - DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDRBT, , , ), \ - DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STR, , U, ), \ - DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDR, , U, ), \ - DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STRT, , U, ), \ - DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDRT, , U, ), \ - DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STRB, , U, ), \ - DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDRB, , U, ), \ - DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STRBT, , U, ), \ - DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDRBT, , U, ), \ - DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STR, P, , ), \ - DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDR, P, , ), \ - DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STR, P, , W), \ - DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDR, P, , W), \ - DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STRB, P, , ), \ - DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDRB, P, , ), \ - DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STRB, P, , W), \ - DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDRB, P, , W), \ - DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STR, P, U, ), \ - DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDR, P, U, ), \ - DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STR, P, U, W), \ - DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDR, P, U, W), \ - DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STRB, P, U, ), \ - DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDRB, P, U, ), \ - DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STRB, P, U, W), \ - DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDRB, P, U, W), \ - DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STM, DA, ), \ - DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDM, DA, ), \ - DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STM, DA, W), \ - DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDM, DA, W), \ - DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STMS, DA, ), \ - DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDMS, DA, ), \ - DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STMS, DA, W), \ - DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDMS, DA, W), \ - DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STM, IA, ), \ - DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDM, IA, ), \ - DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STM, IA, W), \ - DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDM, IA, W), \ - DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STMS, IA, ), \ - DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDMS, IA, ), \ - DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STMS, IA, W), \ - DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDMS, IA, W), \ - DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STM, DB, ), \ - DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDM, DB, ), \ - DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STM, DB, W), \ - DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDM, DB, W), \ - DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STMS, DB, ), \ - DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDMS, DB, ), \ - DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STMS, DB, W), \ - DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDMS, DB, W), \ - DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STM, IB, ), \ - DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDM, IB, ), \ - DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STM, IB, W), \ - DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDM, IB, W), \ - DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STMS, IB, ), \ - DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDMS, IB, ), \ - DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STMS, IB, W), \ - DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDMS, IB, W), \ - DECLARE_ARM_BRANCH_BLOCK(EMITTER, B), \ - DECLARE_ARM_BRANCH_BLOCK(EMITTER, BL), \ - DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, , , , ), \ - DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, , , , ), \ - DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, , , , W), \ - DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, , , , W), \ - DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, , , N, ), \ - DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, , , N, ), \ - DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, , , N, W), \ - DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, , , N, W), \ - DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, , U, , ), \ - DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, , U, , ), \ - DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, , U, , W), \ - DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, , U, , W), \ - DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, , U, N, ), \ - DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, , U, N, ), \ - DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, , U, N, W), \ - DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, , U, N, W), \ - DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, P, , , ), \ - DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, P, , , ), \ - DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, P, , , W), \ - DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, P, , , W), \ - DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, P, U, N, ), \ - DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, P, U, N, ), \ - DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, P, U, N, W), \ - DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, P, U, N, W), \ - DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, P, , N, ), \ - DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, P, , N, ), \ - DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, P, , N, W), \ - DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, P, , N, W), \ - DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, P, U, N, ), \ - DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, P, U, N, ), \ - DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, P, U, N, W), \ - DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, P, U, N, W), \ - DECLARE_ARM_COPROCESSOR_BLOCK(EMITTER, CDP, MCR), \ - DECLARE_ARM_COPROCESSOR_BLOCK(EMITTER, CDP, MRC), \ - DECLARE_ARM_SWI_BLOCK(EMITTER) +DEFINE_INSTRUCTION_ARM(SWI, cpu->irqh.swi32(cpu, opcode & 0xFFFFFF)) const ARMInstruction _armTable[0x1000] = { DECLARE_ARM_EMITTER_BLOCK(_ARMInstruction)
M src/arm/isa-arm.hsrc/arm/isa-arm.h

@@ -1,9 +1,9 @@

#ifndef ISA_ARM_H #define ISA_ARM_H -#include <stdint.h> +#include "common.h" -#define ARM_PREFETCH_CYCLES (1 + cpu->memory->activePrefetchCycles32) +#define ARM_PREFETCH_CYCLES (1 + cpu->memory.activeSeqCycles32) struct ARMCore;
M src/arm/isa-inlines.hsrc/arm/isa-inlines.h

@@ -1,32 +1,9 @@

#ifndef ISA_INLINES_H #define ISA_INLINES_H -#include "arm.h" +#include "common.h" -#define UNUSED(V) (void)(V) - -#define DO_4(DIRECTIVE) \ - DIRECTIVE, \ - DIRECTIVE, \ - DIRECTIVE, \ - DIRECTIVE - -#define DO_8(DIRECTIVE) \ - DIRECTIVE, \ - DIRECTIVE, \ - DIRECTIVE, \ - DIRECTIVE, \ - DIRECTIVE, \ - DIRECTIVE, \ - DIRECTIVE, \ - DIRECTIVE - -#define DO_256(DIRECTIVE) \ - DO_4(DO_8(DO_8(DIRECTIVE))) - -#define DO_INTERLACE(LEFT, RIGHT) \ - LEFT, \ - RIGHT +#include "arm.h" #define ARM_COND_EQ (cpu->cpsr.z) #define ARM_COND_NE (!cpu->cpsr.z)

@@ -63,18 +40,18 @@ } else { \

cpu->cycles += 4; \ } -#define ARM_STUB cpu->board->hitStub(cpu->board, opcode) -#define ARM_ILL cpu->board->hitIllegal(cpu->board, opcode) +#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->memory, cpu->gprs[ARM_PC] - WORD_SIZE_ARM); \ - currentCycles += 2 + cpu->memory->activeNonseqCycles32 + cpu->memory->activePrefetchCycles32; + cpu->memory.setActiveRegion(cpu, 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->memory, cpu->gprs[ARM_PC] - WORD_SIZE_THUMB); \ - currentCycles += 2 + cpu->memory->activeNonseqCycles16 + cpu->memory->activePrefetchCycles16; + cpu->memory.setActiveRegion(cpu, cpu->gprs[ARM_PC] - WORD_SIZE_THUMB); \ + currentCycles += 2 + cpu->memory.activeUncachedCycles16 + cpu->memory.activeSeqCycles16; static inline int _ARMModeHasSPSR(enum PrivilegeMode mode) { return mode != MODE_SYSTEM && mode != MODE_USER;

@@ -98,7 +75,7 @@

static inline void _ARMReadCPSR(struct ARMCore* cpu) { _ARMSetMode(cpu, cpu->cpsr.t); ARMSetPrivilegeMode(cpu, cpu->cpsr.priv); - cpu->board->readCPSR(cpu->board); + cpu->irqh.readCPSR(cpu); } #endif
M src/arm/isa-thumb.csrc/arm/isa-thumb.c

@@ -1,6 +1,7 @@

#include "isa-thumb.h" #include "isa-inlines.h" +#include "emitter-thumb.h" // Instruction definitions // Beware pre-processor insanity

@@ -33,58 +34,12 @@ int m = M; \

D = M - N; \ THUMB_SUBTRACTION_S(m, n, D) -#define THUMB_PREFETCH_CYCLES (1 + cpu->memory->activePrefetchCycles16) - -#define THUMB_STORE_POST_BODY \ - currentCycles += cpu->memory->activeNonseqCycles16 - cpu->memory->activePrefetchCycles16; - -#define APPLY(F, ...) F(__VA_ARGS__) +#define THUMB_PREFETCH_CYCLES (1 + cpu->memory.activeSeqCycles16) -#define COUNT_1(EMITTER, PREFIX, ...) \ - EMITTER(PREFIX ## 0, 0, __VA_ARGS__) \ - EMITTER(PREFIX ## 1, 1, __VA_ARGS__) - -#define COUNT_2(EMITTER, PREFIX, ...) \ - COUNT_1(EMITTER, PREFIX, __VA_ARGS__) \ - EMITTER(PREFIX ## 2, 2, __VA_ARGS__) \ - EMITTER(PREFIX ## 3, 3, __VA_ARGS__) - -#define COUNT_3(EMITTER, PREFIX, ...) \ - COUNT_2(EMITTER, PREFIX, __VA_ARGS__) \ - EMITTER(PREFIX ## 4, 4, __VA_ARGS__) \ - EMITTER(PREFIX ## 5, 5, __VA_ARGS__) \ - EMITTER(PREFIX ## 6, 6, __VA_ARGS__) \ - EMITTER(PREFIX ## 7, 7, __VA_ARGS__) - -#define COUNT_4(EMITTER, PREFIX, ...) \ - COUNT_3(EMITTER, PREFIX, __VA_ARGS__) \ - EMITTER(PREFIX ## 8, 8, __VA_ARGS__) \ - EMITTER(PREFIX ## 9, 9, __VA_ARGS__) \ - EMITTER(PREFIX ## A, 10, __VA_ARGS__) \ - EMITTER(PREFIX ## B, 11, __VA_ARGS__) \ - EMITTER(PREFIX ## C, 12, __VA_ARGS__) \ - EMITTER(PREFIX ## D, 13, __VA_ARGS__) \ - EMITTER(PREFIX ## E, 14, __VA_ARGS__) \ - EMITTER(PREFIX ## F, 15, __VA_ARGS__) +#define THUMB_LOAD_POST_BODY ++currentCycles; -#define COUNT_5(EMITTER, PREFIX, ...) \ - COUNT_4(EMITTER, PREFIX ## 0, __VA_ARGS__) \ - EMITTER(PREFIX ## 10, 16, __VA_ARGS__) \ - EMITTER(PREFIX ## 11, 17, __VA_ARGS__) \ - EMITTER(PREFIX ## 12, 18, __VA_ARGS__) \ - EMITTER(PREFIX ## 13, 19, __VA_ARGS__) \ - EMITTER(PREFIX ## 14, 20, __VA_ARGS__) \ - EMITTER(PREFIX ## 15, 21, __VA_ARGS__) \ - EMITTER(PREFIX ## 16, 22, __VA_ARGS__) \ - EMITTER(PREFIX ## 17, 23, __VA_ARGS__) \ - EMITTER(PREFIX ## 18, 24, __VA_ARGS__) \ - EMITTER(PREFIX ## 19, 25, __VA_ARGS__) \ - EMITTER(PREFIX ## 1A, 26, __VA_ARGS__) \ - EMITTER(PREFIX ## 1B, 27, __VA_ARGS__) \ - EMITTER(PREFIX ## 1C, 28, __VA_ARGS__) \ - EMITTER(PREFIX ## 1D, 29, __VA_ARGS__) \ - EMITTER(PREFIX ## 1E, 30, __VA_ARGS__) \ - EMITTER(PREFIX ## 1F, 31, __VA_ARGS__) \ +#define THUMB_STORE_POST_BODY \ + currentCycles += cpu->memory.activeNonseqCycles16 - cpu->memory.activeSeqCycles16; #define DEFINE_INSTRUCTION_THUMB(NAME, BODY) \ static void _ThumbInstruction ## NAME (struct ARMCore* cpu, uint16_t opcode) { \

@@ -101,7 +56,7 @@ int rm = (opcode >> 3) & 0x0007; \

BODY;) #define DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(NAME, BODY) \ - COUNT_5(DEFINE_IMMEDIATE_5_INSTRUCTION_EX_THUMB, NAME ## _, BODY) + COUNT_CALL_5(DEFINE_IMMEDIATE_5_INSTRUCTION_EX_THUMB, NAME ## _, BODY) DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(LSL1, if (!immediate) {

@@ -136,12 +91,12 @@ cpu->gprs[rd] = cpu->gprs[rm] >> immediate;

} THUMB_NEUTRAL_S( , , cpu->gprs[rd]);) -DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(LDR1, cpu->gprs[rd] = cpu->memory->load32(cpu->memory, cpu->gprs[rm] + immediate * 4, &currentCycles)) -DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(LDRB1, cpu->gprs[rd] = cpu->memory->loadU8(cpu->memory, cpu->gprs[rm] + immediate, &currentCycles)) -DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(LDRH1, cpu->gprs[rd] = cpu->memory->loadU16(cpu->memory, cpu->gprs[rm] + immediate * 2, &currentCycles)) -DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(STR1, cpu->memory->store32(cpu->memory, cpu->gprs[rm] + immediate * 4, cpu->gprs[rd], &currentCycles); THUMB_STORE_POST_BODY;) -DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(STRB1, cpu->memory->store8(cpu->memory, cpu->gprs[rm] + immediate, cpu->gprs[rd], &currentCycles); THUMB_STORE_POST_BODY;) -DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(STRH1, cpu->memory->store16(cpu->memory, cpu->gprs[rm] + immediate * 2, cpu->gprs[rd], &currentCycles); THUMB_STORE_POST_BODY;) +DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(LDR1, cpu->gprs[rd] = cpu->memory.load32(cpu, cpu->gprs[rm] + immediate * 4, &currentCycles); THUMB_LOAD_POST_BODY;) +DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(LDRB1, cpu->gprs[rd] = cpu->memory.loadU8(cpu, cpu->gprs[rm] + immediate, &currentCycles); THUMB_LOAD_POST_BODY;) +DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(LDRH1, cpu->gprs[rd] = cpu->memory.loadU16(cpu, cpu->gprs[rm] + immediate * 2, &currentCycles); THUMB_LOAD_POST_BODY;) +DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(STR1, cpu->memory.store32(cpu, cpu->gprs[rm] + immediate * 4, cpu->gprs[rd], &currentCycles); THUMB_STORE_POST_BODY;) +DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(STRB1, cpu->memory.store8(cpu, cpu->gprs[rm] + immediate, cpu->gprs[rd], &currentCycles); THUMB_STORE_POST_BODY;) +DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(STRH1, cpu->memory.store16(cpu, cpu->gprs[rm] + immediate * 2, cpu->gprs[rd], &currentCycles); THUMB_STORE_POST_BODY;) #define DEFINE_DATA_FORM_1_INSTRUCTION_EX_THUMB(NAME, RM, BODY) \ DEFINE_INSTRUCTION_THUMB(NAME, \

@@ -151,7 +106,7 @@ int rn = (opcode >> 3) & 0x0007; \

BODY;) #define DEFINE_DATA_FORM_1_INSTRUCTION_THUMB(NAME, BODY) \ - COUNT_3(DEFINE_DATA_FORM_1_INSTRUCTION_EX_THUMB, NAME ## 3_R, BODY) + COUNT_CALL_3(DEFINE_DATA_FORM_1_INSTRUCTION_EX_THUMB, NAME ## 3_R, BODY) DEFINE_DATA_FORM_1_INSTRUCTION_THUMB(ADD, THUMB_ADDITION(cpu->gprs[rd], cpu->gprs[rn], cpu->gprs[rm])) DEFINE_DATA_FORM_1_INSTRUCTION_THUMB(SUB, THUMB_SUBTRACTION(cpu->gprs[rd], cpu->gprs[rn], cpu->gprs[rm]))

@@ -164,7 +119,7 @@ int rn = (opcode >> 3) & 0x0007; \

BODY;) #define DEFINE_DATA_FORM_2_INSTRUCTION_THUMB(NAME, BODY) \ - COUNT_3(DEFINE_DATA_FORM_2_INSTRUCTION_EX_THUMB, NAME ## 1_, BODY) + COUNT_CALL_3(DEFINE_DATA_FORM_2_INSTRUCTION_EX_THUMB, NAME ## 1_, BODY) DEFINE_DATA_FORM_2_INSTRUCTION_THUMB(ADD, THUMB_ADDITION(cpu->gprs[rd], cpu->gprs[rn], immediate)) DEFINE_DATA_FORM_2_INSTRUCTION_THUMB(SUB, THUMB_SUBTRACTION(cpu->gprs[rd], cpu->gprs[rn], immediate))

@@ -176,7 +131,7 @@ int immediate = opcode & 0x00FF; \

BODY;) #define DEFINE_DATA_FORM_3_INSTRUCTION_THUMB(NAME, BODY) \ - COUNT_3(DEFINE_DATA_FORM_3_INSTRUCTION_EX_THUMB, NAME ## _R, BODY) + COUNT_CALL_3(DEFINE_DATA_FORM_3_INSTRUCTION_EX_THUMB, NAME ## _R, BODY) DEFINE_DATA_FORM_3_INSTRUCTION_THUMB(ADD2, THUMB_ADDITION(cpu->gprs[rd], cpu->gprs[rd], immediate)) DEFINE_DATA_FORM_3_INSTRUCTION_THUMB(CMP1, int aluOut = cpu->gprs[rd] - immediate; THUMB_SUBTRACTION_S(cpu->gprs[rd], immediate, aluOut))

@@ -306,11 +261,11 @@ int immediate = (opcode & 0x00FF) << 2; \

BODY;) #define DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(NAME, BODY) \ - COUNT_3(DEFINE_IMMEDIATE_WITH_REGISTER_EX_THUMB, NAME ## _R, BODY) + COUNT_CALL_3(DEFINE_IMMEDIATE_WITH_REGISTER_EX_THUMB, NAME ## _R, BODY) -DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(LDR3, cpu->gprs[rd] = cpu->memory->load32(cpu->memory, (cpu->gprs[ARM_PC] & 0xFFFFFFFC) + immediate, &currentCycles)) -DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(LDR4, cpu->gprs[rd] = cpu->memory->load32(cpu->memory, cpu->gprs[ARM_SP] + immediate, &currentCycles)) -DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(STR3, cpu->memory->store32(cpu->memory, cpu->gprs[ARM_SP] + immediate, cpu->gprs[rd], &currentCycles); THUMB_STORE_POST_BODY;) +DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(LDR3, cpu->gprs[rd] = cpu->memory.load32(cpu, (cpu->gprs[ARM_PC] & 0xFFFFFFFC) + immediate, &currentCycles); THUMB_LOAD_POST_BODY;) +DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(LDR4, cpu->gprs[rd] = cpu->memory.load32(cpu, cpu->gprs[ARM_SP] + immediate, &currentCycles); THUMB_LOAD_POST_BODY;) +DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(STR3, cpu->memory.store32(cpu, cpu->gprs[ARM_SP] + immediate, cpu->gprs[rd], &currentCycles); THUMB_STORE_POST_BODY;) DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(ADD5, cpu->gprs[rd] = (cpu->gprs[ARM_PC] & 0xFFFFFFFC) + immediate) DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(ADD6, cpu->gprs[rd] = cpu->gprs[ARM_SP] + immediate)

@@ -323,16 +278,16 @@ int rn = (opcode >> 3) & 0x0007; \

BODY;) #define DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(NAME, BODY) \ - COUNT_3(DEFINE_LOAD_STORE_WITH_REGISTER_EX_THUMB, NAME ## _R, BODY) + COUNT_CALL_3(DEFINE_LOAD_STORE_WITH_REGISTER_EX_THUMB, NAME ## _R, BODY) -DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDR2, cpu->gprs[rd] = cpu->memory->load32(cpu->memory, cpu->gprs[rn] + cpu->gprs[rm], &currentCycles)) -DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRB2, cpu->gprs[rd] = cpu->memory->loadU8(cpu->memory, cpu->gprs[rn] + cpu->gprs[rm], &currentCycles)) -DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRH2, cpu->gprs[rd] = cpu->memory->loadU16(cpu->memory, cpu->gprs[rn] + cpu->gprs[rm], &currentCycles)) -DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRSB, cpu->gprs[rd] = cpu->memory->load8(cpu->memory, cpu->gprs[rn] + cpu->gprs[rm], &currentCycles)) -DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRSH, cpu->gprs[rd] = cpu->memory->load16(cpu->memory, cpu->gprs[rn] + cpu->gprs[rm], &currentCycles)) -DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(STR2, cpu->memory->store32(cpu->memory, cpu->gprs[rn] + cpu->gprs[rm], cpu->gprs[rd], &currentCycles); THUMB_STORE_POST_BODY;) -DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(STRB2, cpu->memory->store8(cpu->memory, cpu->gprs[rn] + cpu->gprs[rm], cpu->gprs[rd], &currentCycles); THUMB_STORE_POST_BODY;) -DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(STRH2, cpu->memory->store16(cpu->memory, cpu->gprs[rn] + cpu->gprs[rm], cpu->gprs[rd], &currentCycles); THUMB_STORE_POST_BODY;) +DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDR2, cpu->gprs[rd] = cpu->memory.load32(cpu, cpu->gprs[rn] + cpu->gprs[rm], &currentCycles); THUMB_LOAD_POST_BODY;) +DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRB2, cpu->gprs[rd] = cpu->memory.loadU8(cpu, cpu->gprs[rn] + cpu->gprs[rm], &currentCycles); THUMB_LOAD_POST_BODY;) +DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRH2, cpu->gprs[rd] = cpu->memory.loadU16(cpu, cpu->gprs[rn] + cpu->gprs[rm], &currentCycles); THUMB_LOAD_POST_BODY;) +DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRSB, cpu->gprs[rd] = cpu->memory.load8(cpu, cpu->gprs[rn] + cpu->gprs[rm], &currentCycles); THUMB_LOAD_POST_BODY;) +DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRSH, cpu->gprs[rd] = cpu->memory.load16(cpu, cpu->gprs[rn] + cpu->gprs[rm], &currentCycles); THUMB_LOAD_POST_BODY;) +DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(STR2, cpu->memory.store32(cpu, cpu->gprs[rn] + cpu->gprs[rm], cpu->gprs[rd], &currentCycles); THUMB_STORE_POST_BODY;) +DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(STRB2, cpu->memory.store8(cpu, cpu->gprs[rn] + cpu->gprs[rm], cpu->gprs[rd], &currentCycles); THUMB_STORE_POST_BODY;) +DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(STRH2, cpu->memory.store16(cpu, cpu->gprs[rn] + cpu->gprs[rm], cpu->gprs[rd], &currentCycles); THUMB_STORE_POST_BODY;) #define DEFINE_LOAD_STORE_MULTIPLE_EX_THUMB(NAME, RN, ADDRESS, LOOP, BODY, OP, PRE_BODY, POST_BODY, WRITEBACK) \ DEFINE_INSTRUCTION_THUMB(NAME, \

@@ -352,20 +307,21 @@ ++total; \

} \ } \ POST_BODY; \ - currentCycles += cpu->memory->waitMultiple(cpu->memory, address, total); \ + currentCycles += cpu->memory.waitMultiple(cpu, address, total); \ WRITEBACK;) #define DEFINE_LOAD_STORE_MULTIPLE_THUMB(NAME, BODY, WRITEBACK) \ - COUNT_3(DEFINE_LOAD_STORE_MULTIPLE_EX_THUMB, NAME ## _R, cpu->gprs[rn], (m = 0x01, i = 0; i < 8; m <<= 1, ++i), BODY, +=, , , WRITEBACK) + COUNT_CALL_3(DEFINE_LOAD_STORE_MULTIPLE_EX_THUMB, NAME ## _R, cpu->gprs[rn], (m = 0x01, i = 0; i < 8; m <<= 1, ++i), BODY, +=, , , WRITEBACK) DEFINE_LOAD_STORE_MULTIPLE_THUMB(LDMIA, - cpu->gprs[i] = cpu->memory->load32(cpu->memory, address, 0), + cpu->gprs[i] = cpu->memory.load32(cpu, address, 0), + THUMB_LOAD_POST_BODY; if (!((1 << rn) & rs)) { cpu->gprs[rn] = address; }) DEFINE_LOAD_STORE_MULTIPLE_THUMB(STMIA, - cpu->memory->store32(cpu->memory, address, cpu->gprs[i], 0), + cpu->memory.store32(cpu, address, cpu->gprs[i], 0), THUMB_STORE_POST_BODY; cpu->gprs[rn] = address;)

@@ -399,20 +355,22 @@ DEFINE_LOAD_STORE_MULTIPLE_EX_THUMB(POP,

opcode & 0x00FF, cpu->gprs[ARM_SP], (m = 0x01, i = 0; i < 8; m <<= 1, ++i), - cpu->gprs[i] = cpu->memory->load32(cpu->memory, address, 0), + cpu->gprs[i] = cpu->memory.load32(cpu, address, 0), +=, - , , + , + THUMB_LOAD_POST_BODY;, cpu->gprs[ARM_SP] = address) DEFINE_LOAD_STORE_MULTIPLE_EX_THUMB(POPR, opcode & 0x00FF, cpu->gprs[ARM_SP], (m = 0x01, i = 0; i < 8; m <<= 1, ++i), - cpu->gprs[i] = cpu->memory->load32(cpu->memory, address, 0), + cpu->gprs[i] = cpu->memory.load32(cpu, address, 0), +=, , - cpu->gprs[ARM_PC] = cpu->memory->load32(cpu->memory, address, 0) & 0xFFFFFFFE; - address += 4;, + cpu->gprs[ARM_PC] = cpu->memory.load32(cpu, address, 0) & 0xFFFFFFFE; + address += 4; + THUMB_LOAD_POST_BODY;, cpu->gprs[ARM_SP] = address; THUMB_WRITE_PC;)

@@ -420,7 +378,7 @@ DEFINE_LOAD_STORE_MULTIPLE_EX_THUMB(PUSH,

opcode & 0x00FF, cpu->gprs[ARM_SP] - 4, (m = 0x80, i = 7; m; m >>= 1, --i), - cpu->memory->store32(cpu->memory, address, cpu->gprs[i], 0), + cpu->memory.store32(cpu, address, cpu->gprs[i], 0), -=, , THUMB_STORE_POST_BODY,

@@ -430,9 +388,9 @@ DEFINE_LOAD_STORE_MULTIPLE_EX_THUMB(PUSHR,

opcode & 0x00FF, cpu->gprs[ARM_SP] - 4, (m = 0x80, i = 7; m; m >>= 1, --i), - cpu->memory->store32(cpu->memory, address, cpu->gprs[i], 0), + cpu->memory.store32(cpu, address, cpu->gprs[i], 0), -=, - cpu->memory->store32(cpu->memory, address, cpu->gprs[ARM_LR], 0); + cpu->memory.store32(cpu, address, cpu->gprs[ARM_LR], 0); address -= 4;, THUMB_STORE_POST_BODY, cpu->gprs[ARM_SP] = address + 4)

@@ -469,116 +427,7 @@ } else {

ARM_WRITE_PC; }) -DEFINE_INSTRUCTION_THUMB(SWI, cpu->board->swi16(cpu->board, opcode & 0xFF)) - -#define DECLARE_INSTRUCTION_THUMB(EMITTER, NAME) \ - EMITTER ## NAME - -#define DECLARE_INSTRUCTION_WITH_HIGH_THUMB(EMITTER, NAME) \ - DECLARE_INSTRUCTION_THUMB(EMITTER, NAME ## 00), \ - DECLARE_INSTRUCTION_THUMB(EMITTER, NAME ## 01), \ - DECLARE_INSTRUCTION_THUMB(EMITTER, NAME ## 10), \ - DECLARE_INSTRUCTION_THUMB(EMITTER, NAME ## 11) - -#define DUMMY(X, ...) X, -#define DUMMY_4(...) \ - DUMMY(__VA_ARGS__) \ - DUMMY(__VA_ARGS__) \ - DUMMY(__VA_ARGS__) \ - DUMMY(__VA_ARGS__) - -#define DECLARE_THUMB_EMITTER_BLOCK(EMITTER) \ - APPLY(COUNT_5, DUMMY, DECLARE_INSTRUCTION_THUMB(EMITTER, LSL1_)) \ - APPLY(COUNT_5, DUMMY, DECLARE_INSTRUCTION_THUMB(EMITTER, LSR1_)) \ - APPLY(COUNT_5, DUMMY, DECLARE_INSTRUCTION_THUMB(EMITTER, ASR1_)) \ - APPLY(COUNT_3, DUMMY, DECLARE_INSTRUCTION_THUMB(EMITTER, ADD3_R)) \ - APPLY(COUNT_3, DUMMY, DECLARE_INSTRUCTION_THUMB(EMITTER, SUB3_R)) \ - APPLY(COUNT_3, DUMMY, DECLARE_INSTRUCTION_THUMB(EMITTER, ADD1_)) \ - APPLY(COUNT_3, DUMMY, DECLARE_INSTRUCTION_THUMB(EMITTER, SUB1_)) \ - APPLY(COUNT_3, DUMMY_4, DECLARE_INSTRUCTION_THUMB(EMITTER, MOV1_R)) \ - APPLY(COUNT_3, DUMMY_4, DECLARE_INSTRUCTION_THUMB(EMITTER, CMP1_R)) \ - APPLY(COUNT_3, DUMMY_4, DECLARE_INSTRUCTION_THUMB(EMITTER, ADD2_R)) \ - APPLY(COUNT_3, DUMMY_4, DECLARE_INSTRUCTION_THUMB(EMITTER, SUB2_R)) \ - DECLARE_INSTRUCTION_THUMB(EMITTER, AND), \ - DECLARE_INSTRUCTION_THUMB(EMITTER, EOR), \ - DECLARE_INSTRUCTION_THUMB(EMITTER, LSL2), \ - DECLARE_INSTRUCTION_THUMB(EMITTER, LSR2), \ - DECLARE_INSTRUCTION_THUMB(EMITTER, ASR2), \ - DECLARE_INSTRUCTION_THUMB(EMITTER, ADC), \ - DECLARE_INSTRUCTION_THUMB(EMITTER, SBC), \ - DECLARE_INSTRUCTION_THUMB(EMITTER, ROR), \ - DECLARE_INSTRUCTION_THUMB(EMITTER, TST), \ - DECLARE_INSTRUCTION_THUMB(EMITTER, NEG), \ - DECLARE_INSTRUCTION_THUMB(EMITTER, CMP2), \ - DECLARE_INSTRUCTION_THUMB(EMITTER, CMN), \ - DECLARE_INSTRUCTION_THUMB(EMITTER, ORR), \ - DECLARE_INSTRUCTION_THUMB(EMITTER, MUL), \ - DECLARE_INSTRUCTION_THUMB(EMITTER, BIC), \ - DECLARE_INSTRUCTION_THUMB(EMITTER, MVN), \ - DECLARE_INSTRUCTION_WITH_HIGH_THUMB(EMITTER, ADD4), \ - DECLARE_INSTRUCTION_WITH_HIGH_THUMB(EMITTER, CMP3), \ - DECLARE_INSTRUCTION_WITH_HIGH_THUMB(EMITTER, MOV3), \ - DECLARE_INSTRUCTION_THUMB(EMITTER, BX), \ - DECLARE_INSTRUCTION_THUMB(EMITTER, BX), \ - DECLARE_INSTRUCTION_THUMB(EMITTER, ILL), \ - DECLARE_INSTRUCTION_THUMB(EMITTER, ILL), \ - APPLY(COUNT_3, DUMMY_4, DECLARE_INSTRUCTION_THUMB(EMITTER, LDR3_R)) \ - APPLY(COUNT_3, DUMMY, DECLARE_INSTRUCTION_THUMB(EMITTER, STR2_R)) \ - APPLY(COUNT_3, DUMMY, DECLARE_INSTRUCTION_THUMB(EMITTER, STRH2_R)) \ - APPLY(COUNT_3, DUMMY, DECLARE_INSTRUCTION_THUMB(EMITTER, STRB2_R)) \ - APPLY(COUNT_3, DUMMY, DECLARE_INSTRUCTION_THUMB(EMITTER, LDRSB_R)) \ - APPLY(COUNT_3, DUMMY, DECLARE_INSTRUCTION_THUMB(EMITTER, LDR2_R)) \ - APPLY(COUNT_3, DUMMY, DECLARE_INSTRUCTION_THUMB(EMITTER, LDRH2_R)) \ - APPLY(COUNT_3, DUMMY, DECLARE_INSTRUCTION_THUMB(EMITTER, LDRB2_R)) \ - APPLY(COUNT_3, DUMMY, DECLARE_INSTRUCTION_THUMB(EMITTER, LDRSH_R)) \ - APPLY(COUNT_5, DUMMY, DECLARE_INSTRUCTION_THUMB(EMITTER, STR1_)) \ - APPLY(COUNT_5, DUMMY, DECLARE_INSTRUCTION_THUMB(EMITTER, LDR1_)) \ - APPLY(COUNT_5, DUMMY, DECLARE_INSTRUCTION_THUMB(EMITTER, STRB1_)) \ - APPLY(COUNT_5, DUMMY, DECLARE_INSTRUCTION_THUMB(EMITTER, LDRB1_)) \ - APPLY(COUNT_5, DUMMY, DECLARE_INSTRUCTION_THUMB(EMITTER, STRH1_)) \ - APPLY(COUNT_5, DUMMY, DECLARE_INSTRUCTION_THUMB(EMITTER, LDRH1_)) \ - APPLY(COUNT_3, DUMMY_4, DECLARE_INSTRUCTION_THUMB(EMITTER, STR3_R)) \ - APPLY(COUNT_3, DUMMY_4, DECLARE_INSTRUCTION_THUMB(EMITTER, LDR4_R)) \ - APPLY(COUNT_3, DUMMY_4, DECLARE_INSTRUCTION_THUMB(EMITTER, ADD5_R)) \ - APPLY(COUNT_3, DUMMY_4, DECLARE_INSTRUCTION_THUMB(EMITTER, ADD6_R)) \ - DECLARE_INSTRUCTION_THUMB(EMITTER, ADD7), \ - DECLARE_INSTRUCTION_THUMB(EMITTER, ADD7), \ - DECLARE_INSTRUCTION_THUMB(EMITTER, SUB4), \ - DECLARE_INSTRUCTION_THUMB(EMITTER, SUB4), \ - DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, ILL)), \ - DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, ILL)), \ - DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, ILL)), \ - DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, PUSH)), \ - DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, PUSHR)), \ - DO_8(DECLARE_INSTRUCTION_THUMB(EMITTER, ILL)), \ - DO_8(DECLARE_INSTRUCTION_THUMB(EMITTER, ILL)), \ - DO_8(DECLARE_INSTRUCTION_THUMB(EMITTER, ILL)), \ - DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, POP)), \ - DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, POPR)), \ - DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, BKPT)), \ - DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, ILL)), \ - APPLY(COUNT_3, DUMMY_4, DECLARE_INSTRUCTION_THUMB(EMITTER, STMIA_R)) \ - APPLY(COUNT_3, DUMMY_4, DECLARE_INSTRUCTION_THUMB(EMITTER, LDMIA_R)) \ - DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, BEQ)), \ - DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, BNE)), \ - DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, BCS)), \ - DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, BCC)), \ - DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, BMI)), \ - DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, BPL)), \ - DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, BVS)), \ - DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, BVC)), \ - DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, BHI)), \ - DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, BLS)), \ - DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, BGE)), \ - DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, BLT)), \ - DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, BGT)), \ - DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, BLE)), \ - DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, ILL)), \ - DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, SWI)), \ - DO_8(DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, B))), \ - DO_8(DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, ILL))), \ - DO_8(DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, BL1))), \ - DO_8(DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, BL2))) +DEFINE_INSTRUCTION_THUMB(SWI, cpu->irqh.swi16(cpu, opcode & 0xFF)) const ThumbInstruction _thumbTable[0x400] = { DECLARE_THUMB_EMITTER_BLOCK(_ThumbInstruction)
M src/arm/isa-thumb.hsrc/arm/isa-thumb.h

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

#ifndef ISA_THUMB_H #define ISA_THUMB_H -#include <stdint.h> +#include "common.h" struct ARMCore;
M src/debugger/cli-debugger.csrc/debugger/cli-debugger.c

@@ -1,11 +1,8 @@

#include "cli-debugger.h" +#include "decoder.h" +#include "parser.h" #include <signal.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> #ifdef USE_PTHREADS #include <pthread.h>

@@ -14,9 +11,9 @@

struct DebugVector { struct DebugVector* next; enum DVType { - ERROR_TYPE, - INT_TYPE, - CHAR_TYPE + DV_ERROR_TYPE, + DV_INT_TYPE, + DV_CHAR_TYPE } type; union { int32_t intValue;

@@ -32,6 +29,7 @@ typedef void (DebuggerCommand)(struct CLIDebugger*, struct DebugVector*);

static void _breakInto(struct CLIDebugger*, struct DebugVector*); static void _continue(struct CLIDebugger*, struct DebugVector*); +static void _disassemble(struct CLIDebugger*, struct DebugVector*); static void _next(struct CLIDebugger*, struct DebugVector*); static void _print(struct CLIDebugger*, struct DebugVector*); static void _printHex(struct CLIDebugger*, struct DebugVector*);

@@ -45,6 +43,7 @@ static void _clearBreakpoint(struct CLIDebugger*, struct DebugVector*);

static void _setWatchpoint(struct CLIDebugger*, struct DebugVector*); static void _breakIntoDefault(int signal); +static void _printLine(struct CLIDebugger* debugger, uint32_t address, enum ExecutionMode mode); static struct { const char* name;

@@ -56,6 +55,8 @@ { "c", _continue },

{ "continue", _continue }, { "d", _clearBreakpoint }, { "delete", _clearBreakpoint }, + { "dis", _disassemble }, + { "disasm", _disassemble }, { "i", _printStatus }, { "info", _printStatus }, { "n", _next },

@@ -88,13 +89,13 @@ psr.t ? 'T' : '-');

} static void _handleDeath(int sig) { - (void)(sig); + UNUSED(sig); printf("No debugger attached!\n"); } static void _breakInto(struct CLIDebugger* debugger, struct DebugVector* dv) { - (void)(debugger); - (void)(dv); + UNUSED(debugger); + UNUSED(dv); struct sigaction sa, osa; sa.sa_handler = _handleDeath; sigemptyset(&sa.sa_mask);

@@ -110,18 +111,51 @@ sigaction(SIGTRAP, &osa, 0);

} static void _continue(struct CLIDebugger* debugger, struct DebugVector* dv) { - (void)(dv); + UNUSED(dv); debugger->d.state = DEBUGGER_RUNNING; } static void _next(struct CLIDebugger* debugger, struct DebugVector* dv) { - (void)(dv); + UNUSED(dv); ARMRun(debugger->d.cpu); _printStatus(debugger, 0); } +static void _disassemble(struct CLIDebugger* debugger, struct DebugVector* dv) { + uint32_t address; + int size; + int wordSize; + enum ExecutionMode mode = debugger->d.cpu->executionMode; + + if (mode == MODE_ARM) { + wordSize = WORD_SIZE_ARM; + } else { + wordSize = WORD_SIZE_THUMB; + } + + if (!dv || dv->type != DV_INT_TYPE) { + address = debugger->d.cpu->gprs[ARM_PC] - wordSize; + } else { + address = dv->intValue; + dv = dv->next; + } + + if (!dv || dv->type != DV_INT_TYPE) { + size = 1; + } else { + size = dv->intValue; + dv = dv->next; // TODO: Check for excess args + } + + int i; + for (i = 0; i < size; ++i) { + _printLine(debugger, address, mode); + address += wordSize; + } +} + static void _print(struct CLIDebugger* debugger, struct DebugVector* dv) { - (void)(debugger); + UNUSED(debugger); for ( ; dv; dv = dv->next) { printf(" %u", dv->intValue); }

@@ -129,7 +163,7 @@ printf("\n");

} static void _printHex(struct CLIDebugger* debugger, struct DebugVector* dv) { - (void)(debugger); + UNUSED(debugger); for ( ; dv; dv = dv->next) { printf(" 0x%08X", dv->intValue); }

@@ -137,18 +171,23 @@ printf("\n");

} static inline void _printLine(struct CLIDebugger* debugger, uint32_t address, enum ExecutionMode mode) { - // TODO: write a disassembler + char disassembly[48]; + struct ARMInstructionInfo info; if (mode == MODE_ARM) { - uint32_t instruction = debugger->d.cpu->memory->load32(debugger->d.cpu->memory, address, 0); - printf("%08X\n", instruction); + uint32_t instruction = debugger->d.cpu->memory.load32(debugger->d.cpu, address, 0); + ARMDecodeARM(instruction, &info); + ARMDisassemble(&info, address + WORD_SIZE_ARM * 2, disassembly, sizeof(disassembly)); + printf("%08X: %s\n", instruction, disassembly); } else { - uint16_t instruction = debugger->d.cpu->memory->loadU16(debugger->d.cpu->memory, address, 0); - printf("%04X\n", instruction); + uint16_t instruction = debugger->d.cpu->memory.loadU16(debugger->d.cpu, address, 0); + ARMDecodeThumb(instruction, &info); + ARMDisassemble(&info, address + WORD_SIZE_THUMB * 2, disassembly, sizeof(disassembly)); + printf("%04X: %s\n", instruction, disassembly); } } static void _printStatus(struct CLIDebugger* debugger, struct DebugVector* dv) { - (void)(dv); + UNUSED(dv); int r; for (r = 0; r < 4; ++r) { printf("%08X %08X %08X %08X\n",

@@ -169,42 +208,42 @@ _printLine(debugger, debugger->d.cpu->gprs[ARM_PC] - instructionLength, mode);

} static void _quit(struct CLIDebugger* debugger, struct DebugVector* dv) { - (void)(dv); + UNUSED(dv); debugger->d.state = DEBUGGER_SHUTDOWN; } static void _readByte(struct CLIDebugger* debugger, struct DebugVector* dv) { - if (!dv || dv->type != INT_TYPE) { + if (!dv || dv->type != DV_INT_TYPE) { printf("%s\n", ERROR_MISSING_ARGS); return; } uint32_t address = dv->intValue; - uint8_t value = debugger->d.cpu->memory->loadU8(debugger->d.cpu->memory, address, 0); + uint8_t value = debugger->d.cpu->memory.loadU8(debugger->d.cpu, address, 0); printf(" 0x%02X\n", value); } static void _readHalfword(struct CLIDebugger* debugger, struct DebugVector* dv) { - if (!dv || dv->type != INT_TYPE) { + if (!dv || dv->type != DV_INT_TYPE) { printf("%s\n", ERROR_MISSING_ARGS); return; } uint32_t address = dv->intValue; - uint16_t value = debugger->d.cpu->memory->loadU16(debugger->d.cpu->memory, address, 0); + uint16_t value = debugger->d.cpu->memory.loadU16(debugger->d.cpu, address, 0); printf(" 0x%04X\n", value); } static void _readWord(struct CLIDebugger* debugger, struct DebugVector* dv) { - if (!dv || dv->type != INT_TYPE) { + if (!dv || dv->type != DV_INT_TYPE) { printf("%s\n", ERROR_MISSING_ARGS); return; } uint32_t address = dv->intValue; - uint32_t value = debugger->d.cpu->memory->load32(debugger->d.cpu->memory, address, 0); + uint32_t value = debugger->d.cpu->memory.load32(debugger->d.cpu, address, 0); printf(" 0x%08X\n", value); } static void _setBreakpoint(struct CLIDebugger* debugger, struct DebugVector* dv) { - if (!dv || dv->type != INT_TYPE) { + if (!dv || dv->type != DV_INT_TYPE) { printf("%s\n", ERROR_MISSING_ARGS); return; }

@@ -213,7 +252,7 @@ ARMDebuggerSetBreakpoint(&debugger->d, address);

} static void _clearBreakpoint(struct CLIDebugger* debugger, struct DebugVector* dv) { - if (!dv || dv->type != INT_TYPE) { + if (!dv || dv->type != DV_INT_TYPE) { printf("%s\n", ERROR_MISSING_ARGS); return; }

@@ -222,7 +261,7 @@ ARMDebuggerClearBreakpoint(&debugger->d, address);

} static void _setWatchpoint(struct CLIDebugger* debugger, struct DebugVector* dv) { - if (!dv || dv->type != INT_TYPE) { + if (!dv || dv->type != DV_INT_TYPE) { printf("%s\n", ERROR_MISSING_ARGS); return; }

@@ -231,239 +270,117 @@ ARMDebuggerSetWatchpoint(&debugger->d, address);

} static void _breakIntoDefault(int signal) { - (void)(signal); + UNUSED(signal); ARMDebuggerEnter(&_activeDebugger->d, DEBUGGER_ENTER_MANUAL); } -enum _DVParseState { - PARSE_ERROR = -1, - PARSE_ROOT = 0, - PARSE_EXPECT_REGISTER, - PARSE_EXPECT_REGISTER_2, - PARSE_EXPECT_LR, - PARSE_EXPECT_PC, - PARSE_EXPECT_SP, - PARSE_EXPECT_DECIMAL, - PARSE_EXPECT_HEX, - PARSE_EXPECT_PREFIX, - PARSE_EXPECT_SUFFIX, -}; +static uint32_t _performOperation(enum Operation operation, uint32_t current, uint32_t next, struct DebugVector* dv) { + switch (operation) { + case OP_ASSIGN: + current = next; + break; + case OP_ADD: + current += next; + break; + case OP_SUBTRACT: + current -= next; + break; + case OP_MULTIPLY: + current *= next; + break; + case OP_DIVIDE: + if (next != 0) { + current /= next; + } else { + dv->type = DV_ERROR_TYPE; + return 0; + } + break; + } + return current; +} + +static uint32_t _lookupIdentifier(struct ARMDebugger* debugger, const char* name, struct DebugVector* dv) { + if (strcmp(name, "sp") == 0) { + return debugger->cpu->gprs[ARM_SP]; + } + if (strcmp(name, "lr") == 0) { + return debugger->cpu->gprs[ARM_LR]; + } + if (strcmp(name, "pc") == 0) { + return debugger->cpu->gprs[ARM_PC]; + } + if (strcmp(name, "cpsr") == 0) { + return debugger->cpu->cpsr.packed; + } + // TODO: test if mode has SPSR + if (strcmp(name, "spsr") == 0) { + return debugger->cpu->spsr.packed; + } + if (name[0] == 'r' && name[1] >= '0' && name[1] <= '9') { + int reg = atoi(&name[1]); + if (reg < 16) { + return debugger->cpu->gprs[reg]; + } + } + dv->type = DV_ERROR_TYPE; + return 0; +} + +static uint32_t _evaluateParseTree(struct ARMDebugger* debugger, struct ParseTree* tree, struct DebugVector* dv) { + switch (tree->token.type) { + case TOKEN_UINT_TYPE: + return tree->token.uintValue; + case TOKEN_OPERATOR_TYPE: + return _performOperation(tree->token.operatorValue, _evaluateParseTree(debugger, tree->lhs, dv), _evaluateParseTree(debugger, tree->rhs, dv), dv); + case TOKEN_IDENTIFIER_TYPE: + return _lookupIdentifier(debugger, tree->token.identifierValue, dv); + case TOKEN_ERROR_TYPE: + default: + dv->type = DV_ERROR_TYPE; + } + return 0; +} static struct DebugVector* _DVParse(struct CLIDebugger* debugger, const char* string, size_t length) { if (!string || length < 1) { return 0; } - enum _DVParseState state = PARSE_ROOT; - struct DebugVector dvTemp = { .type = INT_TYPE }; - uint32_t current = 0; + struct DebugVector dvTemp = { .type = DV_INT_TYPE }; - while (length > 0 && string[0] && string[0] != ' ' && state != PARSE_ERROR) { - char token = string[0]; - ++string; - --length; - switch (state) { - case PARSE_ROOT: - switch (token) { - case 'r': - state = PARSE_EXPECT_REGISTER; - break; - case 'p': - state = PARSE_EXPECT_PC; - break; - case 's': - state = PARSE_EXPECT_SP; - break; - case 'l': - state = PARSE_EXPECT_LR; - break; - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - state = PARSE_EXPECT_DECIMAL; - current = token - '0'; - break; - case '0': - state = PARSE_EXPECT_PREFIX; - break; - case '$': - state = PARSE_EXPECT_HEX; - current = 0; - break; - default: - state = PARSE_ERROR; - break; - }; - break; - case PARSE_EXPECT_LR: - switch (token) { - case 'r': - current = debugger->d.cpu->gprs[ARM_LR]; - state = PARSE_EXPECT_SUFFIX; - break; - default: - state = PARSE_ERROR; - break; - } - break; - case PARSE_EXPECT_PC: - switch (token) { - case 'c': - current = debugger->d.cpu->gprs[ARM_PC]; - state = PARSE_EXPECT_SUFFIX; - break; - default: - state = PARSE_ERROR; - break; - } - break; - case PARSE_EXPECT_SP: - switch (token) { - case 'p': - current = debugger->d.cpu->gprs[ARM_SP]; - state = PARSE_EXPECT_SUFFIX; - break; - default: - state = PARSE_ERROR; - break; - } - break; - case PARSE_EXPECT_REGISTER: - switch (token) { - case '0': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - current = debugger->d.cpu->gprs[token - '0']; - state = PARSE_EXPECT_SUFFIX; - break; - case '1': - state = PARSE_EXPECT_REGISTER_2; - break; - default: - state = PARSE_ERROR; - break; - } - break; - case PARSE_EXPECT_REGISTER_2: - switch (token) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - current = debugger->d.cpu->gprs[token - '0' + 10]; - state = PARSE_EXPECT_SUFFIX; - break; - default: - state = PARSE_ERROR; - break; - } - break; - case PARSE_EXPECT_DECIMAL: - switch (token) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - // TODO: handle overflow - current *= 10; - current += token - '0'; - break; - default: - state = PARSE_ERROR; - } - break; - case PARSE_EXPECT_HEX: - switch (token) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - // TODO: handle overflow - current *= 16; - current += token - '0'; - break; - case 'A': - case 'B': - case 'C': - case 'D': - case 'E': - case 'F': - // TODO: handle overflow - current *= 16; - current += token - 'A' + 10; - break; - case 'a': - case 'b': - case 'c': - case 'd': - case 'e': - case 'f': - // TODO: handle overflow - current *= 16; - current += token - 'a' + 10; - break; - default: - state = PARSE_ERROR; - break; - } - break; - case PARSE_EXPECT_PREFIX: - switch (token) { - case 'X': - case 'x': - current = 0; - state = PARSE_EXPECT_HEX; - break; - default: - state = PARSE_ERROR; - break; - } - break; - case PARSE_EXPECT_SUFFIX: - // TODO - state = PARSE_ERROR; - break; - case PARSE_ERROR: - // This shouldn't be reached - break; - } + struct LexVector lv = { .next = 0 }; + size_t adjusted = lexExpression(&lv, string, length); + if (adjusted > length) { + dvTemp.type = DV_ERROR_TYPE; + lexFree(lv.next); + } + + struct ParseTree tree; + parseLexedExpression(&tree, &lv); + if (tree.token.type == TOKEN_ERROR_TYPE) { + dvTemp.type = DV_ERROR_TYPE; + } else { + dvTemp.intValue = _evaluateParseTree(&debugger->d, &tree, &dvTemp); } + parseFree(tree.lhs); + parseFree(tree.rhs); + + length -= adjusted; + string += adjusted; + struct DebugVector* dv = malloc(sizeof(struct DebugVector)); - if (state == PARSE_ERROR) { - dv->type = ERROR_TYPE; + if (dvTemp.type == DV_ERROR_TYPE) { + dv->type = DV_ERROR_TYPE; dv->next = 0; } else { - dvTemp.intValue = current; *dv = dvTemp; if (string[0] == ' ') { dv->next = _DVParse(debugger, string + 1, length - 1); + if (dv->next && dv->next->type == DV_ERROR_TYPE) { + dv->type = DV_ERROR_TYPE; + } } } return dv;

@@ -478,17 +395,17 @@ dv = next;

} } -static int _parse(struct CLIDebugger* debugger, const char* line, size_t count) { +static bool _parse(struct CLIDebugger* debugger, const char* line, size_t count) { const char* firstSpace = strchr(line, ' '); size_t cmdLength; struct DebugVector* dv = 0; if (firstSpace) { cmdLength = firstSpace - line; dv = _DVParse(debugger, firstSpace + 1, count - cmdLength - 1); - if (dv && dv->type == ERROR_TYPE) { + if (dv && dv->type == DV_ERROR_TYPE) { printf("Parse error\n"); _DVFree(dv); - return 0; + return false; } } else { cmdLength = count;

@@ -503,16 +420,16 @@ }

if (strncasecmp(name, line, cmdLength) == 0) { _debuggerCommands[i].command(debugger, dv); _DVFree(dv); - return 1; + return true; } } _DVFree(dv); printf("Command not found\n"); - return 0; + return false; } static char* _prompt(EditLine* el) { - (void)(el); + UNUSED(el); return "> "; }

@@ -541,7 +458,7 @@ }

} static void _reportEntry(struct ARMDebugger* debugger, enum DebuggerEntryReason reason) { - (void) (debugger); + UNUSED(debugger); switch (reason) { case DEBUGGER_ENTER_MANUAL: case DEBUGGER_ENTER_ATTACHED:

@@ -559,7 +476,7 @@ }

} static unsigned char _tabComplete(EditLine* elstate, int ch) { - (void)(ch); + UNUSED(ch); const LineInfo* li = el_line(elstate); const char* commandPtr; int cmd = 0, len = 0;

@@ -587,7 +504,7 @@

static void _cliDebuggerInit(struct ARMDebugger* debugger) { struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger; // TODO: get argv[0] - cliDebugger->elstate = el_init("gbac", stdin, stdout, stderr); + cliDebugger->elstate = el_init(BINARY_NAME, stdin, stdout, stderr); el_set(cliDebugger->elstate, EL_PROMPT, _prompt); el_set(cliDebugger->elstate, EL_EDITOR, "emacs");

@@ -609,6 +526,7 @@ el_end(cliDebugger->elstate);

} void CLIDebuggerCreate(struct CLIDebugger* debugger) { + ARMDebuggerCreate(&debugger->d); debugger->d.init = _cliDebuggerInit; debugger->d.deinit = _cliDebuggerDeinit; debugger->d.paused = _commandLine;
M src/debugger/cli-debugger.hsrc/debugger/cli-debugger.h

@@ -1,6 +1,8 @@

#ifndef CLI_DEBUGGER_H #define CLI_DEBUGGER_H +#include "common.h" + #include "debugger.h" #include <histedit.h>
M src/debugger/debugger.csrc/debugger/debugger.c

@@ -4,7 +4,7 @@ #include "arm.h"

#include "memory-debugger.h" -#include <stdlib.h> +const uint32_t ARM_DEBUGGER_ID = 0xDEADBEEF; static void _checkBreakpoints(struct ARMDebugger* debugger) { struct DebugBreakpoint* breakpoint;

@@ -23,20 +23,29 @@ }

} } -void ARMDebuggerInit(struct ARMDebugger* debugger, struct ARMCore* cpu) { +static void ARMDebuggerInit(struct ARMCore*, struct ARMComponent*); +static void ARMDebuggerDeinit(struct ARMComponent*); + +void ARMDebuggerCreate(struct ARMDebugger* debugger) { + debugger->d.id = ARM_DEBUGGER_ID; + debugger->d.init = ARMDebuggerInit; + debugger->d.deinit = ARMDebuggerDeinit; +} + +void ARMDebuggerInit(struct ARMCore* cpu, struct ARMComponent* component) { + struct ARMDebugger* debugger = (struct ARMDebugger*) component; debugger->cpu = cpu; debugger->state = DEBUGGER_RUNNING; debugger->breakpoints = 0; - debugger->memoryShim.original = cpu->memory; - debugger->memoryShim.p = debugger; - debugger->memoryShim.watchpoints = 0; + debugger->originalMemory = cpu->memory; + debugger->watchpoints = 0; if (debugger->init) { debugger->init(debugger); } } -void ARMDebuggerDeinit(struct ARMDebugger* debugger) { - // TODO: actually call this +void ARMDebuggerDeinit(struct ARMComponent* component) { + struct ARMDebugger* debugger = (struct ARMDebugger*) component; debugger->deinit(debugger); }

@@ -98,11 +107,11 @@ }

} void ARMDebuggerSetWatchpoint(struct ARMDebugger* debugger, uint32_t address) { - if (debugger->cpu->memory != &debugger->memoryShim.d) { + if (!debugger->watchpoints) { ARMDebuggerInstallMemoryShim(debugger); } struct DebugBreakpoint* watchpoint = malloc(sizeof(struct DebugBreakpoint)); watchpoint->address = address; - watchpoint->next = debugger->memoryShim.watchpoints; - debugger->memoryShim.watchpoints = watchpoint; + watchpoint->next = debugger->watchpoints; + debugger->watchpoints = watchpoint; }
M src/debugger/debugger.hsrc/debugger/debugger.h

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

#ifndef DEBUGGER_H #define DEBUGGER_H +#include "common.h" + #include "arm.h" + +const uint32_t ARM_DEBUGGER_ID; enum DebuggerState { DEBUGGER_PAUSED,

@@ -15,14 +19,6 @@ struct DebugBreakpoint* next;

uint32_t address; }; -struct DebugMemoryShim { - struct ARMMemory d; - struct ARMMemory* original; - - struct ARMDebugger* p; - struct DebugBreakpoint* watchpoints; -}; - enum DebuggerEntryReason { DEBUGGER_ENTER_MANUAL, DEBUGGER_ENTER_ATTACHED,

@@ -39,11 +35,13 @@ DEBUGGER_LOG_ERROR = 0x08

}; struct ARMDebugger { + struct ARMComponent d; enum DebuggerState state; struct ARMCore* cpu; struct DebugBreakpoint* breakpoints; - struct DebugMemoryShim memoryShim; + struct DebugBreakpoint* watchpoints; + struct ARMMemory originalMemory; void (*init)(struct ARMDebugger*); void (*deinit)(struct ARMDebugger*);

@@ -54,8 +52,7 @@ __attribute__((format (printf, 3, 4)))

void (*log)(struct ARMDebugger*, enum DebuggerLogLevel, const char* format, ...); }; -void ARMDebuggerInit(struct ARMDebugger*, struct ARMCore*); -void ARMDebuggerDeinit(struct ARMDebugger*); +void ARMDebuggerCreate(struct ARMDebugger*); void ARMDebuggerRun(struct ARMDebugger*); void ARMDebuggerEnter(struct ARMDebugger*, enum DebuggerEntryReason); void ARMDebuggerSetBreakpoint(struct ARMDebugger* debugger, uint32_t address);
M src/debugger/gdb-stub.csrc/debugger/gdb-stub.c

@@ -2,7 +2,10 @@ #include "gdb-stub.h"

#include <errno.h> #include <signal.h> -#include <string.h> + +#ifndef SIGTRAP +#define SIGTRAP 5 /* Win32 Signals do not include SIGTRAP */ +#endif enum GDBError { GDB_NO_ERROR = 0x00,

@@ -19,7 +22,7 @@ static void _sendMessage(struct GDBStub* stub);

static void _gdbStubDeinit(struct ARMDebugger* debugger) { struct GDBStub* stub = (struct GDBStub*) debugger; - if (stub->socket >= 0) { + if (!SOCKET_FAILED(stub->socket)) { GDBStubShutdown(stub); } }

@@ -46,7 +49,7 @@

static void _gdbStubPoll(struct ARMDebugger* debugger) { struct GDBStub* stub = (struct GDBStub*) debugger; while (stub->d.state == DEBUGGER_PAUSED) { - if (stub->connection >= 0) { + if (!SOCKET_FAILED(stub->connection)) { if (!SocketSetBlocking(stub->connection, 1)) { GDBStubHangup(stub); return;

@@ -162,14 +165,14 @@ }

static void _continue(struct GDBStub* stub, const char* message) { stub->d.state = DEBUGGER_RUNNING; - if (stub->connection >= 0) { + if (!SOCKET_FAILED(stub->connection)) { if (!SocketSetBlocking(stub->connection, 0)) { GDBStubHangup(stub); return; } } // TODO: parse message - (void) (message); + UNUSED(message); } static void _step(struct GDBStub* stub, const char* message) {

@@ -177,7 +180,7 @@ ARMRun(stub->d.cpu);

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

@@ -190,10 +193,10 @@ if (size > 512) {

_error(stub, GDB_BAD_ARGUMENTS); return; } - struct ARMMemory* memory = stub->d.memoryShim.original; + struct ARMCore* cpu = stub->d.cpu; int writeAddress = 0; for (i = 0; i < size; ++i, writeAddress += 2) { - uint8_t byte = memory->load8(memory, address + i, 0); + uint8_t byte = cpu->memory.load8(cpu, address + i, 0); _int2hex8(byte, &stub->outgoing[writeAddress]); } stub->outgoing[writeAddress] = 0;

@@ -201,7 +204,7 @@ _sendMessage(stub);

} static void _readGPRs(struct GDBStub* stub, const char* message) { - (void) (message); + UNUSED(message); int r; int i = 0; for (r = 0; r < 16; ++r) {

@@ -261,7 +264,7 @@ _sendMessage(stub);

} static void _processVWriteCommand(struct GDBStub* stub, const char* message) { - (void) (message); + UNUSED(message); stub->outgoing[0] = '\0'; _sendMessage(stub); }

@@ -284,7 +287,7 @@ unsigned i = 0;

uint32_t address = _readHex(readAddress, &i); readAddress += i + 1; uint32_t kind = _readHex(readAddress, &i); // We don't use this in hardware watchpoints - (void) (kind); + UNUSED(kind); ARMDebuggerSetBreakpoint(&stub->d, address); strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4); _sendMessage(stub);

@@ -421,8 +424,9 @@ return parsed;

} void GDBStubCreate(struct GDBStub* stub) { - stub->socket = -1; - stub->connection = -1; + ARMDebuggerCreate(&stub->d); + stub->socket = INVALID_SOCKET; + stub->connection = INVALID_SOCKET; stub->d.init = 0; stub->d.deinit = _gdbStubDeinit; stub->d.paused = _gdbStubPoll;

@@ -431,12 +435,12 @@ stub->d.log = 0;

} int GDBStubListen(struct GDBStub* stub, int port, uint32_t bindAddress) { - if (stub->socket >= 0) { + if (!SOCKET_FAILED(stub->socket)) { GDBStubShutdown(stub); } // TODO: support IPv6 stub->socket = SocketOpenTCP(port, bindAddress); - if (stub->socket < 0) { + if (SOCKET_FAILED(stub->socket)) { if (stub->d.log) { stub->d.log(&stub->d, DEBUGGER_LOG_ERROR, "Couldn't open socket"); }

@@ -462,7 +466,7 @@ return 0;

} void GDBStubHangup(struct GDBStub* stub) { - if (stub->connection >= 0) { + if (!SOCKET_FAILED(stub->connection)) { SocketClose(stub->connection); stub->connection = -1; }

@@ -473,19 +477,19 @@ }

void GDBStubShutdown(struct GDBStub* stub) { GDBStubHangup(stub); - if (stub->socket >= 0) { + if (!SOCKET_FAILED(stub->socket)) { SocketClose(stub->socket); stub->socket = -1; } } void GDBStubUpdate(struct GDBStub* stub) { - if (stub->socket == -1) { + if (stub->socket == INVALID_SOCKET) { return; } - if (stub->connection == -1) { + if (stub->connection == INVALID_SOCKET) { stub->connection = SocketAccept(stub->socket, 0, 0); - if (stub->connection >= 0) { + if (!SOCKET_FAILED(stub->connection)) { if (!SocketSetBlocking(stub->connection, 0)) { goto connectionLost; }

@@ -496,7 +500,7 @@ } else {

goto connectionLost; } } - while (1) { + while (true) { ssize_t messageLen = SocketRecv(stub->connection, stub->line, GDB_STUB_MAX_LINE - 1); if (messageLen == 0) { goto connectionLost;
M src/debugger/gdb-stub.hsrc/debugger/gdb-stub.h

@@ -1,8 +1,11 @@

#ifndef GDB_STUB_H #define GDB_STUB_H -#include "debugger.h" -#include "socket.h" +#include "common.h" + +#include "debugger/debugger.h" + +#include "util/socket.h" #define GDB_STUB_MAX_LINE 1200
M src/debugger/memory-debugger.csrc/debugger/memory-debugger.c

@@ -4,81 +4,68 @@ #include "debugger.h"

#include <string.h> -static void ARMDebuggerShim_store32(struct ARMMemory*, uint32_t address, int32_t value, int* cycleCounter); -static void ARMDebuggerShim_store16(struct ARMMemory*, uint32_t address, int16_t value, int* cycleCounter); -static void ARMDebuggerShim_store8(struct ARMMemory*, uint32_t address, int8_t value, int* cycleCounter); -static void ARMDebuggerShim_setActiveRegion(struct ARMMemory* memory, uint32_t address); +static bool _checkWatchpoints(struct DebugBreakpoint* watchpoints, uint32_t address, int width); + +#define FIND_DEBUGGER(DEBUGGER, CPU) \ + { \ + DEBUGGER = 0; \ + int i; \ + for (i = 0; i < CPU->numComponents; ++i) { \ + if (CPU->components[i]->id == ARM_DEBUGGER_ID) { \ + DEBUGGER = (struct ARMDebugger*) cpu->components[i]; \ + break; \ + } \ + } \ + } #define CREATE_SHIM(NAME, RETURN, TYPES, ARGS...) \ static RETURN ARMDebuggerShim_ ## NAME TYPES { \ - struct DebugMemoryShim* debugMemory = (struct DebugMemoryShim*) memory; \ - return debugMemory->original->NAME(debugMemory->original, ARGS); \ + struct ARMDebugger* debugger; \ + FIND_DEBUGGER(debugger, cpu); \ + return debugger->originalMemory.NAME(cpu, ARGS); \ + } + +#define CREATE_WATCHPOINT_SHIM(NAME, WIDTH, RETURN, TYPES, ARGS...) \ + static RETURN ARMDebuggerShim_ ## NAME TYPES { \ + struct ARMDebugger* debugger; \ + FIND_DEBUGGER(debugger, cpu); \ + if (_checkWatchpoints(debugger->watchpoints, address, WIDTH)) { \ + ARMDebuggerEnter(debugger, DEBUGGER_ENTER_WATCHPOINT); \ + } \ + return debugger->originalMemory.NAME(cpu, ARGS); \ } -CREATE_SHIM(load32, int32_t, (struct ARMMemory* memory, uint32_t address, int* cycleCounter), address, cycleCounter) -CREATE_SHIM(load16, int16_t, (struct ARMMemory* memory, uint32_t address, int* cycleCounter), address, cycleCounter) -CREATE_SHIM(loadU16, uint16_t, (struct ARMMemory* memory, uint32_t address, int* cycleCounter), address, cycleCounter) -CREATE_SHIM(load8, int8_t, (struct ARMMemory* memory, uint32_t address, int* cycleCounter), address, cycleCounter) -CREATE_SHIM(loadU8, uint8_t, (struct ARMMemory* memory, uint32_t address, int* cycleCounter), address, cycleCounter) -CREATE_SHIM(waitMultiple, int, (struct ARMMemory* memory, uint32_t startAddress, int count), startAddress, count) +CREATE_WATCHPOINT_SHIM(load32, 4, int32_t, (struct ARMCore* cpu, uint32_t address, int* cycleCounter), address, cycleCounter) +CREATE_WATCHPOINT_SHIM(load16, 2, int16_t, (struct ARMCore* cpu, uint32_t address, int* cycleCounter), address, cycleCounter) +CREATE_WATCHPOINT_SHIM(loadU16, 2, uint16_t, (struct ARMCore* cpu, uint32_t address, int* cycleCounter), address, cycleCounter) +CREATE_WATCHPOINT_SHIM(load8, 1, int8_t, (struct ARMCore* cpu, uint32_t address, int* cycleCounter), address, cycleCounter) +CREATE_WATCHPOINT_SHIM(loadU8, 1, uint8_t, (struct ARMCore* cpu, uint32_t address, int* cycleCounter), address, cycleCounter) +CREATE_WATCHPOINT_SHIM(store32, 4, void, (struct ARMCore* cpu, uint32_t address, int32_t value, int* cycleCounter), address, value, cycleCounter) +CREATE_WATCHPOINT_SHIM(store16, 2, void, (struct ARMCore* cpu, uint32_t address, int16_t value, int* cycleCounter), address, value, cycleCounter) +CREATE_WATCHPOINT_SHIM(store8, 1, void, (struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCounter), address, value, cycleCounter) +CREATE_SHIM(waitMultiple, int, (struct ARMCore* cpu, uint32_t startAddress, int count), startAddress, count) +CREATE_SHIM(setActiveRegion, void, (struct ARMCore* cpu, uint32_t address), address) -static int _checkWatchpoints(struct DebugBreakpoint* watchpoints, uint32_t address, int width) { +static bool _checkWatchpoints(struct DebugBreakpoint* watchpoints, uint32_t address, int width) { width -= 1; for (; watchpoints; watchpoints = watchpoints->next) { if (!((watchpoints->address ^ address) & ~width)) { - return 1; + return true; } } - return 0; + return false; } void ARMDebuggerInstallMemoryShim(struct ARMDebugger* debugger) { - debugger->memoryShim.original = debugger->cpu->memory; - memcpy(&debugger->memoryShim.d, debugger->cpu->memory, sizeof(struct ARMMemory)); - debugger->memoryShim.d.store32 = ARMDebuggerShim_store32; - debugger->memoryShim.d.store16 = ARMDebuggerShim_store16; - debugger->memoryShim.d.store8 = ARMDebuggerShim_store8; - debugger->memoryShim.d.load32 = ARMDebuggerShim_load32; - debugger->memoryShim.d.load16 = ARMDebuggerShim_load16; - debugger->memoryShim.d.loadU16 = ARMDebuggerShim_loadU16; - debugger->memoryShim.d.load8 = ARMDebuggerShim_load8; - debugger->memoryShim.d.loadU8 = ARMDebuggerShim_loadU8; - debugger->memoryShim.d.setActiveRegion = ARMDebuggerShim_setActiveRegion; - debugger->memoryShim.d.waitMultiple = ARMDebuggerShim_waitMultiple; - debugger->cpu->memory = &debugger->memoryShim.d; -} - -void ARMDebuggerShim_store32(struct ARMMemory* memory, uint32_t address, int32_t value, int* cycleCounter) { - struct DebugMemoryShim* debugMemory = (struct DebugMemoryShim*) memory; - if (_checkWatchpoints(debugMemory->watchpoints, address, 4)) { - ARMDebuggerEnter(debugMemory->p, DEBUGGER_ENTER_WATCHPOINT); - } - debugMemory->original->store32(debugMemory->original, address, value, cycleCounter); -} - -void ARMDebuggerShim_store16(struct ARMMemory* memory, uint32_t address, int16_t value, int* cycleCounter) { - struct DebugMemoryShim* debugMemory = (struct DebugMemoryShim*) memory; - if (_checkWatchpoints(debugMemory->watchpoints, address, 2)) { - ARMDebuggerEnter(debugMemory->p, DEBUGGER_ENTER_WATCHPOINT); - } - debugMemory->original->store16(debugMemory->original, address, value, cycleCounter); -} - -void ARMDebuggerShim_store8(struct ARMMemory* memory, uint32_t address, int8_t value, int* cycleCounter) { - struct DebugMemoryShim* debugMemory = (struct DebugMemoryShim*) memory; - if (_checkWatchpoints(debugMemory->watchpoints, address, 1)) { - ARMDebuggerEnter(debugMemory->p, DEBUGGER_ENTER_WATCHPOINT); - } - debugMemory->original->store8(debugMemory->original, address, value, cycleCounter); -} - -void ARMDebuggerShim_setActiveRegion(struct ARMMemory* memory, uint32_t address) { - struct DebugMemoryShim* debugMemory = (struct DebugMemoryShim*) memory; - debugMemory->original->setActiveRegion(debugMemory->original, address); - memory->activeRegion = debugMemory->original->activeRegion; - memory->activeMask = debugMemory->original->activeMask; - memory->activePrefetchCycles32 = debugMemory->original->activePrefetchCycles32; - memory->activePrefetchCycles16 = debugMemory->original->activePrefetchCycles16; - memory->activeNonseqCycles32 = debugMemory->original->activeNonseqCycles32; - memory->activeNonseqCycles16 = debugMemory->original->activeNonseqCycles16; + debugger->originalMemory = debugger->cpu->memory; + debugger->cpu->memory.store32 = ARMDebuggerShim_store32; + debugger->cpu->memory.store16 = ARMDebuggerShim_store16; + debugger->cpu->memory.store8 = ARMDebuggerShim_store8; + debugger->cpu->memory.load32 = ARMDebuggerShim_load32; + debugger->cpu->memory.load16 = ARMDebuggerShim_load16; + debugger->cpu->memory.loadU16 = ARMDebuggerShim_loadU16; + debugger->cpu->memory.load8 = ARMDebuggerShim_load8; + debugger->cpu->memory.loadU8 = ARMDebuggerShim_loadU8; + debugger->cpu->memory.setActiveRegion = ARMDebuggerShim_setActiveRegion; + debugger->cpu->memory.waitMultiple = ARMDebuggerShim_waitMultiple; }
M src/debugger/memory-debugger.hsrc/debugger/memory-debugger.h

@@ -1,6 +1,8 @@

#ifndef MEMORY_DEBUGGER_H #define MEMORY_DEBUGGER_H +#include "common.h" + #include "arm.h" struct ARMDebugger;
A src/debugger/parser.c

@@ -0,0 +1,357 @@

+#include "parser.h" + +static struct LexVector* _lexOperator(struct LexVector* lv, char operator) { + struct LexVector* lvNext = malloc(sizeof(struct LexVector)); + lvNext->token.type = TOKEN_OPERATOR_TYPE; + switch (operator) { + case '+': + lvNext->token.operatorValue = OP_ADD; + break; + case '-': + lvNext->token.operatorValue = OP_SUBTRACT; + break; + case '*': + lvNext->token.operatorValue = OP_MULTIPLY; + break; + case '/': + lvNext->token.operatorValue = OP_DIVIDE; + break; + default: + lvNext->token.type = TOKEN_ERROR_TYPE; + break; + } + lvNext->next = lv->next; + lv->next = lvNext; + lv = lvNext; + lvNext = malloc(sizeof(struct LexVector)); + lvNext->next = lv->next; + lvNext->token.type = TOKEN_ERROR_TYPE; + lv->next = lvNext; + return lvNext; +} + +size_t lexExpression(struct LexVector* lv, const char* string, size_t length) { + if (!string || length < 1) { + return 0; + } + + uint32_t next = 0; + size_t adjusted = 0; + + enum LexState state = LEX_ROOT; + const char* tokenStart = 0; + struct LexVector* lvNext; + + while (length > 0 && string[0] && string[0] != ' ' && state != LEX_ERROR) { + char token = string[0]; + ++string; + ++adjusted; + --length; + switch (state) { + case LEX_ROOT: + tokenStart = string - 1; + switch (token) { + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + state = LEX_EXPECT_DECIMAL; + next = token - '0'; + break; + case '0': + state = LEX_EXPECT_PREFIX; + break; + case '$': + state = LEX_EXPECT_HEX; + next = 0; + break; + case '(': + state = LEX_ROOT; + lv->token.type = TOKEN_OPEN_PAREN_TYPE; + lvNext = malloc(sizeof(struct LexVector)); + lvNext->next = lv->next; + lvNext->token.type = TOKEN_ERROR_TYPE; + lv->next = lvNext; + lv = lvNext; + break; + default: + if (tolower(token) >= 'a' && tolower(token <= 'z')) { + state = LEX_EXPECT_IDENTIFIER; + } else { + state = LEX_ERROR; + } + break; + }; + break; + case LEX_EXPECT_IDENTIFIER: + switch (token) { + case '+': + case '-': + case '*': + case '/': + lv->token.type = TOKEN_IDENTIFIER_TYPE; + 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); + state = LEX_EXPECT_OPERATOR; + break; + default: + break; + } + break; + case LEX_EXPECT_DECIMAL: + switch (token) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + // TODO: handle overflow + next *= 10; + next += token - '0'; + break; + case '+': + case '-': + case '*': + case '/': + lv->token.type = TOKEN_UINT_TYPE; + lv->token.uintValue = next; + lv = _lexOperator(lv, token); + state = LEX_ROOT; + break; + case ')': + lv->token.type = TOKEN_UINT_TYPE; + lv->token.uintValue = next; + state = LEX_EXPECT_OPERATOR; + break; + default: + state = LEX_ERROR; + } + break; + case LEX_EXPECT_HEX: + switch (token) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + // TODO: handle overflow + next *= 16; + next += token - '0'; + break; + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + // TODO: handle overflow + next *= 16; + next += token - 'A' + 10; + break; + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + // TODO: handle overflow + next *= 16; + next += token - 'a' + 10; + break; + case '+': + case '-': + case '*': + case '/': + lv->token.type = TOKEN_UINT_TYPE; + lv->token.uintValue = next; + lv = _lexOperator(lv, token); + state = LEX_ROOT; + break; + case ')': + lv->token.type = TOKEN_UINT_TYPE; + lv->token.uintValue = next; + state = LEX_EXPECT_OPERATOR; + break; + default: + state = LEX_ERROR; + break; + } + break; + case LEX_EXPECT_PREFIX: + switch (token) { + case 'X': + case 'x': + next = 0; + state = LEX_EXPECT_HEX; + break; + default: + state = LEX_ERROR; + break; + } + break; + case LEX_EXPECT_OPERATOR: + switch (token) { + case '+': + case '-': + case '*': + case '/': + lvNext = malloc(sizeof(struct LexVector)); + lvNext->next = lv->next; + lvNext->token.type = TOKEN_CLOSE_PAREN_TYPE; + lv->next = lvNext; + lv = _lexOperator(lv->next, token); + state = LEX_ROOT; + break; + default: + state = LEX_ERROR; + } + break; + case LEX_ERROR: + // This shouldn't be reached + break; + } + } + + switch (state) { + case LEX_EXPECT_DECIMAL: + case LEX_EXPECT_HEX: + lv->token.type = TOKEN_UINT_TYPE; + lv->token.uintValue = next; + break; + case LEX_EXPECT_IDENTIFIER: + lv->token.type = TOKEN_IDENTIFIER_TYPE; + lv->token.identifierValue = strndup(tokenStart, string - tokenStart); + break; + case LEX_EXPECT_OPERATOR: + lvNext = malloc(sizeof(struct LexVector)); + lvNext->next = lv->next; + lvNext->token.type = TOKEN_CLOSE_PAREN_TYPE; + lv->next = lvNext; + break; + case LEX_ERROR: + default: + lv->token.type = TOKEN_ERROR_TYPE; + break; + } + return adjusted; +} + +static const int _operatorPrecedence[] = { + 2, + 1, + 1, + 0, + 0 +}; + +static struct ParseTree* _parseTreeCreate() { + struct ParseTree* tree = malloc(sizeof(struct ParseTree)); + tree->token.type = TOKEN_ERROR_TYPE; + tree->rhs = 0; + tree->lhs = 0; + return tree; +} + +static struct LexVector* _parseExpression(struct ParseTree* tree, struct LexVector* lv, int precedence, int openParens) { + struct ParseTree* newTree = 0; + while (lv) { + int newPrecedence; + switch (lv->token.type) { + case TOKEN_IDENTIFIER_TYPE: + case TOKEN_UINT_TYPE: + if (tree->token.type == TOKEN_ERROR_TYPE) { + tree->token = lv->token; + lv = lv->next; + } else { + tree->token.type = TOKEN_ERROR_TYPE; + return 0; + } + break; + case TOKEN_OPEN_PAREN_TYPE: + lv = _parseExpression(tree, lv->next, INT_MAX, openParens + 1); + break; + case TOKEN_CLOSE_PAREN_TYPE: + if (openParens <= 0) { + tree->token.type = TOKEN_ERROR_TYPE; + return 0; + } + return lv->next; + break; + case TOKEN_OPERATOR_TYPE: + newPrecedence = _operatorPrecedence[lv->token.operatorValue]; + if (newPrecedence < precedence) { + newTree = _parseTreeCreate(); + *newTree = *tree; + tree->lhs = newTree; + tree->rhs = _parseTreeCreate(); + tree->token = lv->token; + lv = _parseExpression(tree->rhs, lv->next, newPrecedence, openParens); + if (tree->token.type == TOKEN_ERROR_TYPE) { + tree->token.type = TOKEN_ERROR_TYPE; + } + } else { + return lv; + } + break; + case TOKEN_ERROR_TYPE: + tree->token.type = TOKEN_ERROR_TYPE; + return 0; + } + } + + return 0; +} + +void parseLexedExpression(struct ParseTree* tree, struct LexVector* lv) { + if (!tree) { + return; + } + + tree->token.type = TOKEN_ERROR_TYPE; + tree->lhs = 0; + tree->rhs = 0; + + _parseExpression(tree, lv, _operatorPrecedence[OP_ASSIGN], 0); +} + +void lexFree(struct LexVector* lv) { + while (lv) { + struct LexVector* lvNext = lv->next; + free(lv); + lv = lvNext; + } +} + +void parseFree(struct ParseTree* tree) { + if (!tree) { + return; + } + + parseFree(tree->lhs); + parseFree(tree->rhs); + + if (tree->token.type == TOKEN_IDENTIFIER_TYPE) { + free(tree->token.identifierValue); + } + free(tree); +}
A src/debugger/parser.h

@@ -0,0 +1,58 @@

+#ifndef PARSER_H +#define PARSER_H + +#include "common.h" +#include "debugger.h" + +enum LexState { + LEX_ERROR = -1, + LEX_ROOT = 0, + LEX_EXPECT_IDENTIFIER, + LEX_EXPECT_DECIMAL, + LEX_EXPECT_HEX, + LEX_EXPECT_PREFIX, + LEX_EXPECT_OPERATOR +}; + +enum Operation { + OP_ASSIGN, + OP_ADD, + OP_SUBTRACT, + OP_MULTIPLY, + OP_DIVIDE +}; + +struct Token { + enum TokenType { + TOKEN_ERROR_TYPE, + TOKEN_UINT_TYPE, + TOKEN_IDENTIFIER_TYPE, + TOKEN_OPERATOR_TYPE, + TOKEN_OPEN_PAREN_TYPE, + TOKEN_CLOSE_PAREN_TYPE, + } type; + union { + uint32_t uintValue; + char* identifierValue; + enum Operation operatorValue; + }; +}; + +struct LexVector { + struct LexVector* next; + struct Token token; +}; + +struct ParseTree { + struct Token token; + struct ParseTree* lhs; + struct ParseTree* rhs; +}; + +size_t lexExpression(struct LexVector* lv, const char* string, size_t length); +void parseLexedExpression(struct ParseTree* tree, struct LexVector* lv); + +void lexFree(struct LexVector* lv); +void parseFree(struct ParseTree* tree); + +#endif
M src/gba/gba-audio.csrc/gba/gba-audio.c

@@ -5,16 +5,13 @@ #include "gba-io.h"

#include "gba-serialize.h" #include "gba-thread.h" -#include <limits.h> -#include <math.h> - const unsigned GBA_AUDIO_SAMPLES = 512; const unsigned GBA_AUDIO_FIFO_SIZE = 8 * sizeof(int32_t); #define SWEEP_CYCLES (GBA_ARM7TDMI_FREQUENCY / 128) static int32_t _updateSquareChannel(struct GBAAudioSquareControl* envelope, int duty); static void _updateEnvelope(struct GBAAudioEnvelope* envelope); -static int _updateSweep(struct GBAAudioChannel1* ch); +static bool _updateSweep(struct GBAAudioChannel1* ch); static int32_t _updateChannel1(struct GBAAudioChannel1* ch); static int32_t _updateChannel2(struct GBAAudioChannel2* ch); static int32_t _updateChannel3(struct GBAAudioChannel3* ch);

@@ -23,6 +20,13 @@ static int _applyBias(struct GBAAudio* audio, int sample);

static void _sample(struct GBAAudio* audio); void GBAAudioInit(struct GBAAudio* audio) { + CircleBufferInit(&audio->left, GBA_AUDIO_SAMPLES * sizeof(int32_t)); + CircleBufferInit(&audio->right, GBA_AUDIO_SAMPLES * sizeof(int32_t)); + CircleBufferInit(&audio->chA.fifo, GBA_AUDIO_FIFO_SIZE); + CircleBufferInit(&audio->chB.fifo, GBA_AUDIO_FIFO_SIZE); +} + +void GBAAudioReset(struct GBAAudio* audio) { audio->nextEvent = 0; audio->nextCh1 = 0; audio->nextCh2 = 0;

@@ -52,10 +56,10 @@ audio->soundcntHi = 0;

audio->soundcntX = 0; audio->sampleInterval = GBA_ARM7TDMI_FREQUENCY / audio->sampleRate; - CircleBufferInit(&audio->left, GBA_AUDIO_SAMPLES * sizeof(int32_t)); - CircleBufferInit(&audio->right, GBA_AUDIO_SAMPLES * sizeof(int32_t)); - CircleBufferInit(&audio->chA.fifo, GBA_AUDIO_FIFO_SIZE); - CircleBufferInit(&audio->chB.fifo, GBA_AUDIO_FIFO_SIZE); + CircleBufferClear(&audio->left); + CircleBufferClear(&audio->right); + CircleBufferClear(&audio->chA.fifo); + CircleBufferClear(&audio->chB.fifo); } void GBAAudioDeinit(struct GBAAudio* audio) {

@@ -68,7 +72,7 @@

int32_t GBAAudioProcessEvents(struct GBAAudio* audio, int32_t cycles) { audio->nextEvent -= cycles; audio->eventDiff += cycles; - while (audio->nextEvent <= 0) { + if (audio->nextEvent <= 0) { audio->nextEvent = INT_MAX; if (audio->enable) { if (audio->playingCh1 && !audio->ch1.envelope.dead) {

@@ -400,7 +404,7 @@ if (CircleBufferSize(&channel->fifo) <= 4 * sizeof(int32_t)) {

struct GBADMA* dma = &audio->p->memory.dma[channel->dmaSource]; dma->nextCount = 4; dma->nextEvent = 0; - GBAMemoryUpdateDMAs(&audio->p->memory, -cycles); + GBAMemoryUpdateDMAs(audio->p, -cycles); } CircleBufferRead8(&channel->fifo, &channel->sample); }

@@ -500,7 +504,7 @@ envelope->nextStep += envelope->stepTime * (GBA_ARM7TDMI_FREQUENCY >> 6);

} } -static int _updateSweep(struct GBAAudioChannel1* ch) { +static bool _updateSweep(struct GBAAudioChannel1* ch) { if (ch->sweep.direction) { int frequency = ch->control.frequency; frequency -= frequency >> ch->sweep.shift;

@@ -513,11 +517,11 @@ frequency += frequency >> ch->sweep.shift;

if (frequency < 2048) { ch->control.frequency = frequency; } else { - return 0; + return false; } } ch->nextSweep += ch->sweep.time * SWEEP_CYCLES; - return 1; + return true; } static int32_t _updateChannel1(struct GBAAudioChannel1* ch) {

@@ -566,15 +570,17 @@ } else {

start = 3; end = 0; } - uint32_t bitsCarry = ch->wavedata[end] & 0xF0000000; + uint32_t bitsCarry = ch->wavedata[end] & 0x0F000000; uint32_t bits; for (i = start; i >= end; --i) { - bits = ch->wavedata[i] & 0xF0000000; - ch->wavedata[i] <<= 4; - ch->wavedata[i] |= bitsCarry >> 28; + bits = ch->wavedata[i] & 0x0F000000; + ch->wavedata[i] = ((ch->wavedata[i] & 0xF0F0F0F0) >> 4) | ((ch->wavedata[i] & 0x000F0F0F) << 12); + ch->wavedata[i] |= bitsCarry >> 20; bitsCarry = bits; } - ch->sample = ((bitsCarry >> 26) - 0x20) * volume; + ch->sample = (bitsCarry >> 20); + ch->sample >>= 2; + ch->sample *= volume; return 8 * (2048 - ch->control.rate); }

@@ -695,6 +701,10 @@ state->audio.ch4.lfsr = audio->ch4.lfsr;

state->audio.ch4.endTime = audio->ch4.control.endTime; state->audio.ch4.nextEvent = audio->nextCh4; + CircleBufferDump(&audio->chA.fifo, state->audio.fifoA, sizeof(state->audio.fifoA)); + CircleBufferDump(&audio->chB.fifo, state->audio.fifoB, sizeof(state->audio.fifoB)); + state->audio.fifoSize = CircleBufferSize(&audio->chA.fifo); + state->audio.nextEvent = audio->nextEvent; state->audio.eventDiff = audio->eventDiff; state->audio.nextSample = audio->nextSample;

@@ -728,6 +738,14 @@ audio->ch4.envelope.nextStep = state->audio.ch4.envelopeNextStep;

audio->ch4.lfsr = state->audio.ch4.lfsr; audio->ch4.control.endTime = state->audio.ch4.endTime; audio->nextCh4 = state->audio.ch4.nextEvent; + + CircleBufferClear(&audio->chA.fifo); + CircleBufferClear(&audio->chB.fifo); + int i; + for (i = 0; i < state->audio.fifoSize; ++i) { + CircleBufferWrite8(&audio->chA.fifo, state->audio.fifoA[i]); + CircleBufferWrite8(&audio->chB.fifo, state->audio.fifoB[i]); + } audio->nextEvent = state->audio.nextEvent; audio->eventDiff = state->audio.eventDiff;
M src/gba/gba-audio.hsrc/gba/gba-audio.h

@@ -1,9 +1,9 @@

#ifndef GBA_AUDIO_H #define GBA_AUDIO_H -#include "circle-buffer.h" +#include "common.h" -#include <stdint.h> +#include "util/circle-buffer.h" struct GBADMA;

@@ -219,6 +219,7 @@ int16_t right;

}; void GBAAudioInit(struct GBAAudio* audio); +void GBAAudioReset(struct GBAAudio* audio); void GBAAudioDeinit(struct GBAAudio* audio); int32_t GBAAudioProcessEvents(struct GBAAudio* audio, int32_t cycles);
M src/gba/gba-bios.csrc/gba/gba-bios.c

@@ -4,129 +4,41 @@ #include "gba.h"

#include "gba-io.h" #include "gba-memory.h" -#include <math.h> -#include <stdlib.h> - const uint32_t GBA_BIOS_CHECKSUM = 0xBAAE187F; const uint32_t GBA_DS_BIOS_CHECKSUM = 0xBAAE1880; -static void _unLz77(struct GBAMemory* memory, uint32_t source, uint8_t* dest); -static void _unHuffman(struct GBAMemory* memory, uint32_t source, uint32_t* dest); -static void _unRl(struct GBAMemory* memory, uint32_t source, uint8_t* dest); +static void _unLz77(struct GBA* gba, uint32_t source, uint8_t* dest); +static void _unHuffman(struct GBA* gba, uint32_t source, uint32_t* dest); +static void _unRl(struct GBA* gba, uint32_t source, uint8_t* dest); static void _RegisterRamReset(struct GBA* gba) { - uint32_t registers = gba->cpu.gprs[0]; - (void)(registers); + uint32_t registers = gba->cpu->gprs[0]; + UNUSED(registers); GBALog(gba, GBA_LOG_STUB, "RegisterRamReset unimplemented"); } -static void _CpuSet(struct GBA* gba) { - uint32_t source = gba->cpu.gprs[0]; - uint32_t dest = gba->cpu.gprs[1]; - uint32_t mode = gba->cpu.gprs[2]; - int count = mode & 0x000FFFFF; - int fill = mode & 0x01000000; - int wordsize = (mode & 0x04000000) ? 4 : 2; - int i; - if (fill) { - if (wordsize == 4) { - source &= 0xFFFFFFFC; - dest &= 0xFFFFFFFC; - int32_t word = gba->memory.d.load32(&gba->memory.d, source, &gba->cpu.cycles); - for (i = 0; i < count; ++i) { - gba->memory.d.store32(&gba->memory.d, dest + (i << 2), word, &gba->cpu.cycles); - gba->board.d.processEvents(&gba->board.d); - } - } else { - source &= 0xFFFFFFFE; - dest &= 0xFFFFFFFE; - uint16_t word = gba->memory.d.load16(&gba->memory.d, source, &gba->cpu.cycles); - for (i = 0; i < count; ++i) { - gba->memory.d.store16(&gba->memory.d, dest + (i << 1), word, &gba->cpu.cycles); - gba->board.d.processEvents(&gba->board.d); - } - } - } else { - if (wordsize == 4) { - source &= 0xFFFFFFFC; - dest &= 0xFFFFFFFC; - for (i = 0; i < count; ++i) { - int32_t word = gba->memory.d.load32(&gba->memory.d, source + (i << 2), &gba->cpu.cycles); - gba->memory.d.store32(&gba->memory.d, dest + (i << 2), word, &gba->cpu.cycles); - gba->board.d.processEvents(&gba->board.d); - } - } else { - source &= 0xFFFFFFFE; - dest &= 0xFFFFFFFE; - for (i = 0; i < count; ++i) { - uint16_t word = gba->memory.d.load16(&gba->memory.d, source + (i << 1), &gba->cpu.cycles); - gba->memory.d.store16(&gba->memory.d, dest + (i << 1), word, &gba->cpu.cycles); - gba->board.d.processEvents(&gba->board.d); - } - } - } -} - -static void _FastCpuSet(struct GBA* gba) { - uint32_t source = gba->cpu.gprs[0] & 0xFFFFFFFC; - uint32_t dest = gba->cpu.gprs[1] & 0xFFFFFFFC; - uint32_t mode = gba->cpu.gprs[2]; - int count = mode & 0x000FFFFF; - int storeCycles = gba->memory.d.waitMultiple(&gba->memory.d, dest, 4); - count = ((count + 7) >> 3) << 3; - int i; - if (mode & 0x01000000) { - int32_t word = gba->memory.d.load32(&gba->memory.d, source, &gba->cpu.cycles); - for (i = 0; i < count; i += 4) { - gba->memory.d.store32(&gba->memory.d, dest + ((i + 0) << 2), word, 0); - gba->memory.d.store32(&gba->memory.d, dest + ((i + 1) << 2), word, 0); - gba->memory.d.store32(&gba->memory.d, dest + ((i + 2) << 2), word, 0); - gba->memory.d.store32(&gba->memory.d, dest + ((i + 3) << 2), word, 0); - gba->cpu.cycles += storeCycles; - gba->board.d.processEvents(&gba->board.d); - } - } else { - int loadCycles = gba->memory.d.waitMultiple(&gba->memory.d, source, 4); - for (i = 0; i < count; i += 4) { - int32_t word0 = gba->memory.d.load32(&gba->memory.d, source + ((i + 0) << 2), 0); - int32_t word1 = gba->memory.d.load32(&gba->memory.d, source + ((i + 1) << 2), 0); - int32_t word2 = gba->memory.d.load32(&gba->memory.d, source + ((i + 2) << 2), 0); - int32_t word3 = gba->memory.d.load32(&gba->memory.d, source + ((i + 3) << 2), 0); - gba->cpu.cycles += loadCycles; - gba->board.d.processEvents(&gba->board.d); - gba->memory.d.store32(&gba->memory.d, dest + ((i + 0) << 2), word0, 0); - gba->memory.d.store32(&gba->memory.d, dest + ((i + 1) << 2), word1, 0); - gba->memory.d.store32(&gba->memory.d, dest + ((i + 2) << 2), word2, 0); - gba->memory.d.store32(&gba->memory.d, dest + ((i + 3) << 2), word3, 0); - gba->cpu.cycles += storeCycles; - gba->board.d.processEvents(&gba->board.d); - } - } -} - static void _BgAffineSet(struct GBA* gba) { - int i = gba->cpu.gprs[2]; + struct ARMCore* cpu = gba->cpu; + int i = cpu->gprs[2]; float ox, oy; float cx, cy; float sx, sy; float theta; - int offset = gba->cpu.gprs[0]; - int destination = gba->cpu.gprs[1]; - int diff = gba->cpu.gprs[3]; - (void)(diff); // Are we supposed to use this? + int offset = cpu->gprs[0]; + int destination = cpu->gprs[1]; float a, b, c, d; float rx, ry; while (i--) { // [ sx 0 0 ] [ cos(theta) -sin(theta) 0 ] [ 1 0 cx - ox ] [ A B rx ] // [ 0 sy 0 ] * [ sin(theta) cos(theta) 0 ] * [ 0 1 cy - oy ] = [ C D ry ] // [ 0 0 1 ] [ 0 0 1 ] [ 0 0 1 ] [ 0 0 1 ] - ox = gba->memory.d.load32(&gba->memory.d, offset, 0) / 256.f; - oy = gba->memory.d.load32(&gba->memory.d, offset + 4, 0) / 256.f; - cx = gba->memory.d.load16(&gba->memory.d, offset + 8, 0); - cy = gba->memory.d.load16(&gba->memory.d, offset + 10, 0); - sx = gba->memory.d.load16(&gba->memory.d, offset + 12, 0) / 256.f; - sy = gba->memory.d.load16(&gba->memory.d, offset + 14, 0) / 256.f; - theta = (gba->memory.d.loadU16(&gba->memory.d, offset + 16, 0) >> 8) / 128.f * M_PI; + ox = cpu->memory.load32(cpu, offset, 0) / 256.f; + oy = cpu->memory.load32(cpu, offset + 4, 0) / 256.f; + cx = cpu->memory.load16(cpu, offset + 8, 0); + cy = cpu->memory.load16(cpu, offset + 10, 0); + sx = cpu->memory.load16(cpu, offset + 12, 0) / 256.f; + sy = cpu->memory.load16(cpu, offset + 14, 0) / 256.f; + theta = (cpu->memory.loadU16(cpu, offset + 16, 0) >> 8) / 128.f * M_PI; offset += 20; // Rotation a = d = cosf(theta);

@@ -139,30 +51,31 @@ d *= sy;

// Translate rx = ox - (a * cx + b * cy); ry = oy - (c * cx + d * cy); - gba->memory.d.store16(&gba->memory.d, destination, a * 256, 0); - gba->memory.d.store16(&gba->memory.d, destination + 2, b * 256, 0); - gba->memory.d.store16(&gba->memory.d, destination + 4, c * 256, 0); - gba->memory.d.store16(&gba->memory.d, destination + 6, d * 256, 0); - gba->memory.d.store32(&gba->memory.d, destination + 8, rx * 256, 0); - gba->memory.d.store32(&gba->memory.d, destination + 12, ry * 256, 0); + cpu->memory.store16(cpu, destination, a * 256, 0); + cpu->memory.store16(cpu, destination + 2, b * 256, 0); + cpu->memory.store16(cpu, destination + 4, c * 256, 0); + cpu->memory.store16(cpu, destination + 6, d * 256, 0); + cpu->memory.store32(cpu, destination + 8, rx * 256, 0); + cpu->memory.store32(cpu, destination + 12, ry * 256, 0); destination += 16; } } static void _ObjAffineSet(struct GBA* gba) { - int i = gba->cpu.gprs[2]; + struct ARMCore* cpu = gba->cpu; + int i = cpu->gprs[2]; float sx, sy; float theta; - int offset = gba->cpu.gprs[0]; - int destination = gba->cpu.gprs[1]; - int diff = gba->cpu.gprs[3]; + int offset = cpu->gprs[0]; + int destination = cpu->gprs[1]; + int diff = cpu->gprs[3]; float a, b, c, d; while (i--) { // [ sx 0 ] [ cos(theta) -sin(theta) ] [ A B ] // [ 0 sy ] * [ sin(theta) cos(theta) ] = [ C D ] - sx = gba->memory.d.load16(&gba->memory.d, offset, 0) / 256.f; - sy = gba->memory.d.load16(&gba->memory.d, offset + 2, 0) / 256.f; - theta = (gba->memory.d.loadU16(&gba->memory.d, offset + 4, 0) >> 8) / 128.f * M_PI; + sx = cpu->memory.load16(cpu, offset, 0) / 256.f; + sy = cpu->memory.load16(cpu, offset + 2, 0) / 256.f; + theta = (cpu->memory.loadU16(cpu, offset + 4, 0) >> 8) / 128.f * M_PI; offset += 6; // Rotation a = d = cosf(theta);

@@ -172,23 +85,41 @@ a *= sx;

b *= -sx; c *= sy; d *= sy; - gba->memory.d.store16(&gba->memory.d, destination, a * 256, 0); - gba->memory.d.store16(&gba->memory.d, destination + diff, b * 256, 0); - gba->memory.d.store16(&gba->memory.d, destination + diff * 2, c * 256, 0); - gba->memory.d.store16(&gba->memory.d, destination + diff * 3, d * 256, 0); + cpu->memory.store16(cpu, destination, a * 256, 0); + cpu->memory.store16(cpu, destination + diff, b * 256, 0); + cpu->memory.store16(cpu, destination + diff * 2, c * 256, 0); + cpu->memory.store16(cpu, destination + diff * 3, d * 256, 0); destination += diff * 4; } } static void _MidiKey2Freq(struct GBA* gba) { - uint32_t key = gba->memory.d.load32(&gba->memory.d, gba->cpu.gprs[0] + 4, 0); - gba->cpu.gprs[0] = key / powf(2, (180.f - gba->cpu.gprs[1] - gba->cpu.gprs[2] / 256.f) / 12.f); + struct ARMCore* cpu = gba->cpu; + uint32_t key = cpu->memory.load32(cpu, cpu->gprs[0] + 4, 0); + cpu->gprs[0] = key / powf(2, (180.f - cpu->gprs[1] - cpu->gprs[2] / 256.f) / 12.f); +} + +static void _Div(struct ARMCore* cpu, int32_t num, int32_t denom) { + if (denom != 0) { + div_t result = div(num, denom); + cpu->gprs[0] = result.quot; + cpu->gprs[1] = result.rem; + cpu->gprs[3] = abs(result.quot); + } else { + // If abs(num) > 1, this should hang, but that would be painful to + // emulate in HLE, and no game will get into a state where it hangs... + cpu->gprs[0] = (num < 0) ? -1 : 1; + cpu->gprs[1] = num; + cpu->gprs[3] = 1; + } } -void GBASwi16(struct ARMBoard* board, int immediate) { - struct GBA* gba = ((struct GBABoard*) board)->p; +void GBASwi16(struct ARMCore* cpu, int immediate) { + struct GBA* gba = (struct GBA*) cpu->master; + GBALog(gba, GBA_LOG_DEBUG, "SWI: %02x", immediate); + if (gba->memory.fullBios) { - ARMRaiseSWI(&gba->cpu); + ARMRaiseSWI(cpu); return; } switch (immediate) {

@@ -200,48 +131,29 @@ GBAHalt(gba);

break; case 0x05: // VBlankIntrWait - gba->cpu.gprs[0] = 1; - gba->cpu.gprs[1] = 1; // Fall through: case 0x04: // IntrWait - gba->memory.io[REG_IME >> 1] = 1; - if (!gba->cpu.gprs[0] && gba->memory.io[REG_IF >> 1] & gba->cpu.gprs[1]) { - break; - } - gba->memory.io[REG_IF >> 1] = 0; - ARMRaiseSWI(&gba->cpu); + ARMRaiseSWI(cpu); break; case 0x6: - { - div_t result = div(gba->cpu.gprs[0], gba->cpu.gprs[1]); - gba->cpu.gprs[0] = result.quot; - gba->cpu.gprs[1] = result.rem; - gba->cpu.gprs[3] = abs(result.quot); - } + _Div(cpu, cpu->gprs[0], cpu->gprs[1]); break; case 0x7: - { - div_t result = div(gba->cpu.gprs[1], gba->cpu.gprs[0]); - gba->cpu.gprs[0] = result.quot; - gba->cpu.gprs[1] = result.rem; - gba->cpu.gprs[3] = abs(result.quot); - } + _Div(cpu, cpu->gprs[1], cpu->gprs[0]); break; case 0x8: - gba->cpu.gprs[0] = sqrt(gba->cpu.gprs[0]); + cpu->gprs[0] = sqrt(cpu->gprs[0]); break; case 0xA: - gba->cpu.gprs[0] = atan2f(gba->cpu.gprs[1] / 16384.f, gba->cpu.gprs[0] / 16384.f) / (2 * M_PI) * 0x10000; + cpu->gprs[0] = atan2f(cpu->gprs[1] / 16384.f, cpu->gprs[0] / 16384.f) / (2 * M_PI) * 0x10000; break; case 0xB: - _CpuSet(gba); - break; case 0xC: - _FastCpuSet(gba); + ARMRaiseSWI(cpu); break; case 0xD: - gba->cpu.gprs[0] = GBAChecksum(gba->memory.bios, SIZE_BIOS); + cpu->gprs[0] = GBAChecksum(gba->memory.bios, SIZE_BIOS); case 0xE: _BgAffineSet(gba); break;

@@ -250,19 +162,19 @@ _ObjAffineSet(gba);

break; case 0x11: case 0x12: - if (gba->cpu.gprs[0] < BASE_WORKING_RAM) { + if (cpu->gprs[0] < BASE_WORKING_RAM) { GBALog(gba, GBA_LOG_GAME_ERROR, "Bad LZ77 source"); break; } - switch (gba->cpu.gprs[1] >> BASE_OFFSET) { + switch (cpu->gprs[1] >> BASE_OFFSET) { case REGION_WORKING_RAM: - _unLz77(&gba->memory, gba->cpu.gprs[0], &((uint8_t*) gba->memory.wram)[(gba->cpu.gprs[1] & (SIZE_WORKING_RAM - 1))]); + _unLz77(gba, cpu->gprs[0], &((uint8_t*) gba->memory.wram)[(cpu->gprs[1] & (SIZE_WORKING_RAM - 1))]); break; case REGION_WORKING_IRAM: - _unLz77(&gba->memory, gba->cpu.gprs[0], &((uint8_t*) gba->memory.iwram)[(gba->cpu.gprs[1] & (SIZE_WORKING_IRAM - 1))]); + _unLz77(gba, cpu->gprs[0], &((uint8_t*) gba->memory.iwram)[(cpu->gprs[1] & (SIZE_WORKING_IRAM - 1))]); break; case REGION_VRAM: - _unLz77(&gba->memory, gba->cpu.gprs[0], &((uint8_t*) gba->video.renderer->vram)[(gba->cpu.gprs[1] & 0x0001FFFF)]); + _unLz77(gba, cpu->gprs[0], &((uint8_t*) gba->video.renderer->vram)[(cpu->gprs[1] & 0x0001FFFF)]); break; default: GBALog(gba, GBA_LOG_GAME_ERROR, "Bad LZ77 destination");

@@ -270,19 +182,19 @@ break;

} break; case 0x13: - if (gba->cpu.gprs[0] < BASE_WORKING_RAM) { + if (cpu->gprs[0] < BASE_WORKING_RAM) { GBALog(gba, GBA_LOG_GAME_ERROR, "Bad Huffman source"); break; } - switch (gba->cpu.gprs[1] >> BASE_OFFSET) { + switch (cpu->gprs[1] >> BASE_OFFSET) { case REGION_WORKING_RAM: - _unHuffman(&gba->memory, gba->cpu.gprs[0], &((uint32_t*) gba->memory.wram)[(gba->cpu.gprs[1] & (SIZE_WORKING_RAM - 3)) >> 2]); + _unHuffman(gba, cpu->gprs[0], &((uint32_t*) gba->memory.wram)[(cpu->gprs[1] & (SIZE_WORKING_RAM - 3)) >> 2]); break; case REGION_WORKING_IRAM: - _unHuffman(&gba->memory, gba->cpu.gprs[0], &((uint32_t*) gba->memory.iwram)[(gba->cpu.gprs[1] & (SIZE_WORKING_IRAM - 3)) >> 2]); + _unHuffman(gba, cpu->gprs[0], &((uint32_t*) gba->memory.iwram)[(cpu->gprs[1] & (SIZE_WORKING_IRAM - 3)) >> 2]); break; case REGION_VRAM: - _unHuffman(&gba->memory, gba->cpu.gprs[0], &((uint32_t*) gba->video.renderer->vram)[(gba->cpu.gprs[1] & 0x0001FFFC) >> 2]); + _unHuffman(gba, cpu->gprs[0], &((uint32_t*) gba->video.renderer->vram)[(cpu->gprs[1] & 0x0001FFFC) >> 2]); break; default: GBALog(gba, GBA_LOG_GAME_ERROR, "Bad Huffman destination");

@@ -291,19 +203,19 @@ }

break; case 0x14: case 0x15: - if (gba->cpu.gprs[0] < BASE_WORKING_RAM) { + if (cpu->gprs[0] < BASE_WORKING_RAM) { GBALog(gba, GBA_LOG_GAME_ERROR, "Bad RL source"); break; } - switch (gba->cpu.gprs[1] >> BASE_OFFSET) { + switch (cpu->gprs[1] >> BASE_OFFSET) { case REGION_WORKING_RAM: - _unRl(&gba->memory, gba->cpu.gprs[0], &((uint8_t*) gba->memory.wram)[(gba->cpu.gprs[1] & (SIZE_WORKING_RAM - 1))]); + _unRl(gba, cpu->gprs[0], &((uint8_t*) gba->memory.wram)[(cpu->gprs[1] & (SIZE_WORKING_RAM - 1))]); break; case REGION_WORKING_IRAM: - _unRl(&gba->memory, gba->cpu.gprs[0], &((uint8_t*) gba->memory.iwram)[(gba->cpu.gprs[1] & (SIZE_WORKING_IRAM - 1))]); + _unRl(gba, cpu->gprs[0], &((uint8_t*) gba->memory.iwram)[(cpu->gprs[1] & (SIZE_WORKING_IRAM - 1))]); break; case REGION_VRAM: - _unRl(&gba->memory, gba->cpu.gprs[0], &((uint8_t*) gba->video.renderer->vram)[(gba->cpu.gprs[1] & 0x0001FFFF)]); + _unRl(gba, cpu->gprs[0], &((uint8_t*) gba->video.renderer->vram)[(cpu->gprs[1] & 0x0001FFFF)]); break; default: GBALog(gba, GBA_LOG_GAME_ERROR, "Bad RL destination");

@@ -318,8 +230,8 @@ GBALog(gba, GBA_LOG_STUB, "Stub software interrupt: %02x", immediate);

} } -void GBASwi32(struct ARMBoard* board, int immediate) { - GBASwi16(board, immediate >> 16); +void GBASwi32(struct ARMCore* cpu, int immediate) { + GBASwi16(cpu, immediate >> 16); } uint32_t GBAChecksum(uint32_t* memory, size_t size) {

@@ -331,8 +243,9 @@ }

return sum; } -static void _unLz77(struct GBAMemory* memory, uint32_t source, uint8_t* dest) { - int remaining = (memory->d.load32(&memory->d, source, 0) & 0xFFFFFF00) >> 8; +static void _unLz77(struct GBA* gba, uint32_t source, uint8_t* dest) { + struct ARMCore* cpu = gba->cpu; + int remaining = (cpu->memory.load32(cpu, source, 0) & 0xFFFFFF00) >> 8; // We assume the signature byte (0x10) is correct int blockheader; uint32_t sPointer = source + 4;

@@ -345,7 +258,7 @@ while (remaining > 0) {

if (blocksRemaining) { if (blockheader & 0x80) { // Compressed - block = memory->d.loadU8(&memory->d, sPointer, 0) | (memory->d.loadU8(&memory->d, sPointer + 1, 0) << 8); + block = cpu->memory.loadU8(cpu, sPointer, 0) | (cpu->memory.loadU8(cpu, sPointer + 1, 0) << 8); sPointer += 2; disp = dPointer - (((block & 0x000F) << 8) | ((block & 0xFF00) >> 8)) - 1; bytes = ((block & 0x00F0) >> 4) + 3;

@@ -357,33 +270,34 @@ ++dPointer;

} } else { // Uncompressed - *dPointer = memory->d.loadU8(&memory->d, sPointer++, 0); + *dPointer = cpu->memory.loadU8(cpu, sPointer++, 0); ++dPointer; --remaining; } blockheader <<= 1; --blocksRemaining; } else { - blockheader = memory->d.loadU8(&memory->d, sPointer++, 0); + blockheader = cpu->memory.loadU8(cpu, sPointer++, 0); blocksRemaining = 8; } } } -static void _unHuffman(struct GBAMemory* memory, uint32_t source, uint32_t* dest) { +static void _unHuffman(struct GBA* gba, uint32_t source, uint32_t* dest) { + struct ARMCore* cpu = gba->cpu; source = source & 0xFFFFFFFC; - uint32_t header = memory->d.load32(&memory->d, source, 0); + uint32_t header = cpu->memory.load32(cpu, source, 0); int remaining = header >> 8; int bits = header & 0xF; if (32 % bits) { - GBALog(memory->p, GBA_LOG_STUB, "Unimplemented unaligned Huffman"); + GBALog(gba, GBA_LOG_STUB, "Unimplemented unaligned Huffman"); return; } int padding = (4 - remaining) & 0x3; remaining &= 0xFFFFFFFC; // We assume the signature byte (0x20) is correct //var tree = []; - int treesize = (memory->d.loadU8(&memory->d, source + 4, 0) << 1) + 1; + int treesize = (cpu->memory.loadU8(cpu, source + 4, 0) << 1) + 1; int block = 0; uint32_t treeBase = source + 5; uint32_t sPointer = source + 5 + treesize;

@@ -400,28 +314,28 @@ } node;

int bitsRemaining; int readBits; int bitsSeen = 0; - node.packed = memory->d.load8(&memory->d, nPointer, 0); + node.packed = cpu->memory.load8(cpu, nPointer, 0); while (remaining > 0) { - uint32_t bitstream = memory->d.load32(&memory->d, sPointer, 0); + uint32_t bitstream = cpu->memory.load32(cpu, sPointer, 0); sPointer += 4; for (bitsRemaining = 32; bitsRemaining > 0; --bitsRemaining, bitstream <<= 1) { uint32_t next = (nPointer & ~1) + node.offset * 2 + 2; if (bitstream & 0x80000000) { // Go right if (node.rTerm) { - readBits = memory->d.load8(&memory->d, next + 1, 0); + readBits = cpu->memory.load8(cpu, next + 1, 0); } else { nPointer = next + 1; - node.packed = memory->d.load8(&memory->d, nPointer, 0); + node.packed = cpu->memory.load8(cpu, nPointer, 0); continue; } } else { // Go left if (node.lTerm) { - readBits = memory->d.load8(&memory->d, next, 0); + readBits = cpu->memory.load8(cpu, next, 0); } else { nPointer = next; - node.packed = memory->d.load8(&memory->d, nPointer, 0); + node.packed = cpu->memory.load8(cpu, nPointer, 0); continue; } }

@@ -429,7 +343,7 @@

block |= (readBits & ((1 << bits) - 1)) << bitsSeen; bitsSeen += bits; nPointer = treeBase; - node.packed = memory->d.load8(&memory->d, nPointer, 0); + node.packed = cpu->memory.load8(cpu, nPointer, 0); if (bitsSeen == 32) { bitsSeen = 0; *dPointer = block;

@@ -445,9 +359,10 @@ *dPointer = block;

} } -static void _unRl(struct GBAMemory* memory, uint32_t source, uint8_t* dest) { +static void _unRl(struct GBA* gba, uint32_t source, uint8_t* dest) { + struct ARMCore* cpu = gba->cpu; source = source & 0xFFFFFFFC; - int remaining = (memory->d.load32(&memory->d, source, 0) & 0xFFFFFF00) >> 8; + int remaining = (cpu->memory.load32(cpu, source, 0) & 0xFFFFFF00) >> 8; int padding = (4 - remaining) & 0x3; // We assume the signature byte (0x30) is correct int blockheader;

@@ -455,12 +370,12 @@ int block;

uint32_t sPointer = source + 4; uint8_t* dPointer = dest; while (remaining > 0) { - blockheader = memory->d.loadU8(&memory->d, sPointer++, 0); + blockheader = cpu->memory.loadU8(cpu, sPointer++, 0); if (blockheader & 0x80) { // Compressed blockheader &= 0x7F; blockheader += 3; - block = memory->d.loadU8(&memory->d, sPointer++, 0); + block = cpu->memory.loadU8(cpu, sPointer++, 0); while (blockheader-- && remaining) { --remaining; *dPointer = block;

@@ -471,7 +386,7 @@ // Uncompressed

blockheader++; while (blockheader-- && remaining) { --remaining; - *dPointer = memory->d.loadU8(&memory->d, sPointer++, 0); + *dPointer = cpu->memory.loadU8(cpu, sPointer++, 0); ++dPointer; } }
M src/gba/gba-bios.hsrc/gba/gba-bios.h

@@ -1,12 +1,12 @@

#ifndef GBA_BIOS_H #define GBA_BIOS_H -#include "arm.h" +#include "common.h" -#include <string.h> +#include "arm.h" -void GBASwi16(struct ARMBoard* board, int immediate); -void GBASwi32(struct ARMBoard* board, int immediate); +void GBASwi16(struct ARMCore* cpu, int immediate); +void GBASwi32(struct ARMCore* cpu, int immediate); uint32_t GBAChecksum(uint32_t* memory, size_t size); const uint32_t GBA_BIOS_CHECKSUM;
M src/gba/gba-gpio.csrc/gba/gba-gpio.c

@@ -2,6 +2,7 @@ #include "gba.h"

#include "gba-gpio.h" #include "gba-sensors.h" +#include "gba-serialize.h" #include <time.h>

@@ -302,3 +303,25 @@ }

rumble->setRumble(rumble, gpio->p3); } + +// == Serialization + +void GBAGPIOSerialize(struct GBACartridgeGPIO* gpio, struct GBASerializedState* state) { + state->gpio.readWrite = gpio->readWrite; + state->gpio.pinState = gpio->pinState; + state->gpio.pinDirection = gpio->direction; + state->gpio.devices = gpio->gpioDevices; + state->gpio.rtc = gpio->rtc; + state->gpio.gyroSample = gpio->gyroSample; + state->gpio.gyroEdge = gpio->gyroEdge; +} + +void GBAGPIODeserialize(struct GBACartridgeGPIO* gpio, struct GBASerializedState* state) { + gpio->readWrite = state->gpio.readWrite; + gpio->pinState = state->gpio.pinState; + gpio->direction = state->gpio.pinDirection; + // TODO: Deterministic RTC + gpio->rtc = state->gpio.rtc; + gpio->gyroSample = state->gpio.gyroSample; + gpio->gyroEdge = state->gpio.gyroEdge; +}
M src/gba/gba-gpio.hsrc/gba/gba-gpio.h

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

#ifndef GBA_GPIO_H #define GBA_GPIO_H -#include <stdint.h> +#include "common.h" #define IS_GPIO_REGISTER(reg) ((reg) == GPIO_REG_DATA || (reg) == GPIO_REG_DIRECTION || (reg) == GPIO_REG_CONTROL)

@@ -10,7 +10,8 @@ GPIO_NONE = 0,

GPIO_RTC = 1, GPIO_RUMBLE = 2, GPIO_LIGHT_SENSOR = 4, - GPIO_GYRO = 8 + GPIO_GYRO = 8, + GPIO_TILT = 16 }; enum GPIORegister {

@@ -61,7 +62,7 @@ int commandActive;

union RTCCommandData command; union RTCControl control; uint8_t time[7]; -}; +} __attribute__((packed)); struct GBARumble { void (*setRumble)(struct GBARumble*, int enable);

@@ -96,7 +97,7 @@

struct GBARTC rtc; uint16_t gyroSample; - int gyroEdge; + bool gyroEdge; }; void GBAGPIOInit(struct GBACartridgeGPIO* gpio, uint16_t* gpioBase);

@@ -107,5 +108,9 @@

void GBAGPIOInitGyro(struct GBACartridgeGPIO* gpio); void GBAGPIOInitRumble(struct GBACartridgeGPIO* gpio); + +struct GBASerializedState; +void GBAGPIOSerialize(struct GBACartridgeGPIO* gpio, struct GBASerializedState* state); +void GBAGPIODeserialize(struct GBACartridgeGPIO* gpio, struct GBASerializedState* state); #endif
M src/gba/gba-io.csrc/gba/gba-io.c

@@ -1,5 +1,6 @@

#include "gba-io.h" +#include "gba-rr.h" #include "gba-serialize.h" #include "gba-sio.h" #include "gba-video.h"

@@ -61,9 +62,9 @@ 0, 0, 0, 0, 0, 0, 0, 0,

1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, // DMA - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Timers

@@ -135,7 +136,8 @@ value &= 0xE000;

break; case REG_SOUND3CNT_X: GBAAudioWriteSOUND3CNT_X(&gba->audio, value); - value &= 0x4000; + // TODO: The low bits need to not be readable, but still 8-bit writable + value &= 0x43FF; break; case REG_SOUND4CNT_LO: GBAAudioWriteSOUND4CNT_LO(&gba->audio, value);

@@ -200,28 +202,28 @@ GBAIOWrite32(gba, address - 2, gba->memory.io[(address >> 1) - 1] | (value << 16));

break; case REG_DMA0CNT_LO: - GBAMemoryWriteDMACNT_LO(&gba->memory, 0, value); + GBAMemoryWriteDMACNT_LO(gba, 0, value); break; case REG_DMA0CNT_HI: - value = GBAMemoryWriteDMACNT_HI(&gba->memory, 0, value); + value = GBAMemoryWriteDMACNT_HI(gba, 0, value); break; case REG_DMA1CNT_LO: - GBAMemoryWriteDMACNT_LO(&gba->memory, 1, value); + GBAMemoryWriteDMACNT_LO(gba, 1, value); break; case REG_DMA1CNT_HI: - value = GBAMemoryWriteDMACNT_HI(&gba->memory, 1, value); + value = GBAMemoryWriteDMACNT_HI(gba, 1, value); break; case REG_DMA2CNT_LO: - GBAMemoryWriteDMACNT_LO(&gba->memory, 2, value); + GBAMemoryWriteDMACNT_LO(gba, 2, value); break; case REG_DMA2CNT_HI: - value = GBAMemoryWriteDMACNT_HI(&gba->memory, 2, value); + value = GBAMemoryWriteDMACNT_HI(gba, 2, value); break; case REG_DMA3CNT_LO: - GBAMemoryWriteDMACNT_LO(&gba->memory, 3, value); + GBAMemoryWriteDMACNT_LO(gba, 3, value); break; case REG_DMA3CNT_HI: - value = GBAMemoryWriteDMACNT_HI(&gba->memory, 3, value); + value = GBAMemoryWriteDMACNT_HI(gba, 3, value); break; // Timers

@@ -269,7 +271,7 @@ break;

// Interrupts and misc case REG_WAITCNT: - GBAAdjustWaitstates(&gba->memory, value); + GBAAdjustWaitstates(gba, value); break; case REG_IE: GBAWriteIE(gba, value);

@@ -325,28 +327,28 @@ case REG_FIFO_B_LO:

GBAAudioWriteFIFO(&gba->audio, address, value); break; case REG_DMA0SAD_LO: - GBAMemoryWriteDMASAD(&gba->memory, 0, value); + GBAMemoryWriteDMASAD(gba, 0, value); break; case REG_DMA0DAD_LO: - GBAMemoryWriteDMADAD(&gba->memory, 0, value); + GBAMemoryWriteDMADAD(gba, 0, value); break; case REG_DMA1SAD_LO: - GBAMemoryWriteDMASAD(&gba->memory, 1, value); + GBAMemoryWriteDMASAD(gba, 1, value); break; case REG_DMA1DAD_LO: - GBAMemoryWriteDMADAD(&gba->memory, 1, value); + GBAMemoryWriteDMADAD(gba, 1, value); break; case REG_DMA2SAD_LO: - GBAMemoryWriteDMASAD(&gba->memory, 2, value); + GBAMemoryWriteDMASAD(gba, 2, value); break; case REG_DMA2DAD_LO: - GBAMemoryWriteDMADAD(&gba->memory, 2, value); + GBAMemoryWriteDMADAD(gba, 2, value); break; case REG_DMA3SAD_LO: - GBAMemoryWriteDMASAD(&gba->memory, 3, value); + GBAMemoryWriteDMASAD(gba, 3, value); break; case REG_DMA3DAD_LO: - GBAMemoryWriteDMADAD(&gba->memory, 3, value); + GBAMemoryWriteDMADAD(gba, 3, value); break; default: GBAIOWrite(gba, address, value & 0xFFFF);

@@ -359,10 +361,6 @@ }

uint16_t GBAIORead(struct GBA* gba, uint32_t address) { switch (address) { - case REG_DISPSTAT: - return gba->memory.io[REG_DISPSTAT >> 1] | GBAVideoReadDISPSTAT(&gba->video); - break; - case REG_TM0CNT_LO: GBATimerUpdateRegister(gba, 0); break;

@@ -377,8 +375,14 @@ GBATimerUpdateRegister(gba, 3);

break; case REG_KEYINPUT: - if (gba->keySource) { - return 0x3FF ^ *gba->keySource; + if (GBARRIsPlaying(gba->rr)) { + return 0x3FF ^ GBARRQueryInput(gba->rr); + } else if (gba->keySource) { + uint16_t input = *gba->keySource; + if (GBARRIsRecording(gba->rr)) { + GBARRLogInput(gba->rr, input); + } + return 0x3FF ^ input; } break;

@@ -394,6 +398,7 @@ case REG_DMA3CNT_LO:

// Write-only register return 0; case REG_DISPCNT: + case REG_DISPSTAT: case REG_VCOUNT: case REG_BG0CNT: case REG_BG1CNT:

@@ -457,6 +462,7 @@ state->dma[i].nextEvent = gba->memory.dma[i].nextEvent;

} memcpy(state->timers, gba->timers, sizeof(state->timers)); + GBAGPIOSerialize(&gba->memory.gpio, state); } void GBAIODeserialize(struct GBA* gba, struct GBASerializedState* state) {

@@ -469,12 +475,20 @@ GBAIOWrite(gba, i, state->io[i >> 1]);

} } + gba->timersEnabled = 0; + memcpy(gba->timers, state->timers, sizeof(gba->timers)); for (i = 0; i < 4; ++i) { 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) { + GBAMemoryScheduleDMA(gba, i, &gba->memory.dma[i]); + } + + if (gba->timers[i].enable) { + gba->timersEnabled |= 1 << i; + } } - - memcpy(state->timers, gba->timers, sizeof(gba->timers)); + GBAGPIODeserialize(&gba->memory.gpio, state); }
M src/gba/gba-io.hsrc/gba/gba-io.h

@@ -1,6 +1,8 @@

#ifndef GBA_IO_H #define GBA_IO_H +#include "common.h" + #include "gba.h" enum GBAIORegisters {
M src/gba/gba-memory.csrc/gba/gba-memory.c

@@ -4,15 +4,11 @@ #include "gba-gpio.h"

#include "gba-io.h" #include "gba-serialize.h" #include "hle-bios.h" -#include "memory.h" +#include "util/memory.h" -#include <limits.h> -#include <stdlib.h> -#include <string.h> - -static void GBASetActiveRegion(struct ARMMemory* memory, uint32_t region); -static int GBAWaitMultiple(struct ARMMemory* memory, uint32_t startAddress, int count); -static void GBAMemoryServiceDMA(struct GBAMemory* memory, int number, struct GBADMA* info); +static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t region); +static int GBAWaitMultiple(struct ARMCore* cpu, uint32_t startAddress, int count); +static void GBAMemoryServiceDMA(struct GBA* gba, int number, struct GBADMA* info); static const char GBA_BASE_WAITSTATES[16] = { 0, 0, 2, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4 }; static const char GBA_BASE_WAITSTATES_32[16] = { 0, 0, 5, 0, 0, 0, 0, 0, 7, 7, 9, 9, 13, 13, 9 };

@@ -22,98 +18,117 @@ static const char GBA_ROM_WAITSTATES[] = { 4, 3, 2, 8 };

static const char GBA_ROM_WAITSTATES_SEQ[] = { 2, 1, 4, 1, 8, 1 }; static const int DMA_OFFSET[] = { 1, -1, 0, 1 }; -void GBAMemoryInit(struct GBAMemory* memory) { - memory->d.load32 = GBALoad32; - memory->d.load16 = GBALoad16; - memory->d.loadU16 = GBALoadU16; - memory->d.load8 = GBALoad8; - memory->d.loadU8 = GBALoadU8; - memory->d.store32 = GBAStore32; - memory->d.store16 = GBAStore16; - memory->d.store8 = GBAStore8; +void GBAMemoryInit(struct GBA* gba) { + struct ARMCore* cpu = gba->cpu; + cpu->memory.load32 = GBALoad32; + cpu->memory.load16 = GBALoad16; + cpu->memory.loadU16 = GBALoadU16; + cpu->memory.load8 = GBALoad8; + cpu->memory.loadU8 = GBALoadU8; + cpu->memory.store32 = GBAStore32; + cpu->memory.store16 = GBAStore16; + cpu->memory.store8 = GBAStore8; - memory->bios = (uint32_t*) hleBios; - memory->fullBios = 0; - memory->wram = anonymousMemoryMap(SIZE_WORKING_RAM); - memory->iwram = anonymousMemoryMap(SIZE_WORKING_IRAM); - memory->rom = 0; - memory->gpio.p = memory->p; - memset(memory->io, 0, sizeof(memory->io)); - memset(memory->dma, 0, sizeof(memory->dma)); + gba->memory.bios = (uint32_t*) hleBios; + gba->memory.fullBios = 0; + gba->memory.wram = 0; + gba->memory.iwram = 0; + gba->memory.rom = 0; + gba->memory.gpio.p = gba; + int i; - for (i = 0; i < 4; ++i) { - memory->dma[i].count = 0x10000; - memory->dma[i].nextEvent = INT_MAX; + for (i = 0; i < 16; ++i) { + gba->memory.waitstatesNonseq16[i] = GBA_BASE_WAITSTATES[i]; + gba->memory.waitstatesSeq16[i] = GBA_BASE_WAITSTATES_SEQ[i]; + gba->memory.waitstatesPrefetchNonseq16[i] = GBA_BASE_WAITSTATES[i]; + gba->memory.waitstatesPrefetchSeq16[i] = GBA_BASE_WAITSTATES_SEQ[i]; + gba->memory.waitstatesNonseq32[i] = GBA_BASE_WAITSTATES_32[i]; + gba->memory.waitstatesSeq32[i] = GBA_BASE_WAITSTATES_SEQ_32[i]; + gba->memory.waitstatesPrefetchNonseq32[i] = GBA_BASE_WAITSTATES_32[i]; + gba->memory.waitstatesPrefetchSeq32[i] = GBA_BASE_WAITSTATES_SEQ_32[i]; } - memory->activeDMA = -1; - memory->nextDMA = INT_MAX; + for (; i < 256; ++i) { + gba->memory.waitstatesNonseq16[i] = 0; + gba->memory.waitstatesSeq16[i] = 0; + gba->memory.waitstatesNonseq32[i] = 0; + gba->memory.waitstatesSeq32[i] = 0; + } - if (!memory->wram || !memory->iwram) { - GBAMemoryDeinit(memory); - GBALog(memory->p, GBA_LOG_FATAL, "Could not map memory"); - return; + gba->memory.activeRegion = -1; + cpu->memory.activeRegion = 0; + cpu->memory.activeMask = 0; + cpu->memory.setActiveRegion = GBASetActiveRegion; + cpu->memory.activeSeqCycles32 = 0; + cpu->memory.activeSeqCycles16 = 0; + cpu->memory.activeNonseqCycles32 = 0; + cpu->memory.activeNonseqCycles16 = 0; + cpu->memory.activeUncachedCycles32 = 0; + cpu->memory.activeUncachedCycles16 = 0; + gba->memory.biosPrefetch = 0; + cpu->memory.waitMultiple = GBAWaitMultiple; +} + +void GBAMemoryDeinit(struct GBA* gba) { + mappedMemoryFree(gba->memory.wram, SIZE_WORKING_RAM); + mappedMemoryFree(gba->memory.iwram, SIZE_WORKING_IRAM); + if (gba->memory.rom) { + mappedMemoryFree(gba->memory.rom, gba->memory.romSize); } + GBASavedataDeinit(&gba->memory.savedata); +} - for (i = 0; i < 16; ++i) { - memory->waitstates16[i] = GBA_BASE_WAITSTATES[i]; - memory->waitstatesSeq16[i] = GBA_BASE_WAITSTATES_SEQ[i]; - memory->waitstatesPrefetch16[i] = GBA_BASE_WAITSTATES_SEQ[i]; - memory->waitstates32[i] = GBA_BASE_WAITSTATES_32[i]; - memory->waitstatesSeq32[i] = GBA_BASE_WAITSTATES_SEQ_32[i]; - memory->waitstatesPrefetch32[i] = GBA_BASE_WAITSTATES_SEQ_32[i]; +void GBAMemoryReset(struct GBA* gba) { + if (gba->memory.wram) { + mappedMemoryFree(gba->memory.wram, SIZE_WORKING_RAM); } - for (; i < 256; ++i) { - memory->waitstates16[i] = 0; - memory->waitstatesSeq16[i] = 0; - memory->waitstatesPrefetch16[i] = 0; - memory->waitstates32[i] = 0; - memory->waitstatesSeq32[i] = 0; - memory->waitstatesPrefetch32[i] = 0; + gba->memory.wram = anonymousMemoryMap(SIZE_WORKING_RAM); + + if (gba->memory.iwram) { + mappedMemoryFree(gba->memory.iwram, SIZE_WORKING_IRAM); } + gba->memory.iwram = anonymousMemoryMap(SIZE_WORKING_IRAM); - memory->activeRegion = -1; - memory->d.activeRegion = 0; - memory->d.activeMask = 0; - memory->d.setActiveRegion = GBASetActiveRegion; - memory->d.activePrefetchCycles32 = 0; - memory->d.activePrefetchCycles16 = 0; - memory->biosPrefetch = 0; - memory->d.waitMultiple = GBAWaitMultiple; -} + memset(gba->memory.io, 0, sizeof(gba->memory.io)); + memset(gba->memory.dma, 0, sizeof(gba->memory.dma)); + int i; + for (i = 0; i < 4; ++i) { + gba->memory.dma[i].count = 0x10000; + gba->memory.dma[i].nextEvent = INT_MAX; + } + gba->memory.activeDMA = -1; + gba->memory.nextDMA = INT_MAX; + gba->memory.eventDiff = 0; -void GBAMemoryDeinit(struct GBAMemory* memory) { - mappedMemoryFree(memory->wram, SIZE_WORKING_RAM); - mappedMemoryFree(memory->iwram, SIZE_WORKING_IRAM); - GBASavedataDeinit(&memory->savedata); + if (!gba->memory.wram || !gba->memory.iwram) { + GBAMemoryDeinit(gba); + GBALog(gba, GBA_LOG_FATAL, "Could not map memory"); + } } -static void GBASetActiveRegion(struct ARMMemory* memory, uint32_t address) { - struct GBAMemory* gbaMemory = (struct GBAMemory*) memory; +static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) { + struct GBA* gba = (struct GBA*) cpu->master; + struct GBAMemory* memory = &gba->memory; int newRegion = address >> BASE_OFFSET; - if (newRegion == gbaMemory->activeRegion) { + if (newRegion == memory->activeRegion) { return; } - if (gbaMemory->activeRegion == REGION_BIOS) { - gbaMemory->biosPrefetch = memory->load32(memory, gbaMemory->p->cpu.currentPC + WORD_SIZE_ARM * 2, 0); + if (memory->activeRegion == REGION_BIOS) { + memory->biosPrefetch = cpu->memory.load32(cpu, cpu->currentPC + WORD_SIZE_ARM * 2, 0); } - gbaMemory->activeRegion = newRegion; - memory->activePrefetchCycles32 = gbaMemory->waitstatesPrefetch32[gbaMemory->activeRegion]; - memory->activePrefetchCycles16 = gbaMemory->waitstatesPrefetch16[gbaMemory->activeRegion]; - memory->activeNonseqCycles32 = gbaMemory->waitstates32[gbaMemory->activeRegion]; - memory->activeNonseqCycles16 = gbaMemory->waitstates16[gbaMemory->activeRegion]; + memory->activeRegion = newRegion; switch (address & ~OFFSET_MASK) { case BASE_BIOS: - memory->activeRegion = gbaMemory->bios; - memory->activeMask = SIZE_BIOS - 1; + cpu->memory.activeRegion = memory->bios; + cpu->memory.activeMask = SIZE_BIOS - 1; break; case BASE_WORKING_RAM: - memory->activeRegion = gbaMemory->wram; - memory->activeMask = SIZE_WORKING_RAM - 1; + cpu->memory.activeRegion = memory->wram; + cpu->memory.activeMask = SIZE_WORKING_RAM - 1; break; case BASE_WORKING_IRAM: - memory->activeRegion = gbaMemory->iwram; - memory->activeMask = SIZE_WORKING_IRAM - 1; + cpu->memory.activeRegion = memory->iwram; + cpu->memory.activeMask = SIZE_WORKING_IRAM - 1; break; case BASE_CART0: case BASE_CART0_EX:

@@ -121,52 +136,59 @@ case BASE_CART1:

case BASE_CART1_EX: case BASE_CART2: case BASE_CART2_EX: - memory->activeRegion = gbaMemory->rom; - memory->activeMask = SIZE_CART0 - 1; + cpu->memory.activeRegion = memory->rom; + cpu->memory.activeMask = SIZE_CART0 - 1; break; default: - memory->activeRegion = 0; - memory->activeMask = 0; - GBALog(gbaMemory->p, GBA_LOG_FATAL, "Jumped to invalid address"); + cpu->memory.activeRegion = 0; + cpu->memory.activeMask = 0; + GBALog(gba, GBA_LOG_FATAL, "Jumped to invalid address"); break; } + cpu->memory.activeSeqCycles32 = memory->waitstatesPrefetchSeq32[memory->activeRegion]; + cpu->memory.activeSeqCycles16 = memory->waitstatesPrefetchSeq16[memory->activeRegion]; + cpu->memory.activeNonseqCycles32 = memory->waitstatesPrefetchNonseq32[memory->activeRegion]; + cpu->memory.activeNonseqCycles16 = memory->waitstatesPrefetchNonseq16[memory->activeRegion]; + cpu->memory.activeUncachedCycles32 = memory->waitstatesNonseq32[memory->activeRegion]; + cpu->memory.activeUncachedCycles16 = memory->waitstatesNonseq16[memory->activeRegion]; } -int32_t GBALoad32(struct ARMMemory* memory, uint32_t address, int* cycleCounter) { - struct GBAMemory* gbaMemory = (struct GBAMemory*) memory; +int32_t GBALoad32(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { + struct GBA* gba = (struct GBA*) cpu->master; + struct GBAMemory* memory = &gba->memory; uint32_t value = 0; int wait = 0; switch (address & ~OFFSET_MASK) { case BASE_BIOS: - if (gbaMemory->p->cpu.currentPC >> BASE_OFFSET == REGION_BIOS) { + if (cpu->currentPC >> BASE_OFFSET == REGION_BIOS) { if (address < SIZE_BIOS) { - LOAD_32(value, address, gbaMemory->bios); + LOAD_32(value, address, memory->bios); } else { value = 0; } } else { - value = gbaMemory->biosPrefetch; + value = memory->biosPrefetch; } break; case BASE_WORKING_RAM: - LOAD_32(value, address & (SIZE_WORKING_RAM - 1), gbaMemory->wram); - wait = gbaMemory->waitstates32[REGION_WORKING_RAM]; + LOAD_32(value, address & (SIZE_WORKING_RAM - 1), memory->wram); + wait = memory->waitstatesNonseq32[REGION_WORKING_RAM]; break; case BASE_WORKING_IRAM: - LOAD_32(value, address & (SIZE_WORKING_IRAM - 1), gbaMemory->iwram); + LOAD_32(value, address & (SIZE_WORKING_IRAM - 1), memory->iwram); break; case BASE_IO: - value = GBAIORead(gbaMemory->p, (address & (SIZE_IO - 1)) & ~2) | (GBAIORead(gbaMemory->p, (address & (SIZE_IO - 1)) | 2) << 16); + value = GBAIORead(gba, (address & (SIZE_IO - 1)) & ~2) | (GBAIORead(gba, (address & (SIZE_IO - 1)) | 2) << 16); break; case BASE_PALETTE_RAM: - LOAD_32(value, address & (SIZE_PALETTE_RAM - 1), gbaMemory->p->video.palette); + LOAD_32(value, address & (SIZE_PALETTE_RAM - 1), gba->video.palette); break; case BASE_VRAM: - LOAD_32(value, address & 0x0001FFFF, gbaMemory->p->video.renderer->vram); + LOAD_32(value, address & 0x0001FFFF, gba->video.renderer->vram); break; case BASE_OAM: - LOAD_32(value, address & (SIZE_OAM - 1), gbaMemory->p->video.oam.raw); + LOAD_32(value, address & (SIZE_OAM - 1), gba->video.oam.raw); break; case BASE_CART0: case BASE_CART0_EX:

@@ -174,21 +196,21 @@ case BASE_CART1:

case BASE_CART1_EX: case BASE_CART2: case BASE_CART2_EX: - wait = gbaMemory->waitstates32[address >> BASE_OFFSET]; - if ((address & (SIZE_CART0 - 1)) < gbaMemory->romSize) { - LOAD_32(value, address & (SIZE_CART0 - 1), gbaMemory->rom); + 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: - GBALog(gbaMemory->p, GBA_LOG_STUB, "Unimplemented memory Load32: 0x%08X", address); + GBALog(gba, GBA_LOG_STUB, "Unimplemented memory Load32: 0x%08X", address); break; default: - GBALog(gbaMemory->p, GBA_LOG_GAME_ERROR, "Bad memory Load32: 0x%08X", address); - if (gbaMemory->p->cpu.executionMode == MODE_ARM) { - value = memory->load32(memory, gbaMemory->p->cpu.currentPC + WORD_SIZE_ARM * 2, 0); + 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 = memory->load16(memory, gbaMemory->p->cpu.currentPC + WORD_SIZE_THUMB * 2, 0); + value = cpu->memory.load16(cpu, cpu->currentPC + WORD_SIZE_THUMB * 2, 0); value |= value << 16; } break;

@@ -203,71 +225,72 @@ int rotate = (address & 3) << 3;

return (value >> rotate) | (value << (32 - rotate)); } -uint16_t GBALoadU16(struct ARMMemory* memory, uint32_t address, int* cycleCounter) { - return GBALoad16(memory, address, cycleCounter); +uint16_t GBALoadU16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { + return GBALoad16(cpu, address, cycleCounter); } -int16_t GBALoad16(struct ARMMemory* memory, uint32_t address, int* cycleCounter) { - struct GBAMemory* gbaMemory = (struct GBAMemory*) memory; +int16_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { + struct GBA* gba = (struct GBA*) cpu->master; + struct GBAMemory* memory = &gba->memory; uint16_t value = 0; int wait = 0; switch (address & ~OFFSET_MASK) { case BASE_BIOS: - if (gbaMemory->p->cpu.currentPC >> BASE_OFFSET == REGION_BIOS) { + if (cpu->currentPC >> BASE_OFFSET == REGION_BIOS) { if (address < SIZE_BIOS) { - LOAD_16(value, address, gbaMemory->bios); + LOAD_16(value, address, memory->bios); } else { value = 0; } } else { - value = gbaMemory->biosPrefetch; + value = memory->biosPrefetch; } break; case BASE_WORKING_RAM: - LOAD_16(value, address & (SIZE_WORKING_RAM - 1), gbaMemory->wram); - wait = gbaMemory->waitstates16[REGION_WORKING_RAM]; + LOAD_16(value, address & (SIZE_WORKING_RAM - 1), memory->wram); + wait = memory->waitstatesNonseq16[REGION_WORKING_RAM]; break; case BASE_WORKING_IRAM: - LOAD_16(value, address & (SIZE_WORKING_IRAM - 1), gbaMemory->iwram); + LOAD_16(value, address & (SIZE_WORKING_IRAM - 1), memory->iwram); break; case BASE_IO: - value = GBAIORead(gbaMemory->p, address & (SIZE_IO - 1)); + value = GBAIORead(gba, address & (SIZE_IO - 1)); break; case BASE_PALETTE_RAM: - LOAD_16(value, address & (SIZE_PALETTE_RAM - 1), gbaMemory->p->video.palette); + LOAD_16(value, address & (SIZE_PALETTE_RAM - 1), gba->video.palette); break; case BASE_VRAM: - LOAD_16(value, address & 0x0001FFFF, gbaMemory->p->video.renderer->vram); + LOAD_16(value, address & 0x0001FFFF, gba->video.renderer->vram); break; case BASE_OAM: - LOAD_16(value, address & (SIZE_OAM - 1), gbaMemory->p->video.oam.raw); + 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: - wait = gbaMemory->waitstates16[address >> BASE_OFFSET]; - if ((address & (SIZE_CART0 - 1)) < gbaMemory->romSize) { - LOAD_16(value, address & (SIZE_CART0 - 1), gbaMemory->rom); + 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: - wait = gbaMemory->waitstates16[address >> BASE_OFFSET]; - if (gbaMemory->savedata.type == SAVEDATA_EEPROM) { - value = GBASavedataReadEEPROM(&gbaMemory->savedata); - } else if ((address & (SIZE_CART0 - 1)) < gbaMemory->romSize) { - LOAD_16(value, address & (SIZE_CART0 - 1), gbaMemory->rom); + wait = memory->waitstatesNonseq16[address >> BASE_OFFSET]; + if (memory->savedata.type == SAVEDATA_EEPROM) { + value = GBASavedataReadEEPROM(&memory->savedata); + } 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: - GBALog(gbaMemory->p, GBA_LOG_STUB, "Unimplemented memory Load16: 0x%08X", address); + GBALog(gba, GBA_LOG_STUB, "Unimplemented memory Load16: 0x%08X", address); break; default: - GBALog(gbaMemory->p, GBA_LOG_GAME_ERROR, "Bad memory Load16: 0x%08X", address); - value = memory->load16(memory, gbaMemory->p->cpu.currentPC + (gbaMemory->p->cpu.executionMode == MODE_ARM ? WORD_SIZE_ARM : WORD_SIZE_THUMB) * 2, 0); + 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); break; }

@@ -279,45 +302,46 @@ int rotate = (address & 1) << 3;

return (value >> rotate) | (value << (16 - rotate)); } -uint8_t GBALoadU8(struct ARMMemory* memory, uint32_t address, int* cycleCounter) { - return GBALoad8(memory, address, cycleCounter); +uint8_t GBALoadU8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { + return GBALoad8(cpu, address, cycleCounter); } -int8_t GBALoad8(struct ARMMemory* memory, uint32_t address, int* cycleCounter) { - struct GBAMemory* gbaMemory = (struct GBAMemory*) memory; +int8_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { + struct GBA* gba = (struct GBA*) cpu->master; + struct GBAMemory* memory = &gba->memory; int8_t value = 0; int wait = 0; switch (address & ~OFFSET_MASK) { case BASE_BIOS: - if (gbaMemory->p->cpu.currentPC >> BASE_OFFSET == REGION_BIOS) { + if (cpu->currentPC >> BASE_OFFSET == REGION_BIOS) { if (address < SIZE_BIOS) { - value = ((int8_t*) gbaMemory->bios)[address]; + value = ((int8_t*) memory->bios)[address]; } else { value = 0; } } else { - value = gbaMemory->biosPrefetch; + value = memory->biosPrefetch; } break; case BASE_WORKING_RAM: - value = ((int8_t*) gbaMemory->wram)[address & (SIZE_WORKING_RAM - 1)]; - wait = gbaMemory->waitstates16[REGION_WORKING_RAM]; + value = ((int8_t*) memory->wram)[address & (SIZE_WORKING_RAM - 1)]; + wait = memory->waitstatesNonseq16[REGION_WORKING_RAM]; break; case BASE_WORKING_IRAM: - value = ((int8_t*) gbaMemory->iwram)[address & (SIZE_WORKING_IRAM - 1)]; + value = ((int8_t*) memory->iwram)[address & (SIZE_WORKING_IRAM - 1)]; break; case BASE_IO: - value = (GBAIORead(gbaMemory->p, address & 0xFFFE) >> ((address & 0x0001) << 3)) & 0xFF; + value = (GBAIORead(gba, address & 0xFFFE) >> ((address & 0x0001) << 3)) & 0xFF; break; case BASE_PALETTE_RAM: - value = ((int8_t*) gbaMemory->p->video.renderer->palette)[address & (SIZE_PALETTE_RAM - 1)]; + value = ((int8_t*) gba->video.renderer->palette)[address & (SIZE_PALETTE_RAM - 1)]; break; case BASE_VRAM: - value = ((int8_t*) gbaMemory->p->video.renderer->vram)[address & 0x0001FFFF]; + value = ((int8_t*) gba->video.renderer->vram)[address & 0x0001FFFF]; break; case BASE_OAM: - GBALog(gbaMemory->p, GBA_LOG_STUB, "Unimplemented memory Load8: 0x%08X", address); + GBALog(gba, GBA_LOG_STUB, "Unimplemented memory Load8: 0x%08X", address); break; case BASE_CART0: case BASE_CART0_EX:

@@ -325,26 +349,26 @@ case BASE_CART1:

case BASE_CART1_EX: case BASE_CART2: case BASE_CART2_EX: - wait = gbaMemory->waitstates16[address >> BASE_OFFSET]; - if ((address & (SIZE_CART0 - 1)) < gbaMemory->romSize) { - value = ((int8_t*) gbaMemory->rom)[address & (SIZE_CART0 - 1)]; + 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: - wait = gbaMemory->waitstates16[address >> BASE_OFFSET]; - if (gbaMemory->savedata.type == SAVEDATA_NONE) { - GBASavedataInitSRAM(&gbaMemory->savedata); + wait = memory->waitstatesNonseq16[address >> BASE_OFFSET]; + if (memory->savedata.type == SAVEDATA_NONE) { + GBASavedataInitSRAM(&memory->savedata); } - if (gbaMemory->savedata.type == SAVEDATA_SRAM) { - value = gbaMemory->savedata.data[address & (SIZE_CART_SRAM - 1)]; - } else if (gbaMemory->savedata.type == SAVEDATA_FLASH512 || gbaMemory->savedata.type == SAVEDATA_FLASH1M) { - value = GBASavedataReadFlash(&gbaMemory->savedata, address); + if (memory->savedata.type == SAVEDATA_SRAM) { + value = memory->savedata.data[address & (SIZE_CART_SRAM - 1)]; + } else if (memory->savedata.type == SAVEDATA_FLASH512 || memory->savedata.type == SAVEDATA_FLASH1M) { + value = GBASavedataReadFlash(&memory->savedata, address); } break; default: - GBALog(gbaMemory->p, GBA_LOG_GAME_ERROR, "Bad memory Load8: 0x%08x", address); - value = memory->load16(memory, gbaMemory->p->cpu.currentPC + (gbaMemory->p->cpu.executionMode == MODE_ARM ? WORD_SIZE_ARM : WORD_SIZE_THUMB) * 2, 0) >> ((address & 1) << 3); + 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); break; }

@@ -354,45 +378,48 @@ }

return value; } -void GBAStore32(struct ARMMemory* memory, uint32_t address, int32_t value, int* cycleCounter) { - struct GBAMemory* gbaMemory = (struct GBAMemory*) memory; +void GBAStore32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycleCounter) { + struct GBA* gba = (struct GBA*) cpu->master; + struct GBAMemory* memory = &gba->memory; int wait = 0; switch (address & ~OFFSET_MASK) { case BASE_WORKING_RAM: - STORE_32(value, address & (SIZE_WORKING_RAM - 1), gbaMemory->wram); - wait = gbaMemory->waitstates32[REGION_WORKING_RAM]; + STORE_32(value, address & (SIZE_WORKING_RAM - 1), memory->wram); + wait = memory->waitstatesNonseq32[REGION_WORKING_RAM]; break; case BASE_WORKING_IRAM: - STORE_32(value, address & (SIZE_WORKING_IRAM - 1), gbaMemory->iwram); + STORE_32(value, address & (SIZE_WORKING_IRAM - 1), memory->iwram); break; case BASE_IO: - GBAIOWrite32(gbaMemory->p, address & (SIZE_IO - 1), value); + GBAIOWrite32(gba, address & (SIZE_IO - 1), value); break; case BASE_PALETTE_RAM: - STORE_32(value, address & (SIZE_PALETTE_RAM - 1), gbaMemory->p->video.palette); - gbaMemory->p->video.renderer->writePalette(gbaMemory->p->video.renderer, (address & (SIZE_PALETTE_RAM - 1)) + 2, value >> 16); - gbaMemory->p->video.renderer->writePalette(gbaMemory->p->video.renderer, address & (SIZE_PALETTE_RAM - 1), value); + 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: - if ((address & OFFSET_MASK) < SIZE_VRAM - 2) { - STORE_32(value, address & 0x0001FFFF, gbaMemory->p->video.renderer->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: - STORE_32(value, address & (SIZE_OAM - 1), gbaMemory->p->video.oam.raw); - gbaMemory->p->video.renderer->writeOAM(gbaMemory->p->video.renderer, (address & (SIZE_OAM - 4)) >> 1); - gbaMemory->p->video.renderer->writeOAM(gbaMemory->p->video.renderer, ((address & (SIZE_OAM - 4)) >> 1) + 1); + 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: - GBALog(gbaMemory->p, GBA_LOG_STUB, "Unimplemented memory Store32: 0x%08X", address); + GBALog(gba, GBA_LOG_STUB, "Unimplemented memory Store32: 0x%08X", address); break; case BASE_CART_SRAM: case BASE_CART_SRAM_MIRROR: - GBALog(gbaMemory->p, GBA_LOG_STUB, "Unimplemented memory Store32: 0x%08X", address); + GBALog(gba, GBA_LOG_STUB, "Unimplemented memory Store32: 0x%08X", address); break; default: - GBALog(gbaMemory->p, GBA_LOG_GAME_ERROR, "Bad memory Store32: 0x%08X", address); + GBALog(gba, GBA_LOG_GAME_ERROR, "Bad memory Store32: 0x%08X", address); break; }

@@ -401,54 +428,57 @@ *cycleCounter += 1 + wait;

} } -void GBAStore16(struct ARMMemory* memory, uint32_t address, int16_t value, int* cycleCounter) { - struct GBAMemory* gbaMemory = (struct GBAMemory*) memory; +void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycleCounter) { + struct GBA* gba = (struct GBA*) cpu->master; + struct GBAMemory* memory = &gba->memory; int wait = 0; switch (address & ~OFFSET_MASK) { case BASE_WORKING_RAM: - STORE_16(value, address & (SIZE_WORKING_RAM - 1), gbaMemory->wram); - wait = gbaMemory->waitstates16[REGION_WORKING_RAM]; + STORE_16(value, address & (SIZE_WORKING_RAM - 1), memory->wram); + wait = memory->waitstatesNonseq16[REGION_WORKING_RAM]; break; case BASE_WORKING_IRAM: - STORE_16(value, address & (SIZE_WORKING_IRAM - 1), gbaMemory->iwram); + STORE_16(value, address & (SIZE_WORKING_IRAM - 1), memory->iwram); break; case BASE_IO: - GBAIOWrite(gbaMemory->p, address & (SIZE_IO - 1), value); + GBAIOWrite(gba, address & (SIZE_IO - 1), value); break; case BASE_PALETTE_RAM: - STORE_16(value, address & (SIZE_PALETTE_RAM - 1), gbaMemory->p->video.palette); - gbaMemory->p->video.renderer->writePalette(gbaMemory->p->video.renderer, address & (SIZE_PALETTE_RAM - 1), value); + 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: if ((address & OFFSET_MASK) < SIZE_VRAM) { - STORE_16(value, address & 0x0001FFFF, gbaMemory->p->video.renderer->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: - STORE_16(value, address & (SIZE_OAM - 1), gbaMemory->p->video.oam.raw); - gbaMemory->p->video.renderer->writeOAM(gbaMemory->p->video.renderer, (address & (SIZE_OAM - 1)) >> 1); + 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: if (IS_GPIO_REGISTER(address & 0xFFFFFF)) { uint32_t reg = address & 0xFFFFFF; - GBAGPIOWrite(&gbaMemory->gpio, reg, value); + GBAGPIOWrite(&memory->gpio, reg, value); } else { - GBALog(gbaMemory->p, GBA_LOG_GAME_ERROR, "Bad cartridge Store16: 0x%08X", address); + GBALog(gba, GBA_LOG_GAME_ERROR, "Bad cartridge Store16: 0x%08X", address); } break; case BASE_CART2_EX: - if (gbaMemory->savedata.type == SAVEDATA_NONE) { - GBASavedataInitEEPROM(&gbaMemory->savedata); + if (memory->savedata.type == SAVEDATA_NONE) { + GBASavedataInitEEPROM(&memory->savedata); } - GBASavedataWriteEEPROM(&gbaMemory->savedata, value, 1); + GBASavedataWriteEEPROM(&memory->savedata, value, 1); break; case BASE_CART_SRAM: case BASE_CART_SRAM_MIRROR: - GBALog(gbaMemory->p, GBA_LOG_STUB, "Unimplemented memory Store16: 0x%08X", address); + GBALog(gba, GBA_LOG_STUB, "Unimplemented memory Store16: 0x%08X", address); break; default: - GBALog(gbaMemory->p, GBA_LOG_GAME_ERROR, "Bad memory Store16: 0x%08X", address); + GBALog(gba, GBA_LOG_GAME_ERROR, "Bad memory Store16: 0x%08X", address); break; }

@@ -457,57 +487,58 @@ *cycleCounter += 1 + wait;

} } -void GBAStore8(struct ARMMemory* memory, uint32_t address, int8_t value, int* cycleCounter) { - struct GBAMemory* gbaMemory = (struct GBAMemory*) memory; +void GBAStore8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCounter) { + struct GBA* gba = (struct GBA*) cpu->master; + struct GBAMemory* memory = &gba->memory; int wait = 0; switch (address & ~OFFSET_MASK) { case BASE_WORKING_RAM: - ((int8_t*) gbaMemory->wram)[address & (SIZE_WORKING_RAM - 1)] = value; - wait = gbaMemory->waitstates16[REGION_WORKING_RAM]; + ((int8_t*) memory->wram)[address & (SIZE_WORKING_RAM - 1)] = value; + wait = memory->waitstatesNonseq16[REGION_WORKING_RAM]; break; case BASE_WORKING_IRAM: - ((int8_t*) gbaMemory->iwram)[address & (SIZE_WORKING_IRAM - 1)] = value; + ((int8_t*) memory->iwram)[address & (SIZE_WORKING_IRAM - 1)] = value; break; case BASE_IO: - GBAIOWrite8(gbaMemory->p, address & (SIZE_IO - 1), value); + GBAIOWrite8(gba, address & (SIZE_IO - 1), value); break; case BASE_PALETTE_RAM: - GBALog(gbaMemory->p, GBA_LOG_STUB, "Unimplemented memory Store8: 0x%08X", address); + GBALog(gba, GBA_LOG_STUB, "Unimplemented memory Store8: 0x%08X", address); break; case BASE_VRAM: if (address >= 0x06018000) { // TODO: check BG mode - GBALog(gbaMemory->p, GBA_LOG_GAME_ERROR, "Cannot Store8 to OBJ: 0x%08X", address); + GBALog(gba, GBA_LOG_GAME_ERROR, "Cannot Store8 to OBJ: 0x%08X", address); break; } - ((int8_t*) gbaMemory->p->video.renderer->vram)[address & 0x1FFFE] = value; - ((int8_t*) gbaMemory->p->video.renderer->vram)[(address & 0x1FFFE) | 1] = value; + ((int8_t*) gba->video.renderer->vram)[address & 0x1FFFE] = value; + ((int8_t*) gba->video.renderer->vram)[(address & 0x1FFFE) | 1] = value; break; case BASE_OAM: - GBALog(gbaMemory->p, GBA_LOG_GAME_ERROR, "Cannot Store8 to OAM: 0x%08X", address); + GBALog(gba, GBA_LOG_GAME_ERROR, "Cannot Store8 to OAM: 0x%08X", address); break; case BASE_CART0: - GBALog(gbaMemory->p, GBA_LOG_STUB, "Unimplemented memory Store8: 0x%08X", address); + GBALog(gba, GBA_LOG_STUB, "Unimplemented memory Store8: 0x%08X", address); break; case BASE_CART_SRAM: case BASE_CART_SRAM_MIRROR: - if (gbaMemory->savedata.type == SAVEDATA_NONE) { + if (memory->savedata.type == SAVEDATA_NONE) { if (address == SAVEDATA_FLASH_BASE) { - GBASavedataInitFlash(&gbaMemory->savedata); + GBASavedataInitFlash(&memory->savedata); } else { - GBASavedataInitSRAM(&gbaMemory->savedata); + GBASavedataInitSRAM(&memory->savedata); } } - if (gbaMemory->savedata.type == SAVEDATA_FLASH512 || gbaMemory->savedata.type == SAVEDATA_FLASH1M) { - GBASavedataWriteFlash(&gbaMemory->savedata, address, value); - } else if (gbaMemory->savedata.type == SAVEDATA_SRAM) { - gbaMemory->savedata.data[address & (SIZE_CART_SRAM - 1)] = value; + if (memory->savedata.type == SAVEDATA_FLASH512 || memory->savedata.type == SAVEDATA_FLASH1M) { + GBASavedataWriteFlash(&memory->savedata, address, value); + } else if (memory->savedata.type == SAVEDATA_SRAM) { + memory->savedata.data[address & (SIZE_CART_SRAM - 1)] = value; } - wait = gbaMemory->waitstates16[REGION_CART_SRAM]; + wait = memory->waitstatesNonseq16[REGION_CART_SRAM]; break; default: - GBALog(gbaMemory->p, GBA_LOG_GAME_ERROR, "Bad memory Store8: 0x%08X", address); + GBALog(gba, GBA_LOG_GAME_ERROR, "Bad memory Store8: 0x%08X", address); break; }

@@ -516,14 +547,17 @@ *cycleCounter += 1 + wait;

} } -static int GBAWaitMultiple(struct ARMMemory* memory, uint32_t startAddress, int count) { - struct GBAMemory* gbaMemory = (struct GBAMemory*) memory; - int wait = 1 + gbaMemory->waitstates32[startAddress >> BASE_OFFSET]; - wait += (1 + gbaMemory->waitstatesSeq32[startAddress >> BASE_OFFSET]) * (count - 1); +static int GBAWaitMultiple(struct ARMCore* cpu, uint32_t startAddress, int count) { + struct GBA* gba = (struct GBA*) cpu->master; + struct GBAMemory* memory = &gba->memory; + int wait = 1 + memory->waitstatesNonseq32[startAddress >> BASE_OFFSET]; + wait += (1 + memory->waitstatesSeq32[startAddress >> BASE_OFFSET]) * (count - 1); return wait; } -void GBAAdjustWaitstates(struct GBAMemory* memory, uint16_t parameters) { +void GBAAdjustWaitstates(struct GBA* gba, uint16_t parameters) { + struct GBAMemory* memory = &gba->memory; + struct ARMCore* cpu = gba->cpu; int sram = parameters & 0x0003; int ws0 = (parameters & 0x000C) >> 2; int ws0seq = (parameters & 0x0010) >> 4;

@@ -533,87 +567,112 @@ int ws2 = (parameters & 0x0300) >> 8;

int ws2seq = (parameters & 0x0400) >> 10; int prefetch = parameters & 0x4000; - memory->waitstates16[REGION_CART_SRAM] = memory->waitstates16[REGION_CART_SRAM_MIRROR] = GBA_ROM_WAITSTATES[sram]; + memory->waitstatesNonseq16[REGION_CART_SRAM] = memory->waitstatesNonseq16[REGION_CART_SRAM_MIRROR] = GBA_ROM_WAITSTATES[sram]; memory->waitstatesSeq16[REGION_CART_SRAM] = memory->waitstatesSeq16[REGION_CART_SRAM_MIRROR] = GBA_ROM_WAITSTATES[sram]; - memory->waitstates32[REGION_CART_SRAM] = memory->waitstates32[REGION_CART_SRAM_MIRROR] = 2 * GBA_ROM_WAITSTATES[sram] + 1; + memory->waitstatesNonseq32[REGION_CART_SRAM] = memory->waitstatesNonseq32[REGION_CART_SRAM_MIRROR] = 2 * GBA_ROM_WAITSTATES[sram] + 1; memory->waitstatesSeq32[REGION_CART_SRAM] = memory->waitstatesSeq32[REGION_CART_SRAM_MIRROR] = 2 * GBA_ROM_WAITSTATES[sram] + 1; - memory->waitstates16[REGION_CART0] = memory->waitstates16[REGION_CART0_EX] = GBA_ROM_WAITSTATES[ws0]; - memory->waitstates16[REGION_CART1] = memory->waitstates16[REGION_CART1_EX] = GBA_ROM_WAITSTATES[ws1]; - memory->waitstates16[REGION_CART2] = memory->waitstates16[REGION_CART2_EX] = GBA_ROM_WAITSTATES[ws2]; + memory->waitstatesNonseq16[REGION_CART0] = memory->waitstatesNonseq16[REGION_CART0_EX] = GBA_ROM_WAITSTATES[ws0]; + memory->waitstatesNonseq16[REGION_CART1] = memory->waitstatesNonseq16[REGION_CART1_EX] = GBA_ROM_WAITSTATES[ws1]; + memory->waitstatesNonseq16[REGION_CART2] = memory->waitstatesNonseq16[REGION_CART2_EX] = GBA_ROM_WAITSTATES[ws2]; memory->waitstatesSeq16[REGION_CART0] = memory->waitstatesSeq16[REGION_CART0_EX] = GBA_ROM_WAITSTATES_SEQ[ws0seq]; memory->waitstatesSeq16[REGION_CART1] = memory->waitstatesSeq16[REGION_CART1_EX] = GBA_ROM_WAITSTATES_SEQ[ws1seq + 2]; memory->waitstatesSeq16[REGION_CART2] = memory->waitstatesSeq16[REGION_CART2_EX] = GBA_ROM_WAITSTATES_SEQ[ws2seq + 4]; - memory->waitstates32[REGION_CART0] = memory->waitstates32[REGION_CART0_EX] = memory->waitstates16[REGION_CART0] + 1 + memory->waitstatesSeq16[REGION_CART0]; - memory->waitstates32[REGION_CART1] = memory->waitstates32[REGION_CART1_EX] = memory->waitstates16[REGION_CART1] + 1 + memory->waitstatesSeq16[REGION_CART1]; - memory->waitstates32[REGION_CART2] = memory->waitstates32[REGION_CART2_EX] = memory->waitstates16[REGION_CART2] + 1 + memory->waitstatesSeq16[REGION_CART2]; + memory->waitstatesNonseq32[REGION_CART0] = memory->waitstatesNonseq32[REGION_CART0_EX] = memory->waitstatesSeq16[REGION_CART0] + 1 + memory->waitstatesSeq16[REGION_CART0]; + memory->waitstatesNonseq32[REGION_CART1] = memory->waitstatesNonseq32[REGION_CART1_EX] = memory->waitstatesSeq16[REGION_CART1] + 1 + memory->waitstatesSeq16[REGION_CART1]; + memory->waitstatesNonseq32[REGION_CART2] = memory->waitstatesNonseq32[REGION_CART2_EX] = memory->waitstatesSeq16[REGION_CART2] + 1 + memory->waitstatesSeq16[REGION_CART2]; memory->waitstatesSeq32[REGION_CART0] = memory->waitstatesSeq32[REGION_CART0_EX] = 2 * memory->waitstatesSeq16[REGION_CART0] + 1; memory->waitstatesSeq32[REGION_CART1] = memory->waitstatesSeq32[REGION_CART1_EX] = 2 * memory->waitstatesSeq16[REGION_CART1] + 1; memory->waitstatesSeq32[REGION_CART2] = memory->waitstatesSeq32[REGION_CART2_EX] = 2 * memory->waitstatesSeq16[REGION_CART2] + 1; if (!prefetch) { - memory->waitstatesPrefetch16[REGION_CART0] = memory->waitstatesPrefetch16[REGION_CART0_EX] = memory->waitstatesSeq16[REGION_CART0]; - memory->waitstatesPrefetch16[REGION_CART1] = memory->waitstatesPrefetch16[REGION_CART1_EX] = memory->waitstatesSeq16[REGION_CART1]; - memory->waitstatesPrefetch16[REGION_CART2] = memory->waitstatesPrefetch16[REGION_CART2_EX] = memory->waitstatesSeq16[REGION_CART2]; + memory->waitstatesPrefetchSeq16[REGION_CART0] = memory->waitstatesPrefetchSeq16[REGION_CART0_EX] = memory->waitstatesSeq16[REGION_CART0]; + memory->waitstatesPrefetchSeq16[REGION_CART1] = memory->waitstatesPrefetchSeq16[REGION_CART1_EX] = memory->waitstatesSeq16[REGION_CART1]; + memory->waitstatesPrefetchSeq16[REGION_CART2] = memory->waitstatesPrefetchSeq16[REGION_CART2_EX] = memory->waitstatesSeq16[REGION_CART2]; + + memory->waitstatesPrefetchSeq32[REGION_CART0] = memory->waitstatesPrefetchSeq32[REGION_CART0_EX] = memory->waitstatesSeq32[REGION_CART0]; + memory->waitstatesPrefetchSeq32[REGION_CART1] = memory->waitstatesPrefetchSeq32[REGION_CART1_EX] = memory->waitstatesSeq32[REGION_CART1]; + memory->waitstatesPrefetchSeq32[REGION_CART2] = memory->waitstatesPrefetchSeq32[REGION_CART2_EX] = memory->waitstatesSeq32[REGION_CART2]; - memory->waitstatesPrefetch32[REGION_CART0] = memory->waitstatesPrefetch32[REGION_CART0_EX] = memory->waitstatesSeq32[REGION_CART0]; - memory->waitstatesPrefetch32[REGION_CART1] = memory->waitstatesPrefetch32[REGION_CART1_EX] = memory->waitstatesSeq32[REGION_CART1]; - memory->waitstatesPrefetch32[REGION_CART2] = memory->waitstatesPrefetch32[REGION_CART2_EX] = memory->waitstatesSeq32[REGION_CART2]; + memory->waitstatesPrefetchNonseq16[REGION_CART0] = memory->waitstatesPrefetchNonseq16[REGION_CART0_EX] = memory->waitstatesNonseq16[REGION_CART0]; + memory->waitstatesPrefetchNonseq16[REGION_CART1] = memory->waitstatesPrefetchNonseq16[REGION_CART1_EX] = memory->waitstatesNonseq16[REGION_CART1]; + memory->waitstatesPrefetchNonseq16[REGION_CART2] = memory->waitstatesPrefetchNonseq16[REGION_CART2_EX] = memory->waitstatesNonseq16[REGION_CART2]; + + memory->waitstatesPrefetchNonseq32[REGION_CART0] = memory->waitstatesPrefetchNonseq32[REGION_CART0_EX] = memory->waitstatesNonseq32[REGION_CART0]; + memory->waitstatesPrefetchNonseq32[REGION_CART1] = memory->waitstatesPrefetchNonseq32[REGION_CART1_EX] = memory->waitstatesNonseq32[REGION_CART1]; + memory->waitstatesPrefetchNonseq32[REGION_CART2] = memory->waitstatesPrefetchNonseq32[REGION_CART2_EX] = memory->waitstatesNonseq32[REGION_CART2]; } else { - memory->waitstatesPrefetch16[REGION_CART0] = memory->waitstatesPrefetch16[REGION_CART0_EX] = 0; - memory->waitstatesPrefetch16[REGION_CART1] = memory->waitstatesPrefetch16[REGION_CART1_EX] = 0; - memory->waitstatesPrefetch16[REGION_CART2] = memory->waitstatesPrefetch16[REGION_CART2_EX] = 0; + memory->waitstatesPrefetchSeq16[REGION_CART0] = memory->waitstatesPrefetchSeq16[REGION_CART0_EX] = 0; + memory->waitstatesPrefetchSeq16[REGION_CART1] = memory->waitstatesPrefetchSeq16[REGION_CART1_EX] = 0; + memory->waitstatesPrefetchSeq16[REGION_CART2] = memory->waitstatesPrefetchSeq16[REGION_CART2_EX] = 0; + + memory->waitstatesPrefetchSeq32[REGION_CART0] = memory->waitstatesPrefetchSeq32[REGION_CART0_EX] = 0; + memory->waitstatesPrefetchSeq32[REGION_CART1] = memory->waitstatesPrefetchSeq32[REGION_CART1_EX] = 0; + memory->waitstatesPrefetchSeq32[REGION_CART2] = memory->waitstatesPrefetchSeq32[REGION_CART2_EX] = 0; - memory->waitstatesPrefetch32[REGION_CART0] = memory->waitstatesPrefetch32[REGION_CART0_EX] = 0; - memory->waitstatesPrefetch32[REGION_CART1] = memory->waitstatesPrefetch32[REGION_CART1_EX] = 0; - memory->waitstatesPrefetch32[REGION_CART2] = memory->waitstatesPrefetch32[REGION_CART2_EX] = 0; + memory->waitstatesPrefetchNonseq16[REGION_CART0] = memory->waitstatesPrefetchNonseq16[REGION_CART0_EX] = 0; + memory->waitstatesPrefetchNonseq16[REGION_CART1] = memory->waitstatesPrefetchNonseq16[REGION_CART1_EX] = 0; + memory->waitstatesPrefetchNonseq16[REGION_CART2] = memory->waitstatesPrefetchNonseq16[REGION_CART2_EX] = 0; + + memory->waitstatesPrefetchNonseq32[REGION_CART0] = memory->waitstatesPrefetchNonseq32[REGION_CART0_EX] = 0; + memory->waitstatesPrefetchNonseq32[REGION_CART1] = memory->waitstatesPrefetchNonseq32[REGION_CART1_EX] = 0; + memory->waitstatesPrefetchNonseq32[REGION_CART2] = memory->waitstatesPrefetchNonseq32[REGION_CART2_EX] = 0; } - memory->d.activePrefetchCycles32 = memory->waitstatesPrefetch32[memory->activeRegion]; - memory->d.activePrefetchCycles16 = memory->waitstatesPrefetch16[memory->activeRegion]; - memory->d.activeNonseqCycles32 = memory->waitstates32[memory->activeRegion]; - memory->d.activeNonseqCycles16 = memory->waitstates16[memory->activeRegion]; + cpu->memory.activeSeqCycles32 = memory->waitstatesPrefetchSeq32[memory->activeRegion]; + cpu->memory.activeSeqCycles16 = memory->waitstatesPrefetchSeq16[memory->activeRegion]; + + cpu->memory.activeNonseqCycles32 = memory->waitstatesPrefetchNonseq32[memory->activeRegion]; + cpu->memory.activeNonseqCycles16 = memory->waitstatesPrefetchNonseq16[memory->activeRegion]; + + cpu->memory.activeUncachedCycles32 = memory->waitstatesNonseq32[memory->activeRegion]; + cpu->memory.activeUncachedCycles16 = memory->waitstatesNonseq16[memory->activeRegion]; } -void GBAMemoryWriteDMASAD(struct GBAMemory* memory, int dma, uint32_t address) { +void GBAMemoryWriteDMASAD(struct GBA* gba, int dma, uint32_t address) { + struct GBAMemory* memory = &gba->memory; memory->dma[dma].source = address & 0xFFFFFFFE; } -void GBAMemoryWriteDMADAD(struct GBAMemory* memory, int dma, uint32_t address) { +void GBAMemoryWriteDMADAD(struct GBA* gba, int dma, uint32_t address) { + struct GBAMemory* memory = &gba->memory; memory->dma[dma].dest = address & 0xFFFFFFFE; } -void GBAMemoryWriteDMACNT_LO(struct GBAMemory* memory, int dma, uint16_t count) { +void GBAMemoryWriteDMACNT_LO(struct GBA* gba, int dma, uint16_t count) { + struct GBAMemory* memory = &gba->memory; memory->dma[dma].count = count ? count : (dma == 3 ? 0x10000 : 0x4000); } -uint16_t GBAMemoryWriteDMACNT_HI(struct GBAMemory* memory, int dma, uint16_t control) { +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; if (currentDma->drq) { - GBALog(memory->p, GBA_LOG_STUB, "DRQ not implemented"); + GBALog(gba, GBA_LOG_STUB, "DRQ not implemented"); } if (!wasEnabled && currentDma->enable) { currentDma->nextSource = currentDma->source; currentDma->nextDest = currentDma->dest; currentDma->nextCount = currentDma->count; - GBAMemoryScheduleDMA(memory, dma, currentDma); + GBAMemoryScheduleDMA(gba, dma, currentDma); } // If the DMA has already occurred, this value might have changed since the function started return currentDma->packed; }; -void GBAMemoryScheduleDMA(struct GBAMemory* memory, int number, struct GBADMA* info) { +void GBAMemoryScheduleDMA(struct GBA* gba, int number, struct GBADMA* info) { + struct ARMCore* cpu = gba->cpu; switch (info->timing) { case DMA_TIMING_NOW: - info->nextEvent = memory->p->cpu.cycles; - GBAMemoryUpdateDMAs(memory, 0); + info->nextEvent = cpu->cycles; + GBAMemoryUpdateDMAs(gba, 0); break; case DMA_TIMING_HBLANK: // Handled implicitly

@@ -627,11 +686,11 @@ case DMA_TIMING_CUSTOM:

info->nextEvent = INT_MAX; switch (number) { case 0: - GBALog(memory->p, GBA_LOG_WARN, "Discarding invalid DMA0 scheduling"); + GBALog(gba, GBA_LOG_WARN, "Discarding invalid DMA0 scheduling"); break; case 1: case 2: - GBAAudioScheduleFifoDma(&memory->p->audio, number, info); + GBAAudioScheduleFifoDma(&gba->audio, number, info); break; case 3: // GBAVideoScheduleVCaptureDma(dma, info);

@@ -640,7 +699,8 @@ }

} } -void GBAMemoryRunHblankDMAs(struct GBAMemory* memory, int32_t cycles) { +void GBAMemoryRunHblankDMAs(struct GBA* gba, int32_t cycles) { + struct GBAMemory* memory = &gba->memory; struct GBADMA* dma; int i; for (i = 0; i < 4; ++i) {

@@ -649,10 +709,11 @@ if (dma->enable && dma->timing == DMA_TIMING_HBLANK) {

dma->nextEvent = cycles; } } - GBAMemoryUpdateDMAs(memory, 0); + GBAMemoryUpdateDMAs(gba, 0); } -void GBAMemoryRunVblankDMAs(struct GBAMemory* memory, int32_t cycles) { +void GBAMemoryRunVblankDMAs(struct GBA* gba, int32_t cycles) { + struct GBAMemory* memory = &gba->memory; struct GBADMA* dma; int i; for (i = 0; i < 4; ++i) {

@@ -661,10 +722,11 @@ if (dma->enable && dma->timing == DMA_TIMING_VBLANK) {

dma->nextEvent = cycles; } } - GBAMemoryUpdateDMAs(memory, 0); + GBAMemoryUpdateDMAs(gba, 0); } -int32_t GBAMemoryRunDMAs(struct GBAMemory* memory, int32_t cycles) { +int32_t GBAMemoryRunDMAs(struct GBA* gba, int32_t cycles) { + struct GBAMemory* memory = &gba->memory; if (memory->nextDMA == INT_MAX) { return INT_MAX; }

@@ -672,15 +734,17 @@ memory->nextDMA -= cycles;

memory->eventDiff += cycles; if (memory->nextDMA <= 0) { struct GBADMA* dma = &memory->dma[memory->activeDMA]; - GBAMemoryServiceDMA(memory, memory->activeDMA, dma); - GBAMemoryUpdateDMAs(memory, memory->eventDiff); + GBAMemoryServiceDMA(gba, memory->activeDMA, dma); + GBAMemoryUpdateDMAs(gba, memory->eventDiff); memory->eventDiff = 0; } return memory->nextDMA; } -void GBAMemoryUpdateDMAs(struct GBAMemory* memory, int32_t cycles) { +void GBAMemoryUpdateDMAs(struct GBA* gba, int32_t cycles) { int i; + struct GBAMemory* memory = &gba->memory; + struct ARMCore* cpu = gba->cpu; memory->activeDMA = -1; memory->nextDMA = INT_MAX; for (i = 3; i >= 0; --i) {

@@ -693,12 +757,14 @@ memory->nextDMA = dma->nextEvent;

} } } - if (memory->nextDMA < memory->p->cpu.nextEvent) { - memory->p->cpu.nextEvent = memory->nextDMA; + if (memory->nextDMA < cpu->nextEvent) { + cpu->nextEvent = memory->nextDMA; } } -void GBAMemoryServiceDMA(struct GBAMemory* memory, int number, struct GBADMA* info) { +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;

@@ -713,11 +779,11 @@ if (source == info->source) {

// TODO: support 4 cycles for ROM access cycles += 2; if (width == 4) { - cycles += memory->waitstates32[sourceRegion] + memory->waitstates32[destRegion]; + cycles += memory->waitstatesNonseq32[sourceRegion] + memory->waitstatesNonseq32[destRegion]; source &= 0xFFFFFFFC; dest &= 0xFFFFFFFC; } else { - cycles += memory->waitstates16[sourceRegion] + memory->waitstates16[destRegion]; + cycles += memory->waitstatesNonseq16[sourceRegion] + memory->waitstatesNonseq16[destRegion]; } } else { if (width == 4) {

@@ -729,8 +795,8 @@ }

if (width == 4) { int32_t word; - word = memory->d.load32(&memory->d, source, 0); - memory->d.store32(&memory->d, dest, word, 0); + word = cpu->memory.load32(cpu, source, 0); + cpu->memory.store32(cpu, dest, word, 0); source += sourceOffset; dest += destOffset; --wordsRemaining;

@@ -738,7 +804,7 @@ } else {

uint16_t word; if (sourceRegion == REGION_CART2_EX && memory->savedata.type == SAVEDATA_EEPROM) { word = GBASavedataReadEEPROM(&memory->savedata); - memory->d.store16(&memory->d, dest, word, 0); + cpu->memory.store16(cpu, dest, word, 0); source += sourceOffset; dest += destOffset; --wordsRemaining;

@@ -746,14 +812,14 @@ } else if (destRegion == REGION_CART2_EX) {

if (memory->savedata.type == SAVEDATA_NONE) { GBASavedataInitEEPROM(&memory->savedata); } - word = memory->d.load16(&memory->d, source, 0); + word = cpu->memory.load16(cpu, source, 0); GBASavedataWriteEEPROM(&memory->savedata, word, wordsRemaining); source += sourceOffset; dest += destOffset; --wordsRemaining; } else { - word = memory->d.load16(&memory->d, source, 0); - memory->d.store16(&memory->d, dest, word, 0); + word = cpu->memory.load16(cpu, source, 0); + cpu->memory.store16(cpu, dest, word, 0); source += sourceOffset; dest += destOffset; --wordsRemaining;

@@ -772,10 +838,10 @@ info->nextCount = info->count;

if (info->dstControl == DMA_INCREMENT_RELOAD) { info->nextDest = info->dest; } - GBAMemoryScheduleDMA(memory, number, info); + GBAMemoryScheduleDMA(gba, number, info); } if (info->doIrq) { - GBARaiseIRQ(memory->p, IRQ_DMA0 + number); + GBARaiseIRQ(gba, IRQ_DMA0 + number); } } else { info->nextDest = dest;

@@ -789,7 +855,7 @@ if (memory->dma[i].nextEvent != INT_MAX) {

memory->dma[i].nextEvent += cycles; } } - memory->p->cpu.cycles += cycles; + cpu->cycles += cycles; } void GBAMemorySerialize(struct GBAMemory* memory, struct GBASerializedState* state) {
M src/gba/gba-memory.hsrc/gba/gba-memory.h

@@ -1,12 +1,12 @@

#ifndef GBA_MEMORY_H #define GBA_MEMORY_H +#include "common.h" + #include "arm.h" #include "gba-gpio.h" #include "gba-savedata.h" - -#include <string.h> enum GBAMemoryRegion { REGION_BIOS = 0x0,

@@ -106,9 +106,6 @@ int32_t nextEvent;

}; struct GBAMemory { - struct ARMMemory d; - struct GBA* p; - uint32_t* bios; uint32_t* wram; uint32_t* iwram;

@@ -121,12 +118,14 @@ size_t romSize;

uint16_t romID; int fullBios; - char waitstates32[256]; - char waitstates16[256]; char waitstatesSeq32[256]; char waitstatesSeq16[256]; - char waitstatesPrefetch32[256]; - char waitstatesPrefetch16[256]; + char waitstatesNonseq32[256]; + char waitstatesNonseq16[256]; + char waitstatesPrefetchSeq32[16]; + char waitstatesPrefetchSeq16[16]; + char waitstatesPrefetchNonseq32[16]; + char waitstatesPrefetchNonseq16[16]; int activeRegion; uint32_t biosPrefetch;

@@ -136,28 +135,33 @@ int32_t nextDMA;

int32_t eventDiff; }; -int32_t GBALoad32(struct ARMMemory* memory, uint32_t address, int* cycleCounter); -int16_t GBALoad16(struct ARMMemory* memory, uint32_t address, int* cycleCounter); -uint16_t GBALoadU16(struct ARMMemory* memory, uint32_t address, int* cycleCounter); -int8_t GBALoad8(struct ARMMemory* memory, uint32_t address, int* cycleCounter); -uint8_t GBALoadU8(struct ARMMemory* memory, uint32_t address, int* cycleCounter); +void GBAMemoryInit(struct GBA* gba); +void GBAMemoryDeinit(struct GBA* gba); -void GBAStore32(struct ARMMemory* memory, uint32_t address, int32_t value, int* cycleCounter); -void GBAStore16(struct ARMMemory* memory, uint32_t address, int16_t value, int* cycleCounter); -void GBAStore8(struct ARMMemory* memory, uint32_t address, int8_t value, int* cycleCounter); +void GBAMemoryReset(struct GBA* gba); -void GBAAdjustWaitstates(struct GBAMemory* memory, uint16_t parameters); +int32_t GBALoad32(struct ARMCore* cpu, uint32_t address, int* cycleCounter); +int16_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter); +uint16_t GBALoadU16(struct ARMCore* cpu, uint32_t address, int* cycleCounter); +int8_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter); +uint8_t GBALoadU8(struct ARMCore* cpu, uint32_t address, int* cycleCounter); -void GBAMemoryWriteDMASAD(struct GBAMemory* memory, int dma, uint32_t address); -void GBAMemoryWriteDMADAD(struct GBAMemory* memory, int dma, uint32_t address); -void GBAMemoryWriteDMACNT_LO(struct GBAMemory* memory, int dma, uint16_t count); -uint16_t GBAMemoryWriteDMACNT_HI(struct GBAMemory* memory, int dma, uint16_t control); +void GBAStore32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycleCounter); +void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycleCounter); +void GBAStore8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCounter); + +void GBAAdjustWaitstates(struct GBA* gba, uint16_t parameters); + +void GBAMemoryWriteDMASAD(struct GBA* gba, int dma, uint32_t address); +void GBAMemoryWriteDMADAD(struct GBA* gba, int dma, uint32_t address); +void GBAMemoryWriteDMACNT_LO(struct GBA* gba, int dma, uint16_t count); +uint16_t GBAMemoryWriteDMACNT_HI(struct GBA* gba, int dma, uint16_t control); -void GBAMemoryScheduleDMA(struct GBAMemory* memory, int number, struct GBADMA* info); -void GBAMemoryRunHblankDMAs(struct GBAMemory* memory, int32_t cycles); -void GBAMemoryRunVblankDMAs(struct GBAMemory* memory, int32_t cycles); -void GBAMemoryUpdateDMAs(struct GBAMemory* memory, int32_t cycles); -int32_t GBAMemoryRunDMAs(struct GBAMemory* memory, int32_t cycles); +void GBAMemoryScheduleDMA(struct GBA* gba, int number, struct GBADMA* info); +void GBAMemoryRunHblankDMAs(struct GBA* gba, int32_t cycles); +void GBAMemoryRunVblankDMAs(struct GBA* gba, int32_t cycles); +void GBAMemoryUpdateDMAs(struct GBA* gba, int32_t cycles); +int32_t GBAMemoryRunDMAs(struct GBA* gba, int32_t cycles); struct GBASerializedState; void GBAMemorySerialize(struct GBAMemory* memory, struct GBASerializedState* state);
A src/gba/gba-rr.c

@@ -0,0 +1,108 @@

+#include "gba-rr.h" + +#include "gba.h" +#include "util/vfs.h" + +#define FILE_INPUTS "input.log" + +void GBARRContextCreate(struct GBA* gba) { + if (gba->rr) { + return; + } + + gba->rr = calloc(1, sizeof(*gba->rr)); +} + +void GBARRContextDestroy(struct GBA* gba) { + if (!gba->rr) { + return; + } + + free(gba->rr); + gba->rr = 0; +} + +bool GBARRSetStream(struct GBARRContext* rr, struct VDir* stream) { + if (rr->inputsStream && !rr->inputsStream->close(rr->inputsStream)) { + return false; + } + rr->streamDir = stream; + rr->inputsStream = stream->openFile(stream, FILE_INPUTS, O_CREAT | O_RDWR); + return !!rr->inputsStream; +} + +bool GBARRStartPlaying(struct GBARRContext* rr, bool autorecord) { + if (GBARRIsRecording(rr) || GBARRIsPlaying(rr)) { + return false; + } + + rr->autorecord = autorecord; + if (rr->inputsStream->seek(rr->inputsStream, 0, SEEK_SET) < 0) { + return false; + } + if (rr->inputsStream->read(rr->inputsStream, &rr->nextInput, sizeof(rr->nextInput)) != sizeof(rr->nextInput)) { + return false; + } + rr->isPlaying = true; + return true; +} + +void GBARRStopPlaying(struct GBARRContext* rr) { + rr->isPlaying = false; +} + +bool GBARRStartRecording(struct GBARRContext* rr) { + if (GBARRIsRecording(rr) || GBARRIsPlaying(rr)) { + return false; + } + + rr->isRecording = true; + return true; +} + +void GBARRStopRecording(struct GBARRContext* rr) { + rr->isRecording = false; +} + +bool GBARRIsPlaying(struct GBARRContext* rr) { + return rr && rr->isPlaying; +} + +bool GBARRIsRecording(struct GBARRContext* rr) { + return rr && rr->isRecording; +} + +void GBARRNextFrame(struct GBARRContext* rr) { + if (!GBARRIsRecording(rr)) { + return; + } + + ++rr->frames; + if (!rr->inputThisFrame) { + ++rr->lagFrames; + } + + rr->inputThisFrame = false; +} + +void GBARRLogInput(struct GBARRContext* rr, uint16_t keys) { + if (!GBARRIsRecording(rr)) { + return; + } + + rr->inputsStream->write(rr->inputsStream, &keys, sizeof(keys)); + rr->inputThisFrame = true; +} + +uint16_t GBARRQueryInput(struct GBARRContext* rr) { + if (!GBARRIsPlaying(rr)) { + return 0; + } + + uint16_t keys = rr->nextInput; + rr->isPlaying = rr->inputsStream->read(rr->inputsStream, &rr->nextInput, sizeof(rr->nextInput)) == sizeof(rr->nextInput); + if (!rr->isPlaying && rr->autorecord) { + rr->isRecording = true; + } + return keys; +}
A src/gba/gba-rr.h

@@ -0,0 +1,46 @@

+#ifndef GBA_RR_H +#define GBA_RR_H + +#include "common.h" + +struct GBA; +struct VDir; +struct VFile; + +struct GBARRContext { + // Playback state + bool isPlaying; + bool autorecord; + uint16_t nextInput; + + // Recording state + bool isRecording; + bool inputThisFrame; + + // Metadata + uint32_t frames; + uint32_t lagFrames; + + // Streaming state + struct VDir* streamDir; + struct VFile* inputsStream; +}; + +void GBARRContextCreate(struct GBA*); +void GBARRContextDestroy(struct GBA*); + +bool GBARRSetStream(struct GBARRContext*, struct VDir*); + +bool GBARRStartPlaying(struct GBARRContext*, bool autorecord); +void GBARRStopPlaying(struct GBARRContext*); +bool GBARRStartRecording(struct GBARRContext*); +void GBARRStopRecording(struct GBARRContext*); + +bool GBARRIsPlaying(struct GBARRContext*); +bool GBARRIsRecording(struct GBARRContext*); + +void GBARRNextFrame(struct GBARRContext*); +void GBARRLogInput(struct GBARRContext*, uint16_t input); +uint16_t GBARRQueryInput(struct GBARRContext*); + +#endif
M src/gba/gba-savedata.csrc/gba/gba-savedata.c

@@ -1,53 +1,61 @@

#include "gba-savedata.h" #include "gba.h" -#include "memory.h" + +#include "util/memory.h" +#include "util/vfs.h" #include <errno.h> #include <fcntl.h> -#include <string.h> -#include <unistd.h> static void _flashSwitchBank(struct GBASavedata* savedata, int bank); static void _flashErase(struct GBASavedata* savedata); static void _flashEraseSector(struct GBASavedata* savedata, uint16_t sectorStart); -void GBASavedataInit(struct GBASavedata* savedata, const char* filename) { +void GBASavedataInit(struct GBASavedata* savedata, struct VFile* vf) { savedata->type = SAVEDATA_NONE; savedata->data = 0; savedata->command = EEPROM_COMMAND_NULL; savedata->flashState = FLASH_STATE_RAW; - savedata->fd = -1; - savedata->filename = filename; -} - -void GBASavedataForceType(struct GBASavedata* savedata, enum SavedataType type) { - if (savedata->type != SAVEDATA_NONE) { - GBALog(0, GBA_LOG_WARN, "Can't re-initialize savedata"); - return; - } - savedata->type = type; + savedata->vf = vf; } void GBASavedataDeinit(struct GBASavedata* savedata) { - switch (savedata->type) { - case SAVEDATA_SRAM: - mappedMemoryFree(savedata->data, SIZE_CART_SRAM); - break; - case SAVEDATA_FLASH512: - mappedMemoryFree(savedata->data, SIZE_CART_FLASH512); - break; - case SAVEDATA_FLASH1M: - mappedMemoryFree(savedata->data, SIZE_CART_FLASH1M); - break; - case SAVEDATA_EEPROM: - mappedMemoryFree(savedata->data, SIZE_CART_EEPROM); - break; - default: - break; - } - if (savedata->fd >= 0) { - close(savedata->fd); + if (savedata->vf) { + switch (savedata->type) { + case SAVEDATA_SRAM: + savedata->vf->unmap(savedata->vf, savedata->data, SIZE_CART_SRAM); + break; + case SAVEDATA_FLASH512: + savedata->vf->unmap(savedata->vf, savedata->data, SIZE_CART_FLASH512); + break; + case SAVEDATA_FLASH1M: + savedata->vf->unmap(savedata->vf, savedata->data, SIZE_CART_FLASH1M); + break; + case SAVEDATA_EEPROM: + savedata->vf->unmap(savedata->vf, savedata->data, SIZE_CART_EEPROM); + break; + case SAVEDATA_NONE: + break; + } + savedata->vf = 0; + } else { + switch (savedata->type) { + case SAVEDATA_SRAM: + mappedMemoryFree(savedata->data, SIZE_CART_SRAM); + break; + case SAVEDATA_FLASH512: + mappedMemoryFree(savedata->data, SIZE_CART_FLASH512); + break; + case SAVEDATA_FLASH1M: + mappedMemoryFree(savedata->data, SIZE_CART_FLASH1M); + break; + case SAVEDATA_EEPROM: + mappedMemoryFree(savedata->data, SIZE_CART_EEPROM); + break; + case SAVEDATA_NONE: + break; + } } savedata->type = SAVEDATA_NONE; }

@@ -60,18 +68,16 @@ if (savedata->type != SAVEDATA_FLASH512 && savedata->type != SAVEDATA_FLASH1M) {

GBALog(0, GBA_LOG_WARN, "Can't re-initialize savedata"); return; } - savedata->fd = open(savedata->filename, O_RDWR | O_CREAT, 0666); off_t end; - if (savedata->fd < 0) { - GBALog(0, GBA_LOG_ERROR, "Cannot open savedata file %s (errno: %d)", savedata->filename, errno); + if (!savedata->vf) { end = 0; savedata->data = anonymousMemoryMap(SIZE_CART_FLASH1M); } else { - end = lseek(savedata->fd, 0, SEEK_END); + end = savedata->vf->seek(savedata->vf, 0, SEEK_END); if (end < SIZE_CART_FLASH512) { - ftruncate(savedata->fd, SIZE_CART_FLASH1M); + savedata->vf->truncate(savedata->vf, SIZE_CART_FLASH1M); } - savedata->data = fileMemoryMap(savedata->fd, SIZE_CART_FLASH1M, MEMORY_WRITE); + savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_FLASH1M, MAP_WRITE); } savedata->currentBank = savedata->data;

@@ -87,18 +93,16 @@ } else {

GBALog(0, GBA_LOG_WARN, "Can't re-initialize savedata"); return; } - savedata->fd = open(savedata->filename, O_RDWR | O_CREAT, 0666); off_t end; - if (savedata->fd < 0) { - GBALog(0, GBA_LOG_ERROR, "Cannot open savedata file %s (errno: %d)", savedata->filename, errno); + if (!savedata->vf) { end = 0; savedata->data = anonymousMemoryMap(SIZE_CART_EEPROM); } else { - end = lseek(savedata->fd, 0, SEEK_END); + end = savedata->vf->seek(savedata->vf, 0, SEEK_END); if (end < SIZE_CART_EEPROM) { - ftruncate(savedata->fd, SIZE_CART_EEPROM); + savedata->vf->truncate(savedata->vf, SIZE_CART_EEPROM); } - savedata->data = fileMemoryMap(savedata->fd, SIZE_CART_EEPROM, MEMORY_WRITE); + savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_EEPROM, MAP_WRITE); } if (end < SIZE_CART_EEPROM) { memset(&savedata->data[end], 0xFF, SIZE_CART_EEPROM - end);

@@ -112,18 +116,16 @@ } else {

GBALog(0, GBA_LOG_WARN, "Can't re-initialize savedata"); return; } - savedata->fd = open(savedata->filename, O_RDWR | O_CREAT, 0666); off_t end; - if (savedata->fd < 0) { - GBALog(0, GBA_LOG_ERROR, "Cannot open savedata file %s (errno: %d)", savedata->filename, errno); + if (!savedata->vf) { end = 0; savedata->data = anonymousMemoryMap(SIZE_CART_SRAM); } else { - end = lseek(savedata->fd, 0, SEEK_END); + end = savedata->vf->seek(savedata->vf, 0, SEEK_END); if (end < SIZE_CART_SRAM) { - ftruncate(savedata->fd, SIZE_CART_SRAM); + savedata->vf->truncate(savedata->vf, SIZE_CART_SRAM); } - savedata->data = fileMemoryMap(savedata->fd, SIZE_CART_SRAM, MEMORY_WRITE); + savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_SRAM, MAP_WRITE); } if (end < SIZE_CART_SRAM) {

@@ -300,7 +302,7 @@ void _flashSwitchBank(struct GBASavedata* savedata, int bank) {

savedata->currentBank = &savedata->data[bank << 16]; if (bank > 0) { savedata->type = SAVEDATA_FLASH1M; - ftruncate(savedata->fd, SIZE_CART_FLASH1M); + savedata->vf->truncate(savedata->vf, SIZE_CART_FLASH1M); } }
M src/gba/gba-savedata.hsrc/gba/gba-savedata.h

@@ -1,7 +1,9 @@

#ifndef GBA_SAVEDATA_H #define GBA_SAVEDATA_H -#include <stdint.h> +#include "common.h" + +struct VFile; enum SavedataType { SAVEDATA_NONE = 0,

@@ -53,9 +55,8 @@

struct GBASavedata { enum SavedataType type; uint8_t* data; - const char* filename; enum SavedataCommand command; - int fd; + struct VFile* vf; int readBitsRemaining; int readAddress;

@@ -68,10 +69,8 @@

enum FlashStateMachine flashState; }; -void GBASavedataInit(struct GBASavedata* savedata, const char* filename); +void GBASavedataInit(struct GBASavedata* savedata, struct VFile* vf); void GBASavedataDeinit(struct GBASavedata* savedata); - -void GBASavedataForceType(struct GBASavedata* savedata, enum SavedataType type); void GBASavedataInitFlash(struct GBASavedata* savedata); void GBASavedataInitEEPROM(struct GBASavedata* savedata);
M src/gba/gba-sensors.hsrc/gba/gba-sensors.h

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

#ifndef GBA_SENSORS_H #define GBA_SENSORS_H -#include <stdint.h> +#include "common.h" struct GBARotationSource { void (*sample)(struct GBARotationSource*);
M src/gba/gba-serialize.csrc/gba/gba-serialize.c

@@ -3,29 +3,29 @@

#include "gba-audio.h" #include "gba-io.h" #include "gba-thread.h" -#include "memory.h" + +#include "util/memory.h" +#include "util/vfs.h" #include <fcntl.h> -#include <limits.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> const uint32_t GBA_SAVESTATE_MAGIC = 0x01000000; void GBASerialize(struct GBA* gba, struct GBASerializedState* state) { state->versionMagic = GBA_SAVESTATE_MAGIC; state->biosChecksum = gba->biosChecksum; + state->romCrc32 = gba->romCrc32; + state->id = ((struct GBACartridge*) gba->memory.rom)->id; memcpy(state->title, ((struct GBACartridge*) gba->memory.rom)->title, sizeof(state->title)); - memcpy(state->cpu.gprs, gba->cpu.gprs, sizeof(state->cpu.gprs)); - state->cpu.cpsr = gba->cpu.cpsr; - state->cpu.spsr = gba->cpu.spsr; - state->cpu.cycles = gba->cpu.cycles; - state->cpu.nextEvent = gba->cpu.nextEvent; - memcpy(state->cpu.bankedRegisters, gba->cpu.bankedRegisters, 6 * 7 * sizeof(int32_t)); - memcpy(state->cpu.bankedSPSRs, gba->cpu.bankedSPSRs, 6 * sizeof(int32_t)); + memcpy(state->cpu.gprs, gba->cpu->gprs, sizeof(state->cpu.gprs)); + state->cpu.cpsr = gba->cpu->cpsr; + state->cpu.spsr = gba->cpu->spsr; + state->cpu.cycles = gba->cpu->cycles; + state->cpu.nextEvent = gba->cpu->nextEvent; + memcpy(state->cpu.bankedRegisters, gba->cpu->bankedRegisters, 6 * 7 * sizeof(int32_t)); + memcpy(state->cpu.bankedSPSRs, gba->cpu->bankedSPSRs, 6 * sizeof(int32_t)); GBAMemorySerialize(&gba->memory, state); GBAIOSerialize(gba, state);

@@ -40,22 +40,27 @@ return;

} if (state->biosChecksum != gba->biosChecksum) { GBALog(gba, GBA_LOG_WARN, "Savestate created using a different version of the BIOS"); - return; + if (state->cpu.gprs[ARM_PC] < SIZE_BIOS && state->cpu.gprs[ARM_PC] >= 0x20) { + return; + } } if (state->id != ((struct GBACartridge*) gba->memory.rom)->id || memcmp(state->title, ((struct GBACartridge*) gba->memory.rom)->title, sizeof(state->title))) { GBALog(gba, GBA_LOG_WARN, "Savestate is for a different game"); return; } - memcpy(gba->cpu.gprs, state->cpu.gprs, sizeof(gba->cpu.gprs)); - gba->cpu.cpsr = state->cpu.cpsr; - gba->cpu.spsr = state->cpu.spsr; - 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.memory, gba->cpu.gprs[ARM_PC]); + if (state->romCrc32 != gba->romCrc32) { + GBALog(gba, GBA_LOG_WARN, "Savestate is for a different version of the game"); + } + memcpy(gba->cpu->gprs, state->cpu.gprs, sizeof(gba->cpu->gprs)); + gba->cpu->cpsr = state->cpu.cpsr; + gba->cpu->spsr = state->cpu.spsr; + 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]); GBAMemoryDeserialize(&gba->memory, state); GBAIODeserialize(gba, state);

@@ -63,43 +68,47 @@ GBAVideoDeserialize(&gba->video, state);

GBAAudioDeserialize(&gba->audio, state); } -static int _getStateFd(struct GBA* gba, int slot) { +static struct VFile* _getStateVf(struct GBA* gba, int slot) { char path[PATH_MAX]; path[PATH_MAX - 1] = '\0'; snprintf(path, PATH_MAX - 1, "%s.ss%d", gba->activeFile, slot); - int fd = open(path, O_CREAT | O_RDWR, 0777); - if (fd >= 0) { - ftruncate(fd, sizeof(struct GBASerializedState)); + struct VFile* vf = VFileOpen(path, O_CREAT | O_RDWR); + if (vf) { + vf->truncate(vf, sizeof(struct GBASerializedState)); } - return fd; + return vf; } -int GBASaveState(struct GBA* gba, int slot) { - int fd = _getStateFd(gba, slot); - if (fd < 0) { - return 0; +bool GBASaveState(struct GBA* gba, int slot) { + struct VFile* vf = _getStateVf(gba, slot); + if (!vf) { + return false; } - struct GBASerializedState* state = GBAMapState(fd); + struct GBASerializedState* state = GBAMapState(vf); GBASerialize(gba, state); - GBADeallocateState(state); - close(fd); - return 1; + GBAUnmapState(vf, state); + vf->close(vf); + return true; } -int GBALoadState(struct GBA* gba, int slot) { - int fd = _getStateFd(gba, slot); - if (fd < 0) { - return 0; +bool GBALoadState(struct GBA* gba, int slot) { + struct VFile* vf = _getStateVf(gba, slot); + if (!vf) { + return false; } - struct GBASerializedState* state = GBAMapState(fd); + struct GBASerializedState* state = GBAMapState(vf); GBADeserialize(gba, state); - GBADeallocateState(state); - close(fd); - return 1; + GBAUnmapState(vf, state); + vf->close(vf); + return true; } -struct GBASerializedState* GBAMapState(int fd) { - return fileMemoryMap(fd, sizeof(struct GBASerializedState), MEMORY_WRITE); +struct GBASerializedState* GBAMapState(struct VFile* vf) { + return vf->map(vf, sizeof(struct GBASerializedState), MAP_WRITE); +} + +void GBAUnmapState(struct VFile* vf, struct GBASerializedState* state) { + vf->unmap(vf, state, sizeof(struct GBASerializedState)); } struct GBASerializedState* GBAAllocateState(void) {
M src/gba/gba-serialize.hsrc/gba/gba-serialize.h

@@ -1,6 +1,8 @@

#ifndef GBA_SERIALIZE_H #define GBA_SERIALIZE_H +#include "common.h" + #include "gba.h" const uint32_t GBA_SAVESTATE_MAGIC;

@@ -8,7 +10,8 @@

/* Savestate format: * 0x00000 - 0x00003: Version Magic (0x01000000) * 0x00004 - 0x00007: BIOS checksum (e.g. 0xBAAE187F for official BIOS) - * 0x00008 - 0x0000F: Reserved (leave zero) + * 0x00008 - 0x0000B: ROM CRC32 + * 0x0000C - 0x0000F: Reserved (leave zero) * 0x00010 - 0x0001B: Game title (e.g. METROID4USA) * 0x0001C - 0x0001F: Game code (e.g. AMTE) * 0x00020 - 0x0012F: CPU state:

@@ -76,28 +79,28 @@ * | 0x00202 - 0x00203: Old reload value

* | 0x00204 - 0x00207: Last event * | 0x00208 - 0x0020B: Next event * | 0x0020C - 0x0020F: Overflow interval - * | 0x00210 - 0x00213: Miscellaenous flags + * | 0x00210 - 0x00213: Miscellaneous flags * 0x00214 - 0x00227: Timer 1 * | 0x00214 - 0x00215: Reload value * | 0x00216 - 0x00217: Old reload value * | 0x00218 - 0x0021B: Last event * | 0x0021C - 0x0021F: Next event * | 0x00220 - 0x00223: Overflow interval - * | 0x00224 - 0x00227: Miscellaenous flags + * | 0x00224 - 0x00227: Miscellaneous flags * 0x00228 - 0x0023B: Timer 2 * | 0x00228 - 0x00229: Reload value * | 0x0022A - 0x0022B: Old reload value * | 0x0022C - 0x0022F: Last event * | 0x00230 - 0x00233: Next event * | 0x00234 - 0x00237: Overflow interval - * | 0x00238 - 0x0023B: Miscellaenous flags + * | 0x00238 - 0x0023B: Miscellaneous flags * 0x0023C - 0x00250: Timer 3 * | 0x0023C - 0x0023D: Reload value * | 0x0023E - 0x0023F: Old reload value * | 0x00240 - 0x00243: Last event * | 0x00244 - 0x00247: Next event * | 0x00248 - 0x0024B: Overflow interval - * | 0x0024C - 0x0024F: Miscellaenous flags + * | 0x0024C - 0x0024F: Miscellaneous flags * 0x00250 - 0x0025F: DMA 0 * | 0x00250 - 0x00253: DMA next source * | 0x00254 - 0x00257: DMA next destination

@@ -118,7 +121,25 @@ * | 0x00280 - 0x00283: DMA next source

* | 0x00284 - 0x00287: DMA next destination * | 0x00288 - 0x0028B: DMA next count * | 0x0028C - 0x0028F: DMA next event - * 0x00290 - 0x003FF: Reserved (leave zero) + * 0x00290 - 0x002BF: GPIO state + * | 0x00290 - 0x00291: Pin state + * | 0x00292 - 0x00293: Direction state + * | 0x00294 - 0x002B6: RTC state (see gba-gpio.h for format) + * | 0x002B7 - 0x002B7: GPIO devices + * | bit 0: Has RTC values + * | bit 1: Has rumble value (reserved) + * | bit 2: Has light sensor value (reserved) + * | bit 3: Has gyroscope value + * | bit 4: Has tilt values (reserved) + * | bits 5 - 7: Reserved + * | 0x002B8 - 0x002B9: Gyroscope sample + * | 0x002BA - 0x002BB: Tilt x sample (reserved) + * | 0x002BC - 0x002BD: Tilt y sample (reserved) + * | 0x002BE - 0x002BF: Flags + * | bit 0: Is read enabled + * | bit 1: Gyroscope sample is edge + * | bits 2 - 15: Reserved + * 0x002C0 - 0x003FF: Reserved (leave zero) * 0x00400 - 0x007FF: I/O memory * 0x00800 - 0x00BFF: Palette * 0x00C00 - 0x00FFF: OAM

@@ -131,7 +152,8 @@

struct GBASerializedState { uint32_t versionMagic; uint32_t biosChecksum; - uint32_t reservedHeader[2]; + uint32_t romCrc32; + uint32_t reservedHeader; char title[12]; uint32_t id;

@@ -173,8 +195,8 @@ int32_t envelopeNextStep;

int32_t endTime; int32_t nextEvent; } ch4; - uint32_t fifoA[8]; - uint32_t fifoB[8]; + uint8_t fifoA[32]; + uint8_t fifoB[32]; int32_t nextEvent; int32_t eventDiff; int32_t nextSample;

@@ -213,7 +235,20 @@ int32_t nextCount;

int32_t nextEvent; } dma[4]; - uint32_t reservedGpio[92]; + struct { + uint16_t pinState; + uint16_t pinDirection; + struct GBARTC rtc; + uint8_t devices; + uint16_t gyroSample; + uint16_t tiltSampleX; + uint16_t tiltSampleY; + enum GPIODirection readWrite : 1; + unsigned gyroEdge : 1; + unsigned reserved : 14; + } gpio; + + uint32_t reserved[80]; uint16_t io[SIZE_IO >> 1]; uint16_t pram[SIZE_PALETTE_RAM >> 1];

@@ -222,14 +257,18 @@ uint16_t vram[SIZE_VRAM >> 1];

uint8_t iwram[SIZE_WORKING_IRAM]; uint8_t wram[SIZE_WORKING_RAM]; }; + +struct VFile; void GBASerialize(struct GBA* gba, struct GBASerializedState* state); void GBADeserialize(struct GBA* gba, struct GBASerializedState* state); -int GBASaveState(struct GBA* gba, int slot); -int GBALoadState(struct GBA* gba, int slot); +bool GBASaveState(struct GBA* gba, int slot); +bool GBALoadState(struct GBA* gba, int slot); -struct GBASerializedState* GBAMapState(int fd); +struct GBASerializedState* GBAMapState(struct VFile* vf); +void GBAUnmapState(struct VFile* vf, struct GBASerializedState* state); + struct GBASerializedState* GBAAllocateState(void); void GBADeallocateState(struct GBASerializedState* state);
M src/gba/gba-sio.csrc/gba/gba-sio.c

@@ -2,8 +2,6 @@ #include "gba-sio.h"

#include "gba-io.h" -#include <limits.h> - static struct GBASIODriver* _lookupDriver(struct GBASIO* sio, enum GBASIOMode mode) { switch (mode) { case SIO_NORMAL_8:

@@ -19,7 +17,7 @@ }

} static void _switchMode(struct GBASIO* sio) { - int mode = ((sio->rcnt >> 14) & 0xC) | ((sio->siocnt >> 12) & 0x3); + unsigned mode = ((sio->rcnt >> 14) & 0xC) | ((sio->siocnt >> 12) & 0x3); enum GBASIOMode oldMode = sio->mode; if (mode < 8) { sio->mode = (enum GBASIOMode) (mode & 0x3);

@@ -40,6 +38,11 @@

void GBASIOInit(struct GBASIO* sio) { sio->rcnt = RCNT_INITIAL; sio->siocnt = 0; + sio->mode = -1; + sio->activeDriver = 0; + sio->drivers.normal = 0; + sio->drivers.multiplayer = 0; + sio->drivers.joybus = 0; _switchMode(sio); }

@@ -53,15 +56,9 @@ }

} void GBASIOSetDriverSet(struct GBASIO* sio, struct GBASIODriverSet* drivers) { - if (drivers->normal) { - GBASIOSetDriver(sio, drivers->normal, SIO_NORMAL_8); - } - if (drivers->multiplayer) { - GBASIOSetDriver(sio, drivers->multiplayer, SIO_MULTI); - } - if (drivers->joybus) { - GBASIOSetDriver(sio, drivers->multiplayer, SIO_JOYBUS); - } + GBASIOSetDriver(sio, drivers->normal, SIO_NORMAL_8); + GBASIOSetDriver(sio, drivers->multiplayer, SIO_MULTI); + GBASIOSetDriver(sio, drivers->joybus, SIO_JOYBUS); } void GBASIOSetDriver(struct GBASIO* sio, struct GBASIODriver* driver, enum GBASIOMode mode) {
M src/gba/gba-sio.hsrc/gba/gba-sio.h

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

#ifndef GBA_SIO_H #define GBA_SIO_H -#include <stdint.h> +#include "common.h" enum GBASIOMode { SIO_NORMAL_8 = 0,
M src/gba/gba-thread.csrc/gba/gba-thread.c

@@ -1,11 +1,14 @@

#include "gba-thread.h" #include "arm.h" -#include "debugger.h" #include "gba.h" #include "gba-serialize.h" -#include <stdlib.h> +#include "debugger/debugger.h" + +#include "util/patch.h" +#include "util/vfs.h" + #include <signal.h> #ifdef USE_PTHREADS

@@ -20,9 +23,9 @@ static DWORD _contextKey;

static INIT_ONCE _contextOnce = INIT_ONCE_STATIC_INIT; static BOOL CALLBACK _createTLS(PINIT_ONCE once, PVOID param, PVOID* context) { - (void) (once); - (void) (param); - (void) (context); + UNUSED(once); + UNUSED(param); + UNUSED(context); _contextKey = TlsAlloc(); return TRUE; }

@@ -51,8 +54,16 @@ InitOnceExecuteOnce(&_contextOnce, _createTLS, NULL, 0);

#endif struct GBA gba; + struct ARMCore cpu; + struct Patch patch; struct GBAThread* threadContext = context; - char* savedata = 0; + struct ARMComponent* components[1] = {}; + int numComponents = 0; + + if (threadContext->debugger) { + components[numComponents] = &threadContext->debugger->d; + ++numComponents; + } #if !defined(_WIN32) && defined(USE_PTHREADS) sigset_t signals;

@@ -61,9 +72,13 @@ pthread_sigmask(SIG_SETMASK, &signals, 0);

#endif gba.logHandler = threadContext->logHandler; - GBAInit(&gba); + GBACreate(&gba); + ARMSetComponents(&cpu, &gba.d, numComponents, components); + ARMInit(&cpu); + ARMReset(&cpu); threadContext->gba = &gba; gba.sync = &threadContext->sync; + gba.logLevel = threadContext->logLevel; #ifdef USE_PTHREADS pthread_setspecific(_contextKey, threadContext); #else

@@ -73,30 +88,14 @@ if (threadContext->renderer) {

GBAVideoAssociateRenderer(&gba.video, threadContext->renderer); } - if (threadContext->fd >= 0) { - if (threadContext->fname) { - char* dotPoint = strrchr(threadContext->fname, '.'); - if (dotPoint > strrchr(threadContext->fname, '/') && dotPoint[1] && dotPoint[2] && dotPoint[3]) { - savedata = strdup(threadContext->fname); - dotPoint = strrchr(savedata, '.'); - dotPoint[1] = 's'; - dotPoint[2] = 'a'; - dotPoint[3] = 'v'; - dotPoint[4] = '\0'; - } else if (dotPoint) { - savedata = malloc((dotPoint - threadContext->fname + 5) * sizeof(char)); - strncpy(savedata, threadContext->fname, dotPoint - threadContext->fname + 1); - strcat(savedata, "sav"); - } else { - savedata = malloc(strlen(threadContext->fname + 5)); - strcpy(savedata, threadContext->fname); - strcat(savedata, "sav"); - } + if (threadContext->rom) { + GBALoadROM(&gba, threadContext->rom, threadContext->save, threadContext->fname); + if (threadContext->bios) { + GBALoadBIOS(&gba, threadContext->bios); } - gba.savefile = savedata; - GBALoadROM(&gba, threadContext->fd, threadContext->fname); - if (threadContext->biosFd >= 0) { - GBALoadBIOS(&gba, threadContext->biosFd); + + if (threadContext->patch && loadPatch(threadContext->patch, &patch)) { + GBAApplyPatch(&gba, &patch); } }

@@ -124,14 +123,31 @@ _changeState(threadContext, THREAD_EXITING, 0);

} } else { while (threadContext->state == THREAD_RUNNING) { - ARMRun(&gba.cpu); + ARMRun(&cpu); } } + + int resetScheduled = 0; MutexLock(&threadContext->stateMutex); - while (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_INTERRUPTED) { + if (threadContext->state == THREAD_PAUSING) { + threadContext->state = THREAD_PAUSED; + ConditionWake(&threadContext->stateCond); + } + if (threadContext->state == THREAD_INTERRUPTING) { + threadContext->state = THREAD_INTERRUPTED; + ConditionWake(&threadContext->stateCond); + } + if (threadContext->state == THREAD_RESETING) { + threadContext->state = THREAD_RUNNING; + resetScheduled = 1; + } + while (threadContext->state == THREAD_PAUSED) { ConditionWait(&threadContext->stateCond, &threadContext->stateMutex); } MutexUnlock(&threadContext->stateMutex); + if (resetScheduled) { + ARMReset(&cpu); + } } while (threadContext->state != THREAD_SHUTDOWN) {

@@ -143,16 +159,35 @@ threadContext->cleanCallback(threadContext);

} threadContext->gba = 0; - GBADeinit(&gba); + ARMDeinit(&cpu); + GBADestroy(&gba); ConditionWake(&threadContext->sync.videoFrameAvailableCond); ConditionWake(&threadContext->sync.audioRequiredCond); - free(savedata); return 0; } -int GBAThreadStart(struct GBAThread* threadContext) { +void GBAMapOptionsToContext(struct StartupOptions* opts, struct GBAThread* threadContext) { + if (opts->dirmode) { + threadContext->gameDir = VDirOpen(opts->fname); + threadContext->stateDir = threadContext->gameDir; + } else { + threadContext->rom = VFileOpen(opts->fname, O_RDONLY); +#if ENABLE_LIBZIP + threadContext->gameDir = VDirOpenZip(opts->fname, 0); +#endif + } + threadContext->fname = opts->fname; + threadContext->bios = VFileOpen(opts->bios, O_RDONLY); + threadContext->patch = VFileOpen(opts->patch, O_RDONLY); + threadContext->frameskip = opts->frameskip; + threadContext->logLevel = opts->logLevel; + threadContext->rewindBufferCapacity = opts->rewindBufferCapacity; + threadContext->rewindBufferInterval = opts->rewindBufferInterval; +} + +bool GBAThreadStart(struct GBAThread* threadContext) { // TODO: error check threadContext->activeKeys = 0; threadContext->state = THREAD_INITIALIZED;

@@ -167,6 +202,61 @@ } else {

threadContext->rewindBuffer = 0; } + if (threadContext->rom && !GBAIsROM(threadContext->rom)) { + threadContext->rom->close(threadContext->rom); + threadContext->rom = 0; + } + + if (threadContext->gameDir) { + threadContext->gameDir->rewind(threadContext->gameDir); + struct VDirEntry* dirent = threadContext->gameDir->listNext(threadContext->gameDir); + while (dirent) { + struct Patch patchTemp; + struct VFile* vf = threadContext->gameDir->openFile(threadContext->gameDir, dirent->name(dirent), O_RDONLY); + if (!vf) { + continue; + } + if (!threadContext->rom && GBAIsROM(vf)) { + threadContext->rom = vf; + } else if (!threadContext->patch && loadPatch(vf, &patchTemp)) { + threadContext->patch = vf; + } else { + vf->close(vf); + } + dirent = threadContext->gameDir->listNext(threadContext->gameDir); + } + + } + if (threadContext->stateDir) { + threadContext->save = threadContext->stateDir->openFile(threadContext->stateDir, "sram.sav", O_RDWR | O_CREAT); + } + + if (!threadContext->rom) { + return false; + } + + if (threadContext->fname && !threadContext->save) { + char* savedata = 0; + char* dotPoint = strrchr(threadContext->fname, '.'); + if (dotPoint > strrchr(threadContext->fname, '/') && dotPoint[1] && dotPoint[2] && dotPoint[3]) { + savedata = strdup(threadContext->fname); + dotPoint = strrchr(savedata, '.'); + dotPoint[1] = 's'; + dotPoint[2] = 'a'; + dotPoint[3] = 'v'; + dotPoint[4] = '\0'; + } else if (dotPoint) { + savedata = malloc((dotPoint - threadContext->fname + 5) * sizeof(char)); + strncpy(savedata, threadContext->fname, dotPoint - threadContext->fname + 1); + strcat(savedata, "sav"); + } else { + savedata = malloc(strlen(threadContext->fname + 5) * sizeof(char)); + sprintf(savedata, "%s.sav", threadContext->fname); + } + threadContext->save = VFileOpen(savedata, O_RDWR | O_CREAT); + free(savedata); + } + MutexInit(&threadContext->stateMutex); ConditionInit(&threadContext->stateCond);

@@ -191,11 +281,11 @@ ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);

} MutexUnlock(&threadContext->stateMutex); - return 0; + return true; } -int GBAThreadHasStarted(struct GBAThread* threadContext) { - int hasStarted; +bool GBAThreadHasStarted(struct GBAThread* threadContext) { + bool hasStarted; MutexLock(&threadContext->stateMutex); hasStarted = threadContext->state > THREAD_INITIALIZED; MutexUnlock(&threadContext->stateMutex);

@@ -213,6 +303,14 @@ MutexLock(&threadContext->sync.audioBufferMutex);

threadContext->sync.audioWait = 0; ConditionWake(&threadContext->sync.audioRequiredCond); MutexUnlock(&threadContext->sync.audioBufferMutex); +} + +void GBAThreadReset(struct GBAThread* threadContext) { + MutexLock(&threadContext->stateMutex); + _waitOnInterrupt(threadContext); + threadContext->state = THREAD_RESETING; + ConditionWake(&threadContext->stateCond); + MutexUnlock(&threadContext->stateMutex); } void GBAThreadJoin(struct GBAThread* threadContext) {

@@ -243,15 +341,52 @@ GBADeallocateState(threadContext->rewindBuffer[i]);

} } free(threadContext->rewindBuffer); + + if (threadContext->rom) { + threadContext->rom->close(threadContext->rom); + threadContext->rom = 0; + } + + if (threadContext->save) { + threadContext->save->close(threadContext->save); + threadContext->save = 0; + } + + if (threadContext->bios) { + threadContext->bios->close(threadContext->bios); + threadContext->bios = 0; + } + + if (threadContext->patch) { + threadContext->patch->close(threadContext->patch); + threadContext->patch = 0; + } + + if (threadContext->gameDir) { + if (threadContext->stateDir == threadContext->gameDir) { + threadContext->stateDir = 0; + } + threadContext->gameDir->close(threadContext->gameDir); + threadContext->gameDir = 0; + } + + if (threadContext->stateDir) { + threadContext->stateDir->close(threadContext->stateDir); + threadContext->stateDir = 0; + } } void GBAThreadInterrupt(struct GBAThread* threadContext) { MutexLock(&threadContext->stateMutex); + threadContext->savedState = threadContext->state; _waitOnInterrupt(threadContext); - threadContext->savedState = threadContext->state; - threadContext->state = THREAD_INTERRUPTED; + threadContext->state = THREAD_INTERRUPTING; if (threadContext->debugger && threadContext->debugger->state == DEBUGGER_RUNNING) { threadContext->debugger->state = DEBUGGER_EXITING; + } + ConditionWake(&threadContext->stateCond); + while (threadContext->state == THREAD_INTERRUPTING) { + ConditionWait(&threadContext->stateCond, &threadContext->stateMutex); } MutexUnlock(&threadContext->stateMutex); }

@@ -268,7 +403,7 @@ if (threadContext->state == THREAD_RUNNING) {

if (threadContext->debugger && threadContext->debugger->state == DEBUGGER_RUNNING) { threadContext->debugger->state = DEBUGGER_EXITING; } - threadContext->state = THREAD_PAUSED; + threadContext->state = THREAD_PAUSING; frameOn = 0; } MutexUnlock(&threadContext->stateMutex);

@@ -284,7 +419,7 @@ void GBAThreadUnpause(struct GBAThread* threadContext) {

int frameOn = 1; MutexLock(&threadContext->stateMutex); _waitOnInterrupt(threadContext); - if (threadContext->state == THREAD_PAUSED) { + if (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_PAUSING) { threadContext->state = THREAD_RUNNING; ConditionWake(&threadContext->stateCond); }

@@ -297,8 +432,8 @@ }

MutexUnlock(&threadContext->sync.videoFrameMutex); } -int GBAThreadIsPaused(struct GBAThread* threadContext) { - int isPaused; +bool GBAThreadIsPaused(struct GBAThread* threadContext) { + bool isPaused; MutexLock(&threadContext->stateMutex); _waitOnInterrupt(threadContext); isPaused = threadContext->state == THREAD_PAUSED;

@@ -307,7 +442,7 @@ return isPaused;

} void GBAThreadTogglePause(struct GBAThread* threadContext) { - int frameOn = 1; + bool frameOn = true; MutexLock(&threadContext->stateMutex); _waitOnInterrupt(threadContext); if (threadContext->state == THREAD_PAUSED) {

@@ -318,7 +453,7 @@ if (threadContext->debugger && threadContext->debugger->state == DEBUGGER_RUNNING) {

threadContext->debugger->state = DEBUGGER_EXITING; } threadContext->state = THREAD_PAUSED; - frameOn = 0; + frameOn = false; } MutexUnlock(&threadContext->stateMutex); MutexLock(&threadContext->sync.videoFrameMutex);

@@ -370,20 +505,20 @@ thread->frameCallback(thread);

} } -int GBASyncWaitFrameStart(struct GBASync* sync, int frameskip) { +bool GBASyncWaitFrameStart(struct GBASync* sync, int frameskip) { if (!sync) { - return 1; + return true; } MutexLock(&sync->videoFrameMutex); ConditionWake(&sync->videoFrameRequiredCond); if (!sync->videoFrameOn) { - return 0; + return false; } ConditionWait(&sync->videoFrameAvailableCond, &sync->videoFrameMutex); sync->videoFramePending = 0; sync->videoFrameSkip = frameskip; - return 1; + return true; } void GBASyncWaitFrameEnd(struct GBASync* sync) {

@@ -394,7 +529,7 @@

MutexUnlock(&sync->videoFrameMutex); } -int GBASyncDrawingFrame(struct GBASync* sync) { +bool GBASyncDrawingFrame(struct GBASync* sync) { return sync->videoFrameSkip <= 0; }
M src/gba/gba-thread.hsrc/gba/gba-thread.h

@@ -1,8 +1,12 @@

#ifndef GBA_THREAD_H #define GBA_THREAD_H +#include "common.h" + #include "gba.h" -#include "threading.h" + +#include "util/threading.h" +#include "platform/commandline.h" struct GBAThread; typedef void (*ThreadCallback)(struct GBAThread* threadContext);

@@ -10,17 +14,20 @@

enum ThreadState { THREAD_INITIALIZED = -1, THREAD_RUNNING = 0, - THREAD_INTERRUPTED = 1, - THREAD_PAUSED = 2, - THREAD_EXITING = 3, - THREAD_SHUTDOWN = 4 + THREAD_INTERRUPTED, + THREAD_INTERRUPTING, + THREAD_PAUSED, + THREAD_PAUSING, + THREAD_RESETING, + THREAD_EXITING, + THREAD_SHUTDOWN }; struct GBASync { int videoFramePending; int videoFrameWait; int videoFrameSkip; - int videoFrameOn; + bool videoFrameOn; Mutex videoFrameMutex; Condition videoFrameAvailableCond; Condition videoFrameRequiredCond;

@@ -34,13 +41,18 @@ struct GBAThread {

// Output enum ThreadState state; struct GBA* gba; + struct ARMCore* cpu; // Input struct GBAVideoRenderer* renderer; struct GBASIODriverSet sioDrivers; struct ARMDebugger* debugger; - int fd; - int biosFd; + struct VDir* gameDir; + struct VDir* stateDir; + struct VFile* rom; + struct VFile* save; + struct VFile* bios; + struct VFile* patch; const char* fname; int activeKeys; int frameskip;

@@ -53,6 +65,7 @@ Condition stateCond;

enum ThreadState savedState; GBALogHandler logHandler; + int logLevel; ThreadCallback startCallback; ThreadCallback cleanCallback; ThreadCallback frameCallback;

@@ -68,9 +81,12 @@ struct GBASerializedState** rewindBuffer;

int rewindBufferWriteOffset; }; -int GBAThreadStart(struct GBAThread* threadContext); -int GBAThreadHasStarted(struct GBAThread* threadContext); +void GBAMapOptionsToContext(struct StartupOptions*, struct GBAThread*); + +bool GBAThreadStart(struct GBAThread* threadContext); +bool GBAThreadHasStarted(struct GBAThread* threadContext); void GBAThreadEnd(struct GBAThread* threadContext); +void GBAThreadReset(struct GBAThread* threadContext); void GBAThreadJoin(struct GBAThread* threadContext); void GBAThreadInterrupt(struct GBAThread* threadContext);

@@ -78,14 +94,14 @@ void GBAThreadContinue(struct GBAThread* threadContext);

void GBAThreadPause(struct GBAThread* threadContext); void GBAThreadUnpause(struct GBAThread* threadContext); -int GBAThreadIsPaused(struct GBAThread* threadContext); +bool GBAThreadIsPaused(struct GBAThread* threadContext); void GBAThreadTogglePause(struct GBAThread* threadContext); struct GBAThread* GBAThreadGetContext(void); void GBASyncPostFrame(struct GBASync* sync); -int GBASyncWaitFrameStart(struct GBASync* sync, int frameskip); +bool GBASyncWaitFrameStart(struct GBASync* sync, int frameskip); void GBASyncWaitFrameEnd(struct GBASync* sync); -int GBASyncDrawingFrame(struct GBASync* sync); +bool GBASyncDrawingFrame(struct GBASync* sync); void GBASyncProduceAudio(struct GBASync* sync, int wait); void GBASyncLockAudio(struct GBASync* sync);
M src/gba/gba-video.csrc/gba/gba-video.c

@@ -2,14 +2,14 @@ #include "gba-video.h"

#include "gba.h" #include "gba-io.h" +#include "gba-rr.h" #include "gba-serialize.h" #include "gba-thread.h" -#include "memory.h" -#include <limits.h> -#include <string.h> +#include "util/memory.h" static void GBAVideoDummyRendererInit(struct GBAVideoRenderer* renderer); +static void GBAVideoDummyRendererReset(struct GBAVideoRenderer* renderer); static void GBAVideoDummyRendererDeinit(struct GBAVideoRenderer* renderer); static uint16_t GBAVideoDummyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value); static void GBAVideoDummyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y);

@@ -17,6 +17,7 @@ static void GBAVideoDummyRendererFinishFrame(struct GBAVideoRenderer* renderer);

static struct GBAVideoRenderer dummyRenderer = { .init = GBAVideoDummyRendererInit, + .reset = GBAVideoDummyRendererReset, .deinit = GBAVideoDummyRendererDeinit, .writeVideoRegister = GBAVideoDummyRendererWriteVideoRegister, .drawScanline = GBAVideoDummyRendererDrawScanline,

@@ -25,7 +26,10 @@ };

void GBAVideoInit(struct GBAVideo* video) { video->renderer = &dummyRenderer; + video->vram = 0; +} +void GBAVideoReset(struct GBAVideo* video) { video->inHblank = 0; video->inVblank = 0; video->vcounter = 0;

@@ -34,7 +38,7 @@ video->hblankIRQ = 0;

video->vcounterIRQ = 0; video->vcountSetting = 0; - video->vcount = -1; + video->vcount = 0; video->lastHblank = 0; video->nextHblank = VIDEO_HDRAW_LENGTH;

@@ -45,11 +49,18 @@ video->nextHblankIRQ = 0;

video->nextVblankIRQ = 0; video->nextVcounterIRQ = 0; + if (video->vram) { + mappedMemoryFree(video->vram, SIZE_VRAM); + } video->vram = anonymousMemoryMap(SIZE_VRAM); + video->renderer->vram = video->vram; int i; for (i = 0; i < 128; ++i) { - video->oam.obj[i].disable = 1; + video->oam.raw[i * 4] = 0x0200; + video->oam.raw[i * 4 + 1] = 0x0000; + video->oam.raw[i * 4 + 2] = 0x0000; + video->oam.raw[i * 4 + 3] = 0x0000; } }

@@ -92,13 +103,16 @@ if (GBASyncDrawingFrame(video->p->sync)) {

video->renderer->finishFrame(video->renderer); } video->nextVblankIRQ = video->nextEvent + VIDEO_TOTAL_LENGTH; - GBAMemoryRunVblankDMAs(&video->p->memory, lastEvent); + GBAMemoryRunVblankDMAs(video->p, lastEvent); if (video->vblankIRQ) { GBARaiseIRQ(video->p, IRQ_VBLANK); } GBASyncPostFrame(video->p->sync); break; case VIDEO_VERTICAL_TOTAL_PIXELS - 1: + if (video->p->rr) { + GBARRNextFrame(video->p->rr); + } video->inVblank = 0; break; case VIDEO_VERTICAL_TOTAL_PIXELS:

@@ -112,10 +126,6 @@ if (video->vcounter && video->vcounterIRQ) {

GBARaiseIRQ(video->p, IRQ_VCOUNTER); video->nextVcounterIRQ += VIDEO_TOTAL_LENGTH; } - - if (video->vcount < VIDEO_VERTICAL_PIXELS && GBASyncDrawingFrame(video->p->sync)) { - video->renderer->drawScanline(video->renderer, video->vcount); - } } else { // Begin Hblank video->inHblank = 1;

@@ -124,8 +134,12 @@ video->nextEvent = video->lastHblank + VIDEO_HBLANK_LENGTH;

video->nextHblank = video->nextEvent + VIDEO_HDRAW_LENGTH; video->nextHblankIRQ = video->nextHblank; + if (video->vcount < VIDEO_VERTICAL_PIXELS && GBASyncDrawingFrame(video->p->sync)) { + video->renderer->drawScanline(video->renderer, video->vcount); + } + if (video->vcount < VIDEO_VERTICAL_PIXELS) { - GBAMemoryRunHblankDMAs(&video->p->memory, lastEvent); + GBAMemoryRunHblankDMAs(video->p, lastEvent); } if (video->hblankIRQ) { GBARaiseIRQ(video->p, IRQ_HBLANK);

@@ -134,6 +148,8 @@ }

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); return video->nextEvent; }

@@ -154,34 +170,35 @@ }

} } -uint16_t GBAVideoReadDISPSTAT(struct GBAVideo* video) { - return (video->inVblank) | (video->inHblank << 1) | (video->vcounter << 2); +static void GBAVideoDummyRendererInit(struct GBAVideoRenderer* renderer) { + UNUSED(renderer); + // Nothing to do } -static void GBAVideoDummyRendererInit(struct GBAVideoRenderer* renderer) { - (void)(renderer); +static void GBAVideoDummyRendererReset(struct GBAVideoRenderer* renderer) { + UNUSED(renderer); // Nothing to do } static void GBAVideoDummyRendererDeinit(struct GBAVideoRenderer* renderer) { - (void)(renderer); + UNUSED(renderer); // Nothing to do } static uint16_t GBAVideoDummyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) { - (void)(renderer); - (void)(address); + UNUSED(renderer); + UNUSED(address); return value; } static void GBAVideoDummyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) { - (void)(renderer); - (void)(y); + UNUSED(renderer); + UNUSED(y); // Nothing to do } static void GBAVideoDummyRendererFinishFrame(struct GBAVideoRenderer* renderer) { - (void)(renderer); + UNUSED(renderer); // Nothing to do }

@@ -211,10 +228,10 @@ void GBAVideoDeserialize(struct GBAVideo* video, struct GBASerializedState* state) {

memcpy(video->renderer->vram, state->vram, SIZE_VRAM); int i; for (i = 0; i < SIZE_OAM; i += 2) { - GBAStore16(&video->p->memory.d, BASE_OAM | i, state->oam[i >> 1], 0); + GBAStore16(video->p->cpu, BASE_OAM | i, state->oam[i >> 1], 0); } for (i = 0; i < SIZE_PALETTE_RAM; i += 2) { - GBAStore16(&video->p->memory.d, BASE_PALETTE_RAM | i, state->pram[i >> 1], 0); + GBAStore16(video->p->cpu, BASE_PALETTE_RAM | i, state->pram[i >> 1], 0); } union GBARegisterDISPSTAT dispstat; dispstat.packed = state->io[REG_DISPSTAT >> 1];
M src/gba/gba-video.hsrc/gba/gba-video.h

@@ -1,9 +1,9 @@

#ifndef GBA_VIDEO_H #define GBA_VIDEO_H -#include "gba-memory.h" +#include "common.h" -#include <stdint.h> +#include "gba-memory.h" enum { VIDEO_CYCLES_PER_PIXEL = 4,

@@ -169,6 +169,7 @@ };

struct GBAVideoRenderer { void (*init)(struct GBAVideoRenderer* renderer); + void (*reset)(struct GBAVideoRenderer* renderer); void (*deinit)(struct GBAVideoRenderer* renderer); uint16_t (*writeVideoRegister)(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);

@@ -213,12 +214,12 @@ union GBAOAM oam;

}; void GBAVideoInit(struct GBAVideo* video); +void GBAVideoReset(struct GBAVideo* video); void GBAVideoDeinit(struct GBAVideo* video); void GBAVideoAssociateRenderer(struct GBAVideo* video, struct GBAVideoRenderer* renderer); int32_t GBAVideoProcessEvents(struct GBAVideo* video, int32_t cycles); void GBAVideoWriteDISPSTAT(struct GBAVideo* video, uint16_t value); -uint16_t GBAVideoReadDISPSTAT(struct GBAVideo* video); struct GBASerializedState; void GBAVideoSerialize(struct GBAVideo* video, struct GBASerializedState* state);
M src/gba/gba.csrc/gba/gba.c

@@ -2,19 +2,19 @@ #include "gba.h"

#include "gba-bios.h" #include "gba-io.h" +#include "gba-rr.h" #include "gba-sio.h" #include "gba-thread.h" -#include "memory.h" - -#include "debugger.h" -#include <limits.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/stat.h> +#include "util/crc32.h" +#include "util/memory.h" +#include "util/patch.h" +#include "util/vfs.h" const uint32_t GBA_ARM7TDMI_FREQUENCY = 0x1000000; +const uint32_t GBA_COMPONENT_MAGIC = 0x1000000; + +static const uint64_t GBA_ROM_MAGIC = 0x21A29A6951AEFF24; enum { SP_BASE_SYSTEM = 0x03FFFF00,

@@ -94,26 +94,28 @@

{ { 0, 0, 0, 0 }, 0, 0 } }; -static void GBAProcessEvents(struct ARMBoard* board); +static void GBAInit(struct ARMCore* cpu, struct ARMComponent* component); +static void GBAInterruptHandlerInit(struct ARMInterruptHandler* irqh); +static void GBAProcessEvents(struct ARMCore* cpu); static int32_t GBATimersProcessEvents(struct GBA* gba, int32_t cycles); -static void GBAHitStub(struct ARMBoard* board, uint32_t opcode); -static void GBAIllegal(struct ARMBoard* board, uint32_t opcode); +static void GBAHitStub(struct ARMCore* cpu, uint32_t opcode); +static void GBAIllegal(struct ARMCore* cpu, uint32_t opcode); static void _checkOverrides(struct GBA* gba, uint32_t code); -void GBAInit(struct GBA* gba) { +void GBACreate(struct GBA* gba) { + gba->d.id = GBA_COMPONENT_MAGIC; + gba->d.init = GBAInit; + gba->d.deinit = 0; +} + +static void GBAInit(struct ARMCore* cpu, struct ARMComponent* component) { + struct GBA* gba = (struct GBA*) component; + gba->cpu = cpu; gba->debugger = 0; - gba->savefile = 0; - ARMInit(&gba->cpu); - - gba->memory.p = gba; - GBAMemoryInit(&gba->memory); - ARMAssociateMemory(&gba->cpu, &gba->memory.d); - - gba->board.p = gba; - GBABoardInit(&gba->board); - ARMAssociateBoard(&gba->cpu, &gba->board.d); + GBAInterruptHandlerInit(&cpu->irqh); + GBAMemoryInit(gba); gba->video.p = gba; GBAVideoInit(&gba->video);

@@ -134,79 +136,102 @@ gba->keySource = 0;

gba->rotationSource = 0; gba->rumble = 0; + gba->romVf = 0; + gba->biosVf = 0; + gba->logLevel = GBA_LOG_INFO | GBA_LOG_WARN | GBA_LOG_ERROR | GBA_LOG_FATAL; gba->biosChecksum = GBAChecksum(gba->memory.bios, SIZE_BIOS); - - ARMReset(&gba->cpu); } -void GBADeinit(struct GBA* gba) { - GBAMemoryDeinit(&gba->memory); +void GBADestroy(struct GBA* gba) { + if (gba->pristineRom == gba->memory.rom) { + gba->memory.rom = 0; + } + + if (gba->romVf) { + gba->romVf->unmap(gba->romVf, gba->pristineRom, gba->pristineRomSize); + } + + if (gba->biosVf) { + gba->biosVf->unmap(gba->biosVf, gba->memory.bios, SIZE_BIOS); + } + + GBAMemoryDeinit(gba); GBAVideoDeinit(&gba->video); GBAAudioDeinit(&gba->audio); + GBARRContextDestroy(gba); } -void GBABoardInit(struct GBABoard* board) { - board->d.reset = GBABoardReset; - board->d.processEvents = GBAProcessEvents; - board->d.swi16 = GBASwi16; - board->d.swi32 = GBASwi32; - board->d.hitIllegal = GBAIllegal; - board->d.readCPSR = GBATestIRQ; - board->d.hitStub = GBAHitStub; +void GBAInterruptHandlerInit(struct ARMInterruptHandler* irqh) { + irqh->reset = GBAReset; + irqh->processEvents = GBAProcessEvents; + irqh->swi16 = GBASwi16; + irqh->swi32 = GBASwi32; + irqh->hitIllegal = GBAIllegal; + irqh->readCPSR = GBATestIRQ; + irqh->hitStub = GBAHitStub; } -void GBABoardReset(struct ARMBoard* board) { - struct ARMCore* cpu = board->cpu; +void GBAReset(struct ARMCore* cpu) { ARMSetPrivilegeMode(cpu, MODE_IRQ); cpu->gprs[ARM_SP] = SP_BASE_IRQ; ARMSetPrivilegeMode(cpu, MODE_SUPERVISOR); cpu->gprs[ARM_SP] = SP_BASE_SUPERVISOR; ARMSetPrivilegeMode(cpu, MODE_SYSTEM); cpu->gprs[ARM_SP] = SP_BASE_SYSTEM; + + struct GBA* gba = (struct GBA*) cpu->master; + GBAMemoryReset(gba); + GBAVideoReset(&gba->video); + GBAAudioReset(&gba->audio); + GBAIOInit(gba); } -static void GBAProcessEvents(struct ARMBoard* board) { +static void GBAProcessEvents(struct ARMCore* cpu) { do { - struct GBABoard* gbaBoard = (struct GBABoard*) board; - int32_t cycles = board->cpu->cycles; + struct GBA* gba = (struct GBA*) cpu->master; + int32_t cycles = cpu->cycles; int32_t nextEvent = INT_MAX; int32_t testEvent; - if (gbaBoard->p->springIRQ) { - ARMRaiseIRQ(&gbaBoard->p->cpu); - gbaBoard->p->springIRQ = 0; + if (gba->springIRQ) { + ARMRaiseIRQ(cpu); + gba->springIRQ = 0; } - testEvent = GBAVideoProcessEvents(&gbaBoard->p->video, cycles); + testEvent = GBAVideoProcessEvents(&gba->video, cycles); if (testEvent < nextEvent) { nextEvent = testEvent; } - testEvent = GBAAudioProcessEvents(&gbaBoard->p->audio, cycles); + testEvent = GBAAudioProcessEvents(&gba->audio, cycles); if (testEvent < nextEvent) { nextEvent = testEvent; } - testEvent = GBATimersProcessEvents(gbaBoard->p, cycles); + testEvent = GBATimersProcessEvents(gba, cycles); if (testEvent < nextEvent) { nextEvent = testEvent; } - testEvent = GBAMemoryRunDMAs(&gbaBoard->p->memory, cycles); + testEvent = GBAMemoryRunDMAs(gba, cycles); if (testEvent < nextEvent) { nextEvent = testEvent; } - testEvent = GBASIOProcessEvents(&gbaBoard->p->sio, cycles); + testEvent = GBASIOProcessEvents(&gba->sio, cycles); if (testEvent < nextEvent) { nextEvent = testEvent; } - board->cpu->cycles -= cycles; - board->cpu->nextEvent = nextEvent; - } while (board->cpu->cycles >= board->cpu->nextEvent); + cpu->cycles -= cycles; + cpu->nextEvent = nextEvent; + + if (cpu->halted) { + cpu->cycles = cpu->nextEvent; + } + } while (cpu->cycles >= cpu->nextEvent); } static int32_t GBATimersProcessEvents(struct GBA* gba, int32_t cycles) {

@@ -351,31 +376,31 @@ return nextEvent;

} void GBAAttachDebugger(struct GBA* gba, struct ARMDebugger* debugger) { - ARMDebuggerInit(debugger, &gba->cpu); gba->debugger = debugger; } void GBADetachDebugger(struct GBA* gba) { - ARMDebuggerDeinit(gba->debugger); gba->debugger = 0; } -void GBALoadROM(struct GBA* gba, int fd, const char* fname) { - struct stat info; - gba->memory.rom = fileMemoryMap(fd, SIZE_CART0, MEMORY_READ); +void GBALoadROM(struct GBA* gba, struct VFile* vf, struct VFile* sav, const char* fname) { + gba->romVf = vf; + gba->pristineRomSize = vf->seek(vf, 0, SEEK_END); + vf->seek(vf, 0, SEEK_SET); + gba->pristineRom = vf->map(vf, SIZE_CART0, MAP_READ); + gba->memory.rom = gba->pristineRom; gba->activeFile = fname; - fstat(fd, &info); - gba->memory.romSize = info.st_size; - if (gba->savefile) { - GBASavedataInit(&gba->memory.savedata, gba->savefile); - } + gba->memory.romSize = gba->pristineRomSize; + gba->romCrc32 = crc32(gba->memory.rom, gba->memory.romSize); + GBASavedataInit(&gba->memory.savedata, sav); GBAGPIOInit(&gba->memory.gpio, &((uint16_t*) gba->memory.rom)[GPIO_REG_DATA >> 1]); _checkOverrides(gba, ((struct GBACartridge*) gba->memory.rom)->id); // TODO: error check } -void GBALoadBIOS(struct GBA* gba, int fd) { - gba->memory.bios = fileMemoryMap(fd, SIZE_BIOS, MEMORY_READ); +void GBALoadBIOS(struct GBA* gba, struct VFile* vf) { + gba->biosVf = vf; + gba->memory.bios = vf->map(vf, SIZE_BIOS, MAP_READ); gba->memory.fullBios = 1; uint32_t checksum = GBAChecksum(gba->memory.bios, SIZE_BIOS); GBALog(gba, GBA_LOG_DEBUG, "BIOS Checksum: 0x%X", checksum);

@@ -387,16 +412,32 @@ } else {

GBALog(gba, GBA_LOG_WARN, "BIOS checksum incorrect"); } gba->biosChecksum = checksum; - if ((gba->cpu.gprs[ARM_PC] >> BASE_OFFSET) == BASE_BIOS) { - gba->memory.d.setActiveRegion(&gba->memory.d, gba->cpu.gprs[ARM_PC]); + if ((gba->cpu->gprs[ARM_PC] >> BASE_OFFSET) == BASE_BIOS) { + gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]); } // TODO: error check } +void GBAApplyPatch(struct GBA* gba, struct Patch* patch) { + size_t patchedSize = patch->outputSize(patch, gba->memory.romSize); + if (!patchedSize) { + return; + } + gba->memory.rom = anonymousMemoryMap(patchedSize); + memcpy(gba->memory.rom, gba->pristineRom, gba->memory.romSize > patchedSize ? patchedSize : gba->memory.romSize); + if (!patch->applyPatch(patch, gba->memory.rom, patchedSize)) { + mappedMemoryFree(gba->memory.rom, patchedSize); + gba->memory.rom = gba->pristineRom; + return; + } + gba->memory.romSize = patchedSize; + gba->romCrc32 = crc32(gba->memory.rom, gba->memory.romSize); +} + void GBATimerUpdateRegister(struct GBA* gba, int timer) { struct GBATimer* currentTimer = &gba->timers[timer]; if (currentTimer->enable && !currentTimer->countUp) { - gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->oldReload + ((gba->cpu.cycles - currentTimer->lastEvent) >> currentTimer->prescaleBits); + gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->oldReload + ((gba->cpu->cycles - currentTimer->lastEvent) >> currentTimer->prescaleBits); } }

@@ -430,16 +471,17 @@ int wasEnabled = currentTimer->enable;

currentTimer->enable = !!(control & 0x0080); if (!wasEnabled && currentTimer->enable) { if (!currentTimer->countUp) { - currentTimer->nextEvent = gba->cpu.cycles + currentTimer->overflowInterval; + currentTimer->nextEvent = gba->cpu->cycles + currentTimer->overflowInterval; } else { currentTimer->nextEvent = INT_MAX; } gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->reload; currentTimer->oldReload = currentTimer->reload; + currentTimer->lastEvent = 0; gba->timersEnabled |= 1 << timer; } else if (wasEnabled && !currentTimer->enable) { if (!currentTimer->countUp) { - gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->oldReload + ((gba->cpu.cycles - currentTimer->lastEvent) >> oldPrescale); + gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->oldReload + ((gba->cpu->cycles - currentTimer->lastEvent) >> oldPrescale); } gba->timersEnabled &= ~(1 << timer); } else if (currentTimer->prescaleBits != oldPrescale && !currentTimer->countUp) {

@@ -447,8 +489,8 @@ // FIXME: this might be before present

currentTimer->nextEvent = currentTimer->lastEvent + currentTimer->overflowInterval; } - if (currentTimer->nextEvent < gba->cpu.nextEvent) { - gba->cpu.nextEvent = currentTimer->nextEvent; + if (currentTimer->nextEvent < gba->cpu->nextEvent) { + gba->cpu->nextEvent = currentTimer->nextEvent; } };

@@ -462,55 +504,36 @@ GBALog(gba, GBA_LOG_STUB, "Gamepak interrupts not implemented");

} if (gba->memory.io[REG_IME >> 1] && value & gba->memory.io[REG_IF >> 1]) { - ARMRaiseIRQ(&gba->cpu); + ARMRaiseIRQ(gba->cpu); } } void GBAWriteIME(struct GBA* gba, uint16_t value) { if (value && gba->memory.io[REG_IE >> 1] & gba->memory.io[REG_IF >> 1]) { - ARMRaiseIRQ(&gba->cpu); + ARMRaiseIRQ(gba->cpu); } } void GBARaiseIRQ(struct GBA* gba, enum GBAIRQ irq) { gba->memory.io[REG_IF >> 1] |= 1 << irq; + gba->cpu->halted = 0; if (gba->memory.io[REG_IME >> 1] && (gba->memory.io[REG_IE >> 1] & 1 << irq)) { - ARMRaiseIRQ(&gba->cpu); + ARMRaiseIRQ(gba->cpu); } } -void GBATestIRQ(struct ARMBoard* board) { - struct GBABoard* gbaBoard = (struct GBABoard*) board; - struct GBA* gba = gbaBoard->p; +void GBATestIRQ(struct ARMCore* cpu) { + struct GBA* gba = (struct GBA*) cpu->master; if (gba->memory.io[REG_IME >> 1] && gba->memory.io[REG_IE >> 1] & gba->memory.io[REG_IF >> 1]) { gba->springIRQ = 1; - gba->cpu.nextEvent = 0; - } -} - -int GBAWaitForIRQ(struct GBA* gba) { - int irqs = gba->memory.io[REG_IF >> 1]; - int newIRQs = 0; - gba->memory.io[REG_IF >> 1] = 0; - while (1) { - if (gba->cpu.nextEvent == INT_MAX) { - break; - } else { - gba->cpu.cycles = gba->cpu.nextEvent; - GBAProcessEvents(&gba->board.d); - if (gba->memory.io[REG_IF >> 1]) { - newIRQs = gba->memory.io[REG_IF >> 1]; - break; - } - } + gba->cpu->nextEvent = 0; } - gba->memory.io[REG_IF >> 1] = newIRQs | irqs; - return newIRQs; } -int GBAHalt(struct GBA* gba) { - return GBAWaitForIRQ(gba); +void GBAHalt(struct GBA* gba) { + gba->cpu->nextEvent = 0; + gba->cpu->halted = 1; } static void _GBAVLog(struct GBA* gba, enum GBALogLevel level, const char* format, va_list args) {

@@ -546,9 +569,9 @@ va_end(args);

} void GBADebuggerLogShim(struct ARMDebugger* debugger, enum DebuggerLogLevel level, const char* format, ...) { - struct GBABoard* gbaBoard = 0; - if (debugger->cpu && debugger->cpu->board) { - gbaBoard = (struct GBABoard*) debugger->cpu->board; + struct GBA* gba = 0; + if (debugger->cpu) { + gba = (struct GBA*) debugger->cpu->master; } enum GBALogLevel gbaLevel;

@@ -568,26 +591,36 @@ break;

} va_list args; va_start(args, format); - _GBAVLog(gbaBoard ? gbaBoard->p : 0, gbaLevel, format, args); + _GBAVLog(gba, gbaLevel, format, args); va_end(args); } +bool GBAIsROM(struct VFile* vf) { + if (vf->seek(vf, 4, SEEK_SET) < 0) { + return false; + } + uint64_t signature; + if (vf->read(vf, &signature, sizeof(signature)) != sizeof(signature)) { + return false; + } + return signature == GBA_ROM_MAGIC; +} -void GBAHitStub(struct ARMBoard* board, uint32_t opcode) { - struct GBABoard* gbaBoard = (struct GBABoard*) board; +void GBAHitStub(struct ARMCore* cpu, uint32_t opcode) { + struct GBA* gba = (struct GBA*) cpu->master; enum GBALogLevel level = GBA_LOG_FATAL; - if (gbaBoard->p->debugger) { + if (gba->debugger) { level = GBA_LOG_STUB; - ARMDebuggerEnter(gbaBoard->p->debugger, DEBUGGER_ENTER_ILLEGAL_OP); + ARMDebuggerEnter(gba->debugger, DEBUGGER_ENTER_ILLEGAL_OP); } - GBALog(gbaBoard->p, level, "Stub opcode: %08x", opcode); + GBALog(gba, level, "Stub opcode: %08x", opcode); } -void GBAIllegal(struct ARMBoard* board, uint32_t opcode) { - struct GBABoard* gbaBoard = (struct GBABoard*) board; - GBALog(gbaBoard->p, GBA_LOG_WARN, "Illegal opcode: %08x", opcode); - if (gbaBoard->p->debugger) { - ARMDebuggerEnter(gbaBoard->p->debugger, DEBUGGER_ENTER_ILLEGAL_OP); +void GBAIllegal(struct ARMCore* cpu, uint32_t opcode) { + struct GBA* gba = (struct GBA*) cpu->master; + GBALog(gba, GBA_LOG_WARN, "Illegal opcode: %08x", opcode); + if (gba->debugger) { + ARMDebuggerEnter(gba->debugger, DEBUGGER_ENTER_ILLEGAL_OP); } }
M src/gba/gba.hsrc/gba/gba.h

@@ -1,15 +1,15 @@

#ifndef GBA_H #define GBA_H +#include "common.h" + #include "arm.h" -#include "debugger.h" +#include "debugger/debugger.h" #include "gba-memory.h" #include "gba-video.h" #include "gba-audio.h" #include "gba-sio.h" - -#include <stdarg.h> extern const uint32_t GBA_ARM7TDMI_FREQUENCY;

@@ -36,12 +36,12 @@ GBA_OUT_OF_MEMORY = -1

}; enum GBALogLevel { - GBA_LOG_STUB = 0x01, - GBA_LOG_DEBUG = 0x02, - GBA_LOG_INFO = 0x04, - GBA_LOG_WARN = 0x08, - GBA_LOG_ERROR = 0x10, - GBA_LOG_FATAL = 0x20, + GBA_LOG_FATAL = 0x01, + GBA_LOG_ERROR = 0x02, + GBA_LOG_WARN = 0x04, + GBA_LOG_INFO = 0x08, + GBA_LOG_DEBUG = 0x10, + GBA_LOG_STUB = 0x20, GBA_LOG_GAME_ERROR = 0x100 };

@@ -60,18 +60,17 @@ GBA_KEY_L = 9,

GBA_KEY_NONE = -1 }; +struct GBA; struct GBARotationSource; -struct GBA; +struct Patch; +struct VFile; + typedef void (*GBALogHandler)(struct GBA*, enum GBALogLevel, const char* format, va_list args); -struct GBABoard { - struct ARMBoard d; - struct GBA* p; -}; - struct GBA { - struct ARMCore cpu; - struct GBABoard board; + struct ARMComponent d; + + struct ARMCore* cpu; struct GBAMemory memory; struct GBAVideo video; struct GBAAudio audio;

@@ -99,9 +98,14 @@ uint32_t biosChecksum;

int* keySource; struct GBARotationSource* rotationSource; struct GBARumble* rumble; + struct GBARRContext* rr; + void* pristineRom; + size_t pristineRomSize; + uint32_t romCrc32; + struct VFile* romVf; + struct VFile* biosVf; const char* activeFile; - const char* savefile; int logLevel; GBALogHandler logHandler;

@@ -122,14 +126,10 @@ uint8_t checksum;

// And ROM data... }; -void GBAInit(struct GBA* gba); -void GBADeinit(struct GBA* gba); +void GBACreate(struct GBA* gba); +void GBADestroy(struct GBA* gba); -void GBAMemoryInit(struct GBAMemory* memory); -void GBAMemoryDeinit(struct GBAMemory* memory); - -void GBABoardInit(struct GBABoard* board); -void GBABoardReset(struct ARMBoard* board); +void GBAReset(struct ARMCore* cpu); void GBATimerUpdateRegister(struct GBA* gba, int timer); void GBATimerWriteTMCNT_LO(struct GBA* gba, int timer, uint16_t value);

@@ -138,15 +138,17 @@

void GBAWriteIE(struct GBA* gba, uint16_t value); void GBAWriteIME(struct GBA* gba, uint16_t value); void GBARaiseIRQ(struct GBA* gba, enum GBAIRQ irq); -void GBATestIRQ(struct ARMBoard* board); -int GBAWaitForIRQ(struct GBA* gba); -int GBAHalt(struct GBA* gba); +void GBATestIRQ(struct ARMCore* cpu); +void GBAHalt(struct GBA* gba); void GBAAttachDebugger(struct GBA* gba, struct ARMDebugger* debugger); void GBADetachDebugger(struct GBA* gba); -void GBALoadROM(struct GBA* gba, int fd, const char* fname); -void GBALoadBIOS(struct GBA* gba, int fd); +void GBALoadROM(struct GBA* gba, struct VFile* vf, struct VFile* sav, const char* fname); +void GBALoadBIOS(struct GBA* gba, struct VFile* vf); +void GBAApplyPatch(struct GBA* gba, struct Patch* patch); + +bool GBAIsROM(struct VFile* vf); __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

@@ -2,23 +2,49 @@ #include "hle-bios.h"

#include "gba-memory.h" -const size_t hleBiosLength = 196; +const size_t hleBiosLength = 516; const uint8_t hleBios[SIZE_BIOS] = { 0x06, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x05, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x00, 0x00, 0xa0, 0xe1, - 0x0e, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x02, 0xf3, 0xa0, 0xe3, + 0x1a, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x02, 0xf3, 0xa0, 0xe3, 0x00, 0x00, 0x5d, 0xe3, 0x01, 0xd3, 0xa0, 0x03, 0x20, 0xd0, 0x4d, 0x02, - 0x04, 0x40, 0x2d, 0xe9, 0x02, 0x20, 0x5e, 0xe5, 0x04, 0x00, 0x52, 0xe3, - 0x0b, 0x00, 0x00, 0x0b, 0x05, 0x00, 0x52, 0xe3, 0x01, 0x00, 0xa0, 0x03, - 0x01, 0x10, 0xa0, 0x03, 0x07, 0x00, 0x00, 0x0b, 0x04, 0x40, 0xbd, 0xe8, - 0x0e, 0xf0, 0xb0, 0xe1, 0x0f, 0x50, 0x2d, 0xe9, 0x01, 0x03, 0xa0, 0xe3, + 0x30, 0x40, 0x2d, 0xe9, 0x02, 0x40, 0x5e, 0xe5, 0x54, 0x50, 0xa0, 0xe3, + 0x04, 0x41, 0x95, 0xe7, 0x00, 0x00, 0x54, 0xe3, 0x0f, 0xe0, 0xa0, 0xe1, + 0x14, 0xff, 0x2f, 0x11, 0x30, 0x40, 0xbd, 0xe8, 0x0e, 0xf0, 0xb0, 0xe1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x00, + 0x94, 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, 0x10, 0x40, 0x2d, 0xe9, 0x00, 0x30, 0x0f, 0xe1, - 0x80, 0x30, 0xc3, 0xe3, 0x03, 0xf0, 0x29, 0xe1, 0x01, 0x43, 0xa0, 0xe3, - 0x00, 0x00, 0x50, 0xe3, 0x00, 0x00, 0xa0, 0xe3, 0x01, 0x20, 0xa0, 0xe3, - 0x00, 0x00, 0x00, 0x0a, 0x01, 0x03, 0xc4, 0xe5, 0x04, 0x22, 0xc4, 0xe5, - 0xb8, 0x30, 0x54, 0xe1, 0x01, 0x30, 0x13, 0xe0, 0x01, 0x30, 0x23, 0x10, - 0xb8, 0x30, 0x44, 0x11, 0x04, 0x02, 0xc4, 0xe5, 0xf7, 0xff, 0xff, 0x0a, - 0x00, 0x00, 0x0f, 0xe1, 0x80, 0x00, 0x80, 0xe3, 0x00, 0xf0, 0x29, 0xe1, - 0x10, 0x80, 0xbd, 0xe8 + 0x04, 0xf0, 0x5e, 0xe2, 0x01, 0x00, 0xa0, 0xe3, 0x01, 0x10, 0xa0, 0xe3, + 0x0c, 0x40, 0x2d, 0xe9, 0x00, 0x50, 0x4f, 0xe1, 0x1f, 0xf0, 0x29, 0xe3, + 0x01, 0x43, 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, 0x93, 0xf0, 0x29, 0xe3, 0x05, 0xf0, 0x69, 0xe1, + 0x0c, 0x80, 0xbd, 0xe8, 0x00, 0x40, 0x2d, 0xe9, 0x00, 0x50, 0x4f, 0xe1, + 0x1f, 0xf0, 0x29, 0xe3, 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, 0x03, 0x00, 0x51, 0xe1, + 0x04, 0x00, 0xa1, 0xb8, 0xfc, 0xff, 0xff, 0xba, 0x16, 0x00, 0x00, 0xea, + 0x01, 0x00, 0xc0, 0xe3, 0x01, 0x10, 0xc1, 0xe3, 0xa3, 0x35, 0x81, 0xe0, + 0xb0, 0x20, 0xd0, 0xe1, 0x03, 0x00, 0x51, 0xe1, 0xb2, 0x20, 0xc1, 0xb0, + 0xfc, 0xff, 0xff, 0xba, 0x0e, 0x00, 0x00, 0xea, 0x01, 0x03, 0x12, 0xe3, + 0x05, 0x00, 0x00, 0x0a, 0x23, 0x35, 0x81, 0xe0, 0x03, 0x00, 0x51, 0xe1, + 0x04, 0x00, 0xb0, 0xb8, 0x04, 0x00, 0xa1, 0xb8, 0xfb, 0xff, 0xff, 0xba, + 0x06, 0x00, 0x00, 0xea, 0xa3, 0x35, 0x81, 0xe0, 0x01, 0x00, 0xc0, 0xe3, + 0x01, 0x10, 0xc1, 0xe3, 0x03, 0x00, 0x51, 0xe1, 0xb2, 0x20, 0xd0, 0xb0, + 0xb2, 0x20, 0xc1, 0xb0, 0xfb, 0xff, 0xff, 0xba, 0x93, 0xf0, 0x29, 0xe3, + 0x05, 0xf0, 0x69, 0xe1, 0x00, 0x80, 0xbd, 0xe8, 0x00, 0x40, 0x2d, 0xe9, + 0x00, 0x50, 0x4f, 0xe1, 0x1f, 0xf0, 0x29, 0xe3, 0xf0, 0x07, 0x2d, 0xe9, + 0x01, 0x04, 0x12, 0xe3, 0x02, 0x36, 0xa0, 0xe1, 0x23, 0x25, 0x81, 0xe0, + 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x30, 0x90, 0xe5, 0x03, 0x40, 0xa0, 0xe1, + 0x03, 0x50, 0xa0, 0xe1, 0x03, 0x60, 0xa0, 0xe1, 0x03, 0x70, 0xa0, 0xe1, + 0x03, 0x80, 0xa0, 0xe1, 0x03, 0x90, 0xa0, 0xe1, 0x03, 0xa0, 0xa0, 0xe1, + 0x02, 0x00, 0x51, 0xe1, 0xf8, 0x07, 0xa1, 0xb8, 0xfc, 0xff, 0xff, 0xba, + 0x03, 0x00, 0x00, 0xea, 0x02, 0x00, 0x51, 0xe1, 0xf8, 0x07, 0xb0, 0xb8, + 0xf8, 0x07, 0xa1, 0xb8, 0xfb, 0xff, 0xff, 0xba, 0xf0, 0x07, 0xbd, 0xe8, + 0x93, 0xf0, 0x29, 0xe3, 0x05, 0xf0, 0x69, 0xe1, 0x00, 0x80, 0xbd, 0xe8 };
M src/gba/hle-bios.hsrc/gba/hle-bios.h

@@ -1,8 +1,7 @@

#ifndef HLE_BIOS_H #define HLE_BIOS_H -#include <stdint.h> -#include <string.h> +#include "common.h" extern const size_t hleBiosLength; extern const uint8_t hleBios[];
M src/gba/hle-bios.ssrc/gba/hle-bios.s

@@ -18,17 +18,32 @@ swiBase:

cmp sp, #0 moveq sp, #0x04000000 subeq sp, #0x20 -stmfd sp!, {r2, lr} -ldrb r2, [lr, #-2] -cmp r2, #4 -bleq IntrWait -cmp r2, #5 -moveq r0, #1 -moveq r1, #1 -bleq IntrWait -ldmfd sp!, {r2, lr} +stmfd sp!, {r4-r5, lr} +ldrb r4, [lr, #-2] +mov r5, #swiTable +ldr r4, [r5, r4, lsl #2] +cmp r4, #0 +mov lr, pc +bxne r4 +ldmfd sp!, {r4-r5, lr} movs pc, lr +swiTable: +.word SoftReset +.word RegisterRamReset +.word Halt +.word Stop +.word IntrWait +.word VBlankIntrWait +.word Div +.word DivArm +.word Sqrt +.word ArcTan +.word ArcTan2 +.word CpuSet +.word CpuFastSet +# ... The rest of this table isn't needed if the rest aren't implemented + irqBase: stmfd sp!, {r0-r3, r12, lr} mov r0, #0x04000000

@@ -37,33 +52,123 @@ ldr pc, [r0, #-4]

ldmfd sp!, {r0-r3, r12, lr} subs pc, lr, #4 +VBlankIntrWait: +mov r0, #1 +mov r1, #1 IntrWait: -stmfd sp!, {r4, lr} -# Save inputs -mrs r3, cpsr -bic r3, #0x80 -msr cpsr, r3 +stmfd sp!, {r2-r3, lr} +mrs r5, spsr +msr cpsr, #0x1F # Pull current interrupts enabled and add the ones we need mov r4, #0x04000000 # See if we want to return immediately cmp r0, #0 mov r0, #0 mov r2, #1 -beq .L1 +beq 1f # Halt -.L0: +0: strb r0, [r4, #0x301] -.L1: +1: # Check which interrupts were acknowledged -strb r2, [r4, #0x204] +strb r0, [r4, #0x208] ldrh r3, [r4, #-8] ands r3, r1 eorne r3, r1 strneh r3, [r4, #-8] -strb r0, [r4, #0x204] -beq .L0 -#Restore state -mrs r0, cpsr -orr r0, #0x80 -msr cpsr, r0 -ldmfd sp!, {r4, pc} +strb r2, [r4, #0x208] +beq 0b +msr cpsr, #0x93 +msr spsr, r5 +ldmfd sp!, {r2-r3, pc} + +CpuSet: +stmfd sp!, {lr} +mrs r5, spsr +msr cpsr, #0x1F +mov r3, r2, lsl #12 +tst r2, #0x01000000 +beq 0f +# Fill +tst r2, #0x04000000 +beq 1f +# Word +add r3, r1, r3, lsr #10 +ldmia r0!, {r2} +2: +cmp r1, r3 +stmltia r1!, {r2} +blt 2b +b 3f +# Halfword +1: +bic r0, #1 +bic r1, #1 +add r3, r1, r3, lsr #11 +ldrh r2, [r0] +2: +cmp r1, r3 +strlth r2, [r1], #2 +blt 2b +b 3f +# Copy +0: +tst r2, #0x04000000 +beq 1f +# Word +add r3, r1, r3, lsr #10 +2: +cmp r1, r3 +ldmltia r0!, {r2} +stmltia r1!, {r2} +blt 2b +b 3f +# Halfword +1: +add r3, r1, r3, lsr #11 +bic r0, #1 +bic r1, #1 +2: +cmp r1, r3 +ldrlth r2, [r0], #2 +strlth r2, [r1], #2 +blt 2b +3: +msr cpsr, #0x93 +msr spsr, r5 +ldmfd sp!, {pc} + +CpuFastSet: +stmfd sp!, {lr} +mrs r5, spsr +msr cpsr, #0x1F +stmfd sp!, {r4-r10} +tst r2, #0x01000000 +mov r3, r2, lsl #12 +add r2, r1, r3, lsr #10 +beq 0f +# Fill +ldr r3, [r0] +mov r4, r3 +mov r5, r3 +mov r6, r3 +mov r7, r3 +mov r8, r3 +mov r9, r3 +mov r10, r3 +1: +cmp r1, r2 +stmltia r1!, {r3-r10} +blt 1b +b 2f +# Copy +0: +cmp r1, r2 +ldmltia r0!, {r3-r10} +stmltia r1!, {r3-r10} +blt 0b +2: +ldmfd sp!, {r4-r10} +msr cpsr, #0x93 +msr spsr, r5 +ldmfd sp!, {pc}
M src/gba/renderers/video-glsl.hsrc/gba/renderers/video-glsl.h

@@ -1,6 +1,8 @@

#ifndef VIDEO_GLSL_H #define VIDEO_GLSL_H +#include "common.h" + #include "gba-video.h" #include <pthread.h>
M src/gba/renderers/video-software.csrc/gba/renderers/video-software.c

@@ -3,10 +3,6 @@

#include "gba.h" #include "gba-io.h" -#include <string.h> - -#define UNUSED(X) (void) (X) - static const int _objSizes[32] = { 8, 8, 16, 16,

@@ -64,6 +60,7 @@ static unsigned _mix(int weightA, unsigned colorA, int weightB, unsigned colorB);

void GBAVideoSoftwareRendererCreate(struct GBAVideoSoftwareRenderer* renderer) { renderer->d.init = GBAVideoSoftwareRendererInit; + renderer->d.reset = GBAVideoSoftwareRendererInit; renderer->d.deinit = GBAVideoSoftwareRendererDeinit; renderer->d.writeVideoRegister = GBAVideoSoftwareRendererWriteVideoRegister; renderer->d.writeOAM = GBAVideoSoftwareRendererWriteOAM;

@@ -90,9 +87,17 @@ 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->mosaic.packed = 0;

@@ -253,24 +258,36 @@ _updatePalettes(softwareRenderer);

break; case REG_WIN0H: softwareRenderer->winN[0].h.packed = value; + 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; + } if (softwareRenderer->winN[0].h.start > softwareRenderer->winN[0].h.end || softwareRenderer->winN[0].h.end > VIDEO_HORIZONTAL_PIXELS) { softwareRenderer->winN[0].h.end = VIDEO_HORIZONTAL_PIXELS; } break; case REG_WIN1H: softwareRenderer->winN[1].h.packed = value; + 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; + } if (softwareRenderer->winN[1].h.start > softwareRenderer->winN[1].h.end || softwareRenderer->winN[1].h.end > VIDEO_HORIZONTAL_PIXELS) { softwareRenderer->winN[1].h.end = VIDEO_HORIZONTAL_PIXELS; } break; case REG_WIN0V: softwareRenderer->winN[0].v.packed = value; + 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; + } if (softwareRenderer->winN[0].v.start > softwareRenderer->winN[0].v.end || softwareRenderer->winN[0].v.end > VIDEO_HORIZONTAL_PIXELS) { softwareRenderer->winN[0].v.end = VIDEO_VERTICAL_PIXELS; } break; case REG_WIN1V: softwareRenderer->winN[1].v.packed = value; + 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; + } if (softwareRenderer->winN[1].v.start > softwareRenderer->winN[1].v.end || softwareRenderer->winN[1].v.end > VIDEO_HORIZONTAL_PIXELS) { softwareRenderer->winN[1].v.end = VIDEO_VERTICAL_PIXELS; }

@@ -1048,18 +1065,20 @@ charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) >> 2) + (localY << 1); \

tileData = carryData; \ for (x = 0; x < 8; ++x) { \ if (!mosaicWait) { \ - if (x >= 4) { \ - tileData = ((uint32_t*)renderer->d.vram)[charBase + 1]; \ - if (!GBA_TEXT_MAP_HFLIP(mapData)) { \ + if (!GBA_TEXT_MAP_HFLIP(mapData)) { \ + if (x >= 4) { \ + tileData = ((uint32_t*)renderer->d.vram)[charBase + 1]; \ tileData >>= (x - 4) * 8; \ } else { \ - tileData >>= (7 - x) * 8; \ + tileData = ((uint32_t*)renderer->d.vram)[charBase]; \ + tileData >>= x * 8; \ } \ } else { \ - tileData = ((uint32_t*)renderer->d.vram)[charBase]; \ - if (!GBA_TEXT_MAP_HFLIP(mapData)) { \ - tileData >>= x * 8; \ + if (x >= 4) { \ + tileData = ((uint32_t*)renderer->d.vram)[charBase]; \ + tileData >>= (7 - x) * 8; \ } else { \ + tileData = ((uint32_t*)renderer->d.vram)[charBase + 1]; \ tileData >>= (3 - x) * 8; \ } \ } \

@@ -1101,7 +1120,7 @@ outX = renderer->start + tileX * 8 - (inX & 0x7); \

} \ \ uint32_t* pixel = &renderer->row[outX]; \ - if (background->mosaic) { \ + if (background->mosaic && renderer->mosaic.bgH) { \ int mosaicH = renderer->mosaic.bgH + 1; \ int x; \ int mosaicWait = outX % mosaicH; \

@@ -1402,14 +1421,14 @@ #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_DRAW_PIXEL_16_NORMAL(localX) \ - unsigned tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \ + unsigned tileData = vramBase[((yBase + charBase + xBase) & 0x7FFF) >> 1]; \ 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 = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \ + unsigned tileData = vramBase[((yBase + charBase + xBase) & 0x7FFF) >> 1]; \ tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \ if (tileData) { \ renderer->row[outX] |= FLAG_OBJWIN; \

@@ -1419,14 +1438,14 @@ #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_DRAW_PIXEL_256_NORMAL(localX) \ - unsigned tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \ + unsigned tileData = vramBase[((yBase + charBase + xBase) & 0x7FFF) >> 1]; \ 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 = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \ + unsigned tileData = vramBase[((yBase + charBase + xBase) & 0x7FFF) >> 1]; \ tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \ if (tileData) { \ renderer->row[outX] |= FLAG_OBJWIN; \

@@ -1441,7 +1460,8 @@ 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; - unsigned charBase = BASE_TILE + sprite->tile * 0x20; + 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) { // Hack: if a sprite is blended, then the variant palette is not used, but we don't know if it's blended in advance

@@ -1509,7 +1529,8 @@ 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; - unsigned charBase = BASE_TILE + sprite->tile * 0x20; + 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) {
M src/gba/renderers/video-software.hsrc/gba/renderers/video-software.h

@@ -1,9 +1,9 @@

#ifndef VIDEO_SOFTWARE_H #define VIDEO_SOFTWARE_H -#include "gba-video.h" +#include "common.h" -#include <pthread.h> +#include "gba-video.h" #ifdef COLOR_16_BIT typedef uint16_t color_t;

@@ -65,17 +65,14 @@ OFFSET_PRIORITY = 30,

OFFSET_INDEX = 28, }; -enum PixelFlags { - FLAG_PRIORITY = 0xC0000000, - FLAG_INDEX = 0x30000000, - FLAG_IS_BACKGROUND = 0x08000000, - FLAG_UNWRITTEN = 0xFC000000, - FLAG_TARGET_1 = 0x02000000, - FLAG_TARGET_2 = 0x01000000, - FLAG_OBJWIN = 0x01000000, - - FLAG_ORDER_MASK = 0xF8000000 -}; +#define FLAG_PRIORITY 0xC0000000 +#define FLAG_INDEX 0x30000000 +#define FLAG_IS_BACKGROUND 0x08000000 +#define FLAG_UNWRITTEN 0xFC000000 +#define FLAG_TARGET_1 0x02000000 +#define FLAG_TARGET_2 0x01000000 +#define FLAG_OBJWIN 0x01000000 +#define FLAG_ORDER_MASK 0xF8000000 #define IS_WRITABLE(PIXEL) ((PIXEL) & 0xFE000000)

@@ -87,17 +84,19 @@ };

uint16_t packed; }; -union WindowControl { - struct { - unsigned bg0Enable : 1; - unsigned bg1Enable : 1; - unsigned bg2Enable : 1; - unsigned bg3Enable : 1; - unsigned objEnable : 1; - unsigned blendEnable : 1; - unsigned : 2; +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; }; - uint8_t packed; int8_t priority; };

@@ -105,7 +104,7 @@ #define MAX_WINDOW 5

struct Window { uint8_t endX; - union WindowControl control; + struct WindowControl control; }; struct GBAVideoSoftwareRenderer {

@@ -146,13 +145,13 @@

struct WindowN { union WindowRegion h; union WindowRegion v; - union WindowControl control; + struct WindowControl control; } winN[2]; - union WindowControl winout; - union WindowControl objwin; + struct WindowControl winout; + struct WindowControl objwin; - union WindowControl currentWindow; + struct WindowControl currentWindow; int nWindows; struct Window windows[MAX_WINDOW];
A src/platform/commandline.c

@@ -0,0 +1,214 @@

+#include "commandline.h" + +#include "debugger/debugger.h" + +#ifdef USE_CLI_DEBUGGER +#include "debugger/cli-debugger.h" +#endif + +#ifdef USE_GDB_STUB +#include "debugger/gdb-stub.h" +#endif + +#include <fcntl.h> +#include <getopt.h> + +#define GRAPHICS_OPTIONS "234f" +#define GRAPHICS_USAGE \ + "\nGraphics options:\n" \ + " -2 2x viewport\n" \ + " -3 3x viewport\n" \ + " -4 4x viewport\n" \ + " -f Start full-screen" + +static const struct option _options[] = { + { "bios", required_argument, 0, 'b' }, + { "dirmode", required_argument, 0, 'D' }, + { "frameskip", required_argument, 0, 's' }, +#ifdef USE_CLI_DEBUGGER + { "debug", no_argument, 0, 'd' }, +#endif +#ifdef USE_GDB_STUB + { "gdb", no_argument, 0, 'g' }, +#endif + { "patch", required_argument, 0, 'p' }, + { 0, 0, 0, 0 } +}; + +bool _parseGraphicsArg(struct SubParser* parser, int option, const char* arg); + +bool parseCommandArgs(struct StartupOptions* opts, int argc, char* const* argv, struct SubParser* subparser) { + memset(opts, 0, sizeof(*opts)); + + int ch; + char options[64] = + "b:Dl:p:s:" +#ifdef USE_CLI_DEBUGGER + "d" +#endif +#ifdef USE_GDB_STUB + "g" +#endif + ; + if (subparser->extraOptions) { + // TODO: modularize options to subparsers + strncat(options, subparser->extraOptions, sizeof(options) - strlen(options) - 1); + } + while ((ch = getopt_long(argc, argv, options, _options, 0)) != -1) { + switch (ch) { + case 'b': + opts->bios = strdup(optarg); + break; + case 'D': + opts->dirmode = true; + break; +#ifdef USE_CLI_DEBUGGER + case 'd': + if (opts->debuggerType != DEBUGGER_NONE) { + return false; + } + opts->debuggerType = DEBUGGER_CLI; + break; +#endif +#ifdef USE_GDB_STUB + case 'g': + if (opts->debuggerType != DEBUGGER_NONE) { + return false; + } + opts->debuggerType = DEBUGGER_GDB; + break; +#endif + case 'l': + opts->logLevel = atoi(optarg); + break; + case 'p': + opts->patch = strdup(optarg); + break; + case 's': + opts->frameskip = atoi(optarg); + break; + default: + if (subparser) { + if (!subparser->parse(subparser, ch, optarg)) { + return false; + } + } + break; + } + } + argc -= optind; + argv += optind; + if (argc != 1) { + return false; + } + opts->fname = strdup(argv[0]); + return true; +} + +void freeOptions(struct StartupOptions* opts) { + free(opts->fname); + opts->fname = 0; + + free(opts->bios); + opts->bios = 0; + + free(opts->patch); + opts->patch = 0; +} + +void initParserForGraphics(struct SubParser* parser, struct GraphicsOpts* opts) { + parser->usage = GRAPHICS_USAGE; + parser->opts = opts; + parser->parse = _parseGraphicsArg; + parser->extraOptions = GRAPHICS_OPTIONS; + opts->multiplier = 1; + opts->fullscreen = 0; + opts->width = 240; + opts->height = 160; +} + +bool _parseGraphicsArg(struct SubParser* parser, int option, const char* arg) { + UNUSED(arg); + struct GraphicsOpts* graphicsOpts = parser->opts; + switch (option) { + case 'f': + graphicsOpts->fullscreen = 1; + return true; + case '2': + if (graphicsOpts->multiplier != 1) { + return false; + } + graphicsOpts->multiplier = 2; + graphicsOpts->width *= graphicsOpts->multiplier; + graphicsOpts->height *= graphicsOpts->multiplier; + return true; + case '3': + if (graphicsOpts->multiplier != 1) { + return false; + } + graphicsOpts->multiplier = 3; + graphicsOpts->width *= graphicsOpts->multiplier; + graphicsOpts->height *= graphicsOpts->multiplier; + return true; + case '4': + if (graphicsOpts->multiplier != 1) { + return false; + } + graphicsOpts->multiplier = 4; + graphicsOpts->width *= graphicsOpts->multiplier; + graphicsOpts->height *= graphicsOpts->multiplier; + return true; + default: + return false; + } +} + +struct ARMDebugger* createDebugger(struct StartupOptions* opts) { + union DebugUnion { + struct ARMDebugger d; +#ifdef USE_CLI_DEBUGGER + struct CLIDebugger cli; +#endif +#ifdef USE_GDB_STUB + struct GDBStub gdb; +#endif + }; + + union DebugUnion* debugger = malloc(sizeof(union DebugUnion)); + + switch (opts->debuggerType) { +#ifdef USE_CLI_DEBUGGER + case DEBUGGER_CLI: + CLIDebuggerCreate(&debugger->cli); + break; +#endif +#ifdef USE_GDB_STUB + case DEBUGGER_GDB: + GDBStubCreate(&debugger->gdb); + GDBStubListen(&debugger->gdb, 2345, 0); + break; +#endif + case DEBUGGER_NONE: + case DEBUGGER_MAX: + free(debugger); + return 0; + break; + } + + return &debugger->d; +} + +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"); +#ifdef USE_CLI_DEBUGGER + puts(" -d, --debug Use command-line debugger"); +#endif +#ifdef USE_GDB_STUB + puts(" -g, --gdb Start GDB session (default port 2345)"); +#endif + if (extraOptions) { + puts(extraOptions); + } +}
A src/platform/commandline.h

@@ -0,0 +1,53 @@

+#ifndef COMMAND_LINE_H +#define COMMAND_LINE_H + +#include "common.h" + +enum DebuggerType { + DEBUGGER_NONE = 0, +#ifdef USE_CLI_DEBUGGER + DEBUGGER_CLI, +#endif +#ifdef USE_GDB_STUB + DEBUGGER_GDB, +#endif + DEBUGGER_MAX +}; + +struct StartupOptions { + char* fname; + char* bios; + char* patch; + bool dirmode; + int logLevel; + int frameskip; + int rewindBufferCapacity; + int rewindBufferInterval; + + enum DebuggerType debuggerType; + int debugAtStart; +}; + +struct SubParser { + const char* usage; + bool (*parse)(struct SubParser* parser, int option, const char* arg); + const char* extraOptions; + void* opts; +}; + +struct GraphicsOpts { + int multiplier; + int fullscreen; + int width; + int height; +}; + +bool parseCommandArgs(struct StartupOptions* opts, int argc, char* const* argv, struct SubParser* subparser); +void freeOptions(struct StartupOptions* opts); + +void usage(const char* arg0, const char* extraOptions); + +void initParserForGraphics(struct SubParser* parser, struct GraphicsOpts* opts); +struct ARMDebugger* createDebugger(struct StartupOptions* opts); + +#endif
M src/platform/egl-main.csrc/platform/sdl/egl-main.c

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

-#include "debugger.h" +#include "debugger/debugger.h" #include "gba-thread.h" #include "gba.h" #include "renderers/video-software.h" #include "sdl-audio.h" #include "sdl-events.h" -#include <SDL.h> +#include <SDL/SDL.h> #include <GLES2/gl2.h> #include <EGL/egl.h>

@@ -16,7 +16,6 @@ #include <fcntl.h>

#include <malloc.h> #include <signal.h> #include <sys/time.h> -#include <unistd.h> struct GBAVideoEGLRenderer { struct GBAVideoSoftwareRenderer d;

@@ -50,7 +49,9 @@ "varying vec2 texCoord;\n"

"uniform sampler2D tex;\n" "void main() {\n" - " gl_FragColor = texture2D(tex, texCoord);\n" + " vec4 color = texture2D(tex, texCoord);\n" + " color.a = 1.;\n" + " gl_FragColor = color;" "}"; static const GLfloat _vertices[] = {

@@ -76,7 +77,6 @@ if (fd < 0) {

return 1; } - struct GBAThread context; struct GBAVideoEGLRenderer renderer; if (!_GBAEGLInit(&renderer)) {

@@ -84,16 +84,19 @@ return 1;

} GBAVideoSoftwareRendererCreate(&renderer.d); - context.fd = fd; - context.fname = fname; - context.useDebugger = 0; - context.renderer = &renderer.d.d; - context.frameskip = 0; - context.sync.videoFrameWait = 0; - context.sync.audioWait = 1; - context.startCallback = _GBASDLStart; - context.cleanCallback = _GBASDLClean; - context.userData = &renderer; + struct GBAThread context = { + .fd = fd, + .fname = fname, + .biosFd = -1, + .useDebugger = 0, + .renderer = &renderer.d.d, + .frameskip = 0, + .sync.videoFrameWait = 0, + .sync.audioWait = 0, + .startCallback = _GBASDLStart, + .cleanCallback = _GBASDLClean, + .userData = &renderer + }; GBAThreadStart(&context); _GBAEGLRunloop(&context, &renderer);
M src/platform/glsl-main.csrc/platform/glsl-main.c

@@ -15,7 +15,6 @@ #include <fcntl.h>

#include <errno.h> #include <signal.h> #include <sys/time.h> -#include <unistd.h> static int _GBASDLInit(void); static void _GBASDLDeinit(void);
M src/platform/perf-main.csrc/platform/perf-main.c

@@ -2,56 +2,71 @@ #include "gba-thread.h"

#include "gba.h" #include "renderers/video-software.h" +#include <errno.h> #include <fcntl.h> #include <signal.h> #include <sys/time.h> -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> + +#define PERF_OPTIONS "S:" +#define PERF_USAGE \ + "\nBenchmark options:\n" \ + " -S SEC Run for SEC in-game seconds before exiting" + +struct PerfOpts { + int duration; +}; static void _GBAPerfRunloop(struct GBAThread* context, int* frames); static void _GBAPerfShutdown(int signal); +static int _parsePerfOpts(struct SubParser* parser, int option, const char* arg); static struct GBAThread* _thread; int main(int argc, char** argv) { - const char* fname = "test.rom"; - if (argc > 1) { - fname = argv[1]; - } - int fd = open(fname, O_RDONLY); - if (fd < 0) { - return 1; - } - signal(SIGINT, _GBAPerfShutdown); struct GBAVideoSoftwareRenderer renderer; GBAVideoSoftwareRendererCreate(&renderer); + struct PerfOpts perfOpts = { 0 }; + struct SubParser subparser = { + .usage = PERF_USAGE, + .parse = _parsePerfOpts, + .extraOptions = PERF_OPTIONS, + .opts = &perfOpts + }; + + struct StartupOptions opts; + if (!parseCommandArgs(&opts, argc, argv, &subparser)) { + usage(argv[0], PERF_USAGE); + return 1; + } + renderer.outputBuffer = malloc(256 * 256 * 4); renderer.outputBufferStride = 256; struct GBAThread context = { - .fd = fd, - .fname = fname, - .biosFd = -1, .renderer = &renderer.d, - .frameskip = 0, .sync.videoFrameWait = 0, .sync.audioWait = 0 }; _thread = &context; + + context.debugger = createDebugger(&opts); + + GBAMapOptionsToContext(&opts, &context); + GBAThreadStart(&context); - int frames = 0; + int frames = perfOpts.duration; time_t start = time(0); _GBAPerfRunloop(&context, &frames); time_t end = time(0); int duration = end - start; GBAThreadJoin(&context); - close(fd); + freeOptions(&opts); + free(context.debugger); free(renderer.outputBuffer);

@@ -63,6 +78,8 @@

static void _GBAPerfRunloop(struct GBAThread* context, int* frames) { struct timeval lastEcho; gettimeofday(&lastEcho, 0); + int duration = *frames; + *frames = 0; int lastFrames = 0; while (context->state < THREAD_EXITING) { if (GBASyncWaitFrameStart(&context->sync, 0)) {

@@ -82,12 +99,27 @@ lastFrames = 0;

} } GBASyncWaitFrameEnd(&context->sync); + if (*frames == duration * 60) { + _GBAPerfShutdown(0); + } } + printf("\033[2K\r"); } static void _GBAPerfShutdown(int signal) { - (void) (signal); + UNUSED(signal); pthread_mutex_lock(&_thread->stateMutex); _thread->state = THREAD_EXITING; pthread_mutex_unlock(&_thread->stateMutex); } + +static int _parsePerfOpts(struct SubParser* parser, int option, const char* arg) { + struct PerfOpts* opts = parser->opts; + switch (option) { + case 'S': + opts->duration = strtol(arg, 0, 10); + return !errno; + default: + return 0; + } +}
M src/platform/posix/memory.csrc/platform/posix/memory.c

@@ -1,16 +1,9 @@

-#include "memory.h" +#include "util/memory.h" #include <sys/mman.h> void* anonymousMemoryMap(size_t size) { return mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); -} -void* fileMemoryMap(int fd, size_t size, int flags) { - int mmapFlags = MAP_PRIVATE; - if (flags & MEMORY_WRITE) { - mmapFlags = MAP_SHARED; - } - return mmap(0, size, PROT_READ | PROT_WRITE, mmapFlags, fd, 0); } void mappedMemoryFree(void* memory, size_t size) {
M src/platform/sdl/CMakeLists.txtsrc/platform/sdl/CMakeLists.txt

@@ -1,4 +1,5 @@

set(SDL_VERSION "2" CACHE STRING "Version of SDL to use (1.2 or 2)") +set(BUILD_GL ON CACHE STRING "Build with OpenGL") if (SDL_VERSION EQUAL "2") include(FindPkgConfig)

@@ -7,6 +8,7 @@ if (SDL2_FOUND)

set(SDL_INCLUDE_DIR ${SDL2_INCLUDE_DIRS}) set(SDL_LIBRARY ${SDL2_LIBRARIES}) set(SDLMAIN_LIBRARY "") + link_directories(${SDL2_LIBDIR}) endif() endif()

@@ -16,19 +18,22 @@ endif()

file(GLOB PLATFORM_SRC ${CMAKE_SOURCE_DIR}/src/platform/sdl/sdl-*.c) set(PLATFORM_LIBRARY "${SDL_LIBRARY};${SDLMAIN_LIBRARY}") -include_directories(${CMAKE_SOURCE_DIR}/src/platform/sdl) +include_directories(${CMAKE_SOURCE_DIR}/src/platform/sdl ${SDL_INCLUDE_DIR}) -if(BUILD_RASPI AND BUILD_EGL) - set(MAIN_SRC ${CMAKE_SOURCE_DIR}/src/platform/sdl/egl-main.c) - set(OPENGL_LIBRARY "-lEGL -lGLESv2 -lbcm_host") - set(OPENGL_INCLUDE_DIR "") +if(BUILD_RASPI) add_definitions(-DBUILD_RASPI) -elseif(BUILD_BBB OR BUILD_RASPI) + set(EGL_MAIN_SRC ${CMAKE_SOURCE_DIR}/src/platform/sdl/egl-main.c) + set(EGL_LIBRARY "-lEGL -lGLESv2 -lbcm_host") + add_executable(${BINARY_NAME}-rpi ${PLATFORM_SRC} ${EGL_MAIN_SRC}) + target_link_libraries(${BINARY_NAME}-rpi ${BINARY_NAME} ${PLATFORM_LIBRARY} ${EGL_LIBRARY}) +endif() + +if(BUILD_BBB OR BUILD_RASPI OR NOT BUILD_GL) set(MAIN_SRC ${CMAKE_SOURCE_DIR}/src/platform/sdl/sw-main.c) else() set(MAIN_SRC ${CMAKE_SOURCE_DIR}/src/platform/sdl/gl-main.c) find_package(OpenGL REQUIRED) - include_directories(${SDL_INCLUDE_DIR} ${OPENGL_INCLUDE_DIR}) + include_directories(${OPENGL_INCLUDE_DIR}) endif() add_executable(${BINARY_NAME}-sdl WIN32 ${PLATFORM_SRC} ${MAIN_SRC})
M src/platform/sdl/gl-main.csrc/platform/sdl/gl-main.c

@@ -1,9 +1,17 @@

-#include "cli-debugger.h" +#ifdef USE_CLI_DEBUGGER +#include "debugger/cli-debugger.h" +#endif + +#ifdef USE_GDB_STUB +#include "debugger/gdb-stub.h" +#endif + #include "gba-thread.h" #include "gba.h" #include "sdl-audio.h" #include "sdl-events.h" #include "renderers/video-software.h" +#include "platform/commandline.h" #include <SDL.h> #ifdef __APPLE__

@@ -12,11 +20,9 @@ #else

#include <GL/gl.h> #endif -#include <fcntl.h> #include <errno.h> #include <signal.h> #include <sys/time.h> -#include <unistd.h> struct GLSoftwareRenderer { struct GBAVideoSoftwareRenderer d;

@@ -52,48 +58,51 @@ 0, 1

}; int main(int argc, char** argv) { - const char* fname = "test.rom"; - if (argc > 1) { - fname = argv[1]; - } - int fd = open(fname, O_RDONLY); - if (fd < 0) { + struct GLSoftwareRenderer renderer; + GBAVideoSoftwareRendererCreate(&renderer.d); + + struct StartupOptions opts; + struct SubParser subparser; + struct GraphicsOpts graphicsOpts; + initParserForGraphics(&subparser, &graphicsOpts); + if (!parseCommandArgs(&opts, argc, argv, &subparser)) { + usage(argv[0], subparser.usage); + freeOptions(&opts); return 1; } - struct GLSoftwareRenderer renderer; - GBAVideoSoftwareRendererCreate(&renderer.d); - - renderer.viewportWidth = 240; - renderer.viewportHeight = 160; + renderer.viewportWidth = graphicsOpts.width; + renderer.viewportHeight = graphicsOpts.height; +#if SDL_VERSION_ATLEAST(2, 0, 0) + renderer.events.fullscreen = graphicsOpts.fullscreen; + renderer.events.windowUpdated = 0; +#endif if (!_GBASDLInit(&renderer)) { + freeOptions(&opts); return 1; } - struct CLIDebugger debugger; - CLIDebuggerCreate(&debugger); struct GBAThread context = { - .fd = fd, - .biosFd = -1, - .fname = fname, - .debugger = &debugger.d, .renderer = &renderer.d.d, - .frameskip = 0, - .sync.videoFrameWait = 0, - .sync.audioWait = 1, .startCallback = _GBASDLStart, .cleanCallback = _GBASDLClean, - .userData = &renderer, - .rewindBufferCapacity = 10, - .rewindBufferInterval = 30 + .sync.videoFrameWait = 0, + .sync.audioWait = 1, + .userData = &renderer }; + + context.debugger = createDebugger(&opts); + + GBAMapOptionsToContext(&opts, &context); + GBAThreadStart(&context); _GBASDLRunloop(&context, &renderer); GBAThreadJoin(&context); - close(fd); + freeOptions(&opts); + free(context.debugger); _GBASDLDeinit(&renderer);

@@ -129,11 +138,10 @@ SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);

#endif #if SDL_VERSION_ATLEAST(2, 0, 0) - renderer->window = SDL_CreateWindow("GBAc", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, renderer->viewportWidth, renderer->viewportHeight, SDL_WINDOW_OPENGL); + 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_GetWindowSize(renderer->window, &renderer->viewportWidth, &renderer->viewportHeight); renderer->events.window = renderer->window; - renderer->events.fullscreen = 0; #else #ifdef COLOR_16_BIT SDL_SetVideoMode(renderer->viewportWidth, renderer->viewportHeight, 16, SDL_OPENGL);

@@ -174,15 +182,19 @@ while (context->state < THREAD_EXITING) {

if (GBASyncWaitFrameStart(&context->sync, context->frameskip)) { glBindTexture(GL_TEXTURE_2D, renderer->tex); #ifdef COLOR_16_BIT +#ifdef COLOR_5_6_5 + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, renderer->d.outputBuffer); +#else glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, renderer->d.outputBuffer); +#endif #else glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, renderer->d.outputBuffer); #endif - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); if (context->sync.videoFrameWait) { glFlush(); } } + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); GBASyncWaitFrameEnd(&context->sync); #if SDL_VERSION_ATLEAST(2, 0, 0) SDL_GL_SwapWindow(renderer->window);

@@ -191,13 +203,15 @@ SDL_GL_SwapBuffers();

#endif while (SDL_PollEvent(&event)) { - int fullscreen = renderer->events.fullscreen; GBASDLHandleEvent(context, &renderer->events, &event); +#if SDL_VERSION_ATLEAST(2, 0, 0) // Event handling can change the size of the screen - if (renderer->events.fullscreen != fullscreen) { + if (renderer->events.windowUpdated) { SDL_GetWindowSize(renderer->window, &renderer->viewportWidth, &renderer->viewportHeight); glViewport(0, 0, renderer->viewportWidth, renderer->viewportHeight); + renderer->events.windowUpdated = 0; } +#endif } } }
M src/platform/sdl/sdl-audio.csrc/platform/sdl/sdl-audio.c

@@ -8,10 +8,10 @@ #define FPS_TARGET 60.f

static void _GBASDLAudioCallback(void* context, Uint8* data, int len); -int GBASDLInitAudio(struct GBASDLAudio* context) { +bool GBASDLInitAudio(struct GBASDLAudio* context) { if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { GBALog(0, GBA_LOG_ERROR, "Could not initialize SDL sound system"); - return 0; + return false; } context->desiredSpec.freq = 44100;

@@ -24,14 +24,14 @@ context->audio = 0;

context->drift = 0.f; if (SDL_OpenAudio(&context->desiredSpec, &context->obtainedSpec) < 0) { GBALog(0, GBA_LOG_ERROR, "Could not open SDL sound system"); - return 0; + return false; } SDL_PauseAudio(0); - return 1; + return true; } void GBASDLDeinitAudio(struct GBASDLAudio* context) { - (void)(context); + UNUSED(context); SDL_PauseAudio(1); SDL_CloseAudio(); SDL_QuitSubSystem(SDL_INIT_AUDIO);
M src/platform/sdl/sdl-audio.hsrc/platform/sdl/sdl-audio.h

@@ -1,6 +1,8 @@

#ifndef SDL_AUDIO_H #define SDL_AUDIO_H +#include "common.h" + #include <SDL.h> struct GBASDLAudio {

@@ -11,7 +13,7 @@ float ratio;

struct GBAAudio* audio; }; -int GBASDLInitAudio(struct GBASDLAudio* context); +bool GBASDLInitAudio(struct GBASDLAudio* context); void GBASDLDeinitAudio(struct GBASDLAudio* context); #endif
M src/platform/sdl/sdl-events.csrc/platform/sdl/sdl-events.c

@@ -1,7 +1,8 @@

#include "sdl-events.h" -#include "debugger.h" +#include "debugger/debugger.h" #include "gba-io.h" +#include "gba-rr.h" #include "gba-serialize.h" #include "gba-video.h"

@@ -11,16 +12,16 @@ #else

#define GUI_MOD KMOD_CTRL #endif -int GBASDLInitEvents(struct GBASDLEvents* context) { +bool GBASDLInitEvents(struct GBASDLEvents* context) { if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0) { - return 0; + return false; } SDL_JoystickEventState(SDL_ENABLE); context->joystick = SDL_JoystickOpen(0); #if !SDL_VERSION_ATLEAST(2, 0, 0) SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); #endif - return 1; + return true; } void GBASDLDeinitEvents(struct GBASDLEvents* context) {

@@ -99,6 +100,12 @@ GBAThreadInterrupt(context);

GBARewind(context, 10); GBAThreadContinue(context); return; + case SDLK_ESCAPE: + GBAThreadInterrupt(context); + GBARRStopPlaying(context->gba->rr); + GBARRStopRecording(context->gba->rr); + GBAThreadContinue(context); + return; default: if (event->type == SDL_KEYDOWN) { if (event->keysym.mod & GUI_MOD) {

@@ -107,6 +114,7 @@ #if SDL_VERSION_ATLEAST(2, 0, 0)

case SDLK_f: SDL_SetWindowFullscreen(sdlContext->window, sdlContext->fullscreen ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP); sdlContext->fullscreen = !sdlContext->fullscreen; + sdlContext->windowUpdated = 1; break; #endif case SDLK_p:

@@ -117,6 +125,27 @@ GBAThreadPause(context);

context->frameCallback = _pauseAfterFrame; GBAThreadUnpause(context); break; + case SDLK_r: + GBAThreadReset(context); + break; + case SDLK_t: + GBAThreadReset(context); + GBAThreadInterrupt(context); + GBARRContextCreate(context->gba); + GBARRSetStream(context->gba->rr, context->stateDir); + GBARRStopPlaying(context->gba->rr); + GBARRStartRecording(context->gba->rr); + GBAThreadContinue(context); + break; + case SDLK_y: + GBAThreadReset(context); + GBAThreadInterrupt(context); + GBARRContextCreate(context->gba); + GBARRSetStream(context->gba->rr, context->stateDir); + GBARRStopRecording(context->gba->rr); + GBARRStartPlaying(context->gba->rr, event->keysym.mod & KMOD_SHIFT); + GBAThreadContinue(context); + break; default: break; }

@@ -205,6 +234,17 @@ context->activeKeys &= ~((1 << GBA_KEY_UP) | (1 << GBA_KEY_LEFT) | (1 << GBA_KEY_DOWN) | (1 << GBA_KEY_RIGHT));

context->activeKeys |= key; } +#if SDL_VERSION_ATLEAST(2, 0, 0) +static void _GBASDLHandleWindowEvent(struct GBAThread* context, struct GBASDLEvents* sdlContext, const struct SDL_WindowEvent* event) { + UNUSED(context); + switch (event->event) { + case SDL_WINDOWEVENT_SIZE_CHANGED: + sdlContext->windowUpdated = 1; + break; + } +} +#endif + void GBASDLHandleEvent(struct GBAThread* context, struct GBASDLEvents* sdlContext, const union SDL_Event* event) { switch (event->type) { case SDL_QUIT:

@@ -217,6 +257,11 @@ context->state = THREAD_EXITING;

ConditionWake(&context->stateCond); MutexUnlock(&context->stateMutex); break; +#if SDL_VERSION_ATLEAST(2, 0, 0) + case SDL_WINDOWEVENT: + _GBASDLHandleWindowEvent(context, sdlContext, &event->window); + break; +#endif case SDL_KEYDOWN: case SDL_KEYUP: _GBASDLHandleKeypress(context, sdlContext, &event->key);
M src/platform/sdl/sdl-events.hsrc/platform/sdl/sdl-events.h

@@ -1,6 +1,8 @@

#ifndef SDL_EVENTS_H #define SDL_EVENTS_H +#include "common.h" + #include "gba-thread.h" #include <SDL.h>

@@ -10,10 +12,11 @@ SDL_Joystick* joystick;

#if SDL_VERSION_ATLEAST(2, 0, 0) SDL_Window* window; int fullscreen; + int windowUpdated; #endif }; -int GBASDLInitEvents(struct GBASDLEvents*); +bool GBASDLInitEvents(struct GBASDLEvents*); void GBASDLDeinitEvents(struct GBASDLEvents*); void GBASDLHandleEvent(struct GBAThread* context, struct GBASDLEvents* sdlContext, const union SDL_Event* event);
M src/platform/sdl/sw-main.csrc/platform/sdl/sw-main.c

@@ -1,4 +1,11 @@

-#include "debugger.h" +#ifdef USE_CLI_DEBUGGER +#include "debugger/cli-debugger.h" +#endif + +#ifdef USE_GDB_STUB +#include "debugger/gdb-stub.h" +#endif + #include "gba-thread.h" #include "gba.h" #include "renderers/video-software.h"

@@ -7,69 +14,125 @@ #include "sdl-events.h"

#include <SDL.h> -#include <fcntl.h> #include <errno.h> #include <signal.h> #include <sys/time.h> -#include <unistd.h> + +#ifdef __ARM_NEON +void _neon2x(void* dest, void* src, int width, int height); +void _neon4x(void* dest, void* src, int width, int height); +#endif struct SoftwareRenderer { struct GBAVideoSoftwareRenderer d; struct GBASDLAudio audio; struct GBASDLEvents events; +#if SDL_VERSION_ATLEAST(2, 0, 0) + SDL_Window* window; + SDL_Texture* tex; + SDL_Renderer* sdlRenderer; +#else + int ratio; +#endif + int viewportWidth; + int viewportHeight; }; static int _GBASDLInit(struct SoftwareRenderer* renderer); static void _GBASDLDeinit(struct SoftwareRenderer* renderer); -static void _GBASDLRunloop(struct GBAThread* context); +static void _GBASDLRunloop(struct GBAThread* context, struct SoftwareRenderer* renderer); static void _GBASDLStart(struct GBAThread* context); static void _GBASDLClean(struct GBAThread* context); int main(int argc, char** argv) { - const char* fname = "test.rom"; - if (argc > 1) { - fname = argv[1]; - } - int fd = open(fname, O_RDONLY); - if (fd < 0) { + struct SoftwareRenderer renderer; + GBAVideoSoftwareRendererCreate(&renderer.d); + + struct StartupOptions opts; + struct SubParser subparser; + struct GraphicsOpts graphicsOpts; + initParserForGraphics(&subparser, &graphicsOpts); + if (!parseCommandArgs(&opts, argc, argv, &subparser)) { + usage(argv[0], subparser.usage); + freeOptions(&opts); return 1; } - struct GBAThread context; - struct SoftwareRenderer renderer; - GBAVideoSoftwareRendererCreate(&renderer.d); + renderer.viewportWidth = graphicsOpts.width; + renderer.viewportHeight = graphicsOpts.height; if (!_GBASDLInit(&renderer)) { + freeOptions(&opts); return 1; } - context.fd = fd; - context.fname = fname; - context.useDebugger = 1; - context.renderer = &renderer.d.d; - context.frameskip = 0; - context.sync.videoFrameWait = 0; - context.sync.audioWait = 1; - context.startCallback = _GBASDLStart; - context.cleanCallback = _GBASDLClean; - context.userData = &renderer; + struct GBAThread context = { + .renderer = &renderer.d.d, + .startCallback = _GBASDLStart, + .cleanCallback = _GBASDLClean, + .sync.videoFrameWait = 0, + .sync.audioWait = 1, + .userData = &renderer + }; + + context.debugger = createDebugger(&opts); + + GBAMapOptionsToContext(&opts, &context); + +#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)); + SDL_GetWindowSize(renderer.window, &renderer.viewportWidth, &renderer.viewportHeight); + renderer.events.window = renderer.window; + renderer.sdlRenderer = SDL_CreateRenderer(renderer.window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); +#ifdef COLOR_16_BIT +#ifdef COLOR_5_6_5 + renderer.tex = SDL_CreateTexture(renderer.sdlRenderer, SDL_PIXELFORMAT_RGB565, SDL_TEXTUREACCESS_STREAMING, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS); +#else + renderer.tex = SDL_CreateTexture(renderer.sdlRenderer, SDL_PIXELFORMAT_ABGR1555, SDL_TEXTUREACCESS_STREAMING, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS); +#endif +#else + renderer.tex = SDL_CreateTexture(renderer.sdlRenderer, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS); +#endif + SDL_LockTexture(renderer.tex, 0, &renderer.d.outputBuffer, &renderer.d.outputBufferStride); +#ifdef COLOR_16_BIT + renderer.d.outputBufferStride /= 2; +#else + renderer.d.outputBufferStride /= 4; +#endif +#else SDL_Surface* surface = SDL_GetVideoSurface(); SDL_LockSurface(surface); - renderer.d.outputBuffer = surface->pixels; + + renderer.ratio = graphicsOpts.multiplier; + if (renderer.ratio == 1) { + renderer.d.outputBuffer = surface->pixels; +#ifdef COLOR_16_BIT + renderer.d.outputBufferStride = surface->pitch / 2; +#else + renderer.d.outputBufferStride = surface->pitch / 4; +#endif + } else { #ifdef COLOR_16_BIT - renderer.d.outputBufferStride = surface->pitch / 2; + renderer.d.outputBuffer = malloc(240 * 160 * 2); #else - renderer.d.outputBufferStride = surface->pitch / 4; + renderer.d.outputBuffer = malloc(240 * 160 * 4); +#endif + renderer.d.outputBufferStride = 240; + } #endif GBAThreadStart(&context); - _GBASDLRunloop(&context); + _GBASDLRunloop(&context, &renderer); +#if !SDL_VERSION_ATLEAST(2, 0, 0) SDL_UnlockSurface(surface); +#endif GBAThreadJoin(&context); - close(fd); + free(context.debugger); + freeOptions(&opts); _GBASDLDeinit(&renderer);

@@ -84,29 +147,59 @@

GBASDLInitEvents(&renderer->events); GBASDLInitAudio(&renderer->audio); +#if !SDL_VERSION_ATLEAST(2, 0, 0) #ifdef COLOR_16_BIT - SDL_SetVideoMode(240, 160, 16, SDL_DOUBLEBUF | SDL_HWSURFACE); + SDL_SetVideoMode(renderer->viewportWidth, renderer->viewportHeight, 16, SDL_DOUBLEBUF | SDL_HWSURFACE); #else - SDL_SetVideoMode(240, 160, 32, SDL_DOUBLEBUF | SDL_HWSURFACE); + SDL_SetVideoMode(renderer->viewportWidth, renderer->viewportHeight, 32, SDL_DOUBLEBUF | SDL_HWSURFACE); +#endif #endif return 1; } -static void _GBASDLRunloop(struct GBAThread* context) { +static void _GBASDLRunloop(struct GBAThread* context, struct SoftwareRenderer* renderer) { SDL_Event event; +#if !SDL_VERSION_ATLEAST(2, 0, 0) SDL_Surface* surface = SDL_GetVideoSurface(); +#endif while (context->state < THREAD_EXITING) { if (GBASyncWaitFrameStart(&context->sync, context->frameskip)) { +#if SDL_VERSION_ATLEAST(2, 0, 0) + SDL_UnlockTexture(renderer->tex); + SDL_RenderCopy(renderer->sdlRenderer, renderer->tex, 0, 0); + SDL_RenderPresent(renderer->sdlRenderer); + SDL_LockTexture(renderer->tex, 0, &renderer->d.outputBuffer, &renderer->d.outputBufferStride); +#ifdef COLOR_16_BIT + renderer->d.outputBufferStride /= 2; +#else + renderer->d.outputBufferStride /= 4; +#endif +#else + switch (renderer->ratio) { +#if defined(__ARM_NEON) && COLOR_16_BIT + case 2: + _neon2x(surface->pixels, renderer->d.outputBuffer, 240, 160); + break; + case 4: + _neon4x(surface->pixels, renderer->d.outputBuffer, 240, 160); + break; +#endif + case 1: + break; + default: + abort(); + } SDL_UnlockSurface(surface); SDL_Flip(surface); SDL_LockSurface(surface); +#endif } GBASyncWaitFrameEnd(&context->sync); while (SDL_PollEvent(&event)) { - GBASDLHandleEvent(context, &event); + GBASDLHandleEvent(context, &renderer->events, &event); } } }
M src/platform/windows/memory.csrc/platform/windows/memory.c

@@ -1,30 +1,13 @@

-#include "memory.h" +#include "util/memory.h" -#include <io.h> #include <Windows.h> void* anonymousMemoryMap(size_t size) { - HANDLE hMap = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, size & 0xFFFFFFFF, 0); - return MapViewOfFile(hMap, FILE_MAP_WRITE, 0, 0, size); -} - -void* fileMemoryMap(int fd, size_t size, int flags) { - int createFlags = PAGE_READONLY; - int mapFiles = FILE_MAP_READ; - if (flags & MEMORY_WRITE) { - createFlags = PAGE_READWRITE; - mapFiles = FILE_MAP_WRITE; - } - size_t location = lseek(fd, 0, SEEK_CUR); - size_t fileSize = lseek(fd, 0, SEEK_END); - lseek(fd, location, SEEK_SET); - if (size > fileSize) { - size = fileSize; - } - HANDLE hMap = CreateFileMapping((HANDLE) _get_osfhandle(fd), 0, createFlags, 0, size & 0xFFFFFFFF, 0); - return MapViewOfFile(hMap, mapFiles, 0, 0, size); + return VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); } void mappedMemoryFree(void* memory, size_t size) { - // TODO fill in + UNUSED(size); + // size is not useful here because we're freeing the memory, not decommitting it + VirtualFree(memory, 0, MEM_RELEASE); }
A src/util/arm-algo.S

@@ -0,0 +1,94 @@

+# r0: Destination +# r1: Source +# r2: Number of words to copy as halfwords +.global _to16Bit +_to16Bit: +push {r4-r10} +mov r8, r0 +mov r9, r1 +mov r10, r2 +.L0: +tst r10, #7 +beq .L1 +ldr r0, [r9], #4 +strh r0, [r8], #2 +sub r10, #1 +b .L0 +.L1: +ldmia r9!, {r0-r7} +strh r0, [r8], #2 +strh r1, [r8], #2 +strh r2, [r8], #2 +strh r3, [r8], #2 +strh r4, [r8], #2 +strh r5, [r8], #2 +strh r6, [r8], #2 +strh r7, [r8], #2 +subs r10, #8 +bne .L1 +pop {r4-r10} +bx lr + +#ifdef __ARM_NEON +# r0: Destination +# r1: Source +# r2: Width +# r3: Height +.global _neon2x +_neon2x: +push {r4-r5} +lsl r4, r2, #2 +.n20: +mov r2, r4, lsr #4 +add r5, r0, r4 +.n21: +vld2.32 {d0[], d1[]}, [r1]! +vmov d2, d0 +vmov d3, d1 +vzip.16 d0, d2 +vzip.16 d1, d3 +vst1.32 {q0}, [r0]! +vst1.32 {q0}, [r5]! +subs r2, #1 +bne .n21 +subs r3, #1 +mov r0, r5 +bne .n20 +pop {r4-r5} +bx lr + +.global _neon4x +_neon4x: +push {r4-r7} +lsl r4, r2, #3 +.n40: +mov r2, r4, lsr #5 +add r5, r0, r4 +add r6, r5, r4 +add r7, r6, r4 +.n41: +vld4.16 {d0[], d1[], d2[], d3[]}, [r1]! +vst1.16 {d0}, [r0]! +vst1.16 {d0}, [r5]! +vst1.16 {d0}, [r6]! +vst1.16 {d0}, [r7]! +vst1.16 {d1}, [r0]! +vst1.16 {d1}, [r5]! +vst1.16 {d1}, [r6]! +vst1.16 {d1}, [r7]! +vst1.16 {d2}, [r0]! +vst1.16 {d2}, [r5]! +vst1.16 {d2}, [r6]! +vst1.16 {d2}, [r7]! +vst1.16 {d3}, [r0]! +vst1.16 {d3}, [r5]! +vst1.16 {d3}, [r6]! +vst1.16 {d3}, [r7]! +subs r2, #1 +bne .n41 +subs r3, #1 +mov r0, r7 +bne .n40 +pop {r4-r7} +bx lr +#endif
M src/util/circle-buffer.csrc/util/circle-buffer.c

@@ -1,8 +1,5 @@

#include "circle-buffer.h" -#include <stddef.h> -#include <stdlib.h> - #ifndef NDEBUG static int _checkIntegrity(struct CircleBuffer* buffer) { if ((int8_t*) buffer->writePtr - (int8_t*) buffer->readPtr == buffer->size) {

@@ -21,9 +18,7 @@

void CircleBufferInit(struct CircleBuffer* buffer, unsigned capacity) { buffer->data = malloc(capacity); buffer->capacity = capacity; - buffer->size = 0; - buffer->readPtr = buffer->data; - buffer->writePtr = buffer->data; + CircleBufferClear(buffer); } void CircleBufferDeinit(struct CircleBuffer* buffer) {

@@ -33,6 +28,12 @@ }

unsigned CircleBufferSize(const struct CircleBuffer* buffer) { return buffer->size; +} + +void CircleBufferClear(struct CircleBuffer* buffer) { + buffer->size = 0; + buffer->readPtr = buffer->data; + buffer->writePtr = buffer->data; } int CircleBufferWrite8(struct CircleBuffer* buffer, int8_t value) {

@@ -169,3 +170,22 @@ }

#endif return length; } + +int CircleBufferDump(const struct CircleBuffer* buffer, void* output, size_t length) { + int8_t* data = buffer->readPtr; + if (buffer->size == 0) { + return 0; + } + if (length > buffer->size) { + length = buffer->size; + } + size_t remaining = buffer->capacity - ((int8_t*) data - (int8_t*) buffer->data); + if (length <= remaining) { + memcpy(output, data, length); + } else { + memcpy(output, data, remaining); + memcpy((int8_t*) output + remaining, buffer->data, length - remaining); + } + + return length; +}
M src/util/circle-buffer.hsrc/util/circle-buffer.h

@@ -1,8 +1,7 @@

#ifndef CIRCLE_BUFFER_H #define CIRCLE_BUFFER_H -#include <stdint.h> -#include <string.h> +#include "common.h" struct CircleBuffer { void* data;

@@ -15,10 +14,12 @@

void CircleBufferInit(struct CircleBuffer* buffer, unsigned capacity); void CircleBufferDeinit(struct CircleBuffer* buffer); unsigned CircleBufferSize(const struct CircleBuffer* buffer); +void CircleBufferClear(struct CircleBuffer* buffer); int CircleBufferWrite8(struct CircleBuffer* buffer, int8_t value); int CircleBufferWrite32(struct CircleBuffer* buffer, int32_t value); int CircleBufferRead8(struct CircleBuffer* buffer, int8_t* value); int CircleBufferRead32(struct CircleBuffer* buffer, int32_t* value); int CircleBufferRead(struct CircleBuffer* buffer, void* output, size_t length); +int CircleBufferDump(const struct CircleBuffer* buffer, void* output, size_t length); #endif
A src/util/crc32.c

@@ -0,0 +1,133 @@

+/*- + * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or + * code or tables extracted from it, as desired without restriction. + * + * First, the polynomial itself and its table of feedback terms. The + * polynomial is + * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 + * + * Note that we take it "backwards" and put the highest-order term in + * the lowest-order bit. The X^32 term is "implied"; the LSB is the + * X^31 term, etc. The X^0 term (usually shown as "+1") results in + * the MSB being 1 + * + * Note that the usual hardware shift register implementation, which + * is what we're using (we're merely optimizing it by doing eight-bit + * chunks at a time) shifts bits into the lowest-order term. In our + * implementation, that means shifting towards the right. Why do we + * do it this way? Because the calculated CRC must be transmitted in + * order from highest-order term to lowest-order term. UARTs transmit + * characters in order from LSB to MSB. By storing the CRC this way + * we hand it to the UART in the order low-byte to high-byte; the UART + * sends each low-bit to hight-bit; and the result is transmission bit + * by bit from highest- to lowest-order term without requiring any bit + * shuffling on our part. Reception works similarly + * + * The feedback terms table consists of 256, 32-bit entries. Notes + * + * The table can be generated at runtime if desired; code to do so + * is shown later. It might not be obvious, but the feedback + * terms simply represent the results of eight shift/xor opera + * tions for all combinations of data and CRC register values + * + * The values must be right-shifted by eight bits by the "updcrc + * logic; the shift must be unsigned (bring in zeroes). On some + * hardware you could probably optimize the shift in assembler by + * using byte-swap instructions + * polynomial $edb88320 + * + * + * CRC32 code derived from work by Gary S. Brown. + */ + +#include "util/crc32.h" + +#include "util/vfs.h" + +enum { + BUFFER_SIZE = 1024 +}; + +static uint32_t crc32Table[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +uint32_t crc32(const void* buf, size_t size) { + return updateCrc32(0, buf, size); +} + +uint32_t updateCrc32(uint32_t crc, const void* buf, size_t size) { + const uint8_t* p = buf; + + crc = ~crc; + for (size_t i = 0; i < size; ++i) { + crc = crc32Table[(crc ^ p[i]) & 0xFF] ^ (crc >> 8); + } + + return ~crc; +} + +uint32_t fileCrc32(struct VFile* vf, size_t endOffset) { + char buffer[BUFFER_SIZE]; + size_t blocksize; + size_t alreadyRead = 0; + if (vf->seek(vf, 0, SEEK_SET) < 0) { + return 0; + } + uint32_t crc = 0; + while (alreadyRead < endOffset) { + size_t toRead = sizeof(buffer); + if (toRead + alreadyRead > endOffset) { + toRead = endOffset - alreadyRead; + } + blocksize = vf->read(vf, buffer, toRead); + alreadyRead += blocksize; + crc = updateCrc32(crc, buffer, blocksize); + if (blocksize < toRead) { + return 0; + } + } + return crc; +}
A src/util/crc32.h

@@ -0,0 +1,13 @@

+#ifndef CRC32_H +#define CRC32_H + +#include <stdint.h> +#include <string.h> + +struct VFile; + +uint32_t crc32(const void* buf, size_t size); +uint32_t updateCrc32(uint32_t crc, const void* buf, size_t size); +uint32_t fileCrc32(struct VFile* file, size_t endOffset); + +#endif
M src/util/memory.hsrc/util/memory.h

@@ -1,13 +1,9 @@

#ifndef MEMORY_H #define MEMORY_H -#include <unistd.h> - -#define MEMORY_READ 1 -#define MEMORY_WRITE 2 +#include "common.h" void* anonymousMemoryMap(size_t size); -void* fileMemoryMap(int fd, size_t size, int flags); void mappedMemoryFree(void* memory, size_t size); -#endif+#endif
A src/util/patch-ips.c

@@ -0,0 +1,86 @@

+#include "util/patch-ips.h" + +#include "util/patch.h" +#include "util/vfs.h" + +static size_t _IPSOutputSize(struct Patch* patch, size_t inSize); +static bool _IPSApplyPatch(struct Patch* patch, void* out, size_t outSize); + +bool loadPatchIPS(struct Patch* patch) { + patch->vf->seek(patch->vf, 0, SEEK_SET); + + char buffer[5]; + if (patch->vf->read(patch->vf, buffer, 5) != 5) { + return false; + } + + if (memcmp(buffer, "PATCH", 5) != 0) { + return false; + } + + patch->vf->seek(patch->vf, -3, SEEK_END); + if (patch->vf->read(patch->vf, buffer, 3) != 3) { + return false; + } + + if (memcmp(buffer, "EOF", 3) != 0) { + return false; + } + + patch->outputSize = _IPSOutputSize; + patch->applyPatch = _IPSApplyPatch; + return true; +} + +size_t _IPSOutputSize(struct Patch* patch, size_t inSize) { + UNUSED(patch); + return inSize; +} + +bool _IPSApplyPatch(struct Patch* patch, void* out, size_t outSize) { + if (patch->vf->seek(patch->vf, 5, SEEK_SET) != 5) { + return false; + } + uint8_t* buf = out; + + while (true) { + uint32_t offset = 0; + uint16_t size = 0; + + if (patch->vf->read(patch->vf, &offset, 3) != 3) { + return false; + } + + if (offset == 0x464F45) { + return true; + } + + offset = (offset >> 16) | (offset & 0xFF00) | ((offset << 16) & 0xFF0000); + if (patch->vf->read(patch->vf, &size, 2) != 2) { + return false; + } + if (!size) { + // RLE chunk + if (patch->vf->read(patch->vf, &size, 2) != 2) { + return false; + } + size = (size >> 8) | (size << 8); + uint8_t byte; + if (patch->vf->read(patch->vf, &byte, 1) != 1) { + return false; + } + if (offset + size > outSize) { + return false; + } + memset(&buf[offset], byte, size); + } else { + size = (size >> 8) | (size << 8); + if (offset + size > outSize) { + return false; + } + if (patch->vf->read(patch->vf, &buf[offset], size) != size) { + return false; + } + } + } +}
A src/util/patch-ips.h

@@ -0,0 +1,10 @@

+#ifndef PATCH_IPS_H +#define PATCH_IPS_H + +#include "common.h" + +struct Patch; + +bool loadPatchIPS(struct Patch* patch); + +#endif
A src/util/patch-ups.c

@@ -0,0 +1,115 @@

+#include "util/patch-ips.h" + +#include "util/crc32.h" +#include "util/patch.h" +#include "util/vfs.h" + +enum { + IN_CHECKSUM = -12, + OUT_CHECKSUM = -8, + PATCH_CHECKSUM = -4, +}; + +static size_t _UPSOutputSize(struct Patch* patch, size_t inSize); +static bool _UPSApplyPatch(struct Patch* patch, void* out, size_t outSize); +static size_t _UPSDecodeLength(struct VFile* vf); + +bool loadPatchUPS(struct Patch* patch) { + patch->vf->seek(patch->vf, 0, SEEK_SET); + + char buffer[4]; + if (patch->vf->read(patch->vf, buffer, 4) != 4) { + return false; + } + + if (memcmp(buffer, "UPS1", 4) != 0) { + return false; + } + + size_t filesize = patch->vf->seek(patch->vf, 0, SEEK_END); + + uint32_t goodCrc32; + patch->vf->seek(patch->vf, PATCH_CHECKSUM, SEEK_END); + if (patch->vf->read(patch->vf, &goodCrc32, 4) != 4) { + return false; + } + + uint32_t crc = fileCrc32(patch->vf, filesize + PATCH_CHECKSUM); + if (crc != goodCrc32) { + return false; + } + + patch->outputSize = _UPSOutputSize; + patch->applyPatch = _UPSApplyPatch; + return true; +} + +size_t _UPSOutputSize(struct Patch* patch, size_t inSize) { + UNUSED(inSize); + patch->vf->seek(patch->vf, 4, SEEK_SET); + if (_UPSDecodeLength(patch->vf) != inSize) { + return 0; + } + return _UPSDecodeLength(patch->vf); +} + +bool _UPSApplyPatch(struct Patch* patch, void* out, size_t outSize) { + // TODO: Input checksum + + size_t filesize = patch->vf->seek(patch->vf, 0, SEEK_END); + patch->vf->seek(patch->vf, 4, SEEK_SET); + _UPSDecodeLength(patch->vf); // Discard input size + if (_UPSDecodeLength(patch->vf) != outSize) { + return false; + } + + size_t offset = 0; + size_t alreadyRead = 0; + uint8_t* buf = out; + while (alreadyRead < filesize + IN_CHECKSUM) { + offset += _UPSDecodeLength(patch->vf); + uint8_t byte; + + while (true) { + if (patch->vf->read(patch->vf, &byte, 1) != 1) { + return false; + } + buf[offset] ^= byte; + ++offset; + if (!byte) { + break; + } + } + alreadyRead = patch->vf->seek(patch->vf, 0, SEEK_CUR); + } + + uint32_t goodCrc32; + patch->vf->seek(patch->vf, OUT_CHECKSUM, SEEK_END); + if (patch->vf->read(patch->vf, &goodCrc32, 4) != 4) { + return false; + } + + patch->vf->seek(patch->vf, 0, SEEK_SET); + if (crc32(out, outSize) != goodCrc32) { + return false; + } + return true; +} + +size_t _UPSDecodeLength(struct VFile* vf) { + size_t shift = 1; + size_t value = 0; + uint8_t byte; + while (true) { + if (vf->read(vf, &byte, 1) != 1) { + break; + } + value += (byte & 0x7f) * shift; + if (byte & 0x80) { + break; + } + shift <<= 7; + value += shift; + } + return value; +}
A src/util/patch-ups.h

@@ -0,0 +1,10 @@

+#ifndef PATCH_UPS_H +#define PATCH_UPS_H + +#include "common.h" + +struct Patch; + +bool loadPatchUPS(struct Patch* patch); + +#endif
A src/util/patch.c

@@ -0,0 +1,20 @@

+#include "util/patch.h" + +#include "util/patch-ips.h" +#include "util/patch-ups.h" + +bool loadPatch(struct VFile* vf, struct Patch* patch) { + patch->vf = vf; + + if (loadPatchIPS(patch)) { + return true; + } + + if (loadPatchUPS(patch)) { + return true; + } + + patch->outputSize = 0; + patch->applyPatch = 0; + return false; +}
A src/util/patch.h

@@ -0,0 +1,17 @@

+#ifndef PATCH_H +#define PATCH_H + +#include "common.h" + +struct VFile; + +struct Patch { + struct VFile* vf; + + size_t (*outputSize)(struct Patch* patch, size_t inSize); + bool (*applyPatch)(struct Patch* patch, void* out, size_t outSize); +}; + +bool loadPatch(struct VFile* vf, struct Patch* patch); + +#endif
M src/util/socket.hsrc/util/socket.h

@@ -1,18 +1,22 @@

#ifndef SOCKET_H #define SOCKET_H +#include "common.h" + #ifdef _WIN32 #include <winsock2.h> +#include <ws2tcpip.h> +#define SOCKET_FAILED(s) (s) == INVALID_SOCKET typedef SOCKET Socket; #else #include <fcntl.h> #include <netinet/in.h> #include <netinet/tcp.h> -#include <stdio.h> #include <sys/socket.h> -#include <unistd.h> +#define INVALID_SOCKET (-1) +#define SOCKET_FAILED(s) (s) < 0 typedef int Socket; #endif

@@ -33,7 +37,7 @@ }

static inline Socket SocketOpenTCP(int port, uint32_t bindAddress) { Socket sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); - if (sock < 0) { + if (SOCKET_FAILED(sock)) { return sock; }

@@ -52,6 +56,27 @@ }

return sock; } +static inline Socket SocketConnectTCP(int port, uint32_t destinationAddress) { + Socket sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (SOCKET_FAILED(sock)) { + return sock; + } + + struct sockaddr_in bindInfo = { + .sin_family = AF_INET, + .sin_port = htons(port), + .sin_addr = { + .s_addr = htonl(destinationAddress) + } + }; + int err = connect(sock, (const struct sockaddr*) &bindInfo, sizeof(struct sockaddr_in)); + if (err) { + close(sock); + return -1; + } + return sock; +} + static inline Socket SocketListen(Socket socket, int queueLength) { return listen(socket, queueLength); }

@@ -66,8 +91,8 @@ }

static inline int SocketSetBlocking(Socket socket, int blocking) { #ifdef _WIN32 - blocking = !blocking; - return ioctlsocket(socket, FIONBIO, &blocking) == NO_ERROR; + u_long unblocking = !blocking; + return ioctlsocket(socket, FIONBIO, &unblocking) == NO_ERROR; #else int flags = fcntl(socket, F_GETFL); if (flags == -1) {
M src/util/threading.hsrc/util/threading.h

@@ -1,6 +1,7 @@

#ifndef THREADING_H #define THREADING_H +#include "common.h" #ifdef USE_PTHREADS #include <pthread.h>

@@ -89,7 +90,7 @@ }

static inline int ConditionDeinit(Condition* cond) { // This is a no-op on Windows - (void)(cond); + UNUSED(cond); return 0; }
A src/util/vfs.c

@@ -0,0 +1,231 @@

+#include "util/vfs.h" + +#include <fcntl.h> +#include <dirent.h> + +#ifndef _WIN32 +#include <sys/mman.h> +#define PATH_SEP '/' +#else +#include <io.h> +#include <Windows.h> +#define PATH_SEP '\\' +#endif + +struct VFileFD { + struct VFile d; + int fd; +#ifdef _WIN32 + HANDLE hMap; +#endif +}; + +static bool _vfdClose(struct VFile* vf); +static off_t _vfdSeek(struct VFile* vf, off_t offset, int whence); +static ssize_t _vfdRead(struct VFile* vf, void* buffer, size_t size); +static ssize_t _vfdReadline(struct VFile* vf, char* buffer, size_t size); +static ssize_t _vfdWrite(struct VFile* vf, void* buffer, size_t size); +static void* _vfdMap(struct VFile* vf, size_t size, int flags); +static void _vfdUnmap(struct VFile* vf, void* memory, size_t size); +static void _vfdTruncate(struct VFile* vf, size_t size); + +static bool _vdClose(struct VDir* vd); +static void _vdRewind(struct VDir* vd); +static struct VDirEntry* _vdListNext(struct VDir* vd); +static struct VFile* _vdOpenFile(struct VDir* vd, const char* path, int mode); + +static const char* _vdeName(struct VDirEntry* vde); + +struct VFile* VFileOpen(const char* path, int flags) { + int fd = open(path, flags, 0666); + return VFileFromFD(fd); +} + +struct VFile* VFileFromFD(int fd) { + if (fd < 0) { + return 0; + } + + struct VFileFD* vfd = malloc(sizeof(struct VFileFD)); + if (!vfd) { + return 0; + } + + vfd->fd = fd; + vfd->d.close = _vfdClose; + vfd->d.seek = _vfdSeek; + vfd->d.read = _vfdRead; + vfd->d.readline = _vfdReadline; + vfd->d.write = _vfdWrite; + vfd->d.map = _vfdMap; + vfd->d.unmap = _vfdUnmap; + vfd->d.truncate = _vfdTruncate; + + return &vfd->d; +} + +bool _vfdClose(struct VFile* vf) { + struct VFileFD* vfd = (struct VFileFD*) vf; + if (close(vfd->fd) < 0) { + return false; + } + free(vfd); + return true; +} + +off_t _vfdSeek(struct VFile* vf, off_t offset, int whence) { + struct VFileFD* vfd = (struct VFileFD*) vf; + return lseek(vfd->fd, offset, whence); +} + +ssize_t _vfdRead(struct VFile* vf, void* buffer, size_t size) { + struct VFileFD* vfd = (struct VFileFD*) vf; + return read(vfd->fd, buffer, size); +} + +ssize_t _vfdReadline(struct VFile* vf, char* buffer, size_t size) { + struct VFileFD* vfd = (struct VFileFD*) vf; + size_t bytesRead = 0; + while (bytesRead < size - 1) { + size_t newRead = read(vfd->fd, &buffer[bytesRead], 1); + bytesRead += newRead; + if (!newRead || buffer[bytesRead] == '\n') { + break; + } + } + return buffer[bytesRead] = '\0'; +} + +ssize_t _vfdWrite(struct VFile* vf, void* buffer, size_t size) { + struct VFileFD* vfd = (struct VFileFD*) vf; + return write(vfd->fd, buffer, size); +} + +#ifndef _WIN32 +static void* _vfdMap(struct VFile* vf, size_t size, int flags) { + struct VFileFD* vfd = (struct VFileFD*) vf; + int mmapFlags = MAP_PRIVATE; + if (flags & MAP_WRITE) { + mmapFlags = MAP_SHARED; + } + return mmap(0, size, PROT_READ | PROT_WRITE, mmapFlags, vfd->fd, 0); +} + +static void _vfdUnmap(struct VFile* vf, void* memory, size_t size) { + UNUSED(vf); + munmap(memory, size); +} +#else +static void* _vfdMap(struct VFile* vf, size_t size, int flags) { + struct VFileFD* vfd = (struct VFileFD*) vf; + int createFlags = PAGE_WRITECOPY; + int mapFiles = FILE_MAP_COPY; + if (flags & MAP_WRITE) { + createFlags = PAGE_READWRITE; + mapFiles = FILE_MAP_WRITE; + } + size_t location = lseek(vfd->fd, 0, SEEK_CUR); + size_t fileSize = lseek(vfd->fd, 0, SEEK_END); + lseek(vfd->fd, location, SEEK_SET); + if (size > fileSize) { + size = fileSize; + } + vfd->hMap = CreateFileMapping((HANDLE) _get_osfhandle(vfd->fd), 0, createFlags, 0, size & 0xFFFFFFFF, 0); + return MapViewOfFile(hMap, mapFiles, 0, 0, size); +} + +static void _vfdUnmap(struct VFile* vf, void* memory, size_t size) { + UNUSED(size); + struct VFileFD* vfd = (struct VFileFD*) vf; + UnmapViewOfFile(memory); + CloseHandle(vfd->hMap); + vfd->hMap = 0; +} +#endif + +static void _vfdTruncate(struct VFile* vf, size_t size) { + struct VFileFD* vfd = (struct VFileFD*) vf; + ftruncate(vfd->fd, size); +} + +struct VDirEntryDE { + struct VDirEntry d; + struct dirent* ent; +}; + +struct VDirDE { + struct VDir d; + DIR* de; + struct VDirEntryDE vde; + char* path; +}; + +struct VDir* VDirOpen(const char* path) { + DIR* de = opendir(path); + if (!de) { + return 0; + } + + struct VDirDE* vd = malloc(sizeof(struct VDirDE)); + if (!vd) { + return 0; + } + + vd->d.close = _vdClose; + vd->d.rewind = _vdRewind; + vd->d.listNext = _vdListNext; + vd->d.openFile = _vdOpenFile; + vd->path = strdup(path); + vd->de = de; + + vd->vde.d.name = _vdeName; + + return &vd->d; +} + +bool _vdClose(struct VDir* vd) { + struct VDirDE* vdde = (struct VDirDE*) vd; + if (closedir(vdde->de) < 0) { + return false; + } + free(vdde->path); + free(vdde); + return true; +} + +void _vdRewind(struct VDir* vd) { + struct VDirDE* vdde = (struct VDirDE*) vd; + rewinddir(vdde->de); +} + +struct VDirEntry* _vdListNext(struct VDir* vd) { + struct VDirDE* vdde = (struct VDirDE*) vd; + vdde->vde.ent = readdir(vdde->de); + if (vdde->vde.ent) { + return &vdde->vde.d; + } + + return 0; +} + +struct VFile* _vdOpenFile(struct VDir* vd, const char* path, int mode) { + struct VDirDE* vdde = (struct VDirDE*) vd; + if (!path) { + return 0; + } + const char* dir = vdde->path; + char* combined = malloc(sizeof(char) * (strlen(path) + strlen(dir) + 2)); + sprintf(combined, "%s%c%s", dir, PATH_SEP, path); + + struct VFile* file = VFileOpen(combined, mode); + free(combined); + return file; +} + +const char* _vdeName(struct VDirEntry* vde) { + struct VDirEntryDE* vdede = (struct VDirEntryDE*) vde; + if (vdede->ent) { + return vdede->ent->d_name; + } + return 0; +}
A src/util/vfs.h

@@ -0,0 +1,42 @@

+#ifndef VFS_H +#define VFS_H + +#include "common.h" + +enum { + MAP_READ = 1, + MAP_WRITE = 2 +}; + +struct VFile { + bool (*close)(struct VFile* vf); + off_t (*seek)(struct VFile* vf, off_t offset, int whence); + ssize_t (*read)(struct VFile* vf, void* buffer, size_t size); + ssize_t (*readline)(struct VFile* vf, char* buffer, size_t size); + ssize_t (*write)(struct VFile* vf, void* buffer, size_t size); + void* (*map)(struct VFile* vf, size_t size, int flags); + void (*unmap)(struct VFile* vf, void* memory, size_t size); + void (*truncate)(struct VFile* vf, size_t size); +}; + +struct VDirEntry { + const char* (*name)(struct VDirEntry* vde); +}; + +struct VDir { + bool (*close)(struct VDir* vd); + void (*rewind)(struct VDir* vd); + struct VDirEntry* (*listNext)(struct VDir* vd); + struct VFile* (*openFile)(struct VDir* vd, const char* name, int mode); +}; + +struct VFile* VFileOpen(const char* path, int flags); +struct VFile* VFileFromFD(int fd); + +struct VDir* VDirOpen(const char* path); + +#ifdef ENABLE_LIBZIP +struct VDir* VDirOpenZip(const char* path, int flags); +#endif + +#endif
A src/util/vfs/vfs-zip.c

@@ -0,0 +1,304 @@

+#include "util/vfs.h" + +#ifdef ENABLE_LIBZIP +#include <zip.h> + + +enum { + BLOCK_SIZE = 1024 +}; + +struct VDirEntryZip { + struct VDirEntry d; + struct zip* z; + zip_int64_t index; +}; + +struct VDirZip { + struct VDir d; + struct zip* z; + struct VDirEntryZip dirent; +}; + +struct VFileZip { + struct VFile d; + struct zip_file* zf; + void* buffer; + size_t offset; + size_t bufferSize; + size_t readSize; + size_t fileSize; +}; + +static bool _vfzClose(struct VFile* vf); +static off_t _vfzSeek(struct VFile* vf, off_t offset, int whence); +static ssize_t _vfzRead(struct VFile* vf, void* buffer, size_t size); +static ssize_t _vfzReadline(struct VFile* vf, char* buffer, size_t size); +static ssize_t _vfzWrite(struct VFile* vf, void* buffer, size_t size); +static void* _vfzMap(struct VFile* vf, size_t size, int flags); +static void _vfzUnmap(struct VFile* vf, void* memory, size_t size); +static void _vfzTruncate(struct VFile* vf, size_t size); + +static bool _vdzClose(struct VDir* vd); +static void _vdzRewind(struct VDir* vd); +static struct VDirEntry* _vdzListNext(struct VDir* vd); +static struct VFile* _vdzOpenFile(struct VDir* vd, const char* path, int mode); + +static const char* _vdezName(struct VDirEntry* vde); + +struct VDir* VDirOpenZip(const char* path, int flags) { + int zflags = 0; + if (flags & O_CREAT) { + zflags |= ZIP_CREATE; + } + if (flags & O_EXCL) { + zflags |= ZIP_EXCL; + } + + struct zip* z = zip_open(path, zflags, 0); + if (!z) { + return 0; + } + struct VDirZip* vd = malloc(sizeof(struct VDirZip)); + + vd->d.close = _vdzClose; + vd->d.rewind = _vdzRewind; + vd->d.listNext = _vdzListNext; + vd->d.openFile = _vdzOpenFile; + vd->z = z; + + vd->dirent.d.name = _vdezName; + vd->dirent.index = -1; + vd->dirent.z = z; + + return &vd->d; +} + +bool _vfzClose(struct VFile* vf) { + struct VFileZip* vfz = (struct VFileZip*) vf; + if (zip_fclose(vfz->zf) < 0) { + return false; + } + free(vfz->buffer); + free(vfz); + return true; +} + +off_t _vfzSeek(struct VFile* vf, off_t offset, int whence) { + struct VFileZip* vfz = (struct VFileZip*) vf; + + size_t position; + switch (whence) { + case SEEK_SET: + position = offset; + break; + case SEEK_CUR: + if (offset < 0 && ((vfz->offset < (size_t) -offset) || (offset == INT_MIN))) { + return -1; + } + position = vfz->offset + offset; + break; + case SEEK_END: + if (offset < 0 && ((vfz->fileSize < (size_t) -offset) || (offset == INT_MIN))) { + return -1; + } + position = vfz->fileSize + offset; + break; + } + + if (position <= vfz->offset) { + vfz->offset = position; + return position; + } + + if (position <= vfz->fileSize) { + ssize_t read = vf->read(vf, 0, position - vfz->offset); + if (read < 0) { + return -1; + } + return vfz->offset; + } + + return -1; +} + +ssize_t _vfzRead(struct VFile* vf, void* buffer, size_t size) { + struct VFileZip* vfz = (struct VFileZip*) vf; + + size_t bytesRead = 0; + if (!vfz->buffer) { + vfz->bufferSize = BLOCK_SIZE; + vfz->buffer = malloc(BLOCK_SIZE); + } + + while (bytesRead < size) { + if (vfz->offset < vfz->readSize) { + size_t diff = vfz->readSize - vfz->offset; + void* start = &((uint8_t*) vfz->buffer)[vfz->offset]; + if (diff > size - bytesRead) { + diff = size - bytesRead; + } + if (buffer) { + void* bufferOffset = &((uint8_t*) buffer)[bytesRead]; + memcpy(bufferOffset, start, diff); + } + vfz->offset += diff; + bytesRead += diff; + if (diff == size) { + break; + } + } + // offset == readSize + if (vfz->readSize == vfz->bufferSize) { + vfz->bufferSize *= 2; + if (vfz->bufferSize > vfz->fileSize) { + vfz->bufferSize = vfz->fileSize; + } + vfz->buffer = realloc(vfz->buffer, vfz->bufferSize); + } + if (vfz->readSize < vfz->bufferSize) { + void* start = &((uint8_t*) vfz->buffer)[vfz->readSize]; + size_t toRead = vfz->bufferSize - vfz->readSize; + if (toRead > BLOCK_SIZE) { + toRead = BLOCK_SIZE; + } + ssize_t zipRead = zip_fread(vfz->zf, start, toRead); + if (zipRead < 0) { + if (bytesRead == 0) { + return -1; + } + break; + } + if (zipRead == 0) { + break; + } + vfz->readSize += zipRead; + } else { + break; + } + } + return bytesRead; +} + +ssize_t _vfzReadline(struct VFile* vf, char* buffer, size_t size) { + size_t bytesRead = 0; + while (bytesRead < size - 1) { + size_t newRead = vf->read(vf, &buffer[bytesRead], 1); + bytesRead += newRead; + if (!newRead || buffer[bytesRead] == '\n') { + break; + } + } + return buffer[bytesRead] = '\0'; +} + +ssize_t _vfzWrite(struct VFile* vf, void* buffer, size_t size) { + // TODO + UNUSED(vf); + UNUSED(buffer); + UNUSED(size); + return -1; +} + +void* _vfzMap(struct VFile* vf, size_t size, int flags) { + struct VFileZip* vfz = (struct VFileZip*) vf; + + UNUSED(flags); + if (size > vfz->readSize) { + vf->read(vf, 0, size - vfz->readSize); + } + return vfz->buffer; +} + +void _vfzUnmap(struct VFile* vf, void* memory, size_t size) { + UNUSED(vf); + UNUSED(memory); + UNUSED(size); +} + +void _vfzTruncate(struct VFile* vf, size_t size) { + // TODO + UNUSED(vf); + UNUSED(size); +} + +bool _vdzClose(struct VDir* vd) { + struct VDirZip* vdz = (struct VDirZip*) vd; + if (zip_close(vdz->z) < 0) { + return false; + } + free(vdz); + return true; +} + +void _vdzRewind(struct VDir* vd) { + struct VDirZip* vdz = (struct VDirZip*) vd; + vdz->dirent.index = -1; +} + +struct VDirEntry* _vdzListNext(struct VDir* vd) { + struct VDirZip* vdz = (struct VDirZip*) vd; + zip_int64_t maxIndex = zip_get_num_entries(vdz->z, 0); + if (maxIndex <= vdz->dirent.index + 1) { + return 0; + } + ++vdz->dirent.index; + return &vdz->dirent.d; +} + +struct VFile* _vdzOpenFile(struct VDir* vd, const char* path, int mode) { + UNUSED(mode); + // TODO: support truncating, appending and creating, and write + struct VDirZip* vdz = (struct VDirZip*) vd; + + if ((mode & O_RDWR) == O_RDWR) { + // libzip doesn't allow for random access, so read/write is impossible without + // reading the entire file first. This approach will be supported eventually. + return 0; + } + + if (mode & O_WRONLY) { + // Write support is not yet implemented. + return 0; + } + + struct zip_stat s; + if (zip_stat(vdz->z, path, 0, &s) < 0) { + return 0; + } + + struct zip_file* zf = zip_fopen(vdz->z, path, 0); + if (!zf) { + return 0; + } + + struct VFileZip* vfz = malloc(sizeof(struct VFileZip)); + vfz->zf = zf; + vfz->buffer = 0; + vfz->offset = 0; + vfz->bufferSize = 0; + vfz->readSize = 0; + vfz->fileSize = s.size; + + vfz->d.close = _vfzClose; + vfz->d.seek = _vfzSeek; + vfz->d.read = _vfzRead; + vfz->d.readline = _vfzReadline; + vfz->d.write = _vfzWrite; + vfz->d.map = _vfzMap; + vfz->d.unmap = _vfzUnmap; + vfz->d.truncate = _vfzTruncate; + + return &vfz->d; +} + +const char* _vdezName(struct VDirEntry* vde) { + struct VDirEntryZip* vdez = (struct VDirEntryZip*) vde; + struct zip_stat s; + if (zip_stat_index(vdez->z, vdez->index, 0, &s) < 0) { + return 0; + } + return s.name; +} + +#endif