Merge branch 'master' into qt

Conflicts:
	CMakeLists.txt
This commit is contained in:
Jeffrey Pfau 2014-10-07 02:48:38 -07:00
commit 6560db2ef5
39 changed files with 1311 additions and 984 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -8,5 +8,6 @@
struct ARMDebugger;
void ARMDebuggerInstallMemoryShim(struct ARMDebugger* debugger);
void ARMDebuggerRemoveMemoryShim(struct ARMDebugger* debugger);
#endif

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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, ...);

View File

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

18
src/gba/hle-bios.make Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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