mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'master' into medusa
This commit is contained in:
commit
ef9081da7e
63
CHANGES
63
CHANGES
|
@ -37,19 +37,13 @@ Features:
|
|||
- 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 @@ Emulation fixes:
|
|||
- 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:
|
||||
|
|
|
@ -71,7 +71,7 @@ if(NOT LIBMGBA_ONLY)
|
|||
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 @@ if(DEFINED 3DS OR DEFINED PSP2 OR DEFINED WII OR DEFINED SWITCH)
|
|||
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()
|
||||
|
@ -287,10 +283,6 @@ if(WII)
|
|||
add_definitions(-U__STRICT_ANSI__)
|
||||
endif()
|
||||
|
||||
if(3DS OR WII)
|
||||
add_definitions(-D_GNU_SOURCE)
|
||||
endif()
|
||||
|
||||
include(CheckCCompilerFlag)
|
||||
include(CheckFunctionExists)
|
||||
include(CheckIncludeFiles)
|
||||
|
@ -440,6 +432,7 @@ if(CMAKE_SYSTEM_NAME MATCHES ".*BSD|DragonFly")
|
|||
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 @@ if(BUILD_GLES2)
|
|||
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 @@ else()
|
|||
endif()
|
||||
|
||||
if(BUILD_GL)
|
||||
add_definitions(-DBUILD_GL)
|
||||
add_definitions(-DBUILD_GL -DBUILD_GLES2 -DBUILD_GLES3)
|
||||
endif()
|
||||
|
||||
if(BUILD_GLES2)
|
||||
|
|
|
@ -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 @@ Missing features on DS are
|
|||
|
||||
<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/
|
||||
|
|
|
@ -15,11 +15,7 @@
|
|||
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 @@ static inline int MutexUnlock(Mutex* mutex) {
|
|||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,12 +35,19 @@ void HashTableInit(struct Table* table, size_t initialSize, void (deinitializer(
|
|||
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
|
||||
|
|
|
@ -22,4 +22,16 @@
|
|||
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -14,6 +14,8 @@ CXX_GUARD_START
|
|||
#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 @@ enum mStackTraceMode {
|
|||
};
|
||||
|
||||
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 mStackTraceDeinit(struct mStackTrace* stack);
|
|||
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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -10,9 +10,12 @@
|
|||
|
||||
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,
|
||||
GB_VIDEO_VERTICAL_PIXELS = 144,
|
||||
|
@ -160,7 +163,10 @@ struct GBVideo {
|
|||
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);
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ CXX_GUARD_START
|
|||
#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>
|
||||
|
|
|
@ -226,6 +226,8 @@ struct GBAVideo {
|
|||
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);
|
||||
|
|
|
@ -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>
|
||||
|
@ -16,14 +17,56 @@
|
|||
|
||||
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 @@ static bool ARMDebuggerUpdateStackTraceInternal(struct mDebuggerPlatform* d, uin
|
|||
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 @@ static bool ARMDebuggerUpdateStackTraceInternal(struct mDebuggerPlatform* d, uin
|
|||
//
|
||||
// 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 @@ static bool ARMDebuggerUpdateStackTraceInternal(struct mDebuggerPlatform* d, uin
|
|||
}
|
||||
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 @@ static bool ARMDebuggerUpdateStackTraceInternal(struct mDebuggerPlatform* d, uin
|
|||
}
|
||||
|
||||
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) {
|
||||
mStackTracePop(stack);
|
||||
if (!(debugger->stackTraceMode & STACK_TRACE_BREAK_ON_RETURN)) {
|
||||
return false;
|
||||
}
|
||||
if (!frame->breakWhenFinished && !(debugger->stackTraceMode & STACK_TRACE_BREAK_ON_RETURN)) {
|
||||
mStackTracePop(stack);
|
||||
return false;
|
||||
}
|
||||
frame->finished = true;
|
||||
}
|
||||
struct mDebuggerEntryInfo debuggerInfo = {
|
||||
.address = pc,
|
||||
|
@ -403,21 +465,18 @@ static void ARMDebuggerTrace(struct mDebuggerPlatform* d, char* out, size_t* len
|
|||
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: "));
|
||||
|
|
|
@ -513,12 +513,12 @@ ATTRIBUTE_NOINLINE static void _neutralS(struct ARMCore* cpu, int32_t d) {
|
|||
#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, \
|
||||
|
|
|
@ -6,6 +6,10 @@ set(SOURCE_FILES
|
|||
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)
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -13,7 +13,7 @@
|
|||
#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 @@ static void _source(struct CLIDebugger*, struct CLIDebugVector*);
|
|||
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 @@ static struct CLIDebuggerCommandSummary _debuggerCommands[] = {
|
|||
{ "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 @@ static struct CLIDebuggerCommandAlias _debuggerCommandAliases[] = {
|
|||
{ "p/x", "print/x" },
|
||||
{ "q", "quit" },
|
||||
{ "w", "watch" },
|
||||
{ ".", "source" },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
|
@ -940,7 +945,7 @@ static int _tryCommands(struct CLIDebugger* debugger, struct CLIDebuggerCommandS
|
|||
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 @@ static void _commandLine(struct mDebugger* debugger) {
|
|||
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 @@ static void _backtrace(struct CLIDebugger* debugger, struct CLIDebugVector* dv)
|
|||
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 @@ static void _setStackTraceMode(struct CLIDebugger* debugger, struct CLIDebugVect
|
|||
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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#include <mgba/internal/debugger/gdb-stub.h>
|
||||
#endif
|
||||
|
||||
#if ENABLE_SCRIPTING
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
#include <mgba/core/scripting.h>
|
||||
#endif
|
||||
|
||||
|
|
|
@ -82,6 +82,7 @@ static void _gdbStubEntered(struct mDebugger* debugger, enum mDebuggerEntryReaso
|
|||
snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGILL);
|
||||
break;
|
||||
case DEBUGGER_ENTER_ATTACHED:
|
||||
case DEBUGGER_ENTER_STACK:
|
||||
return;
|
||||
}
|
||||
_sendMessage(stub);
|
||||
|
|
|
@ -4,6 +4,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/debugger/stack-trace.h>
|
||||
#include <mgba/internal/debugger/symbols.h>
|
||||
|
||||
#include <mgba/core/core.h>
|
||||
|
||||
|
@ -40,8 +41,11 @@ 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* 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 @@ struct mStackFrame* mStackTracePush(struct mStackTrace* stack, uint32_t pc, uint
|
|||
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 @@ struct mStackFrame* mStackTraceGetFrame(struct mStackTrace* stack, uint32_t fram
|
|||
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 @@ void mStackTraceFormatFrame(struct mStackTrace* stack, uint32_t frame, char* out
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#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 mDebuggerSymbol {
|
|||
|
||||
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 @@ bool mDebuggerSymbolLookup(const struct mDebuggerSymbols* st, const char* name,
|
|||
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) {
|
||||
|
|
|
@ -287,6 +287,9 @@ static void _log(struct mLogger* logger, int category, enum mLogLevel level, con
|
|||
guiLogger->vf = NULL;
|
||||
}
|
||||
}
|
||||
#ifdef GEKKO
|
||||
puts(log2);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void _updateLoading(size_t read, size_t size, void* context) {
|
||||
|
|
|
@ -68,9 +68,12 @@ static const struct mCoreMemoryBlock _GBCMemoryBlocks[] = {
|
|||
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 @@ static bool _GBCoreInit(struct mCore* core) {
|
|||
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,9 +109,16 @@ static bool _GBCoreInit(struct mCore* core) {
|
|||
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;
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
#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 @@ void GBVideoInit(struct GBVideo* video) {
|
|||
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 @@ void GBVideoReset(struct GBVideo* video) {
|
|||
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];
|
||||
|
@ -131,6 +115,11 @@ void GBVideoReset(struct GBVideo* video) {
|
|||
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 @@ void GBVideoDeinit(struct GBVideo* video) {
|
|||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#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 @@ struct mVideoLogContext;
|
|||
|
||||
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 @@ static bool _GBACoreInit(struct mCore* core) {
|
|||
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
|
|||
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 @@ static void _GBACoreReloadConfigOption(struct mCore* core, const char* option, c
|
|||
}
|
||||
|
||||
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 @@ static void _GBACoreReloadConfigOption(struct mCore* core, const char* option, c
|
|||
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 _GBACoreReloadConfigOption(struct mCore* core, const char* option, c
|
|||
}
|
||||
|
||||
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 @@ static void _GBACoreSetVideoBuffer(struct mCore* core, color_t* buffer, size_t s
|
|||
}
|
||||
|
||||
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 @@ static void _GBACoreReset(struct mCore* core) {
|
|||
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;
|
||||
|
|
|
@ -130,7 +130,9 @@ void GBAUnloadROM(struct GBA* gba) {
|
|||
if (gba->yankedRomSize) {
|
||||
gba->yankedRomSize = 0;
|
||||
}
|
||||
#if !defined(FIXED_ROM_BUFFER) && !defined(__wii__)
|
||||
mappedMemoryFree(gba->memory.rom, SIZE_CART0);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (gba->romVf) {
|
||||
|
|
|
@ -527,6 +527,8 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) {
|
|||
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:
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 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>
|
||||
|
|
|
@ -30,6 +30,7 @@ void GBASIOLockstepInit(struct GBASIOLockstep* lockstep) {
|
|||
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 GBASIOLock
|
|||
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 GBASIOLock
|
|||
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 @@ void GBASIOLockstepDetachNode(struct GBASIOLockstep* lockstep, struct GBASIOLock
|
|||
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 @@ bool GBASIOLockstepNodeLoad(struct GBASIODriver* driver) {
|
|||
}
|
||||
break;
|
||||
case SIO_NORMAL_32:
|
||||
ATOMIC_ADD(node->p->attachedNormal, 1);
|
||||
node->d.writeRegister = GBASIOLockstepNodeNormalWriteRegister;
|
||||
break;
|
||||
default:
|
||||
|
@ -132,6 +140,9 @@ bool GBASIOLockstepNodeUnload(struct GBASIODriver* driver) {
|
|||
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 @@ bool GBASIOLockstepNodeUnload(struct GBASIODriver* driver) {
|
|||
|
||||
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 @@ static uint16_t GBASIOLockstepNodeMultiWriteRegister(struct GBASIODriver* driver
|
|||
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 @@ static uint16_t GBASIOLockstepNodeMultiWriteRegister(struct GBASIODriver* driver
|
|||
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 @@ static void _finishTransfer(struct GBASIOLockstepNode* node) {
|
|||
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 @@ static int32_t _masterUpdate(struct GBASIOLockstepNode* node) {
|
|||
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 @@ static uint16_t GBASIOLockstepNodeNormalWriteRegister(struct GBASIODriver* drive
|
|||
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);
|
||||
|
|
|
@ -55,23 +55,8 @@ MGBA_EXPORT const int GBAVideoObjSizes[16][2] = {
|
|||
{ 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 @@ void GBAVideoReset(struct GBAVideo* video) {
|
|||
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 @@ void GBAVideoReset(struct GBAVideo* video) {
|
|||
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 @@ void GBAVideoDeinit(struct GBAVideo* video) {
|
|||
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 @@ void _startHdraw(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
|||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -7,7 +7,7 @@ else()
|
|||
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")
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#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 @@ static bool _initGpu(void) {
|
|||
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;
|
||||
}
|
||||
|
@ -130,6 +145,11 @@ static bool _initGpu(void) {
|
|||
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 u32 _setupTex(int out, bool faded) {
|
|||
|
||||
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 @@ static void _drawTex(struct mCore* core, bool faded, bool both) {
|
|||
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 @@ static void _drawTex(struct mCore* core, bool faded, bool both) {
|
|||
screen_h = 512;
|
||||
break;
|
||||
}
|
||||
int wide = isWide ? 2 : 1;
|
||||
|
||||
unsigned corew, coreh;
|
||||
core->desiredVideoDimensions(core, &corew, &coreh);
|
||||
|
@ -483,24 +505,16 @@ static void _drawTex(struct mCore* core, bool faded, bool both) {
|
|||
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 @@ static void _drawTex(struct mCore* core, bool faded, bool both) {
|
|||
}
|
||||
ctrFlushBatch();
|
||||
|
||||
innerw = corew;
|
||||
innerh = coreh;
|
||||
int innerw = corew;
|
||||
int innerh = coreh;
|
||||
corew = w;
|
||||
coreh = h;
|
||||
screen_h = 240;
|
||||
|
@ -540,7 +554,7 @@ static void _drawTex(struct mCore* core, bool faded, bool both) {
|
|||
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 @@ static void _drawTex(struct mCore* core, bool faded, bool both) {
|
|||
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 @@ int main() {
|
|||
|
||||
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;
|
||||
|
|
|
@ -159,6 +159,7 @@ static void mGLES2ContextInit(struct VideoBackend* v, WHandle handle) {
|
|||
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 @@ static void mGLES2ContextInit(struct VideoBackend* v, WHandle handle) {
|
|||
glBindBuffer(GL_ARRAY_BUFFER, context->vbo);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
glDeleteFramebuffers(1, &context->finalShader.fbo);
|
||||
glDeleteTextures(1, &context->finalShader.tex);
|
||||
|
@ -305,9 +307,12 @@ void _drawShader(struct mGLES2Context* context, struct mGLES2Shader* shader) {
|
|||
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 @@ void mGLES2ContextDrawFrame(struct VideoBackend* v) {
|
|||
}
|
||||
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 @@ void mGLES2ShaderInit(struct mGLES2Shader* shader, const char* vs, const char* f
|
|||
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 @@ void mGLES2ShaderInit(struct mGLES2Shader* shader, const char* vs, const char* f
|
|||
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 @@ void mGLES2ShaderDeinit(struct mGLES2Shader* shader) {
|
|||
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 @@ void mGLES2ShaderAttach(struct mGLES2Context* context, struct mGLES2Shader* shad
|
|||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,9 @@ CXX_GUARD_START
|
|||
#endif
|
||||
#else
|
||||
#include <GLES2/gl2.h>
|
||||
#ifdef BUILD_GLES3
|
||||
#include <GLES3/gl3.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "platform/video-backend.h"
|
||||
|
|
|
@ -52,7 +52,7 @@ static enum 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 @@ void mPSP2Setup(struct mGUIRunner* runner) {
|
|||
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 @@ void mPSP2Teardown(struct mGUIRunner* 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 @@ void _drawTex(vita2d_texture* t, unsigned width, unsigned height, bool faded, bo
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -130,8 +130,6 @@ void AssetTile::setFlip(bool h, bool v) {
|
|||
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);
|
||||
|
|
|
@ -144,8 +144,8 @@ QImage AssetView::compositeObj(const ObjInfo& objInfo) {
|
|||
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 @@ bool AssetView::lookupObjGBA(int id, struct ObjInfo* info) {
|
|||
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 @@ bool AssetView::lookupObjGB(int id, struct ObjInfo* info) {
|
|||
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)) {
|
||||
|
|
|
@ -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 @@ if(NOT DEFINED DATADIR)
|
|||
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 @@ if(WIN32)
|
|||
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 @@ if(QT_STATIC)
|
|||
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)
|
||||
|
|
|
@ -42,11 +42,10 @@ QVariant CheatsModel::data(const QModelIndex& index, int role) const {
|
|||
}
|
||||
}
|
||||
|
||||
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 @@ QVariant CheatsModel::data(const QModelIndex& index, int role) const {
|
|||
}
|
||||
|
||||
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 @@ Qt::ItemFlags CheatsModel::flags(const QModelIndex& index) const {
|
|||
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 @@ mCheatSet* CheatsModel::itemAt(const QModelIndex& index) {
|
|||
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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -10,21 +10,21 @@
|
|||
|
||||
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 @@ Display* Display::create(QWidget* parent) {
|
|||
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);
|
||||
|
|
|
@ -27,12 +27,8 @@ Q_OBJECT
|
|||
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 @@ public:
|
|||
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();
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 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 @@
|
|||
#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 @@ PainterGL::PainterGL(QWindow* surface, QOpenGLContext* parent, int forceVersion)
|
|||
#ifdef BUILD_GL
|
||||
mGLContext* glBackend;
|
||||
#endif
|
||||
#ifdef BUILD_GLES2
|
||||
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
|
||||
mGLES2Context* gl2Backend;
|
||||
#endif
|
||||
|
||||
|
@ -287,7 +287,7 @@ PainterGL::PainterGL(QWindow* surface, QOpenGLContext* parent, int forceVersion)
|
|||
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 @@ PainterGL::PainterGL(QWindow* surface, QOpenGLContext* parent, int forceVersion)
|
|||
};
|
||||
|
||||
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 @@ PainterGL::PainterGL(QWindow* surface, QOpenGLContext* parent, int forceVersion)
|
|||
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 @@ void PainterGL::start() {
|
|||
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 @@ void PainterGL::performDraw() {
|
|||
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::performDraw() {
|
|||
}
|
||||
|
||||
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 @@ void PainterGL::enqueue(const uint32_t* backing) {
|
|||
} 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 @@ void PainterGL::dequeue() {
|
|||
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 @@ VideoShader* PainterGL::shaders() {
|
|||
}
|
||||
|
||||
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;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 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"
|
||||
|
||||
|
@ -25,6 +25,8 @@
|
|||
#include <QThread>
|
||||
#include <QTimer>
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "VideoProxy.h"
|
||||
|
||||
#include "platform/video-backend.h"
|
||||
|
@ -119,6 +121,7 @@ private:
|
|||
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;
|
||||
|
|
|
@ -97,6 +97,7 @@ void DisplayQt::paintEvent(QPaintEvent*) {
|
|||
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()) {
|
||||
|
|
|
@ -42,8 +42,8 @@ protected:
|
|||
|
||||
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;
|
||||
|
|
|
@ -85,7 +85,7 @@ void GIFView::stopRecording() {
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -221,6 +221,7 @@ bool MultiplayerController::attachGame(CoreController* controller) {
|
|||
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 @@ void MultiplayerController::detachGame(CoreController* controller) {
|
|||
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;
|
||||
|
|
|
@ -30,7 +30,7 @@ public:
|
|||
|
||||
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; }
|
||||
|
|
|
@ -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 @@ QModelIndex ShortcutModel::parent(const QModelIndex& index) const {
|
|||
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 @@ void ShortcutModel::addRowNamed(const QString& name) {
|
|||
endInsertRows();
|
||||
}
|
||||
|
||||
void ShortcutModel::clearMenu(const QString& name) {
|
||||
void ShortcutModel::clearMenu(const QString&) {
|
||||
// TODO
|
||||
beginResetModel();
|
||||
endResetModel();
|
||||
|
|
|
@ -599,7 +599,7 @@ void Window::consoleOpen() {
|
|||
}
|
||||
#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 @@ void Window::gameFailed() {
|
|||
fail->show();
|
||||
}
|
||||
|
||||
void Window::unimplementedBiosCall(int call) {
|
||||
void Window::unimplementedBiosCall(int) {
|
||||
// TODO: Mention which call?
|
||||
if (m_hitUnimplementedBiosCall) {
|
||||
return;
|
||||
}
|
||||
|
@ -1192,7 +1193,7 @@ void Window::setupMenu(QMenuBar* menubar) {
|
|||
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 @@ void Window::setupMenu(QMenuBar* menubar) {
|
|||
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 @@ void Window::setupMenu(QMenuBar* menubar) {
|
|||
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 @@ void Window::setupMenu(QMenuBar* menubar) {
|
|||
#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 @@ void Window::setupMenu(QMenuBar* menubar) {
|
|||
}, "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 @@ void Window::setupMenu(QMenuBar* menubar) {
|
|||
|
||||
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 @@ void Window::setupMenu(QMenuBar* menubar) {
|
|||
|
||||
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 @@ void Window::setupMenu(QMenuBar* menubar) {
|
|||
|
||||
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 @@ void Window::setupMenu(QMenuBar* menubar) {
|
|||
}, "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 @@ Action* Window::addGameAction(const QString& visibleName, const QString& name, A
|
|||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -164,6 +164,7 @@ private:
|
|||
|
||||
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);
|
||||
|
|
|
@ -210,6 +210,9 @@ int mSDLRun(struct mSDLRenderer* renderer, struct mArguments* args) {
|
|||
#ifdef ENABLE_PYTHON
|
||||
mPythonSetup(bridge);
|
||||
#endif
|
||||
#ifdef USE_DEBUGGERS
|
||||
CLIDebuggerScriptEngineInstall(bridge);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef USE_DEBUGGERS
|
||||
|
@ -223,7 +226,7 @@ int mSDLRun(struct mSDLRenderer* renderer, struct mArguments* args) {
|
|||
#endif
|
||||
mDebuggerAttach(debugger, renderer->core);
|
||||
mDebuggerEnter(debugger, DEBUGGER_ENTER_MANUAL, NULL);
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
mScriptBridgeSetDebugger(bridge, debugger);
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ enum CInemaRebaseline {
|
|||
};
|
||||
|
||||
struct CInemaTest {
|
||||
char directory[MAX_TEST];
|
||||
char directory[PATH_MAX];
|
||||
char filename[MAX_TEST];
|
||||
char name[MAX_TEST];
|
||||
enum CInemaStatus status;
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <switch.h>
|
||||
#endif
|
||||
#ifdef GEKKO
|
||||
#define asm __asm__
|
||||
#include <fat.h>
|
||||
#include <gccore.h>
|
||||
#ifdef FIXED_ROM_BUFFER
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -12,7 +12,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 HashTableDeinit(struct Table* table) {
|
|||
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* HashTableLookup(const struct Table* table, const char* key) {
|
|||
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 @@ void HashTableInsert(struct Table* table, const char* key, void* value) {
|
|||
++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;
|
||||
}
|
||||
|
@ -240,6 +280,55 @@ void HashTableEnumerate(const struct Table* table, void (handler(const char* key
|
|||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
return table->size;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue