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