mirror of https://github.com/mgba-emu/mgba.git
commit
6560db2ef5
|
@ -6,9 +6,11 @@ set(CMAKE_C_FLAGS_RELEASE "-O3 -Wall -Wextra -std=gnu99")
|
|||
set(USE_CLI_DEBUGGER ON CACHE BOOL "Whether or not to enable the CLI-mode ARM debugger")
|
||||
set(USE_GDB_STUB ON CACHE BOOL "Whether or not to enable the GDB stub ARM debugger")
|
||||
set(USE_FFMPEG ON CACHE BOOL "Whether or not to enable FFmpeg support")
|
||||
set(USE_PNG ON CACHE BOOL "Whether or not to enable PNG support")
|
||||
set(USE_LIBZIP ON CACHE BOOL "Whether or not to enable ZIP support")
|
||||
set(BUILD_QT ON CACHE BOOL "Build Qt frontend")
|
||||
set(BUILD_SDL ON CACHE BOOL "Build SDL frontend")
|
||||
set(BUILD_PERF ON CACHE BOOL "Build performance profiling tool")
|
||||
set(BUILD_PERF OFF CACHE BOOL "Build performance profiling tool")
|
||||
file(GLOB ARM_SRC ${CMAKE_SOURCE_DIR}/src/arm/*.c)
|
||||
file(GLOB GBA_SRC ${CMAKE_SOURCE_DIR}/src/gba/*.c)
|
||||
file(GLOB UTIL_SRC ${CMAKE_SOURCE_DIR}/src/util/*.[cS])
|
||||
|
@ -22,12 +24,28 @@ include_directories(${CMAKE_SOURCE_DIR}/src/arm)
|
|||
include_directories(${CMAKE_SOURCE_DIR}/src/gba)
|
||||
include_directories(${CMAKE_SOURCE_DIR}/src)
|
||||
|
||||
# Function definitions
|
||||
include(FindPkgConfig)
|
||||
function(find_feature FEATURE_NAME FEATURE_REQUIRES)
|
||||
foreach(REQUIRE ${FEATURE_REQUIRES})
|
||||
find_package(${REQUIRE} QUIET)
|
||||
pkg_search_module(${REQUIRE} ${REQUIRE})
|
||||
if (NOT ${REQUIRE}_FOUND)
|
||||
message(WARNING "Requested module ${REQUIRE} missing for feature ${FEATURE_NAME}. Feature disabled.")
|
||||
set(${FEATURE_NAME} OFF PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
# Version information
|
||||
set(LIB_VERSION_MAJOR 0)
|
||||
set(LIB_VERSION_MINOR 1)
|
||||
set(LIB_VERSION_PATCH 0)
|
||||
set(LIB_VERSION_ABI 0.1)
|
||||
set(LIB_VERSION_STRING ${LIB_VERSION_MAJOR}.${LIB_VERSION_MINOR}.${LIB_VERSION_PATCH})
|
||||
|
||||
# Advanced settings
|
||||
set(BUILD_PGO CACHE BOOL "Build with profiling-guided optimization")
|
||||
set(PGO_STAGE_2 CACHE BOOL "Rebuild for profiling-guided optimization after profiles have been generated")
|
||||
set(PGO_DIR "/tmp/gba-pgo/" CACHE PATH "Profiling-guided optimization profiles path")
|
||||
|
@ -47,16 +65,13 @@ endif()
|
|||
|
||||
add_definitions(-DBINARY_NAME="${BINARY_NAME}" -DPROJECT_NAME="${PROJECT_NAME}")
|
||||
|
||||
include(FindPkgConfig)
|
||||
pkg_search_module(LIBZIP libzip)
|
||||
if(LIBZIP_FOUND)
|
||||
include_directories(${LIBZIP_INCLUDE_DIRS})
|
||||
list(APPEND DEPENDENCY_LIB ${LIBZIP_LIBRARIES})
|
||||
add_definitions(-DENABLE_LIBZIP)
|
||||
else()
|
||||
message(WARNING "Could not find libzip for archive support")
|
||||
endif()
|
||||
# Feature dependencies
|
||||
find_feature(USE_CLI_DEBUGGER "libedit")
|
||||
find_feature(USE_FFMPEG "libavcodec;libavformat;libavutil")
|
||||
find_feature(USE_PNG "PNG;ZLIB")
|
||||
find_feature(USE_LIBZIP "libzip")
|
||||
|
||||
# Platform support
|
||||
if(WIN32)
|
||||
add_definitions(-D_WIN32_WINNT=0x0600)
|
||||
list(APPEND OS_LIB Ws2_32)
|
||||
|
@ -76,19 +91,14 @@ if(BUILD_BBB OR BUILD_RASPI)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
# Features
|
||||
set(DEBUGGER_SRC ${CMAKE_SOURCE_DIR}/src/debugger/debugger.c ${CMAKE_SOURCE_DIR}/src/debugger/memory-debugger.c)
|
||||
|
||||
if(USE_CLI_DEBUGGER)
|
||||
pkg_search_module(EDIT libedit)
|
||||
if(EDIT_FOUND)
|
||||
add_definitions(-DUSE_CLI_DEBUGGER)
|
||||
list(APPEND DEBUGGER_SRC ${CMAKE_SOURCE_DIR}/src/debugger/cli-debugger.c)
|
||||
list(APPEND DEBUGGER_SRC ${CMAKE_SOURCE_DIR}/src/debugger/parser.c)
|
||||
set(DEBUGGER_LIB ${EDIT_LIBRARIES})
|
||||
else()
|
||||
message(WARNING "Could not find libedit for CLI debugger support")
|
||||
set(USE_CLI_DEBUGGER OFF)
|
||||
endif()
|
||||
add_definitions(-DUSE_CLI_DEBUGGER)
|
||||
list(APPEND DEBUGGER_SRC ${CMAKE_SOURCE_DIR}/src/debugger/cli-debugger.c)
|
||||
list(APPEND DEBUGGER_SRC ${CMAKE_SOURCE_DIR}/src/debugger/parser.c)
|
||||
set(DEBUGGER_LIB ${EDIT_LIBRARIES})
|
||||
else()
|
||||
set(DEBUGGER_LIB "")
|
||||
endif()
|
||||
|
@ -100,18 +110,29 @@ endif()
|
|||
source_group("ARM debugger" FILES ${DEBUGGER_SRC})
|
||||
|
||||
if(USE_FFMPEG)
|
||||
pkg_search_module(LIBAVCODEC libavcodec REQUIRED)
|
||||
pkg_search_module(LIBAVFORMAT libavformat REQUIRED)
|
||||
pkg_search_module(LIBAVUTIL libavutil REQUIRED)
|
||||
pkg_search_module(LIBAVCODEC libavcodec)
|
||||
pkg_search_module(LIBAVFORMAT libavcodec;libavformat;libavutil)
|
||||
pkg_search_module(LIBAVUTIL libavutil)
|
||||
add_definitions(-DUSE_FFMPEG)
|
||||
list(APPEND UTIL_SRC "${CMAKE_SOURCE_DIR}/src/platform/ffmpeg/ffmpeg-encoder.c")
|
||||
list(APPEND DEPENDENCY_LIB ${LIBAVCODEC_LIBRARIES} ${LIBAVFORMAT_LIBRARIES} ${LIBAVUTIL_LIBRARIES})
|
||||
endif()
|
||||
|
||||
find_package(PNG REQUIRED)
|
||||
find_package(ZLIB REQUIRED)
|
||||
list(APPEND DEPENDENCY_LIB ${PNG_LIBRARIES} ${ZLIB_LIBRARIES})
|
||||
if(USE_PNG)
|
||||
find_package(PNG)
|
||||
find_package(ZLIB)
|
||||
add_definitions(-DUSE_PNG)
|
||||
include_directories(${PNG_PNG_INCLUDE_DIR})
|
||||
list(APPEND DEPENDENCY_LIB ${PNG_LIBRARIES} ${ZLIB_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(USE_LIBZIP)
|
||||
include_directories(${LIBZIP_INCLUDE_DIRS})
|
||||
list(APPEND DEPENDENCY_LIB ${LIBZIP_LIBRARIES})
|
||||
add_definitions(-DENABLE_LIBZIP)
|
||||
endif()
|
||||
|
||||
# Binaries
|
||||
add_library(${BINARY_NAME} SHARED ${ARM_SRC} ${GBA_SRC} ${DEBUGGER_SRC} ${RENDERER_SRC} ${UTIL_SRC} ${VFS_SRC} ${OS_SRC})
|
||||
target_link_libraries(${BINARY_NAME} m ${DEBUGGER_LIB} ${OS_LIB} ${DEPENDENCY_LIB})
|
||||
install(TARGETS ${BINARY_NAME} DESTINATION lib)
|
||||
|
@ -122,6 +143,10 @@ if(BUILD_SDL)
|
|||
add_subdirectory(${CMAKE_SOURCE_DIR}/src/platform/sdl ${CMAKE_BINARY_DIR}/sdl)
|
||||
endif()
|
||||
|
||||
if(BUILD_QT)
|
||||
add_subdirectory(${CMAKE_SOURCE_DIR}/src/platform/qt ${CMAKE_BINARY_DIR}/qt)
|
||||
endif()
|
||||
|
||||
if(BUILD_PERF)
|
||||
set(PERF_SRC ${CMAKE_SOURCE_DIR}/src/platform/perf-main.c)
|
||||
if(UNIX AND NOT APPLE)
|
||||
|
@ -133,6 +158,14 @@ if(BUILD_PERF)
|
|||
install(TARGETS ${BINARY_NAME}-perf DESTINATION bin)
|
||||
endif()
|
||||
|
||||
if(BUILD_QT)
|
||||
add_subdirectory(${CMAKE_SOURCE_DIR}/src/platform/qt ${CMAKE_BINARY_DIR}/qt)
|
||||
endif()
|
||||
# Summaries
|
||||
message(STATUS "Feature summary:")
|
||||
message(STATUS " CLI debugger: ${USE_CLI_DEBUGGER}")
|
||||
message(STATUS " GDB stub: ${USE_GDB_STUB}")
|
||||
message(STATUS " Video recording: ${USE_FFMPEG}")
|
||||
message(STATUS " Screenshot/advanced savestate support: ${USE_PNG}")
|
||||
message(STATUS " ZIP support: ${USE_LIBZIP}")
|
||||
message(STATUS "Frontend summary:")
|
||||
message(STATUS " Qt: ${BUILD_QT}")
|
||||
message(STATUS " SDL (${SDL_VERSION}): ${BUILD_SDL}")
|
||||
message(STATUS " Profiling: ${BUILD_PERF}")
|
||||
|
|
|
@ -117,7 +117,6 @@ void ARMReset(struct ARMCore* cpu) {
|
|||
cpu->executionMode = MODE_THUMB;
|
||||
_ARMSetMode(cpu, MODE_ARM);
|
||||
|
||||
cpu->currentPC = 0;
|
||||
int currentCycles = 0;
|
||||
ARM_WRITE_PC;
|
||||
|
||||
|
@ -142,7 +141,9 @@ void ARMRaiseIRQ(struct ARMCore* cpu) {
|
|||
ARMSetPrivilegeMode(cpu, MODE_IRQ);
|
||||
cpu->cpsr.priv = MODE_IRQ;
|
||||
cpu->gprs[ARM_LR] = cpu->gprs[ARM_PC] - instructionWidth + WORD_SIZE_ARM;
|
||||
cpu->gprs[ARM_PC] = BASE_IRQ + WORD_SIZE_ARM;
|
||||
cpu->gprs[ARM_PC] = BASE_IRQ;
|
||||
int currentCycles = 0;
|
||||
ARM_WRITE_PC;
|
||||
cpu->memory.setActiveRegion(cpu, cpu->gprs[ARM_PC]);
|
||||
_ARMSetMode(cpu, MODE_ARM);
|
||||
cpu->spsr = cpsr;
|
||||
|
@ -160,7 +161,9 @@ void ARMRaiseSWI(struct ARMCore* cpu) {
|
|||
ARMSetPrivilegeMode(cpu, MODE_SUPERVISOR);
|
||||
cpu->cpsr.priv = MODE_SUPERVISOR;
|
||||
cpu->gprs[ARM_LR] = cpu->gprs[ARM_PC] - instructionWidth;
|
||||
cpu->gprs[ARM_PC] = BASE_SWI + WORD_SIZE_ARM;
|
||||
cpu->gprs[ARM_PC] = BASE_SWI;
|
||||
int currentCycles = 0;
|
||||
ARM_WRITE_PC;
|
||||
cpu->memory.setActiveRegion(cpu, cpu->gprs[ARM_PC]);
|
||||
_ARMSetMode(cpu, MODE_ARM);
|
||||
cpu->spsr = cpsr;
|
||||
|
@ -168,17 +171,12 @@ 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);
|
||||
uint32_t opcode = cpu->prefetch;
|
||||
LOAD_32(cpu->prefetch, cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion);
|
||||
cpu->gprs[ARM_PC] += WORD_SIZE_ARM;
|
||||
|
||||
int condition = opcode >> 28;
|
||||
if (condition == 0xE) {
|
||||
ARMInstruction instruction = _armTable[((opcode >> 16) & 0xFF0) | ((opcode >> 4) & 0x00F)];
|
||||
instruction(cpu, opcode);
|
||||
return;
|
||||
} else {
|
||||
unsigned condition = opcode >> 28;
|
||||
if (condition != 0xE) {
|
||||
switch (condition) {
|
||||
case 0x0:
|
||||
if (!ARM_COND_EQ) {
|
||||
|
@ -273,10 +271,9 @@ static inline void ARMStep(struct ARMCore* cpu) {
|
|||
}
|
||||
|
||||
static inline void ThumbStep(struct ARMCore* cpu) {
|
||||
cpu->currentPC = cpu->gprs[ARM_PC] - WORD_SIZE_THUMB;
|
||||
uint32_t opcode = cpu->prefetch;
|
||||
LOAD_16(cpu->prefetch, cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion);
|
||||
cpu->gprs[ARM_PC] += WORD_SIZE_THUMB;
|
||||
uint16_t opcode;
|
||||
LOAD_16(opcode, cpu->currentPC & cpu->memory.activeMask, cpu->memory.activeRegion);
|
||||
ThumbInstruction instruction = _thumbTable[opcode >> 6];
|
||||
instruction(cpu, opcode);
|
||||
}
|
||||
|
@ -291,3 +288,14 @@ void ARMRun(struct ARMCore* cpu) {
|
|||
cpu->irqh.processEvents(cpu);
|
||||
}
|
||||
}
|
||||
|
||||
void ARMRunLoop(struct ARMCore* cpu) {
|
||||
while (cpu->cycles < cpu->nextEvent) {
|
||||
if (cpu->executionMode == MODE_THUMB) {
|
||||
ThumbStep(cpu);
|
||||
} else {
|
||||
ARMStep(cpu);
|
||||
}
|
||||
}
|
||||
cpu->irqh.processEvents(cpu);
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ struct ARMCore;
|
|||
|
||||
union PSR {
|
||||
struct {
|
||||
#ifdef __POWERPC__
|
||||
#if defined(__POWERPC__) || defined(__PPC__)
|
||||
unsigned n : 1;
|
||||
unsigned z : 1;
|
||||
unsigned c : 1;
|
||||
|
@ -133,7 +133,7 @@ struct ARMCore {
|
|||
int32_t shifterOperand;
|
||||
int32_t shifterCarryOut;
|
||||
|
||||
uint32_t currentPC;
|
||||
uint32_t prefetch;
|
||||
enum ExecutionMode executionMode;
|
||||
enum PrivilegeMode privilegeMode;
|
||||
|
||||
|
@ -156,5 +156,6 @@ void ARMRaiseIRQ(struct ARMCore*);
|
|||
void ARMRaiseSWI(struct ARMCore*);
|
||||
|
||||
void ARMRun(struct ARMCore* cpu);
|
||||
void ARMRunLoop(struct ARMCore* cpu);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -16,11 +16,30 @@
|
|||
|
||||
#define UNUSED(V) (void)(V)
|
||||
|
||||
#ifdef __POWERPC__
|
||||
#define LOAD_32(DEST, ADDR, ARR) asm("lwbrx %0, %1, %2" : "=r"(DEST) : "r"(ADDR), "r"(ARR))
|
||||
#define LOAD_16(DEST, ADDR, ARR) asm("lhbrx %0, %1, %2" : "=r"(DEST) : "r"(ADDR), "r"(ARR))
|
||||
#define STORE_32(SRC, ADDR, ARR) asm("stwbrx %0, %1, %2" : : "r"(SRC), "r"(ADDR), "r"(ARR))
|
||||
#define STORE_16(SRC, ADDR, ARR) asm("sthbrx %0, %1, %2" : : "r"(SRC), "r"(ADDR), "r"(ARR))
|
||||
#if defined(__PPC__) || defined(__POWERPC__)
|
||||
#define LOAD_32(DEST, ADDR, ARR) { \
|
||||
uint32_t _addr = (ADDR); \
|
||||
void* _ptr = (ARR); \
|
||||
asm("lwbrx %0, %1, %2" : "=r"(DEST) : "b"(_ptr), "r"(_addr)); \
|
||||
}
|
||||
|
||||
#define LOAD_16(DEST, ADDR, ARR) { \
|
||||
uint32_t _addr = (ADDR); \
|
||||
void* _ptr = (ARR); \
|
||||
asm("lhbrx %0, %1, %2" : "=r"(DEST) : "b"(_ptr), "r"(_addr)); \
|
||||
}
|
||||
|
||||
#define STORE_32(SRC, ADDR, ARR) { \
|
||||
uint32_t _addr = (ADDR); \
|
||||
void* _ptr = (ARR); \
|
||||
asm("stwbrx %0, %1, %2" : : "r"(SRC), "b"(_ptr), "r"(_addr)); \
|
||||
}
|
||||
|
||||
#define STORE_16(SRC, ADDR, ARR) { \
|
||||
uint32_t _addr = (ADDR); \
|
||||
void* _ptr = (ARR); \
|
||||
asm("sthbrx %0, %1, %2" : : "r"(SRC), "b"(_ptr), "r"(_addr)); \
|
||||
}
|
||||
#else
|
||||
#define LOAD_32(DEST, ADDR, ARR) DEST = ((uint32_t*) ARR)[(ADDR) >> 2]
|
||||
#define LOAD_16(DEST, ADDR, ARR) DEST = ((uint16_t*) ARR)[(ADDR) >> 1]
|
||||
|
@ -28,4 +47,31 @@
|
|||
#define STORE_16(SRC, ADDR, ARR) ((uint16_t*) ARR)[(ADDR) >> 1] = SRC
|
||||
#endif
|
||||
|
||||
#define MAKE_MASK(START, END) (((1 << ((END) - (START))) - 1) << (START))
|
||||
#define CHECK_BITS(SRC, START, END) ((SRC) & MAKE_MASK(START, END))
|
||||
#define EXT_BITS(SRC, START, END) (((SRC) >> (START)) & ((1 << ((END) - (START))) - 1))
|
||||
#define INS_BITS(SRC, START, END, BITS) (CLEAR_BITS(SRC, START, END) | (((BITS) << (START)) & MAKE_MASK(START, END)))
|
||||
#define CLEAR_BITS(SRC, START, END) ((SRC) & ~MAKE_MASK(START, END))
|
||||
#define FILL_BITS(SRC, START, END) ((SRC) | MAKE_MASK(START, END))
|
||||
|
||||
#define DECL_BITFIELD(NAME, TYPE) typedef TYPE NAME
|
||||
|
||||
#define DECL_BITS(TYPE, FIELD, START, SIZE) \
|
||||
__attribute__((unused)) static inline TYPE TYPE ## Is ## FIELD (TYPE src) { \
|
||||
return CHECK_BITS(src, (START), (START) + (SIZE)); \
|
||||
} \
|
||||
__attribute__((unused)) static inline TYPE TYPE ## Get ## FIELD (TYPE src) { \
|
||||
return EXT_BITS(src, (START), (START) + (SIZE)); \
|
||||
} \
|
||||
__attribute__((unused)) static inline TYPE TYPE ## Clear ## FIELD (TYPE src) { \
|
||||
return CLEAR_BITS(src, (START), (START) + (SIZE)); \
|
||||
} \
|
||||
__attribute__((unused)) static inline TYPE TYPE ## Fill ## FIELD (TYPE src) { \
|
||||
return FILL_BITS(src, (START), (START) + (SIZE)); \
|
||||
} \
|
||||
__attribute__((unused)) static inline TYPE TYPE ## Set ## FIELD (TYPE src, TYPE bits) { \
|
||||
return INS_BITS(src, (START), (START) + (SIZE), bits); \
|
||||
}
|
||||
|
||||
#define DECL_BIT(TYPE, FIELD, BIT) DECL_BITS(TYPE, FIELD, BIT, 1)
|
||||
#endif
|
||||
|
|
|
@ -89,7 +89,7 @@ enum ARMShifterOperation {
|
|||
union ARMOperand {
|
||||
struct {
|
||||
uint8_t reg;
|
||||
enum ARMShifterOperation shifterOp;
|
||||
uint8_t shifterOp;
|
||||
union {
|
||||
uint8_t shifterReg;
|
||||
uint8_t shifterImm;
|
||||
|
@ -110,9 +110,9 @@ enum ARMMemoryAccessType {
|
|||
|
||||
struct ARMMemoryAccess {
|
||||
uint8_t baseReg;
|
||||
uint8_t width;
|
||||
uint16_t format;
|
||||
union ARMOperand offset;
|
||||
enum ARMMemoryAccessType width;
|
||||
};
|
||||
|
||||
enum ARMMnemonic {
|
||||
|
@ -167,25 +167,25 @@ enum {
|
|||
};
|
||||
|
||||
struct ARMInstructionInfo {
|
||||
enum ExecutionMode execMode;
|
||||
uint32_t opcode;
|
||||
enum ARMMnemonic mnemonic;
|
||||
union ARMOperand op1;
|
||||
union ARMOperand op2;
|
||||
union ARMOperand op3;
|
||||
union ARMOperand op4;
|
||||
struct ARMMemoryAccess memory;
|
||||
int operandFormat;
|
||||
int branches;
|
||||
int traps;
|
||||
int affectsCPSR;
|
||||
int condition;
|
||||
int sDataCycles;
|
||||
int nDataCycles;
|
||||
int sInstructionCycles;
|
||||
int nInstructionCycles;
|
||||
int iCycles;
|
||||
int cCycles;
|
||||
unsigned execMode : 1;
|
||||
bool branches : 1;
|
||||
bool traps : 1;
|
||||
bool affectsCPSR : 1;
|
||||
unsigned condition : 4;
|
||||
unsigned mnemonic : 6;
|
||||
unsigned iCycles : 2;
|
||||
unsigned cCycles : 4;
|
||||
unsigned sDataCycles : 10;
|
||||
unsigned nDataCycles : 10;
|
||||
unsigned sInstructionCycles : 4;
|
||||
unsigned nInstructionCycles : 4;
|
||||
};
|
||||
|
||||
void ARMDecodeARM(uint32_t opcode, struct ARMInstructionInfo* info);
|
||||
|
|
|
@ -44,13 +44,17 @@
|
|||
#define ARM_ILL cpu->irqh.hitIllegal(cpu, opcode)
|
||||
|
||||
#define ARM_WRITE_PC \
|
||||
cpu->gprs[ARM_PC] = (cpu->gprs[ARM_PC] & -WORD_SIZE_ARM) + WORD_SIZE_ARM; \
|
||||
cpu->memory.setActiveRegion(cpu, cpu->gprs[ARM_PC] - WORD_SIZE_ARM); \
|
||||
cpu->gprs[ARM_PC] = (cpu->gprs[ARM_PC] & -WORD_SIZE_ARM); \
|
||||
cpu->memory.setActiveRegion(cpu, cpu->gprs[ARM_PC]); \
|
||||
LOAD_32(cpu->prefetch, cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion); \
|
||||
cpu->gprs[ARM_PC] += WORD_SIZE_ARM; \
|
||||
currentCycles += 2 + cpu->memory.activeUncachedCycles32 + cpu->memory.activeSeqCycles32;
|
||||
|
||||
#define THUMB_WRITE_PC \
|
||||
cpu->gprs[ARM_PC] = (cpu->gprs[ARM_PC] & -WORD_SIZE_THUMB) + WORD_SIZE_THUMB; \
|
||||
cpu->memory.setActiveRegion(cpu, cpu->gprs[ARM_PC] - WORD_SIZE_THUMB); \
|
||||
cpu->gprs[ARM_PC] = (cpu->gprs[ARM_PC] & -WORD_SIZE_THUMB); \
|
||||
cpu->memory.setActiveRegion(cpu, cpu->gprs[ARM_PC]); \
|
||||
LOAD_16(cpu->prefetch, cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion); \
|
||||
cpu->gprs[ARM_PC] += WORD_SIZE_THUMB; \
|
||||
currentCycles += 2 + cpu->memory.activeUncachedCycles16 + cpu->memory.activeSeqCycles16;
|
||||
|
||||
static inline int _ARMModeHasSPSR(enum PrivilegeMode mode) {
|
||||
|
|
|
@ -17,7 +17,7 @@ struct DebugVector {
|
|||
} type;
|
||||
union {
|
||||
int32_t intValue;
|
||||
const char* charValue;
|
||||
char* charValue;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -25,15 +25,23 @@ static const char* ERROR_MISSING_ARGS = "Arguments missing";
|
|||
|
||||
static struct CLIDebugger* _activeDebugger;
|
||||
|
||||
typedef void (DebuggerCommand)(struct CLIDebugger*, struct DebugVector*);
|
||||
typedef void (*DebuggerCommand)(struct CLIDebugger*, struct DebugVector*);
|
||||
typedef struct DebugVector* (*DVParser)(struct CLIDebugger* debugger, const char* string, size_t length);
|
||||
|
||||
static struct DebugVector* _DVParse(struct CLIDebugger* debugger, const char* string, size_t length);
|
||||
static struct DebugVector* _DVStringParse(struct CLIDebugger* debugger, const char* string, size_t length);
|
||||
|
||||
static void _breakInto(struct CLIDebugger*, struct DebugVector*);
|
||||
static void _continue(struct CLIDebugger*, struct DebugVector*);
|
||||
static void _disassemble(struct CLIDebugger*, struct DebugVector*);
|
||||
static void _disassembleArm(struct CLIDebugger*, struct DebugVector*);
|
||||
static void _disassembleThumb(struct CLIDebugger*, struct DebugVector*);
|
||||
static void _next(struct CLIDebugger*, struct DebugVector*);
|
||||
static void _print(struct CLIDebugger*, struct DebugVector*);
|
||||
static void _printBin(struct CLIDebugger*, struct DebugVector*);
|
||||
static void _printHex(struct CLIDebugger*, struct DebugVector*);
|
||||
static void _printStatus(struct CLIDebugger*, struct DebugVector*);
|
||||
static void _printHelp(struct CLIDebugger*, struct DebugVector*);
|
||||
static void _quit(struct CLIDebugger*, struct DebugVector*);
|
||||
static void _readByte(struct CLIDebugger*, struct DebugVector*);
|
||||
static void _readHalfword(struct CLIDebugger*, struct DebugVector*);
|
||||
|
@ -43,38 +51,49 @@ static void _clearBreakpoint(struct CLIDebugger*, struct DebugVector*);
|
|||
static void _setWatchpoint(struct CLIDebugger*, struct DebugVector*);
|
||||
|
||||
static void _breakIntoDefault(int signal);
|
||||
static void _disassembleMode(struct CLIDebugger*, struct DebugVector*, enum ExecutionMode mode);
|
||||
static void _printLine(struct CLIDebugger* debugger, uint32_t address, enum ExecutionMode mode);
|
||||
|
||||
static struct {
|
||||
const char* name;
|
||||
DebuggerCommand* command;
|
||||
DebuggerCommand command;
|
||||
DVParser parser;
|
||||
const char* summary;
|
||||
} _debuggerCommands[] = {
|
||||
{ "b", _setBreakpoint },
|
||||
{ "break", _setBreakpoint },
|
||||
{ "c", _continue },
|
||||
{ "continue", _continue },
|
||||
{ "d", _clearBreakpoint },
|
||||
{ "delete", _clearBreakpoint },
|
||||
{ "dis", _disassemble },
|
||||
{ "disasm", _disassemble },
|
||||
{ "i", _printStatus },
|
||||
{ "info", _printStatus },
|
||||
{ "n", _next },
|
||||
{ "next", _next },
|
||||
{ "p", _print },
|
||||
{ "p/x", _printHex },
|
||||
{ "print", _print },
|
||||
{ "print/x", _printHex },
|
||||
{ "q", _quit },
|
||||
{ "quit", _quit },
|
||||
{ "rb", _readByte },
|
||||
{ "rh", _readHalfword },
|
||||
{ "rw", _readWord },
|
||||
{ "status", _printStatus },
|
||||
{ "w", _setWatchpoint },
|
||||
{ "watch", _setWatchpoint },
|
||||
{ "x", _breakInto },
|
||||
{ 0, 0 }
|
||||
{ "b", _setBreakpoint, _DVParse, "Set a breakpoint" },
|
||||
{ "break", _setBreakpoint, _DVParse, "Set a breakpoint" },
|
||||
{ "c", _continue, 0, "Continue execution" },
|
||||
{ "continue", _continue, 0, "Continue execution" },
|
||||
{ "d", _clearBreakpoint, _DVParse, "Delete a breakpoint" },
|
||||
{ "delete", _clearBreakpoint, _DVParse, "Delete a breakpoint" },
|
||||
{ "dis", _disassemble, _DVParse, "Disassemble instructions" },
|
||||
{ "dis/a", _disassembleArm, _DVParse, "Disassemble instructions as ARM" },
|
||||
{ "dis/t", _disassembleThumb, _DVParse, "Disassemble instructions as Thumb" },
|
||||
{ "disasm", _disassemble, _DVParse, "Disassemble instructions" },
|
||||
{ "disasm/a", _disassembleArm, _DVParse, "Disassemble instructions as ARM" },
|
||||
{ "disasm/t", _disassembleThumb, _DVParse, "Disassemble instructions as Thumb" },
|
||||
{ "h", _printHelp, _DVStringParse, "Print help" },
|
||||
{ "help", _printHelp, _DVStringParse, "Print help" },
|
||||
{ "i", _printStatus, 0, "Print the current status" },
|
||||
{ "info", _printStatus, 0, "Print the current status" },
|
||||
{ "n", _next, 0, "Execute next instruction" },
|
||||
{ "next", _next, 0, "Execute next instruction" },
|
||||
{ "p", _print, _DVParse, "Print a value" },
|
||||
{ "p/t", _printBin, _DVParse, "Print a value as binary" },
|
||||
{ "p/x", _printHex, _DVParse, "Print a value as hexadecimal" },
|
||||
{ "print", _print, _DVParse, "Print a value" },
|
||||
{ "print/t", _printBin, _DVParse, "Print a value as binary" },
|
||||
{ "print/x", _printHex, _DVParse, "Print a value as hexadecimal" },
|
||||
{ "q", _quit, 0, "Quit the emulator" },
|
||||
{ "quit", _quit, 0, "Quit the emulator" },
|
||||
{ "rb", _readByte, _DVParse, "Read a byte from a specified offset" },
|
||||
{ "rh", _readHalfword, _DVParse, "Read a halfword from a specified offset" },
|
||||
{ "rw", _readWord, _DVParse, "Read a word from a specified offset" },
|
||||
{ "status", _printStatus, 0, "Print the current status" },
|
||||
{ "w", _setWatchpoint, _DVParse, "Set a watchpoint" },
|
||||
{ "watch", _setWatchpoint, _DVParse, "Set a watchpoint" },
|
||||
{ "x", _breakInto, 0, "Break into attached debugger (for developers)" },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
static inline void _printPSR(union PSR psr) {
|
||||
|
@ -122,10 +141,21 @@ static void _next(struct CLIDebugger* debugger, struct DebugVector* dv) {
|
|||
}
|
||||
|
||||
static void _disassemble(struct CLIDebugger* debugger, struct DebugVector* dv) {
|
||||
_disassembleMode(debugger, dv, debugger->d.cpu->executionMode);
|
||||
}
|
||||
|
||||
static void _disassembleArm(struct CLIDebugger* debugger, struct DebugVector* dv) {
|
||||
_disassembleMode(debugger, dv, MODE_ARM);
|
||||
}
|
||||
|
||||
static void _disassembleThumb(struct CLIDebugger* debugger, struct DebugVector* dv) {
|
||||
_disassembleMode(debugger, dv, MODE_THUMB);
|
||||
}
|
||||
|
||||
static void _disassembleMode(struct CLIDebugger* debugger, struct DebugVector* dv, enum ExecutionMode mode) {
|
||||
uint32_t address;
|
||||
int size;
|
||||
int wordSize;
|
||||
enum ExecutionMode mode = debugger->d.cpu->executionMode;
|
||||
|
||||
if (mode == MODE_ARM) {
|
||||
wordSize = WORD_SIZE_ARM;
|
||||
|
@ -162,6 +192,18 @@ static void _print(struct CLIDebugger* debugger, struct DebugVector* dv) {
|
|||
printf("\n");
|
||||
}
|
||||
|
||||
static void _printBin(struct CLIDebugger* debugger, struct DebugVector* dv) {
|
||||
UNUSED(debugger);
|
||||
for ( ; dv; dv = dv->next) {
|
||||
printf(" 0b");
|
||||
int i = 32;
|
||||
while (i--) {
|
||||
printf("%u", (dv->intValue >> i) & 1);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void _printHex(struct CLIDebugger* debugger, struct DebugVector* dv) {
|
||||
UNUSED(debugger);
|
||||
for ( ; dv; dv = dv->next) {
|
||||
|
@ -170,6 +212,24 @@ static void _printHex(struct CLIDebugger* debugger, struct DebugVector* dv) {
|
|||
printf("\n");
|
||||
}
|
||||
|
||||
static void _printHelp(struct CLIDebugger* debugger, struct DebugVector* dv) {
|
||||
UNUSED(debugger);
|
||||
UNUSED(dv);
|
||||
if (!dv) {
|
||||
int i;
|
||||
for (i = 0; _debuggerCommands[i].name; ++i) {
|
||||
printf("%-10s %s\n", _debuggerCommands[i].name, _debuggerCommands[i].summary);
|
||||
}
|
||||
} else {
|
||||
int i;
|
||||
for (i = 0; _debuggerCommands[i].name; ++i) {
|
||||
if (strcmp(_debuggerCommands[i].name, dv->charValue) == 0) {
|
||||
printf(" %s\n", _debuggerCommands[i].summary);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void _printLine(struct CLIDebugger* debugger, uint32_t address, enum ExecutionMode mode) {
|
||||
char disassembly[48];
|
||||
struct ARMInstructionInfo info;
|
||||
|
@ -386,10 +446,44 @@ static struct DebugVector* _DVParse(struct CLIDebugger* debugger, const char* st
|
|||
return dv;
|
||||
}
|
||||
|
||||
static struct DebugVector* _DVStringParse(struct CLIDebugger* debugger, const char* string, size_t length) {
|
||||
if (!string || length < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct DebugVector dvTemp = { .type = DV_CHAR_TYPE };
|
||||
|
||||
size_t adjusted;
|
||||
const char* next = strchr(string, ' ');
|
||||
if (next) {
|
||||
adjusted = next - string;
|
||||
} else {
|
||||
adjusted = length;
|
||||
}
|
||||
dvTemp.charValue = malloc(adjusted);
|
||||
strncpy(dvTemp.charValue, string, adjusted);
|
||||
|
||||
length -= adjusted;
|
||||
string += adjusted;
|
||||
|
||||
struct DebugVector* dv = malloc(sizeof(struct DebugVector));
|
||||
*dv = dvTemp;
|
||||
if (string[0] == ' ') {
|
||||
dv->next = _DVStringParse(debugger, string + 1, length - 1);
|
||||
if (dv->next && dv->next->type == DV_ERROR_TYPE) {
|
||||
dv->type = DV_ERROR_TYPE;
|
||||
}
|
||||
}
|
||||
return dv;
|
||||
}
|
||||
|
||||
static void _DVFree(struct DebugVector* dv) {
|
||||
struct DebugVector* next;
|
||||
while (dv) {
|
||||
next = dv->next;
|
||||
if (dv->type == DV_CHAR_TYPE) {
|
||||
free(dv->charValue);
|
||||
}
|
||||
free(dv);
|
||||
dv = next;
|
||||
}
|
||||
|
@ -401,12 +495,6 @@ static bool _parse(struct CLIDebugger* debugger, const char* line, size_t count)
|
|||
struct DebugVector* dv = 0;
|
||||
if (firstSpace) {
|
||||
cmdLength = firstSpace - line;
|
||||
dv = _DVParse(debugger, firstSpace + 1, count - cmdLength - 1);
|
||||
if (dv && dv->type == DV_ERROR_TYPE) {
|
||||
printf("Parse error\n");
|
||||
_DVFree(dv);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
cmdLength = count;
|
||||
}
|
||||
|
@ -418,6 +506,20 @@ static bool _parse(struct CLIDebugger* debugger, const char* line, size_t count)
|
|||
continue;
|
||||
}
|
||||
if (strncasecmp(name, line, cmdLength) == 0) {
|
||||
if (_debuggerCommands[i].parser) {
|
||||
if (firstSpace) {
|
||||
dv = _debuggerCommands[i].parser(debugger, firstSpace + 1, count - cmdLength - 1);
|
||||
if (dv && dv->type == DV_ERROR_TYPE) {
|
||||
printf("Parse error\n");
|
||||
_DVFree(dv);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
printf("Wrong number of arguments");
|
||||
}
|
||||
} else if (firstSpace) {
|
||||
printf("Wrong number of arguments");
|
||||
}
|
||||
_debuggerCommands[i].command(debugger, dv);
|
||||
_DVFree(dv);
|
||||
return true;
|
||||
|
@ -450,9 +552,8 @@ static void _commandLine(struct ARMDebugger* debugger) {
|
|||
_parse(cliDebugger, ev.str, strlen(ev.str) - 1);
|
||||
}
|
||||
} else {
|
||||
if (_parse(cliDebugger, line, count - 1)) {
|
||||
history(cliDebugger->histate, &ev, H_ENTER, line);
|
||||
}
|
||||
_parse(cliDebugger, line, count - 1);
|
||||
history(cliDebugger->histate, &ev, H_ENTER, line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -115,3 +115,17 @@ void ARMDebuggerSetWatchpoint(struct ARMDebugger* debugger, uint32_t address) {
|
|||
watchpoint->next = debugger->watchpoints;
|
||||
debugger->watchpoints = watchpoint;
|
||||
}
|
||||
|
||||
void ARMDebuggerClearWatchpoint(struct ARMDebugger* debugger, uint32_t address) {
|
||||
struct DebugBreakpoint** previous = &debugger->watchpoints;
|
||||
struct DebugBreakpoint* breakpoint;
|
||||
for (; (breakpoint = *previous); previous = &breakpoint->next) {
|
||||
if (breakpoint->address == address) {
|
||||
*previous = breakpoint->next;
|
||||
free(breakpoint);
|
||||
}
|
||||
}
|
||||
if (!debugger->watchpoints) {
|
||||
ARMDebuggerRemoveMemoryShim(debugger);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,5 +58,6 @@ void ARMDebuggerEnter(struct ARMDebugger*, enum DebuggerEntryReason);
|
|||
void ARMDebuggerSetBreakpoint(struct ARMDebugger* debugger, uint32_t address);
|
||||
void ARMDebuggerClearBreakpoint(struct ARMDebugger* debugger, uint32_t address);
|
||||
void ARMDebuggerSetWatchpoint(struct ARMDebugger* debugger, uint32_t address);
|
||||
void ARMDebuggerClearWatchpoint(struct ARMDebugger* debugger, uint32_t address);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -177,7 +177,7 @@ static void _continue(struct GDBStub* stub, const char* message) {
|
|||
|
||||
static void _step(struct GDBStub* stub, const char* message) {
|
||||
ARMRun(stub->d.cpu);
|
||||
snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGINT);
|
||||
snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGTRAP);
|
||||
_sendMessage(stub);
|
||||
// TODO: parse message
|
||||
UNUSED(message);
|
||||
|
@ -279,46 +279,53 @@ static void _processVReadCommand(struct GDBStub* stub, const char* message) {
|
|||
}
|
||||
|
||||
static void _setBreakpoint(struct GDBStub* stub, const char* message) {
|
||||
const char* readAddress = &message[2];
|
||||
unsigned i = 0;
|
||||
uint32_t address = _readHex(readAddress, &i);
|
||||
readAddress += i + 1;
|
||||
uint32_t kind = _readHex(readAddress, &i); // We don't use this in hardware watchpoints
|
||||
UNUSED(kind);
|
||||
|
||||
switch (message[0]) {
|
||||
case '0': // Memory breakpoints are not currently supported
|
||||
case '1': {
|
||||
const char* readAddress = &message[2];
|
||||
unsigned i = 0;
|
||||
uint32_t address = _readHex(readAddress, &i);
|
||||
readAddress += i + 1;
|
||||
uint32_t kind = _readHex(readAddress, &i); // We don't use this in hardware watchpoints
|
||||
UNUSED(kind);
|
||||
case '1':
|
||||
ARMDebuggerSetBreakpoint(&stub->d, address);
|
||||
strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
|
||||
_sendMessage(stub);
|
||||
break;
|
||||
}
|
||||
case '2':
|
||||
case '3':
|
||||
// TODO: Watchpoints
|
||||
case '4':
|
||||
ARMDebuggerSetWatchpoint(&stub->d, address);
|
||||
strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
|
||||
_sendMessage(stub);
|
||||
break;
|
||||
default:
|
||||
stub->outgoing[0] = '\0';
|
||||
_sendMessage(stub);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void _clearBreakpoint(struct GDBStub* stub, const char* message) {
|
||||
const char* readAddress = &message[2];
|
||||
unsigned i = 0;
|
||||
uint32_t address = _readHex(readAddress, &i);
|
||||
switch (message[0]) {
|
||||
case '0': // Memory breakpoints are not currently supported
|
||||
case '1': {
|
||||
const char* readAddress = &message[2];
|
||||
unsigned i = 0;
|
||||
uint32_t address = _readHex(readAddress, &i);
|
||||
case '1':
|
||||
ARMDebuggerClearBreakpoint(&stub->d, address);
|
||||
strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
|
||||
_sendMessage(stub);
|
||||
break;
|
||||
}
|
||||
case '2':
|
||||
case '3':
|
||||
// TODO: Watchpoints
|
||||
case '4':
|
||||
ARMDebuggerClearWatchpoint(&stub->d, address);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
|
||||
_sendMessage(stub);
|
||||
}
|
||||
|
||||
size_t _parseGDBMessage(struct GDBStub* stub, const char* message) {
|
||||
|
|
|
@ -69,3 +69,16 @@ void ARMDebuggerInstallMemoryShim(struct ARMDebugger* debugger) {
|
|||
debugger->cpu->memory.setActiveRegion = ARMDebuggerShim_setActiveRegion;
|
||||
debugger->cpu->memory.waitMultiple = ARMDebuggerShim_waitMultiple;
|
||||
}
|
||||
|
||||
void ARMDebuggerRemoveMemoryShim(struct ARMDebugger* debugger) {
|
||||
debugger->cpu->memory.store32 = debugger->originalMemory.store32;
|
||||
debugger->cpu->memory.store16 = debugger->originalMemory.store16;
|
||||
debugger->cpu->memory.store8 = debugger->originalMemory.store8;
|
||||
debugger->cpu->memory.load32 = debugger->originalMemory.load32;
|
||||
debugger->cpu->memory.load16 = debugger->originalMemory.load16;
|
||||
debugger->cpu->memory.loadU16 = debugger->originalMemory.loadU16;
|
||||
debugger->cpu->memory.load8 = debugger->originalMemory.load8;
|
||||
debugger->cpu->memory.loadU8 = debugger->originalMemory.loadU8;
|
||||
debugger->cpu->memory.setActiveRegion = debugger->originalMemory.setActiveRegion;
|
||||
debugger->cpu->memory.waitMultiple = debugger->originalMemory.waitMultiple;
|
||||
}
|
||||
|
|
|
@ -8,5 +8,6 @@
|
|||
struct ARMDebugger;
|
||||
|
||||
void ARMDebuggerInstallMemoryShim(struct ARMDebugger* debugger);
|
||||
void ARMDebuggerRemoveMemoryShim(struct ARMDebugger* debugger);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,5 +1,17 @@
|
|||
#include "parser.h"
|
||||
|
||||
static inline char* _strndup(const char* start, size_t len) {
|
||||
#ifdef HAVE_STRNDUP
|
||||
return strndup(start, len);
|
||||
#else
|
||||
// This is suboptimal, but anything recent should have strndup
|
||||
char* out = malloc((len + 1) * sizeof(char));
|
||||
strncpy(out, start, len);
|
||||
out[len] = '\0';
|
||||
return out;
|
||||
#endif
|
||||
}
|
||||
|
||||
static struct LexVector* _lexOperator(struct LexVector* lv, char operator) {
|
||||
struct LexVector* lvNext = malloc(sizeof(struct LexVector));
|
||||
lvNext->token.type = TOKEN_OPERATOR_TYPE;
|
||||
|
@ -95,13 +107,13 @@ size_t lexExpression(struct LexVector* lv, const char* string, size_t length) {
|
|||
case '*':
|
||||
case '/':
|
||||
lv->token.type = TOKEN_IDENTIFIER_TYPE;
|
||||
lv->token.identifierValue = strndup(tokenStart, string - tokenStart - 1);
|
||||
lv->token.identifierValue = _strndup(tokenStart, string - tokenStart - 1);
|
||||
lv = _lexOperator(lv, token);
|
||||
state = LEX_ROOT;
|
||||
break;
|
||||
case ')':
|
||||
lv->token.type = TOKEN_IDENTIFIER_TYPE;
|
||||
lv->token.identifierValue = strndup(tokenStart, string - tokenStart - 1);
|
||||
lv->token.identifierValue = _strndup(tokenStart, string - tokenStart - 1);
|
||||
state = LEX_EXPECT_OPERATOR;
|
||||
break;
|
||||
default:
|
||||
|
@ -240,7 +252,7 @@ size_t lexExpression(struct LexVector* lv, const char* string, size_t length) {
|
|||
break;
|
||||
case LEX_EXPECT_IDENTIFIER:
|
||||
lv->token.type = TOKEN_IDENTIFIER_TYPE;
|
||||
lv->token.identifierValue = strndup(tokenStart, string - tokenStart);
|
||||
lv->token.identifierValue = _strndup(tokenStart, string - tokenStart);
|
||||
break;
|
||||
case LEX_EXPECT_OPERATOR:
|
||||
lvNext = malloc(sizeof(struct LexVector));
|
||||
|
|
|
@ -10,6 +10,7 @@ const unsigned GBA_AUDIO_SAMPLES = 2048;
|
|||
const unsigned GBA_AUDIO_FIFO_SIZE = 8 * sizeof(int32_t);
|
||||
#define SWEEP_CYCLES (GBA_ARM7TDMI_FREQUENCY / 128)
|
||||
|
||||
static bool _writeEnvelope(struct GBAAudioEnvelope* envelope, uint16_t value);
|
||||
static int32_t _updateSquareChannel(struct GBAAudioSquareControl* envelope, int duty);
|
||||
static void _updateEnvelope(struct GBAAudioEnvelope* envelope);
|
||||
static bool _updateSweep(struct GBAAudioChannel1* ch);
|
||||
|
@ -33,28 +34,40 @@ void GBAAudioReset(struct GBAAudio* audio) {
|
|||
audio->nextCh2 = 0;
|
||||
audio->nextCh3 = 0;
|
||||
audio->nextCh4 = 0;
|
||||
audio->ch1.sweep.time = 0;
|
||||
audio->ch1.envelope.nextStep = INT_MAX;
|
||||
audio->ch1.control.nextStep = 0;
|
||||
audio->ch1.control.endTime = 0;
|
||||
audio->ch1.nextSweep = INT_MAX;
|
||||
audio->ch1.sample = 0;
|
||||
audio->ch2.envelope.nextStep = INT_MAX;
|
||||
audio->ch2.control.nextStep = 0;
|
||||
audio->ch2.control.endTime = 0;
|
||||
audio->ch2.sample = 0;
|
||||
audio->ch3.bank.packed = 0;
|
||||
audio->ch3.control.endTime = 0;
|
||||
audio->ch3.sample = 0;
|
||||
audio->ch4.sample = 0;
|
||||
audio->ch4.envelope.nextStep = INT_MAX;
|
||||
audio->ch1 = (struct GBAAudioChannel1) { .envelope = { .nextStep = INT_MAX }, .nextSweep = INT_MAX };
|
||||
audio->ch2 = (struct GBAAudioChannel2) { .envelope = { .nextStep = INT_MAX } };
|
||||
audio->ch3 = (struct GBAAudioChannel3) { .bank = { .bank = 0 } };
|
||||
audio->ch4 = (struct GBAAudioChannel4) { .envelope = { .nextStep = INT_MAX } };
|
||||
audio->chA.dmaSource = 0;
|
||||
audio->chB.dmaSource = 0;
|
||||
audio->eventDiff = 0;
|
||||
audio->nextSample = 0;
|
||||
audio->sampleRate = 0x8000;
|
||||
audio->soundbias = 0x200;
|
||||
audio->soundcntLo = 0;
|
||||
audio->soundcntHi = 0;
|
||||
audio->soundcntX = 0;
|
||||
audio->volumeRight = 0;
|
||||
audio->volumeLeft = 0;
|
||||
audio->ch1Right = false;
|
||||
audio->ch2Right = false;
|
||||
audio->ch3Right = false;
|
||||
audio->ch4Right = false;
|
||||
audio->ch1Left = false;
|
||||
audio->ch2Left = false;
|
||||
audio->ch3Left = false;
|
||||
audio->ch4Left = false;
|
||||
audio->volume = 0;
|
||||
audio->volumeChA = false;
|
||||
audio->volumeChB = false;
|
||||
audio->chARight = false;
|
||||
audio->chALeft = false;
|
||||
audio->chATimer = false;
|
||||
audio->chBRight = false;
|
||||
audio->chBLeft = false;
|
||||
audio->chBTimer = false;
|
||||
audio->playingCh1 = false;
|
||||
audio->playingCh2 = false;
|
||||
audio->playingCh3 = false;
|
||||
audio->playingCh4 = false;
|
||||
audio->enable = false;
|
||||
audio->sampleInterval = GBA_ARM7TDMI_FREQUENCY / audio->sampleRate;
|
||||
|
||||
CircleBufferClear(&audio->left);
|
||||
|
@ -251,11 +264,13 @@ void GBAAudioScheduleFifoDma(struct GBAAudio* audio, int number, struct GBADMA*
|
|||
GBALog(audio->p, GBA_LOG_GAME_ERROR, "Invalid FIFO destination: 0x%08X", info->dest);
|
||||
return;
|
||||
}
|
||||
info->dstControl = DMA_FIXED;
|
||||
info->reg = GBADMARegisterSetDestControl(info->reg, DMA_FIXED);
|
||||
}
|
||||
|
||||
void GBAAudioWriteSOUND1CNT_LO(struct GBAAudio* audio, uint16_t value) {
|
||||
audio->ch1.sweep.packed = value;
|
||||
audio->ch1.sweep.shift = GBAAudioRegisterSquareSweepGetShift(value);
|
||||
audio->ch1.sweep.direction = GBAAudioRegisterSquareSweepGetDirection(value);
|
||||
audio->ch1.sweep.time = GBAAudioRegisterSquareSweepGetTime(value);
|
||||
if (audio->ch1.sweep.time) {
|
||||
audio->ch1.nextSweep = audio->ch1.sweep.time * SWEEP_CYCLES;
|
||||
} else {
|
||||
|
@ -264,23 +279,16 @@ void GBAAudioWriteSOUND1CNT_LO(struct GBAAudio* audio, uint16_t value) {
|
|||
}
|
||||
|
||||
void GBAAudioWriteSOUND1CNT_HI(struct GBAAudio* audio, uint16_t value) {
|
||||
audio->ch1.envelope.packed = value;
|
||||
audio->ch1.envelope.dead = 0;
|
||||
if (audio->ch1.envelope.stepTime) {
|
||||
audio->ch1.envelope.nextStep = 0;
|
||||
} else {
|
||||
audio->ch1.envelope.nextStep = INT_MAX;
|
||||
if (audio->ch1.envelope.initialVolume == 0) {
|
||||
audio->ch1.envelope.dead = 1;
|
||||
audio->ch1.sample = 0;
|
||||
}
|
||||
if (!_writeEnvelope(&audio->ch1.envelope, value)) {
|
||||
audio->ch1.sample = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void GBAAudioWriteSOUND1CNT_X(struct GBAAudio* audio, uint16_t value) {
|
||||
audio->ch1.control.packed = value;
|
||||
audio->ch1.control.frequency = GBAAudioRegisterControlGetFrequency(value);
|
||||
audio->ch1.control.stop = GBAAudioRegisterControlGetStop(value);
|
||||
audio->ch1.control.endTime = (GBA_ARM7TDMI_FREQUENCY * (64 - audio->ch1.envelope.length)) >> 8;
|
||||
if (audio->ch1.control.restart) {
|
||||
if (GBAAudioRegisterControlIsRestart(value)) {
|
||||
if (audio->ch1.sweep.time) {
|
||||
audio->ch1.nextSweep = audio->ch1.sweep.time * SWEEP_CYCLES;
|
||||
} else {
|
||||
|
@ -305,23 +313,16 @@ void GBAAudioWriteSOUND1CNT_X(struct GBAAudio* audio, uint16_t value) {
|
|||
}
|
||||
|
||||
void GBAAudioWriteSOUND2CNT_LO(struct GBAAudio* audio, uint16_t value) {
|
||||
audio->ch2.envelope.packed = value;
|
||||
audio->ch2.envelope.dead = 0;
|
||||
if (audio->ch2.envelope.stepTime) {
|
||||
audio->ch2.envelope.nextStep = 0;
|
||||
} else {
|
||||
audio->ch2.envelope.nextStep = INT_MAX;
|
||||
if (audio->ch2.envelope.initialVolume == 0) {
|
||||
audio->ch2.envelope.dead = 1;
|
||||
audio->ch2.sample = 0;
|
||||
}
|
||||
if (!_writeEnvelope(&audio->ch2.envelope, value)) {
|
||||
audio->ch2.sample = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void GBAAudioWriteSOUND2CNT_HI(struct GBAAudio* audio, uint16_t value) {
|
||||
audio->ch2.control.packed = value;
|
||||
audio->ch1.control.endTime = (GBA_ARM7TDMI_FREQUENCY * (64 - audio->ch2.envelope.length)) >> 8;
|
||||
if (audio->ch2.control.restart) {
|
||||
audio->ch2.control.frequency = GBAAudioRegisterControlGetFrequency(value);
|
||||
audio->ch2.control.stop = GBAAudioRegisterControlGetStop(value);
|
||||
audio->ch2.control.endTime = (GBA_ARM7TDMI_FREQUENCY * (64 - audio->ch2.envelope.length)) >> 8;
|
||||
if (GBAAudioRegisterControlIsRestart(value)) {
|
||||
audio->playingCh2 = 1;
|
||||
audio->ch2.envelope.currentVolume = audio->ch2.envelope.initialVolume;
|
||||
if (audio->ch2.envelope.stepTime) {
|
||||
|
@ -334,42 +335,41 @@ void GBAAudioWriteSOUND2CNT_HI(struct GBAAudio* audio, uint16_t value) {
|
|||
}
|
||||
|
||||
void GBAAudioWriteSOUND3CNT_LO(struct GBAAudio* audio, uint16_t value) {
|
||||
audio->ch3.bank.packed = value;
|
||||
audio->ch3.bank.size = GBAAudioRegisterBankGetSize(value);
|
||||
audio->ch3.bank.bank = GBAAudioRegisterBankGetBank(value);
|
||||
audio->ch3.bank.enable = GBAAudioRegisterBankGetEnable(value);
|
||||
if (audio->ch3.control.endTime >= 0) {
|
||||
audio->playingCh3 = audio->ch3.bank.enable;
|
||||
}
|
||||
}
|
||||
|
||||
void GBAAudioWriteSOUND3CNT_HI(struct GBAAudio* audio, uint16_t value) {
|
||||
audio->ch3.wave.packed = value;
|
||||
audio->ch3.wave.length = GBAAudioRegisterBankWaveGetLength(value);
|
||||
audio->ch3.wave.volume = GBAAudioRegisterBankWaveGetVolume(value);
|
||||
}
|
||||
|
||||
void GBAAudioWriteSOUND3CNT_X(struct GBAAudio* audio, uint16_t value) {
|
||||
audio->ch3.control.packed = value;
|
||||
audio->ch3.control.rate = GBAAudioRegisterControlGetRate(value);
|
||||
audio->ch3.control.stop = GBAAudioRegisterControlGetStop(value);
|
||||
audio->ch3.control.endTime = (GBA_ARM7TDMI_FREQUENCY * (256 - audio->ch3.wave.length)) >> 8;
|
||||
if (audio->ch3.control.restart) {
|
||||
if (GBAAudioRegisterControlIsRestart(value)) {
|
||||
audio->playingCh3 = audio->ch3.bank.enable;
|
||||
}
|
||||
}
|
||||
|
||||
void GBAAudioWriteSOUND4CNT_LO(struct GBAAudio* audio, uint16_t value) {
|
||||
audio->ch4.envelope.packed = value;
|
||||
audio->ch4.envelope.dead = 0;
|
||||
if (audio->ch4.envelope.stepTime) {
|
||||
audio->ch4.envelope.nextStep = 0;
|
||||
} else {
|
||||
audio->ch4.envelope.nextStep = INT_MAX;
|
||||
if (audio->ch4.envelope.initialVolume == 0) {
|
||||
audio->ch4.envelope.dead = 1;
|
||||
audio->ch4.sample = 0;
|
||||
}
|
||||
if (!_writeEnvelope(&audio->ch4.envelope, value)) {
|
||||
audio->ch4.sample = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void GBAAudioWriteSOUND4CNT_HI(struct GBAAudio* audio, uint16_t value) {
|
||||
audio->ch4.control.packed = value;
|
||||
audio->ch4.control.ratio = GBAAudioRegisterCh4ControlGetRatio(value);
|
||||
audio->ch4.control.frequency = GBAAudioRegisterCh4ControlGetFrequency(value);
|
||||
audio->ch4.control.power = GBAAudioRegisterCh4ControlGetPower(value);
|
||||
audio->ch4.control.stop = GBAAudioRegisterCh4ControlGetStop(value);
|
||||
audio->ch4.control.endTime = (GBA_ARM7TDMI_FREQUENCY * (64 - audio->ch4.envelope.length)) >> 8;
|
||||
if (audio->ch4.control.restart) {
|
||||
if (GBAAudioRegisterCh4ControlIsRestart(value)) {
|
||||
audio->playingCh4 = 1;
|
||||
audio->ch4.envelope.currentVolume = audio->ch4.envelope.initialVolume;
|
||||
if (audio->ch4.envelope.stepTime) {
|
||||
|
@ -387,15 +387,33 @@ void GBAAudioWriteSOUND4CNT_HI(struct GBAAudio* audio, uint16_t value) {
|
|||
}
|
||||
|
||||
void GBAAudioWriteSOUNDCNT_LO(struct GBAAudio* audio, uint16_t value) {
|
||||
audio->soundcntLo = value;
|
||||
audio->volumeRight = GBARegisterSOUNDCNT_LOGetVolumeRight(value);
|
||||
audio->volumeLeft = GBARegisterSOUNDCNT_LOGetVolumeLeft(value);
|
||||
audio->ch1Right = GBARegisterSOUNDCNT_LOGetCh1Right(value);
|
||||
audio->ch2Right = GBARegisterSOUNDCNT_LOGetCh2Right(value);
|
||||
audio->ch3Right = GBARegisterSOUNDCNT_LOGetCh3Right(value);
|
||||
audio->ch4Right = GBARegisterSOUNDCNT_LOGetCh4Right(value);
|
||||
audio->ch1Left = GBARegisterSOUNDCNT_LOGetCh1Left(value);
|
||||
audio->ch2Left = GBARegisterSOUNDCNT_LOGetCh2Left(value);
|
||||
audio->ch3Left = GBARegisterSOUNDCNT_LOGetCh3Left(value);
|
||||
audio->ch4Left = GBARegisterSOUNDCNT_LOGetCh4Left(value);
|
||||
}
|
||||
|
||||
void GBAAudioWriteSOUNDCNT_HI(struct GBAAudio* audio, uint16_t value) {
|
||||
audio->soundcntHi = value;
|
||||
audio->volume = GBARegisterSOUNDCNT_HIGetVolume(value);
|
||||
audio->volumeChA = GBARegisterSOUNDCNT_HIGetVolumeChA(value);
|
||||
audio->volumeChB = GBARegisterSOUNDCNT_HIGetVolumeChB(value);
|
||||
audio->chARight = GBARegisterSOUNDCNT_HIGetChARight(value);
|
||||
audio->chALeft = GBARegisterSOUNDCNT_HIGetChALeft(value);
|
||||
audio->chATimer = GBARegisterSOUNDCNT_HIGetChATimer(value);
|
||||
audio->chBRight = GBARegisterSOUNDCNT_HIGetChBRight(value);
|
||||
audio->chBLeft = GBARegisterSOUNDCNT_HIGetChBLeft(value);
|
||||
audio->chBTimer = GBARegisterSOUNDCNT_HIGetChBTimer(value);
|
||||
// TODO: Implement channel reset
|
||||
}
|
||||
|
||||
void GBAAudioWriteSOUNDCNT_X(struct GBAAudio* audio, uint16_t value) {
|
||||
audio->soundcntX = (value & 0x80) | (audio->soundcntX & 0x0F);
|
||||
audio->enable = GBARegisterSOUNDCNT_XGetEnable(value);
|
||||
}
|
||||
|
||||
void GBAAudioWriteSOUNDBIAS(struct GBAAudio* audio, uint16_t value) {
|
||||
|
@ -419,9 +437,12 @@ void GBAAudioWriteFIFO(struct GBAAudio* audio, int address, uint32_t value) {
|
|||
GBALog(audio->p, GBA_LOG_ERROR, "Bad FIFO write to address 0x%03x", address);
|
||||
return;
|
||||
}
|
||||
while (!CircleBufferWrite32(fifo, value)) {
|
||||
int32_t dummy;
|
||||
CircleBufferRead32(fifo, &dummy);
|
||||
int i;
|
||||
for (i = 0; i < 4; ++i) {
|
||||
while (!CircleBufferWrite8(fifo, value >> (8 * i))) {
|
||||
int8_t dummy;
|
||||
CircleBufferRead8(fifo, &dummy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -503,6 +524,25 @@ unsigned GBAAudioResampleNN(struct GBAAudio* audio, float ratio, float* drift, s
|
|||
return totalRead;
|
||||
}
|
||||
|
||||
bool _writeEnvelope(struct GBAAudioEnvelope* envelope, uint16_t value) {
|
||||
envelope->length = GBAAudioRegisterEnvelopeGetLength(value);
|
||||
envelope->duty = GBAAudioRegisterEnvelopeGetDuty(value);
|
||||
envelope->stepTime = GBAAudioRegisterEnvelopeGetStepTime(value);
|
||||
envelope->direction = GBAAudioRegisterEnvelopeGetDirection(value);
|
||||
envelope->initialVolume = GBAAudioRegisterEnvelopeGetInitialVolume(value);
|
||||
envelope->dead = 0;
|
||||
if (envelope->stepTime) {
|
||||
envelope->nextStep = 0;
|
||||
} else {
|
||||
envelope->nextStep = INT_MAX;
|
||||
if (envelope->initialVolume == 0) {
|
||||
envelope->dead = 1;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int32_t _updateSquareChannel(struct GBAAudioSquareControl* control, int duty) {
|
||||
control->hi = !control->hi;
|
||||
int period = 16 * (2048 - control->frequency);
|
||||
|
|
|
@ -9,46 +9,64 @@ struct GBADMA;
|
|||
|
||||
extern const unsigned GBA_AUDIO_SAMPLES;
|
||||
|
||||
DECL_BITFIELD(GBAAudioRegisterEnvelope, uint16_t);
|
||||
DECL_BITS(GBAAudioRegisterEnvelope, Length, 0, 6);
|
||||
DECL_BITS(GBAAudioRegisterEnvelope, Duty, 6, 2);
|
||||
DECL_BITS(GBAAudioRegisterEnvelope, StepTime, 8, 3);
|
||||
DECL_BIT(GBAAudioRegisterEnvelope, Direction, 11);
|
||||
DECL_BITS(GBAAudioRegisterEnvelope, InitialVolume, 12, 4);
|
||||
|
||||
DECL_BITFIELD(GBAAudioRegisterControl, uint16_t);
|
||||
DECL_BITS(GBAAudioRegisterControl, Rate, 0, 11);
|
||||
DECL_BITS(GBAAudioRegisterControl, Frequency, 0, 11);
|
||||
DECL_BIT(GBAAudioRegisterControl, Stop, 14);
|
||||
DECL_BIT(GBAAudioRegisterControl, Restart, 15);
|
||||
|
||||
DECL_BITFIELD(GBAAudioRegisterSquareSweep, uint16_t);
|
||||
DECL_BITS(GBAAudioRegisterSquareSweep, Shift, 0, 3);
|
||||
DECL_BIT(GBAAudioRegisterSquareSweep, Direction, 3);
|
||||
DECL_BITS(GBAAudioRegisterSquareSweep, Time, 4, 3);
|
||||
|
||||
DECL_BITFIELD(GBAAudioRegisterBank, uint16_t);
|
||||
DECL_BIT(GBAAudioRegisterBank, Size, 5);
|
||||
DECL_BIT(GBAAudioRegisterBank, Bank, 6);
|
||||
DECL_BIT(GBAAudioRegisterBank, Enable, 7);
|
||||
|
||||
DECL_BITFIELD(GBAAudioRegisterBankWave, uint16_t);
|
||||
DECL_BITS(GBAAudioRegisterBankWave, Length, 0, 8);
|
||||
DECL_BITS(GBAAudioRegisterBankWave, Volume, 13, 3);
|
||||
|
||||
DECL_BITFIELD(GBAAudioRegisterCh4Control, uint16_t);
|
||||
DECL_BITS(GBAAudioRegisterCh4Control, Ratio, 0, 3);
|
||||
DECL_BIT(GBAAudioRegisterCh4Control, Power, 3);
|
||||
DECL_BITS(GBAAudioRegisterCh4Control, Frequency, 4, 4);
|
||||
DECL_BIT(GBAAudioRegisterCh4Control, Stop, 14);
|
||||
DECL_BIT(GBAAudioRegisterCh4Control, Restart, 15);
|
||||
|
||||
struct GBAAudioEnvelope {
|
||||
union {
|
||||
struct {
|
||||
unsigned length : 6;
|
||||
unsigned duty : 2;
|
||||
unsigned stepTime : 3;
|
||||
unsigned direction : 1;
|
||||
unsigned initialVolume : 4;
|
||||
};
|
||||
uint16_t packed;
|
||||
};
|
||||
uint8_t length;
|
||||
uint8_t duty;
|
||||
uint8_t stepTime;
|
||||
uint8_t initialVolume;
|
||||
bool direction;
|
||||
int currentVolume;
|
||||
int dead;
|
||||
int32_t nextStep;
|
||||
};
|
||||
|
||||
struct GBAAudioSquareControl {
|
||||
union {
|
||||
struct {
|
||||
unsigned frequency : 11;
|
||||
unsigned : 3;
|
||||
unsigned stop : 1;
|
||||
unsigned restart : 1;
|
||||
};
|
||||
uint16_t packed;
|
||||
};
|
||||
uint16_t frequency;
|
||||
bool stop;
|
||||
int hi;
|
||||
int32_t nextStep;
|
||||
int32_t endTime;
|
||||
};
|
||||
|
||||
struct GBAAudioChannel1 {
|
||||
union GBAAudioSquareSweep {
|
||||
struct {
|
||||
unsigned shift : 3;
|
||||
unsigned direction : 1;
|
||||
unsigned time : 3;
|
||||
unsigned : 9;
|
||||
};
|
||||
uint16_t packed;
|
||||
struct GBAAudioSquareSweep {
|
||||
uint8_t shift;
|
||||
uint8_t time;
|
||||
bool direction;
|
||||
} sweep;
|
||||
int32_t nextSweep;
|
||||
|
||||
|
@ -64,36 +82,20 @@ struct GBAAudioChannel2 {
|
|||
};
|
||||
|
||||
struct GBAAudioChannel3 {
|
||||
union {
|
||||
struct {
|
||||
unsigned : 5;
|
||||
unsigned size : 1;
|
||||
unsigned bank : 1;
|
||||
unsigned enable : 1;
|
||||
unsigned : 7;
|
||||
};
|
||||
uint16_t packed;
|
||||
struct {
|
||||
bool size;
|
||||
bool bank;
|
||||
bool enable;
|
||||
} bank;
|
||||
|
||||
union {
|
||||
struct {
|
||||
unsigned length : 8;
|
||||
unsigned : 5;
|
||||
unsigned volume : 3;
|
||||
};
|
||||
uint16_t packed;
|
||||
struct {
|
||||
uint8_t length;
|
||||
uint8_t volume;
|
||||
} wave;
|
||||
|
||||
struct {
|
||||
union {
|
||||
struct {
|
||||
unsigned rate : 11;
|
||||
unsigned : 3;
|
||||
unsigned stop : 1;
|
||||
unsigned restart : 1;
|
||||
};
|
||||
uint16_t packed;
|
||||
};
|
||||
uint16_t rate;
|
||||
bool stop;
|
||||
int32_t endTime;
|
||||
} control;
|
||||
|
||||
|
@ -103,18 +105,12 @@ struct GBAAudioChannel3 {
|
|||
|
||||
struct GBAAudioChannel4 {
|
||||
struct GBAAudioEnvelope envelope;
|
||||
|
||||
struct {
|
||||
union {
|
||||
struct {
|
||||
unsigned ratio : 3;
|
||||
unsigned power : 1;
|
||||
unsigned frequency : 4;
|
||||
unsigned : 6;
|
||||
unsigned stop : 1;
|
||||
unsigned restart : 1;
|
||||
};
|
||||
uint16_t packed;
|
||||
};
|
||||
uint8_t ratio;
|
||||
uint8_t frequency;
|
||||
bool power;
|
||||
bool stop;
|
||||
int32_t endTime;
|
||||
} control;
|
||||
|
||||
|
@ -128,6 +124,38 @@ struct GBAAudioFIFO {
|
|||
int8_t sample;
|
||||
};
|
||||
|
||||
DECL_BITFIELD(GBARegisterSOUNDCNT_LO, uint16_t);
|
||||
DECL_BITS(GBARegisterSOUNDCNT_LO, VolumeRight, 0, 3);
|
||||
DECL_BITS(GBARegisterSOUNDCNT_LO, VolumeLeft, 4, 3);
|
||||
DECL_BIT(GBARegisterSOUNDCNT_LO, Ch1Right, 8);
|
||||
DECL_BIT(GBARegisterSOUNDCNT_LO, Ch2Right, 9);
|
||||
DECL_BIT(GBARegisterSOUNDCNT_LO, Ch3Right, 10);
|
||||
DECL_BIT(GBARegisterSOUNDCNT_LO, Ch4Right, 11);
|
||||
DECL_BIT(GBARegisterSOUNDCNT_LO, Ch1Left, 12);
|
||||
DECL_BIT(GBARegisterSOUNDCNT_LO, Ch2Left, 13);
|
||||
DECL_BIT(GBARegisterSOUNDCNT_LO, Ch3Left, 14);
|
||||
DECL_BIT(GBARegisterSOUNDCNT_LO, Ch4Left, 15);
|
||||
|
||||
DECL_BITFIELD(GBARegisterSOUNDCNT_HI, uint16_t);
|
||||
DECL_BITS(GBARegisterSOUNDCNT_HI, Volume, 0, 2);
|
||||
DECL_BIT(GBARegisterSOUNDCNT_HI, VolumeChA, 2);
|
||||
DECL_BIT(GBARegisterSOUNDCNT_HI, VolumeChB, 3);
|
||||
DECL_BIT(GBARegisterSOUNDCNT_HI, ChARight, 8);
|
||||
DECL_BIT(GBARegisterSOUNDCNT_HI, ChALeft, 9);
|
||||
DECL_BIT(GBARegisterSOUNDCNT_HI, ChATimer, 10);
|
||||
DECL_BIT(GBARegisterSOUNDCNT_HI, ChAReset, 11);
|
||||
DECL_BIT(GBARegisterSOUNDCNT_HI, ChBRight, 12);
|
||||
DECL_BIT(GBARegisterSOUNDCNT_HI, ChBLeft, 13);
|
||||
DECL_BIT(GBARegisterSOUNDCNT_HI, ChBTimer, 14);
|
||||
DECL_BIT(GBARegisterSOUNDCNT_HI, ChBReset, 15);
|
||||
|
||||
DECL_BITFIELD(GBARegisterSOUNDCNT_X, uint16_t);
|
||||
DECL_BIT(GBARegisterSOUNDCNT_X, PlayingCh1, 0);
|
||||
DECL_BIT(GBARegisterSOUNDCNT_X, PlayingCh2, 1);
|
||||
DECL_BIT(GBARegisterSOUNDCNT_X, PlayingCh3, 2);
|
||||
DECL_BIT(GBARegisterSOUNDCNT_X, PlayingCh4, 3);
|
||||
DECL_BIT(GBARegisterSOUNDCNT_X, Enable, 7);
|
||||
|
||||
struct GBAAudio {
|
||||
struct GBA* p;
|
||||
|
||||
|
@ -142,54 +170,32 @@ struct GBAAudio {
|
|||
struct CircleBuffer left;
|
||||
struct CircleBuffer right;
|
||||
|
||||
union {
|
||||
struct {
|
||||
unsigned volumeRight : 3;
|
||||
unsigned : 1;
|
||||
unsigned volumeLeft : 3;
|
||||
unsigned : 1;
|
||||
unsigned ch1Right : 1;
|
||||
unsigned ch2Right : 1;
|
||||
unsigned ch3Right : 1;
|
||||
unsigned ch4Right : 1;
|
||||
unsigned ch1Left : 1;
|
||||
unsigned ch2Left : 1;
|
||||
unsigned ch3Left : 1;
|
||||
unsigned ch4Left : 1;
|
||||
};
|
||||
uint16_t soundcntLo;
|
||||
};
|
||||
uint8_t volumeRight;
|
||||
uint8_t volumeLeft;
|
||||
bool ch1Right;
|
||||
bool ch2Right;
|
||||
bool ch3Right;
|
||||
bool ch4Right;
|
||||
bool ch1Left;
|
||||
bool ch2Left;
|
||||
bool ch3Left;
|
||||
bool ch4Left;
|
||||
|
||||
union {
|
||||
struct {
|
||||
unsigned volume : 2;
|
||||
unsigned volumeChA : 1;
|
||||
unsigned volumeChB : 1;
|
||||
unsigned : 4;
|
||||
unsigned chARight : 1;
|
||||
unsigned chALeft : 1;
|
||||
unsigned chATimer : 1;
|
||||
unsigned chAReset : 1;
|
||||
unsigned chBRight : 1;
|
||||
unsigned chBLeft : 1;
|
||||
unsigned chBTimer : 1;
|
||||
unsigned chBReset : 1;
|
||||
};
|
||||
uint16_t soundcntHi;
|
||||
};
|
||||
uint8_t volume;
|
||||
bool volumeChA;
|
||||
bool volumeChB;
|
||||
bool chARight;
|
||||
bool chALeft;
|
||||
bool chATimer;
|
||||
bool chBRight;
|
||||
bool chBLeft;
|
||||
bool chBTimer;
|
||||
|
||||
union {
|
||||
struct {
|
||||
unsigned playingCh1 : 1;
|
||||
unsigned playingCh2 : 1;
|
||||
unsigned playingCh3 : 1;
|
||||
unsigned playingCh4 : 1;
|
||||
unsigned : 3;
|
||||
unsigned enable : 1;
|
||||
unsigned : 8;
|
||||
};
|
||||
uint16_t soundcntX;
|
||||
};
|
||||
bool playingCh1;
|
||||
bool playingCh2;
|
||||
bool playingCh3;
|
||||
bool playingCh4;
|
||||
bool enable;
|
||||
|
||||
unsigned sampleRate;
|
||||
|
||||
|
|
|
@ -482,7 +482,7 @@ void GBAIODeserialize(struct GBA* gba, struct GBASerializedState* state) {
|
|||
gba->memory.dma[i].nextDest = state->dma[i].nextDest;
|
||||
gba->memory.dma[i].nextCount = state->dma[i].nextCount;
|
||||
gba->memory.dma[i].nextEvent = state->dma[i].nextEvent;
|
||||
if (gba->memory.dma[i].timing != DMA_TIMING_NOW) {
|
||||
if (GBADMARegisterGetTiming(gba->memory.dma[i].reg) != DMA_TIMING_NOW) {
|
||||
GBAMemoryScheduleDMA(gba, i, &gba->memory.dma[i]);
|
||||
}
|
||||
|
||||
|
|
|
@ -109,7 +109,7 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) {
|
|||
struct GBA* gba = (struct GBA*) cpu->master;
|
||||
struct GBAMemory* memory = &gba->memory;
|
||||
|
||||
if (cpu->currentPC == gba->busyLoop) {
|
||||
if (address == gba->busyLoop && memory->activeRegion != REGION_BIOS) {
|
||||
GBAHalt(gba);
|
||||
}
|
||||
|
||||
|
@ -118,7 +118,7 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) {
|
|||
return;
|
||||
}
|
||||
if (memory->activeRegion == REGION_BIOS) {
|
||||
memory->biosPrefetch = cpu->memory.load32(cpu, cpu->currentPC + WORD_SIZE_ARM * 2, 0);
|
||||
memory->biosPrefetch = cpu->prefetch;
|
||||
}
|
||||
memory->activeRegion = newRegion;
|
||||
switch (address & ~OFFSET_MASK) {
|
||||
|
@ -163,9 +163,9 @@ int32_t GBALoad32(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
|||
uint32_t value = 0;
|
||||
int wait = 0;
|
||||
|
||||
switch (address & ~OFFSET_MASK) {
|
||||
case BASE_BIOS:
|
||||
if (cpu->currentPC >> BASE_OFFSET == REGION_BIOS) {
|
||||
switch (address >> BASE_OFFSET) {
|
||||
case REGION_BIOS:
|
||||
if (memory->activeRegion == REGION_BIOS) {
|
||||
if (address < SIZE_BIOS) {
|
||||
LOAD_32(value, address, memory->bios);
|
||||
} else {
|
||||
|
@ -175,52 +175,49 @@ int32_t GBALoad32(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
|||
value = memory->biosPrefetch;
|
||||
}
|
||||
break;
|
||||
case BASE_WORKING_RAM:
|
||||
case REGION_WORKING_RAM:
|
||||
LOAD_32(value, address & (SIZE_WORKING_RAM - 1), memory->wram);
|
||||
wait = memory->waitstatesNonseq32[REGION_WORKING_RAM];
|
||||
break;
|
||||
case BASE_WORKING_IRAM:
|
||||
case REGION_WORKING_IRAM:
|
||||
LOAD_32(value, address & (SIZE_WORKING_IRAM - 1), memory->iwram);
|
||||
break;
|
||||
case BASE_IO:
|
||||
case REGION_IO:
|
||||
value = GBAIORead(gba, (address & (SIZE_IO - 1)) & ~2) | (GBAIORead(gba, (address & (SIZE_IO - 1)) | 2) << 16);
|
||||
break;
|
||||
case BASE_PALETTE_RAM:
|
||||
case REGION_PALETTE_RAM:
|
||||
LOAD_32(value, address & (SIZE_PALETTE_RAM - 1), gba->video.palette);
|
||||
break;
|
||||
case BASE_VRAM:
|
||||
case REGION_VRAM:
|
||||
LOAD_32(value, address & 0x0001FFFF, gba->video.renderer->vram);
|
||||
break;
|
||||
case BASE_OAM:
|
||||
case REGION_OAM:
|
||||
LOAD_32(value, address & (SIZE_OAM - 1), gba->video.oam.raw);
|
||||
break;
|
||||
case BASE_CART0:
|
||||
case BASE_CART0_EX:
|
||||
case BASE_CART1:
|
||||
case BASE_CART1_EX:
|
||||
case BASE_CART2:
|
||||
case BASE_CART2_EX:
|
||||
case REGION_CART0:
|
||||
case REGION_CART0_EX:
|
||||
case REGION_CART1:
|
||||
case REGION_CART1_EX:
|
||||
case REGION_CART2:
|
||||
case REGION_CART2_EX:
|
||||
wait = memory->waitstatesNonseq32[address >> BASE_OFFSET];
|
||||
if ((address & (SIZE_CART0 - 1)) < memory->romSize) {
|
||||
LOAD_32(value, address & (SIZE_CART0 - 1), memory->rom);
|
||||
}
|
||||
break;
|
||||
case BASE_CART_SRAM:
|
||||
case BASE_CART_SRAM_MIRROR:
|
||||
case REGION_CART_SRAM:
|
||||
case REGION_CART_SRAM_MIRROR:
|
||||
GBALog(gba, GBA_LOG_STUB, "Unimplemented memory Load32: 0x%08X", address);
|
||||
break;
|
||||
default:
|
||||
GBALog(gba, GBA_LOG_GAME_ERROR, "Bad memory Load32: 0x%08X", address);
|
||||
if (cpu->executionMode == MODE_ARM) {
|
||||
value = cpu->memory.load32(cpu, cpu->currentPC + WORD_SIZE_ARM * 2, 0);
|
||||
} else {
|
||||
value = cpu->memory.load16(cpu, cpu->currentPC + WORD_SIZE_THUMB * 2, 0);
|
||||
value = cpu->prefetch;
|
||||
if (cpu->executionMode == MODE_THUMB) {
|
||||
value |= value << 16;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (cycleCounter) {
|
||||
*cycleCounter += 2 + wait;
|
||||
}
|
||||
|
@ -239,9 +236,9 @@ int16_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
|||
uint16_t value = 0;
|
||||
int wait = 0;
|
||||
|
||||
switch (address & ~OFFSET_MASK) {
|
||||
case BASE_BIOS:
|
||||
if (cpu->currentPC >> BASE_OFFSET == REGION_BIOS) {
|
||||
switch (address >> BASE_OFFSET) {
|
||||
case REGION_BIOS:
|
||||
if (memory->activeRegion == REGION_BIOS) {
|
||||
if (address < SIZE_BIOS) {
|
||||
LOAD_16(value, address, memory->bios);
|
||||
} else {
|
||||
|
@ -251,36 +248,36 @@ int16_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
|||
value = memory->biosPrefetch;
|
||||
}
|
||||
break;
|
||||
case BASE_WORKING_RAM:
|
||||
case REGION_WORKING_RAM:
|
||||
LOAD_16(value, address & (SIZE_WORKING_RAM - 1), memory->wram);
|
||||
wait = memory->waitstatesNonseq16[REGION_WORKING_RAM];
|
||||
break;
|
||||
case BASE_WORKING_IRAM:
|
||||
case REGION_WORKING_IRAM:
|
||||
LOAD_16(value, address & (SIZE_WORKING_IRAM - 1), memory->iwram);
|
||||
break;
|
||||
case BASE_IO:
|
||||
case REGION_IO:
|
||||
value = GBAIORead(gba, address & (SIZE_IO - 1));
|
||||
break;
|
||||
case BASE_PALETTE_RAM:
|
||||
case REGION_PALETTE_RAM:
|
||||
LOAD_16(value, address & (SIZE_PALETTE_RAM - 1), gba->video.palette);
|
||||
break;
|
||||
case BASE_VRAM:
|
||||
case REGION_VRAM:
|
||||
LOAD_16(value, address & 0x0001FFFF, gba->video.renderer->vram);
|
||||
break;
|
||||
case BASE_OAM:
|
||||
case REGION_OAM:
|
||||
LOAD_16(value, address & (SIZE_OAM - 1), gba->video.oam.raw);
|
||||
break;
|
||||
case BASE_CART0:
|
||||
case BASE_CART0_EX:
|
||||
case BASE_CART1:
|
||||
case BASE_CART1_EX:
|
||||
case BASE_CART2:
|
||||
case REGION_CART0:
|
||||
case REGION_CART0_EX:
|
||||
case REGION_CART1:
|
||||
case REGION_CART1_EX:
|
||||
case REGION_CART2:
|
||||
wait = memory->waitstatesNonseq16[address >> BASE_OFFSET];
|
||||
if ((address & (SIZE_CART0 - 1)) < memory->romSize) {
|
||||
LOAD_16(value, address & (SIZE_CART0 - 1), memory->rom);
|
||||
}
|
||||
break;
|
||||
case BASE_CART2_EX:
|
||||
case REGION_CART2_EX:
|
||||
wait = memory->waitstatesNonseq16[address >> BASE_OFFSET];
|
||||
if (memory->savedata.type == SAVEDATA_EEPROM) {
|
||||
value = GBASavedataReadEEPROM(&memory->savedata);
|
||||
|
@ -288,13 +285,13 @@ int16_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
|||
LOAD_16(value, address & (SIZE_CART0 - 1), memory->rom);
|
||||
}
|
||||
break;
|
||||
case BASE_CART_SRAM:
|
||||
case BASE_CART_SRAM_MIRROR:
|
||||
case REGION_CART_SRAM:
|
||||
case REGION_CART_SRAM_MIRROR:
|
||||
GBALog(gba, GBA_LOG_STUB, "Unimplemented memory Load16: 0x%08X", address);
|
||||
break;
|
||||
default:
|
||||
GBALog(gba, GBA_LOG_GAME_ERROR, "Bad memory Load16: 0x%08X", address);
|
||||
value = cpu->memory.load16(cpu, cpu->currentPC + (cpu->executionMode == MODE_ARM ? WORD_SIZE_ARM : WORD_SIZE_THUMB) * 2, 0);
|
||||
value = cpu->prefetch;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -316,9 +313,9 @@ int8_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
|||
int8_t value = 0;
|
||||
int wait = 0;
|
||||
|
||||
switch (address & ~OFFSET_MASK) {
|
||||
case BASE_BIOS:
|
||||
if (cpu->currentPC >> BASE_OFFSET == REGION_BIOS) {
|
||||
switch (address >> BASE_OFFSET) {
|
||||
case REGION_BIOS:
|
||||
if (memory->activeRegion == REGION_BIOS) {
|
||||
if (address < SIZE_BIOS) {
|
||||
value = ((int8_t*) memory->bios)[address];
|
||||
} else {
|
||||
|
@ -328,38 +325,38 @@ int8_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
|||
value = memory->biosPrefetch;
|
||||
}
|
||||
break;
|
||||
case BASE_WORKING_RAM:
|
||||
case REGION_WORKING_RAM:
|
||||
value = ((int8_t*) memory->wram)[address & (SIZE_WORKING_RAM - 1)];
|
||||
wait = memory->waitstatesNonseq16[REGION_WORKING_RAM];
|
||||
break;
|
||||
case BASE_WORKING_IRAM:
|
||||
case REGION_WORKING_IRAM:
|
||||
value = ((int8_t*) memory->iwram)[address & (SIZE_WORKING_IRAM - 1)];
|
||||
break;
|
||||
case BASE_IO:
|
||||
case REGION_IO:
|
||||
value = (GBAIORead(gba, address & 0xFFFE) >> ((address & 0x0001) << 3)) & 0xFF;
|
||||
break;
|
||||
case BASE_PALETTE_RAM:
|
||||
value = ((int8_t*) gba->video.renderer->palette)[address & (SIZE_PALETTE_RAM - 1)];
|
||||
case REGION_PALETTE_RAM:
|
||||
value = ((int8_t*) gba->video.palette)[address & (SIZE_PALETTE_RAM - 1)];
|
||||
break;
|
||||
case BASE_VRAM:
|
||||
case REGION_VRAM:
|
||||
value = ((int8_t*) gba->video.renderer->vram)[address & 0x0001FFFF];
|
||||
break;
|
||||
case BASE_OAM:
|
||||
case REGION_OAM:
|
||||
GBALog(gba, GBA_LOG_STUB, "Unimplemented memory Load8: 0x%08X", address);
|
||||
break;
|
||||
case BASE_CART0:
|
||||
case BASE_CART0_EX:
|
||||
case BASE_CART1:
|
||||
case BASE_CART1_EX:
|
||||
case BASE_CART2:
|
||||
case BASE_CART2_EX:
|
||||
case REGION_CART0:
|
||||
case REGION_CART0_EX:
|
||||
case REGION_CART1:
|
||||
case REGION_CART1_EX:
|
||||
case REGION_CART2:
|
||||
case REGION_CART2_EX:
|
||||
wait = memory->waitstatesNonseq16[address >> BASE_OFFSET];
|
||||
if ((address & (SIZE_CART0 - 1)) < memory->romSize) {
|
||||
value = ((int8_t*) memory->rom)[address & (SIZE_CART0 - 1)];
|
||||
}
|
||||
break;
|
||||
case BASE_CART_SRAM:
|
||||
case BASE_CART_SRAM_MIRROR:
|
||||
case REGION_CART_SRAM:
|
||||
case REGION_CART_SRAM_MIRROR:
|
||||
wait = memory->waitstatesNonseq16[address >> BASE_OFFSET];
|
||||
if (memory->savedata.type == SAVEDATA_NONE) {
|
||||
GBASavedataInitSRAM(&memory->savedata);
|
||||
|
@ -372,7 +369,7 @@ int8_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
|||
break;
|
||||
default:
|
||||
GBALog(gba, GBA_LOG_GAME_ERROR, "Bad memory Load8: 0x%08x", address);
|
||||
value = cpu->memory.load16(cpu, cpu->currentPC + (cpu->executionMode == MODE_ARM ? WORD_SIZE_ARM : WORD_SIZE_THUMB) * 2, 0) >> ((address & 1) << 3);
|
||||
value = cpu->prefetch & 0xFF;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -387,39 +384,39 @@ void GBAStore32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycle
|
|||
struct GBAMemory* memory = &gba->memory;
|
||||
int wait = 0;
|
||||
|
||||
switch (address & ~OFFSET_MASK) {
|
||||
case BASE_WORKING_RAM:
|
||||
switch (address >> BASE_OFFSET) {
|
||||
case REGION_WORKING_RAM:
|
||||
STORE_32(value, address & (SIZE_WORKING_RAM - 1), memory->wram);
|
||||
wait = memory->waitstatesNonseq32[REGION_WORKING_RAM];
|
||||
break;
|
||||
case BASE_WORKING_IRAM:
|
||||
case REGION_WORKING_IRAM:
|
||||
STORE_32(value, address & (SIZE_WORKING_IRAM - 1), memory->iwram);
|
||||
break;
|
||||
case BASE_IO:
|
||||
case REGION_IO:
|
||||
GBAIOWrite32(gba, address & (SIZE_IO - 1), value);
|
||||
break;
|
||||
case BASE_PALETTE_RAM:
|
||||
case REGION_PALETTE_RAM:
|
||||
STORE_32(value, address & (SIZE_PALETTE_RAM - 1), gba->video.palette);
|
||||
gba->video.renderer->writePalette(gba->video.renderer, (address & (SIZE_PALETTE_RAM - 1)) + 2, value >> 16);
|
||||
gba->video.renderer->writePalette(gba->video.renderer, address & (SIZE_PALETTE_RAM - 1), value);
|
||||
break;
|
||||
case BASE_VRAM:
|
||||
case REGION_VRAM:
|
||||
if ((address & OFFSET_MASK) < SIZE_VRAM) {
|
||||
STORE_32(value, address & 0x0001FFFF, gba->video.renderer->vram);
|
||||
} else if ((address & OFFSET_MASK) < 0x00020000) {
|
||||
STORE_32(value, address & 0x00017FFF, gba->video.renderer->vram);
|
||||
}
|
||||
break;
|
||||
case BASE_OAM:
|
||||
case REGION_OAM:
|
||||
STORE_32(value, address & (SIZE_OAM - 1), gba->video.oam.raw);
|
||||
gba->video.renderer->writeOAM(gba->video.renderer, (address & (SIZE_OAM - 4)) >> 1);
|
||||
gba->video.renderer->writeOAM(gba->video.renderer, ((address & (SIZE_OAM - 4)) >> 1) + 1);
|
||||
break;
|
||||
case BASE_CART0:
|
||||
case REGION_CART0:
|
||||
GBALog(gba, GBA_LOG_STUB, "Unimplemented memory Store32: 0x%08X", address);
|
||||
break;
|
||||
case BASE_CART_SRAM:
|
||||
case BASE_CART_SRAM_MIRROR:
|
||||
case REGION_CART_SRAM:
|
||||
case REGION_CART_SRAM_MIRROR:
|
||||
GBALog(gba, GBA_LOG_STUB, "Unimplemented memory Store32: 0x%08X", address);
|
||||
break;
|
||||
default:
|
||||
|
@ -437,33 +434,33 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle
|
|||
struct GBAMemory* memory = &gba->memory;
|
||||
int wait = 0;
|
||||
|
||||
switch (address & ~OFFSET_MASK) {
|
||||
case BASE_WORKING_RAM:
|
||||
switch (address >> BASE_OFFSET) {
|
||||
case REGION_WORKING_RAM:
|
||||
STORE_16(value, address & (SIZE_WORKING_RAM - 1), memory->wram);
|
||||
wait = memory->waitstatesNonseq16[REGION_WORKING_RAM];
|
||||
break;
|
||||
case BASE_WORKING_IRAM:
|
||||
case REGION_WORKING_IRAM:
|
||||
STORE_16(value, address & (SIZE_WORKING_IRAM - 1), memory->iwram);
|
||||
break;
|
||||
case BASE_IO:
|
||||
case REGION_IO:
|
||||
GBAIOWrite(gba, address & (SIZE_IO - 1), value);
|
||||
break;
|
||||
case BASE_PALETTE_RAM:
|
||||
case REGION_PALETTE_RAM:
|
||||
STORE_16(value, address & (SIZE_PALETTE_RAM - 1), gba->video.palette);
|
||||
gba->video.renderer->writePalette(gba->video.renderer, address & (SIZE_PALETTE_RAM - 1), value);
|
||||
break;
|
||||
case BASE_VRAM:
|
||||
case REGION_VRAM:
|
||||
if ((address & OFFSET_MASK) < SIZE_VRAM) {
|
||||
STORE_16(value, address & 0x0001FFFF, gba->video.renderer->vram);
|
||||
} else if ((address & OFFSET_MASK) < 0x00020000) {
|
||||
STORE_16(value, address & 0x00017FFF, gba->video.renderer->vram);
|
||||
}
|
||||
break;
|
||||
case BASE_OAM:
|
||||
case REGION_OAM:
|
||||
STORE_16(value, address & (SIZE_OAM - 1), gba->video.oam.raw);
|
||||
gba->video.renderer->writeOAM(gba->video.renderer, (address & (SIZE_OAM - 1)) >> 1);
|
||||
break;
|
||||
case BASE_CART0:
|
||||
case REGION_CART0:
|
||||
if (IS_GPIO_REGISTER(address & 0xFFFFFF)) {
|
||||
uint32_t reg = address & 0xFFFFFF;
|
||||
GBAGPIOWrite(&memory->gpio, reg, value);
|
||||
|
@ -471,14 +468,14 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle
|
|||
GBALog(gba, GBA_LOG_GAME_ERROR, "Bad cartridge Store16: 0x%08X", address);
|
||||
}
|
||||
break;
|
||||
case BASE_CART2_EX:
|
||||
case REGION_CART2_EX:
|
||||
if (memory->savedata.type == SAVEDATA_NONE) {
|
||||
GBASavedataInitEEPROM(&memory->savedata);
|
||||
}
|
||||
GBASavedataWriteEEPROM(&memory->savedata, value, 1);
|
||||
break;
|
||||
case BASE_CART_SRAM:
|
||||
case BASE_CART_SRAM_MIRROR:
|
||||
case REGION_CART_SRAM:
|
||||
case REGION_CART_SRAM_MIRROR:
|
||||
GBALog(gba, GBA_LOG_STUB, "Unimplemented memory Store16: 0x%08X", address);
|
||||
break;
|
||||
default:
|
||||
|
@ -496,21 +493,21 @@ void GBAStore8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCo
|
|||
struct GBAMemory* memory = &gba->memory;
|
||||
int wait = 0;
|
||||
|
||||
switch (address & ~OFFSET_MASK) {
|
||||
case BASE_WORKING_RAM:
|
||||
switch (address >> BASE_OFFSET) {
|
||||
case REGION_WORKING_RAM:
|
||||
((int8_t*) memory->wram)[address & (SIZE_WORKING_RAM - 1)] = value;
|
||||
wait = memory->waitstatesNonseq16[REGION_WORKING_RAM];
|
||||
break;
|
||||
case BASE_WORKING_IRAM:
|
||||
case REGION_WORKING_IRAM:
|
||||
((int8_t*) memory->iwram)[address & (SIZE_WORKING_IRAM - 1)] = value;
|
||||
break;
|
||||
case BASE_IO:
|
||||
case REGION_IO:
|
||||
GBAIOWrite8(gba, address & (SIZE_IO - 1), value);
|
||||
break;
|
||||
case BASE_PALETTE_RAM:
|
||||
case REGION_PALETTE_RAM:
|
||||
GBALog(gba, GBA_LOG_STUB, "Unimplemented memory Store8: 0x%08X", address);
|
||||
break;
|
||||
case BASE_VRAM:
|
||||
case REGION_VRAM:
|
||||
if (address >= 0x06018000) {
|
||||
// TODO: check BG mode
|
||||
GBALog(gba, GBA_LOG_GAME_ERROR, "Cannot Store8 to OBJ: 0x%08X", address);
|
||||
|
@ -519,14 +516,14 @@ void GBAStore8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCo
|
|||
((int8_t*) gba->video.renderer->vram)[address & 0x1FFFE] = value;
|
||||
((int8_t*) gba->video.renderer->vram)[(address & 0x1FFFE) | 1] = value;
|
||||
break;
|
||||
case BASE_OAM:
|
||||
case REGION_OAM:
|
||||
GBALog(gba, GBA_LOG_GAME_ERROR, "Cannot Store8 to OAM: 0x%08X", address);
|
||||
break;
|
||||
case BASE_CART0:
|
||||
case REGION_CART0:
|
||||
GBALog(gba, GBA_LOG_STUB, "Unimplemented memory Store8: 0x%08X", address);
|
||||
break;
|
||||
case BASE_CART_SRAM:
|
||||
case BASE_CART_SRAM_MIRROR:
|
||||
case REGION_CART_SRAM:
|
||||
case REGION_CART_SRAM_MIRROR:
|
||||
if (memory->savedata.type == SAVEDATA_NONE) {
|
||||
if (address == SAVEDATA_FLASH_BASE) {
|
||||
GBASavedataInitFlash(&memory->savedata);
|
||||
|
@ -654,26 +651,26 @@ void GBAMemoryWriteDMACNT_LO(struct GBA* gba, int dma, uint16_t count) {
|
|||
uint16_t GBAMemoryWriteDMACNT_HI(struct GBA* gba, int dma, uint16_t control) {
|
||||
struct GBAMemory* memory = &gba->memory;
|
||||
struct GBADMA* currentDma = &memory->dma[dma];
|
||||
int wasEnabled = currentDma->enable;
|
||||
currentDma->packed = control;
|
||||
int wasEnabled = GBADMARegisterIsEnable(currentDma->reg);
|
||||
currentDma->reg = control;
|
||||
|
||||
if (currentDma->drq) {
|
||||
if (GBADMARegisterIsDRQ(currentDma->reg)) {
|
||||
GBALog(gba, GBA_LOG_STUB, "DRQ not implemented");
|
||||
}
|
||||
|
||||
if (!wasEnabled && currentDma->enable) {
|
||||
if (!wasEnabled && GBADMARegisterIsEnable(currentDma->reg)) {
|
||||
currentDma->nextSource = currentDma->source;
|
||||
currentDma->nextDest = currentDma->dest;
|
||||
currentDma->nextCount = currentDma->count;
|
||||
GBAMemoryScheduleDMA(gba, dma, currentDma);
|
||||
}
|
||||
// If the DMA has already occurred, this value might have changed since the function started
|
||||
return currentDma->packed;
|
||||
return currentDma->reg;
|
||||
};
|
||||
|
||||
void GBAMemoryScheduleDMA(struct GBA* gba, int number, struct GBADMA* info) {
|
||||
struct ARMCore* cpu = gba->cpu;
|
||||
switch (info->timing) {
|
||||
switch (GBADMARegisterGetTiming(info->reg)) {
|
||||
case DMA_TIMING_NOW:
|
||||
info->nextEvent = cpu->cycles;
|
||||
GBAMemoryUpdateDMAs(gba, 0);
|
||||
|
@ -709,7 +706,7 @@ void GBAMemoryRunHblankDMAs(struct GBA* gba, int32_t cycles) {
|
|||
int i;
|
||||
for (i = 0; i < 4; ++i) {
|
||||
dma = &memory->dma[i];
|
||||
if (dma->enable && dma->timing == DMA_TIMING_HBLANK) {
|
||||
if (GBADMARegisterIsEnable(dma->reg) && GBADMARegisterGetTiming(dma->reg) == DMA_TIMING_HBLANK) {
|
||||
dma->nextEvent = cycles;
|
||||
}
|
||||
}
|
||||
|
@ -722,7 +719,7 @@ void GBAMemoryRunVblankDMAs(struct GBA* gba, int32_t cycles) {
|
|||
int i;
|
||||
for (i = 0; i < 4; ++i) {
|
||||
dma = &memory->dma[i];
|
||||
if (dma->enable && dma->timing == DMA_TIMING_VBLANK) {
|
||||
if (GBADMARegisterIsEnable(dma->reg) && GBADMARegisterGetTiming(dma->reg) == DMA_TIMING_VBLANK) {
|
||||
dma->nextEvent = cycles;
|
||||
}
|
||||
}
|
||||
|
@ -755,7 +752,7 @@ void GBAMemoryUpdateDMAs(struct GBA* gba, int32_t cycles) {
|
|||
struct GBADMA* dma = &memory->dma[i];
|
||||
if (dma->nextEvent != INT_MAX) {
|
||||
dma->nextEvent -= cycles;
|
||||
if (dma->enable) {
|
||||
if (GBADMARegisterIsEnable(dma->reg)) {
|
||||
memory->activeDMA = i;
|
||||
memory->nextDMA = dma->nextEvent;
|
||||
}
|
||||
|
@ -769,9 +766,9 @@ void GBAMemoryUpdateDMAs(struct GBA* gba, int32_t cycles) {
|
|||
void GBAMemoryServiceDMA(struct GBA* gba, int number, struct GBADMA* info) {
|
||||
struct GBAMemory* memory = &gba->memory;
|
||||
struct ARMCore* cpu = gba->cpu;
|
||||
uint32_t width = info->width ? 4 : 2;
|
||||
int sourceOffset = DMA_OFFSET[info->srcControl] * width;
|
||||
int destOffset = DMA_OFFSET[info->dstControl] * width;
|
||||
uint32_t width = GBADMARegisterGetWidth(info->reg) ? 4 : 2;
|
||||
int sourceOffset = DMA_OFFSET[GBADMARegisterGetSrcControl(info->reg)] * width;
|
||||
int destOffset = DMA_OFFSET[GBADMARegisterGetDestControl(info->reg)] * width;
|
||||
int32_t wordsRemaining = info->nextCount;
|
||||
uint32_t source = info->nextSource;
|
||||
uint32_t dest = info->nextDest;
|
||||
|
@ -831,20 +828,20 @@ void GBAMemoryServiceDMA(struct GBA* gba, int number, struct GBADMA* info) {
|
|||
}
|
||||
|
||||
if (!wordsRemaining) {
|
||||
if (!info->repeat) {
|
||||
info->enable = 0;
|
||||
if (!GBADMARegisterIsRepeat(info->reg)) {
|
||||
info->reg = GBADMARegisterClearEnable(info->reg);
|
||||
info->nextEvent = INT_MAX;
|
||||
|
||||
// Clear the enable bit in memory
|
||||
memory->io[(REG_DMA0CNT_HI + number * (REG_DMA1CNT_HI - REG_DMA0CNT_HI)) >> 1] &= 0x7FE0;
|
||||
} else {
|
||||
info->nextCount = info->count;
|
||||
if (info->dstControl == DMA_INCREMENT_RELOAD) {
|
||||
if (GBADMARegisterGetDestControl(info->reg) == DMA_INCREMENT_RELOAD) {
|
||||
info->nextDest = info->dest;
|
||||
}
|
||||
GBAMemoryScheduleDMA(gba, number, info);
|
||||
}
|
||||
if (info->doIrq) {
|
||||
if (GBADMARegisterIsDoIRQ(info->reg)) {
|
||||
GBARaiseIRQ(gba, IRQ_DMA0 + number);
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -80,21 +80,19 @@ enum DMATiming {
|
|||
DMA_TIMING_CUSTOM = 3
|
||||
};
|
||||
|
||||
|
||||
DECL_BITFIELD(GBADMARegister, uint16_t);
|
||||
DECL_BITS(GBADMARegister, DestControl, 5, 2);
|
||||
DECL_BITS(GBADMARegister, SrcControl, 7, 2);
|
||||
DECL_BIT(GBADMARegister, Repeat, 9);
|
||||
DECL_BIT(GBADMARegister, Width, 10);
|
||||
DECL_BIT(GBADMARegister, DRQ, 11);
|
||||
DECL_BITS(GBADMARegister, Timing, 12, 2);
|
||||
DECL_BIT(GBADMARegister, DoIRQ, 14);
|
||||
DECL_BIT(GBADMARegister, Enable, 15);
|
||||
|
||||
struct GBADMA {
|
||||
union {
|
||||
struct {
|
||||
int : 5;
|
||||
enum DMAControl dstControl : 2;
|
||||
enum DMAControl srcControl : 2;
|
||||
unsigned repeat : 1;
|
||||
unsigned width : 1;
|
||||
unsigned drq : 1;
|
||||
enum DMATiming timing : 2;
|
||||
unsigned doIrq : 1;
|
||||
unsigned enable : 1;
|
||||
};
|
||||
uint16_t packed;
|
||||
};
|
||||
GBADMARegister reg;
|
||||
|
||||
uint32_t source;
|
||||
uint32_t dest;
|
||||
|
|
|
@ -7,12 +7,15 @@
|
|||
#include "gba-video.h"
|
||||
|
||||
#include "util/memory.h"
|
||||
#include "util/png-io.h"
|
||||
#include "util/vfs.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#ifdef USE_PNG
|
||||
#include "util/png-io.h"
|
||||
#include <png.h>
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
|
||||
const uint32_t GBA_SAVESTATE_MAGIC = 0x01000000;
|
||||
|
||||
|
@ -70,9 +73,15 @@ void GBADeserialize(struct GBA* gba, struct GBASerializedState* state) {
|
|||
gba->cpu->nextEvent = state->cpu.nextEvent;
|
||||
memcpy(gba->cpu->bankedRegisters, state->cpu.bankedRegisters, 6 * 7 * sizeof(int32_t));
|
||||
memcpy(gba->cpu->bankedSPSRs, state->cpu.bankedSPSRs, 6 * sizeof(int32_t));
|
||||
gba->cpu->executionMode = gba->cpu->cpsr.t ? MODE_THUMB : MODE_ARM;
|
||||
gba->cpu->privilegeMode = gba->cpu->cpsr.priv;
|
||||
gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]);
|
||||
if (gba->cpu->cpsr.t) {
|
||||
gba->cpu->executionMode = MODE_THUMB;
|
||||
LOAD_16(gba->cpu->prefetch, (gba->cpu->gprs[ARM_PC] - WORD_SIZE_THUMB) & gba->cpu->memory.activeMask, gba->cpu->memory.activeRegion);
|
||||
} else {
|
||||
gba->cpu->executionMode = MODE_ARM;
|
||||
LOAD_32(gba->cpu->prefetch, (gba->cpu->gprs[ARM_PC] - WORD_SIZE_ARM) & gba->cpu->memory.activeMask, gba->cpu->memory.activeRegion);
|
||||
}
|
||||
|
||||
GBAMemoryDeserialize(&gba->memory, state);
|
||||
GBAIODeserialize(gba, state);
|
||||
|
@ -107,6 +116,7 @@ static struct VFile* _getStateVf(struct GBA* gba, struct VDir* dir, int slot, bo
|
|||
return vf;
|
||||
}
|
||||
|
||||
#ifdef USE_PNG
|
||||
static bool _savePNGState(struct GBA* gba, struct VFile* vf) {
|
||||
unsigned stride;
|
||||
void* pixels = 0;
|
||||
|
@ -162,6 +172,7 @@ static bool _loadPNGState(struct GBA* gba, struct VFile* vf) {
|
|||
GBASyncPostFrame(gba->sync);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool GBASaveState(struct GBA* gba, struct VDir* dir, int slot, bool screenshot) {
|
||||
struct VFile* vf = _getStateVf(gba, dir, slot, true);
|
||||
|
@ -192,23 +203,28 @@ bool GBASaveStateNamed(struct GBA* gba, struct VFile* vf, bool screenshot) {
|
|||
}
|
||||
GBASerialize(gba, state);
|
||||
vf->unmap(vf, state, sizeof(struct GBASerializedState));
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
#ifdef USE_PNG
|
||||
else {
|
||||
return _savePNGState(gba, vf);
|
||||
}
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GBALoadStateNamed(struct GBA* gba, struct VFile* vf) {
|
||||
if (!isPNG(vf)) {
|
||||
struct GBASerializedState* state = vf->map(vf, sizeof(struct GBASerializedState), MAP_READ);
|
||||
if (!state) {
|
||||
return false;
|
||||
}
|
||||
GBADeserialize(gba, state);
|
||||
vf->unmap(vf, state, sizeof(struct GBASerializedState));
|
||||
} else {
|
||||
#ifdef USE_PNG
|
||||
if (isPNG(vf)) {
|
||||
return _loadPNGState(gba, vf);
|
||||
}
|
||||
#endif
|
||||
struct GBASerializedState* state = vf->map(vf, sizeof(struct GBASerializedState), MAP_READ);
|
||||
if (!state) {
|
||||
return false;
|
||||
}
|
||||
GBADeserialize(gba, state);
|
||||
vf->unmap(vf, state, sizeof(struct GBASerializedState));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -144,6 +144,7 @@ static THREAD_ENTRY _GBAThreadRun(void* context) {
|
|||
}
|
||||
|
||||
if (threadContext->debugger) {
|
||||
threadContext->debugger->log = GBADebuggerLogShim;
|
||||
GBAAttachDebugger(&gba, threadContext->debugger);
|
||||
ARMDebuggerEnter(threadContext->debugger, DEBUGGER_ENTER_ATTACHED);
|
||||
}
|
||||
|
@ -167,7 +168,7 @@ static THREAD_ENTRY _GBAThreadRun(void* context) {
|
|||
}
|
||||
} else {
|
||||
while (threadContext->state == THREAD_RUNNING) {
|
||||
ARMRun(&cpu);
|
||||
ARMRunLoop(&cpu);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -519,6 +520,7 @@ struct GBAThread* GBAThreadGetContext(void) {
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_PNG
|
||||
void GBAThreadTakeScreenshot(struct GBAThread* threadContext) {
|
||||
unsigned stride;
|
||||
void* pixels = 0;
|
||||
|
@ -530,6 +532,7 @@ void GBAThreadTakeScreenshot(struct GBAThread* threadContext) {
|
|||
PNGWriteClose(png, info);
|
||||
vf->close(vf);
|
||||
}
|
||||
#endif
|
||||
|
||||
void GBASyncPostFrame(struct GBASync* sync) {
|
||||
if (!sync) {
|
||||
|
@ -577,9 +580,7 @@ bool GBASyncWaitFrameStart(struct GBASync* sync, int frameskip) {
|
|||
if (!sync->videoFrameOn && !sync->videoFramePending) {
|
||||
return false;
|
||||
}
|
||||
if (!sync->videoFramePending) {
|
||||
ConditionWait(&sync->videoFrameAvailableCond, &sync->videoFrameMutex);
|
||||
}
|
||||
ConditionWait(&sync->videoFrameAvailableCond, &sync->videoFrameMutex);
|
||||
sync->videoFramePending = 0;
|
||||
sync->videoFrameSkip = frameskip;
|
||||
return true;
|
||||
|
|
|
@ -112,7 +112,9 @@ void GBAThreadTogglePause(struct GBAThread* threadContext);
|
|||
void GBAThreadPauseFromThread(struct GBAThread* threadContext);
|
||||
struct GBAThread* GBAThreadGetContext(void);
|
||||
|
||||
#ifdef USE_PNG
|
||||
void GBAThreadTakeScreenshot(struct GBAThread* threadContext);
|
||||
#endif
|
||||
|
||||
void GBASyncPostFrame(struct GBASync* sync);
|
||||
bool GBASyncWaitFrameStart(struct GBASync* sync, int frameskip);
|
||||
|
|
|
@ -36,14 +36,7 @@ void GBAVideoInit(struct GBAVideo* video) {
|
|||
}
|
||||
|
||||
void GBAVideoReset(struct GBAVideo* video) {
|
||||
video->inHblank = 0;
|
||||
video->inVblank = 0;
|
||||
video->vcounter = 0;
|
||||
video->vblankIRQ = 0;
|
||||
video->hblankIRQ = 0;
|
||||
video->vcounterIRQ = 0;
|
||||
video->vcountSetting = 0;
|
||||
|
||||
video->dispstat = 0;
|
||||
video->vcount = 0;
|
||||
|
||||
video->lastHblank = 0;
|
||||
|
@ -94,9 +87,9 @@ int32_t GBAVideoProcessEvents(struct GBAVideo* video, int32_t cycles) {
|
|||
video->nextHblankIRQ -= video->eventDiff;
|
||||
video->nextVcounterIRQ -= video->eventDiff;
|
||||
|
||||
if (video->inHblank) {
|
||||
if (GBARegisterDISPSTATIsInHblank(video->dispstat)) {
|
||||
// End Hblank
|
||||
video->inHblank = 0;
|
||||
video->dispstat = GBARegisterDISPSTATClearInHblank(video->dispstat);
|
||||
video->nextEvent = video->nextHblank;
|
||||
|
||||
++video->vcount;
|
||||
|
@ -104,13 +97,13 @@ int32_t GBAVideoProcessEvents(struct GBAVideo* video, int32_t cycles) {
|
|||
|
||||
switch (video->vcount) {
|
||||
case VIDEO_VERTICAL_PIXELS:
|
||||
video->inVblank = 1;
|
||||
video->dispstat = GBARegisterDISPSTATFillInVblank(video->dispstat);
|
||||
if (GBASyncDrawingFrame(video->p->sync)) {
|
||||
video->renderer->finishFrame(video->renderer);
|
||||
}
|
||||
video->nextVblankIRQ = video->nextEvent + VIDEO_TOTAL_LENGTH;
|
||||
GBAMemoryRunVblankDMAs(video->p, lastEvent);
|
||||
if (video->vblankIRQ) {
|
||||
if (GBARegisterDISPSTATIsVblankIRQ(video->dispstat)) {
|
||||
GBARaiseIRQ(video->p, IRQ_VBLANK);
|
||||
}
|
||||
GBASyncPostFrame(video->p->sync);
|
||||
|
@ -119,7 +112,7 @@ int32_t GBAVideoProcessEvents(struct GBAVideo* video, int32_t cycles) {
|
|||
if (video->p->rr) {
|
||||
GBARRNextFrame(video->p->rr);
|
||||
}
|
||||
video->inVblank = 0;
|
||||
video->dispstat = GBARegisterDISPSTATClearInVblank(video->dispstat);
|
||||
break;
|
||||
case VIDEO_VERTICAL_TOTAL_PIXELS:
|
||||
video->vcount = 0;
|
||||
|
@ -127,14 +120,18 @@ int32_t GBAVideoProcessEvents(struct GBAVideo* video, int32_t cycles) {
|
|||
break;
|
||||
}
|
||||
|
||||
video->vcounter = video->vcount == video->vcountSetting;
|
||||
if (video->vcounter && video->vcounterIRQ) {
|
||||
GBARaiseIRQ(video->p, IRQ_VCOUNTER);
|
||||
video->nextVcounterIRQ += VIDEO_TOTAL_LENGTH;
|
||||
if (video->vcount == GBARegisterDISPSTATGetVcountSetting(video->dispstat)) {
|
||||
video->dispstat = GBARegisterDISPSTATFillVcounter(video->dispstat);
|
||||
if (GBARegisterDISPSTATIsVcounterIRQ(video->dispstat)) {
|
||||
GBARaiseIRQ(video->p, IRQ_VCOUNTER);
|
||||
video->nextVcounterIRQ += VIDEO_TOTAL_LENGTH;
|
||||
}
|
||||
} else {
|
||||
video->dispstat = GBARegisterDISPSTATClearVcounter(video->dispstat);
|
||||
}
|
||||
} else {
|
||||
// Begin Hblank
|
||||
video->inHblank = 1;
|
||||
video->dispstat = GBARegisterDISPSTATFillInHblank(video->dispstat);
|
||||
video->lastHblank = video->nextHblank;
|
||||
video->nextEvent = video->lastHblank + VIDEO_HBLANK_LENGTH;
|
||||
video->nextHblank = video->nextEvent + VIDEO_HDRAW_LENGTH;
|
||||
|
@ -147,7 +144,7 @@ int32_t GBAVideoProcessEvents(struct GBAVideo* video, int32_t cycles) {
|
|||
if (video->vcount < VIDEO_VERTICAL_PIXELS) {
|
||||
GBAMemoryRunHblankDMAs(video->p, lastEvent);
|
||||
}
|
||||
if (video->hblankIRQ) {
|
||||
if (GBARegisterDISPSTATIsHblankIRQ(video->dispstat)) {
|
||||
GBARaiseIRQ(video->p, IRQ_HBLANK);
|
||||
}
|
||||
}
|
||||
|
@ -155,21 +152,17 @@ 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);
|
||||
video->p->memory.io[REG_DISPSTAT >> 1] |= video->dispstat & 0x7;
|
||||
return video->nextEvent;
|
||||
}
|
||||
|
||||
void GBAVideoWriteDISPSTAT(struct GBAVideo* video, uint16_t value) {
|
||||
union GBARegisterDISPSTAT dispstat;
|
||||
dispstat.packed = value;
|
||||
video->vblankIRQ = dispstat.vblankIRQ;
|
||||
video->hblankIRQ = dispstat.hblankIRQ;
|
||||
video->vcounterIRQ = dispstat.vcounterIRQ;
|
||||
video->vcountSetting = dispstat.vcountSetting;
|
||||
video->dispstat &= 0x7;
|
||||
video->dispstat |= value & 0xFFF8;
|
||||
|
||||
if (video->vcounterIRQ) {
|
||||
if (GBARegisterDISPSTATIsVcounterIRQ(video->dispstat)) {
|
||||
// FIXME: this can be too late if we're in the middle of an Hblank
|
||||
video->nextVcounterIRQ = video->nextHblank + VIDEO_HBLANK_LENGTH + (video->vcountSetting - video->vcount) * VIDEO_HORIZONTAL_LENGTH;
|
||||
video->nextVcounterIRQ = video->nextHblank + VIDEO_HBLANK_LENGTH + (GBARegisterDISPSTATGetVcountSetting(video->dispstat) - video->vcount) * VIDEO_HORIZONTAL_LENGTH;
|
||||
if (video->nextVcounterIRQ < video->nextEvent) {
|
||||
video->nextVcounterIRQ += VIDEO_TOTAL_LENGTH;
|
||||
}
|
||||
|
@ -233,15 +226,7 @@ void GBAVideoSerialize(struct GBAVideo* video, struct GBASerializedState* state)
|
|||
memcpy(state->vram, video->renderer->vram, SIZE_VRAM);
|
||||
memcpy(state->oam, video->oam.raw, SIZE_OAM);
|
||||
memcpy(state->pram, video->palette, SIZE_PALETTE_RAM);
|
||||
union GBARegisterDISPSTAT dispstat;
|
||||
dispstat.inVblank = video->inVblank;
|
||||
dispstat.inHblank = video->inHblank;
|
||||
dispstat.vcounter = video->vcounter;
|
||||
dispstat.vblankIRQ = video->vblankIRQ;
|
||||
dispstat.hblankIRQ = video->hblankIRQ;
|
||||
dispstat.vcounterIRQ = video->vcounterIRQ;
|
||||
dispstat.vcountSetting = video->vcountSetting;
|
||||
state->io[REG_DISPSTAT >> 1] = dispstat.packed;
|
||||
state->io[REG_DISPSTAT >> 1] = video->dispstat;
|
||||
state->video.nextEvent = video->nextEvent;
|
||||
state->video.eventDiff = video->eventDiff;
|
||||
state->video.lastHblank = video->lastHblank;
|
||||
|
@ -260,15 +245,7 @@ void GBAVideoDeserialize(struct GBAVideo* video, struct GBASerializedState* stat
|
|||
for (i = 0; i < SIZE_PALETTE_RAM; i += 2) {
|
||||
GBAStore16(video->p->cpu, BASE_PALETTE_RAM | i, state->pram[i >> 1], 0);
|
||||
}
|
||||
union GBARegisterDISPSTAT dispstat;
|
||||
dispstat.packed = state->io[REG_DISPSTAT >> 1];
|
||||
video->inVblank = dispstat.inVblank;
|
||||
video->inHblank = dispstat.inHblank;
|
||||
video->vcounter = dispstat.vcounter;
|
||||
video->vblankIRQ = dispstat.vblankIRQ;
|
||||
video->hblankIRQ = dispstat.hblankIRQ;
|
||||
video->vcounterIRQ = dispstat.vcounterIRQ;
|
||||
video->vcountSetting = dispstat.vcountSetting;
|
||||
video->dispstat = state->io[REG_DISPSTAT >> 1];
|
||||
video->nextEvent = state->video.nextEvent;
|
||||
video->eventDiff = state->video.eventDiff;
|
||||
video->lastHblank = state->video.lastHblank;
|
||||
|
|
|
@ -43,78 +43,48 @@ enum ObjShape {
|
|||
OBJ_SHAPE_VERTICAL = 2
|
||||
};
|
||||
|
||||
union GBAColor {
|
||||
struct {
|
||||
unsigned r : 5;
|
||||
unsigned g : 5;
|
||||
unsigned b : 5;
|
||||
};
|
||||
uint16_t packed;
|
||||
};
|
||||
DECL_BITFIELD(GBAObjAttributesA, uint16_t);
|
||||
DECL_BITS(GBAObjAttributesA, Y, 0, 8);
|
||||
DECL_BIT(GBAObjAttributesA, Transformed, 8);
|
||||
DECL_BIT(GBAObjAttributesA, Disable, 9);
|
||||
DECL_BIT(GBAObjAttributesA, DoubleSize, 9);
|
||||
DECL_BITS(GBAObjAttributesA, Mode, 10, 2);
|
||||
DECL_BIT(GBAObjAttributesA, Mosaic, 12);
|
||||
DECL_BIT(GBAObjAttributesA, 256Color, 13);
|
||||
DECL_BITS(GBAObjAttributesA, Shape, 14, 2);
|
||||
|
||||
|
||||
DECL_BITFIELD(GBAObjAttributesB, uint16_t);
|
||||
DECL_BITS(GBAObjAttributesB, X, 0, 9);
|
||||
DECL_BITS(GBAObjAttributesB, MatIndex, 9, 5);
|
||||
DECL_BIT(GBAObjAttributesB, HFlip, 12);
|
||||
DECL_BIT(GBAObjAttributesB, VFlip, 13);
|
||||
DECL_BITS(GBAObjAttributesB, Size, 14, 2);
|
||||
|
||||
DECL_BITFIELD(GBAObjAttributesC, uint16_t);
|
||||
DECL_BITS(GBAObjAttributesC, Tile, 0, 10);
|
||||
DECL_BITS(GBAObjAttributesC, Priority, 10, 2);
|
||||
DECL_BITS(GBAObjAttributesC, Palette, 12, 4);
|
||||
|
||||
struct GBAObj {
|
||||
unsigned y : 8;
|
||||
unsigned transformed : 1;
|
||||
unsigned disable : 1;
|
||||
enum ObjMode mode : 2;
|
||||
unsigned mosaic : 1;
|
||||
unsigned multipalette : 1;
|
||||
enum ObjShape shape : 2;
|
||||
|
||||
int x : 9;
|
||||
int : 3;
|
||||
unsigned hflip : 1;
|
||||
unsigned vflip : 1;
|
||||
unsigned size : 2;
|
||||
|
||||
unsigned tile : 10;
|
||||
unsigned priority : 2;
|
||||
unsigned palette : 4;
|
||||
|
||||
int : 16;
|
||||
};
|
||||
|
||||
struct GBATransformedObj {
|
||||
unsigned y : 8;
|
||||
unsigned transformed : 1;
|
||||
unsigned doublesize : 1;
|
||||
enum ObjMode mode : 2;
|
||||
unsigned mosaic : 1;
|
||||
unsigned multipalette : 1;
|
||||
enum ObjShape shape : 2;
|
||||
|
||||
int x : 9;
|
||||
unsigned matIndex : 5;
|
||||
unsigned size : 2;
|
||||
|
||||
unsigned tile : 10;
|
||||
unsigned priority : 2;
|
||||
unsigned palette : 4;
|
||||
|
||||
int : 16;
|
||||
GBAObjAttributesA a;
|
||||
GBAObjAttributesB b;
|
||||
GBAObjAttributesC c;
|
||||
uint16_t d;
|
||||
};
|
||||
|
||||
union GBAOAM {
|
||||
struct GBAObj obj[128];
|
||||
struct GBATransformedObj tobj[128];
|
||||
|
||||
struct GBAOAMMatrix {
|
||||
int : 16;
|
||||
int : 16;
|
||||
int : 16;
|
||||
int a : 16;
|
||||
int : 16;
|
||||
int : 16;
|
||||
int : 16;
|
||||
int b : 16;
|
||||
int : 16;
|
||||
int : 16;
|
||||
int : 16;
|
||||
int c : 16;
|
||||
int : 16;
|
||||
int : 16;
|
||||
int : 16;
|
||||
int d : 16;
|
||||
int16_t padding0[3];
|
||||
int16_t a;
|
||||
int16_t padding1[3];
|
||||
int16_t b;
|
||||
int16_t padding2[3];
|
||||
int16_t c;
|
||||
int16_t padding3[3];
|
||||
int16_t d;
|
||||
} mat[32];
|
||||
|
||||
uint16_t raw[512];
|
||||
|
@ -125,53 +95,54 @@ union GBAOAM {
|
|||
#define GBA_TEXT_MAP_VFLIP(MAP) ((MAP) & 0x0800)
|
||||
#define GBA_TEXT_MAP_PALETTE(MAP) (((MAP) & 0xF000) >> 12)
|
||||
|
||||
union GBARegisterDISPCNT {
|
||||
struct {
|
||||
unsigned mode : 3;
|
||||
unsigned cgb : 1;
|
||||
unsigned frameSelect : 1;
|
||||
unsigned hblankIntervalFree : 1;
|
||||
unsigned objCharacterMapping : 1;
|
||||
unsigned forcedBlank : 1;
|
||||
unsigned bg0Enable : 1;
|
||||
unsigned bg1Enable : 1;
|
||||
unsigned bg2Enable : 1;
|
||||
unsigned bg3Enable : 1;
|
||||
unsigned objEnable : 1;
|
||||
unsigned win0Enable : 1;
|
||||
unsigned win1Enable : 1;
|
||||
unsigned objwinEnable : 1;
|
||||
};
|
||||
uint16_t packed;
|
||||
};
|
||||
DECL_BITFIELD(GBARegisterDISPCNT, uint16_t);
|
||||
DECL_BITS(GBARegisterDISPCNT, Mode, 0, 3);
|
||||
DECL_BIT(GBARegisterDISPCNT, Cgb, 3);
|
||||
DECL_BIT(GBARegisterDISPCNT, FrameSelect, 4);
|
||||
DECL_BIT(GBARegisterDISPCNT, HblankIntervalFree, 5);
|
||||
DECL_BIT(GBARegisterDISPCNT, ObjCharacterMapping, 6);
|
||||
DECL_BIT(GBARegisterDISPCNT, ForcedBlank, 7);
|
||||
DECL_BIT(GBARegisterDISPCNT, Bg0Enable, 8);
|
||||
DECL_BIT(GBARegisterDISPCNT, Bg1Enable, 9);
|
||||
DECL_BIT(GBARegisterDISPCNT, Bg2Enable, 10);
|
||||
DECL_BIT(GBARegisterDISPCNT, Bg3Enable, 11);
|
||||
DECL_BIT(GBARegisterDISPCNT, ObjEnable, 12);
|
||||
DECL_BIT(GBARegisterDISPCNT, Win0Enable, 13);
|
||||
DECL_BIT(GBARegisterDISPCNT, Win1Enable, 14);
|
||||
DECL_BIT(GBARegisterDISPCNT, ObjwinEnable, 15);
|
||||
|
||||
union GBARegisterDISPSTAT {
|
||||
struct {
|
||||
unsigned inVblank : 1;
|
||||
unsigned inHblank : 1;
|
||||
unsigned vcounter : 1;
|
||||
unsigned vblankIRQ : 1;
|
||||
unsigned hblankIRQ : 1;
|
||||
unsigned vcounterIRQ : 1;
|
||||
unsigned : 2;
|
||||
unsigned vcountSetting : 8;
|
||||
};
|
||||
uint32_t packed;
|
||||
};
|
||||
DECL_BITFIELD(GBARegisterDISPSTAT, uint16_t);
|
||||
DECL_BIT(GBARegisterDISPSTAT, InVblank, 0);
|
||||
DECL_BIT(GBARegisterDISPSTAT, InHblank, 1);
|
||||
DECL_BIT(GBARegisterDISPSTAT, Vcounter, 2);
|
||||
DECL_BIT(GBARegisterDISPSTAT, VblankIRQ, 3);
|
||||
DECL_BIT(GBARegisterDISPSTAT, HblankIRQ, 4);
|
||||
DECL_BIT(GBARegisterDISPSTAT, VcounterIRQ, 5);
|
||||
DECL_BITS(GBARegisterDISPSTAT, VcountSetting, 8, 8);
|
||||
|
||||
union GBARegisterBGCNT {
|
||||
struct {
|
||||
unsigned priority : 2;
|
||||
unsigned charBase : 2;
|
||||
unsigned : 2;
|
||||
unsigned mosaic : 1;
|
||||
unsigned multipalette : 1;
|
||||
unsigned screenBase : 5;
|
||||
unsigned overflow : 1;
|
||||
unsigned size : 2;
|
||||
};
|
||||
uint16_t packed;
|
||||
};
|
||||
DECL_BITFIELD(GBARegisterBGCNT, uint16_t);
|
||||
DECL_BITS(GBARegisterBGCNT, Priority, 0, 2);
|
||||
DECL_BITS(GBARegisterBGCNT, CharBase, 2, 2);
|
||||
DECL_BIT(GBARegisterBGCNT, Mosaic, 6);
|
||||
DECL_BIT(GBARegisterBGCNT, 256Color, 7);
|
||||
DECL_BITS(GBARegisterBGCNT, ScreenBase, 8, 5);
|
||||
DECL_BIT(GBARegisterBGCNT, Overflow, 13);
|
||||
DECL_BITS(GBARegisterBGCNT, Size, 14, 2);
|
||||
|
||||
DECL_BITFIELD(GBARegisterBLDCNT, uint16_t);
|
||||
DECL_BIT(GBARegisterBLDCNT, Target1Bg0, 0);
|
||||
DECL_BIT(GBARegisterBLDCNT, Target1Bg1, 1);
|
||||
DECL_BIT(GBARegisterBLDCNT, Target1Bg2, 2);
|
||||
DECL_BIT(GBARegisterBLDCNT, Target1Bg3, 3);
|
||||
DECL_BIT(GBARegisterBLDCNT, Target1Obj, 4);
|
||||
DECL_BIT(GBARegisterBLDCNT, Target1Bd, 5);
|
||||
DECL_BITS(GBARegisterBLDCNT, Effect, 6, 2);
|
||||
DECL_BIT(GBARegisterBLDCNT, Target2Bg0, 8);
|
||||
DECL_BIT(GBARegisterBLDCNT, Target2Bg1, 9);
|
||||
DECL_BIT(GBARegisterBLDCNT, Target2Bg2, 10);
|
||||
DECL_BIT(GBARegisterBLDCNT, Target2Bg3, 11);
|
||||
DECL_BIT(GBARegisterBLDCNT, Target2Obj, 12);
|
||||
DECL_BIT(GBARegisterBLDCNT, Target2Bd, 13);
|
||||
|
||||
struct GBAVideoRenderer {
|
||||
void (*init)(struct GBAVideoRenderer* renderer);
|
||||
|
@ -196,14 +167,7 @@ struct GBAVideo {
|
|||
struct GBA* p;
|
||||
struct GBAVideoRenderer* renderer;
|
||||
|
||||
// DISPSTAT
|
||||
int inHblank;
|
||||
int inVblank;
|
||||
int vcounter;
|
||||
int vblankIRQ;
|
||||
int hblankIRQ;
|
||||
int vcounterIRQ;
|
||||
int vcountSetting;
|
||||
GBARegisterDISPSTAT dispstat;
|
||||
|
||||
// VCOUNT
|
||||
int vcount;
|
||||
|
|
|
@ -14,7 +14,8 @@
|
|||
const uint32_t GBA_ARM7TDMI_FREQUENCY = 0x1000000;
|
||||
const uint32_t GBA_COMPONENT_MAGIC = 0x1000000;
|
||||
|
||||
static const uint64_t GBA_ROM_MAGIC = 0x21A29A6951AEFF24;
|
||||
static const size_t GBA_ROM_MAGIC_OFFSET = 4;
|
||||
static const uint8_t GBA_ROM_MAGIC[] = { 0x24, 0xFF, 0xAE, 0x51, 0x69, 0x9A, 0xA2, 0x21 };
|
||||
|
||||
enum {
|
||||
SP_BASE_SYSTEM = 0x03FFFF00,
|
||||
|
@ -42,8 +43,11 @@ static const struct GBACartridgeOverride _overrides[] = {
|
|||
{ "V49J", SAVEDATA_SRAM, GPIO_RUMBLE, -1 },
|
||||
{ "V49E", SAVEDATA_SRAM, GPIO_RUMBLE, -1 },
|
||||
|
||||
// Final Fantasy Tactics Advance
|
||||
{ "AFXE", SAVEDATA_FLASH512, GPIO_NONE, 0x8000418 },
|
||||
|
||||
// Mega Man Battle Network
|
||||
{ "AREE", SAVEDATA_SRAM, GPIO_NONE, 0x8000338 },
|
||||
{ "AREE", SAVEDATA_SRAM, GPIO_NONE, 0x800032E },
|
||||
|
||||
// Pokemon Ruby
|
||||
{ "AXVJ", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
||||
|
@ -604,14 +608,18 @@ void GBADebuggerLogShim(struct ARMDebugger* debugger, enum DebuggerLogLevel leve
|
|||
}
|
||||
|
||||
bool GBAIsROM(struct VFile* vf) {
|
||||
if (vf->seek(vf, 4, SEEK_SET) < 0) {
|
||||
if (vf->seek(vf, GBA_ROM_MAGIC_OFFSET, SEEK_SET) < 0) {
|
||||
return false;
|
||||
}
|
||||
uint64_t signature;
|
||||
uint8_t signature[sizeof(GBA_ROM_MAGIC)];
|
||||
if (vf->read(vf, &signature, sizeof(signature)) != sizeof(signature)) {
|
||||
return false;
|
||||
}
|
||||
return signature == GBA_ROM_MAGIC;
|
||||
return memcmp(signature, GBA_ROM_MAGIC, sizeof(signature)) == 0;
|
||||
}
|
||||
|
||||
void GBAGetGameCode(struct GBA* gba, char* out) {
|
||||
memcpy(out, &((struct GBACartridge*) gba->memory.rom)->id, 4);
|
||||
}
|
||||
|
||||
void GBAHitStub(struct ARMCore* cpu, uint32_t opcode) {
|
||||
|
|
|
@ -148,6 +148,7 @@ void GBALoadBIOS(struct GBA* gba, struct VFile* vf);
|
|||
void GBAApplyPatch(struct GBA* gba, struct Patch* patch);
|
||||
|
||||
bool GBAIsROM(struct VFile* vf);
|
||||
void GBAGetGameCode(struct GBA* gba, char* out);
|
||||
|
||||
__attribute__((format (printf, 3, 4)))
|
||||
void GBALog(struct GBA* gba, enum GBALogLevel level, const char* format, ...);
|
||||
|
|
|
@ -7,12 +7,12 @@ const uint8_t hleBios[SIZE_BIOS] = {
|
|||
0xfe, 0xff, 0xff, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x00, 0x00, 0xa0, 0xe1,
|
||||
0x24, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x02, 0xf3, 0xa0, 0xe3,
|
||||
0x00, 0x00, 0x5d, 0xe3, 0x01, 0xd3, 0xa0, 0x03, 0x20, 0xd0, 0x4d, 0x02,
|
||||
0x30, 0x40, 0x2d, 0xe9, 0x02, 0x40, 0x5e, 0xe5, 0x7c, 0x50, 0xa0, 0xe3,
|
||||
0x04, 0x41, 0x95, 0xe7, 0x00, 0x00, 0x54, 0xe3, 0x00, 0x50, 0x4f, 0xe1,
|
||||
0x20, 0x00, 0x2d, 0xe9, 0x80, 0x50, 0x05, 0xe2, 0x1f, 0x50, 0x85, 0xe3,
|
||||
0x05, 0xf0, 0x29, 0xe1, 0x00, 0x40, 0x2d, 0xe9, 0x0f, 0xe0, 0xa0, 0xe1,
|
||||
0x14, 0xff, 0x2f, 0x11, 0x00, 0x40, 0xbd, 0xe8, 0x93, 0xf0, 0x29, 0xe3,
|
||||
0x20, 0x00, 0xbd, 0xe8, 0x05, 0xf0, 0x69, 0xe1, 0x30, 0x40, 0xbd, 0xe8,
|
||||
0x00, 0x58, 0x2d, 0xe9, 0x02, 0xb0, 0x5e, 0xe5, 0x7c, 0xc0, 0xa0, 0xe3,
|
||||
0x0b, 0xb1, 0x9c, 0xe7, 0x00, 0x00, 0x5b, 0xe3, 0x00, 0xc0, 0x4f, 0xe1,
|
||||
0x00, 0x10, 0x2d, 0xe9, 0x80, 0xc0, 0x0c, 0xe2, 0x1f, 0xc0, 0x8c, 0xe3,
|
||||
0x0c, 0xf0, 0x29, 0xe1, 0x00, 0x40, 0x2d, 0xe9, 0x0f, 0xe0, 0xa0, 0xe1,
|
||||
0x1b, 0xff, 0x2f, 0x11, 0x00, 0x40, 0xbd, 0xe8, 0x93, 0xf0, 0x29, 0xe3,
|
||||
0x00, 0x10, 0xbd, 0xe8, 0x0c, 0xf0, 0x69, 0xe1, 0x00, 0x58, 0xbd, 0xe8,
|
||||
0x0e, 0xf0, 0xb0, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x00, 0x00, 0x00,
|
||||
0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
@ -20,11 +20,11 @@ const uint8_t hleBios[SIZE_BIOS] = {
|
|||
0x0c, 0x01, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x0f, 0x50, 0x2d, 0xe9,
|
||||
0x01, 0x03, 0xa0, 0xe3, 0x00, 0xe0, 0x8f, 0xe2, 0x04, 0xf0, 0x10, 0xe5,
|
||||
0x0f, 0x50, 0xbd, 0xe8, 0x04, 0xf0, 0x5e, 0xe2, 0x01, 0x00, 0xa0, 0xe3,
|
||||
0x01, 0x10, 0xa0, 0xe3, 0x0c, 0x40, 0x2d, 0xe9, 0x01, 0x43, 0xa0, 0xe3,
|
||||
0x01, 0x10, 0xa0, 0xe3, 0x0c, 0x40, 0x2d, 0xe9, 0x01, 0xc3, 0xa0, 0xe3,
|
||||
0x00, 0x00, 0x50, 0xe3, 0x00, 0x00, 0xa0, 0xe3, 0x01, 0x20, 0xa0, 0xe3,
|
||||
0x00, 0x00, 0x00, 0x0a, 0x01, 0x03, 0xc4, 0xe5, 0x08, 0x02, 0xc4, 0xe5,
|
||||
0xb8, 0x30, 0x54, 0xe1, 0x01, 0x30, 0x13, 0xe0, 0x01, 0x30, 0x23, 0x10,
|
||||
0xb8, 0x30, 0x44, 0x11, 0x08, 0x22, 0xc4, 0xe5, 0xf7, 0xff, 0xff, 0x0a,
|
||||
0x00, 0x00, 0x00, 0x0a, 0x01, 0x03, 0xcc, 0xe5, 0x08, 0x02, 0xcc, 0xe5,
|
||||
0xb8, 0x30, 0x5c, 0xe1, 0x01, 0x30, 0x13, 0xe0, 0x01, 0x30, 0x23, 0x10,
|
||||
0xb8, 0x30, 0x4c, 0x11, 0x08, 0x22, 0xcc, 0xe5, 0xf7, 0xff, 0xff, 0x0a,
|
||||
0x0c, 0x80, 0xbd, 0xe8, 0x00, 0x40, 0x2d, 0xe9, 0x02, 0x36, 0xa0, 0xe1,
|
||||
0x01, 0x04, 0x12, 0xe3, 0x0f, 0x00, 0x00, 0x0a, 0x01, 0x03, 0x12, 0xe3,
|
||||
0x05, 0x00, 0x00, 0x0a, 0x23, 0x35, 0x81, 0xe0, 0x04, 0x00, 0xb0, 0xe8,
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
PREFIX := $(DEVKITARM)/bin/arm-none-eabi-
|
||||
AS := $(PREFIX)as
|
||||
OBJCOPY := $(PREFIX)objcopy
|
||||
|
||||
all: hle-bios.c
|
||||
|
||||
hle-bios.o: hle-bios.s
|
||||
$(AS) -o $@ $<
|
||||
|
||||
hle-bios.bin: hle-bios.o
|
||||
$(OBJCOPY) -O binary $< $@
|
||||
|
||||
hle-bios.c: hle-bios.bin
|
||||
echo '#include "hle-bios.h"' > $@
|
||||
echo >> $@
|
||||
echo '#include "gba-memory.h"' >> $@
|
||||
echo >> $@
|
||||
xxd -i $< | sed -e 's/unsigned char hle_bios_bin\[\]/const uint8_t hleBios[SIZE_BIOS]/' | grep -v hle_bios_bin_len >> $@
|
|
@ -18,24 +18,24 @@ swiBase:
|
|||
cmp sp, #0
|
||||
moveq sp, #0x04000000
|
||||
subeq sp, #0x20
|
||||
stmfd sp!, {r4-r5, lr}
|
||||
ldrb r4, [lr, #-2]
|
||||
mov r5, #swiTable
|
||||
ldr r4, [r5, r4, lsl #2]
|
||||
cmp r4, #0
|
||||
mrs r5, spsr
|
||||
stmfd sp!, {r5}
|
||||
and r5, #0x80
|
||||
orr r5, #0x1F
|
||||
msr cpsr, r5
|
||||
stmfd sp!, {r11-r12, lr}
|
||||
ldrb r11, [lr, #-2]
|
||||
mov r12, #swiTable
|
||||
ldr r11, [r12, r11, lsl #2]
|
||||
cmp r11, #0
|
||||
mrs r12, spsr
|
||||
stmfd sp!, {r12}
|
||||
and r12, #0x80
|
||||
orr r12, #0x1F
|
||||
msr cpsr, r12
|
||||
stmfd sp!, {lr}
|
||||
mov lr, pc
|
||||
bxne r4
|
||||
bxne r11
|
||||
ldmfd sp!, {lr}
|
||||
msr cpsr, #0x93
|
||||
ldmfd sp!, {r5}
|
||||
msr spsr, r5
|
||||
ldmfd sp!, {r4-r5, lr}
|
||||
ldmfd sp!, {r12}
|
||||
msr spsr, r12
|
||||
ldmfd sp!, {r11-r12, lr}
|
||||
movs pc, lr
|
||||
|
||||
swiTable:
|
||||
|
@ -68,7 +68,7 @@ mov r1, #1
|
|||
IntrWait:
|
||||
stmfd sp!, {r2-r3, lr}
|
||||
# Pull current interrupts enabled and add the ones we need
|
||||
mov r4, #0x04000000
|
||||
mov r12, #0x04000000
|
||||
# See if we want to return immediately
|
||||
cmp r0, #0
|
||||
mov r0, #0
|
||||
|
@ -76,15 +76,15 @@ mov r2, #1
|
|||
beq 1f
|
||||
# Halt
|
||||
0:
|
||||
strb r0, [r4, #0x301]
|
||||
strb r0, [r12, #0x301]
|
||||
1:
|
||||
# Check which interrupts were acknowledged
|
||||
strb r0, [r4, #0x208]
|
||||
ldrh r3, [r4, #-8]
|
||||
strb r0, [r12, #0x208]
|
||||
ldrh r3, [r12, #-8]
|
||||
ands r3, r1
|
||||
eorne r3, r1
|
||||
strneh r3, [r4, #-8]
|
||||
strb r2, [r4, #0x208]
|
||||
strneh r3, [r12, #-8]
|
||||
strb r2, [r12, #0x208]
|
||||
beq 0b
|
||||
ldmfd sp!, {r2-r3, pc}
|
||||
|
||||
|
|
|
@ -51,7 +51,6 @@ static void _drawBackgroundMode3(struct GBAVideoSoftwareRenderer* renderer, stru
|
|||
static void _drawBackgroundMode4(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
|
||||
static void _drawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
|
||||
static void _cleanOAM(struct GBAVideoSoftwareRenderer* renderer);
|
||||
static int _preprocessTransformedSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBATransformedObj* sprite, int y);
|
||||
static int _preprocessSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y);
|
||||
static void _postprocessSprite(struct GBAVideoSoftwareRenderer* renderer, unsigned priority);
|
||||
|
||||
|
@ -77,7 +76,7 @@ static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer) {
|
|||
struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
|
||||
int i;
|
||||
|
||||
softwareRenderer->dispcnt.packed = 0x0080;
|
||||
softwareRenderer->dispcnt = 0x0080;
|
||||
|
||||
softwareRenderer->target1Obj = 0;
|
||||
softwareRenderer->target1Bd = 0;
|
||||
|
@ -91,20 +90,13 @@ 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->winN[0] = (struct WindowN) { .control = { .priority = 0 } };
|
||||
softwareRenderer->winN[1] = (struct WindowN) { .control = { .priority = 1 } };
|
||||
softwareRenderer->objwin = (struct WindowControl) { .priority = 2 };
|
||||
softwareRenderer->winout = (struct WindowControl) { .priority = 3 };
|
||||
softwareRenderer->oamMax = 0;
|
||||
|
||||
softwareRenderer->mosaic.packed = 0;
|
||||
softwareRenderer->mosaic = 0;
|
||||
|
||||
for (i = 0; i < 4; ++i) {
|
||||
struct GBAVideoSoftwareBackground* bg = &softwareRenderer->bg[i];
|
||||
|
@ -141,7 +133,7 @@ static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRender
|
|||
struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
|
||||
switch (address) {
|
||||
case REG_DISPCNT:
|
||||
softwareRenderer->dispcnt.packed = value;
|
||||
softwareRenderer->dispcnt = value;
|
||||
GBAVideoSoftwareRendererUpdateDISPCNT(softwareRenderer);
|
||||
break;
|
||||
case REG_BG0CNT:
|
||||
|
@ -261,7 +253,8 @@ static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRender
|
|||
_updatePalettes(softwareRenderer);
|
||||
break;
|
||||
case REG_WIN0H:
|
||||
softwareRenderer->winN[0].h.packed = value;
|
||||
softwareRenderer->winN[0].h.end = value;
|
||||
softwareRenderer->winN[0].h.start = value >> 8;
|
||||
if (softwareRenderer->winN[0].h.start > VIDEO_HORIZONTAL_PIXELS && softwareRenderer->winN[0].h.start > softwareRenderer->winN[0].h.end) {
|
||||
softwareRenderer->winN[0].h.start = 0;
|
||||
}
|
||||
|
@ -270,7 +263,8 @@ static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRender
|
|||
}
|
||||
break;
|
||||
case REG_WIN1H:
|
||||
softwareRenderer->winN[1].h.packed = value;
|
||||
softwareRenderer->winN[1].h.end = value;
|
||||
softwareRenderer->winN[1].h.start = value >> 8;
|
||||
if (softwareRenderer->winN[1].h.start > VIDEO_HORIZONTAL_PIXELS && softwareRenderer->winN[1].h.start > softwareRenderer->winN[1].h.end) {
|
||||
softwareRenderer->winN[1].h.start = 0;
|
||||
}
|
||||
|
@ -279,7 +273,8 @@ static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRender
|
|||
}
|
||||
break;
|
||||
case REG_WIN0V:
|
||||
softwareRenderer->winN[0].v.packed = value;
|
||||
softwareRenderer->winN[0].v.end = value;
|
||||
softwareRenderer->winN[0].v.start = value >> 8;
|
||||
if (softwareRenderer->winN[0].v.start > VIDEO_VERTICAL_PIXELS && softwareRenderer->winN[0].v.start > softwareRenderer->winN[0].v.end) {
|
||||
softwareRenderer->winN[0].v.start = 0;
|
||||
}
|
||||
|
@ -288,7 +283,8 @@ static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRender
|
|||
}
|
||||
break;
|
||||
case REG_WIN1V:
|
||||
softwareRenderer->winN[1].v.packed = value;
|
||||
softwareRenderer->winN[1].v.end = value;
|
||||
softwareRenderer->winN[1].v.start = value >> 8;
|
||||
if (softwareRenderer->winN[1].v.start > VIDEO_VERTICAL_PIXELS && softwareRenderer->winN[1].v.start > softwareRenderer->winN[1].v.end) {
|
||||
softwareRenderer->winN[1].v.start = 0;
|
||||
}
|
||||
|
@ -305,7 +301,7 @@ static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRender
|
|||
softwareRenderer->objwin.packed = value >> 8;
|
||||
break;
|
||||
case REG_MOSAIC:
|
||||
softwareRenderer->mosaic.packed = value;
|
||||
softwareRenderer->mosaic = value;
|
||||
break;
|
||||
case REG_GREENSWP:
|
||||
GBALog(0, GBA_LOG_STUB, "Stub video register write: 0x%03X", address);
|
||||
|
@ -393,16 +389,19 @@ static void _cleanOAM(struct GBAVideoSoftwareRenderer* renderer) {
|
|||
int i;
|
||||
int oamMax = 0;
|
||||
for (i = 0; i < 128; ++i) {
|
||||
struct GBAObj* obj = &renderer->d.oam->obj[i];
|
||||
if (obj->transformed || !obj->disable) {
|
||||
int height = _objSizes[obj->shape * 8 + obj->size * 2 + 1];
|
||||
if (obj->transformed) {
|
||||
height <<= ((struct GBATransformedObj*) obj)->doublesize;
|
||||
struct GBAObj obj;
|
||||
LOAD_16(obj.a, 0, &renderer->d.oam->obj[i].a);
|
||||
LOAD_16(obj.b, 0, &renderer->d.oam->obj[i].b);
|
||||
LOAD_16(obj.c, 0, &renderer->d.oam->obj[i].c);
|
||||
if (GBAObjAttributesAIsTransformed(obj.a) || !GBAObjAttributesAIsDisable(obj.a)) {
|
||||
int height = _objSizes[GBAObjAttributesAGetShape(obj.a) * 8 + GBAObjAttributesBGetSize(obj.b) * 2 + 1];
|
||||
if (GBAObjAttributesAIsTransformed(obj.a)) {
|
||||
height <<= GBAObjAttributesAGetDoubleSize(obj.a);
|
||||
}
|
||||
if (obj->y < VIDEO_VERTICAL_PIXELS || obj->y + height >= VIDEO_VERTICAL_TOTAL_PIXELS) {
|
||||
renderer->sprites[oamMax].y = obj->y;
|
||||
renderer->sprites[oamMax].endY = obj->y + height;
|
||||
renderer->sprites[oamMax].obj = *obj;
|
||||
if (GBAObjAttributesAGetY(obj.a) < VIDEO_VERTICAL_PIXELS || GBAObjAttributesAGetY(obj.a) + height >= VIDEO_VERTICAL_TOTAL_PIXELS) {
|
||||
renderer->sprites[oamMax].y = GBAObjAttributesAGetY(obj.a);
|
||||
renderer->sprites[oamMax].endY = GBAObjAttributesAGetY(obj.a) + height;
|
||||
renderer->sprites[oamMax].obj = obj;
|
||||
++oamMax;
|
||||
}
|
||||
}
|
||||
|
@ -416,7 +415,7 @@ static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* render
|
|||
struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
|
||||
|
||||
color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
|
||||
if (softwareRenderer->dispcnt.forcedBlank) {
|
||||
if (GBARegisterDISPCNTIsForcedBlank(softwareRenderer->dispcnt)) {
|
||||
int x;
|
||||
for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
|
||||
row[x] = GBA_COLOR_WHITE;
|
||||
|
@ -434,12 +433,12 @@ static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* render
|
|||
|
||||
softwareRenderer->windows[0].endX = VIDEO_HORIZONTAL_PIXELS;
|
||||
softwareRenderer->nWindows = 1;
|
||||
if (softwareRenderer->dispcnt.win0Enable || softwareRenderer->dispcnt.win1Enable || softwareRenderer->dispcnt.objwinEnable) {
|
||||
if (GBARegisterDISPCNTIsWin0Enable(softwareRenderer->dispcnt) || GBARegisterDISPCNTIsWin1Enable(softwareRenderer->dispcnt) || GBARegisterDISPCNTIsObjwinEnable(softwareRenderer->dispcnt)) {
|
||||
softwareRenderer->windows[0].control = softwareRenderer->winout;
|
||||
if (softwareRenderer->dispcnt.win1Enable && y < softwareRenderer->winN[1].v.end && y >= softwareRenderer->winN[1].v.start) {
|
||||
if (GBARegisterDISPCNTIsWin1Enable(softwareRenderer->dispcnt) && y < softwareRenderer->winN[1].v.end && y >= softwareRenderer->winN[1].v.start) {
|
||||
_breakWindow(softwareRenderer, &softwareRenderer->winN[1]);
|
||||
}
|
||||
if (softwareRenderer->dispcnt.win0Enable && y < softwareRenderer->winN[0].v.end && y >= softwareRenderer->winN[0].v.start) {
|
||||
if (GBARegisterDISPCNTIsWin0Enable(softwareRenderer->dispcnt) && y < softwareRenderer->winN[0].v.end && y >= softwareRenderer->winN[0].v.start) {
|
||||
_breakWindow(softwareRenderer, &softwareRenderer->winN[0]);
|
||||
}
|
||||
} else {
|
||||
|
@ -451,7 +450,7 @@ static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* render
|
|||
for (w = 0; w < softwareRenderer->nWindows; ++w) {
|
||||
// TOOD: handle objwin on backdrop
|
||||
uint32_t backdrop = FLAG_UNWRITTEN | FLAG_PRIORITY | FLAG_IS_BACKGROUND;
|
||||
if (!softwareRenderer->target1Bd || softwareRenderer->blendEffect == BLEND_NONE || softwareRenderer->blendEffect == BLEND_ALPHA || !softwareRenderer->windows[w].control.blendEnable) {
|
||||
if (!softwareRenderer->target1Bd || softwareRenderer->blendEffect == BLEND_NONE || softwareRenderer->blendEffect == BLEND_ALPHA || !GBAWindowControlIsBlendEnable(softwareRenderer->windows[w].control.packed)) {
|
||||
backdrop |= softwareRenderer->normalPalette[0];
|
||||
} else {
|
||||
backdrop |= softwareRenderer->variantPalette[0];
|
||||
|
@ -468,7 +467,7 @@ static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* render
|
|||
x = 0;
|
||||
for (w = 0; w < softwareRenderer->nWindows; ++w) {
|
||||
uint32_t backdrop = FLAG_UNWRITTEN;
|
||||
if (!softwareRenderer->target1Bd || softwareRenderer->blendEffect == BLEND_NONE || softwareRenderer->blendEffect == BLEND_ALPHA || !softwareRenderer->windows[w].control.blendEnable) {
|
||||
if (!softwareRenderer->target1Bd || softwareRenderer->blendEffect == BLEND_NONE || softwareRenderer->blendEffect == BLEND_ALPHA || !GBAWindowControlIsBlendEnable(softwareRenderer->windows[w].control.packed)) {
|
||||
backdrop |= softwareRenderer->normalPalette[0];
|
||||
} else {
|
||||
backdrop |= softwareRenderer->variantPalette[0];
|
||||
|
@ -523,22 +522,21 @@ static void GBAVideoSoftwareRendererPutPixels(struct GBAVideoRenderer* renderer,
|
|||
}
|
||||
|
||||
static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer) {
|
||||
renderer->bg[0].enabled = renderer->dispcnt.bg0Enable;
|
||||
renderer->bg[1].enabled = renderer->dispcnt.bg1Enable;
|
||||
renderer->bg[2].enabled = renderer->dispcnt.bg2Enable;
|
||||
renderer->bg[3].enabled = renderer->dispcnt.bg3Enable;
|
||||
renderer->bg[0].enabled = GBARegisterDISPCNTGetBg0Enable(renderer->dispcnt);
|
||||
renderer->bg[1].enabled = GBARegisterDISPCNTGetBg1Enable(renderer->dispcnt);
|
||||
renderer->bg[2].enabled = GBARegisterDISPCNTGetBg2Enable(renderer->dispcnt);
|
||||
renderer->bg[3].enabled = GBARegisterDISPCNTGetBg3Enable(renderer->dispcnt);
|
||||
}
|
||||
|
||||
static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value) {
|
||||
UNUSED(renderer);
|
||||
union GBARegisterBGCNT reg = { .packed = value };
|
||||
bg->priority = reg.priority;
|
||||
bg->charBase = reg.charBase << 14;
|
||||
bg->mosaic = reg.mosaic;
|
||||
bg->multipalette = reg.multipalette;
|
||||
bg->screenBase = reg.screenBase << 11;
|
||||
bg->overflow = reg.overflow;
|
||||
bg->size = reg.size;
|
||||
bg->priority = GBARegisterBGCNTGetPriority(value);
|
||||
bg->charBase = GBARegisterBGCNTGetCharBase(value) << 14;
|
||||
bg->mosaic = GBARegisterBGCNTGetMosaic(value);
|
||||
bg->multipalette = GBARegisterBGCNTGet256Color(value);
|
||||
bg->screenBase = GBARegisterBGCNTGetScreenBase(value) << 11;
|
||||
bg->overflow = GBARegisterBGCNTGetOverflow(value);
|
||||
bg->size = GBARegisterBGCNTGetSize(value);
|
||||
}
|
||||
|
||||
static void GBAVideoSoftwareRendererWriteBGPA(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
|
||||
|
@ -582,43 +580,24 @@ static void GBAVideoSoftwareRendererWriteBGY_HI(struct GBAVideoSoftwareBackgroun
|
|||
}
|
||||
|
||||
static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer, uint16_t value) {
|
||||
union {
|
||||
struct {
|
||||
unsigned target1Bg0 : 1;
|
||||
unsigned target1Bg1 : 1;
|
||||
unsigned target1Bg2 : 1;
|
||||
unsigned target1Bg3 : 1;
|
||||
unsigned target1Obj : 1;
|
||||
unsigned target1Bd : 1;
|
||||
enum BlendEffect effect : 2;
|
||||
unsigned target2Bg0 : 1;
|
||||
unsigned target2Bg1 : 1;
|
||||
unsigned target2Bg2 : 1;
|
||||
unsigned target2Bg3 : 1;
|
||||
unsigned target2Obj : 1;
|
||||
unsigned target2Bd : 1;
|
||||
};
|
||||
uint16_t packed;
|
||||
} bldcnt = { .packed = value };
|
||||
|
||||
enum BlendEffect oldEffect = renderer->blendEffect;
|
||||
|
||||
renderer->bg[0].target1 = bldcnt.target1Bg0;
|
||||
renderer->bg[1].target1 = bldcnt.target1Bg1;
|
||||
renderer->bg[2].target1 = bldcnt.target1Bg2;
|
||||
renderer->bg[3].target1 = bldcnt.target1Bg3;
|
||||
renderer->bg[0].target2 = bldcnt.target2Bg0;
|
||||
renderer->bg[1].target2 = bldcnt.target2Bg1;
|
||||
renderer->bg[2].target2 = bldcnt.target2Bg2;
|
||||
renderer->bg[3].target2 = bldcnt.target2Bg3;
|
||||
renderer->bg[0].target1 = GBARegisterBLDCNTGetTarget1Bg0(value);
|
||||
renderer->bg[1].target1 = GBARegisterBLDCNTGetTarget1Bg1(value);
|
||||
renderer->bg[2].target1 = GBARegisterBLDCNTGetTarget1Bg2(value);
|
||||
renderer->bg[3].target1 = GBARegisterBLDCNTGetTarget1Bg3(value);
|
||||
renderer->bg[0].target2 = GBARegisterBLDCNTGetTarget2Bg0(value);
|
||||
renderer->bg[1].target2 = GBARegisterBLDCNTGetTarget2Bg1(value);
|
||||
renderer->bg[2].target2 = GBARegisterBLDCNTGetTarget2Bg2(value);
|
||||
renderer->bg[3].target2 = GBARegisterBLDCNTGetTarget2Bg3(value);
|
||||
|
||||
renderer->blendEffect = bldcnt.effect;
|
||||
renderer->target1Obj = bldcnt.target1Obj;
|
||||
renderer->target1Bd = bldcnt.target1Bd;
|
||||
renderer->target2Obj = bldcnt.target2Obj;
|
||||
renderer->target2Bd = bldcnt.target2Bd;
|
||||
renderer->blendEffect = GBARegisterBLDCNTGetEffect(value);
|
||||
renderer->target1Obj = GBARegisterBLDCNTGetTarget1Obj(value);
|
||||
renderer->target1Bd = GBARegisterBLDCNTGetTarget1Bd(value);
|
||||
renderer->target2Obj = GBARegisterBLDCNTGetTarget2Obj(value);
|
||||
renderer->target2Bd = GBARegisterBLDCNTGetTarget2Bd(value);
|
||||
|
||||
renderer->anyTarget2 = bldcnt.packed & 0x3F00;
|
||||
renderer->anyTarget2 = value & 0x3F00;
|
||||
|
||||
if (oldEffect != renderer->blendEffect) {
|
||||
_updatePalettes(renderer);
|
||||
|
@ -627,25 +606,25 @@ static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer*
|
|||
|
||||
#define TEST_LAYER_ENABLED(X) \
|
||||
(renderer->bg[X].enabled && \
|
||||
(renderer->currentWindow.bg ## X ## Enable || \
|
||||
(renderer->dispcnt.objwinEnable && renderer->objwin.bg ## X ## Enable)) && \
|
||||
(GBAWindowControlIsBg ## X ## Enable(renderer->currentWindow.packed) || \
|
||||
(GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt) && GBAWindowControlIsBg ## X ## Enable (renderer->objwin.packed))) && \
|
||||
renderer->bg[X].priority == priority)
|
||||
|
||||
static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y) {
|
||||
int w;
|
||||
renderer->end = 0;
|
||||
int spriteLayers = 0;
|
||||
if (renderer->dispcnt.objEnable) {
|
||||
if (GBARegisterDISPCNTIsObjEnable(renderer->dispcnt)) {
|
||||
if (renderer->oamDirty) {
|
||||
_cleanOAM(renderer);
|
||||
}
|
||||
int mosaicV = renderer->mosaic.objV + 1;
|
||||
int mosaicV = GBAMosaicControlGetObjV(renderer->mosaic) + 1;
|
||||
int mosaicY = y - (y % mosaicV);
|
||||
for (w = 0; w < renderer->nWindows; ++w) {
|
||||
renderer->start = renderer->end;
|
||||
renderer->end = renderer->windows[w].endX;
|
||||
renderer->currentWindow = renderer->windows[w].control;
|
||||
if (!renderer->currentWindow.objEnable) {
|
||||
if (!GBAWindowControlIsObjEnable(renderer->currentWindow.packed)) {
|
||||
continue;
|
||||
}
|
||||
int i;
|
||||
|
@ -653,18 +632,14 @@ static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y) {
|
|||
for (i = 0; i < renderer->oamMax; ++i) {
|
||||
int localY = y;
|
||||
struct GBAVideoSoftwareSprite* sprite = &renderer->sprites[i];
|
||||
if (sprite->obj.mosaic) {
|
||||
if (GBAObjAttributesAIsMosaic(sprite->obj.a)) {
|
||||
localY = mosaicY;
|
||||
}
|
||||
if ((localY < sprite->y && (sprite->endY - 256 < 0 || localY >= sprite->endY - 256)) || localY >= sprite->endY) {
|
||||
continue;
|
||||
}
|
||||
if (sprite->obj.transformed) {
|
||||
drawn = _preprocessTransformedSprite(renderer, &sprite->tobj, localY);
|
||||
} else {
|
||||
drawn = _preprocessSprite(renderer, &sprite->obj, localY);
|
||||
}
|
||||
spriteLayers |= drawn << sprite->obj.priority;
|
||||
drawn = _preprocessSprite(renderer, &sprite->obj, localY);
|
||||
spriteLayers |= drawn << GBAObjAttributesCGetPriority(sprite->obj.c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -679,14 +654,14 @@ static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y) {
|
|||
renderer->start = renderer->end;
|
||||
renderer->end = renderer->windows[w].endX;
|
||||
renderer->currentWindow = renderer->windows[w].control;
|
||||
if (TEST_LAYER_ENABLED(0) && renderer->dispcnt.mode < 2) {
|
||||
if (TEST_LAYER_ENABLED(0) && GBARegisterDISPCNTGetMode(renderer->dispcnt) < 2) {
|
||||
_drawBackgroundMode0(renderer, &renderer->bg[0], y);
|
||||
}
|
||||
if (TEST_LAYER_ENABLED(1) && renderer->dispcnt.mode < 2) {
|
||||
if (TEST_LAYER_ENABLED(1) && GBARegisterDISPCNTGetMode(renderer->dispcnt) < 2) {
|
||||
_drawBackgroundMode0(renderer, &renderer->bg[1], y);
|
||||
}
|
||||
if (TEST_LAYER_ENABLED(2)) {
|
||||
switch (renderer->dispcnt.mode) {
|
||||
switch (GBARegisterDISPCNTGetMode(renderer->dispcnt)) {
|
||||
case 0:
|
||||
_drawBackgroundMode0(renderer, &renderer->bg[2], y);
|
||||
break;
|
||||
|
@ -706,7 +681,7 @@ static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y) {
|
|||
}
|
||||
}
|
||||
if (TEST_LAYER_ENABLED(3)) {
|
||||
switch (renderer->dispcnt.mode) {
|
||||
switch (GBARegisterDISPCNTGetMode(renderer->dispcnt)) {
|
||||
case 0:
|
||||
_drawBackgroundMode0(renderer, &renderer->bg[3], y);
|
||||
break;
|
||||
|
@ -811,39 +786,39 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re
|
|||
xBase += (localX & 0x100) << 5; \
|
||||
} \
|
||||
screenBase = yBase + (xBase >> 3); \
|
||||
mapData = vram[screenBase]; \
|
||||
LOAD_16(mapData, screenBase << 1, vram); \
|
||||
localY = inY & 0x7; \
|
||||
if (GBA_TEXT_MAP_VFLIP(mapData)) { \
|
||||
localY = 7 - localY; \
|
||||
}
|
||||
|
||||
#define PREPARE_OBJWIN \
|
||||
int objwinSlowPath = renderer->dispcnt.objwinEnable; \
|
||||
int objwinSlowPath = GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt); \
|
||||
int objwinOnly = 0; \
|
||||
int objwinForceEnable = 0; \
|
||||
color_t* objwinPalette; \
|
||||
if (objwinSlowPath) { \
|
||||
if (background->target1 && renderer->objwin.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN)) { \
|
||||
if (background->target1 && GBAWindowControlIsBlendEnable(renderer->objwin.packed) && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN)) { \
|
||||
objwinPalette = renderer->variantPalette; \
|
||||
} else { \
|
||||
objwinPalette = renderer->normalPalette; \
|
||||
} \
|
||||
switch (background->index) { \
|
||||
case 0: \
|
||||
objwinForceEnable = renderer->objwin.bg0Enable && renderer->currentWindow.bg0Enable; \
|
||||
objwinOnly = !renderer->objwin.bg0Enable; \
|
||||
objwinForceEnable = GBAWindowControlIsBg0Enable(renderer->objwin.packed) && GBAWindowControlIsBg0Enable(renderer->currentWindow.packed); \
|
||||
objwinOnly = !GBAWindowControlIsBg0Enable(renderer->objwin.packed); \
|
||||
break; \
|
||||
case 1: \
|
||||
objwinForceEnable = renderer->objwin.bg1Enable && renderer->currentWindow.bg1Enable; \
|
||||
objwinOnly = !renderer->objwin.bg1Enable; \
|
||||
objwinForceEnable = GBAWindowControlIsBg1Enable(renderer->objwin.packed) && GBAWindowControlIsBg1Enable(renderer->currentWindow.packed); \
|
||||
objwinOnly = !GBAWindowControlIsBg1Enable(renderer->objwin.packed); \
|
||||
break; \
|
||||
case 2: \
|
||||
objwinForceEnable = renderer->objwin.bg2Enable && renderer->currentWindow.bg2Enable; \
|
||||
objwinOnly = !renderer->objwin.bg2Enable; \
|
||||
objwinForceEnable = GBAWindowControlIsBg2Enable(renderer->objwin.packed) && GBAWindowControlIsBg2Enable(renderer->currentWindow.packed); \
|
||||
objwinOnly = !GBAWindowControlIsBg2Enable(renderer->objwin.packed); \
|
||||
break; \
|
||||
case 3: \
|
||||
objwinForceEnable = renderer->objwin.bg3Enable && renderer->currentWindow.bg3Enable; \
|
||||
objwinOnly = !renderer->objwin.bg3Enable; \
|
||||
objwinForceEnable = GBAWindowControlIsBg3Enable(renderer->objwin.packed) && GBAWindowControlIsBg3Enable(renderer->currentWindow.packed); \
|
||||
objwinOnly = !GBAWindowControlIsBg3Enable(renderer->objwin.packed); \
|
||||
break; \
|
||||
} \
|
||||
}
|
||||
|
@ -851,8 +826,8 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re
|
|||
#define DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_16(BLEND, OBJWIN) \
|
||||
paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
|
||||
palette = &mainPalette[paletteData]; \
|
||||
charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) >> 2) + localY; \
|
||||
tileData = ((uint32_t*) vram)[charBase]; \
|
||||
charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
|
||||
LOAD_32(tileData, charBase, vram); \
|
||||
if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
|
||||
tileData >>= 4 * mod8; \
|
||||
for (; outX < end; ++outX) { \
|
||||
|
@ -867,8 +842,8 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re
|
|||
}
|
||||
|
||||
#define DRAW_BACKGROUND_MODE_0_TILE_PREFIX_16(BLEND, OBJWIN) \
|
||||
charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) >> 2) + localY; \
|
||||
tileData = ((uint32_t*) vram)[charBase]; \
|
||||
charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
|
||||
LOAD_32(tileData, charBase, vram); \
|
||||
paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
|
||||
palette = &mainPalette[paletteData]; \
|
||||
if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
|
||||
|
@ -896,13 +871,13 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re
|
|||
#define DRAW_BACKGROUND_MODE_0_MOSAIC_16(BLEND, OBJWIN) \
|
||||
for (; tileX < tileEnd; ++tileX) { \
|
||||
BACKGROUND_TEXT_SELECT_CHARACTER; \
|
||||
charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) >> 2) + localY; \
|
||||
charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
|
||||
tileData = carryData; \
|
||||
for (x = 0; x < 8; ++x) { \
|
||||
if (!mosaicWait) { \
|
||||
paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
|
||||
palette = &mainPalette[paletteData]; \
|
||||
tileData = ((uint32_t*) vram)[charBase]; \
|
||||
LOAD_32(tileData, charBase, vram); \
|
||||
if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
|
||||
tileData >>= x * 4; \
|
||||
} else { \
|
||||
|
@ -930,8 +905,8 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re
|
|||
BACKGROUND_TEXT_SELECT_CHARACTER; \
|
||||
paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
|
||||
palette = &mainPalette[paletteData]; \
|
||||
charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) >> 2) + localY; \
|
||||
tileData = ((uint32_t*) vram)[charBase]; \
|
||||
charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
|
||||
LOAD_32(tileData, charBase, vram); \
|
||||
if (tileData) { \
|
||||
if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
|
||||
BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
|
||||
|
@ -975,53 +950,93 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re
|
|||
}
|
||||
|
||||
#define DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_256(BLEND, OBJWIN) \
|
||||
/* TODO: hflip */ \
|
||||
charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) >> 2) + (localY << 1); \
|
||||
charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
|
||||
int end2 = end - 4; \
|
||||
int shift = inX & 0x3; \
|
||||
if (end2 > 0) { \
|
||||
tileData = ((uint32_t*) vram)[charBase]; \
|
||||
if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
|
||||
int shift = inX & 0x3; \
|
||||
if (end2 > outX) { \
|
||||
LOAD_32(tileData, charBase, vram); \
|
||||
tileData >>= 8 * shift; \
|
||||
shift = 0; \
|
||||
for (; outX < end2; ++outX) { \
|
||||
pixel = &renderer->row[outX]; \
|
||||
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
LOAD_32(tileData, charBase + 4, vram); \
|
||||
tileData >>= 8 * shift; \
|
||||
shift = 0; \
|
||||
for (; outX < end2; ++outX) { \
|
||||
uint32_t* pixel = &renderer->row[outX]; \
|
||||
for (; outX < end; ++outX) { \
|
||||
pixel = &renderer->row[outX]; \
|
||||
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
tileData = ((uint32_t*) vram)[charBase + 1]; \
|
||||
tileData >>= 8 * shift; \
|
||||
for (; outX < end; ++outX) { \
|
||||
uint32_t* pixel = &renderer->row[outX]; \
|
||||
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
|
||||
} else { \
|
||||
int start = outX; \
|
||||
outX = end - 1; \
|
||||
if (end2 > start) { \
|
||||
LOAD_32(tileData, charBase, vram); \
|
||||
for (; outX >= end2; --outX) { \
|
||||
pixel = &renderer->row[outX]; \
|
||||
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
|
||||
} \
|
||||
charBase += 4; \
|
||||
} \
|
||||
\
|
||||
LOAD_32(tileData, charBase, vram); \
|
||||
for (; outX >= renderer->start; --outX) { \
|
||||
pixel = &renderer->row[outX]; \
|
||||
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
|
||||
} \
|
||||
outX = end; \
|
||||
}
|
||||
|
||||
#define DRAW_BACKGROUND_MODE_0_TILE_PREFIX_256(BLEND, OBJWIN) \
|
||||
/* TODO: hflip */ \
|
||||
charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) >> 2) + (localY << 1); \
|
||||
charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
|
||||
outX = renderer->end - 8 + end; \
|
||||
int end2 = 4 - end; \
|
||||
if (end2 > 0) { \
|
||||
tileData = ((uint32_t*) vram)[charBase]; \
|
||||
for (; outX < renderer->end - end2; ++outX) { \
|
||||
uint32_t* pixel = &renderer->row[outX]; \
|
||||
if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
|
||||
if (end2 > 0) { \
|
||||
LOAD_32(tileData, charBase, vram); \
|
||||
for (; outX < renderer->end - end2; ++outX) { \
|
||||
pixel = &renderer->row[outX]; \
|
||||
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
|
||||
} \
|
||||
charBase += 4; \
|
||||
} \
|
||||
\
|
||||
LOAD_32(tileData, charBase, vram); \
|
||||
for (; outX < renderer->end; ++outX) { \
|
||||
pixel = &renderer->row[outX]; \
|
||||
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
|
||||
} \
|
||||
} else { \
|
||||
int shift = end & 0x3; \
|
||||
int start = outX; \
|
||||
outX = renderer->end - 1; \
|
||||
if (end2 > 0) { \
|
||||
LOAD_32(tileData, charBase, vram); \
|
||||
tileData >>= 8 * shift; \
|
||||
for (; outX >= start + 4; --outX) { \
|
||||
pixel = &renderer->row[outX]; \
|
||||
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
|
||||
} \
|
||||
shift = 0; \
|
||||
} \
|
||||
\
|
||||
LOAD_32(tileData, charBase + 4, vram); \
|
||||
tileData >>= 8 * shift; \
|
||||
for (; outX >= start; --outX) { \
|
||||
pixel = &renderer->row[outX]; \
|
||||
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
|
||||
} \
|
||||
++charBase; \
|
||||
} \
|
||||
\
|
||||
tileData = ((uint32_t*) vram)[charBase]; \
|
||||
for (; outX < renderer->end; ++outX) { \
|
||||
uint32_t* pixel = &renderer->row[outX]; \
|
||||
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
|
||||
}
|
||||
|
||||
#define DRAW_BACKGROUND_MODE_0_TILES_256(BLEND, OBJWIN) \
|
||||
for (; tileX < tileEnd; ++tileX) { \
|
||||
BACKGROUND_TEXT_SELECT_CHARACTER; \
|
||||
charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) >> 2) + (localY << 1); \
|
||||
charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
|
||||
if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
|
||||
tileData = ((uint32_t*) vram)[charBase]; \
|
||||
LOAD_32(tileData, charBase, vram); \
|
||||
if (tileData) { \
|
||||
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
|
||||
++pixel; \
|
||||
|
@ -1034,7 +1049,7 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re
|
|||
} else { \
|
||||
pixel += 4; \
|
||||
} \
|
||||
tileData = ((uint32_t*) vram)[charBase + 1]; \
|
||||
LOAD_32(tileData, charBase + 4, vram); \
|
||||
if (tileData) { \
|
||||
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
|
||||
++pixel; \
|
||||
|
@ -1048,7 +1063,7 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re
|
|||
pixel += 4; \
|
||||
} \
|
||||
} else { \
|
||||
uint32_t tileData = ((uint32_t*) vram)[charBase + 1]; \
|
||||
LOAD_32(tileData, charBase + 4, vram); \
|
||||
if (tileData) { \
|
||||
pixel += 3; \
|
||||
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
|
||||
|
@ -1060,7 +1075,7 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re
|
|||
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
|
||||
} \
|
||||
pixel += 4; \
|
||||
tileData = ((uint32_t*) vram)[charBase]; \
|
||||
LOAD_32(tileData, charBase, vram); \
|
||||
if (tileData) { \
|
||||
pixel += 3; \
|
||||
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
|
||||
|
@ -1078,24 +1093,24 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re
|
|||
#define DRAW_BACKGROUND_MODE_0_MOSAIC_256(BLEND, OBJWIN) \
|
||||
for (; tileX < tileEnd; ++tileX) { \
|
||||
BACKGROUND_TEXT_SELECT_CHARACTER; \
|
||||
charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) >> 2) + (localY << 1); \
|
||||
charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
|
||||
tileData = carryData; \
|
||||
for (x = 0; x < 8; ++x) { \
|
||||
if (!mosaicWait) { \
|
||||
if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
|
||||
if (x >= 4) { \
|
||||
tileData = ((uint32_t*) vram)[charBase + 1]; \
|
||||
LOAD_32(tileData, charBase + 4, vram); \
|
||||
tileData >>= (x - 4) * 8; \
|
||||
} else { \
|
||||
tileData = ((uint32_t*) vram)[charBase]; \
|
||||
LOAD_32(tileData, charBase, vram); \
|
||||
tileData >>= x * 8; \
|
||||
} \
|
||||
} else { \
|
||||
if (x >= 4) { \
|
||||
tileData = ((uint32_t*) vram)[charBase]; \
|
||||
LOAD_32(tileData, charBase, vram); \
|
||||
tileData >>= (7 - x) * 8; \
|
||||
} else { \
|
||||
tileData = ((uint32_t*) vram)[charBase + 1]; \
|
||||
LOAD_32(tileData, charBase + 4, vram); \
|
||||
tileData >>= (3 - x) * 8; \
|
||||
} \
|
||||
} \
|
||||
|
@ -1112,8 +1127,8 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re
|
|||
|
||||
#define DRAW_BACKGROUND_MODE_0(BPP, BLEND, OBJWIN) \
|
||||
uint32_t* pixel = &renderer->row[outX]; \
|
||||
if (background->mosaic && renderer->mosaic.bgH) { \
|
||||
int mosaicH = renderer->mosaic.bgH + 1; \
|
||||
if (background->mosaic && GBAMosaicControlGetBgH(renderer->mosaic)) { \
|
||||
int mosaicH = GBAMosaicControlGetBgH(renderer->mosaic) + 1; \
|
||||
int x; \
|
||||
int mosaicWait = outX % mosaicH; \
|
||||
int carryData = 0; \
|
||||
|
@ -1156,7 +1171,7 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re
|
|||
static void _drawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y) {
|
||||
int inX = renderer->start + background->x;
|
||||
if (background->mosaic) {
|
||||
int mosaicV = renderer->mosaic.bgV + 1;
|
||||
int mosaicV = GBAMosaicControlGetBgV(renderer->mosaic) + 1;
|
||||
y -= y % mosaicV;
|
||||
}
|
||||
int inY = y + background->y;
|
||||
|
@ -1181,7 +1196,7 @@ static void _drawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, stru
|
|||
|
||||
uint32_t screenBase;
|
||||
uint32_t charBase;
|
||||
int variant = background->target1 && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
|
||||
int variant = background->target1 && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
|
||||
color_t* mainPalette = renderer->normalPalette;
|
||||
if (variant) {
|
||||
mainPalette = renderer->variantPalette;
|
||||
|
@ -1240,7 +1255,7 @@ static void _drawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, stru
|
|||
int flags = (background->priority << OFFSET_PRIORITY) | (background->index << OFFSET_INDEX) | FLAG_IS_BACKGROUND; \
|
||||
flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA); \
|
||||
flags |= FLAG_TARGET_2 * background->target2; \
|
||||
int variant = background->target1 && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN); \
|
||||
int variant = background->target1 && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN); \
|
||||
color_t* palette = renderer->normalPalette; \
|
||||
if (variant) { \
|
||||
palette = renderer->variantPalette; \
|
||||
|
@ -1308,7 +1323,7 @@ static void _drawBackgroundMode3(struct GBAVideoSoftwareRenderer* renderer, stru
|
|||
for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
|
||||
BACKGROUND_BITMAP_ITERATE(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
|
||||
|
||||
color = ((uint16_t*)renderer->d.vram)[(localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS];
|
||||
LOAD_16(color, ((localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS) << 1, renderer->d.vram);
|
||||
#ifndef COLOR_16_BIT
|
||||
unsigned color32;
|
||||
color32 = 0;
|
||||
|
@ -1336,7 +1351,7 @@ static void _drawBackgroundMode4(struct GBAVideoSoftwareRenderer* renderer, stru
|
|||
|
||||
uint16_t color;
|
||||
uint32_t offset = 0;
|
||||
if (renderer->dispcnt.frameSelect) {
|
||||
if (GBARegisterDISPCNTIsFrameSelect(renderer->dispcnt)) {
|
||||
offset = 0xA000;
|
||||
}
|
||||
|
||||
|
@ -1364,7 +1379,7 @@ static void _drawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer, stru
|
|||
|
||||
uint32_t color;
|
||||
uint32_t offset = 0;
|
||||
if (renderer->dispcnt.frameSelect) {
|
||||
if (GBARegisterDISPCNTIsFrameSelect(renderer->dispcnt)) {
|
||||
offset = 0xA000;
|
||||
}
|
||||
|
||||
|
@ -1373,7 +1388,7 @@ static void _drawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer, stru
|
|||
for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
|
||||
BACKGROUND_BITMAP_ITERATE(160, 128);
|
||||
|
||||
color = ((uint16_t*)renderer->d.vram)[offset + (localX >> 8) + (localY >> 8) * 160];
|
||||
LOAD_16(color, (offset + (localX >> 8) + (localY >> 8) * 160) << 1, renderer->d.vram);
|
||||
#ifndef COLOR_16_BIT
|
||||
unsigned color32 = 0;
|
||||
color32 |= (color << 9) & 0xF80000;
|
||||
|
@ -1397,6 +1412,7 @@ static void _drawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer, stru
|
|||
|
||||
#define SPRITE_NORMAL_LOOP(DEPTH, TYPE) \
|
||||
SPRITE_YBASE_ ## DEPTH(inY); \
|
||||
unsigned tileData; \
|
||||
for (; outX < condition; ++outX, inX += xOffset) { \
|
||||
if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
|
||||
continue; \
|
||||
|
@ -1407,6 +1423,7 @@ static void _drawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer, stru
|
|||
|
||||
#define SPRITE_MOSAIC_LOOP(DEPTH, TYPE) \
|
||||
SPRITE_YBASE_ ## DEPTH(inY); \
|
||||
unsigned tileData; \
|
||||
if (outX % mosaicH) { \
|
||||
inX += (mosaicH - (outX % mosaicH)) * xOffset; \
|
||||
outX += mosaicH - (outX % mosaicH); \
|
||||
|
@ -1421,14 +1438,15 @@ static void _drawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer, stru
|
|||
}
|
||||
|
||||
#define SPRITE_TRANSFORMED_LOOP(DEPTH, TYPE) \
|
||||
int outX; \
|
||||
for (outX = x >= start ? x : start; outX < x + totalWidth && outX < end; ++outX) { \
|
||||
unsigned tileData; \
|
||||
for (; outX < x + totalWidth && outX < end; ++outX, ++inX) { \
|
||||
if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
|
||||
continue; \
|
||||
} \
|
||||
int inX = outX - x; \
|
||||
int localX = ((mat->a * (inX - (totalWidth >> 1)) + mat->b * (inY - (totalHeight >> 1))) >> 8) + (width >> 1); \
|
||||
int localY = ((mat->c * (inX - (totalWidth >> 1)) + mat->d * (inY - (totalHeight >> 1))) >> 8) + (height >> 1); \
|
||||
xAccum += mat.a; \
|
||||
yAccum += mat.c; \
|
||||
int localX = (xAccum >> 8) + (width >> 1); \
|
||||
int localY = (yAccum >> 8) + (height >> 1); \
|
||||
\
|
||||
if (localX < 0 || localX >= width || localY < 0 || localY >= height) { \
|
||||
continue; \
|
||||
|
@ -1440,52 +1458,53 @@ static void _drawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer, stru
|
|||
}
|
||||
|
||||
#define SPRITE_XBASE_16(localX) unsigned xBase = (localX & ~0x7) * 4 + ((localX >> 1) & 2);
|
||||
#define SPRITE_YBASE_16(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width >> 1 : 0x80) + (localY & 0x7) * 4;
|
||||
#define SPRITE_YBASE_16(localY) unsigned yBase = (localY & ~0x7) * (GBARegisterDISPCNTIsObjCharacterMapping(renderer->dispcnt) ? width >> 1 : 0x80) + (localY & 0x7) * 4;
|
||||
|
||||
#define SPRITE_DRAW_PIXEL_16_NORMAL(localX) \
|
||||
unsigned tileData = vramBase[((yBase + charBase + xBase) & 0x7FFF) >> 1]; \
|
||||
LOAD_16(tileData, ((yBase + charBase + xBase) & 0x7FFF), vramBase); \
|
||||
tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
|
||||
if (tileData && (renderer->spriteLayer[outX] & FLAG_ORDER_MASK) > flags) { \
|
||||
renderer->spriteLayer[outX] = palette[tileData] | flags; \
|
||||
}
|
||||
|
||||
#define SPRITE_DRAW_PIXEL_16_OBJWIN(localX) \
|
||||
unsigned tileData = vramBase[((yBase + charBase + xBase) & 0x7FFF) >> 1]; \
|
||||
LOAD_16(tileData, ((yBase + charBase + xBase) & 0x7FFF), vramBase); \
|
||||
tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
|
||||
if (tileData) { \
|
||||
renderer->row[outX] |= FLAG_OBJWIN; \
|
||||
}
|
||||
|
||||
#define SPRITE_XBASE_256(localX) unsigned xBase = (localX & ~0x7) * 8 + (localX & 6);
|
||||
#define SPRITE_YBASE_256(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width : 0x80) + (localY & 0x7) * 8;
|
||||
#define SPRITE_YBASE_256(localY) unsigned yBase = (localY & ~0x7) * (GBARegisterDISPCNTIsObjCharacterMapping(renderer->dispcnt) ? width : 0x80) + (localY & 0x7) * 8;
|
||||
|
||||
#define SPRITE_DRAW_PIXEL_256_NORMAL(localX) \
|
||||
unsigned tileData = vramBase[((yBase + charBase + xBase) & 0x7FFF) >> 1]; \
|
||||
LOAD_16(tileData, ((yBase + charBase + xBase) & 0x7FFF), vramBase); \
|
||||
tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
|
||||
if (tileData && (renderer->spriteLayer[outX] & FLAG_ORDER_MASK) > flags) { \
|
||||
renderer->spriteLayer[outX] = palette[tileData] | flags; \
|
||||
}
|
||||
|
||||
#define SPRITE_DRAW_PIXEL_256_OBJWIN(localX) \
|
||||
unsigned tileData = vramBase[((yBase + charBase + xBase) & 0x7FFF) >> 1]; \
|
||||
LOAD_16(tileData, ((yBase + charBase + xBase) & 0x7FFF), vramBase); \
|
||||
tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
|
||||
if (tileData) { \
|
||||
renderer->row[outX] |= FLAG_OBJWIN; \
|
||||
}
|
||||
|
||||
static int _preprocessSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y) {
|
||||
int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
|
||||
int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
|
||||
int width = _objSizes[GBAObjAttributesAGetShape(sprite->a) * 8 + GBAObjAttributesBGetSize(sprite->b) * 2];
|
||||
int height = _objSizes[GBAObjAttributesAGetShape(sprite->a) * 8 + GBAObjAttributesBGetSize(sprite->b) * 2 + 1];
|
||||
int start = renderer->start;
|
||||
int end = renderer->end;
|
||||
uint32_t flags = sprite->priority << OFFSET_PRIORITY;
|
||||
flags |= FLAG_TARGET_1 * ((renderer->currentWindow.blendEnable && renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT);
|
||||
flags |= FLAG_OBJWIN * (sprite->mode == OBJ_MODE_OBJWIN);
|
||||
int x = sprite->x;
|
||||
uint32_t flags = GBAObjAttributesCGetPriority(sprite->c) << OFFSET_PRIORITY;
|
||||
flags |= FLAG_TARGET_1 * ((GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_SEMITRANSPARENT);
|
||||
flags |= FLAG_OBJWIN * (GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_OBJWIN);
|
||||
int32_t x = GBAObjAttributesBGetX(sprite->b) << 23;
|
||||
x >>= 23;
|
||||
uint16_t* vramBase = &renderer->d.vram[BASE_TILE >> 1];
|
||||
unsigned charBase = sprite->tile * 0x20;
|
||||
int variant = renderer->target1Obj && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
|
||||
if (sprite->mode == OBJ_MODE_SEMITRANSPARENT && renderer->target2Bd) {
|
||||
unsigned charBase = GBAObjAttributesCGetTile(sprite->c) * 0x20;
|
||||
int variant = renderer->target1Obj && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
|
||||
if (GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_SEMITRANSPARENT && renderer->target2Bd) {
|
||||
// Hack: if a sprite is blended, then the variant palette is not used, but we don't know if it's blended in advance
|
||||
variant = 0;
|
||||
}
|
||||
|
@ -1494,91 +1513,81 @@ static int _preprocessSprite(struct GBAVideoSoftwareRenderer* renderer, struct G
|
|||
palette = &renderer->variantPalette[0x100];
|
||||
}
|
||||
|
||||
int outX = x >= start ? x : start;
|
||||
int condition = x + width;
|
||||
int mosaicH = 1;
|
||||
if (sprite->mosaic) {
|
||||
mosaicH = renderer->mosaic.objH + 1;
|
||||
if (condition % mosaicH) {
|
||||
condition += mosaicH - (condition % mosaicH);
|
||||
}
|
||||
}
|
||||
int inY = y - sprite->y;
|
||||
if (sprite->y + height - 256 >= 0) {
|
||||
inY += 256;
|
||||
}
|
||||
if (sprite->vflip) {
|
||||
inY = height - inY - 1;
|
||||
}
|
||||
if (end < condition) {
|
||||
condition = end;
|
||||
}
|
||||
int inX = outX - x;
|
||||
int xOffset = 1;
|
||||
if (sprite->hflip) {
|
||||
inX = width - inX - 1;
|
||||
xOffset = -1;
|
||||
}
|
||||
if (!sprite->multipalette) {
|
||||
palette = &palette[sprite->palette << 4];
|
||||
if (flags & FLAG_OBJWIN) {
|
||||
SPRITE_NORMAL_LOOP(16, OBJWIN);
|
||||
} else if (sprite->mosaic) {
|
||||
SPRITE_MOSAIC_LOOP(16, NORMAL);
|
||||
} else {
|
||||
SPRITE_NORMAL_LOOP(16, NORMAL);
|
||||
}
|
||||
} else {
|
||||
if (flags & FLAG_OBJWIN) {
|
||||
SPRITE_NORMAL_LOOP(256, OBJWIN);
|
||||
} else if (sprite->mosaic) {
|
||||
SPRITE_MOSAIC_LOOP(256, NORMAL);
|
||||
} else {
|
||||
SPRITE_NORMAL_LOOP(256, NORMAL);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
int inY = y - (int) GBAObjAttributesAGetY(sprite->a);
|
||||
|
||||
static int _preprocessTransformedSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBATransformedObj* sprite, int y) {
|
||||
int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
|
||||
int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
|
||||
int totalWidth = width << sprite->doublesize;
|
||||
int totalHeight = height << sprite->doublesize;
|
||||
int start = renderer->start;
|
||||
int end = renderer->end;
|
||||
uint32_t flags = sprite->priority << OFFSET_PRIORITY;
|
||||
flags |= FLAG_TARGET_1 * ((renderer->currentWindow.blendEnable && renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT);
|
||||
flags |= FLAG_OBJWIN * (sprite->mode == OBJ_MODE_OBJWIN);
|
||||
int x = sprite->x;
|
||||
uint16_t* vramBase = &renderer->d.vram[BASE_TILE >> 1];
|
||||
unsigned charBase = sprite->tile * 0x20;
|
||||
struct GBAOAMMatrix* mat = &renderer->d.oam->mat[sprite->matIndex];
|
||||
int variant = renderer->target1Obj && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
|
||||
if (sprite->mode == OBJ_MODE_SEMITRANSPARENT && renderer->target2Bd) {
|
||||
// Hack: if a sprite is blended, then the variant palette is not used, but we don't know if it's blended in advance
|
||||
variant = 0;
|
||||
}
|
||||
color_t* palette = &renderer->normalPalette[0x100];
|
||||
if (variant) {
|
||||
palette = &renderer->variantPalette[0x100];
|
||||
}
|
||||
int inY = y - sprite->y;
|
||||
if (inY < 0) {
|
||||
inY += 256;
|
||||
}
|
||||
if (!sprite->multipalette) {
|
||||
palette = &palette[sprite->palette << 4];
|
||||
if (flags & FLAG_OBJWIN) {
|
||||
SPRITE_TRANSFORMED_LOOP(16, OBJWIN);
|
||||
if (GBAObjAttributesAIsTransformed(sprite->a)) {
|
||||
int totalWidth = width << GBAObjAttributesAGetDoubleSize(sprite->a);
|
||||
int totalHeight = height << GBAObjAttributesAGetDoubleSize(sprite->a);
|
||||
struct GBAOAMMatrix mat;
|
||||
LOAD_16(mat.a, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].a);
|
||||
LOAD_16(mat.b, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].b);
|
||||
LOAD_16(mat.c, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].c);
|
||||
LOAD_16(mat.d, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].d);
|
||||
|
||||
if (inY < 0) {
|
||||
inY += 256;
|
||||
}
|
||||
int outX = x >= start ? x : start;
|
||||
int inX = outX - x;
|
||||
int xAccum = mat.a * (inX - 1 - (totalWidth >> 1)) + mat.b * (inY - (totalHeight >> 1));
|
||||
int yAccum = mat.c * (inX - 1 - (totalWidth >> 1)) + mat.d * (inY - (totalHeight >> 1));
|
||||
|
||||
if (!GBAObjAttributesAIs256Color(sprite->a)) {
|
||||
palette = &palette[GBAObjAttributesCGetPalette(sprite->c) << 4];
|
||||
if (flags & FLAG_OBJWIN) {
|
||||
SPRITE_TRANSFORMED_LOOP(16, OBJWIN);
|
||||
} else {
|
||||
SPRITE_TRANSFORMED_LOOP(16, NORMAL);
|
||||
}
|
||||
} else {
|
||||
SPRITE_TRANSFORMED_LOOP(16, NORMAL);
|
||||
if (flags & FLAG_OBJWIN) {
|
||||
SPRITE_TRANSFORMED_LOOP(256, OBJWIN);
|
||||
} else {
|
||||
SPRITE_TRANSFORMED_LOOP(256, NORMAL);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (flags & FLAG_OBJWIN) {
|
||||
SPRITE_TRANSFORMED_LOOP(256, OBJWIN);
|
||||
int outX = x >= start ? x : start;
|
||||
int condition = x + width;
|
||||
int mosaicH = 1;
|
||||
if (GBAObjAttributesAIsMosaic(sprite->a)) {
|
||||
mosaicH = GBAMosaicControlGetObjH(renderer->mosaic) + 1;
|
||||
if (condition % mosaicH) {
|
||||
condition += mosaicH - (condition % mosaicH);
|
||||
}
|
||||
}
|
||||
if ((int) GBAObjAttributesAGetY(sprite->a) + height - 256 >= 0) {
|
||||
inY += 256;
|
||||
}
|
||||
if (GBAObjAttributesBIsVFlip(sprite->b)) {
|
||||
inY = height - inY - 1;
|
||||
}
|
||||
if (end < condition) {
|
||||
condition = end;
|
||||
}
|
||||
int inX = outX - x;
|
||||
int xOffset = 1;
|
||||
if (GBAObjAttributesBIsHFlip(sprite->b)) {
|
||||
inX = width - inX - 1;
|
||||
xOffset = -1;
|
||||
}
|
||||
if (!GBAObjAttributesAIs256Color(sprite->a)) {
|
||||
palette = &palette[GBAObjAttributesCGetPalette(sprite->c) << 4];
|
||||
if (flags & FLAG_OBJWIN) {
|
||||
SPRITE_NORMAL_LOOP(16, OBJWIN);
|
||||
} else if (GBAObjAttributesAIsMosaic(sprite->a)) {
|
||||
SPRITE_MOSAIC_LOOP(16, NORMAL);
|
||||
} else {
|
||||
SPRITE_NORMAL_LOOP(16, NORMAL);
|
||||
}
|
||||
} else {
|
||||
SPRITE_TRANSFORMED_LOOP(256, NORMAL);
|
||||
if (flags & FLAG_OBJWIN) {
|
||||
SPRITE_NORMAL_LOOP(256, OBJWIN);
|
||||
} else if (GBAObjAttributesAIsMosaic(sprite->a)) {
|
||||
SPRITE_MOSAIC_LOOP(256, NORMAL);
|
||||
} else {
|
||||
SPRITE_NORMAL_LOOP(256, NORMAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
|
@ -1589,10 +1598,10 @@ static void _postprocessSprite(struct GBAVideoSoftwareRenderer* renderer, unsign
|
|||
uint32_t* pixel = renderer->row;
|
||||
uint32_t flags = FLAG_TARGET_2 * renderer->target2Obj;
|
||||
|
||||
int objwinSlowPath = renderer->dispcnt.objwinEnable;
|
||||
int objwinSlowPath = GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt);
|
||||
int objwinDisable = 0;
|
||||
if (objwinSlowPath) {
|
||||
objwinDisable = !renderer->objwin.objEnable;
|
||||
objwinDisable = !GBAWindowControlIsObjEnable(renderer->objwin.packed);
|
||||
}
|
||||
if (objwinSlowPath && objwinDisable) {
|
||||
for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x, ++pixel) {
|
||||
|
|
|
@ -12,10 +12,7 @@ typedef uint32_t color_t;
|
|||
#endif
|
||||
|
||||
struct GBAVideoSoftwareSprite {
|
||||
union {
|
||||
struct GBAObj obj;
|
||||
struct GBATransformedObj tobj;
|
||||
};
|
||||
struct GBAObj obj;
|
||||
int y;
|
||||
int endY;
|
||||
};
|
||||
|
@ -76,27 +73,27 @@ enum {
|
|||
|
||||
#define IS_WRITABLE(PIXEL) ((PIXEL) & 0xFE000000)
|
||||
|
||||
union WindowRegion {
|
||||
struct {
|
||||
uint8_t end;
|
||||
uint8_t start;
|
||||
};
|
||||
uint16_t packed;
|
||||
struct WindowRegion {
|
||||
uint8_t end;
|
||||
uint8_t start;
|
||||
};
|
||||
|
||||
DECL_BITFIELD(GBAWindowControl, uint8_t);
|
||||
DECL_BIT(GBAWindowControl, Bg0Enable, 0);
|
||||
DECL_BIT(GBAWindowControl, Bg1Enable, 1);
|
||||
DECL_BIT(GBAWindowControl, Bg2Enable, 2);
|
||||
DECL_BIT(GBAWindowControl, Bg3Enable, 3);
|
||||
DECL_BIT(GBAWindowControl, ObjEnable, 4);
|
||||
DECL_BIT(GBAWindowControl, BlendEnable, 5);
|
||||
|
||||
DECL_BITFIELD(GBAMosaicControl, uint16_t);
|
||||
DECL_BITS(GBAMosaicControl, BgH, 0, 4);
|
||||
DECL_BITS(GBAMosaicControl, BgV, 4, 4);
|
||||
DECL_BITS(GBAMosaicControl, ObjH, 8, 4);
|
||||
DECL_BITS(GBAMosaicControl, ObjV, 12, 4);
|
||||
|
||||
struct WindowControl {
|
||||
union {
|
||||
struct {
|
||||
unsigned bg0Enable : 1;
|
||||
unsigned bg1Enable : 1;
|
||||
unsigned bg2Enable : 1;
|
||||
unsigned bg3Enable : 1;
|
||||
unsigned objEnable : 1;
|
||||
unsigned blendEnable : 1;
|
||||
unsigned : 2;
|
||||
};
|
||||
uint8_t packed;
|
||||
};
|
||||
GBAWindowControl packed;
|
||||
int8_t priority;
|
||||
};
|
||||
|
||||
|
@ -113,7 +110,7 @@ struct GBAVideoSoftwareRenderer {
|
|||
color_t* outputBuffer;
|
||||
unsigned outputBufferStride;
|
||||
|
||||
union GBARegisterDISPCNT dispcnt;
|
||||
GBARegisterDISPCNT dispcnt;
|
||||
|
||||
uint32_t row[VIDEO_HORIZONTAL_PIXELS];
|
||||
uint32_t spriteLayer[VIDEO_HORIZONTAL_PIXELS];
|
||||
|
@ -132,19 +129,11 @@ struct GBAVideoSoftwareRenderer {
|
|||
uint16_t bldb;
|
||||
uint16_t bldy;
|
||||
|
||||
union {
|
||||
struct {
|
||||
unsigned bgH : 4;
|
||||
unsigned bgV : 4;
|
||||
unsigned objH : 4;
|
||||
unsigned objV : 4;
|
||||
};
|
||||
uint16_t packed;
|
||||
} mosaic;
|
||||
GBAMosaicControl mosaic;
|
||||
|
||||
struct WindowN {
|
||||
union WindowRegion h;
|
||||
union WindowRegion v;
|
||||
struct WindowRegion h;
|
||||
struct WindowRegion v;
|
||||
struct WindowControl control;
|
||||
} winN[2];
|
||||
|
||||
|
|
|
@ -201,13 +201,15 @@ struct ARMDebugger* createDebugger(struct StartupOptions* opts) {
|
|||
void usage(const char* arg0, const char* extraOptions) {
|
||||
printf("usage: %s [option ...] file\n", arg0);
|
||||
puts("\nGeneric options:");
|
||||
puts(" -b, --bios FILE GBA BIOS file to use");
|
||||
puts(" -b, --bios FILE GBA BIOS file to use");
|
||||
#ifdef USE_CLI_DEBUGGER
|
||||
puts(" -d, --debug Use command-line debugger");
|
||||
puts(" -d, --debug Use command-line debugger");
|
||||
#endif
|
||||
#ifdef USE_GDB_STUB
|
||||
puts(" -g, --gdb Start GDB session (default port 2345)");
|
||||
puts(" -g, --gdb Start GDB session (default port 2345)");
|
||||
#endif
|
||||
puts(" -p, --patch Apply a specified patch file when running");
|
||||
puts(" -s, --frameskip N Skip every N frames");
|
||||
if (extraOptions) {
|
||||
puts(extraOptions);
|
||||
}
|
||||
|
|
|
@ -7,18 +7,22 @@
|
|||
#include <signal.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#define PERF_OPTIONS "NS:"
|
||||
#define PERF_OPTIONS "F:NPS:"
|
||||
#define PERF_USAGE \
|
||||
"\nBenchmark options:\n" \
|
||||
" -N Disable video rendering entirely" \
|
||||
" -F FRAMES Run for the specified number of FRAMES before exiting\n" \
|
||||
" -N Disable video rendering entirely\n" \
|
||||
" -P CSV output, useful for parsing\n" \
|
||||
" -S SEC Run for SEC in-game seconds before exiting"
|
||||
|
||||
struct PerfOpts {
|
||||
bool noVideo;
|
||||
int duration;
|
||||
bool csv;
|
||||
unsigned duration;
|
||||
unsigned frames;
|
||||
};
|
||||
|
||||
static void _GBAPerfRunloop(struct GBAThread* context, int* frames);
|
||||
static void _GBAPerfRunloop(struct GBAThread* context, int* frames, bool quiet);
|
||||
static void _GBAPerfShutdown(int signal);
|
||||
static bool _parsePerfOpts(struct SubParser* parser, int option, const char* arg);
|
||||
|
||||
|
@ -30,7 +34,7 @@ int main(int argc, char** argv) {
|
|||
struct GBAVideoSoftwareRenderer renderer;
|
||||
GBAVideoSoftwareRendererCreate(&renderer);
|
||||
|
||||
struct PerfOpts perfOpts = { false, 0 };
|
||||
struct PerfOpts perfOpts = { false, false, 0, 0 };
|
||||
struct SubParser subparser = {
|
||||
.usage = PERF_USAGE,
|
||||
.parse = _parsePerfOpts,
|
||||
|
@ -58,16 +62,24 @@ int main(int argc, char** argv) {
|
|||
}
|
||||
|
||||
context.debugger = createDebugger(&opts);
|
||||
char gameCode[5] = { 0 };
|
||||
|
||||
GBAMapOptionsToContext(&opts, &context);
|
||||
|
||||
GBAThreadStart(&context);
|
||||
GBAGetGameCode(context.gba, gameCode);
|
||||
|
||||
int frames = perfOpts.duration;
|
||||
time_t start = time(0);
|
||||
_GBAPerfRunloop(&context, &frames);
|
||||
time_t end = time(0);
|
||||
int duration = end - start;
|
||||
int frames = perfOpts.frames;
|
||||
if (!frames) {
|
||||
frames = perfOpts.duration * 60;
|
||||
}
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, 0);
|
||||
uint64_t start = 1000000 * tv.tv_sec + tv.tv_usec;
|
||||
_GBAPerfRunloop(&context, &frames, perfOpts.csv);
|
||||
gettimeofday(&tv, 0);
|
||||
uint64_t end = 1000000 * tv.tv_sec + tv.tv_usec;
|
||||
uint64_t duration = end - start;
|
||||
|
||||
GBAThreadJoin(&context);
|
||||
freeOptions(&opts);
|
||||
|
@ -75,12 +87,24 @@ int main(int argc, char** argv) {
|
|||
|
||||
free(renderer.outputBuffer);
|
||||
|
||||
printf("%u frames in %i seconds: %g fps (%gx)\n", frames, duration, frames / (float) duration, frames / (duration * 60.f));
|
||||
float scaledFrames = frames * 1000000.f;
|
||||
if (perfOpts.csv) {
|
||||
puts("game_code,frames,duration,renderer");
|
||||
const char* rendererName;
|
||||
if (perfOpts.noVideo) {
|
||||
rendererName = "none";
|
||||
} else {
|
||||
rendererName = "software";
|
||||
}
|
||||
printf("%s,%i,%lli,%s\n", gameCode, frames, duration, rendererName);
|
||||
} else {
|
||||
printf("%u frames in %lli microseconds: %g fps (%gx)\n", frames, duration, scaledFrames / duration, scaledFrames / (duration * 60.f));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _GBAPerfRunloop(struct GBAThread* context, int* frames) {
|
||||
static void _GBAPerfRunloop(struct GBAThread* context, int* frames, bool quiet) {
|
||||
struct timeval lastEcho;
|
||||
gettimeofday(&lastEcho, 0);
|
||||
int duration = *frames;
|
||||
|
@ -90,25 +114,29 @@ static void _GBAPerfRunloop(struct GBAThread* context, int* frames) {
|
|||
if (GBASyncWaitFrameStart(&context->sync, 0)) {
|
||||
++*frames;
|
||||
++lastFrames;
|
||||
struct timeval currentTime;
|
||||
long timeDiff;
|
||||
gettimeofday(¤tTime, 0);
|
||||
timeDiff = currentTime.tv_sec - lastEcho.tv_sec;
|
||||
timeDiff *= 1000;
|
||||
timeDiff += (currentTime.tv_usec - lastEcho.tv_usec) / 1000;
|
||||
if (timeDiff >= 1000) {
|
||||
printf("\033[2K\rCurrent FPS: %g (%gx)", lastFrames / (timeDiff / 1000.0f), lastFrames / (float) (60 * (timeDiff / 1000.0f)));
|
||||
fflush(stdout);
|
||||
lastEcho = currentTime;
|
||||
lastFrames = 0;
|
||||
if (!quiet) {
|
||||
struct timeval currentTime;
|
||||
long timeDiff;
|
||||
gettimeofday(¤tTime, 0);
|
||||
timeDiff = currentTime.tv_sec - lastEcho.tv_sec;
|
||||
timeDiff *= 1000;
|
||||
timeDiff += (currentTime.tv_usec - lastEcho.tv_usec) / 1000;
|
||||
if (timeDiff >= 1000) {
|
||||
printf("\033[2K\rCurrent FPS: %g (%gx)", lastFrames / (timeDiff / 1000.0f), lastFrames / (float) (60 * (timeDiff / 1000.0f)));
|
||||
fflush(stdout);
|
||||
lastEcho = currentTime;
|
||||
lastFrames = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
GBASyncWaitFrameEnd(&context->sync);
|
||||
if (*frames == duration * 60) {
|
||||
if (*frames == duration) {
|
||||
_GBAPerfShutdown(0);
|
||||
}
|
||||
}
|
||||
printf("\033[2K\r");
|
||||
if (!quiet) {
|
||||
printf("\033[2K\r");
|
||||
}
|
||||
}
|
||||
|
||||
static void _GBAPerfShutdown(int signal) {
|
||||
|
@ -121,11 +149,17 @@ static void _GBAPerfShutdown(int signal) {
|
|||
static bool _parsePerfOpts(struct SubParser* parser, int option, const char* arg) {
|
||||
struct PerfOpts* opts = parser->opts;
|
||||
switch (option) {
|
||||
case 'F':
|
||||
opts->frames = strtoul(arg, 0, 10);
|
||||
return !errno;
|
||||
case 'N':
|
||||
opts->noVideo = true;
|
||||
return true;
|
||||
case 'P':
|
||||
opts->csv = true;
|
||||
return true;
|
||||
case 'S':
|
||||
opts->duration = strtol(arg, 0, 10);
|
||||
opts->duration = strtoul(arg, 0, 10);
|
||||
return !errno;
|
||||
default:
|
||||
return false;
|
||||
|
|
|
@ -18,8 +18,20 @@ endif()
|
|||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
find_package(Qt5Widgets REQUIRED)
|
||||
find_package(OpenGL REQUIRED)
|
||||
set(REQUIRES "Qt5Widgets")
|
||||
|
||||
if(APPLE)
|
||||
find_package(OpenGL REQUIRED)
|
||||
else()
|
||||
list(APPEND REQUIRES "OpenGL")
|
||||
endif()
|
||||
|
||||
find_feature(BUILD_QT ${REQUIRES})
|
||||
|
||||
if(NOT BUILD_QT)
|
||||
set(BUILD_QT OFF PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(SOURCE_FILES AudioDevice.cpp AudioProcessor.cpp Display.cpp GameController.cpp Window.cpp)
|
||||
|
||||
|
|
|
@ -13,7 +13,12 @@ if (SDL_VERSION EQUAL "2")
|
|||
endif()
|
||||
|
||||
if(SDL_VERSION EQUAL "1.2" OR NOT SDL2_FOUND)
|
||||
find_package(SDL 1.2 REQUIRED)
|
||||
find_package(SDL 1.2)
|
||||
endif()
|
||||
|
||||
if (NOT SDL2_FOUND AND NOT SDL_FOUND)
|
||||
set(BUILD_SDL OFF)
|
||||
return()
|
||||
endif()
|
||||
|
||||
file(GLOB PLATFORM_SRC ${CMAKE_SOURCE_DIR}/src/platform/sdl/sdl-*.c)
|
||||
|
|
|
@ -121,13 +121,6 @@ static int _GBASDLInit(struct GLSoftwareRenderer* renderer) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
#else
|
||||
SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1);
|
||||
#endif
|
||||
|
||||
#ifndef COLOR_16_BIT
|
||||
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
|
||||
|
@ -145,9 +138,11 @@ static int _GBASDLInit(struct GLSoftwareRenderer* renderer) {
|
|||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
renderer->window = SDL_CreateWindow(PROJECT_NAME, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, renderer->viewportWidth, renderer->viewportHeight, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | (SDL_WINDOW_FULLSCREEN_DESKTOP * renderer->events.fullscreen));
|
||||
SDL_GL_CreateContext(renderer->window);
|
||||
SDL_GL_SetSwapInterval(1);
|
||||
SDL_GetWindowSize(renderer->window, &renderer->viewportWidth, &renderer->viewportHeight);
|
||||
renderer->events.window = renderer->window;
|
||||
#else
|
||||
SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1);
|
||||
#ifdef COLOR_16_BIT
|
||||
SDL_SetVideoMode(renderer->viewportWidth, renderer->viewportHeight, 16, SDL_OPENGL);
|
||||
#else
|
||||
|
|
|
@ -77,6 +77,7 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents
|
|||
ARMDebuggerEnter(context->debugger, DEBUGGER_ENTER_MANUAL);
|
||||
}
|
||||
return;
|
||||
#ifdef USE_PNG
|
||||
case SDLK_F12:
|
||||
if (event->type == SDL_KEYDOWN) {
|
||||
GBAThreadInterrupt(context);
|
||||
|
@ -84,6 +85,7 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents
|
|||
GBAThreadContinue(context);
|
||||
}
|
||||
return;
|
||||
#endif
|
||||
case SDLK_TAB:
|
||||
context->sync.audioWait = event->type != SDL_KEYDOWN;
|
||||
return;
|
||||
|
|
|
@ -82,6 +82,9 @@ int main(int argc, char** argv) {
|
|||
renderer.audio.samples = context.audioBuffers;
|
||||
GBASDLInitAudio(&renderer.audio);
|
||||
|
||||
renderer.events.bindings = &context.inputMap;
|
||||
GBASDLInitEvents(&renderer.events);
|
||||
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
renderer.events.fullscreen = graphicsOpts.fullscreen;
|
||||
renderer.window = SDL_CreateWindow(PROJECT_NAME, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, renderer.viewportWidth, renderer.viewportHeight, SDL_WINDOW_OPENGL | (SDL_WINDOW_FULLSCREEN_DESKTOP * renderer.events.fullscreen));
|
||||
|
@ -139,8 +142,6 @@ static int _GBASDLInit(struct SoftwareRenderer* renderer) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
GBASDLInitEvents(&renderer->events);
|
||||
|
||||
#if !SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
#ifdef COLOR_16_BIT
|
||||
SDL_SetVideoMode(renderer->viewportWidth, renderer->viewportHeight, 16, SDL_DOUBLEBUF | SDL_HWSURFACE);
|
||||
|
@ -203,6 +204,7 @@ static void _GBASDLDeinit(struct SoftwareRenderer* renderer) {
|
|||
static void _GBASDLStart(struct GBAThread* threadContext) {
|
||||
struct SoftwareRenderer* renderer = threadContext->userData;
|
||||
renderer->audio.audio = &threadContext->gba->audio;
|
||||
renderer->audio.thread = threadContext;
|
||||
}
|
||||
|
||||
static void _GBASDLClean(struct GBAThread* threadContext) {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#include "util/png-io.h"
|
||||
|
||||
#ifdef USE_PNG
|
||||
|
||||
#include "vfs.h"
|
||||
|
||||
static void _pngWrite(png_structp png, png_bytep buffer, png_size_t size) {
|
||||
|
@ -74,7 +76,7 @@ bool PNGWriteCustomChunk(png_structp png, const char* name, size_t size, void* d
|
|||
if (setjmp(png_jmpbuf(png))) {
|
||||
return false;
|
||||
}
|
||||
png_write_chunk(png, (png_const_bytep) realName, data, size);
|
||||
png_write_chunk(png, (const png_bytep) realName, data, size);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -110,7 +112,7 @@ bool PNGInstallChunkHandler(png_structp png, void* context, ChunkHandler handler
|
|||
return false;
|
||||
}
|
||||
png_set_read_user_chunk_fn(png, context, handler);
|
||||
png_set_keep_unknown_chunks(png, PNG_HANDLE_CHUNK_ALWAYS, (png_const_bytep) chunkName, 1);
|
||||
png_set_keep_unknown_chunks(png, PNG_HANDLE_CHUNK_ALWAYS, (const png_bytep) chunkName, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -177,3 +179,5 @@ bool PNGReadFooter(png_structp png, png_infop end) {
|
|||
void PNGReadClose(png_structp png, png_infop info, png_infop end) {
|
||||
png_destroy_read_struct(&png, &info, &end);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include "common.h"
|
||||
|
||||
#ifdef USE_PNG
|
||||
|
||||
#include <png.h>
|
||||
|
||||
struct VFile;
|
||||
|
@ -29,3 +31,5 @@ bool PNGReadFooter(png_structp png, png_infop end);
|
|||
void PNGReadClose(png_structp png, png_infop info, png_infop end);
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue