Merge branch 'master' into qt

This commit is contained in:
Jeffrey Pfau 2014-07-20 16:28:12 -07:00
commit f62ccde49d
90 changed files with 6144 additions and 2177 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
/build
*~
*.swp

View File

@ -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 @@ if(USE_GDB_STUB)
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)

View File

@ -4,8 +4,6 @@
#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,19 +65,32 @@ static inline enum RegisterBank _ARMSelectBank(enum PrivilegeMode mode) {
}
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;
for (i = 0; i < 16; ++i) {
@ -112,8 +123,9 @@ void ARMReset(struct ARMCore* cpu) {
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 @@ void ARMRaiseIRQ(struct ARMCore* cpu) {
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 @@ void ARMRaiseSWI(struct ARMCore* cpu) {
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 @@ void ARMRaiseSWI(struct ARMCore* cpu) {
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 @@ void ARMRun(struct ARMCore* cpu) {
ARMStep(cpu);
}
if (cpu->cycles >= cpu->nextEvent) {
cpu->board->processEvents(cpu->board);
cpu->irqh.processEvents(cpu);
}
}

View File

@ -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 @@ union PSR {
};
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 @@ struct ARMCore {
int32_t cycles;
int32_t nextEvent;
int halted;
int32_t bankedRegisters[6][7];
int32_t bankedSPSRs[6];
@ -141,16 +137,18 @@ struct ARMCore {
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);

31
src/arm/common.h Normal file
View File

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

454
src/arm/decoder-arm.c Normal file
View File

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

20
src/arm/decoder-inlines.h Normal file
View File

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

325
src/arm/decoder-thumb.c Normal file
View File

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

349
src/arm/decoder.c Normal file
View File

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

195
src/arm/decoder.h Normal file
View File

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

330
src/arm/emitter-arm.h Normal file
View File

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

128
src/arm/emitter-inlines.h Normal file
View File

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

108
src/arm/emitter-thumb.h Normal file
View File

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

View File

@ -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 @@ static inline void _immediate(struct ARMCore* cpu, uint32_t opcode) {
#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 @@ static inline void _immediate(struct ARMCore* cpu, uint32_t opcode) {
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 @@ DEFINE_MULTIPLY_INSTRUCTION_ARM(UMULL,
// 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 @@ DEFINE_INSTRUCTION_ARM(MSRRI,
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)

View File

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

View File

@ -1,33 +1,10 @@
#ifndef ISA_INLINES_H
#define ISA_INLINES_H
#include "common.h"
#include "arm.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
#define ARM_COND_EQ (cpu->cpsr.z)
#define ARM_COND_NE (!cpu->cpsr.z)
#define ARM_COND_CS (cpu->cpsr.c)
@ -63,18 +40,18 @@
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 _ARMSetMode(struct ARMCore* cpu, enum ExecutionMode execution
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

View File

@ -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 @@
D = M - N; \
THUMB_SUBTRACTION_S(m, n, D)
#define THUMB_PREFETCH_CYCLES (1 + cpu->memory->activePrefetchCycles16)
#define THUMB_PREFETCH_CYCLES (1 + cpu->memory.activeSeqCycles16)
#define THUMB_LOAD_POST_BODY ++currentCycles;
#define THUMB_STORE_POST_BODY \
currentCycles += cpu->memory->activeNonseqCycles16 - cpu->memory->activePrefetchCycles16;
#define APPLY(F, ...) F(__VA_ARGS__)
#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 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__) \
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 @@
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 @@ DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(ASR1,
}
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 @@ DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(STRH1, cpu->memory->store16(cpu->memory, cp
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 @@ DEFINE_DATA_FORM_1_INSTRUCTION_THUMB(SUB, THUMB_SUBTRACTION(cpu->gprs[rd], cpu->
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 @@ DEFINE_DATA_FORM_2_INSTRUCTION_THUMB(SUB, THUMB_SUBTRACTION(cpu->gprs[rd], cpu->
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 @@ DEFINE_INSTRUCTION_WITH_HIGH_THUMB(MOV3,
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 @@ DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(ADD6, cpu->gprs[rd] = cpu->gprs[ARM_SP] + i
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 @@ DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(STRH2, cpu->memory->store16(cpu->memory, c
} \
} \
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 @@ DEFINE_INSTRUCTION_THUMB(BX,
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)

View File

@ -1,7 +1,7 @@
#ifndef ISA_THUMB_H
#define ISA_THUMB_H
#include <stdint.h>
#include "common.h"
struct ARMCore;

View File

@ -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 @@ static struct {
{ "continue", _continue },
{ "d", _clearBreakpoint },
{ "delete", _clearBreakpoint },
{ "dis", _disassemble },
{ "disasm", _disassemble },
{ "i", _printStatus },
{ "info", _printStatus },
{ "n", _next },
@ -88,13 +89,13 @@ static inline void _printPSR(union PSR psr) {
}
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 @@ static void _breakInto(struct CLIDebugger* debugger, struct DebugVector* dv) {
}
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 @@ static void _print(struct CLIDebugger* debugger, struct DebugVector* dv) {
}
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 @@ static void _printHex(struct CLIDebugger* debugger, struct DebugVector* dv) {
}
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 @@ static void _printStatus(struct CLIDebugger* debugger, struct DebugVector* dv) {
}
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 @@ static void _setBreakpoint(struct CLIDebugger* debugger, struct DebugVector* dv)
}
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 @@ static void _clearBreakpoint(struct CLIDebugger* debugger, struct DebugVector* d
}
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 @@ static void _setWatchpoint(struct CLIDebugger* debugger, struct DebugVector* dv)
}
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 @@ static void _DVFree(struct DebugVector* dv) {
}
}
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 @@ static int _parse(struct CLIDebugger* debugger, const char* line, size_t count)
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 _commandLine(struct ARMDebugger* debugger) {
}
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 void _reportEntry(struct ARMDebugger* debugger, enum DebuggerEntryReason
}
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 unsigned char _tabComplete(EditLine* elstate, int ch) {
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 @@ static void _cliDebuggerDeinit(struct ARMDebugger* debugger) {
}
void CLIDebuggerCreate(struct CLIDebugger* debugger) {
ARMDebuggerCreate(&debugger->d);
debugger->d.init = _cliDebuggerInit;
debugger->d.deinit = _cliDebuggerDeinit;
debugger->d.paused = _commandLine;

View File

@ -1,6 +1,8 @@
#ifndef CLI_DEBUGGER_H
#define CLI_DEBUGGER_H
#include "common.h"
#include "debugger.h"
#include <histedit.h>

View File

@ -4,7 +4,7 @@
#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 @@ static void _checkBreakpoints(struct ARMDebugger* debugger) {
}
}
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 ARMDebuggerClearBreakpoint(struct ARMDebugger* debugger, uint32_t address)
}
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;
}

View File

@ -1,8 +1,12 @@
#ifndef DEBUGGER_H
#define DEBUGGER_H
#include "common.h"
#include "arm.h"
const uint32_t ARM_DEBUGGER_ID;
enum DebuggerState {
DEBUGGER_PAUSED,
DEBUGGER_RUNNING,
@ -15,14 +19,6 @@ struct DebugBreakpoint {
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 @@ enum DebuggerLogLevel {
};
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 @@ struct ARMDebugger {
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);

View File

@ -2,7 +2,10 @@
#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 _gdbStubEntered(struct ARMDebugger* debugger, enum DebuggerEntryReas
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 _writeHostInfo(struct GDBStub* stub) {
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 @@ static void _step(struct GDBStub* stub, const char* message) {
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 @@ static void _readMemory(struct GDBStub* stub, const char* message) {
_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 @@ static void _readMemory(struct GDBStub* stub, const char* message) {
}
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 @@ static void _processQWriteCommand(struct GDBStub* stub, const char* message) {
}
static void _processVWriteCommand(struct GDBStub* stub, const char* message) {
(void) (message);
UNUSED(message);
stub->outgoing[0] = '\0';
_sendMessage(stub);
}
@ -284,7 +287,7 @@ static void _setBreakpoint(struct GDBStub* stub, const char* message) {
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 @@ size_t _parseGDBMessage(struct GDBStub* stub, const char* message) {
}
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 @@ void GDBStubCreate(struct GDBStub* stub) {
}
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 @@ cleanup:
}
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 GDBStubHangup(struct GDBStub* stub) {
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 @@ void GDBStubUpdate(struct GDBStub* stub) {
goto connectionLost;
}
}
while (1) {
while (true) {
ssize_t messageLen = SocketRecv(stub->connection, stub->line, GDB_STUB_MAX_LINE - 1);
if (messageLen == 0) {
goto connectionLost;

View File

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

View File

@ -4,81 +4,68 @@
#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); \
}
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)
#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); \
}
static int _checkWatchpoints(struct DebugBreakpoint* watchpoints, uint32_t address, int width) {
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 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;
}

View File

@ -1,6 +1,8 @@
#ifndef MEMORY_DEBUGGER_H
#define MEMORY_DEBUGGER_H
#include "common.h"
#include "arm.h"
struct ARMDebugger;

357
src/debugger/parser.c Normal file
View File

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

58
src/debugger/parser.h Normal file
View File

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

View File

@ -5,16 +5,13 @@
#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 @@ void GBAAudioInit(struct GBAAudio* audio) {
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 @@ void GBAAudioDeinit(struct GBAAudio* audio) {
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 @@ void GBAAudioSampleFIFO(struct GBAAudio* audio, int fifoId, int32_t cycles) {
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 @@ static void _updateEnvelope(struct GBAAudioEnvelope* envelope) {
}
}
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 @@ static int _updateSweep(struct GBAAudioChannel1* ch) {
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 @@ static int32_t _updateChannel3(struct GBAAudioChannel3* ch) {
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 @@ void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState*
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;
@ -729,6 +739,14 @@ void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState
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;
audio->nextSample = state->audio.nextSample;

View File

@ -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 @@ struct GBAStereoSample {
};
void GBAAudioInit(struct GBAAudio* audio);
void GBAAudioReset(struct GBAAudio* audio);
void GBAAudioDeinit(struct GBAAudio* audio);
int32_t GBAAudioProcessEvents(struct GBAAudio* audio, int32_t cycles);

View File

@ -4,129 +4,41 @@
#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 @@ static void _BgAffineSet(struct GBA* gba) {
// 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 @@ static void _ObjAffineSet(struct GBA* gba) {
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);
}
void GBASwi16(struct ARMBoard* board, int immediate) {
struct GBA* gba = ((struct GBABoard*) board)->p;
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 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 @@ void GBASwi16(struct ARMBoard* board, int immediate) {
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 @@ void GBASwi16(struct ARMBoard* board, int immediate) {
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 @@ void GBASwi16(struct ARMBoard* board, int immediate) {
}
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 @@ void GBASwi16(struct ARMBoard* board, int immediate) {
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 @@ void GBASwi16(struct ARMBoard* board, int 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 @@ uint32_t GBAChecksum(uint32_t* memory, size_t size) {
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 @@ static void _unLz77(struct GBAMemory* memory, uint32_t source, uint8_t* dest) {
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 @@ static void _unLz77(struct GBAMemory* memory, uint32_t source, uint8_t* dest) {
}
} 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 @@ static void _unHuffman(struct GBAMemory* memory, uint32_t source, uint32_t* dest
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 @@ static void _unHuffman(struct GBAMemory* memory, uint32_t source, uint32_t* dest
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 @@ 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 _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 @@ static void _unRl(struct GBAMemory* memory, uint32_t source, uint8_t* dest) {
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 @@ static void _unRl(struct GBAMemory* memory, uint32_t source, uint8_t* dest) {
blockheader++;
while (blockheader-- && remaining) {
--remaining;
*dPointer = memory->d.loadU8(&memory->d, sPointer++, 0);
*dPointer = cpu->memory.loadU8(cpu, sPointer++, 0);
++dPointer;
}
}

View File

@ -1,12 +1,12 @@
#ifndef GBA_BIOS_H
#define GBA_BIOS_H
#include "common.h"
#include "arm.h"
#include <string.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;

View File

@ -2,6 +2,7 @@
#include "gba-gpio.h"
#include "gba-sensors.h"
#include "gba-serialize.h"
#include <time.h>
@ -302,3 +303,25 @@ void _rumbleReadPins(struct GBACartridgeGPIO* gpio) {
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;
}

View File

@ -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 @@ enum GPIODevice {
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 @@ struct GBARTC {
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 GBACartridgeGPIO {
struct GBARTC rtc;
uint16_t gyroSample;
int gyroEdge;
bool gyroEdge;
};
void GBAGPIOInit(struct GBACartridgeGPIO* gpio, uint16_t* gpioBase);
@ -108,4 +109,8 @@ 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

View File

@ -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 @@ static const int _isSpecialRegister[REG_MAX >> 1] = {
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 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) {
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 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) {
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 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) {
// Interrupts and misc
case REG_WAITCNT:
GBAAdjustWaitstates(&gba->memory, value);
GBAAdjustWaitstates(gba, value);
break;
case REG_IE:
GBAWriteIE(gba, value);
@ -325,28 +327,28 @@ void GBAIOWrite32(struct GBA* gba, uint32_t address, uint32_t value) {
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 @@ void GBAIOWrite32(struct GBA* gba, uint32_t address, uint32_t value) {
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 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) {
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 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) {
// Write-only register
return 0;
case REG_DISPCNT:
case REG_DISPSTAT:
case REG_VCOUNT:
case REG_BG0CNT:
case REG_BG1CNT:
@ -457,6 +462,7 @@ void GBAIOSerialize(struct GBA* gba, struct GBASerializedState* state) {
}
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 @@ void GBAIODeserialize(struct GBA* gba, struct GBASerializedState* state) {
}
}
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]);
}
memcpy(state->timers, gba->timers, sizeof(gba->timers));
if (gba->timers[i].enable) {
gba->timersEnabled |= 1 << i;
}
}
GBAGPIODeserialize(&gba->memory.gpio, state);
}

View File

@ -1,6 +1,8 @@
#ifndef GBA_IO_H
#define GBA_IO_H
#include "common.h"
#include "gba.h"
enum GBAIORegisters {

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,13 @@
#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,
REGION_WORKING_RAM = 0x2,
@ -106,9 +106,6 @@ struct GBADMA {
};
struct GBAMemory {
struct ARMMemory d;
struct GBA* p;
uint32_t* bios;
uint32_t* wram;
uint32_t* iwram;
@ -121,12 +118,14 @@ struct GBAMemory {
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 @@ struct GBAMemory {
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 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 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 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);

108
src/gba/gba-rr.c Normal file
View File

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

46
src/gba/gba-rr.h Normal file
View File

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

View File

@ -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 @@ void GBASavedataInitFlash(struct GBASavedata* savedata) {
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 @@ void GBASavedataInitEEPROM(struct GBASavedata* savedata) {
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 @@ void GBASavedataInitSRAM(struct GBASavedata* savedata) {
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);
}
}

View File

@ -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 @@ enum {
struct GBASavedata {
enum SavedataType type;
uint8_t* data;
const char* filename;
enum SavedataCommand command;
int fd;
struct VFile* vf;
int readBitsRemaining;
int readAddress;
@ -68,11 +69,9 @@ struct GBASavedata {
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);
void GBASavedataInitSRAM(struct GBASavedata* savedata);

View File

@ -1,7 +1,7 @@
#ifndef GBA_SENSORS_H
#define GBA_SENSORS_H
#include <stdint.h>
#include "common.h"
struct GBARotationSource {
void (*sample)(struct GBARotationSource*);

View File

@ -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 @@ void GBADeserialize(struct GBA* gba, struct GBASerializedState* state) {
}
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 @@ void GBADeserialize(struct GBA* gba, struct GBASerializedState* 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) {

View File

@ -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 @@ const uint32_t GBA_SAVESTATE_MAGIC;
/* 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 @@ const uint32_t GBA_SAVESTATE_MAGIC;
* | 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 @@ const uint32_t GBA_SAVESTATE_MAGIC;
* | 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 @@ const uint32_t GBA_SAVESTATE_MAGIC;
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 @@ struct GBASerializedState {
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 @@ struct GBASerializedState {
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];
@ -223,13 +258,17 @@ struct GBASerializedState {
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(struct VFile* vf);
void GBAUnmapState(struct VFile* vf, struct GBASerializedState* state);
struct GBASerializedState* GBAMapState(int fd);
struct GBASerializedState* GBAAllocateState(void);
void GBADeallocateState(struct GBASerializedState* state);

View File

@ -2,8 +2,6 @@
#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 struct GBASIODriver* _lookupDriver(struct GBASIO* sio, enum GBASIOMode mo
}
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 @@ static void _switchMode(struct GBASIO* sio) {
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 GBASIODeinit(struct GBASIO* sio) {
}
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) {

View File

@ -1,7 +1,7 @@
#ifndef GBA_SIO_H
#define GBA_SIO_H
#include <stdint.h>
#include "common.h"
enum GBASIOMode {
SIO_NORMAL_8 = 0,

View File

@ -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 @@ static THREAD_ENTRY _GBAThreadRun(void* context) {
#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 @@ static THREAD_ENTRY _GBAThreadRun(void* context) {
#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 @@ static THREAD_ENTRY _GBAThreadRun(void* context) {
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 @@ static THREAD_ENTRY _GBAThreadRun(void* context) {
}
} 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 @@ static THREAD_ENTRY _GBAThreadRun(void* context) {
}
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 @@ int GBAThreadStart(struct GBAThread* threadContext) {
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 @@ int GBAThreadStart(struct GBAThread* threadContext) {
}
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);
@ -215,6 +305,14 @@ void GBAThreadEnd(struct GBAThread* threadContext) {
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) {
MutexLock(&threadContext->sync.videoFrameMutex);
threadContext->sync.videoFrameWait = 0;
@ -243,16 +341,53 @@ void GBAThreadJoin(struct GBAThread* threadContext) {
}
}
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);
_waitOnInterrupt(threadContext);
threadContext->savedState = threadContext->state;
threadContext->state = THREAD_INTERRUPTED;
_waitOnInterrupt(threadContext);
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 @@ void GBAThreadPause(struct GBAThread* threadContext) {
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 @@ void GBAThreadUnpause(struct GBAThread* threadContext) {
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 @@ int GBAThreadIsPaused(struct GBAThread* threadContext) {
}
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 @@ void GBAThreadTogglePause(struct GBAThread* threadContext) {
threadContext->debugger->state = DEBUGGER_EXITING;
}
threadContext->state = THREAD_PAUSED;
frameOn = 0;
frameOn = false;
}
MutexUnlock(&threadContext->stateMutex);
MutexLock(&threadContext->sync.videoFrameMutex);
@ -370,20 +505,20 @@ void GBASyncPostFrame(struct GBASync* sync) {
}
}
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 @@ void GBASyncWaitFrameEnd(struct GBASync* sync) {
MutexUnlock(&sync->videoFrameMutex);
}
int GBASyncDrawingFrame(struct GBASync* sync) {
bool GBASyncDrawingFrame(struct GBASync* sync) {
return sync->videoFrameSkip <= 0;
}

View File

@ -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 @@ typedef void (*ThreadCallback)(struct GBAThread* threadContext);
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 @@ struct GBAThread {
enum ThreadState savedState;
GBALogHandler logHandler;
int logLevel;
ThreadCallback startCallback;
ThreadCallback cleanCallback;
ThreadCallback frameCallback;
@ -68,9 +81,12 @@ struct GBAThread {
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);

View File

@ -2,14 +2,14 @@
#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 @@ static struct GBAVideoRenderer dummyRenderer = {
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 @@ void GBAVideoInit(struct GBAVideo* video) {
video->vcounterIRQ = 0;
video->vcountSetting = 0;
video->vcount = -1;
video->vcount = 0;
video->lastHblank = 0;
video->nextHblank = VIDEO_HDRAW_LENGTH;
@ -45,11 +49,18 @@ void GBAVideoInit(struct GBAVideo* video) {
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 @@ int32_t GBAVideoProcessEvents(struct GBAVideo* video, int32_t cycles) {
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 @@ int32_t GBAVideoProcessEvents(struct GBAVideo* video, int32_t cycles) {
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 @@ int32_t GBAVideoProcessEvents(struct GBAVideo* video, int32_t cycles) {
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 @@ int32_t GBAVideoProcessEvents(struct GBAVideo* video, int32_t cycles) {
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 @@ void GBAVideoWriteDISPSTAT(struct GBAVideo* video, uint16_t value) {
}
}
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* stat
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];

View File

@ -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 @@ union GBARegisterBGCNT {
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 @@ struct GBAVideo {
};
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);

View File

@ -2,19 +2,19 @@
#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 @@ static const struct GBACartridgeOverride _overrides[] = {
{ { 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 @@ void GBAInit(struct GBA* gba) {
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 @@ static int32_t GBATimersProcessEvents(struct GBA* gba, int32_t cycles) {
}
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 @@ void GBALoadBIOS(struct GBA* gba, int fd) {
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 @@ void GBATimerWriteTMCNT_HI(struct GBA* gba, int timer, uint16_t control) {
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 @@ void GBATimerWriteTMCNT_HI(struct GBA* gba, int timer, uint16_t control) {
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 @@ void GBAWriteIE(struct GBA* gba, uint16_t value) {
}
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;
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->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 @@ void GBALog(struct GBA* gba, enum GBALogLevel level, const char* format, ...) {
}
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 @@ void GBADebuggerLogShim(struct ARMDebugger* debugger, enum DebuggerLogLevel leve
}
va_list args;
va_start(args, format);
_GBAVLog(gbaBoard ? gbaBoard->p : 0, gbaLevel, format, args);
_GBAVLog(gba, gbaLevel, format, args);
va_end(args);
}
void GBAHitStub(struct ARMBoard* board, uint32_t opcode) {
struct GBABoard* gbaBoard = (struct GBABoard*) board;
enum GBALogLevel level = GBA_LOG_FATAL;
if (gbaBoard->p->debugger) {
level = GBA_LOG_STUB;
ARMDebuggerEnter(gbaBoard->p->debugger, DEBUGGER_ENTER_ILLEGAL_OP);
bool GBAIsROM(struct VFile* vf) {
if (vf->seek(vf, 4, SEEK_SET) < 0) {
return false;
}
GBALog(gbaBoard->p, level, "Stub opcode: %08x", opcode);
uint64_t signature;
if (vf->read(vf, &signature, sizeof(signature)) != sizeof(signature)) {
return false;
}
return signature == GBA_ROM_MAGIC;
}
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 GBAHitStub(struct ARMCore* cpu, uint32_t opcode) {
struct GBA* gba = (struct GBA*) cpu->master;
enum GBALogLevel level = GBA_LOG_FATAL;
if (gba->debugger) {
level = GBA_LOG_STUB;
ARMDebuggerEnter(gba->debugger, DEBUGGER_ENTER_ILLEGAL_OP);
}
GBALog(gba, level, "Stub opcode: %08x", opcode);
}
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);
}
}

View File

@ -1,16 +1,16 @@
#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;
enum GBAIRQ {
@ -36,12 +36,12 @@ enum GBAError {
};
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 @@ enum GBAKey {
GBA_KEY_NONE = -1
};
struct GBARotationSource;
struct GBA;
struct GBARotationSource;
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 @@ struct GBA {
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 @@ struct GBACartridge {
// 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 GBATimerWriteTMCNT_HI(struct GBA* gba, int timer, uint16_t value);
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, ...);

View File

@ -2,23 +2,49 @@
#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
};

View File

@ -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[];

View File

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

View File

@ -1,6 +1,8 @@
#ifndef VIDEO_GLSL_H
#define VIDEO_GLSL_H
#include "common.h"
#include "gba-video.h"
#include <pthread.h>

View File

@ -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 @@ static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer) {
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 @@ static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRender
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 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re
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 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re
} \
\
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 @@ static void _drawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer, stru
#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 @@ static void _drawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer, stru
#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 @@ static int _preprocessSprite(struct GBAVideoSoftwareRenderer* renderer, struct G
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 @@ static int _preprocessTransformedSprite(struct GBAVideoSoftwareRenderer* rendere
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) {

View File

@ -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 @@ enum {
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 @@ union WindowRegion {
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 @@ union WindowControl {
struct Window {
uint8_t endX;
union WindowControl control;
struct WindowControl control;
};
struct GBAVideoSoftwareRenderer {
@ -146,13 +145,13 @@ struct GBAVideoSoftwareRenderer {
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];

214
src/platform/commandline.c Normal file
View File

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

View File

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

View File

@ -15,7 +15,6 @@
#include <errno.h>
#include <signal.h>
#include <sys/time.h>
#include <unistd.h>
static int _GBASDLInit(void);
static void _GBASDLDeinit(void);

View File

@ -2,56 +2,71 @@
#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 @@ int main(int argc, char** argv) {
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 @@ static void _GBAPerfRunloop(struct GBAThread* context, int* frames) {
}
}
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;
}
}

View File

@ -1,17 +1,10 @@
#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) {
munmap(memory, size);

View File

@ -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 (SDL_VERSION EQUAL "2")
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})

View File

@ -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 <malloc.h>
#include <signal.h>
#include <sys/time.h>
#include <unistd.h>
struct GBAVideoEGLRenderer {
struct GBAVideoSoftwareRenderer d;
@ -50,7 +49,9 @@ static const char* _fragmentShader =
"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 @@ int main(int argc, char** argv) {
return 1;
}
struct GBAThread context;
struct GBAVideoEGLRenderer renderer;
if (!_GBAEGLInit(&renderer)) {
@ -84,16 +84,19 @@ int main(int argc, char** argv) {
}
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);

View File

@ -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 @@
#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 @@ static const GLint _glTexCoords[] = {
};
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;
}
struct GLSoftwareRenderer renderer;
GBAVideoSoftwareRendererCreate(&renderer.d);
renderer.viewportWidth = 240;
renderer.viewportHeight = 160;
if (!_GBASDLInit(&renderer)) {
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;
}
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 @@ static int _GBASDLInit(struct GLSoftwareRenderer* renderer) {
#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 @@ static void _GBASDLRunloop(struct GBAThread* context, struct GLSoftwareRenderer*
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 @@ static void _GBASDLRunloop(struct GBAThread* context, struct GLSoftwareRenderer*
#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
}
}
}

View File

@ -8,10 +8,10 @@
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 @@ int GBASDLInitAudio(struct GBASDLAudio* context) {
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);

View File

@ -1,6 +1,8 @@
#ifndef SDL_AUDIO_H
#define SDL_AUDIO_H
#include "common.h"
#include <SDL.h>
struct GBASDLAudio {
@ -11,7 +13,7 @@ struct GBASDLAudio {
struct GBAAudio* audio;
};
int GBASDLInitAudio(struct GBASDLAudio* context);
bool GBASDLInitAudio(struct GBASDLAudio* context);
void GBASDLDeinitAudio(struct GBASDLAudio* context);
#endif

View File

@ -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 @@
#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 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents
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 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents
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 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents
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 @@ static void _GBASDLHandleJoyHat(struct GBAThread* context, const struct SDL_JoyH
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 @@ void GBASDLHandleEvent(struct GBAThread* context, struct GBASDLEvents* sdlContex
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);

View File

@ -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 @@ struct GBASDLEvents {
#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);

View File

@ -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.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) {
return 1;
}
struct GBAThread context;
struct SoftwareRenderer renderer;
GBAVideoSoftwareRendererCreate(&renderer.d);
if (!_GBASDLInit(&renderer)) {
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;
}
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;
renderer.viewportWidth = graphicsOpts.width;
renderer.viewportHeight = graphicsOpts.height;
if (!_GBASDLInit(&renderer)) {
freeOptions(&opts);
return 1;
}
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;
renderer.d.outputBufferStride = surface->pitch / 2;
#else
renderer.d.outputBufferStride = surface->pitch / 4;
renderer.d.outputBufferStride = surface->pitch / 4;
#endif
} else {
#ifdef COLOR_16_BIT
renderer.d.outputBuffer = malloc(240 * 160 * 2);
#else
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 @@ static int _GBASDLInit(struct SoftwareRenderer* renderer) {
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);
}
}
}

View File

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

94
src/util/arm-algo.S Normal file
View File

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

View File

@ -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 @@ static int _checkIntegrity(struct CircleBuffer* buffer) {
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) {
@ -35,6 +30,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) {
int8_t* data = buffer->writePtr;
if (buffer->size + sizeof(int8_t) > buffer->capacity) {
@ -169,3 +170,22 @@ int CircleBufferRead(struct CircleBuffer* buffer, void* output, size_t length) {
#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;
}

View File

@ -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 @@ struct CircleBuffer {
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

133
src/util/crc32.c Normal file
View File

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

13
src/util/crc32.h Normal file
View File

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

View File

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

86
src/util/patch-ips.c Normal file
View File

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

10
src/util/patch-ips.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef PATCH_IPS_H
#define PATCH_IPS_H
#include "common.h"
struct Patch;
bool loadPatchIPS(struct Patch* patch);
#endif

115
src/util/patch-ups.c Normal file
View File

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

10
src/util/patch-ups.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef PATCH_UPS_H
#define PATCH_UPS_H
#include "common.h"
struct Patch;
bool loadPatchUPS(struct Patch* patch);
#endif

20
src/util/patch.c Normal file
View File

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

17
src/util/patch.h Normal file
View File

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

View File

@ -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 ssize_t SocketRecv(Socket socket, void* buffer, size_t size) {
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 @@ static inline Socket SocketOpenTCP(int port, uint32_t bindAddress) {
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 SocketClose(Socket socket) {
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) {

View File

@ -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 ConditionInit(Condition* cond) {
static inline int ConditionDeinit(Condition* cond) {
// This is a no-op on Windows
(void)(cond);
UNUSED(cond);
return 0;
}

231
src/util/vfs.c Normal file
View File

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

42
src/util/vfs.h Normal file
View File

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

304
src/util/vfs/vfs-zip.c Normal file
View File

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