all repos — mgba @ ef9081da7e5dfcb108887b0deb17e89a9610430b

mGBA Game Boy Advance Emulator

Merge branch 'master' into medusa
Vicki Pfau vi@endrift.com
Tue, 04 Aug 2020 23:11:14 -0700
commit

ef9081da7e5dfcb108887b0deb17e89a9610430b

parent

24aed3f57691c1737bc8f5a0f5cbff65628c9ac6

60 files changed, 919 insertions(+), 368 deletions(-)

jump to
M CHANGESCHANGES

@@ -37,19 +37,13 @@ - e-Reader card scanning

- Add WebP and APNG recording - Support for unlicensed Pokemon Jade/Diamond Game Boy mapper - Stack tracing tools in ARM debugger (by ahigerd) + - Command scripts for CLI debugger (by ahigerd) Emulation fixes: - ARM: Fix ALU reading PC after shifting - ARM: Fix STR storing PC after address calculation - - ARM: Fix LDM^ writeback to user-mode register - - ARM: Fix LDM^ {pc} differences (fixes mgba.io/i/1698) - - ARM: Fix edge case with Thumb SBC flags (fixes mgba.io/i/1818) - GB: Partially fix timing for skipped BIOS - - GB Memory: Fix OAM DMA from top 8 kB - GB MBC: Fix MBC1 mode changing behavior - - GB MBC: Fix MBC1 RAM enable bit selection - - GB MBC: Fix MBC2 bit selection - GB Video: Fix state after skipping BIOS (fixes mgba.io/i/1715 and mgba.io/i/1716) - - GB Video: Always initialize palette - GBA: Fix timing advancing too quickly in rare cases - GBA BIOS: Implement dummy sound driver calls - GBA BIOS: Improve HLE BIOS timing

@@ -57,48 +51,67 @@ - GBA BIOS: Fix reloading video registers after reset (fixes mgba.io/i/1808)

- GBA DMA: Linger last DMA on bus (fixes mgba.io/i/301 and mgba.io/i/1320) - GBA Memory: Improve gamepak prefetch timing - GBA Memory: Stall on VRAM access in mode 2 (fixes mgba.io/i/190) - - GBA Savedata: Fix potential corruption when loading a 1Mbit flash save - GBA SIO: Fix copying Normal mode transfer values + - GBA SIO: Fix Normal mode being totally broken (fixes mgba.io/i/1800) - GBA Video: Latch scanline at end of Hblank (fixes mgba.io/i/1319) - GBA Video: Fix Hblank timing - - GBA Video: Fix invalid read in mode 4 mosaic - - GBA Video: Fix color of disabled screen - SM83: Emulate HALT bug - - SM83: Fix flags on little endian PowerPC Other fixes: - All: Improve export headers (fixes mgba.io/i/1738) - - All: Correct format strings for some numbers on Windows (fixes mgba.io/i/1794) - - All: Correct more format strings on Windows (fixes mgba.io/i/1817) - - ARM: Fix decoder detection of branches with ALU and LDR instrctions - - CMake: Fix build with libzip 1.7 - Core: Ensure ELF regions can be written before trying - Debugger: Don't skip undefined instructions when debugger attached - FFmpeg: Fix some small memory leaks - FFmpeg: Fix encoding of time base - - GB Core: Fix extracting SRAM when none is present - - GBA: Fix leak if attempting to load BIOS multiple times - - GBA Savedata: Fix extracting save when not yet configured in-game - Qt: Force OpenGL paint engine creation thread (fixes mgba.io/i/1642) - Qt: Fix static compilation in MinGW (fixes mgba.io/i/1769) - - Qt: Fix file handle leak on opening an invalid ROM - Qt: Fix a race condition in the frame inspector - - Qt: Fix Italian RTC translation (fixes mgba.io/i/1798) - - Qt: Add missing option for Wisdom Tree in overrides list - - Util: Fix crash if PNG header fails to write - SM83: Simplify register pair access on big endian - - Wii: Fix pixelated filtering on interframe blending (fixes mgba.io/i/1830) Misc: + - 3DS: Use "wide mode" where applicable for slightly better filtering - GB: Allow pausing event loop while CPU is blocked - GBA: Allow pausing event loop while CPU is blocked - Debugger: Keep track of global cycle count - FFmpeg: Add looping option for GIF/APNG - - FFmpeg: Use range coder for FFV1 to reduce output size - Qt: Renderer can be changed while a game is running - Qt: Add hex index to palette view - Qt: Add transformation matrix info to sprite view + - Util: Reset vector size on deinit + +0.8.3: (2020-08-03) +Emulation fixes: + - ARM: Fix LDM^ writeback to user-mode register + - ARM: Fix LDM^ {pc} differences (fixes mgba.io/i/1698) + - ARM: Fix edge case with Thumb SBC flags (fixes mgba.io/i/1818) + - GB MBC: Fix MBC1 RAM enable bit selection + - GB MBC: Fix MBC2 bit selection + - GB Memory: Fix OAM DMA from top 8 kB + - GB Video: Always initialize palette + - GBA Savedata: Fix potential corruption when loading a 1Mbit flash save + - GBA Video: Fix invalid read in mode 4 mosaic + - GBA Video: Fix color of disabled screen + - SM83: Fix flags on little endian PowerPC +Other fixes: + - 3DS: Fix garbage on borders of scaled screens + - All: Correct format strings for some numbers on Windows (fixes mgba.io/i/1794) + - All: Correct more format strings on Windows (fixes mgba.io/i/1817) + - ARM: Fix decoder detection of branches with ALU and LDR instrctions + - CMake: Fix build with libzip 1.7 + - CMake: Add missing dllexports.h file to dev installation + - GB Core: Fix extracting SRAM when none is present + - GBA: Fix leak if attempting to load BIOS multiple times + - GBA Memory: Fix instability on Wii when using AGBPrint + - GBA Savedata: Fix extracting save when not yet configured in-game + - Qt: Fix file handle leak on opening an invalid ROM + - Qt: Fix Italian RTC translation (fixes mgba.io/i/1798) + - Qt: Add missing option for Wisdom Tree in overrides list + - Qt: Fix stability regression on AMD drivers (fixes mgba.io/i/1791) + - Util: Fix crash if PNG header fails to write + - Vita: Fix flickering when using frameskip (fixes mgba.io/i/1822) + - Wii: Fix pixelated filtering on interframe blending (fixes mgba.io/i/1830) +Misc: + - FFmpeg: Use range coder for FFV1 to reduce output size - Qt: Add per-page scrolling to memory view (fixes mgba.io/i/1795) - Qt: Add setting to display ROM filename in title (closes mgba.io/i/1784) - - Util: Reset vector size on deinit 0.8.2: (2020-06-14) Emulation fixes:
M CMakeLists.txtCMakeLists.txt

@@ -71,7 +71,7 @@ set(BUILD_SHARED ON CACHE BOOL "Build a shared library")

set(SKIP_LIBRARY OFF CACHE BOOL "Skip building the library (useful for only building libretro or OpenEmu cores)") set(BUILD_GL ON CACHE BOOL "Build with OpenGL") set(BUILD_GLES2 ON CACHE BOOL "Build with OpenGL|ES 2") - set(BUILD_GLES3 OFF CACHE BOOL "Build with OpenGL|ES 3") + set(BUILD_GLES3 ON CACHE BOOL "Build with OpenGL|ES 3") set(USE_EPOXY ON CACHE STRING "Build with libepoxy") set(DISABLE_DEPS OFF CACHE BOOL "Build without dependencies") set(DISTBUILD OFF CACHE BOOL "Build distribution packages")

@@ -266,10 +266,6 @@ set(USE_DISCORD_RPC OFF)

set(USE_LIBZIP OFF CACHE BOOL "") endif() -if(DEFINED 3DS) - add_definitions(-DFIXED_ROM_BUFFER) -endif() - if(DEFINED SWITCH) set(BUILD_GLES3 ON CACHE BOOL "Build with OpenGL|ES 3" FORCE) endif()

@@ -285,10 +281,6 @@ endif()

if(WII) add_definitions(-U__STRICT_ANSI__) -endif() - -if(3DS OR WII) - add_definitions(-D_GNU_SOURCE) endif() include(CheckCCompilerFlag)

@@ -440,6 +432,7 @@ endif()

else() find_feature(USE_EDITLINE "libedit") endif() + if(BUILD_GL) find_package(OpenGL QUIET) if(NOT OPENGL_FOUND)

@@ -449,18 +442,26 @@ endif()

if(NOT BUILD_GL) set(OPENGL_LIBRARY "" CACHE PATH "" FORCE) endif() -if(BUILD_GLES2 AND NOT BUILD_RASPI AND NOT CMAKE_SYSTEM_NAME MATCHES "^(Windows|Darwin|Linux|.*BSD|DragonFly|Haiku)$") + +if(BUILD_GLES2 AND NOT BUILD_RASPI) find_path(OPENGLES2_INCLUDE_DIR NAMES GLES2/gl2.h) find_library(OPENGLES2_LIBRARY NAMES GLESv2 GLESv2_CM) if(NOT OPENGLES2_INCLUDE_DIR OR NOT OPENGLES2_LIBRARY) set(BUILD_GLES2 OFF CACHE BOOL "OpenGL|ES 2 not found" FORCE) endif() endif() -if(NOT BUILD_GLES2) - set(OPENGLES2_LIBRARY "" CACHE PATH "" FORCE) +if(BUILD_GLES3) + find_path(OPENGLES3_INCLUDE_DIR NAMES GLES3/gl3.h) + find_library(OPENGLES3_LIBRARY NAMES GLESv3 GLESv2) + if(NOT OPENGLES3_INCLUDE_DIR OR NOT OPENGLES3_LIBRARY) + set(BUILD_GLES3 OFF CACHE BOOL "OpenGL|ES 3 not found" FORCE) + endif() endif() + if(BUILD_GL) - list(APPEND OS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gl.c) + list(APPEND OS_SRC + ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gl.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gles2.c) list(APPEND DEPENDENCY_LIB ${OPENGL_LIBRARY}) include_directories(${OPENGL_INCLUDE_DIR}) endif()

@@ -470,11 +471,9 @@ list(APPEND DEPENDENCY_LIB ${OPENGLES2_LIBRARY})

include_directories(${OPENGLES2_INCLUDE_DIR}) endif() if(BUILD_GLES3) - find_path(OPENGLES3_INCLUDE_DIR NAMES GLES3/gl3.h) - find_library(OPENGLES3_LIBRARY NAMES GLESv3 GLESv2) - if(NOT OPENGLES3_INCLUDE_DIR OR NOT OPENGLES3_LIBRARY) - set(BUILD_GLES3 OFF CACHE BOOL "OpenGL|ES 3 not found" FORCE) - endif() + list(APPEND OS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gles2.c) + list(APPEND DEPENDENCY_LIB ${OPENGLES3_LIBRARY}) + include_directories(${OPENGLES3_INCLUDE_DIR}) endif() if(DISABLE_DEPS)

@@ -726,7 +725,7 @@ endif()

if(USE_EPOXY) list(APPEND OS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gl.c ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gles2.c) - add_definitions(-DBUILD_GL -DBUILD_GLES2) + add_definitions(-DBUILD_GL -DBUILD_GLES2 -DBUILD_GLES3) list(APPEND FEATURES EPOXY) include_directories(AFTER ${EPOXY_INCLUDE_DIRS}) link_directories(${EPOXY_LIBRARY_DIRS})

@@ -933,7 +932,7 @@ endif()

endif() if(BUILD_GL) - add_definitions(-DBUILD_GL) + add_definitions(-DBUILD_GL -DBUILD_GLES2 -DBUILD_GLES3) endif() if(BUILD_GLES2)
M README.mdREADME.md

@@ -80,13 +80,14 @@ Supported Platforms

------------------- - Windows Vista or newer -- OS X 10.7 (Lion)[<sup>[4]</sup>](#osxver) or newer +- OS X 10.8 (Mountain Lion)[<sup>[4]</sup>](#osxver) or newer - Linux - FreeBSD The following platforms are supported for everything except DS: - Nintendo 3DS +- Nintendo Switch - Wii - PlayStation Vita

@@ -295,7 +296,7 @@ <a name="dscaveat">[2]</a> Many feature are still missing on the DS, including savestates, cheats, rumble, HLE BIOS, and more.

<a name="flashdetect">[3]</a> Flash memory size detection does not work in some cases. These can be configured at runtime, but filing a bug is recommended if such a case is encountered. -<a name="osxver">[3]</a> 10.7 is only needed for the Qt port. The SDL port is known to work on 10.5, and may work on older. +<a name="osxver">[4]</a> 10.8 is only needed for the Qt port. It may be possible to build or running the Qt port on 10.7 or older, but this is not officially supported. The SDL port is known to work on 10.5, and may work on older. [downloads]: http://mgba.io/downloads.html [source]: https://github.com/mgba-emu/mgba/
M include/mgba-util/platform/3ds/threading.hinclude/mgba-util/platform/3ds/threading.h

@@ -15,11 +15,7 @@ #define THREAD_ENTRY void

typedef ThreadFunc ThreadEntry; typedef LightLock Mutex; -typedef struct { - Mutex mutex; - Handle semaphore; - u32 waiting; -} Condition; +typedef CondVar Condition; static inline int MutexInit(Mutex* mutex) { LightLock_Init(mutex);

@@ -46,47 +42,26 @@ return 0;

} static inline int ConditionInit(Condition* cond) { - Result res = MutexInit(&cond->mutex); - if (res) { - return res; - } - res = svcCreateSemaphore(&cond->semaphore, 0, 1); - cond->waiting = 0; - return res; + CondVar_Init(cond); + return 0; } static inline int ConditionDeinit(Condition* cond) { - return svcCloseHandle(cond->semaphore); + UNUSED(cond); + return 0; } static inline int ConditionWait(Condition* cond, Mutex* mutex) { - MutexLock(&cond->mutex); - ++cond->waiting; - MutexUnlock(mutex); - MutexUnlock(&cond->mutex); - svcWaitSynchronization(cond->semaphore, U64_MAX); - MutexLock(mutex); + CondVar_Wait(cond, mutex); return 0; } static inline int ConditionWaitTimed(Condition* cond, Mutex* mutex, int32_t timeoutMs) { - MutexLock(&cond->mutex); - ++cond->waiting; - MutexUnlock(mutex); - MutexUnlock(&cond->mutex); - svcWaitSynchronization(cond->semaphore, timeoutMs * 10000000LL); - MutexLock(mutex); - return 0; + return CondVar_WaitTimeout(cond, mutex, timeoutMs * 10000000LL); } static inline int ConditionWake(Condition* cond) { - MutexLock(&cond->mutex); - if (cond->waiting) { - --cond->waiting; - s32 count = 0; - svcReleaseSemaphore(&count, cond->semaphore, 1); - } - MutexUnlock(&cond->mutex); + CondVar_Signal(cond); return 0; }
M include/mgba-util/table.hinclude/mgba-util/table.h

@@ -35,12 +35,19 @@ void HashTableInit(struct Table* table, size_t initialSize, void (deinitializer(void*)));

void HashTableDeinit(struct Table* table); void* HashTableLookup(const struct Table*, const char* key); +void* HashTableLookupBinary(const struct Table*, const void* key, size_t keylen); void HashTableInsert(struct Table*, const char* key, void* value); +void HashTableInsertBinary(struct Table*, const void* key, size_t keylen, void* value); void HashTableRemove(struct Table*, const char* key); +void HashTableRemoveBinary(struct Table*, const void* key, size_t keylen); void HashTableClear(struct Table*); void HashTableEnumerate(const struct Table*, void (handler(const char* key, void* value, void* user)), void* user); +const char* HashTableSearch(const struct Table* table, bool (predicate(const char* key, const void* value, const void* user)), const void* user); +const char* HashTableSearchPointer(const struct Table* table, const void* value); +const char* HashTableSearchData(const struct Table* table, const void* value, size_t bytes); +const char* HashTableSearchString(const struct Table* table, const char* value); size_t HashTableSize(const struct Table*); CXX_GUARD_END
M include/mgba/internal/arm/decoder-inlines.hinclude/mgba/internal/arm/decoder-inlines.h

@@ -22,4 +22,16 @@ info->sInstructionCycles = 0; \

info->nInstructionCycles = 1; \ info->nDataCycles = 1; +static inline bool ARMInstructionIsBranch(enum ARMMnemonic mnemonic) { + switch (mnemonic) { + case ARM_MN_B: + case ARM_MN_BL: + case ARM_MN_BX: + // TODO: case: ARM_MN_BLX: + return true; + default: + return false; + } +} + #endif
M include/mgba/internal/arm/isa-inlines.hinclude/mgba/internal/arm/isa-inlines.h

@@ -107,4 +107,39 @@ static inline uint32_t _ARMPCAddress(struct ARMCore* cpu) {

return cpu->gprs[ARM_PC] - _ARMInstructionLength(cpu) * 2; } +static inline bool ARMTestCondition(struct ARMCore* cpu, unsigned condition) { + switch (condition) { + case 0x0: + return ARM_COND_EQ; + case 0x1: + return ARM_COND_NE; + case 0x2: + return ARM_COND_CS; + case 0x3: + return ARM_COND_CC; + case 0x4: + return ARM_COND_MI; + case 0x5: + return ARM_COND_PL; + case 0x6: + return ARM_COND_VS; + case 0x7: + return ARM_COND_VC; + case 0x8: + return ARM_COND_HI; + case 0x9: + return ARM_COND_LS; + case 0xA: + return ARM_COND_GE; + case 0xB: + return ARM_COND_LT; + case 0xC: + return ARM_COND_GT; + case 0xD: + return ARM_COND_LE; + default: + return true; + } +} + #endif
M include/mgba/internal/debugger/cli-debugger.hinclude/mgba/internal/debugger/cli-debugger.h

@@ -95,6 +95,11 @@ void CLIDebuggerAttachBackend(struct CLIDebugger*, struct CLIDebuggerBackend*);

bool CLIDebuggerTabComplete(struct CLIDebugger*, const char* token, bool initial, size_t len); +bool CLIDebuggerRunCommand(struct CLIDebugger* debugger, const char* line, size_t count); +#ifdef ENABLE_SCRIPTING +void CLIDebuggerScriptEngineInstall(struct mScriptBridge* sb); +#endif + CXX_GUARD_END #endif
M include/mgba/internal/debugger/stack-trace.hinclude/mgba/internal/debugger/stack-trace.h

@@ -14,6 +14,8 @@ #include <mgba/core/cpu.h>

#include <mgba/core/log.h> #include <mgba-util/vector.h> +struct mDebuggerSymbols; + enum mStackTraceMode { STACK_TRACE_DISABLED = 0, STACK_TRACE_ENABLED = 1,

@@ -23,8 +25,11 @@ STACK_TRACE_BREAK_ON_BOTH = STACK_TRACE_BREAK_ON_RETURN | STACK_TRACE_BREAK_ON_CALL

}; struct mStackFrame { + int callSegment; uint32_t callAddress; + int entrySegment; uint32_t entryAddress; + int frameBaseSegment; uint32_t frameBaseAddress; void* regs; bool finished;

@@ -47,8 +52,9 @@

void mStackTraceClear(struct mStackTrace* stack); size_t mStackTraceGetDepth(struct mStackTrace* stack); struct mStackFrame* mStackTracePush(struct mStackTrace* stack, uint32_t pc, uint32_t destAddress, uint32_t sp, void* regs); +struct mStackFrame* mStackTracePushSegmented(struct mStackTrace* stack, int pcSegment, uint32_t pc, int destSegment, uint32_t destAddress, int spSegment, uint32_t sp, void* regs); struct mStackFrame* mStackTraceGetFrame(struct mStackTrace* stack, uint32_t frame); -void mStackTraceFormatFrame(struct mStackTrace* stack, uint32_t frame, char* out, size_t* length); +void mStackTraceFormatFrame(struct mStackTrace* stack, struct mDebuggerSymbols* st, uint32_t frame, char* out, size_t* length); void mStackTracePop(struct mStackTrace* stack); CXX_GUARD_END
M include/mgba/internal/debugger/symbols.hinclude/mgba/internal/debugger/symbols.h

@@ -16,6 +16,7 @@ struct mDebuggerSymbols* mDebuggerSymbolTableCreate(void);

void mDebuggerSymbolTableDestroy(struct mDebuggerSymbols*); bool mDebuggerSymbolLookup(const struct mDebuggerSymbols*, const char* name, int32_t* value, int* segment); +const char* mDebuggerSymbolReverseLookup(const struct mDebuggerSymbols*, int32_t value, int segment); void mDebuggerSymbolAdd(struct mDebuggerSymbols*, const char* name, int32_t value, int segment); void mDebuggerSymbolRemove(struct mDebuggerSymbols*, const char* name);
M include/mgba/internal/gb/video.hinclude/mgba/internal/gb/video.h

@@ -10,8 +10,11 @@ #include <mgba-util/common.h>

CXX_GUARD_START +#include <mgba/core/log.h> #include <mgba/core/timing.h> #include <mgba/gb/interface.h> + +mLOG_DECLARE_CATEGORY(GB_VIDEO); enum { GB_VIDEO_HORIZONTAL_PIXELS = 160,

@@ -160,7 +163,10 @@

void GBVideoInit(struct GBVideo* video); void GBVideoReset(struct GBVideo* video); void GBVideoDeinit(struct GBVideo* video); + +void GBVideoDummyRendererCreate(struct GBVideoRenderer*); void GBVideoAssociateRenderer(struct GBVideo* video, struct GBVideoRenderer* renderer); + void GBVideoSkipBIOS(struct GBVideo* video); void GBVideoProcessDots(struct GBVideo* video, uint32_t cyclesLate);
M include/mgba/internal/gba/renderers/gl.hinclude/mgba/internal/gba/renderers/gl.h

@@ -16,7 +16,7 @@ #include <mgba/internal/gba/io.h>

#include <mgba/internal/gba/renderers/common.h> #include <mgba/internal/gba/video.h> -#if defined(BUILD_GLES2) || defined(BUILD_GLES3) +#ifdef BUILD_GLES3 #ifdef USE_EPOXY #include <epoxy/gl.h>
M include/mgba/internal/gba/video.hinclude/mgba/internal/gba/video.h

@@ -226,6 +226,8 @@

void GBAVideoInit(struct GBAVideo* video); void GBAVideoReset(struct GBAVideo* video); void GBAVideoDeinit(struct GBAVideo* video); + +void GBAVideoDummyRendererCreate(struct GBAVideoRenderer*); void GBAVideoAssociateRenderer(struct GBAVideo* video, struct GBAVideoRenderer* renderer); void GBAVideoWriteDISPSTAT(struct GBAVideo* video, uint16_t value);
M src/arm/debugger/debugger.csrc/arm/debugger/debugger.c

@@ -8,6 +8,7 @@

#include <mgba/core/core.h> #include <mgba/internal/arm/arm.h> #include <mgba/internal/arm/decoder.h> +#include <mgba/internal/arm/decoder-inlines.h> #include <mgba/internal/arm/isa-inlines.h> #include <mgba/internal/arm/debugger/memory-debugger.h> #include <mgba/internal/debugger/parser.h>

@@ -15,15 +16,57 @@ #include <mgba/internal/debugger/stack-trace.h>

#include <mgba-util/math.h> DEFINE_VECTOR(ARMDebugBreakpointList, struct ARMDebugBreakpoint); + +static bool ARMDecodeCombined(struct ARMCore* cpu, struct ARMInstructionInfo* info) { + if (cpu->executionMode == MODE_ARM) { + ARMDecodeARM(cpu->prefetch[0], info); + return true; + } else { + struct ARMInstructionInfo info2; + ARMDecodeThumb(cpu->prefetch[0], info); + ARMDecodeThumb(cpu->prefetch[1], &info2); + return ARMDecodeThumbCombine(info, &info2, info); + } +} static bool ARMDebuggerUpdateStackTraceInternal(struct mDebuggerPlatform* d, uint32_t pc) { struct ARMDebugger* debugger = (struct ARMDebugger*) d; struct ARMCore* cpu = debugger->cpu; struct ARMInstructionInfo info; - uint32_t instruction = cpu->prefetch[0]; struct mStackTrace* stack = &d->p->stackTrace; + + struct mStackFrame* frame = mStackTraceGetFrame(stack, 0); + if (frame && frame->frameBaseAddress < (uint32_t) cpu->gprs[ARM_SP]) { + // The stack frame has been popped off the stack. This means the function + // has been returned from, or that the stack pointer has been otherwise + // manipulated. Either way, the function is done executing. + bool shouldBreak = debugger->stackTraceMode & STACK_TRACE_BREAK_ON_RETURN; + do { + shouldBreak = shouldBreak || frame->breakWhenFinished; + mStackTracePop(stack); + frame = mStackTraceGetFrame(stack, 0); + } while (frame && frame->frameBaseAddress < (uint32_t) cpu->gprs[ARM_SP]); + if (shouldBreak) { + struct mDebuggerEntryInfo debuggerInfo = { + .address = pc, + .type.st.traceType = STACK_TRACE_BREAK_ON_RETURN, + .pointId = 0 + }; + mDebuggerEnter(d->p, DEBUGGER_ENTER_STACK, &debuggerInfo); + return true; + } else { + return false; + } + } + bool interrupt = false; - ARMDecodeARM(instruction, &info); + bool isWideInstruction = ARMDecodeCombined(cpu, &info); + if (!isWideInstruction && info.mnemonic == ARM_MN_BL) { + return false; + } + if (!ARMTestCondition(cpu, info.condition)) { + return false; + } if (_ARMModeHasSPSR(cpu->cpsr.priv)) { struct mStackFrame* irqFrame = mStackTraceGetFrame(stack, 0);

@@ -40,15 +83,9 @@ if (info.branchType == ARM_BRANCH_NONE && !interrupt) {

return false; } - struct mStackFrame* frame = mStackTraceGetFrame(stack, 0); - bool isCall = (info.branchType & ARM_BRANCH_LINKED); + bool isCall = interrupt || (info.branchType & ARM_BRANCH_LINKED); uint32_t destAddress; - if (frame && frame->finished) { - mStackTracePop(stack); - frame = NULL; - } - if (interrupt && info.branchType == ARM_BRANCH_NONE) { // The stack frame was already pushed up above, so there's no // action necessary here, but we still want to check for a

@@ -56,6 +93,7 @@ // breakpoint down below.

// // The first instruction could possibly be a call, which would // need ANOTHER stack frame, so only skip if it's not. + destAddress = pc; } else if (info.operandFormat & ARM_OPERAND_MEMORY_1) { // This is most likely ldmia ..., {..., pc}, which is a function return. // To find which stack slot holds the return address, count the number of set bits.

@@ -68,10 +106,39 @@ return false;

} destAddress = info.op1.immediate + cpu->gprs[ARM_PC]; } else if (info.operandFormat & ARM_OPERAND_REGISTER_1) { - if (!isCall && info.op1.reg != ARM_LR && !(_ARMModeHasSPSR(cpu->cpsr.priv) && info.op1.reg == ARM_PC)) { - return false; + if (isCall) { + destAddress = cpu->gprs[info.op1.reg]; + } else { + bool isExceptionReturn = _ARMModeHasSPSR(cpu->cpsr.priv) && info.affectsCPSR && info.op1.reg == ARM_PC; + bool isMovPcLr = (info.operandFormat & ARM_OPERAND_REGISTER_2) && info.op1.reg == ARM_PC && info.op2.reg == ARM_LR; + bool isBranch = ARMInstructionIsBranch(info.mnemonic); + int reg = (isBranch ? info.op1.reg : info.op2.reg); + destAddress = cpu->gprs[reg]; + if (isBranch || (info.op1.reg == ARM_PC && !isMovPcLr)) { + // ARMv4 doesn't have the BLX opcode, so it uses an assignment to LR before a BX for that purpose. + struct ARMInstructionInfo prevInfo; + if (cpu->executionMode == MODE_ARM) { + ARMDecodeARM(cpu->memory.load32(cpu, pc - 4, NULL), &prevInfo); + } else { + ARMDecodeThumb(cpu->memory.load16(cpu, pc - 2, NULL), &prevInfo); + } + if ((prevInfo.operandFormat & (ARM_OPERAND_REGISTER_1 | ARM_OPERAND_AFFECTED_1)) == (ARM_OPERAND_REGISTER_1 | ARM_OPERAND_AFFECTED_1) && prevInfo.op1.reg == ARM_LR) { + isCall = true; + } else if ((isBranch ? info.op1.reg : info.op2.reg) == ARM_LR) { + isBranch = true; + } else if (frame && frame->frameBaseAddress == (uint32_t) cpu->gprs[ARM_SP]) { + // A branch to something that isn't LR isn't a standard function return, but it might potentially + // be a nonstandard one. As a heuristic, if the stack pointer and the destination address match + // where we came from, consider it to be a function return. + isBranch = (destAddress > frame->callAddress + 1 && destAddress <= frame->callAddress + 5); + } else { + isBranch = false; + } + } + if (!isCall && !isBranch && !isExceptionReturn && !isMovPcLr) { + return false; + } } - destAddress = cpu->gprs[info.op1.reg]; } else { mLOG(DEBUGGER, ERROR, "Unknown branch operand in stack trace"); return false;

@@ -82,21 +149,16 @@ destAddress = cpu->memory.load32(cpu, destAddress, NULL);

} if (isCall) { - int instructionLength = _ARMInstructionLength(debugger->cpu); + int instructionLength = isWideInstruction ? WORD_SIZE_ARM : WORD_SIZE_THUMB; frame = mStackTracePush(stack, pc, destAddress + instructionLength, cpu->gprs[ARM_SP], &cpu->regs); if (!(debugger->stackTraceMode & STACK_TRACE_BREAK_ON_CALL)) { return false; } } else { - frame = mStackTraceGetFrame(stack, 0); - if (!frame) { - return false; - } - if (!frame->breakWhenFinished && !(debugger->stackTraceMode & STACK_TRACE_BREAK_ON_RETURN)) { - mStackTracePop(stack); + mStackTracePop(stack); + if (!(debugger->stackTraceMode & STACK_TRACE_BREAK_ON_RETURN)) { return false; } - frame->finished = true; } struct mDebuggerEntryInfo debuggerInfo = { .address = pc,

@@ -403,21 +465,18 @@

char disassembly[64]; struct ARMInstructionInfo info; + bool isWideInstruction = ARMDecodeCombined(cpu, &info); if (cpu->executionMode == MODE_ARM) { uint32_t instruction = cpu->prefetch[0]; sprintf(disassembly, "%08X: ", instruction); - ARMDecodeARM(instruction, &info); ARMDisassemble(&info, cpu->gprs[ARM_PC], disassembly + strlen("00000000: "), sizeof(disassembly) - strlen("00000000: ")); } else { - struct ARMInstructionInfo info2; - struct ARMInstructionInfo combined; uint16_t instruction = cpu->prefetch[0]; - uint16_t instruction2 = cpu->prefetch[1]; ARMDecodeThumb(instruction, &info); - ARMDecodeThumb(instruction2, &info2); - if (ARMDecodeThumbCombine(&info, &info2, &combined)) { + if (isWideInstruction) { + uint16_t instruction2 = cpu->prefetch[1]; sprintf(disassembly, "%04X%04X: ", instruction, instruction2); - ARMDisassemble(&combined, cpu->gprs[ARM_PC], disassembly + strlen("00000000: "), sizeof(disassembly) - strlen("00000000: ")); + ARMDisassemble(&info, cpu->gprs[ARM_PC], disassembly + strlen("00000000: "), sizeof(disassembly) - strlen("00000000: ")); } else { sprintf(disassembly, " %04X: ", instruction); ARMDisassemble(&info, cpu->gprs[ARM_PC], disassembly + strlen("00000000: "), sizeof(disassembly) - strlen("00000000: "));
M src/arm/isa-arm.csrc/arm/isa-arm.c

@@ -513,12 +513,12 @@

#define ARM_MS_POST_store ARMSetPrivilegeMode(cpu, privilegeMode); #define ARM_MS_POST_load \ - if ((rs & 0x8000) && _ARMModeHasSPSR(cpu->cpsr.priv)) { \ + if (!(rs & 0x8000)) { \ + ARMSetPrivilegeMode(cpu, privilegeMode); \ + } else if (_ARMModeHasSPSR(cpu->cpsr.priv)) { \ cpu->cpsr = cpu->spsr; \ _ARMReadCPSR(cpu); \ - } else { \ - ARMSetPrivilegeMode(cpu, privilegeMode); \ - } \ + } #define DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME, LS, WRITEBACK, S_PRE, S_POST, DIRECTION, POST_BODY) \ DEFINE_INSTRUCTION_ARM(NAME, \
M src/debugger/CMakeLists.txtsrc/debugger/CMakeLists.txt

@@ -6,6 +6,10 @@ parser.c

symbols.c stack-trace.c) +if(ENABLE_SCRIPTING) + list(APPEND SOURCE_FILES cli-debugger-scripting.c) +endif() + set(TEST_FILES test/lexer.c test/parser.c)
A src/debugger/cli-debugger-scripting.c

@@ -0,0 +1,137 @@

+/* Copyright (c) 2013-2020 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include <mgba/core/scripting.h> +#include <mgba-util/string.h> +#include <mgba-util/vfs.h> + +#include <mgba/debugger/debugger.h> +#include <mgba/internal/debugger/cli-debugger.h> + +static const char* CLIScriptEngineName(struct mScriptEngine*); +static bool CLIScriptEngineInit(struct mScriptEngine*, struct mScriptBridge*); +static void CLIScriptEngineDeinit(struct mScriptEngine*); +static bool CLIScriptEngineIsScript(struct mScriptEngine*, const char* name, struct VFile* vf); +static bool CLIScriptEngineLoadScript(struct mScriptEngine*, const char* name, struct VFile* vf); +static void CLIScriptEngineRun(struct mScriptEngine*); +static bool CLIScriptEngineLookupSymbol(struct mScriptEngine*, const char* name, int32_t* out); +static void CLIScriptDebuggerEntered(struct mScriptEngine*, enum mDebuggerEntryReason, struct mDebuggerEntryInfo*); + +struct CLIScriptStatement { + char* command; + size_t commandLen; +}; + +DECLARE_VECTOR(CLIScript, struct CLIScriptStatement); +DEFINE_VECTOR(CLIScript, struct CLIScriptStatement); + +struct CLIScriptEngine { + struct mScriptEngine d; + struct mScriptBridge* sb; + struct CLIScript script; +}; + +static void CLIScriptEngineClear(struct CLIScriptEngine* engine) { + size_t i = CLIScriptSize(&engine->script); + while (i-- > 0) { + struct CLIScriptStatement* statement = CLIScriptGetPointer(&engine->script, i); + free(statement->command); + } + CLIScriptClear(&engine->script); +} + +struct CLIScriptEngine* CLICreateScriptEngine(void) { + struct CLIScriptEngine* engine = malloc(sizeof(*engine)); + engine->d.name = CLIScriptEngineName; + engine->d.init = CLIScriptEngineInit; + engine->d.deinit = CLIScriptEngineDeinit; + engine->d.isScript = CLIScriptEngineIsScript; + engine->d.loadScript = CLIScriptEngineLoadScript; + engine->d.run = CLIScriptEngineRun; + engine->d.lookupSymbol = CLIScriptEngineLookupSymbol; + engine->d.debuggerEntered = CLIScriptDebuggerEntered; + engine->sb = NULL; + return engine; +} + +void CLIDebuggerScriptEngineInstall(struct mScriptBridge* sb) { + struct CLIScriptEngine* se = CLICreateScriptEngine(); + mScriptBridgeInstallEngine(sb, &se->d); +} + +const char* CLIScriptEngineName(struct mScriptEngine* se) { + UNUSED(se); + return "cli-debugger"; +} + +bool CLIScriptEngineInit(struct mScriptEngine* se, struct mScriptBridge* sb) { + struct CLIScriptEngine* engine = (struct CLIScriptEngine*) se; + engine->sb = sb; + CLIScriptInit(&engine->script, 0); + return true; +} + +void CLIScriptEngineDeinit(struct mScriptEngine* se) { + struct CLIScriptEngine* engine = (struct CLIScriptEngine*) se; + CLIScriptEngineClear(engine); + CLIScriptDeinit(&engine->script); + free(se); +} + +bool CLIScriptEngineIsScript(struct mScriptEngine* se, const char* name, struct VFile* vf) { + UNUSED(se); + UNUSED(vf); + return endswith(name, ".mrc"); +} + +bool CLIScriptEngineLoadScript(struct mScriptEngine* se, const char* name, struct VFile* vf) { + UNUSED(name); + struct CLIScriptEngine* engine = (struct CLIScriptEngine*) se; + char buffer[256]; + ssize_t size; + CLIScriptEngineClear(engine); + struct CLIScriptStatement* statement; + while ((size = vf->readline(vf, buffer, sizeof(buffer))) > 0) { + if (buffer[size - 1] == '\n') { + --size; + } + statement = CLIScriptAppend(&engine->script); + statement->command = strndup(buffer, size); + statement->commandLen = size; + } + return true; +} + +void CLIScriptEngineRun(struct mScriptEngine* se) { + struct CLIScriptEngine* engine = (struct CLIScriptEngine*) se; + struct CLIDebugger* debugger = (struct CLIDebugger*) mScriptBridgeGetDebugger(engine->sb); + struct CLIScriptStatement* statement; + size_t statementCount = CLIScriptSize(&engine->script); + size_t i; + for (i = 0; i < statementCount; i++) { + statement = CLIScriptGetPointer(&engine->script, i); + CLIDebuggerRunCommand(debugger, statement->command, statement->commandLen); + } +} + +bool CLIScriptEngineLookupSymbol(struct mScriptEngine* se, const char* name, int32_t* out) { + UNUSED(se); + UNUSED(name); + UNUSED(out); + return false; +} + +void CLIScriptDebuggerEntered(struct mScriptEngine* se, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info) { + UNUSED(reason); + UNUSED(info); + struct CLIScriptEngine* engine = (struct CLIScriptEngine*) se; + + struct mDebugger* debugger = mScriptBridgeGetDebugger(engine->sb); + if (!debugger) { + return; + } + + // TODO: CLIDebuggerEntered(reason, info); +}
M src/debugger/cli-debugger.csrc/debugger/cli-debugger.c

@@ -13,7 +13,7 @@ #include <mgba/internal/debugger/parser.h>

#include <mgba-util/string.h> #include <mgba-util/vfs.h> -#if ENABLE_SCRIPTING +#ifdef ENABLE_SCRIPTING #include <mgba/core/scripting.h> #endif

@@ -72,6 +72,8 @@ #endif

static void _backtrace(struct CLIDebugger*, struct CLIDebugVector*); static void _finish(struct CLIDebugger*, struct CLIDebugVector*); static void _setStackTraceMode(struct CLIDebugger*, struct CLIDebugVector*); +static void _setSymbol(struct CLIDebugger*, struct CLIDebugVector*); +static void _findSymbol(struct CLIDebugger*, struct CLIDebugVector*); static struct CLIDebuggerCommandSummary _debuggerCommands[] = { { "backtrace", _backtrace, "i", "Print backtrace of all or specified frames" },

@@ -92,8 +94,10 @@ { "reset", _reset, "", "Reset the emulation" },

{ "r/1", _readByte, "I", "Read a byte from a specified offset" }, { "r/2", _readHalfword, "I", "Read a halfword from a specified offset" }, { "r/4", _readWord, "I", "Read a word from a specified offset" }, - { "stack", _setStackTraceMode, "S", "Changes the stack tracing mode" }, + { "set", _setSymbol, "SI", "Assign a symbol to an address" }, + { "stack", _setStackTraceMode, "S", "Change the stack tracing mode" }, { "status", _printStatus, "", "Print the current status" }, + { "symbol", _findSymbol, "I", "Find the symbol name for an address" }, { "trace", _trace, "Is", "Trace a number of instructions" }, { "w/1", _writeByte, "II", "Write a byte at a specified offset" }, { "w/2", _writeHalfword, "II", "Write a halfword at a specified offset" },

@@ -133,6 +137,7 @@ { "p/t", "print/t" },

{ "p/x", "print/x" }, { "q", "quit" }, { "w", "watch" }, + { ".", "source" }, { 0, 0 } };

@@ -940,7 +945,7 @@ }

return -1; } -static bool _parse(struct CLIDebugger* debugger, const char* line, size_t count) { +bool CLIDebuggerRunCommand(struct CLIDebugger* debugger, const char* line, size_t count) { const char* firstSpace = strchr(line, ' '); size_t cmdLength; if (firstSpace) {

@@ -980,10 +985,10 @@ }

if (line[0] == '\n') { line = cliDebugger->backend->historyLast(cliDebugger->backend, &len); if (line && len) { - _parse(cliDebugger, line, len); + CLIDebuggerRunCommand(cliDebugger, line, len); } } else { - _parse(cliDebugger, line, len); + CLIDebuggerRunCommand(cliDebugger, line, len); cliDebugger->backend->historyAppend(cliDebugger->backend, line); } }

@@ -1178,10 +1183,11 @@ if (dv && dv->type == CLIDV_INT_TYPE && dv->intValue < frames) {

frames = dv->intValue; } ssize_t i; + struct mDebuggerSymbols* symbolTable = debugger->d.core->symbolTable; for (i = 0; i < frames; ++i) { char trace[1024]; size_t traceSize = sizeof(trace) - 2; - mStackTraceFormatFrame(stack, i, trace, &traceSize); + mStackTraceFormatFrame(stack, symbolTable, i, trace, &traceSize); debugger->backend->printf(debugger->backend, "%s", trace); } }

@@ -1232,3 +1238,46 @@ } else {

debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS); } } + +static void _setSymbol(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { + struct mDebuggerSymbols* symbolTable = debugger->d.core->symbolTable; + if (!symbolTable) { + debugger->backend->printf(debugger->backend, "No symbol table available.\n"); + return; + } + if (!dv || !dv->next) { + debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS); + return; + } + if (dv->type != CLIDV_CHAR_TYPE || dv->next->type != CLIDV_INT_TYPE) { + debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS); + return; + } + mDebuggerSymbolAdd(symbolTable, dv->charValue, dv->next->intValue, dv->next->segmentValue); +} + +static void _findSymbol(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { + struct mDebuggerSymbols* symbolTable = debugger->d.core->symbolTable; + if (!symbolTable) { + debugger->backend->printf(debugger->backend, "No symbol table available.\n"); + return; + } + if (!dv) { + debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS); + return; + } + if (dv->type != CLIDV_INT_TYPE) { + debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS); + return; + } + const char* name = mDebuggerSymbolReverseLookup(symbolTable, dv->intValue, dv->segmentValue); + if (name) { + if (dv->segmentValue >= 0) { + debugger->backend->printf(debugger->backend, " 0x%02X:%08X = %s\n", dv->segmentValue, dv->intValue, name); + } else { + debugger->backend->printf(debugger->backend, " 0x%08X = %s\n", dv->intValue, name); + } + } else { + debugger->backend->printf(debugger->backend, "Not found.\n"); + } +}
M src/debugger/debugger.csrc/debugger/debugger.c

@@ -14,7 +14,7 @@ #ifdef USE_GDB_STUB

#include <mgba/internal/debugger/gdb-stub.h> #endif -#if ENABLE_SCRIPTING +#ifdef ENABLE_SCRIPTING #include <mgba/core/scripting.h> #endif
M src/debugger/gdb-stub.csrc/debugger/gdb-stub.c

@@ -82,6 +82,7 @@ case DEBUGGER_ENTER_ILLEGAL_OP:

snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGILL); break; case DEBUGGER_ENTER_ATTACHED: + case DEBUGGER_ENTER_STACK: return; } _sendMessage(stub);
M src/debugger/stack-trace.csrc/debugger/stack-trace.c

@@ -4,6 +4,7 @@ * This Source Code Form is subject to the terms of the Mozilla Public

* License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include <mgba/internal/debugger/stack-trace.h> +#include <mgba/internal/debugger/symbols.h> #include <mgba/core/core.h>

@@ -40,8 +41,11 @@ }

struct mStackFrame* mStackTracePush(struct mStackTrace* stack, uint32_t pc, uint32_t destAddress, uint32_t sp, void* regs) { struct mStackFrame* frame = mStackFramesAppend(&stack->stack); + frame->callSegment = -1; frame->callAddress = pc; + frame->entrySegment = -1; frame->entryAddress = destAddress; + frame->frameBaseSegment = -1; frame->frameBaseAddress = sp; frame->regs = malloc(stack->registersSize); frame->finished = false;

@@ -51,6 +55,14 @@ memcpy(frame->regs, regs, stack->registersSize);

return frame; } +struct mStackFrame* mStackTracePushSegmented(struct mStackTrace* stack, int pcSegment, uint32_t pc, int destSegment, uint32_t destAddress, int spSegment, uint32_t sp, void* regs) { + struct mStackFrame* frame = mStackTracePush(stack, pc, destAddress, sp, regs); + frame->callSegment = pcSegment; + frame->entrySegment = destSegment; + frame->frameBaseSegment = spSegment; + return frame; +} + struct mStackFrame* mStackTraceGetFrame(struct mStackTrace* stack, uint32_t frame) { size_t depth = mStackTraceGetDepth(stack); if (frame >= depth) {

@@ -59,20 +71,26 @@ }

return mStackFramesGetPointer(&stack->stack, depth - frame - 1); } -void mStackTraceFormatFrame(struct mStackTrace* stack, uint32_t frame, char* out, size_t* length) { +void mStackTraceFormatFrame(struct mStackTrace* stack, struct mDebuggerSymbols* st, uint32_t frame, char* out, size_t* length) { struct mStackFrame* stackFrame = mStackTraceGetFrame(stack, frame); struct mStackFrame* prevFrame = mStackTraceGetFrame(stack, frame + 1); size_t written = snprintf(out, *length, "#%d ", frame); CHECK_LENGTH(); - if (prevFrame) { - written += snprintf(out + written, *length - written, "%08X ", prevFrame->entryAddress); - CHECK_LENGTH(); - } if (!stackFrame) { - written += snprintf(out + written, *length - written, "no stack frame available)\n"); + written += snprintf(out + written, *length - written, "(no stack frame available)\n"); *length = written; return; - } else if (stack->formatRegisters) { + } + const char* functionName = mDebuggerSymbolReverseLookup(st, stackFrame->entryAddress, stackFrame->entrySegment); + if (functionName) { + written += snprintf(out + written, *length - written, "%s ", functionName); + } else if (stackFrame->entrySegment >= 0) { + written += snprintf(out + written, *length - written, "0x%02X:%08X ", stackFrame->entrySegment, stackFrame->entryAddress); + } else { + written += snprintf(out + written, *length - written, "0x%08X ", stackFrame->entryAddress); + } + CHECK_LENGTH(); + if (stack->formatRegisters) { written += snprintf(out + written, *length - written, "("); CHECK_LENGTH(); char buffer[1024];

@@ -81,12 +99,27 @@ stack->formatRegisters(stackFrame, buffer, &formattedSize);

written += snprintf(out + written, *length - written, "%s)\n ", buffer); CHECK_LENGTH(); } + if (stackFrame->callSegment >= 0) { + written += snprintf(out + written, *length - written, "at 0x%02X:%08X", stackFrame->callSegment, stackFrame->callAddress); + } else { + written += snprintf(out + written, *length - written, "at 0x%08X", stackFrame->callAddress); + } + CHECK_LENGTH(); if (prevFrame) { int32_t offset = stackFrame->callAddress - prevFrame->entryAddress; - written += snprintf(out + written, *length - written, "at %08X [%08X+%d]\n", stackFrame->callAddress, prevFrame->entryAddress, offset); - } else { - written += snprintf(out + written, *length - written, "at %08X\n", stackFrame->callAddress); + if (offset >= 0) { + functionName = mDebuggerSymbolReverseLookup(st, prevFrame->entryAddress, prevFrame->entrySegment); + if (functionName) { + written += snprintf(out + written, *length - written, " [%s+%d]", functionName, offset); + } else if (prevFrame->entrySegment >= 0) { + written += snprintf(out + written, *length - written, " [0x%02X:%08X+%d]", prevFrame->entrySegment, prevFrame->entryAddress, offset); + } else { + written += snprintf(out + written, *length - written, " [0x%08X+%d]", prevFrame->entryAddress, offset); + } + } } + CHECK_LENGTH(); + written += snprintf(out + written, *length - written, "\n"); *length = written; }
M src/debugger/symbols.csrc/debugger/symbols.c

@@ -7,6 +7,7 @@ #include <mgba/internal/debugger/symbols.h>

#include <mgba-util/string.h> #include <mgba-util/table.h> +#include <mgba-util/hash.h> #include <mgba-util/vfs.h> struct mDebuggerSymbol {

@@ -16,16 +17,19 @@ };

struct mDebuggerSymbols { struct Table names; + struct Table reverse; }; struct mDebuggerSymbols* mDebuggerSymbolTableCreate(void) { struct mDebuggerSymbols* st = malloc(sizeof(*st)); HashTableInit(&st->names, 0, free); + HashTableInit(&st->reverse, 0, free); return st; } void mDebuggerSymbolTableDestroy(struct mDebuggerSymbols* st) { HashTableDeinit(&st->names); + HashTableDeinit(&st->reverse); free(st); }

@@ -39,15 +43,25 @@ *segment = sym->segment;

return true; } +const char* mDebuggerSymbolReverseLookup(const struct mDebuggerSymbols* st, int32_t value, int segment) { + struct mDebuggerSymbol sym = { value, segment }; + return HashTableLookupBinary(&st->reverse, &sym, sizeof(sym)); +} + void mDebuggerSymbolAdd(struct mDebuggerSymbols* st, const char* name, int32_t value, int segment) { struct mDebuggerSymbol* sym = malloc(sizeof(*sym)); sym->value = value; sym->segment = segment; HashTableInsert(&st->names, name, sym); + HashTableInsertBinary(&st->reverse, sym, sizeof(*sym), strdup(name)); } void mDebuggerSymbolRemove(struct mDebuggerSymbols* st, const char* name) { - HashTableRemove(&st->names, name); + struct mDebuggerSymbol* sym = HashTableLookup(&st->names, name); + if (sym) { + HashTableRemoveBinary(&st->reverse, sym, sizeof(*sym)); + HashTableRemove(&st->names, name); + } } void mDebuggerLoadARMIPSSymbols(struct mDebuggerSymbols* st, struct VFile* vf) {
M src/feature/gui/gui-runner.csrc/feature/gui/gui-runner.c

@@ -287,6 +287,9 @@ guiLogger->vf->close(guiLogger->vf);

guiLogger->vf = NULL; } } +#ifdef GEKKO + puts(log2); +#endif } static void _updateLoading(size_t read, size_t size, void* context) {
M src/gb/core.csrc/gb/core.c

@@ -68,9 +68,12 @@

struct mVideoLogContext; struct GBCore { struct mCore d; + struct GBVideoRenderer dummyRenderer; struct GBVideoSoftwareRenderer renderer; +#ifndef MINIMAL_CORE struct GBVideoProxyRenderer proxyRenderer; struct mVideoLogContext* logContext; +#endif struct mCoreCallbacks logCallbacks; uint8_t keys; struct mCPUComponent* components[CPU_COMPONENT_MAX];

@@ -95,6 +98,9 @@ core->timing = &gb->timing;

gbcore->overrides = NULL; gbcore->debuggerPlatform = NULL; gbcore->cheatDevice = NULL; +#ifndef MINIMAL_CORE + gbcore->logContext = NULL; +#endif GBCreate(gb); memset(gbcore->components, 0, sizeof(gbcore->components));

@@ -103,8 +109,15 @@ SM83Init(cpu);

mRTCGenericSourceInit(&core->rtc, core); gb->memory.rtc = &core->rtc.d; + GBVideoDummyRendererCreate(&gbcore->dummyRenderer); + GBVideoAssociateRenderer(&gb->video, &gbcore->dummyRenderer); + GBVideoSoftwareRendererCreate(&gbcore->renderer); gbcore->renderer.outputBuffer = NULL; + +#ifndef MINIMAL_CORE + gbcore->proxyRenderer.logger = NULL; +#endif gbcore->keys = 0; gb->keySource = &gbcore->keys;
M src/gb/video.csrc/gb/video.c

@@ -16,6 +16,8 @@ #include <mgba/internal/sm83/sm83.h>

#include <mgba-util/memory.h> +mLOG_DEFINE_CATEGORY(GB_VIDEO, "GB Video", "gb.video"); + static void GBVideoDummyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model, bool borders); static void GBVideoDummyRendererDeinit(struct GBVideoRenderer* renderer); static uint8_t GBVideoDummyRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value);

@@ -38,26 +40,8 @@ static void _endMode2(struct mTiming* timing, void* context, uint32_t cyclesLate);

static void _endMode3(struct mTiming* timing, void* context, uint32_t cyclesLate); static void _updateFrameCount(struct mTiming* timing, void* context, uint32_t cyclesLate); -static struct GBVideoRenderer dummyRenderer = { - .init = GBVideoDummyRendererInit, - .deinit = GBVideoDummyRendererDeinit, - .writeVideoRegister = GBVideoDummyRendererWriteVideoRegister, - .writeSGBPacket = GBVideoDummyRendererWriteSGBPacket, - .writeVRAM = GBVideoDummyRendererWriteVRAM, - .writeOAM = GBVideoDummyRendererWriteOAM, - .writePalette = GBVideoDummyRendererWritePalette, - .drawRange = GBVideoDummyRendererDrawRange, - .finishScanline = GBVideoDummyRendererFinishScanline, - .finishFrame = GBVideoDummyRendererFinishFrame, - .enableSGBBorder = GBVideoDummyRendererEnableSGBBorder, - .getPixels = GBVideoDummyRendererGetPixels, - .putPixels = GBVideoDummyRendererPutPixels, -}; - void GBVideoInit(struct GBVideo* video) { - video->renderer = &dummyRenderer; - video->renderer->cache = NULL; - video->renderer->sgbRenderMode = 0; + video->renderer = NULL; video->vram = anonymousMemoryMap(GB_SIZE_VRAM); video->frameskip = 0;

@@ -84,12 +68,6 @@ video->dmgPalette[10] = 0x294A;

video->dmgPalette[11] = 0x0000; video->sgbBorders = true; - - video->renderer->sgbCharRam = NULL; - video->renderer->sgbMapRam = NULL; - video->renderer->sgbPalRam = NULL; - video->renderer->sgbAttributes = NULL; - video->renderer->sgbAttributeFiles = NULL; } void GBVideoReset(struct GBVideo* video) {

@@ -116,6 +94,12 @@ video->renderer->sgbAttributes = malloc(90 * 45);

memset(video->renderer->sgbAttributes, 0, 90 * 45); video->sgbCommandHeader = 0; video->sgbBufferIndex = 0; + } else { + video->renderer->sgbCharRam = NULL; + video->renderer->sgbMapRam = NULL; + video->renderer->sgbPalRam = NULL; + video->renderer->sgbAttributes = NULL; + video->renderer->sgbAttributeFiles = NULL; } video->palette[0] = video->dmgPalette[0];

@@ -130,6 +114,11 @@ video->palette[9 * 4 + 0] = video->dmgPalette[8];

video->palette[9 * 4 + 1] = video->dmgPalette[9]; video->palette[9 * 4 + 2] = video->dmgPalette[10]; video->palette[9 * 4 + 3] = video->dmgPalette[11]; + + if (!video->renderer) { + mLOG(GB_VIDEO, FATAL, "No renderer associated"); + return; + } video->renderer->deinit(video->renderer); video->renderer->init(video->renderer, video->p->model, video->sgbBorders);

@@ -173,15 +162,44 @@ video->renderer->sgbAttributes = NULL;

} } +void GBVideoDummyRendererCreate(struct GBVideoRenderer* renderer) { + static const struct GBVideoRenderer dummyRenderer = { + .init = GBVideoDummyRendererInit, + .deinit = GBVideoDummyRendererDeinit, + .writeVideoRegister = GBVideoDummyRendererWriteVideoRegister, + .writeSGBPacket = GBVideoDummyRendererWriteSGBPacket, + .writeVRAM = GBVideoDummyRendererWriteVRAM, + .writeOAM = GBVideoDummyRendererWriteOAM, + .writePalette = GBVideoDummyRendererWritePalette, + .drawRange = GBVideoDummyRendererDrawRange, + .finishScanline = GBVideoDummyRendererFinishScanline, + .finishFrame = GBVideoDummyRendererFinishFrame, + .enableSGBBorder = GBVideoDummyRendererEnableSGBBorder, + .getPixels = GBVideoDummyRendererGetPixels, + .putPixels = GBVideoDummyRendererPutPixels, + }; + memcpy(renderer, &dummyRenderer, sizeof(*renderer)); +} + void GBVideoAssociateRenderer(struct GBVideo* video, struct GBVideoRenderer* renderer) { - video->renderer->deinit(video->renderer); - renderer->cache = video->renderer->cache; - renderer->sgbRenderMode = video->renderer->sgbRenderMode; - renderer->sgbCharRam = video->renderer->sgbCharRam; - renderer->sgbMapRam = video->renderer->sgbMapRam; - renderer->sgbPalRam = video->renderer->sgbPalRam; - renderer->sgbAttributeFiles = video->renderer->sgbAttributeFiles; - renderer->sgbAttributes = video->renderer->sgbAttributes; + if (video->renderer) { + video->renderer->deinit(video->renderer); + renderer->cache = video->renderer->cache; + renderer->sgbRenderMode = video->renderer->sgbRenderMode; + renderer->sgbCharRam = video->renderer->sgbCharRam; + renderer->sgbMapRam = video->renderer->sgbMapRam; + renderer->sgbPalRam = video->renderer->sgbPalRam; + renderer->sgbAttributeFiles = video->renderer->sgbAttributeFiles; + renderer->sgbAttributes = video->renderer->sgbAttributes; + } else { + renderer->cache = NULL; + renderer->sgbRenderMode = 0; + renderer->sgbCharRam = NULL; + renderer->sgbMapRam = NULL; + renderer->sgbPalRam = NULL; + renderer->sgbAttributeFiles = NULL; + renderer->sgbAttributes = NULL; + } video->renderer = renderer; renderer->vram = video->vram; video->renderer->init(video->renderer, video->p->model, video->sgbBorders);
M src/gba/core.csrc/gba/core.c

@@ -18,7 +18,7 @@ #include <mgba/internal/gba/overrides.h>

#ifndef DISABLE_THREADING #include <mgba/feature/thread-proxy.h> #endif -#if defined(BUILD_GLES2) || defined(BUILD_GLES3) +#ifdef BUILD_GLES3 #include <mgba/internal/gba/renderers/gl.h> #endif #include <mgba/internal/gba/renderers/proxy.h>

@@ -133,8 +133,9 @@ #define CPU_COMPONENT_AUDIO_MIXER CPU_COMPONENT_MISC_1

struct GBACore { struct mCore d; + struct GBAVideoRenderer dummyRenderer; struct GBAVideoSoftwareRenderer renderer; -#if defined(BUILD_GLES2) || defined(BUILD_GLES3) +#ifdef BUILD_GLES3 struct GBAVideoGLRenderer glRenderer; #endif #ifndef MINIMAL_CORE

@@ -186,10 +187,13 @@ ARMInit(cpu);

mRTCGenericSourceInit(&core->rtc, core); gba->rtcSource = &core->rtc.d; + GBAVideoDummyRendererCreate(&gbacore->dummyRenderer); + GBAVideoAssociateRenderer(&gba->video, &gbacore->dummyRenderer); + GBAVideoSoftwareRendererCreate(&gbacore->renderer); gbacore->renderer.outputBuffer = NULL; -#if defined(BUILD_GLES2) || defined(BUILD_GLES3) +#ifdef BUILD_GLES3 GBAVideoGLRendererCreate(&gbacore->glRenderer); gbacore->glRenderer.outputTex = -1; #endif

@@ -245,7 +249,7 @@ static bool _GBACoreSupportsFeature(const struct mCore* core, enum mCoreFeature feature) {

UNUSED(core); switch (feature) { case mCORE_FEATURE_OPENGL: -#if defined(BUILD_GLES2) || defined(BUILD_GLES3) +#ifdef BUILD_GLES3 return true; #else return false;

@@ -357,7 +361,7 @@ return;

} struct GBACore* gbacore = (struct GBACore*) core; -#if defined(BUILD_GLES2) || defined(BUILD_GLES3) +#ifdef BUILD_GLES3 if (strcmp("videoScale", option) == 0) { if (config != &core->config) { mCoreConfigCopyValue(&core->config, config, "videoScale");

@@ -375,7 +379,7 @@ struct GBAVideoRenderer* renderer = NULL;

if (gbacore->renderer.outputBuffer) { renderer = &gbacore->renderer.d; } -#if defined(BUILD_GLES2) || defined(BUILD_GLES3) +#ifdef BUILD_GLES3 if (gbacore->glRenderer.outputTex != (unsigned) -1 && mCoreConfigGetIntValue(&core->config, "hwaccelVideo", &fakeBool) && fakeBool) { mCoreConfigGetIntValue(&core->config, "videoScale", &gbacore->glRenderer.scale); renderer = &gbacore->glRenderer.d;

@@ -397,7 +401,7 @@ }

} static void _GBACoreDesiredVideoDimensions(const struct mCore* core, unsigned* width, unsigned* height) { -#if defined(BUILD_GLES2) || defined(BUILD_GLES3) +#ifdef BUILD_GLES3 const struct GBACore* gbacore = (const struct GBACore*) core; int scale = gbacore->glRenderer.scale; #else

@@ -417,7 +421,7 @@ memset(gbacore->renderer.scanlineDirty, 0xFFFFFFFF, sizeof(gbacore->renderer.scanlineDirty));

} static void _GBACoreSetVideoGLTex(struct mCore* core, unsigned texid) { -#if defined(BUILD_GLES2) || defined(BUILD_GLES3) +#ifdef BUILD_GLES3 struct GBACore* gbacore = (struct GBACore*) core; gbacore->glRenderer.outputTex = texid; #else

@@ -554,7 +558,7 @@ static void _GBACoreReset(struct mCore* core) {

struct GBACore* gbacore = (struct GBACore*) core; struct GBA* gba = (struct GBA*) core->board; if (gbacore->renderer.outputBuffer -#if defined(BUILD_GLES2) || defined(BUILD_GLES3) +#ifdef BUILD_GLES3 || gbacore->glRenderer.outputTex != (unsigned) -1 #endif ) {

@@ -563,7 +567,7 @@ if (gbacore->renderer.outputBuffer) {

renderer = &gbacore->renderer.d; } int fakeBool ATTRIBUTE_UNUSED; -#if defined(BUILD_GLES2) || defined(BUILD_GLES3) +#ifdef BUILD_GLES3 if (gbacore->glRenderer.outputTex != (unsigned) -1 && mCoreConfigGetIntValue(&core->config, "hwaccelVideo", &fakeBool) && fakeBool) { mCoreConfigGetIntValue(&core->config, "videoScale", &gbacore->glRenderer.scale); renderer = &gbacore->glRenderer.d;
M src/gba/gba.csrc/gba/gba.c

@@ -130,7 +130,9 @@ if (gba->memory.rom && !gba->isPristine) {

if (gba->yankedRomSize) { gba->yankedRomSize = 0; } +#if !defined(FIXED_ROM_BUFFER) && !defined(__wii__) mappedMemoryFree(gba->memory.rom, SIZE_CART0); +#endif } if (gba->romVf) {
M src/gba/io.csrc/gba/io.c

@@ -527,6 +527,8 @@ case REG_JOY_TRANS_LO:

case REG_JOY_TRANS_HI: gba->memory.io[REG_JOYSTAT >> 1] |= JOYSTAT_TRANS_BIT; // Fall through + case REG_SIODATA32_LO: + case REG_SIODATA32_HI: case REG_SIOMLT_SEND: case REG_JOYCNT: case REG_JOYSTAT:
M src/gba/renderers/gl.csrc/gba/renderers/gl.c

@@ -5,7 +5,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this

* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include <mgba/internal/gba/renderers/gl.h> -#if defined(BUILD_GLES2) || defined(BUILD_GLES3) +#ifdef BUILD_GLES3 #include <mgba/core/cache-set.h> #include <mgba/internal/arm/macros.h>
M src/gba/sio/lockstep.csrc/gba/sio/lockstep.c

@@ -30,6 +30,7 @@ lockstep->multiRecv[1] = 0xFFFF;

lockstep->multiRecv[2] = 0xFFFF; lockstep->multiRecv[3] = 0xFFFF; lockstep->attachedMulti = 0; + lockstep->attachedNormal = 0; } void GBASIOLockstepNodeCreate(struct GBASIOLockstepNode* node) {

@@ -44,11 +45,14 @@ bool GBASIOLockstepAttachNode(struct GBASIOLockstep* lockstep, struct GBASIOLockstepNode* node) {

if (lockstep->d.attached == MAX_GBAS) { return false; } + mLockstepLock(&lockstep->d); lockstep->players[lockstep->d.attached] = node; node->p = lockstep; node->id = lockstep->d.attached; + node->normalSO = true; node->transferFinished = true; ++lockstep->d.attached; + mLockstepUnlock(&lockstep->d); return true; }

@@ -56,6 +60,7 @@ void GBASIOLockstepDetachNode(struct GBASIOLockstep* lockstep, struct GBASIOLockstepNode* node) {

if (lockstep->d.attached == 0) { return; } + mLockstepLock(&lockstep->d); int i; for (i = 0; i < lockstep->d.attached; ++i) { if (lockstep->players[i] != node) {

@@ -66,8 +71,10 @@ lockstep->players[i - 1] = lockstep->players[i];

lockstep->players[i - 1]->id = i - 1; } --lockstep->d.attached; + lockstep->players[lockstep->d.attached] = NULL; break; } + mLockstepUnlock(&lockstep->d); } bool GBASIOLockstepNodeInit(struct GBASIODriver* driver) {

@@ -107,6 +114,7 @@ node->d.p->siocnt = GBASIOMultiplayerFillSlave(node->d.p->siocnt);

} break; case SIO_NORMAL_32: + ATOMIC_ADD(node->p->attachedNormal, 1); node->d.writeRegister = GBASIOLockstepNodeNormalWriteRegister; break; default:

@@ -132,6 +140,9 @@ switch (node->mode) {

case SIO_MULTI: ATOMIC_SUB(node->p->attachedMulti, 1); break; + case SIO_NORMAL_32: + ATOMIC_SUB(node->p->attachedNormal, 1); + break; default: break; }

@@ -148,11 +159,6 @@ }

node->p->d.unload(&node->p->d, node->id); - node->p->multiRecv[0] = 0xFFFF; - node->p->multiRecv[1] = 0xFFFF; - node->p->multiRecv[2] = 0xFFFF; - node->p->multiRecv[3] = 0xFFFF; - _finishTransfer(node); if (!node->id) {

@@ -173,7 +179,7 @@

mLockstepLock(&node->p->d); if (address == REG_SIOCNT) { - mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIOCNT <- %04x", node->id, value); + mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIOCNT <- %04X", node->id, value); enum mLockstepPhase transferActive; ATOMIC_LOAD(transferActive, node->p->d.transferActive);

@@ -200,7 +206,9 @@ }

value &= 0xFF83; value |= driver->p->siocnt & 0x00FC; } else if (address == REG_SIOMLT_SEND) { - mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIOMLT_SEND <- %04x", node->id, value); + mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIOMLT_SEND <- %04X", node->id, value); + } else { + mLOG(GBA_SIO, STUB, "Lockstep %i: Unknown reg %03X <- %04X", node->id, address, value); } mLockstepUnlock(&node->p->d);

@@ -246,7 +254,7 @@ sio->siocnt = GBASIONormalClearStart(sio->siocnt);

if (node->id) { sio->siocnt = GBASIONormalSetSi(sio->siocnt, GBASIONormalGetIdleSo(node->p->players[node->id - 1]->d.p->siocnt)); node->d.p->p->memory.io[REG_SIODATA32_LO >> 1] = node->p->normalRecv[node->id - 1]; - node->d.p->p->memory.io[REG_SIODATA32_HI >> 1] |= node->p->normalRecv[node->id - 1] >> 16; + node->d.p->p->memory.io[REG_SIODATA32_HI >> 1] = node->p->normalRecv[node->id - 1] >> 16; } else { node->d.p->p->memory.io[REG_SIODATA32_LO >> 1] = 0xFFFF; node->d.p->p->memory.io[REG_SIODATA32_HI >> 1] = 0xFFFF;

@@ -303,8 +311,8 @@ node->p->normalRecv[0] = node->d.p->p->memory.io[REG_SIODATA8 >> 1] & 0xFF;

break; case SIO_NORMAL_32: node->p->multiRecv[0] = 0xFFFF; - mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIODATA32_LO <- %04x", node->id, node->d.p->p->memory.io[REG_SIODATA32_LO >> 1]); - mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIODATA32_HI <- %04x", node->id, node->d.p->p->memory.io[REG_SIODATA32_HI >> 1]); + mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIODATA32_LO <- %04X", node->id, node->d.p->p->memory.io[REG_SIODATA32_LO >> 1]); + mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIODATA32_HI <- %04X", node->id, node->d.p->p->memory.io[REG_SIODATA32_HI >> 1]); node->p->normalRecv[0] = node->d.p->p->memory.io[REG_SIODATA32_LO >> 1]; node->p->normalRecv[0] |= node->d.p->p->memory.io[REG_SIODATA32_HI >> 1] << 16; break;

@@ -473,27 +481,31 @@

mLockstepLock(&node->p->d); if (address == REG_SIOCNT) { - mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIOCNT <- %04x", node->id, value); + mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIOCNT <- %04X", node->id, value); value &= 0xFF8B; if (!node->id) { - value = GBASIONormalFillSi(value); + value = GBASIONormalClearSi(value); } - if (value & 0x0080 && !node->id) { - // Internal shift clock - if (value & 1) { - ATOMIC_STORE(node->p->d.transferActive, TRANSFER_STARTING); - } - // Frequency - if (value & 2) { - node->p->d.transferCycles = GBA_ARM7TDMI_FREQUENCY / 1024; + if (value & 0x0080) { + if (!node->id) { + // Internal shift clock + if (value & 1) { + ATOMIC_STORE(node->p->d.transferActive, TRANSFER_STARTING); + } + // Frequency + if (value & 2) { + node->p->d.transferCycles = GBA_ARM7TDMI_FREQUENCY / 1024; + } else { + node->p->d.transferCycles = GBA_ARM7TDMI_FREQUENCY / 8192; + } } else { - node->p->d.transferCycles = GBA_ARM7TDMI_FREQUENCY / 8192; + } } } else if (address == REG_SIODATA32_LO) { - mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIODATA32_LO <- %04x", node->id, value); + mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIODATA32_LO <- %04X", node->id, value); } else if (address == REG_SIODATA32_HI) { - mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIODATA32_HI <- %04x", node->id, value); + mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIODATA32_HI <- %04X", node->id, value); } mLockstepUnlock(&node->p->d);
M src/gba/video.csrc/gba/video.c

@@ -55,23 +55,8 @@ { 0, 0 },

{ 0, 0 }, }; -static struct GBAVideoRenderer dummyRenderer = { - .init = GBAVideoDummyRendererInit, - .reset = GBAVideoDummyRendererReset, - .deinit = GBAVideoDummyRendererDeinit, - .writeVideoRegister = GBAVideoDummyRendererWriteVideoRegister, - .writeVRAM = GBAVideoDummyRendererWriteVRAM, - .writePalette = GBAVideoDummyRendererWritePalette, - .writeOAM = GBAVideoDummyRendererWriteOAM, - .drawScanline = GBAVideoDummyRendererDrawScanline, - .finishFrame = GBAVideoDummyRendererFinishFrame, - .getPixels = GBAVideoDummyRendererGetPixels, - .putPixels = GBAVideoDummyRendererPutPixels, -}; - void GBAVideoInit(struct GBAVideo* video) { - video->renderer = &dummyRenderer; - video->renderer->cache = NULL; + video->renderer = NULL; video->vram = anonymousMemoryMap(SIZE_VRAM); video->frameskip = 0; video->event.name = "GBA Video";

@@ -97,6 +82,16 @@

video->frameCounter = 0; video->frameskipCounter = 0; + video->shouldStall = 0; + + memset(video->palette, 0, sizeof(video->palette)); + memset(video->oam.raw, 0, sizeof(video->oam.raw)); + + if (!video->renderer) { + mLOG(GBA_VIDEO, FATAL, "No renderer associated"); + return; + } + memset(video->renderer->vramBG, 0, sizeof(video->renderer->vramBG)); video->renderer->vramBG[0] = &video->vram[0x0000]; video->renderer->vramBG[1] = &video->vram[0x2000];

@@ -107,10 +102,6 @@ video->renderer->vramOBJ[0] = &video->vram[0x8000];

video->renderer->vramOBJ[1] = &video->vram[0xA000]; video->renderer->vramOBJ[2] = _zeroes; video->renderer->vramOBJ[3] = _zeroes; - video->shouldStall = 0; - - memset(video->palette, 0, sizeof(video->palette)); - memset(video->oam.raw, 0, sizeof(video->oam.raw)); video->renderer->reset(video->renderer); }

@@ -120,9 +111,30 @@ video->renderer->deinit(video->renderer);

mappedMemoryFree(video->vram, SIZE_VRAM); } +void GBAVideoDummyRendererCreate(struct GBAVideoRenderer* renderer) { + static const struct GBAVideoRenderer dummyRenderer = { + .init = GBAVideoDummyRendererInit, + .reset = GBAVideoDummyRendererReset, + .deinit = GBAVideoDummyRendererDeinit, + .writeVideoRegister = GBAVideoDummyRendererWriteVideoRegister, + .writeVRAM = GBAVideoDummyRendererWriteVRAM, + .writePalette = GBAVideoDummyRendererWritePalette, + .writeOAM = GBAVideoDummyRendererWriteOAM, + .drawScanline = GBAVideoDummyRendererDrawScanline, + .finishFrame = GBAVideoDummyRendererFinishFrame, + .getPixels = GBAVideoDummyRendererGetPixels, + .putPixels = GBAVideoDummyRendererPutPixels, + }; + memcpy(renderer, &dummyRenderer, sizeof(*renderer)); +} + void GBAVideoAssociateRenderer(struct GBAVideo* video, struct GBAVideoRenderer* renderer) { - video->renderer->deinit(video->renderer); - renderer->cache = video->renderer->cache; + if (video->renderer) { + video->renderer->deinit(video->renderer); + renderer->cache = video->renderer->cache; + } else { + renderer->cache = NULL; + } video->renderer = renderer; renderer->palette = video->palette; memset(renderer->vramBG, 0, sizeof(renderer->vramBG));

@@ -159,8 +171,10 @@ video->vcount = 0;

} video->p->memory.io[REG_VCOUNT >> 1] = video->vcount; - if (video->vcount < GBA_VIDEO_VERTICAL_PIXELS && video->frameskipCounter <= 0) { - video->renderer->drawScanline(video->renderer, video->vcount); + if (video->vcount < GBA_VIDEO_VERTICAL_PIXELS) { + if (video->frameskipCounter <= 0) { + video->renderer->drawScanline(video->renderer, video->vcount); + } video->shouldStall = 1; }
M src/platform/3ds/CMakeLists.txtsrc/platform/3ds/CMakeLists.txt

@@ -10,7 +10,7 @@ find_program(RAW2C raw2c)

set(STRIP "${cross_prefix_path}strip" CACHE INTERNAL "symbol stripper") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-format" PARENT_SCOPE) -set(OS_DEFINES COLOR_16_BIT COLOR_5_6_5 IOAPI_NO_64) +set(OS_DEFINES _GNU_SOURCE COLOR_16_BIT COLOR_5_6_5 FIXED_ROM_BUFFER IOAPI_NO_64) include_directories(${CMAKE_CURRENT_BINARY_DIR}) if(${CMAKE_BUILD_TYPE} STREQUAL Debug OR ${CMAKE_BUILD_TYPE} STREQUAL RelWithDebInfo)
M src/platform/3ds/CMakeToolchain.txtsrc/platform/3ds/CMakeToolchain.txt

@@ -7,7 +7,7 @@ set(CTRULIB ${DEVKITPRO}/libctru)

endif() set(cross_prefix arm-none-eabi-) -set(arch_flags "-march=armv6k -mtune=mpcore -mfloat-abi=hard -ffunction-sections") +set(arch_flags "-march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft -ffunction-sections") set(inc_flags "-I${CTRULIB}/include ${arch_flags} -mword-relocations") set(link_flags "-L${CTRULIB}/lib -lctru -specs=3dsx.specs ${arch_flags} -Wl,--gc-sections")
M src/platform/3ds/ctru-heap.csrc/platform/3ds/ctru-heap.c

@@ -15,7 +15,7 @@ size_t romBufferSize;

FS_Archive sdmcArchive; -__attribute__((constructor)) static void init(void) { +void userAppInit(void) { FSUSER_OpenArchive(&sdmcArchive, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, "")); romBuffer = malloc(0x02000000);
M src/platform/3ds/main.csrc/platform/3ds/main.c

@@ -20,6 +20,7 @@ #include <mgba-util/gui.h>

#include <mgba-util/gui/file-select.h> #include <mgba-util/gui/font.h> #include <mgba-util/gui/menu.h> +#include <mgba-util/math.h> #include <mgba-util/memory.h> #include <mgba-util/platform/3ds/3ds-vfs.h>

@@ -114,14 +115,28 @@ if (!C3D_Init(C3D_DEFAULT_CMDBUF_SIZE)) {

return false; } - topScreen[0] = C3D_RenderTargetCreate(240, 400, GPU_RB_RGB8, 0); - topScreen[1] = C3D_RenderTargetCreate(240, 400, GPU_RB_RGB8, 0); + if (gfxIsWide()) { + topScreen[0] = C3D_RenderTargetCreate(240, 800, GPU_RB_RGB8, 0); + topScreen[1] = C3D_RenderTargetCreate(240, 800, GPU_RB_RGB8, 0); + } else { + topScreen[0] = C3D_RenderTargetCreate(240, 400, GPU_RB_RGB8, 0); + topScreen[1] = C3D_RenderTargetCreate(240, 400, GPU_RB_RGB8, 0); + } bottomScreen[0] = C3D_RenderTargetCreate(240, 320, GPU_RB_RGB8, 0); bottomScreen[1] = C3D_RenderTargetCreate(240, 320, GPU_RB_RGB8, 0); if (!topScreen[0] || !topScreen[1] || !bottomScreen[0] || !bottomScreen[1]) { return false; } + C3D_FrameBegin(0); + C3D_FrameDrawOn(bottomScreen[0]); + C3D_RenderTargetClear(bottomScreen[0], C3D_CLEAR_COLOR, 0, 0); + C3D_FrameDrawOn(topScreen[0]); + C3D_RenderTargetClear(topScreen[0], C3D_CLEAR_COLOR, 0, 0); + C3D_RenderTargetSetOutput(topScreen[0], GFX_TOP, GFX_LEFT, GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8)); + C3D_RenderTargetSetOutput(bottomScreen[0], GFX_BOTTOM, GFX_LEFT, GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8)); + C3D_FrameEnd(0); + if (!C3D_TexInitVRAM(&upscaleBufferTex, 512, 512, GPU_RGB8)) { return false; }

@@ -129,6 +144,11 @@ upscaleBuffer = C3D_RenderTargetCreateFromTex(&upscaleBufferTex, GPU_TEXFACE_2D, 0, 0);

if (!upscaleBuffer) { return false; } + + C3D_FrameBegin(0); + C3D_FrameDrawOn(upscaleBuffer); + C3D_RenderTargetClear(upscaleBuffer, C3D_CLEAR_COLOR, 0, 0); + C3D_FrameEnd(0); return ctrInitGpu(); }

@@ -456,6 +476,7 @@ }

static void _drawTex(struct mCore* core, bool faded, bool both) { unsigned screen_w, screen_h; + bool isWide = screenMode >= SM_PA_TOP && gfxIsWide(); switch (screenMode) { case SM_PA_BOTTOM: C3D_FrameDrawOn(bottomScreen[doubleBuffer]);

@@ -464,7 +485,7 @@ screen_h = 240;

break; case SM_PA_TOP: C3D_FrameDrawOn(topScreen[doubleBuffer]); - screen_w = 400; + screen_w = isWide ? 800 : 400; screen_h = 240; break; default:

@@ -473,6 +494,7 @@ screen_w = 512;

screen_h = 512; break; } + int wide = isWide ? 2 : 1; unsigned corew, coreh; core->desiredVideoDimensions(core, &corew, &coreh);

@@ -483,24 +505,16 @@ if (sgbCrop && w == 256 && h == 224) {

w = GB_VIDEO_HORIZONTAL_PIXELS; h = GB_VIDEO_VERTICAL_PIXELS; } - int innerw = w; - int innerh = h; - // Get greatest common divisor - while (w != 0) { - int temp = h % w; - h = w; - w = temp; - } - int gcd = h; - unsigned aspectw = innerw / gcd; - unsigned aspecth = innerh / gcd; + int aspectw = w; + int aspecth = h; + int gcd = reduceFraction(&aspecth, &aspectw); int x = 0; int y = 0; switch (screenMode) { case SM_PA_TOP: case SM_PA_BOTTOM: - w = corew; + w = corew * wide; h = coreh; x = (screen_w - w) / 2; y = (screen_h - h) / 2;

@@ -530,8 +544,8 @@ ctrAddRectEx(color & 0x7FFFFFFF, x, y, w, h, 0, 0, corew, coreh, 0);

} ctrFlushBatch(); - innerw = corew; - innerh = coreh; + int innerw = corew; + int innerh = coreh; corew = w; coreh = h; screen_h = 240;

@@ -540,7 +554,7 @@ C3D_FrameDrawOn(bottomScreen[doubleBuffer]);

screen_w = 320; } else { C3D_FrameDrawOn(topScreen[doubleBuffer]); - screen_w = 400; + screen_w = isWide ? 800 : 400; } ctrSetViewportSize(screen_w, screen_h, true);

@@ -552,6 +566,7 @@ case SM_AF_TOP:

case SM_AF_BOTTOM: afw = screen_w / (float) aspectw; afh = screen_h / (float) aspecth; + innerw *= wide; if (afw * aspecth > screen_h) { w = innerw * afh / gcd; h = innerh * afh / gcd;

@@ -833,12 +848,19 @@ }

gfxInit(GSP_BGR8_OES, GSP_BGR8_OES, true); + u8 model = 0; + CFGU_GetSystemModel(&model); + if (model != 3 /* o2DS */) { + gfxSetWide(true); + } + if (!_initGpu()) { outputTexture[0].data = 0; _cleanup(); return 1; } + C3D_TexSetWrap(&upscaleBufferTex, GPU_CLAMP_TO_EDGE, GPU_CLAMP_TO_EDGE); C3D_TexSetFilter(&upscaleBufferTex, GPU_LINEAR, GPU_LINEAR); int i;
M src/platform/opengl/gles2.csrc/platform/opengl/gles2.c

@@ -159,6 +159,7 @@ mGLES2ShaderInit(&context->initialShader, _vertexShader, _fragmentShader, -1, -1, false, uniforms, 4);

mGLES2ShaderInit(&context->finalShader, 0, 0, 0, 0, false, 0, 0); mGLES2ShaderInit(&context->interframeShader, 0, _interframeFragmentShader, -1, -1, false, 0, 0); +#ifdef BUILD_GLES3 if (context->initialShader.vao != (GLuint) -1) { glBindVertexArray(context->initialShader.vao); glBindBuffer(GL_ARRAY_BUFFER, context->vbo);

@@ -168,6 +169,7 @@ glBindVertexArray(context->interframeShader.vao);

glBindBuffer(GL_ARRAY_BUFFER, context->vbo); glBindVertexArray(0); } +#endif glDeleteFramebuffers(1, &context->finalShader.fbo); glDeleteTextures(1, &context->finalShader.tex);

@@ -305,9 +307,12 @@ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, shader->filter ? GL_LINEAR : GL_NEAREST);

glUseProgram(shader->program); glUniform1i(shader->texLocation, 0); glUniform2f(shader->texSizeLocation, context->d.width - padW, context->d.height - padH); +#ifdef BUILD_GLES3 if (shader->vao != (GLuint) -1) { glBindVertexArray(shader->vao); - } else { + } else +#endif + { glBindBuffer(GL_ARRAY_BUFFER, context->vbo); glEnableVertexAttribArray(shader->positionLocation); glVertexAttribPointer(shader->positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);

@@ -399,9 +404,11 @@ _drawShader(context, &context->interframeShader);

} glBindFramebuffer(GL_FRAMEBUFFER, 0); glUseProgram(0); +#ifdef BUILD_GLES3 if (context->finalShader.vao != (GLuint) -1) { glBindVertexArray(0); } +#endif } void mGLES2ContextPostFrame(struct VideoBackend* v, const void* frame) {

@@ -516,6 +523,7 @@ for (i = 0; i < shader->nUniforms; ++i) {

shader->uniforms[i].location = glGetUniformLocation(shader->program, shader->uniforms[i].name); } +#ifdef BUILD_GLES3 const GLubyte* extensions = glGetString(GL_EXTENSIONS); if (shaderBuffer[0] == _gles2Header || version[0] >= '3' || (extensions && strstr((const char*) extensions, "_vertex_array_object") != NULL)) { glGenVertexArrays(1, &shader->vao);

@@ -523,7 +531,9 @@ glBindVertexArray(shader->vao);

glEnableVertexAttribArray(shader->positionLocation); glVertexAttribPointer(shader->positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL); glBindVertexArray(0); - } else { + } else +#endif + { shader->vao = -1; }

@@ -535,9 +545,11 @@ glDeleteTextures(1, &shader->tex);

glDeleteShader(shader->fragmentShader); glDeleteProgram(shader->program); glDeleteFramebuffers(1, &shader->fbo); +#ifdef BUILD_GLES3 if (shader->vao != (GLuint) -1) { glDeleteVertexArrays(1, &shader->vao); } +#endif } void mGLES2ShaderAttach(struct mGLES2Context* context, struct mGLES2Shader* shaders, size_t nShaders) {

@@ -555,16 +567,20 @@ glBindFramebuffer(GL_FRAMEBUFFER, context->shaders[i].fbo);

glClearColor(0.f, 0.f, 0.f, 1.f); glClear(GL_COLOR_BUFFER_BIT); +#ifdef BUILD_GLES3 if (context->shaders[i].vao != (GLuint) -1) { glBindVertexArray(context->shaders[i].vao); glBindBuffer(GL_ARRAY_BUFFER, context->vbo); glEnableVertexAttribArray(context->shaders[i].positionLocation); glVertexAttribPointer(context->shaders[i].positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL); } +#endif } +#ifdef BUILD_GLES3 if (context->initialShader.vao != (GLuint) -1) { glBindVertexArray(0); } +#endif glBindFramebuffer(GL_FRAMEBUFFER, 0); }
M src/platform/opengl/gles2.hsrc/platform/opengl/gles2.h

@@ -22,6 +22,9 @@ #include <GL/glext.h>

#endif #else #include <GLES2/gl2.h> +#ifdef BUILD_GLES3 +#include <GLES3/gl3.h> +#endif #endif #include "platform/video-backend.h"
M src/platform/psp2/psp2-context.csrc/platform/psp2/psp2-context.c

@@ -52,7 +52,7 @@ } screenMode;

static void* outputBuffer; static int currentTex; -static vita2d_texture* tex[4]; +static vita2d_texture* tex[2]; static vita2d_texture* screenshot; static Thread audioThread; static bool interframeBlending = false;

@@ -326,8 +326,6 @@ unsigned width, height;

runner->core->desiredVideoDimensions(runner->core, &width, &height); tex[0] = vita2d_create_empty_texture_format(256, toPow2(height), SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR); tex[1] = vita2d_create_empty_texture_format(256, toPow2(height), SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR); - tex[2] = vita2d_create_empty_texture_format(256, toPow2(height), SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR); - tex[3] = vita2d_create_empty_texture_format(256, toPow2(height), SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR); currentTex = 0; screenshot = vita2d_create_empty_texture_format(256, toPow2(height), SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR);

@@ -494,8 +492,6 @@ UNUSED(runner);

CircleBufferDeinit(&rumble.history); vita2d_free_texture(tex[0]); vita2d_free_texture(tex[1]); - vita2d_free_texture(tex[2]); - vita2d_free_texture(tex[3]); vita2d_free_texture(screenshot); mappedMemoryFree(outputBuffer, 256 * 256 * 4); frameLimiter = true;

@@ -588,15 +584,32 @@ tint);

} void mPSP2Swap(struct mGUIRunner* runner) { - currentTex = (currentTex + 1) & 3; - runner->core->setVideoBuffer(runner->core, vita2d_texture_get_datap(tex[currentTex]), 256); + bool frameAvailable = true; + switch (runner->core->platform(runner->core)) { +#ifdef M_CORE_GBA + case PLATFORM_GBA: + frameAvailable = ((struct GBA*) runner->core->board)->video.frameskipCounter <= 0; + break; +#endif +#ifdef M_CORE_GB + case PLATFORM_GB: + frameAvailable = ((struct GB*) runner->core->board)->video.frameskipCounter <= 0; + break; +#endif + default: + break; + } + if (frameAvailable) { + currentTex = !currentTex; + runner->core->setVideoBuffer(runner->core, vita2d_texture_get_datap(tex[currentTex]), 256); + } } void mPSP2Draw(struct mGUIRunner* runner, bool faded) { unsigned width, height; runner->core->desiredVideoDimensions(runner->core, &width, &height); if (interframeBlending) { - _drawTex(tex[(currentTex - 1) & 3], width, height, faded, false); + _drawTex(tex[!currentTex], width, height, faded, false); } _drawTex(tex[currentTex], width, height, faded, interframeBlending); }
M src/platform/qt/AssetTile.cppsrc/platform/qt/AssetTile.cpp

@@ -130,8 +130,6 @@

void AssetTile::selectColor(int index) { const color_t* data; mTileCache* tileCache = m_tileCaches[m_index >= m_boundary]; - unsigned bpp = 8 << tileCache->bpp; - int paletteId = m_paletteId; data = mTileCacheGetTile(tileCache, m_index >= m_boundary ? m_index - m_boundary : m_index, m_paletteId); color_t color = data[index]; m_ui.color->setColor(0, color);
M src/platform/qt/AssetView.cppsrc/platform/qt/AssetView.cpp

@@ -144,8 +144,8 @@ QImage image = QImage(QSize(objInfo.width * 8, objInfo.height * 8), QImage::Format_Indexed8);

image.setColorTable(palette); uchar* bits = image.bits(); unsigned t = objInfo.tile; - for (int y = 0; y < objInfo.height; ++y) { - for (int x = 0; x < objInfo.width; ++x, ++t) { + for (unsigned y = 0; y < objInfo.height; ++y) { + for (unsigned x = 0; x < objInfo.width; ++x, ++t) { compositeTile(static_cast<const void*>(mTileCacheGetVRAM(tileCache, t)), bits, objInfo.width * 8, x * 8, y * 8, objInfo.bits); } t += objInfo.stride - objInfo.width;

@@ -183,7 +183,6 @@ unsigned width = GBAVideoObjSizes[shape * 4 + size][0];

unsigned height = GBAVideoObjSizes[shape * 4 + size][1]; unsigned tile = GBAObjAttributesCGetTile(obj->c); unsigned palette = GBAObjAttributesCGetPalette(obj->c); - unsigned tileBase = tile; unsigned paletteSet; unsigned bits; if (GBAObjAttributesAIs256Color(obj->a)) {

@@ -237,7 +236,6 @@

const GB* gb = static_cast<const GB*>(m_controller->thread()->core->board); const GBObj* obj = &gb->video.oam.obj[id]; - unsigned width = 8; unsigned height = 8; GBRegisterLCDC lcdc = gb->memory.io[REG_LCDC]; if (GBRegisterLCDCIsObjSize(lcdc)) {
M src/platform/qt/CMakeLists.txtsrc/platform/qt/CMakeLists.txt

@@ -25,7 +25,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/input)

find_package(Qt5 COMPONENTS Core Widgets OpenGL Network Multimedia) -if(NOT BUILD_GL AND NOT BUILD_GLES2) +if(NOT BUILD_GL AND NOT BUILD_GLES2 AND NOT BUILD_GLES3) message(WARNING "OpenGL is recommended to build the Qt port") endif()

@@ -244,7 +244,7 @@ else()

set(DATADIR ${CMAKE_INSTALL_DATADIR}/${BINARY_NAME}) endif() endif() -if(BUILD_GL OR BUILD_GLES2 OR BUILD_EPOXY) +if(BUILD_GLES2 OR BUILD_GLES3 OR BUILD_EPOXY) install(DIRECTORY ${CMAKE_SOURCE_DIR}/res/shaders DESTINATION ${DATADIR} COMPONENT ${BINARY_NAME}-qt) endif() install(FILES ${CMAKE_SOURCE_DIR}/res/nointro.dat DESTINATION ${DATADIR} COMPONENT ${BINARY_NAME}-qt)

@@ -294,8 +294,8 @@ set_target_properties(${BINARY_NAME}-qt PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}")

endif() list(APPEND QT_LIBRARIES Qt5::Widgets Qt5::Network) -if(BUILD_GL OR BUILD_GLES2 OR BUILD_EPOXY) - list(APPEND QT_LIBRARIES Qt5::OpenGL ${OPENGL_LIBRARY} ${OPENGLES2_LIBRARY}) +if(BUILD_GL OR BUILD_GLES2 OR BUILD_GLES3 OR BUILD_EPOXY) + list(APPEND QT_LIBRARIES Qt5::OpenGL ${OPENGL_LIBRARY} ${OPENGLES2_LIBRARY} ${OPENGLES3_LIBRARY}) endif() if(QT_STATIC) find_library(QTPCRE NAMES qtpcre2 qtpcre)

@@ -305,7 +305,7 @@ set(QWINDOWS_DEPS Qt5EventDispatcherSupport Qt5FontDatabaseSupport Qt5ThemeSupport Qt5WindowsUIAutomationSupport)

endif() list(APPEND QT_LIBRARIES Qt5::QWindowsIntegrationPlugin ${QWINDOWS_DEPS} dwmapi uxtheme imm32 -static-libgcc -static-libstdc++) set_target_properties(Qt5::Core PROPERTIES INTERFACE_LINK_LIBRARIES "${QTPCRE};version;winmm;ssl;crypto;ws2_32;iphlpapi;crypt32;userenv;netapi32;wtsapi32") - set_target_properties(Qt5::Gui PROPERTIES INTERFACE_LINK_LIBRARIES ${OPENGL_LIBRARY} ${OPENGLES2_LIBRARY}) + set_target_properties(Qt5::Gui PROPERTIES INTERFACE_LINK_LIBRARIES ${OPENGL_LIBRARY} ${OPENGLES2_LIBRARY} ${OPENGLES3_LIBRARY}) elseif(APPLE) find_package(Cups) find_package(Qt5PrintSupport)
M src/platform/qt/CheatsModel.cppsrc/platform/qt/CheatsModel.cpp

@@ -42,11 +42,10 @@ return QVariant();

} } - if (index.row() >= mCheatSetsSize(&m_device->cheats)) { + if ((size_t) index.row() >= mCheatSetsSize(&m_device->cheats)) { return QVariant(); } - int row = index.row(); const mCheatSet* cheats = *mCheatSetsGetPointer(&m_device->cheats, index.row()); switch (role) { case Qt::DisplayRole:

@@ -60,11 +59,10 @@ }

} bool CheatsModel::setData(const QModelIndex& index, const QVariant& value, int role) { - if (!index.isValid() || index.parent().isValid() || index.row() > mCheatSetsSize(&m_device->cheats)) { + if (!index.isValid() || index.parent().isValid() || (size_t) index.row() > mCheatSetsSize(&m_device->cheats)) { return false; } - int row = index.row(); mCheatSet* cheats = *mCheatSetsGetPointer(&m_device->cheats, index.row()); switch (role) { case Qt::DisplayRole:

@@ -119,7 +117,7 @@

return Qt::ItemIsUserCheckable | Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable; } -int CheatsModel::columnCount(const QModelIndex& parent) const { +int CheatsModel::columnCount(const QModelIndex&) const { return 1; }

@@ -141,14 +139,14 @@ }

if (index.parent().isValid()) { return static_cast<mCheatSet*>(index.internalPointer()); } - if (index.row() >= mCheatSetsSize(&m_device->cheats)) { + if ((size_t) index.row() >= mCheatSetsSize(&m_device->cheats)) { return nullptr; } return *mCheatSetsGetPointer(&m_device->cheats, index.row()); } void CheatsModel::removeAt(const QModelIndex& index) { - if (!index.isValid() || index.parent().isValid() || index.row() >= mCheatSetsSize(&m_device->cheats)) { + if (!index.isValid() || index.parent().isValid() || (size_t) index.row() >= mCheatSetsSize(&m_device->cheats)) { return; } int row = index.row();
M src/platform/qt/ColorPicker.cppsrc/platform/qt/ColorPicker.cpp

@@ -46,7 +46,6 @@ bool ColorPicker::eventFilter(QObject* obj, QEvent* event) {

if (event->type() != QEvent::MouseButtonRelease) { return false; } - int colorId; if (obj != m_parent) { return false; }
M src/platform/qt/Display.cppsrc/platform/qt/Display.cpp

@@ -10,21 +10,21 @@ #include "DisplayQt.h"

using namespace QGBA; -#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(USE_EPOXY) +#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(BUILD_GLES3) || defined(USE_EPOXY) Display::Driver Display::s_driver = Display::Driver::OPENGL; #else Display::Driver Display::s_driver = Display::Driver::QT; #endif Display* Display::create(QWidget* parent) { -#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(USE_EPOXY) +#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(BUILD_GLES3) || defined(USE_EPOXY) QSurfaceFormat format; format.setSwapInterval(1); format.setSwapBehavior(QSurfaceFormat::DoubleBuffer); #endif switch (s_driver) { -#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(USE_EPOXY) +#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(BUILD_GLES3) || defined(USE_EPOXY) case Driver::OPENGL: if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) { format.setVersion(3, 0);

@@ -44,7 +44,7 @@ case Driver::QT:

return new DisplayQt(parent); default: -#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(USE_EPOXY) +#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(BUILD_GLES3) || defined(USE_EPOXY) return new DisplayGL(format, parent); #else return new DisplayQt(parent);
M src/platform/qt/Display.hsrc/platform/qt/Display.h

@@ -27,12 +27,8 @@

public: enum class Driver { QT = 0, -#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(USE_EPOXY) OPENGL = 1, -#endif -#ifdef BUILD_GL OPENGL1 = 2, -#endif }; Display(QWidget* parent = nullptr);

@@ -51,7 +47,7 @@ virtual bool isDrawing() const = 0;

virtual bool supportsShaders() const = 0; virtual VideoShader* shaders() = 0; virtual int framebufferHandle() { return -1; } - virtual void setVideoScale(int scale) {} + virtual void setVideoScale(int) {} QSize viewportSize();
M src/platform/qt/DisplayGL.cppsrc/platform/qt/DisplayGL.cpp

@@ -5,7 +5,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this

* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "DisplayGL.h" -#if defined(BUILD_GL) || defined(BUILD_GLES2) +#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(BUILD_GLES3) #include "CoreController.h"

@@ -23,7 +23,7 @@ #include <mgba-util/math.h>

#ifdef BUILD_GL #include "platform/opengl/gl.h" #endif -#ifdef BUILD_GLES2 +#if defined(BUILD_GLES2) || defined(BUILD_GLES3) #include "platform/opengl/gles2.h" #ifdef _WIN32 #include <epoxy/wgl.h>

@@ -268,7 +268,7 @@ {

#ifdef BUILD_GL mGLContext* glBackend; #endif -#ifdef BUILD_GLES2 +#if defined(BUILD_GLES2) || defined(BUILD_GLES3) mGLES2Context* gl2Backend; #endif

@@ -287,7 +287,7 @@ #if defined(_WIN32) && defined(USE_EPOXY)

epoxy_handle_external_wglMakeCurrent(); #endif -#ifdef BUILD_GLES2 +#if defined(BUILD_GLES2) || defined(BUILD_GLES3) auto version = m_gl->format().version(); QStringList extensions = QString(reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS))).split(' '); if (forceVersion != 1 && ((version == qMakePair(2, 1) && extensions.contains("GL_ARB_framebuffer_object")) || version.first > 2)) {

@@ -319,7 +319,7 @@ #endif

}; m_backend->init(m_backend, 0); -#ifdef BUILD_GLES2 +#if defined(BUILD_GLES2) || defined(BUILD_GLES3) if (m_supportsShaders) { m_shader.preprocessShader = static_cast<void*>(&reinterpret_cast<mGLES2Context*>(m_backend)->initialShader); }

@@ -330,23 +330,17 @@ m_backend->filter = false;

m_backend->lockAspectRatio = false; m_backend->interframeBlending = false; - for (int i = 0; i < 3; ++i) { - m_free.append(new uint32_t[1024 * 2048]); + for (auto& buf : m_buffers) { + m_free.append(&buf.front()); } } PainterGL::~PainterGL() { - while (!m_queue.isEmpty()) { - delete[] m_queue.dequeue(); - } - for (auto item : m_free) { - delete[] item; - } m_gl->makeCurrent(m_surface); #if defined(_WIN32) && defined(USE_EPOXY) epoxy_handle_external_wglMakeCurrent(); #endif -#ifdef BUILD_GLES2 +#if defined(BUILD_GLES2) || defined(BUILD_GLES3) if (m_shader.passes) { mGLES2ShaderFree(&m_shader); }

@@ -420,7 +414,7 @@ #if defined(_WIN32) && defined(USE_EPOXY)

epoxy_handle_external_wglMakeCurrent(); #endif -#ifdef BUILD_GLES2 +#if defined(BUILD_GLES2) || defined(BUILD_GLES3) if (m_supportsShaders && m_shader.passes) { mGLES2ShaderAttach(reinterpret_cast<mGLES2Context*>(m_backend), static_cast<mGLES2Shader*>(m_shader.passes), m_shader.nPasses); }

@@ -496,8 +490,6 @@ float r = m_surface->devicePixelRatio();

m_backend->resized(m_backend, m_size.width() * r, m_size.height() * r); if (m_buffer) { m_backend->postFrame(m_backend, m_buffer); - m_free.append(m_buffer); - m_buffer = nullptr; } m_backend->drawFrame(m_backend); m_painter.endNativePainting();

@@ -507,7 +499,7 @@ }

} void PainterGL::enqueue(const uint32_t* backing) { - m_mutex.lock(); + QMutexLocker locker(&m_mutex); uint32_t* buffer = nullptr; if (backing) { if (m_free.isEmpty()) {

@@ -515,17 +507,17 @@ buffer = m_queue.dequeue();

} else { buffer = m_free.takeLast(); } - QSize size = m_context->screenDimensions(); - memcpy(buffer, backing, size.width() * size.height() * BYTES_PER_PIXEL); + if (buffer) { + QSize size = m_context->screenDimensions(); + memcpy(buffer, backing, size.width() * size.height() * BYTES_PER_PIXEL); + } } m_queue.enqueue(buffer); - m_mutex.unlock(); } void PainterGL::dequeue() { - m_mutex.lock(); + QMutexLocker locker(&m_mutex); if (m_queue.isEmpty()) { - m_mutex.unlock(); return; } uint32_t* buffer = m_queue.dequeue();

@@ -533,10 +525,7 @@ if (m_buffer) {

m_free.append(m_buffer); m_buffer = nullptr; } - if (buffer) { - m_buffer = buffer; - } - m_mutex.unlock(); + m_buffer = buffer; } void PainterGL::dequeueAll() {

@@ -566,7 +555,7 @@ void PainterGL::setShaders(struct VDir* dir) {

if (!supportsShaders()) { return; } -#ifdef BUILD_GLES2 +#if defined(BUILD_GLES2) || defined(BUILD_GLES3) if (!m_started) { m_gl->makeCurrent(m_surface); #if defined(_WIN32) && defined(USE_EPOXY)

@@ -590,7 +579,7 @@ void PainterGL::clearShaders() {

if (!supportsShaders()) { return; } -#ifdef BUILD_GLES2 +#if defined(BUILD_GLES2) || defined(BUILD_GLES3) if (!m_started) { m_gl->makeCurrent(m_surface); #if defined(_WIN32) && defined(USE_EPOXY)

@@ -612,7 +601,7 @@ return &m_shader;

} int PainterGL::glTex() { -#ifdef BUILD_GLES2 +#if defined(BUILD_GLES2) || defined(BUILD_GLES3) if (supportsShaders()) { mGLES2Context* gl2Backend = reinterpret_cast<mGLES2Context*>(m_backend); return gl2Backend->tex;
M src/platform/qt/DisplayGL.hsrc/platform/qt/DisplayGL.h

@@ -5,7 +5,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this

* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #pragma once -#if defined(BUILD_GL) || defined(BUILD_GLES2) +#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(BUILD_GLES3) #include "Display.h"

@@ -24,6 +24,8 @@ #include <QPainter>

#include <QQueue> #include <QThread> #include <QTimer> + +#include <array> #include "VideoProxy.h"

@@ -119,6 +121,7 @@ void performDraw();

void dequeue(); void dequeueAll(); + std::array<std::array<uint32_t, 0x100000>, 3> m_buffers; QList<uint32_t*> m_free; QQueue<uint32_t*> m_queue; uint32_t* m_buffer;
M src/platform/qt/DisplayQt.cppsrc/platform/qt/DisplayQt.cpp

@@ -97,6 +97,7 @@ painter.fillRect(QRect(QPoint(), size()), Qt::black);

if (isFiltered()) { painter.setRenderHint(QPainter::SmoothPixmapTransform); } + // TODO: Refactor this code out (since it's copied in like 3 places) QSize s = size(); QSize ds = viewportSize(); if (isAspectRatioLocked()) {
M src/platform/qt/DisplayQt.hsrc/platform/qt/DisplayQt.h

@@ -42,8 +42,8 @@ virtual void paintEvent(QPaintEvent*) override;

private: bool m_isDrawing = false; - unsigned m_width; - unsigned m_height; + int m_width; + int m_height; QImage m_backing{nullptr}; QImage m_oldBacking{nullptr}; std::shared_ptr<CoreController> m_context = nullptr;
M src/platform/qt/GIFView.cppsrc/platform/qt/GIFView.cpp

@@ -85,7 +85,7 @@ m_ui.fmtGif->setEnabled(true);

} void GIFView::selectFile() { - QString filename = GBAApp::app()->getSaveFileName(this, tr("Select output file"), tr("Graphics Interchange Format (*.gif);;Animated Portable Network Graphics (*.png *.webp *.apng)")); + QString filename = GBAApp::app()->getSaveFileName(this, tr("Select output file"), tr("Graphics Interchange Format (*.gif);;WebP ( *.webp);;Animated Portable Network Graphics (*.png *.apng)")); m_ui.filename->setText(filename); }
M src/platform/qt/MultiplayerController.cppsrc/platform/qt/MultiplayerController.cpp

@@ -221,6 +221,7 @@ GBASIOLockstepAttachNode(&m_gbaLockstep, node);

m_players.append({controller, node}); GBASIOSetDriver(&gba->sio, &node->d, SIO_MULTI); + GBASIOSetDriver(&gba->sio, &node->d, SIO_NORMAL_32); emit gameAttached(); return true;

@@ -267,6 +268,7 @@ case PLATFORM_GBA: {

GBA* gba = static_cast<GBA*>(thread->core->board); GBASIOLockstepNode* node = reinterpret_cast<GBASIOLockstepNode*>(gba->sio.drivers.multiplayer); GBASIOSetDriver(&gba->sio, nullptr, SIO_MULTI); + GBASIOSetDriver(&gba->sio, nullptr, SIO_NORMAL_32); if (node) { GBASIOLockstepDetachNode(&m_gbaLockstep, node); delete node;
M src/platform/qt/ShortcutController.hsrc/platform/qt/ShortcutController.h

@@ -30,7 +30,7 @@ Shortcut(Action* action);

Action* action() { return m_action; } const Action* action() const { return m_action; } - const int shortcut() const { return m_shortcut; } + int shortcut() const { return m_shortcut; } QString visibleName() const { return m_action ? m_action->visibleName() : QString(); } QString name() const { return m_action ? m_action->name() : QString(); } int button() const { return m_button; }
M src/platform/qt/ShortcutModel.cppsrc/platform/qt/ShortcutModel.cpp

@@ -27,7 +27,6 @@ QVariant ShortcutModel::data(const QModelIndex& index, int role) const {

if (role != Qt::DisplayRole || !index.isValid()) { return QVariant(); } - int row = index.row(); const Item* item = static_cast<Item*>(index.internalPointer()); const Shortcut* shortcut = item->shortcut; switch (index.column()) {

@@ -101,7 +100,7 @@ pitem->shortcut = m_controller->shortcut(parent);

return createIndex(m_controller->indexIn(parent), 0, pitem); } -int ShortcutModel::columnCount(const QModelIndex& index) const { +int ShortcutModel::columnCount(const QModelIndex&) const { return 3; }

@@ -131,7 +130,7 @@ beginInsertRows(createIndex(m_controller->indexIn(parent), 0, item), index, index + 1);

endInsertRows(); } -void ShortcutModel::clearMenu(const QString& name) { +void ShortcutModel::clearMenu(const QString&) { // TODO beginResetModel(); endResetModel();
M src/platform/qt/Window.cppsrc/platform/qt/Window.cpp

@@ -599,7 +599,7 @@ openView(window);

} #endif -void Window::resizeEvent(QResizeEvent* event) { +void Window::resizeEvent(QResizeEvent*) { if (!isFullScreen()) { m_config->setOption("height", m_screenWidget->height()); m_config->setOption("width", m_screenWidget->width());

@@ -940,7 +940,8 @@ fail->setAttribute(Qt::WA_DeleteOnClose);

fail->show(); } -void Window::unimplementedBiosCall(int call) { +void Window::unimplementedBiosCall(int) { + // TODO: Mention which call? if (m_hitUnimplementedBiosCall) { return; }

@@ -1192,7 +1193,7 @@

m_actions.addAction(tr("Load &patch..."), "loadPatch", this, &Window::selectPatch, "file"); #ifdef M_CORE_GBA - Action* bootBIOS = m_actions.addAction(tr("Boot BIOS"), "bootBIOS", [this]() { + m_actions.addAction(tr("Boot BIOS"), "bootBIOS", [this]() { setController(m_manager->loadBIOS(PLATFORM_GBA, m_config->getOption("gba.bios")), QString()); }, "file"); #endif

@@ -1203,7 +1204,7 @@ Action* scanCard = addGameAction(tr("Scan e-Reader dotcodes..."), "scanCard", this, &Window::scanCard, "file");

m_platformActions.insert(PLATFORM_GBA, scanCard); #endif - Action* romInfo = addGameAction(tr("ROM &info..."), "romInfo", openControllerTView<ROMInfo>(), "file"); + addGameAction(tr("ROM &info..."), "romInfo", openControllerTView<ROMInfo>(), "file"); m_actions.addMenu(tr("Recent"), "mru", "file"); m_actions.addSeparator("file");

@@ -1259,16 +1260,12 @@

m_actions.addSeparator("quickLoad"); m_actions.addSeparator("quickSave"); - Action* undoLoadState = addGameAction(tr("Undo load state"), "undoLoadState", [this]() { - m_controller->loadBackupState(); - }, "quickLoad", QKeySequence("F11")); + Action* undoLoadState = addGameAction(tr("Undo load state"), "undoLoadState", &CoreController::loadBackupState, "quickLoad", QKeySequence("F11")); m_nonMpActions.append(undoLoadState); m_platformActions.insert(PLATFORM_GBA, undoLoadState); m_platformActions.insert(PLATFORM_GB, undoLoadState); - Action* undoSaveState = addGameAction(tr("Undo save state"), "undoSaveState", [this]() { - m_controller->saveBackupState(); - }, "quickSave", QKeySequence("Shift+F11")); + Action* undoSaveState = addGameAction(tr("Undo save state"), "undoSaveState", &CoreController::saveBackupState, "quickSave", QKeySequence("Shift+F11")); m_nonMpActions.append(undoSaveState); m_platformActions.insert(PLATFORM_GBA, undoSaveState); m_platformActions.insert(PLATFORM_GB, undoSaveState);

@@ -1320,17 +1317,10 @@ m_actions.addAction(tr("E&xit"), "quit", static_cast<QWidget*>(this), &QWidget::close, "file", QKeySequence::Quit);

#endif m_actions.addMenu(tr("&Emulation"), "emu"); - addGameAction(tr("&Reset"), "reset", [this]() { - m_controller->reset(); - }, "emu", QKeySequence("Ctrl+R")); - addGameAction(tr("Sh&utdown"), "shutdown", [this]() { - m_controller->stop(); - }, "emu"); - - Action* yank = addGameAction(tr("Yank game pak"), "yank", [this]() { - m_controller->yankPak(); - }, "emu"); + addGameAction(tr("&Reset"), "reset", &CoreController::reset, "emu", QKeySequence("Ctrl+R")); + addGameAction(tr("Sh&utdown"), "shutdown", &CoreController::stop, "emu"); + Action* yank = addGameAction(tr("Yank game pak"), "yank", &CoreController::yankPak, "emu"); m_platformActions.insert(PLATFORM_GBA, yank); m_platformActions.insert(PLATFORM_GB, yank);

@@ -1345,9 +1335,7 @@ }

}, "emu", QKeySequence("Ctrl+P")); connect(this, &Window::paused, pause, &Action::setActive); - addGameAction(tr("&Next frame"), "frameAdvance", [this]() { - m_controller->frameAdvance(); - }, "emu", QKeySequence("Ctrl+N")); + addGameAction(tr("&Next frame"), "frameAdvance", &CoreController::frameAdvance, "emu", QKeySequence("Ctrl+N")); m_actions.addSeparator("emu");

@@ -1363,7 +1351,7 @@ }, "emu", QKeySequence("Shift+Tab"));

m_actions.addMenu(tr("Fast forward speed"), "fastForwardSpeed", "emu"); ConfigOption* ffspeed = m_config->addOption("fastForwardRatio"); - ffspeed->connect([this](const QVariant& value) { + ffspeed->connect([this](const QVariant&) { reloadConfig(); }, this); ffspeed->addValue(tr("Unbounded"), -1.0f, &m_actions, "fastForwardSpeed");

@@ -1397,14 +1385,14 @@ m_platformActions.insert(PLATFORM_GB, frameRewind);

ConfigOption* videoSync = m_config->addOption("videoSync"); videoSync->addBoolean(tr("Sync to &video"), &m_actions, "emu"); - videoSync->connect([this](const QVariant& value) { + videoSync->connect([this](const QVariant&) { reloadConfig(); }, this); m_config->updateOption("videoSync"); ConfigOption* audioSync = m_config->addOption("audioSync"); audioSync->addBoolean(tr("Sync to &audio"), &m_actions, "emu"); - audioSync->connect([this](const QVariant& value) { + audioSync->connect([this](const QVariant&) { reloadConfig(); }, this); m_config->updateOption("audioSync");

@@ -1517,7 +1505,7 @@ m_config->updateOption("resampleVideo");

m_actions.addMenu(tr("Frame&skip"),"skip", "av"); ConfigOption* skip = m_config->addOption("frameskip"); - skip->connect([this](const QVariant& value) { + skip->connect([this](const QVariant&) { reloadConfig(); }, this); for (int i = 0; i <= 10; ++i) {

@@ -1656,62 +1644,62 @@ m_controller->endVideoLog();

}, "tools"); ConfigOption* skipBios = m_config->addOption("skipBios"); - skipBios->connect([this](const QVariant& value) { + skipBios->connect([this](const QVariant&) { reloadConfig(); }, this); ConfigOption* useBios = m_config->addOption("useBios"); - useBios->connect([this](const QVariant& value) { + useBios->connect([this](const QVariant&) { reloadConfig(); }, this); ConfigOption* buffers = m_config->addOption("audioBuffers"); - buffers->connect([this](const QVariant& value) { + buffers->connect([this](const QVariant&) { reloadConfig(); }, this); ConfigOption* sampleRate = m_config->addOption("sampleRate"); - sampleRate->connect([this](const QVariant& value) { + sampleRate->connect([this](const QVariant&) { reloadConfig(); }, this); ConfigOption* volume = m_config->addOption("volume"); - volume->connect([this](const QVariant& value) { + volume->connect([this](const QVariant&) { reloadConfig(); }, this); ConfigOption* volumeFf = m_config->addOption("fastForwardVolume"); - volumeFf->connect([this](const QVariant& value) { + volumeFf->connect([this](const QVariant&) { reloadConfig(); }, this); ConfigOption* muteFf = m_config->addOption("fastForwardMute"); - muteFf->connect([this](const QVariant& value) { + muteFf->connect([this](const QVariant&) { reloadConfig(); }, this); ConfigOption* rewindEnable = m_config->addOption("rewindEnable"); - rewindEnable->connect([this](const QVariant& value) { + rewindEnable->connect([this](const QVariant&) { reloadConfig(); }, this); ConfigOption* rewindBufferCapacity = m_config->addOption("rewindBufferCapacity"); - rewindBufferCapacity->connect([this](const QVariant& value) { + rewindBufferCapacity->connect([this](const QVariant&) { reloadConfig(); }, this); ConfigOption* allowOpposingDirections = m_config->addOption("allowOpposingDirections"); - allowOpposingDirections->connect([this](const QVariant& value) { + allowOpposingDirections->connect([this](const QVariant&) { reloadConfig(); }, this); ConfigOption* saveStateExtdata = m_config->addOption("saveStateExtdata"); - saveStateExtdata->connect([this](const QVariant& value) { + saveStateExtdata->connect([this](const QVariant&) { reloadConfig(); }, this); ConfigOption* loadStateExtdata = m_config->addOption("loadStateExtdata"); - loadStateExtdata->connect([this](const QVariant& value) { + loadStateExtdata->connect([this](const QVariant&) { reloadConfig(); }, this);

@@ -1820,9 +1808,14 @@

template<typename T, typename V> Action* Window::addGameAction(const QString& visibleName, const QString& name, T* obj, V (T::*method)(), const QString& menu, const QKeySequence& shortcut) { return addGameAction(visibleName, name, [this, obj, method]() { - if (m_controller) { - (obj->*method)(); - } + (obj->*method)(); + }, menu, shortcut); +} + +template<typename V> +Action* Window::addGameAction(const QString& visibleName, const QString& name, V (CoreController::*method)(), const QString& menu, const QKeySequence& shortcut) { + return addGameAction(visibleName, name, [this, method]() { + (m_controller.get()->*method)(); }, menu, shortcut); }
M src/platform/qt/Window.hsrc/platform/qt/Window.h

@@ -164,6 +164,7 @@ template <typename T, typename... A> std::function<void()> openControllerTView(A... arg);

Action* addGameAction(const QString& visibleName, const QString& name, Action::Function action, const QString& menu = {}, const QKeySequence& = {}); template<typename T, typename V> Action* addGameAction(const QString& visibleName, const QString& name, T* obj, V (T::*action)(), const QString& menu = {}, const QKeySequence& = {}); + template<typename V> Action* addGameAction(const QString& visibleName, const QString& name, V (CoreController::*action)(), const QString& menu = {}, const QKeySequence& = {}); Action* addGameAction(const QString& visibleName, const QString& name, Action::BooleanFunction action, const QString& menu = {}, const QKeySequence& = {}); void updateTitle(float fps = -1);
M src/platform/sdl/main.csrc/platform/sdl/main.c

@@ -210,6 +210,9 @@ struct mScriptBridge* bridge = mScriptBridgeCreate();

#ifdef ENABLE_PYTHON mPythonSetup(bridge); #endif +#ifdef USE_DEBUGGERS + CLIDebuggerScriptEngineInstall(bridge); +#endif #endif #ifdef USE_DEBUGGERS

@@ -223,7 +226,7 @@ }

#endif mDebuggerAttach(debugger, renderer->core); mDebuggerEnter(debugger, DEBUGGER_ENTER_MANUAL, NULL); - #ifdef ENABLE_SCRIPTING +#ifdef ENABLE_SCRIPTING mScriptBridgeSetDebugger(bridge, debugger); #endif }
M src/platform/test/cinema-main.csrc/platform/test/cinema-main.c

@@ -70,7 +70,7 @@ CI_R_MISSING,

}; struct CInemaTest { - char directory[MAX_TEST]; + char directory[PATH_MAX]; char filename[MAX_TEST]; char name[MAX_TEST]; enum CInemaStatus status;
M src/platform/test/perf-main.csrc/platform/test/perf-main.c

@@ -23,6 +23,7 @@ #ifdef __SWITCH__

#include <switch.h> #endif #ifdef GEKKO +#define asm __asm__ #include <fat.h> #include <gccore.h> #ifdef FIXED_ROM_BUFFER
M src/platform/wii/CMakeLists.txtsrc/platform/wii/CMakeLists.txt

@@ -3,7 +3,7 @@ find_program(GXTEXCONV gxtexconv)

find_program(RAW2C raw2c) find_program(WIILOAD wiiload) -set(OS_DEFINES COLOR_16_BIT COLOR_5_6_5 USE_VFS_FILE IOAPI_NO_64 FIXED_ROM_BUFFER) +set(OS_DEFINES _GNU_SOURCE COLOR_16_BIT COLOR_5_6_5 USE_VFS_FILE IOAPI_NO_64 FIXED_ROM_BUFFER) list(APPEND CORE_VFS_SRC ${CMAKE_SOURCE_DIR}/src/util/vfs/vfs-file.c ${CMAKE_SOURCE_DIR}/src/util/vfs/vfs-dirent.c ${CMAKE_SOURCE_DIR}/src/util/vfs/vfs-devlist.c) include_directories(${CMAKE_CURRENT_BINARY_DIR})
M src/util/table.csrc/util/table.c

@@ -12,7 +12,8 @@ #define LIST_INITIAL_SIZE 8

#define TABLE_INITIAL_SIZE 8 #define TABLE_COMPARATOR(LIST, INDEX) LIST->list[(INDEX)].key == key -#define HASH_TABLE_COMPARATOR(LIST, INDEX) LIST->list[(INDEX)].key == hash && strncmp(LIST->list[(INDEX)].stringKey, key, LIST->list[(INDEX)].keylen) == 0 +#define HASH_TABLE_STRNCMP_COMPARATOR(LIST, INDEX) LIST->list[(INDEX)].key == hash && strncmp(LIST->list[(INDEX)].stringKey, key, LIST->list[(INDEX)].keylen) == 0 +#define HASH_TABLE_MEMCMP_COMPARATOR(LIST, INDEX) LIST->list[(INDEX)].key == hash && memcmp(LIST->list[(INDEX)].stringKey, key, LIST->list[(INDEX)].keylen) == 0 #define TABLE_LOOKUP_START(COMPARATOR, LIST, KEY) \ uint32_t entry = (KEY) & (table->tableSize - 1); \

@@ -176,7 +177,16 @@

void* HashTableLookup(const struct Table* table, const char* key) { uint32_t hash = hash32(key, strlen(key), 0); const struct TableList* list; - TABLE_LOOKUP_START(HASH_TABLE_COMPARATOR, list, hash) { + TABLE_LOOKUP_START(HASH_TABLE_STRNCMP_COMPARATOR, list, hash) { + return lookupResult->value; + } TABLE_LOOKUP_END; + return 0; +} + +void* HashTableLookupBinary(const struct Table* table, const void* key, size_t keylen) { + uint32_t hash = hash32(key, keylen, 0); + const struct TableList* list; + TABLE_LOOKUP_START(HASH_TABLE_MEMCMP_COMPARATOR, list, hash) { return lookupResult->value; } TABLE_LOOKUP_END; return 0;

@@ -185,7 +195,7 @@

void HashTableInsert(struct Table* table, const char* key, void* value) { uint32_t hash = hash32(key, strlen(key), 0); struct TableList* list; - TABLE_LOOKUP_START(HASH_TABLE_COMPARATOR, list, hash) { + TABLE_LOOKUP_START(HASH_TABLE_STRNCMP_COMPARATOR, list, hash) { if (value != lookupResult->value) { if (table->deinitializer) { table->deinitializer(lookupResult->value);

@@ -203,10 +213,40 @@ ++list->nEntries;

++table->size; } +void HashTableInsertBinary(struct Table* table, const void* key, size_t keylen, void* value) { + uint32_t hash = hash32(key, keylen, 0); + struct TableList* list; + TABLE_LOOKUP_START(HASH_TABLE_MEMCMP_COMPARATOR, list, hash) { + if (value != lookupResult->value) { + if (table->deinitializer) { + table->deinitializer(lookupResult->value); + } + lookupResult->value = value; + } + return; + } TABLE_LOOKUP_END; + list = _resizeAsNeeded(table, list, hash); + list->list[list->nEntries].key = hash; + list->list[list->nEntries].stringKey = malloc(keylen); + memcpy(list->list[list->nEntries].stringKey, key, keylen); + list->list[list->nEntries].keylen = keylen; + list->list[list->nEntries].value = value; + ++list->nEntries; + ++table->size; +} + void HashTableRemove(struct Table* table, const char* key) { uint32_t hash = hash32(key, strlen(key), 0); struct TableList* list; - TABLE_LOOKUP_START(HASH_TABLE_COMPARATOR, list, hash) { + TABLE_LOOKUP_START(HASH_TABLE_STRNCMP_COMPARATOR, list, hash) { + _removeItemFromList(table, list, i); // TODO: Move i out of the macro + } TABLE_LOOKUP_END; +} + +void HashTableRemoveBinary(struct Table* table, const void* key, size_t keylen) { + uint32_t hash = hash32(key, keylen, 0); + struct TableList* list; + TABLE_LOOKUP_START(HASH_TABLE_MEMCMP_COMPARATOR, list, hash) { _removeItemFromList(table, list, i); // TODO: Move i out of the macro } TABLE_LOOKUP_END; }

@@ -238,6 +278,55 @@ for (j = 0; j < list->nEntries; ++j) {

handler(list->list[j].stringKey, list->list[j].value, user); } } +} + +const char* HashTableSearch(const struct Table* table, bool (predicate(const char* key, const void* value, const void* user)), const void* user) { + size_t i; + const char* result = NULL; + for (i = 0; i < table->tableSize; ++i) { + const struct TableList* list = &table->table[i]; + size_t j; + for (j = 0; j < list->nEntries; ++j) { + if (predicate(list->list[j].stringKey, list->list[j].value, user)) { + return list->list[j].stringKey; + } + } + } + return result; +} + +static bool HashTableRefEqual(const char* key, const void* value, const void* user) { + UNUSED(key); + return value == user; +} + +const char* HashTableSearchPointer(const struct Table* table, const void* value) { + return HashTableSearch(table, HashTableRefEqual, value); +} + +struct HashTableSearchParam { + const void* value; + size_t bytes; +}; + +static bool HashTableMemcmp(const char* key, const void* value, const void* user) { + UNUSED(key); + const struct HashTableSearchParam* ref = user; + return memcmp(value, ref->value, ref->bytes) == 0; +} + +const char* HashTableSearchData(const struct Table* table, const void* value, const size_t bytes) { + struct HashTableSearchParam ref = { value, bytes }; + return HashTableSearch(table, HashTableMemcmp, &ref); +} + +static bool HashTableStrcmp(const char* key, const void* value, const void* user) { + UNUSED(key); + return strcmp(value, user) == 0; +} + +const char* HashTableSearchString(const struct Table* table, const char* value) { + return HashTableSearch(table, HashTableStrcmp, value); } size_t HashTableSize(const struct Table* table) {