mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'master' into qt
This commit is contained in:
commit
f62ccde49d
|
@ -1,2 +1,3 @@
|
|||
/build
|
||||
*~
|
||||
*.swp
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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, ¤tCycles); ARM_LOAD_POST_BODY;)
|
||||
DEFINE_LOAD_STORE_INSTRUCTION_ARM(LDRB, cpu->gprs[rd] = cpu->memory->loadU8(cpu->memory, address, ¤tCycles); ARM_LOAD_POST_BODY;)
|
||||
DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(LDRH, cpu->gprs[rd] = cpu->memory->loadU16(cpu->memory, address, ¤tCycles); ARM_LOAD_POST_BODY;)
|
||||
DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(LDRSB, cpu->gprs[rd] = cpu->memory->load8(cpu->memory, address, ¤tCycles); ARM_LOAD_POST_BODY;)
|
||||
DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(LDRSH, cpu->gprs[rd] = cpu->memory->load16(cpu->memory, address, ¤tCycles); ARM_LOAD_POST_BODY;)
|
||||
DEFINE_LOAD_STORE_INSTRUCTION_ARM(STR, cpu->memory->store32(cpu->memory, address, cpu->gprs[rd], ¤tCycles); ARM_STORE_POST_BODY;)
|
||||
DEFINE_LOAD_STORE_INSTRUCTION_ARM(STRB, cpu->memory->store8(cpu->memory, address, cpu->gprs[rd], ¤tCycles); ARM_STORE_POST_BODY;)
|
||||
DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(STRH, cpu->memory->store16(cpu->memory, address, cpu->gprs[rd], ¤tCycles); ARM_STORE_POST_BODY;)
|
||||
DEFINE_LOAD_STORE_INSTRUCTION_ARM(LDR, cpu->gprs[rd] = cpu->memory.load32(cpu, address, ¤tCycles); ARM_LOAD_POST_BODY;)
|
||||
DEFINE_LOAD_STORE_INSTRUCTION_ARM(LDRB, cpu->gprs[rd] = cpu->memory.loadU8(cpu, address, ¤tCycles); ARM_LOAD_POST_BODY;)
|
||||
DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(LDRH, cpu->gprs[rd] = cpu->memory.loadU16(cpu, address, ¤tCycles); ARM_LOAD_POST_BODY;)
|
||||
DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(LDRSB, cpu->gprs[rd] = cpu->memory.load8(cpu, address, ¤tCycles); ARM_LOAD_POST_BODY;)
|
||||
DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(LDRSH, cpu->gprs[rd] = cpu->memory.load16(cpu, address, ¤tCycles); ARM_LOAD_POST_BODY;)
|
||||
DEFINE_LOAD_STORE_INSTRUCTION_ARM(STR, cpu->memory.store32(cpu, address, cpu->gprs[rd], ¤tCycles); ARM_STORE_POST_BODY;)
|
||||
DEFINE_LOAD_STORE_INSTRUCTION_ARM(STRB, cpu->memory.store8(cpu, address, cpu->gprs[rd], ¤tCycles); ARM_STORE_POST_BODY;)
|
||||
DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(STRH, cpu->memory.store16(cpu, address, cpu->gprs[rd], ¤tCycles); 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, ¤tCycles);
|
||||
cpu->gprs[rd] = cpu->memory.loadU8(cpu, address, ¤tCycles);
|
||||
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, ¤tCycles);
|
||||
cpu->gprs[rd] = cpu->memory.load32(cpu, address, ¤tCycles);
|
||||
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], ¤tCycles);
|
||||
cpu->memory.store32(cpu, address, cpu->gprs[rd], ¤tCycles);
|
||||
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], ¤tCycles);
|
||||
cpu->memory.store8(cpu, address, cpu->gprs[rd], ¤tCycles);
|
||||
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], ¤tCycles);
|
||||
cpu->memory->store32(cpu->memory, cpu->gprs[rn], cpu->gprs[rm], ¤tCycles);
|
||||
int32_t d = cpu->memory.load32(cpu, cpu->gprs[rn], ¤tCycles);
|
||||
cpu->memory.store32(cpu, cpu->gprs[rn], cpu->gprs[rm], ¤tCycles);
|
||||
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], ¤tCycles);
|
||||
cpu->memory->store8(cpu->memory, cpu->gprs[rn], cpu->gprs[rm], ¤tCycles);
|
||||
int32_t d = cpu->memory.loadU8(cpu, cpu->gprs[rn], ¤tCycles);
|
||||
cpu->memory.store8(cpu, cpu->gprs[rn], cpu->gprs[rm], ¤tCycles);
|
||||
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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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, ¤tCycles))
|
||||
DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(LDRB1, cpu->gprs[rd] = cpu->memory->loadU8(cpu->memory, cpu->gprs[rm] + immediate, ¤tCycles))
|
||||
DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(LDRH1, cpu->gprs[rd] = cpu->memory->loadU16(cpu->memory, cpu->gprs[rm] + immediate * 2, ¤tCycles))
|
||||
DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(STR1, cpu->memory->store32(cpu->memory, cpu->gprs[rm] + immediate * 4, cpu->gprs[rd], ¤tCycles); THUMB_STORE_POST_BODY;)
|
||||
DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(STRB1, cpu->memory->store8(cpu->memory, cpu->gprs[rm] + immediate, cpu->gprs[rd], ¤tCycles); THUMB_STORE_POST_BODY;)
|
||||
DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(STRH1, cpu->memory->store16(cpu->memory, cpu->gprs[rm] + immediate * 2, cpu->gprs[rd], ¤tCycles); THUMB_STORE_POST_BODY;)
|
||||
DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(LDR1, cpu->gprs[rd] = cpu->memory.load32(cpu, cpu->gprs[rm] + immediate * 4, ¤tCycles); THUMB_LOAD_POST_BODY;)
|
||||
DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(LDRB1, cpu->gprs[rd] = cpu->memory.loadU8(cpu, cpu->gprs[rm] + immediate, ¤tCycles); THUMB_LOAD_POST_BODY;)
|
||||
DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(LDRH1, cpu->gprs[rd] = cpu->memory.loadU16(cpu, cpu->gprs[rm] + immediate * 2, ¤tCycles); THUMB_LOAD_POST_BODY;)
|
||||
DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(STR1, cpu->memory.store32(cpu, cpu->gprs[rm] + immediate * 4, cpu->gprs[rd], ¤tCycles); THUMB_STORE_POST_BODY;)
|
||||
DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(STRB1, cpu->memory.store8(cpu, cpu->gprs[rm] + immediate, cpu->gprs[rd], ¤tCycles); THUMB_STORE_POST_BODY;)
|
||||
DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(STRH1, cpu->memory.store16(cpu, cpu->gprs[rm] + immediate * 2, cpu->gprs[rd], ¤tCycles); 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, ¤tCycles))
|
||||
DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(LDR4, cpu->gprs[rd] = cpu->memory->load32(cpu->memory, cpu->gprs[ARM_SP] + immediate, ¤tCycles))
|
||||
DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(STR3, cpu->memory->store32(cpu->memory, cpu->gprs[ARM_SP] + immediate, cpu->gprs[rd], ¤tCycles); THUMB_STORE_POST_BODY;)
|
||||
DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(LDR3, cpu->gprs[rd] = cpu->memory.load32(cpu, (cpu->gprs[ARM_PC] & 0xFFFFFFFC) + immediate, ¤tCycles); THUMB_LOAD_POST_BODY;)
|
||||
DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(LDR4, cpu->gprs[rd] = cpu->memory.load32(cpu, cpu->gprs[ARM_SP] + immediate, ¤tCycles); THUMB_LOAD_POST_BODY;)
|
||||
DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(STR3, cpu->memory.store32(cpu, cpu->gprs[ARM_SP] + immediate, cpu->gprs[rd], ¤tCycles); 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], ¤tCycles))
|
||||
DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRB2, cpu->gprs[rd] = cpu->memory->loadU8(cpu->memory, cpu->gprs[rn] + cpu->gprs[rm], ¤tCycles))
|
||||
DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRH2, cpu->gprs[rd] = cpu->memory->loadU16(cpu->memory, cpu->gprs[rn] + cpu->gprs[rm], ¤tCycles))
|
||||
DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRSB, cpu->gprs[rd] = cpu->memory->load8(cpu->memory, cpu->gprs[rn] + cpu->gprs[rm], ¤tCycles))
|
||||
DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRSH, cpu->gprs[rd] = cpu->memory->load16(cpu->memory, cpu->gprs[rn] + cpu->gprs[rm], ¤tCycles))
|
||||
DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(STR2, cpu->memory->store32(cpu->memory, cpu->gprs[rn] + cpu->gprs[rm], cpu->gprs[rd], ¤tCycles); 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], ¤tCycles); 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], ¤tCycles); THUMB_STORE_POST_BODY;)
|
||||
DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDR2, cpu->gprs[rd] = cpu->memory.load32(cpu, cpu->gprs[rn] + cpu->gprs[rm], ¤tCycles); THUMB_LOAD_POST_BODY;)
|
||||
DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRB2, cpu->gprs[rd] = cpu->memory.loadU8(cpu, cpu->gprs[rn] + cpu->gprs[rm], ¤tCycles); THUMB_LOAD_POST_BODY;)
|
||||
DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRH2, cpu->gprs[rd] = cpu->memory.loadU16(cpu, cpu->gprs[rn] + cpu->gprs[rm], ¤tCycles); THUMB_LOAD_POST_BODY;)
|
||||
DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRSB, cpu->gprs[rd] = cpu->memory.load8(cpu, cpu->gprs[rn] + cpu->gprs[rm], ¤tCycles); THUMB_LOAD_POST_BODY;)
|
||||
DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRSH, cpu->gprs[rd] = cpu->memory.load16(cpu, cpu->gprs[rn] + cpu->gprs[rm], ¤tCycles); THUMB_LOAD_POST_BODY;)
|
||||
DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(STR2, cpu->memory.store32(cpu, cpu->gprs[rn] + cpu->gprs[rm], cpu->gprs[rd], ¤tCycles); THUMB_STORE_POST_BODY;)
|
||||
DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(STRB2, cpu->memory.store8(cpu, cpu->gprs[rn] + cpu->gprs[rm], cpu->gprs[rd], ¤tCycles); THUMB_STORE_POST_BODY;)
|
||||
DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(STRH2, cpu->memory.store16(cpu, cpu->gprs[rn] + cpu->gprs[rm], cpu->gprs[rd], ¤tCycles); 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)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef ISA_THUMB_H
|
||||
#define ISA_THUMB_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "common.h"
|
||||
|
||||
struct ARMCore;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#ifndef CLI_DEBUGGER_H
|
||||
#define CLI_DEBUGGER_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "debugger.h"
|
||||
|
||||
#include <histedit.h>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#ifndef MEMORY_DEBUGGER_H
|
||||
#define MEMORY_DEBUGGER_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "arm.h"
|
||||
|
||||
struct ARMDebugger;
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef GBA_SENSORS_H
|
||||
#define GBA_SENSORS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "common.h"
|
||||
|
||||
struct GBARotationSource {
|
||||
void (*sample)(struct GBARotationSource*);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef GBA_SIO_H
|
||||
#define GBA_SIO_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "common.h"
|
||||
|
||||
enum GBASIOMode {
|
||||
SIO_NORMAL_8 = 0,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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);
|
||||
|
|
263
src/gba/gba.c
263
src/gba/gba.c
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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, ...);
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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[];
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#ifndef VIDEO_GLSL_H
|
||||
#define VIDEO_GLSL_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "gba-video.h"
|
||||
|
||||
#include <pthread.h>
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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);
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef PATCH_IPS_H
|
||||
#define PATCH_IPS_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
struct Patch;
|
||||
|
||||
bool loadPatchIPS(struct Patch* patch);
|
||||
|
||||
#endif
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef PATCH_UPS_H
|
||||
#define PATCH_UPS_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
struct Patch;
|
||||
|
||||
bool loadPatchUPS(struct Patch* patch);
|
||||
|
||||
#endif
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue