Merge branch 'master' into medusa
2
CHANGES
|
@ -88,6 +88,8 @@ Other fixes:
|
|||
- SM83: Simplify register pair access on big endian
|
||||
- Wii: Fix pixelated filtering on interframe blending (fixes mgba.io/i/1830)
|
||||
Misc:
|
||||
- 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
|
||||
|
|
|
@ -13,7 +13,7 @@ set(CMAKE_C_STANDARD 99)
|
|||
if(NOT MSVC)
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_C_EXTENSIONS OFF)
|
||||
if(SWITCH)
|
||||
if(SWITCH OR 3DS)
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_C_EXTENSIONS ON)
|
||||
elseif(CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_LESS "4.3")
|
||||
|
@ -297,6 +297,7 @@ include(CheckIncludeFiles)
|
|||
check_function_exists(strdup HAVE_STRDUP)
|
||||
check_function_exists(strndup HAVE_STRNDUP)
|
||||
check_function_exists(strlcpy HAVE_STRLCPY)
|
||||
check_function_exists(vasprintf HAVE_VASPRINTF)
|
||||
if(NOT DEFINED PSP2)
|
||||
check_function_exists(localtime_r HAVE_LOCALTIME_R)
|
||||
endif()
|
||||
|
@ -379,6 +380,10 @@ if(HAVE_STRLCPY)
|
|||
list(APPEND FUNCTION_DEFINES HAVE_STRLCPY)
|
||||
endif()
|
||||
|
||||
if(HAVE_VASPRINTF)
|
||||
list(APPEND FUNCTION_DEFINES HAVE_VASPRINTF)
|
||||
endif()
|
||||
|
||||
if(HAVE_LOCALTIME_R)
|
||||
list(APPEND FUNCTION_DEFINES HAVE_LOCALTIME_R)
|
||||
endif()
|
||||
|
@ -988,6 +993,9 @@ endif()
|
|||
if(NOT USE_CMOCKA)
|
||||
set(BUILD_SUITE OFF)
|
||||
endif()
|
||||
if(BUILD_TEST OR BUILD_SUITE OR BUILD_CINEMA)
|
||||
enable_testing()
|
||||
endif()
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/platform/test ${CMAKE_CURRENT_BINARY_DIR}/test)
|
||||
|
||||
if(BUILD_EXAMPLE)
|
||||
|
|
After Width: | Height: | Size: 879 B |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 411 B |
After Width: | Height: | Size: 711 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 846 B |
|
@ -24,6 +24,7 @@ typedef THREAD_ENTRY (*ThreadEntry)(void*);
|
|||
typedef pthread_t Thread;
|
||||
typedef pthread_mutex_t Mutex;
|
||||
typedef pthread_cond_t Condition;
|
||||
typedef pthread_key_t ThreadLocal;
|
||||
|
||||
static inline int MutexInit(Mutex* mutex) {
|
||||
return pthread_mutex_init(mutex, 0);
|
||||
|
@ -101,6 +102,18 @@ static inline int ThreadSetName(const char* name) {
|
|||
#endif
|
||||
}
|
||||
|
||||
static inline void ThreadLocalInitKey(ThreadLocal* key) {
|
||||
pthread_key_create(key, 0);
|
||||
}
|
||||
|
||||
static inline void ThreadLocalSetKey(ThreadLocal key, void* value) {
|
||||
pthread_setspecific(key, value);
|
||||
}
|
||||
|
||||
static inline void* ThreadLocalGetValue(ThreadLocal key) {
|
||||
return pthread_getspecific(key);
|
||||
}
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
||||
|
|
|
@ -17,6 +17,7 @@ typedef struct {
|
|||
} Condition;
|
||||
#define THREAD_ENTRY int
|
||||
typedef THREAD_ENTRY (*ThreadEntry)(void*);
|
||||
typedef int ThreadLocal;
|
||||
|
||||
static inline int MutexInit(Mutex* mutex) {
|
||||
Mutex id = sceKernelCreateMutex("mutex", 0, 0, 0);
|
||||
|
@ -143,4 +144,19 @@ static inline int ThreadSetName(const char* name) {
|
|||
UNUSED(name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline void ThreadLocalInitKey(ThreadLocal* key) {
|
||||
static int base = 0x90;
|
||||
*key = __atomic_fetch_add(&base, 1, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline void ThreadLocalSetKey(ThreadLocal key, void* value) {
|
||||
void** tls = sceKernelGetTLSAddr(key);
|
||||
*tls = value;
|
||||
}
|
||||
|
||||
static inline void* ThreadLocalGetValue(ThreadLocal key) {
|
||||
void** tls = sceKernelGetTLSAddr(key);
|
||||
return *tls;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -16,6 +16,7 @@ typedef THREAD_ENTRY ThreadEntry(LPVOID);
|
|||
typedef HANDLE Thread;
|
||||
typedef CRITICAL_SECTION Mutex;
|
||||
typedef CONDITION_VARIABLE Condition;
|
||||
typedef DWORD ThreadLocal;
|
||||
|
||||
static inline int MutexInit(Mutex* mutex) {
|
||||
InitializeCriticalSection(mutex);
|
||||
|
@ -88,4 +89,16 @@ static inline int ThreadSetName(const char* name) {
|
|||
return -1;
|
||||
}
|
||||
|
||||
static inline void ThreadLocalInitKey(ThreadLocal* key) {
|
||||
*key = TlsAlloc();
|
||||
}
|
||||
|
||||
static inline void ThreadLocalSetKey(ThreadLocal key, void* value) {
|
||||
TlsSetValue(key, value);
|
||||
}
|
||||
|
||||
static inline void* ThreadLocalGetValue(ThreadLocal key) {
|
||||
return TlsGetValue(key);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -45,6 +45,7 @@ const char* hex4(const char* line, uint8_t* out);
|
|||
void rtrim(char* string);
|
||||
|
||||
ssize_t parseQuotedString(const char* unparsed, ssize_t unparsedLen, char* parsed, ssize_t parsedLen);
|
||||
bool wildcard(const char* search, const char* string);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
|
|
|
@ -11,6 +11,12 @@
|
|||
CXX_GUARD_START
|
||||
|
||||
#ifndef DISABLE_THREADING
|
||||
#if __STDC_VERSION__ >= 201112L
|
||||
#define ThreadLocal _Thread_local void*
|
||||
#define ThreadLocalInitKey(X)
|
||||
#define ThreadLocalSetKey(K, V) K = V
|
||||
#define ThreadLocalGetValue(K) K
|
||||
#endif
|
||||
#ifdef USE_PTHREADS
|
||||
#include <mgba-util/platform/posix/threading.h>
|
||||
#elif defined(_WIN32)
|
||||
|
@ -40,6 +46,7 @@ typedef void* Thread;
|
|||
typedef void* Mutex;
|
||||
#endif
|
||||
typedef void* Condition;
|
||||
typedef int ThreadLocal;
|
||||
|
||||
static inline int MutexInit(Mutex* mutex) {
|
||||
UNUSED(mutex);
|
||||
|
@ -93,6 +100,20 @@ static inline int ConditionWake(Condition* cond) {
|
|||
UNUSED(cond);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void ThreadLocalInitKey(ThreadLocal* key) {
|
||||
UNUSED(key);
|
||||
}
|
||||
|
||||
static inline void ThreadLocalSetKey(ThreadLocal key, void* value) {
|
||||
UNUSED(key);
|
||||
UNUSED(value);
|
||||
}
|
||||
|
||||
static inline void* ThreadLocalGetValue(ThreadLocal key) {
|
||||
UNUSED(key);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
CXX_GUARD_END
|
||||
|
|
|
@ -235,6 +235,8 @@ DECL_BIT(GBSerializedCpuFlags, Condition, 0);
|
|||
DECL_BIT(GBSerializedCpuFlags, IrqPending, 1);
|
||||
DECL_BIT(GBSerializedCpuFlags, DoubleSpeed, 2);
|
||||
DECL_BIT(GBSerializedCpuFlags, EiPending, 3);
|
||||
DECL_BIT(GBSerializedCpuFlags, Halted, 4);
|
||||
DECL_BIT(GBSerializedCpuFlags, Blocked, 5);
|
||||
|
||||
DECL_BITFIELD(GBSerializedTimerFlags, uint8_t);
|
||||
DECL_BIT(GBSerializedTimerFlags, IrqPending, 0);
|
||||
|
|
|
@ -236,6 +236,7 @@ DECL_BITFIELD(GBASerializedMiscFlags, uint32_t);
|
|||
DECL_BIT(GBASerializedMiscFlags, Halted, 0);
|
||||
DECL_BIT(GBASerializedMiscFlags, POSTFLG, 1);
|
||||
DECL_BIT(GBASerializedMiscFlags, IrqPending, 2);
|
||||
DECL_BIT(GBASerializedMiscFlags, Blocked, 3);
|
||||
|
||||
struct GBASerializedState {
|
||||
uint32_t versionMagic;
|
||||
|
|
|
@ -16,23 +16,22 @@
|
|||
#ifndef DISABLE_THREADING
|
||||
|
||||
static const float _defaultFPSTarget = 60.f;
|
||||
static ThreadLocal _contextKey;
|
||||
|
||||
#ifdef USE_PTHREADS
|
||||
static pthread_key_t _contextKey;
|
||||
static pthread_once_t _contextOnce = PTHREAD_ONCE_INIT;
|
||||
|
||||
static void _createTLS(void) {
|
||||
pthread_key_create(&_contextKey, 0);
|
||||
ThreadLocalInitKey(&_contextKey);
|
||||
}
|
||||
#elif _WIN32
|
||||
static DWORD _contextKey;
|
||||
static INIT_ONCE _contextOnce = INIT_ONCE_STATIC_INIT;
|
||||
|
||||
static BOOL CALLBACK _createTLS(PINIT_ONCE once, PVOID param, PVOID* context) {
|
||||
UNUSED(once);
|
||||
UNUSED(param);
|
||||
UNUSED(context);
|
||||
_contextKey = TlsAlloc();
|
||||
ThreadLocalInitKey(&_contextKey);
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
@ -144,12 +143,11 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
|
|||
struct mCoreThread* threadContext = context;
|
||||
#ifdef USE_PTHREADS
|
||||
pthread_once(&_contextOnce, _createTLS);
|
||||
pthread_setspecific(_contextKey, threadContext);
|
||||
#elif _WIN32
|
||||
InitOnceExecuteOnce(&_contextOnce, _createTLS, NULL, 0);
|
||||
TlsSetValue(_contextKey, threadContext);
|
||||
#endif
|
||||
|
||||
ThreadLocalSetKey(_contextKey, threadContext);
|
||||
ThreadSetName("CPU Thread");
|
||||
|
||||
#if !defined(_WIN32) && defined(USE_PTHREADS)
|
||||
|
@ -620,21 +618,14 @@ void mCoreThreadStopWaiting(struct mCoreThread* threadContext) {
|
|||
MutexUnlock(&threadContext->impl->stateMutex);
|
||||
}
|
||||
|
||||
struct mCoreThread* mCoreThreadGet(void) {
|
||||
#ifdef USE_PTHREADS
|
||||
struct mCoreThread* mCoreThreadGet(void) {
|
||||
pthread_once(&_contextOnce, _createTLS);
|
||||
return pthread_getspecific(_contextKey);
|
||||
}
|
||||
#elif _WIN32
|
||||
struct mCoreThread* mCoreThreadGet(void) {
|
||||
InitOnceExecuteOnce(&_contextOnce, _createTLS, NULL, 0);
|
||||
return TlsGetValue(_contextKey);
|
||||
}
|
||||
#else
|
||||
struct mCoreThread* mCoreThreadGet(void) {
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
return ThreadLocalGetValue(_contextKey);
|
||||
}
|
||||
|
||||
static void _mCoreLog(struct mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args) {
|
||||
UNUSED(logger);
|
||||
|
|
|
@ -681,7 +681,7 @@ void GBProcessEvents(struct SM83Core* cpu) {
|
|||
nextEvent = cycles;
|
||||
do {
|
||||
nextEvent = mTimingTick(&gb->timing, nextEvent);
|
||||
} while (gb->cpuBlocked);
|
||||
} while (gb->cpuBlocked && !gb->earlyExit);
|
||||
cpu->nextEvent = nextEvent;
|
||||
|
||||
if (cpu->halted) {
|
||||
|
@ -695,6 +695,9 @@ void GBProcessEvents(struct SM83Core* cpu) {
|
|||
}
|
||||
} while (cpu->cycles >= cpu->nextEvent);
|
||||
gb->earlyExit = false;
|
||||
if (gb->cpuBlocked) {
|
||||
cpu->cycles = cpu->nextEvent;
|
||||
}
|
||||
}
|
||||
|
||||
void GBSetInterrupts(struct SM83Core* cpu, bool enable) {
|
||||
|
|
|
@ -56,6 +56,8 @@ void GBSerialize(struct GB* gb, struct GBSerializedState* state) {
|
|||
flags = GBSerializedCpuFlagsSetIrqPending(flags, gb->cpu->irqPending);
|
||||
flags = GBSerializedCpuFlagsSetDoubleSpeed(flags, gb->doubleSpeed);
|
||||
flags = GBSerializedCpuFlagsSetEiPending(flags, mTimingIsScheduled(&gb->timing, &gb->eiPending));
|
||||
flags = GBSerializedCpuFlagsSetHalted(flags, gb->cpu->halted);
|
||||
flags = GBSerializedCpuFlagsSetBlocked(flags, gb->cpuBlocked);
|
||||
STORE_32LE(flags, 0, &state->cpu.flags);
|
||||
STORE_32LE(gb->eiPending.when - mTimingCurrentTime(&gb->timing), 0, &state->cpu.eiPending);
|
||||
|
||||
|
@ -173,6 +175,9 @@ bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state) {
|
|||
gb->cpu->condition = GBSerializedCpuFlagsGetCondition(flags);
|
||||
gb->cpu->irqPending = GBSerializedCpuFlagsGetIrqPending(flags);
|
||||
gb->doubleSpeed = GBSerializedCpuFlagsGetDoubleSpeed(flags);
|
||||
gb->cpu->halted = GBSerializedCpuFlagsGetHalted(flags);
|
||||
gb->cpuBlocked = GBSerializedCpuFlagsGetBlocked(flags);
|
||||
|
||||
gb->audio.timingFactor = gb->doubleSpeed + 1;
|
||||
|
||||
LOAD_32LE(gb->cpu->cycles, 0, &state->cpu.cycles);
|
||||
|
|
|
@ -379,6 +379,7 @@ void _updateFrameCount(struct mTiming* timing, void* context, uint32_t cyclesLat
|
|||
GBFrameEnded(video->p);
|
||||
mCoreSyncPostFrame(video->p->sync);
|
||||
++video->frameCounter;
|
||||
video->p->earlyExit = true;
|
||||
|
||||
GBFrameStarted(video->p);
|
||||
}
|
||||
|
|
|
@ -286,7 +286,7 @@ static void GBAProcessEvents(struct ARMCore* cpu) {
|
|||
}
|
||||
#endif
|
||||
nextEvent = mTimingTick(&gba->timing, cycles < nextEvent ? nextEvent : cycles);
|
||||
} while (gba->cpuBlocked);
|
||||
} while (gba->cpuBlocked && !gba->earlyExit);
|
||||
|
||||
cpu->nextEvent = nextEvent;
|
||||
if (cpu->halted) {
|
||||
|
@ -305,11 +305,9 @@ static void GBAProcessEvents(struct ARMCore* cpu) {
|
|||
}
|
||||
}
|
||||
gba->earlyExit = false;
|
||||
#ifndef NDEBUG
|
||||
if (gba->cpuBlocked) {
|
||||
mLOG(GBA, FATAL, "CPU is blocked!");
|
||||
cpu->cycles = cpu->nextEvent;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef USE_DEBUGGERS
|
||||
|
|
|
@ -67,6 +67,7 @@ void GBASerialize(struct GBA* gba, struct GBASerializedState* state) {
|
|||
miscFlags = GBASerializedMiscFlagsFillIrqPending(miscFlags);
|
||||
STORE_32(gba->irqEvent.when - mTimingCurrentTime(&gba->timing), 0, &state->nextIrq);
|
||||
}
|
||||
miscFlags = GBASerializedMiscFlagsSetBlocked(miscFlags, gba->cpuBlocked);
|
||||
STORE_32(miscFlags, 0, &state->miscFlags);
|
||||
|
||||
GBAMemorySerialize(&gba->memory, state);
|
||||
|
@ -185,6 +186,7 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
|
|||
LOAD_32(when, 0, &state->nextIrq);
|
||||
mTimingSchedule(&gba->timing, &gba->irqEvent, when);
|
||||
}
|
||||
gba->cpuBlocked = GBASerializedMiscFlagsGetBlocked(miscFlags);
|
||||
|
||||
GBAVideoDeserialize(&gba->video, state);
|
||||
GBAMemoryDeserialize(&gba->memory, state);
|
||||
|
|
|
@ -196,6 +196,7 @@ void _startHdraw(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
|||
video->frameskipCounter = video->frameskip;
|
||||
}
|
||||
++video->frameCounter;
|
||||
video->p->earlyExit = true;
|
||||
break;
|
||||
case VIDEO_VERTICAL_TOTAL_PIXELS - 1:
|
||||
video->p->memory.io[REG_DISPSTAT >> 1] = GBARegisterDISPSTATClearInVblank(dispstat);
|
||||
|
|
|
@ -54,9 +54,8 @@ add_custom_target(${BINARY_NAME}-py-bdist
|
|||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
DEPENDS ${BINARY_NAME}-py)
|
||||
|
||||
file(GLOB BASE_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/test_*.py)
|
||||
file(GLOB SUBTESTS ${CMAKE_CURRENT_SOURCE_DIR}/tests/*/test_*.py)
|
||||
foreach(TEST IN LISTS BASE_TESTS SUBTESTS)
|
||||
file(GLOB TESTS ${CMAKE_CURRENT_SOURCE_DIR}/tests/*/test_*.py)
|
||||
foreach(TEST IN LISTS TESTS)
|
||||
if(APPLE)
|
||||
set(PATH DYLD_LIBRARY_PATH)
|
||||
elseif(WIN32)
|
||||
|
@ -64,7 +63,7 @@ foreach(TEST IN LISTS BASE_TESTS SUBTESTS)
|
|||
else()
|
||||
set(PATH LD_LIBRARY_PATH)
|
||||
endif()
|
||||
string(REGEX REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/(tests/.*/)?test_" "" TEST_NAME "${TEST}")
|
||||
string(REGEX REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/tests/(.*/)?test_" "" TEST_NAME "${TEST}")
|
||||
string(REPLACE ".py" "" TEST_NAME "${TEST_NAME}")
|
||||
add_test(NAME python-${TEST_NAME}
|
||||
COMMAND ${PYTHON_EXECUTABLE} setup.py build -b ${CMAKE_CURRENT_BINARY_DIR} pytest --extras --addopts ${TEST}
|
||||
|
|
|
@ -43,4 +43,5 @@ if(BUILD_CINEMA)
|
|||
add_executable(${BINARY_NAME}-cinema ${CMAKE_CURRENT_SOURCE_DIR}/cinema-main.c)
|
||||
target_link_libraries(${BINARY_NAME}-cinema ${BINARY_NAME} ${PLATFORM_LIBRARY})
|
||||
set_target_properties(${BINARY_NAME}-cinema PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}")
|
||||
add_test(cinema ${BINARY_NAME}-cinema -v)
|
||||
endif()
|
||||
|
|
|
@ -518,4 +518,34 @@ ssize_t parseQuotedString(const char* unparsed, ssize_t unparsedLen, char* parse
|
|||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool wildcard(const char* search, const char* string) {
|
||||
while (true) {
|
||||
if (search[0] == '*') {
|
||||
while (search[0] == '*') {
|
||||
++search;
|
||||
}
|
||||
if (!search[0]) {
|
||||
return true;
|
||||
}
|
||||
while (string[0]) {
|
||||
if (string[0] == search[0] && wildcard(search, string)) {
|
||||
return true;
|
||||
}
|
||||
++string;
|
||||
}
|
||||
return false;
|
||||
} else if (!search[0]) {
|
||||
return !string[0];
|
||||
} else if (!string[0]) {
|
||||
return false;
|
||||
} else if (string[0] != search[0]) {
|
||||
return false;
|
||||
} else {
|
||||
++search;
|
||||
++string;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|