mirror of https://github.com/mgba-emu/mgba.git
GBA Thread: Kill GBAThread
This commit is contained in:
parent
d25ba2ec59
commit
a75c019fab
|
@ -5,7 +5,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include "config.h"
|
||||
|
||||
#include "gba/gba.h"
|
||||
#include "util/formatting.h"
|
||||
#include "util/string.h"
|
||||
#include "util/vfs.h"
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include "audio.h"
|
||||
|
||||
#include "core/sync.h"
|
||||
#include "gba/gba.h"
|
||||
#include "gba/io.h"
|
||||
#include "gba/serialize.h"
|
||||
#include "gba/supervisor/thread.h"
|
||||
#include "gba/video.h"
|
||||
|
||||
const unsigned GBA_AUDIO_SAMPLES = 2048;
|
||||
|
|
|
@ -5,14 +5,16 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include "gba.h"
|
||||
|
||||
#include "core/thread.h"
|
||||
|
||||
#include "arm/decoder.h"
|
||||
#include "arm/isa-inlines.h"
|
||||
|
||||
#include "gba/bios.h"
|
||||
#include "gba/cheats.h"
|
||||
#include "gba/context/overrides.h"
|
||||
#include "gba/io.h"
|
||||
#include "gba/rr/rr.h"
|
||||
#include "gba/supervisor/thread.h"
|
||||
#include "gba/serialize.h"
|
||||
#include "gba/sio.h"
|
||||
|
||||
|
@ -90,8 +92,6 @@ static void GBAInit(void* cpu, struct mCPUComponent* component) {
|
|||
gba->romVf = 0;
|
||||
gba->biosVf = 0;
|
||||
|
||||
gba->logHandler = 0;
|
||||
gba->logLevel = GBA_LOG_WARN | GBA_LOG_ERROR | GBA_LOG_FATAL;
|
||||
gba->stream = 0;
|
||||
gba->keyCallback = 0;
|
||||
gba->stopCallback = 0;
|
||||
|
@ -650,80 +650,12 @@ void GBAStop(struct GBA* gba) {
|
|||
gba->stopCallback->stop(gba->stopCallback);
|
||||
}
|
||||
|
||||
static void _GBAVLog(struct GBA* gba, enum GBALogLevel level, const char* format, va_list args) {
|
||||
struct GBAThread* threadContext = GBAThreadGetContext();
|
||||
enum GBALogLevel logLevel = GBA_LOG_ALL;
|
||||
|
||||
if (gba) {
|
||||
logLevel = gba->logLevel;
|
||||
}
|
||||
|
||||
if (threadContext) {
|
||||
logLevel = threadContext->logLevel;
|
||||
gba = threadContext->gba;
|
||||
}
|
||||
|
||||
if (!(level & logLevel) && level != GBA_LOG_FATAL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (level == GBA_LOG_FATAL && gba) {
|
||||
gba->cpu->nextEvent = 0;
|
||||
}
|
||||
|
||||
if (threadContext) {
|
||||
if (level == GBA_LOG_FATAL) {
|
||||
MutexLock(&threadContext->stateMutex);
|
||||
threadContext->state = THREAD_CRASHED;
|
||||
MutexUnlock(&threadContext->stateMutex);
|
||||
}
|
||||
}
|
||||
if (gba && gba->logHandler) {
|
||||
gba->logHandler(threadContext, level, format, args);
|
||||
return;
|
||||
}
|
||||
|
||||
vprintf(format, args);
|
||||
printf("\n");
|
||||
|
||||
if (level == GBA_LOG_FATAL && !threadContext) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
void GBALog(struct GBA* gba, enum GBALogLevel level, const char* format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
_GBAVLog(gba, level, format, args);
|
||||
va_end(args);
|
||||
// TODO: Kill GBALog
|
||||
}
|
||||
|
||||
void GBADebuggerLogShim(struct Debugger* debugger, enum DebuggerLogLevel level, const char* format, ...) {
|
||||
struct GBA* gba = 0;
|
||||
if (debugger->cpu) {
|
||||
gba = (struct GBA*) debugger->cpu->master;
|
||||
}
|
||||
|
||||
enum GBALogLevel gbaLevel;
|
||||
switch (level) {
|
||||
default: // Avoids compiler warning
|
||||
case DEBUGGER_LOG_DEBUG:
|
||||
gbaLevel = GBA_LOG_DEBUG;
|
||||
break;
|
||||
case DEBUGGER_LOG_INFO:
|
||||
gbaLevel = GBA_LOG_INFO;
|
||||
break;
|
||||
case DEBUGGER_LOG_WARN:
|
||||
gbaLevel = GBA_LOG_WARN;
|
||||
break;
|
||||
case DEBUGGER_LOG_ERROR:
|
||||
gbaLevel = GBA_LOG_ERROR;
|
||||
break;
|
||||
}
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
_GBAVLog(gba, gbaLevel, format, args);
|
||||
va_end(args);
|
||||
// TODO: Kill GBADebuggerLogShim
|
||||
}
|
||||
|
||||
bool GBAIsROM(struct VFile* vf) {
|
||||
|
@ -879,18 +811,7 @@ void GBABreakpoint(struct ARMCore* cpu, int immediate) {
|
|||
void GBAFrameStarted(struct GBA* gba) {
|
||||
UNUSED(gba);
|
||||
|
||||
struct GBAThread* thread = GBAThreadGetContext();
|
||||
if (!thread) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (thread->rewindBuffer) {
|
||||
--thread->rewindBufferNext;
|
||||
if (thread->rewindBufferNext <= 0) {
|
||||
thread->rewindBufferNext = thread->rewindBufferInterval;
|
||||
GBARecordFrame(thread);
|
||||
}
|
||||
}
|
||||
// TODO: Put back rewind
|
||||
}
|
||||
|
||||
void GBAFrameEnded(struct GBA* gba) {
|
||||
|
@ -970,12 +891,6 @@ void GBAClearBreakpoint(struct GBA* gba, uint32_t address, enum ExecutionMode mo
|
|||
}
|
||||
}
|
||||
|
||||
#if (!defined(USE_PTHREADS) && !defined(_WIN32)) || defined(DISABLE_THREADING)
|
||||
struct GBAThread* GBAThreadGetContext(void) {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool _setSoftwareBreakpoint(struct Debugger* debugger, uint32_t address, enum ExecutionMode mode, uint32_t* opcode) {
|
||||
GBASetBreakpoint((struct GBA*) debugger->cpu->master, &debugger->d, address, mode, opcode);
|
||||
return true;
|
||||
|
|
|
@ -56,7 +56,6 @@ enum {
|
|||
};
|
||||
|
||||
struct GBA;
|
||||
struct GBAThread;
|
||||
struct Patch;
|
||||
struct VFile;
|
||||
|
||||
|
@ -114,8 +113,6 @@ struct GBA {
|
|||
|
||||
const char* activeFile;
|
||||
|
||||
GBALogHandler logHandler;
|
||||
enum GBALogLevel logLevel;
|
||||
struct mAVStream* stream;
|
||||
struct mKeyCallback* keyCallback;
|
||||
struct mStopCallback* stopCallback;
|
||||
|
|
|
@ -37,11 +37,8 @@ enum GBASIOMode {
|
|||
struct GBA;
|
||||
struct GBAAudio;
|
||||
struct GBASIO;
|
||||
struct GBAThread;
|
||||
struct GBAVideoRenderer;
|
||||
|
||||
typedef void (*GBALogHandler)(struct GBAThread*, enum GBALogLevel, const char* format, va_list args);
|
||||
|
||||
extern const int GBA_LUX_LEVELS[10];
|
||||
|
||||
struct GBALuminanceSource {
|
||||
|
|
|
@ -5,11 +5,11 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include "serialize.h"
|
||||
|
||||
#include "core/sync.h"
|
||||
#include "gba/audio.h"
|
||||
#include "gba/cheats.h"
|
||||
#include "gba/io.h"
|
||||
#include "gba/rr/rr.h"
|
||||
#include "gba/supervisor/thread.h"
|
||||
#include "gba/video.h"
|
||||
|
||||
#include "util/memory.h"
|
||||
|
@ -356,66 +356,6 @@ static struct GBASerializedState* _loadPNGState(struct VFile* vf, struct GBAExtd
|
|||
}
|
||||
#endif
|
||||
|
||||
bool GBASaveState(struct GBAThread* threadContext, struct VDir* dir, int slot, int flags) {
|
||||
struct VFile* vf = GBAGetState(threadContext->gba, dir, slot, true);
|
||||
if (!vf) {
|
||||
return false;
|
||||
}
|
||||
bool success = GBASaveStateNamed(threadContext->gba, vf, flags);
|
||||
vf->close(vf);
|
||||
if (success) {
|
||||
#if SAVESTATE_DEBUG
|
||||
vf = GBAGetState(threadContext->gba, dir, slot, false);
|
||||
if (vf) {
|
||||
struct GBA* backup = anonymousMemoryMap(sizeof(*backup));
|
||||
memcpy(backup, threadContext->gba, sizeof(*backup));
|
||||
memset(threadContext->gba->memory.io, 0, sizeof(threadContext->gba->memory.io));
|
||||
memset(threadContext->gba->timers, 0, sizeof(threadContext->gba->timers));
|
||||
GBALoadStateNamed(threadContext->gba, vf, flags);
|
||||
if (memcmp(backup, threadContext->gba, sizeof(*backup))) {
|
||||
char suffix[16] = { '\0' };
|
||||
struct VFile* vf2;
|
||||
snprintf(suffix, sizeof(suffix), ".dump.0.%d", slot);
|
||||
vf2 = VDirOptionalOpenFile(dir, threadContext->gba->activeFile, "savestate", suffix, write ? (O_CREAT | O_TRUNC | O_RDWR) : O_RDONLY);
|
||||
if (vf2) {
|
||||
vf2->write(vf2, backup, sizeof(*backup));
|
||||
vf2->close(vf2);
|
||||
}
|
||||
snprintf(suffix, sizeof(suffix), ".dump.1.%d", slot);
|
||||
vf2 = VDirOptionalOpenFile(dir, threadContext->gba->activeFile, "savestate", suffix, write ? (O_CREAT | O_TRUNC | O_RDWR) : O_RDONLY);
|
||||
if (vf2) {
|
||||
vf2->write(vf2, threadContext->gba, sizeof(*threadContext->gba));
|
||||
vf2->close(vf2);
|
||||
}
|
||||
}
|
||||
mappedMemoryFree(backup, sizeof(*backup));
|
||||
vf->close(vf);
|
||||
}
|
||||
#endif
|
||||
GBALog(threadContext->gba, GBA_LOG_STATUS, "State %i saved", slot);
|
||||
} else {
|
||||
GBALog(threadContext->gba, GBA_LOG_STATUS, "State %i failed to save", slot);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool GBALoadState(struct GBAThread* threadContext, struct VDir* dir, int slot, int flags) {
|
||||
struct VFile* vf = GBAGetState(threadContext->gba, dir, slot, false);
|
||||
if (!vf) {
|
||||
return false;
|
||||
}
|
||||
threadContext->rewindBufferSize = 0;
|
||||
bool success = GBALoadStateNamed(threadContext->gba, vf, flags);
|
||||
vf->close(vf);
|
||||
if (success) {
|
||||
GBALog(threadContext->gba, GBA_LOG_STATUS, "State %i loaded", slot);
|
||||
} else {
|
||||
GBALog(threadContext->gba, GBA_LOG_STATUS, "State %i failed to load", slot);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool GBASaveStateNamed(struct GBA* gba, struct VFile* vf, int flags) {
|
||||
struct GBAExtdata extdata;
|
||||
GBAExtdataInit(&extdata);
|
||||
|
@ -671,82 +611,7 @@ void GBADeallocateState(struct GBASerializedState* state) {
|
|||
mappedMemoryFree(state, sizeof(struct GBASerializedState));
|
||||
}
|
||||
|
||||
void GBARecordFrame(struct GBAThread* thread) {
|
||||
int offset = thread->rewindBufferWriteOffset;
|
||||
struct GBASerializedState* state = thread->rewindBuffer[offset];
|
||||
if (!state) {
|
||||
state = GBAAllocateState();
|
||||
thread->rewindBuffer[offset] = state;
|
||||
}
|
||||
GBASerialize(thread->gba, state);
|
||||
|
||||
if (thread->rewindScreenBuffer) {
|
||||
unsigned stride;
|
||||
const uint8_t* pixels = 0;
|
||||
thread->gba->video.renderer->getPixels(thread->gba->video.renderer, &stride, (const void**) &pixels);
|
||||
if (pixels) {
|
||||
size_t y;
|
||||
for (y = 0; y < VIDEO_VERTICAL_PIXELS; ++y) {
|
||||
memcpy(&thread->rewindScreenBuffer[(offset * VIDEO_VERTICAL_PIXELS + y) * VIDEO_HORIZONTAL_PIXELS * BYTES_PER_PIXEL], &pixels[y * stride * BYTES_PER_PIXEL], VIDEO_HORIZONTAL_PIXELS * BYTES_PER_PIXEL);
|
||||
}
|
||||
}
|
||||
}
|
||||
thread->rewindBufferSize = thread->rewindBufferSize == thread->rewindBufferCapacity ? thread->rewindBufferCapacity : thread->rewindBufferSize + 1;
|
||||
thread->rewindBufferWriteOffset = (offset + 1) % thread->rewindBufferCapacity;
|
||||
}
|
||||
|
||||
void GBARewindSettingsChanged(struct GBAThread* threadContext, int newCapacity, int newInterval) {
|
||||
if (newCapacity == threadContext->rewindBufferCapacity && newInterval == threadContext->rewindBufferInterval) {
|
||||
return;
|
||||
}
|
||||
threadContext->rewindBufferInterval = newInterval;
|
||||
threadContext->rewindBufferNext = threadContext->rewindBufferInterval;
|
||||
threadContext->rewindBufferSize = 0;
|
||||
if (threadContext->rewindBuffer) {
|
||||
int i;
|
||||
for (i = 0; i < threadContext->rewindBufferCapacity; ++i) {
|
||||
GBADeallocateState(threadContext->rewindBuffer[i]);
|
||||
}
|
||||
free(threadContext->rewindBuffer);
|
||||
free(threadContext->rewindScreenBuffer);
|
||||
}
|
||||
threadContext->rewindBufferCapacity = newCapacity;
|
||||
if (threadContext->rewindBufferCapacity > 0) {
|
||||
threadContext->rewindBuffer = calloc(threadContext->rewindBufferCapacity, sizeof(struct GBASerializedState*));
|
||||
threadContext->rewindScreenBuffer = calloc(threadContext->rewindBufferCapacity, VIDEO_VERTICAL_PIXELS * VIDEO_HORIZONTAL_PIXELS * BYTES_PER_PIXEL);
|
||||
} else {
|
||||
threadContext->rewindBuffer = 0;
|
||||
threadContext->rewindScreenBuffer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int GBARewind(struct GBAThread* thread, int nStates) {
|
||||
if (nStates > thread->rewindBufferSize || nStates < 0) {
|
||||
nStates = thread->rewindBufferSize;
|
||||
}
|
||||
if (nStates == 0) {
|
||||
return 0;
|
||||
}
|
||||
int offset = thread->rewindBufferWriteOffset - nStates;
|
||||
if (offset < 0) {
|
||||
offset += thread->rewindBufferCapacity;
|
||||
}
|
||||
struct GBASerializedState* state = thread->rewindBuffer[offset];
|
||||
if (!state) {
|
||||
return 0;
|
||||
}
|
||||
thread->rewindBufferSize -= nStates;
|
||||
thread->rewindBufferWriteOffset = offset;
|
||||
GBADeserialize(thread->gba, state);
|
||||
if (thread->rewindScreenBuffer) {
|
||||
thread->gba->video.renderer->putPixels(thread->gba->video.renderer, VIDEO_HORIZONTAL_PIXELS, &thread->rewindScreenBuffer[offset * VIDEO_HORIZONTAL_PIXELS * VIDEO_VERTICAL_PIXELS * BYTES_PER_PIXEL]);
|
||||
}
|
||||
return nStates;
|
||||
}
|
||||
|
||||
void GBARewindAll(struct GBAThread* thread) {
|
||||
GBARewind(thread, thread->rewindBufferSize);
|
||||
}
|
||||
// TODO: Put back rewind
|
||||
|
||||
void GBATakeScreenshot(struct GBA* gba, struct VDir* dir) {
|
||||
#ifdef USE_PNG
|
||||
|
|
|
@ -358,13 +358,10 @@ struct GBAExtdata {
|
|||
};
|
||||
|
||||
struct VDir;
|
||||
struct GBAThread;
|
||||
|
||||
void GBASerialize(struct GBA* gba, struct GBASerializedState* state);
|
||||
bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state);
|
||||
|
||||
bool GBASaveState(struct GBAThread* thread, struct VDir* dir, int slot, int flags);
|
||||
bool GBALoadState(struct GBAThread* thread, struct VDir* dir, int slot, int flags);
|
||||
struct VFile* GBAGetState(struct GBA* gba, struct VDir* dir, int slot, bool write);
|
||||
void GBADeleteState(struct GBA* thread, struct VDir* dir, int slot);
|
||||
|
||||
|
@ -382,11 +379,6 @@ struct GBASerializedState* GBAExtractState(struct VFile* vf, struct GBAExtdata*
|
|||
struct GBASerializedState* GBAAllocateState(void);
|
||||
void GBADeallocateState(struct GBASerializedState* state);
|
||||
|
||||
void GBARecordFrame(struct GBAThread* thread);
|
||||
void GBARewindSettingsChanged(struct GBAThread* thread, int newCapacity, int newInterval);
|
||||
int GBARewind(struct GBAThread* thread, int nStates);
|
||||
void GBARewindAll(struct GBAThread* thread);
|
||||
|
||||
void GBATakeScreenshot(struct GBA* gba, struct VDir* dir);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
#include "gba/io.h"
|
||||
#include "gba/serialize.h"
|
||||
#include "gba/supervisor/thread.h"
|
||||
|
||||
#ifdef USE_CLI_DEBUGGER
|
||||
|
||||
|
|
|
@ -1,736 +0,0 @@
|
|||
/* Copyright (c) 2013-2015 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 "thread.h"
|
||||
|
||||
#include "arm.h"
|
||||
#include "core/config.h"
|
||||
#include "gba/gba.h"
|
||||
#include "gba/cheats.h"
|
||||
#include "gba/serialize.h"
|
||||
#include "gba/rr/mgm.h"
|
||||
#include "gba/rr/vbm.h"
|
||||
|
||||
#include "debugger/debugger.h"
|
||||
|
||||
#include "util/patch.h"
|
||||
#include "util/vfs.h"
|
||||
|
||||
#include "platform/commandline.h"
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
static const float _defaultFPSTarget = 60.f;
|
||||
|
||||
#ifndef DISABLE_THREADING
|
||||
|
||||
static bool _reloadDirectories(struct GBAThread* threadContext) {
|
||||
mDirectorySetDetachBase(&threadContext->dirs);
|
||||
|
||||
char basename[PATH_MAX];
|
||||
if (threadContext->fname) {
|
||||
char dirname[PATH_MAX];
|
||||
separatePath(threadContext->fname, dirname, basename, 0);
|
||||
mDirectorySetAttachBase(&threadContext->dirs, VDirOpen(dirname));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
char path[PATH_MAX];
|
||||
snprintf(path, sizeof(path), "%s.sav", basename);
|
||||
threadContext->save = threadContext->dirs.save->openFile(threadContext->dirs.save, path, O_CREAT | O_RDWR);
|
||||
|
||||
if (!threadContext->patch) {
|
||||
snprintf(path, sizeof(path), "%s.ups", basename);
|
||||
threadContext->patch = threadContext->dirs.patch->openFile(threadContext->dirs.patch, path, O_RDONLY);
|
||||
}
|
||||
if (!threadContext->patch) {
|
||||
snprintf(path, sizeof(path), "%s.ips", basename);
|
||||
threadContext->patch = threadContext->dirs.patch->openFile(threadContext->dirs.patch, path, O_RDONLY);
|
||||
}
|
||||
if (!threadContext->patch) {
|
||||
snprintf(path, sizeof(path), "%s.bps", basename);
|
||||
threadContext->patch = threadContext->dirs.patch->openFile(threadContext->dirs.patch, path, O_RDONLY);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#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);
|
||||
}
|
||||
#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();
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void _changeState(struct GBAThread* threadContext, enum mCoreThreadState newState, bool broadcast) {
|
||||
MutexLock(&threadContext->stateMutex);
|
||||
threadContext->state = newState;
|
||||
if (broadcast) {
|
||||
ConditionWake(&threadContext->stateCond);
|
||||
}
|
||||
MutexUnlock(&threadContext->stateMutex);
|
||||
}
|
||||
|
||||
static void _waitOnInterrupt(struct GBAThread* threadContext) {
|
||||
while (threadContext->state == THREAD_INTERRUPTED) {
|
||||
ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);
|
||||
}
|
||||
}
|
||||
|
||||
static void _waitUntilNotState(struct GBAThread* threadContext, enum mCoreThreadState oldState) {
|
||||
MutexLock(&threadContext->sync.videoFrameMutex);
|
||||
bool videoFrameWait = threadContext->sync.videoFrameWait;
|
||||
threadContext->sync.videoFrameWait = false;
|
||||
MutexUnlock(&threadContext->sync.videoFrameMutex);
|
||||
|
||||
while (threadContext->state == oldState) {
|
||||
MutexUnlock(&threadContext->stateMutex);
|
||||
|
||||
if (!MutexTryLock(&threadContext->sync.videoFrameMutex)) {
|
||||
ConditionWake(&threadContext->sync.videoFrameRequiredCond);
|
||||
MutexUnlock(&threadContext->sync.videoFrameMutex);
|
||||
}
|
||||
|
||||
if (!MutexTryLock(&threadContext->sync.audioBufferMutex)) {
|
||||
ConditionWake(&threadContext->sync.audioRequiredCond);
|
||||
MutexUnlock(&threadContext->sync.audioBufferMutex);
|
||||
}
|
||||
|
||||
MutexLock(&threadContext->stateMutex);
|
||||
ConditionWake(&threadContext->stateCond);
|
||||
}
|
||||
|
||||
MutexLock(&threadContext->sync.videoFrameMutex);
|
||||
threadContext->sync.videoFrameWait = videoFrameWait;
|
||||
MutexUnlock(&threadContext->sync.videoFrameMutex);
|
||||
}
|
||||
|
||||
static void _pauseThread(struct GBAThread* threadContext, bool onThread) {
|
||||
threadContext->state = THREAD_PAUSING;
|
||||
if (!onThread) {
|
||||
_waitUntilNotState(threadContext, THREAD_PAUSING);
|
||||
}
|
||||
}
|
||||
|
||||
struct GBAThreadStop {
|
||||
struct mStopCallback d;
|
||||
struct GBAThread* p;
|
||||
};
|
||||
|
||||
static void _stopCallback(struct mStopCallback* stop) {
|
||||
struct GBAThreadStop* callback = (struct GBAThreadStop*) stop;
|
||||
if (callback->p->stopCallback(callback->p)) {
|
||||
_changeState(callback->p, THREAD_EXITING, false);
|
||||
}
|
||||
}
|
||||
|
||||
static THREAD_ENTRY _GBAThreadRun(void* context) {
|
||||
#ifdef USE_PTHREADS
|
||||
pthread_once(&_contextOnce, _createTLS);
|
||||
#elif _WIN32
|
||||
InitOnceExecuteOnce(&_contextOnce, _createTLS, NULL, 0);
|
||||
#endif
|
||||
|
||||
struct GBA gba;
|
||||
struct ARMCore cpu;
|
||||
struct Patch patch;
|
||||
struct GBACheatDevice cheatDevice;
|
||||
struct GBAThread* threadContext = context;
|
||||
struct mCPUComponent* components[GBA_COMPONENT_MAX] = { 0 };
|
||||
struct GBARRContext* movie = 0;
|
||||
int numComponents = GBA_COMPONENT_MAX;
|
||||
|
||||
ThreadSetName("CPU Thread");
|
||||
|
||||
#if !defined(_WIN32) && defined(USE_PTHREADS)
|
||||
sigset_t signals;
|
||||
sigemptyset(&signals);
|
||||
pthread_sigmask(SIG_SETMASK, &signals, 0);
|
||||
#endif
|
||||
|
||||
GBACreate(&gba);
|
||||
ARMSetComponents(&cpu, &gba.d, numComponents, components);
|
||||
ARMInit(&cpu);
|
||||
gba.sync = &threadContext->sync;
|
||||
threadContext->gba = &gba;
|
||||
threadContext->cpu = &cpu;
|
||||
gba.logLevel = threadContext->logLevel;
|
||||
gba.logHandler = threadContext->logHandler;
|
||||
gba.stream = threadContext->stream;
|
||||
gba.video.frameskip = threadContext->frameskip;
|
||||
|
||||
struct GBAThreadStop stop;
|
||||
if (threadContext->stopCallback) {
|
||||
stop.d.stop = _stopCallback;
|
||||
stop.p = threadContext;
|
||||
gba.stopCallback = &stop.d;
|
||||
}
|
||||
|
||||
gba.idleOptimization = threadContext->idleOptimization;
|
||||
#ifdef USE_PTHREADS
|
||||
pthread_setspecific(_contextKey, threadContext);
|
||||
#elif _WIN32
|
||||
TlsSetValue(_contextKey, threadContext);
|
||||
#endif
|
||||
|
||||
if (threadContext->audioBuffers) {
|
||||
GBAAudioResizeBuffer(&gba.audio, threadContext->audioBuffers);
|
||||
} else {
|
||||
threadContext->audioBuffers = GBA_AUDIO_SAMPLES;
|
||||
}
|
||||
|
||||
if (threadContext->renderer) {
|
||||
GBAVideoAssociateRenderer(&gba.video, threadContext->renderer);
|
||||
}
|
||||
|
||||
if (threadContext->rom) {
|
||||
if (GBAIsMB(threadContext->rom)) {
|
||||
GBALoadMB(&gba, threadContext->rom, threadContext->fname);
|
||||
} else {
|
||||
GBALoadROM(&gba, threadContext->rom, threadContext->save, threadContext->fname);
|
||||
}
|
||||
|
||||
struct GBACartridgeOverride override;
|
||||
const struct GBACartridge* cart = (const struct GBACartridge*) gba.pristineRom;
|
||||
memcpy(override.id, &cart->id, sizeof(override.id));
|
||||
if (GBAOverrideFind(threadContext->overrides, &override)) {
|
||||
GBAOverrideApply(&gba, &override);
|
||||
}
|
||||
if (threadContext->hasOverride) {
|
||||
GBAOverrideApply(&gba, &threadContext->override);
|
||||
}
|
||||
|
||||
if (threadContext->patch && loadPatch(threadContext->patch, &patch)) {
|
||||
GBAApplyPatch(&gba, &patch);
|
||||
}
|
||||
}
|
||||
|
||||
if (threadContext->bios && GBAIsBIOS(threadContext->bios)) {
|
||||
GBALoadBIOS(&gba, threadContext->bios);
|
||||
}
|
||||
|
||||
if (threadContext->movie) {
|
||||
struct VDir* movieDir = VDirOpen(threadContext->movie);
|
||||
if (!movieDir) {
|
||||
movieDir = VDirOpenArchive(threadContext->movie);
|
||||
}
|
||||
if (movieDir) {
|
||||
struct GBAMGMContext* mgm = malloc(sizeof(*mgm));
|
||||
GBAMGMContextCreate(mgm);
|
||||
if (!GBAMGMSetStream(mgm, movieDir)) {
|
||||
mgm->d.destroy(&mgm->d);
|
||||
} else {
|
||||
movie = &mgm->d;
|
||||
}
|
||||
} else {
|
||||
struct VFile* movieFile = VFileOpen(threadContext->movie, O_RDONLY);
|
||||
if (movieFile) {
|
||||
struct GBAVBMContext* vbm = malloc(sizeof(*vbm));
|
||||
GBAVBMContextCreate(vbm);
|
||||
if (!GBAVBMSetStream(vbm, movieFile)) {
|
||||
vbm->d.destroy(&vbm->d);
|
||||
} else {
|
||||
movie = &vbm->d;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ARMReset(&cpu);
|
||||
|
||||
if (movie) {
|
||||
gba.rr = movie;
|
||||
movie->startPlaying(movie, false);
|
||||
GBARRInitPlay(&gba);
|
||||
}
|
||||
|
||||
if (threadContext->skipBios && gba.pristineRom) {
|
||||
GBASkipBIOS(&gba);
|
||||
}
|
||||
|
||||
if (!threadContext->cheats) {
|
||||
GBACheatDeviceCreate(&cheatDevice);
|
||||
threadContext->cheats = &cheatDevice;
|
||||
}
|
||||
if (threadContext->cheatsFile) {
|
||||
GBACheatParseFile(threadContext->cheats, threadContext->cheatsFile);
|
||||
}
|
||||
GBACheatAttachDevice(&gba, threadContext->cheats);
|
||||
|
||||
if (threadContext->debugger) {
|
||||
threadContext->debugger->log = GBADebuggerLogShim;
|
||||
GBAAttachDebugger(&gba, threadContext->debugger);
|
||||
DebuggerEnter(threadContext->debugger, DEBUGGER_ENTER_ATTACHED, 0);
|
||||
}
|
||||
|
||||
GBASIOSetDriverSet(&gba.sio, &threadContext->sioDrivers);
|
||||
|
||||
if (threadContext->volume == 0) {
|
||||
threadContext->volume = GBA_AUDIO_VOLUME_MAX;
|
||||
}
|
||||
if (threadContext->mute) {
|
||||
gba.audio.masterVolume = 0;
|
||||
} else {
|
||||
gba.audio.masterVolume = threadContext->volume;
|
||||
}
|
||||
|
||||
gba.keySource = &threadContext->activeKeys;
|
||||
|
||||
if (threadContext->startCallback) {
|
||||
threadContext->startCallback(threadContext);
|
||||
}
|
||||
|
||||
_changeState(threadContext, THREAD_RUNNING, true);
|
||||
|
||||
while (threadContext->state < THREAD_EXITING) {
|
||||
if (threadContext->debugger) {
|
||||
struct Debugger* debugger = threadContext->debugger;
|
||||
DebuggerRun(debugger);
|
||||
if (debugger->state == DEBUGGER_SHUTDOWN) {
|
||||
_changeState(threadContext, THREAD_EXITING, false);
|
||||
}
|
||||
} else {
|
||||
while (threadContext->state == THREAD_RUNNING) {
|
||||
ARMRunLoop(&cpu);
|
||||
}
|
||||
}
|
||||
|
||||
int resetScheduled = 0;
|
||||
MutexLock(&threadContext->stateMutex);
|
||||
while (threadContext->state > THREAD_RUNNING && threadContext->state < THREAD_EXITING) {
|
||||
if (threadContext->state == THREAD_PAUSING) {
|
||||
threadContext->state = THREAD_PAUSED;
|
||||
ConditionWake(&threadContext->stateCond);
|
||||
}
|
||||
if (threadContext->state == THREAD_INTERRUPTING) {
|
||||
threadContext->state = THREAD_INTERRUPTED;
|
||||
ConditionWake(&threadContext->stateCond);
|
||||
}
|
||||
if (threadContext->state == THREAD_RUN_ON) {
|
||||
if (threadContext->run) {
|
||||
threadContext->run(threadContext);
|
||||
}
|
||||
threadContext->state = threadContext->savedState;
|
||||
ConditionWake(&threadContext->stateCond);
|
||||
}
|
||||
if (threadContext->state == THREAD_RESETING) {
|
||||
threadContext->state = THREAD_RUNNING;
|
||||
resetScheduled = 1;
|
||||
}
|
||||
while (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_INTERRUPTED) {
|
||||
ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);
|
||||
}
|
||||
}
|
||||
MutexUnlock(&threadContext->stateMutex);
|
||||
if (resetScheduled) {
|
||||
ARMReset(&cpu);
|
||||
if (threadContext->skipBios && gba.pristineRom) {
|
||||
GBASkipBIOS(&gba);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (threadContext->state < THREAD_SHUTDOWN) {
|
||||
_changeState(threadContext, THREAD_SHUTDOWN, false);
|
||||
}
|
||||
|
||||
if (threadContext->cleanCallback) {
|
||||
threadContext->cleanCallback(threadContext);
|
||||
}
|
||||
|
||||
threadContext->gba = 0;
|
||||
ARMDeinit(&cpu);
|
||||
GBADestroy(&gba);
|
||||
if (&cheatDevice == threadContext->cheats) {
|
||||
GBACheatDeviceDestroy(&cheatDevice);
|
||||
}
|
||||
|
||||
if (movie) {
|
||||
movie->destroy(movie);
|
||||
free(movie);
|
||||
}
|
||||
|
||||
threadContext->sync.videoFrameOn = false;
|
||||
ConditionWake(&threadContext->sync.videoFrameAvailableCond);
|
||||
ConditionWake(&threadContext->sync.audioRequiredCond);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GBAMapOptionsToContext(const struct mCoreOptions* opts, struct GBAThread* threadContext) {
|
||||
if (opts->useBios) {
|
||||
threadContext->bios = VFileOpen(opts->bios, O_RDONLY);
|
||||
} else {
|
||||
threadContext->bios = 0;
|
||||
}
|
||||
threadContext->frameskip = opts->frameskip;
|
||||
threadContext->volume = opts->volume;
|
||||
threadContext->mute = opts->mute;
|
||||
threadContext->logLevel = opts->logLevel;
|
||||
if (opts->rewindEnable) {
|
||||
threadContext->rewindBufferCapacity = opts->rewindBufferCapacity;
|
||||
threadContext->rewindBufferInterval = opts->rewindBufferInterval;
|
||||
} else {
|
||||
threadContext->rewindBufferCapacity = 0;
|
||||
}
|
||||
threadContext->skipBios = opts->skipBios;
|
||||
threadContext->sync.audioWait = opts->audioSync;
|
||||
threadContext->sync.videoFrameWait = opts->videoSync;
|
||||
|
||||
if (opts->fpsTarget) {
|
||||
threadContext->fpsTarget = opts->fpsTarget;
|
||||
}
|
||||
|
||||
if (opts->audioBuffers) {
|
||||
threadContext->audioBuffers = opts->audioBuffers;
|
||||
}
|
||||
|
||||
mDirectorySetMapOptions(&threadContext->dirs, opts);
|
||||
}
|
||||
|
||||
void GBAMapArgumentsToContext(const struct mArguments* args, struct GBAThread* threadContext) {
|
||||
GBAThreadLoadROM(threadContext, args->fname);
|
||||
threadContext->fname = args->fname;
|
||||
threadContext->patch = VFileOpen(args->patch, O_RDONLY);
|
||||
threadContext->cheatsFile = VFileOpen(args->cheatsFile, O_RDONLY);
|
||||
threadContext->movie = args->movie;
|
||||
}
|
||||
|
||||
bool GBAThreadStart(struct GBAThread* threadContext) {
|
||||
// TODO: error check
|
||||
threadContext->activeKeys = 0;
|
||||
threadContext->state = THREAD_INITIALIZED;
|
||||
threadContext->sync.videoFrameOn = true;
|
||||
|
||||
threadContext->rewindBuffer = 0;
|
||||
threadContext->rewindScreenBuffer = 0;
|
||||
int newCapacity = threadContext->rewindBufferCapacity;
|
||||
int newInterval = threadContext->rewindBufferInterval;
|
||||
threadContext->rewindBufferCapacity = 0;
|
||||
threadContext->rewindBufferInterval = 0;
|
||||
GBARewindSettingsChanged(threadContext, newCapacity, newInterval);
|
||||
|
||||
if (!threadContext->fpsTarget) {
|
||||
threadContext->fpsTarget = _defaultFPSTarget;
|
||||
}
|
||||
|
||||
bool bootBios = threadContext->bootBios && threadContext->bios;
|
||||
|
||||
if (threadContext->rom && (!GBAIsROM(threadContext->rom) || bootBios)) {
|
||||
threadContext->rom->close(threadContext->rom);
|
||||
threadContext->rom = 0;
|
||||
}
|
||||
|
||||
if (!threadContext->rom && !bootBios) {
|
||||
threadContext->state = THREAD_SHUTDOWN;
|
||||
return false;
|
||||
}
|
||||
|
||||
_reloadDirectories(threadContext);
|
||||
|
||||
MutexInit(&threadContext->stateMutex);
|
||||
ConditionInit(&threadContext->stateCond);
|
||||
|
||||
MutexInit(&threadContext->sync.videoFrameMutex);
|
||||
ConditionInit(&threadContext->sync.videoFrameAvailableCond);
|
||||
ConditionInit(&threadContext->sync.videoFrameRequiredCond);
|
||||
MutexInit(&threadContext->sync.audioBufferMutex);
|
||||
ConditionInit(&threadContext->sync.audioRequiredCond);
|
||||
|
||||
threadContext->interruptDepth = 0;
|
||||
|
||||
#ifdef USE_PTHREADS
|
||||
sigset_t signals;
|
||||
sigemptyset(&signals);
|
||||
sigaddset(&signals, SIGINT);
|
||||
sigaddset(&signals, SIGTRAP);
|
||||
pthread_sigmask(SIG_BLOCK, &signals, 0);
|
||||
#endif
|
||||
|
||||
MutexLock(&threadContext->stateMutex);
|
||||
ThreadCreate(&threadContext->thread, _GBAThreadRun, threadContext);
|
||||
while (threadContext->state < THREAD_RUNNING) {
|
||||
ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);
|
||||
}
|
||||
MutexUnlock(&threadContext->stateMutex);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GBAThreadHasStarted(struct GBAThread* threadContext) {
|
||||
bool hasStarted;
|
||||
MutexLock(&threadContext->stateMutex);
|
||||
hasStarted = threadContext->state > THREAD_INITIALIZED;
|
||||
MutexUnlock(&threadContext->stateMutex);
|
||||
return hasStarted;
|
||||
}
|
||||
|
||||
bool GBAThreadHasExited(struct GBAThread* threadContext) {
|
||||
bool hasExited;
|
||||
MutexLock(&threadContext->stateMutex);
|
||||
hasExited = threadContext->state > THREAD_EXITING;
|
||||
MutexUnlock(&threadContext->stateMutex);
|
||||
return hasExited;
|
||||
}
|
||||
|
||||
bool GBAThreadHasCrashed(struct GBAThread* threadContext) {
|
||||
bool hasExited;
|
||||
MutexLock(&threadContext->stateMutex);
|
||||
hasExited = threadContext->state == THREAD_CRASHED;
|
||||
MutexUnlock(&threadContext->stateMutex);
|
||||
return hasExited;
|
||||
}
|
||||
|
||||
void GBAThreadEnd(struct GBAThread* threadContext) {
|
||||
MutexLock(&threadContext->stateMutex);
|
||||
_waitOnInterrupt(threadContext);
|
||||
threadContext->state = THREAD_EXITING;
|
||||
if (threadContext->gba) {
|
||||
threadContext->gba->cpu->halted = false;
|
||||
}
|
||||
ConditionWake(&threadContext->stateCond);
|
||||
MutexUnlock(&threadContext->stateMutex);
|
||||
MutexLock(&threadContext->sync.audioBufferMutex);
|
||||
threadContext->sync.audioWait = 0;
|
||||
ConditionWake(&threadContext->sync.audioRequiredCond);
|
||||
MutexUnlock(&threadContext->sync.audioBufferMutex);
|
||||
|
||||
MutexLock(&threadContext->sync.videoFrameMutex);
|
||||
threadContext->sync.videoFrameWait = false;
|
||||
threadContext->sync.videoFrameOn = false;
|
||||
ConditionWake(&threadContext->sync.videoFrameRequiredCond);
|
||||
ConditionWake(&threadContext->sync.videoFrameAvailableCond);
|
||||
MutexUnlock(&threadContext->sync.videoFrameMutex);
|
||||
}
|
||||
|
||||
void GBAThreadReset(struct GBAThread* threadContext) {
|
||||
MutexLock(&threadContext->stateMutex);
|
||||
_waitOnInterrupt(threadContext);
|
||||
threadContext->state = THREAD_RESETING;
|
||||
ConditionWake(&threadContext->stateCond);
|
||||
MutexUnlock(&threadContext->stateMutex);
|
||||
}
|
||||
|
||||
void GBAThreadJoin(struct GBAThread* threadContext) {
|
||||
ThreadJoin(threadContext->thread);
|
||||
|
||||
MutexDeinit(&threadContext->stateMutex);
|
||||
ConditionDeinit(&threadContext->stateCond);
|
||||
|
||||
MutexDeinit(&threadContext->sync.videoFrameMutex);
|
||||
ConditionWake(&threadContext->sync.videoFrameAvailableCond);
|
||||
ConditionDeinit(&threadContext->sync.videoFrameAvailableCond);
|
||||
ConditionWake(&threadContext->sync.videoFrameRequiredCond);
|
||||
ConditionDeinit(&threadContext->sync.videoFrameRequiredCond);
|
||||
|
||||
ConditionWake(&threadContext->sync.audioRequiredCond);
|
||||
ConditionDeinit(&threadContext->sync.audioRequiredCond);
|
||||
MutexDeinit(&threadContext->sync.audioBufferMutex);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < threadContext->rewindBufferCapacity; ++i) {
|
||||
if (threadContext->rewindBuffer[i]) {
|
||||
GBADeallocateState(threadContext->rewindBuffer[i]);
|
||||
}
|
||||
}
|
||||
free(threadContext->rewindBuffer);
|
||||
free(threadContext->rewindScreenBuffer);
|
||||
|
||||
if (threadContext->rom) {
|
||||
threadContext->rom->close(threadContext->rom);
|
||||
threadContext->rom = 0;
|
||||
}
|
||||
|
||||
if (threadContext->save) {
|
||||
threadContext->save->close(threadContext->save);
|
||||
threadContext->save = 0;
|
||||
}
|
||||
|
||||
if (threadContext->bios) {
|
||||
threadContext->bios->close(threadContext->bios);
|
||||
threadContext->bios = 0;
|
||||
}
|
||||
|
||||
if (threadContext->patch) {
|
||||
threadContext->patch->close(threadContext->patch);
|
||||
threadContext->patch = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool GBAThreadIsActive(struct GBAThread* threadContext) {
|
||||
return threadContext->state >= THREAD_RUNNING && threadContext->state < THREAD_EXITING;
|
||||
}
|
||||
|
||||
void GBAThreadInterrupt(struct GBAThread* threadContext) {
|
||||
MutexLock(&threadContext->stateMutex);
|
||||
++threadContext->interruptDepth;
|
||||
if (threadContext->interruptDepth > 1 || !GBAThreadIsActive(threadContext)) {
|
||||
MutexUnlock(&threadContext->stateMutex);
|
||||
return;
|
||||
}
|
||||
threadContext->savedState = threadContext->state;
|
||||
_waitOnInterrupt(threadContext);
|
||||
threadContext->state = THREAD_INTERRUPTING;
|
||||
threadContext->gba->cpu->nextEvent = 0;
|
||||
ConditionWake(&threadContext->stateCond);
|
||||
_waitUntilNotState(threadContext, THREAD_INTERRUPTING);
|
||||
MutexUnlock(&threadContext->stateMutex);
|
||||
}
|
||||
|
||||
void GBAThreadContinue(struct GBAThread* threadContext) {
|
||||
MutexLock(&threadContext->stateMutex);
|
||||
--threadContext->interruptDepth;
|
||||
if (threadContext->interruptDepth < 1 && GBAThreadIsActive(threadContext)) {
|
||||
threadContext->state = threadContext->savedState;
|
||||
ConditionWake(&threadContext->stateCond);
|
||||
}
|
||||
MutexUnlock(&threadContext->stateMutex);
|
||||
}
|
||||
|
||||
void GBARunOnThread(struct GBAThread* threadContext, void (*run)(struct GBAThread*)) {
|
||||
MutexLock(&threadContext->stateMutex);
|
||||
threadContext->run = run;
|
||||
_waitOnInterrupt(threadContext);
|
||||
threadContext->savedState = threadContext->state;
|
||||
threadContext->state = THREAD_RUN_ON;
|
||||
threadContext->gba->cpu->nextEvent = 0;
|
||||
ConditionWake(&threadContext->stateCond);
|
||||
_waitUntilNotState(threadContext, THREAD_RUN_ON);
|
||||
MutexUnlock(&threadContext->stateMutex);
|
||||
}
|
||||
|
||||
void GBAThreadPause(struct GBAThread* threadContext) {
|
||||
bool frameOn = threadContext->sync.videoFrameOn;
|
||||
MutexLock(&threadContext->stateMutex);
|
||||
_waitOnInterrupt(threadContext);
|
||||
if (threadContext->state == THREAD_RUNNING) {
|
||||
_pauseThread(threadContext, false);
|
||||
threadContext->frameWasOn = frameOn;
|
||||
frameOn = false;
|
||||
}
|
||||
MutexUnlock(&threadContext->stateMutex);
|
||||
|
||||
mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
|
||||
}
|
||||
|
||||
void GBAThreadUnpause(struct GBAThread* threadContext) {
|
||||
bool frameOn = threadContext->sync.videoFrameOn;
|
||||
MutexLock(&threadContext->stateMutex);
|
||||
_waitOnInterrupt(threadContext);
|
||||
if (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_PAUSING) {
|
||||
threadContext->state = THREAD_RUNNING;
|
||||
ConditionWake(&threadContext->stateCond);
|
||||
frameOn = threadContext->frameWasOn;
|
||||
}
|
||||
MutexUnlock(&threadContext->stateMutex);
|
||||
|
||||
mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
|
||||
}
|
||||
|
||||
bool GBAThreadIsPaused(struct GBAThread* threadContext) {
|
||||
bool isPaused;
|
||||
MutexLock(&threadContext->stateMutex);
|
||||
_waitOnInterrupt(threadContext);
|
||||
isPaused = threadContext->state == THREAD_PAUSED;
|
||||
MutexUnlock(&threadContext->stateMutex);
|
||||
return isPaused;
|
||||
}
|
||||
|
||||
void GBAThreadTogglePause(struct GBAThread* threadContext) {
|
||||
bool frameOn = threadContext->sync.videoFrameOn;
|
||||
MutexLock(&threadContext->stateMutex);
|
||||
_waitOnInterrupt(threadContext);
|
||||
if (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_PAUSING) {
|
||||
threadContext->state = THREAD_RUNNING;
|
||||
ConditionWake(&threadContext->stateCond);
|
||||
frameOn = threadContext->frameWasOn;
|
||||
} else if (threadContext->state == THREAD_RUNNING) {
|
||||
_pauseThread(threadContext, false);
|
||||
threadContext->frameWasOn = frameOn;
|
||||
frameOn = false;
|
||||
}
|
||||
MutexUnlock(&threadContext->stateMutex);
|
||||
|
||||
mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
|
||||
}
|
||||
|
||||
void GBAThreadPauseFromThread(struct GBAThread* threadContext) {
|
||||
bool frameOn = true;
|
||||
MutexLock(&threadContext->stateMutex);
|
||||
_waitOnInterrupt(threadContext);
|
||||
if (threadContext->state == THREAD_RUNNING) {
|
||||
_pauseThread(threadContext, true);
|
||||
frameOn = false;
|
||||
}
|
||||
MutexUnlock(&threadContext->stateMutex);
|
||||
|
||||
mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
|
||||
}
|
||||
|
||||
void GBAThreadLoadROM(struct GBAThread* threadContext, const char* fname) {
|
||||
threadContext->rom = mDirectorySetOpenPath(&threadContext->dirs, fname, GBAIsROM);
|
||||
}
|
||||
|
||||
void GBAThreadReplaceROM(struct GBAThread* threadContext, const char* fname) {
|
||||
GBAUnloadROM(threadContext->gba);
|
||||
|
||||
if (threadContext->rom) {
|
||||
threadContext->rom->close(threadContext->rom);
|
||||
threadContext->rom = 0;
|
||||
}
|
||||
|
||||
if (threadContext->save) {
|
||||
threadContext->save->close(threadContext->save);
|
||||
threadContext->save = 0;
|
||||
}
|
||||
|
||||
if (threadContext->dirs.archive) {
|
||||
threadContext->dirs.archive->close(threadContext->dirs.archive);
|
||||
threadContext->dirs.archive = 0;
|
||||
}
|
||||
|
||||
GBAThreadLoadROM(threadContext, fname);
|
||||
|
||||
threadContext->fname = fname;
|
||||
_reloadDirectories(threadContext);
|
||||
|
||||
GBARaiseIRQ(threadContext->gba, IRQ_GAMEPAK);
|
||||
GBALoadROM(threadContext->gba, threadContext->rom, threadContext->save, threadContext->fname);
|
||||
}
|
||||
|
||||
#ifdef USE_PTHREADS
|
||||
struct GBAThread* GBAThreadGetContext(void) {
|
||||
pthread_once(&_contextOnce, _createTLS);
|
||||
return pthread_getspecific(_contextKey);
|
||||
}
|
||||
#elif _WIN32
|
||||
struct GBAThread* GBAThreadGetContext(void) {
|
||||
InitOnceExecuteOnce(&_contextOnce, _createTLS, NULL, 0);
|
||||
return TlsGetValue(_contextKey);
|
||||
}
|
||||
#endif
|
||||
|
||||
void GBAThreadTakeScreenshot(struct GBAThread* threadContext) {
|
||||
GBATakeScreenshot(threadContext->gba, threadContext->dirs.screenshot);
|
||||
}
|
||||
|
||||
#else
|
||||
struct GBAThread* GBAThreadGetContext(void) {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
|
@ -1,127 +0,0 @@
|
|||
/* Copyright (c) 2013-2015 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/. */
|
||||
#ifndef GBA_THREAD_H
|
||||
#define GBA_THREAD_H
|
||||
|
||||
#include "util/common.h"
|
||||
|
||||
#include "core/directories.h"
|
||||
#include "core/sync.h"
|
||||
#include "core/thread.h"
|
||||
#include "gba/gba.h"
|
||||
#include "gba/input.h"
|
||||
#include "gba/context/overrides.h"
|
||||
|
||||
#include "util/threading.h"
|
||||
|
||||
struct GBAThread;
|
||||
struct mArguments;
|
||||
struct GBACheatSet;
|
||||
struct mCoreOptions;
|
||||
|
||||
typedef void (*GBAThreadCallback)(struct GBAThread* threadContext);
|
||||
typedef bool (*ThreadStopCallback)(struct GBAThread* threadContext);
|
||||
|
||||
struct GBAThread {
|
||||
// Output
|
||||
enum mCoreThreadState state;
|
||||
struct GBA* gba;
|
||||
struct ARMCore* cpu;
|
||||
|
||||
// Input
|
||||
struct GBAVideoRenderer* renderer;
|
||||
struct GBASIODriverSet sioDrivers;
|
||||
struct Debugger* debugger;
|
||||
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
|
||||
struct mDirectorySet dirs;
|
||||
#endif
|
||||
struct VFile* rom;
|
||||
struct VFile* save;
|
||||
struct VFile* bios;
|
||||
struct VFile* patch;
|
||||
struct VFile* cheatsFile;
|
||||
const char* fname;
|
||||
const char* movie;
|
||||
int activeKeys;
|
||||
struct mAVStream* stream;
|
||||
struct Configuration* overrides;
|
||||
enum GBAIdleLoopOptimization idleOptimization;
|
||||
bool bootBios;
|
||||
|
||||
bool hasOverride;
|
||||
struct GBACartridgeOverride override;
|
||||
|
||||
// Run-time options
|
||||
int frameskip;
|
||||
float fpsTarget;
|
||||
size_t audioBuffers;
|
||||
bool skipBios;
|
||||
int volume;
|
||||
bool mute;
|
||||
|
||||
// Threading state
|
||||
Thread thread;
|
||||
|
||||
Mutex stateMutex;
|
||||
Condition stateCond;
|
||||
enum mCoreThreadState savedState;
|
||||
int interruptDepth;
|
||||
bool frameWasOn;
|
||||
|
||||
GBALogHandler logHandler;
|
||||
int logLevel;
|
||||
GBAThreadCallback startCallback;
|
||||
GBAThreadCallback cleanCallback;
|
||||
GBAThreadCallback frameCallback;
|
||||
ThreadStopCallback stopCallback;
|
||||
void* userData;
|
||||
void (*run)(struct GBAThread*);
|
||||
|
||||
struct mCoreSync sync;
|
||||
|
||||
int rewindBufferSize;
|
||||
int rewindBufferCapacity;
|
||||
int rewindBufferInterval;
|
||||
int rewindBufferNext;
|
||||
struct GBASerializedState** rewindBuffer;
|
||||
int rewindBufferWriteOffset;
|
||||
uint8_t* rewindScreenBuffer;
|
||||
|
||||
struct GBACheatDevice* cheats;
|
||||
};
|
||||
|
||||
void GBAMapOptionsToContext(const struct mCoreOptions*, struct GBAThread*);
|
||||
void GBAMapArgumentsToContext(const struct mArguments*, struct GBAThread*);
|
||||
|
||||
bool GBAThreadStart(struct GBAThread* threadContext);
|
||||
bool GBAThreadHasStarted(struct GBAThread* threadContext);
|
||||
bool GBAThreadHasExited(struct GBAThread* threadContext);
|
||||
bool GBAThreadHasCrashed(struct GBAThread* threadContext);
|
||||
void GBAThreadEnd(struct GBAThread* threadContext);
|
||||
void GBAThreadReset(struct GBAThread* threadContext);
|
||||
void GBAThreadJoin(struct GBAThread* threadContext);
|
||||
|
||||
bool GBAThreadIsActive(struct GBAThread* threadContext);
|
||||
void GBAThreadInterrupt(struct GBAThread* threadContext);
|
||||
void GBAThreadContinue(struct GBAThread* threadContext);
|
||||
|
||||
void GBARunOnThread(struct GBAThread* threadContext, void (*run)(struct GBAThread*));
|
||||
|
||||
void GBAThreadPause(struct GBAThread* threadContext);
|
||||
void GBAThreadUnpause(struct GBAThread* threadContext);
|
||||
bool GBAThreadIsPaused(struct GBAThread* threadContext);
|
||||
void GBAThreadTogglePause(struct GBAThread* threadContext);
|
||||
void GBAThreadPauseFromThread(struct GBAThread* threadContext);
|
||||
struct GBAThread* GBAThreadGetContext(void);
|
||||
|
||||
void GBAThreadLoadROM(struct GBAThread* threadContext, const char* fname);
|
||||
void GBAThreadReplaceROM(struct GBAThread* threadContext, const char* fname);
|
||||
|
||||
#ifdef USE_PNG
|
||||
void GBAThreadTakeScreenshot(struct GBAThread* threadContext);
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -9,8 +9,8 @@
|
|||
#include <QResizeEvent>
|
||||
|
||||
extern "C" {
|
||||
#include "gba/supervisor/thread.h"
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/thread.h"
|
||||
#ifdef BUILD_GL
|
||||
#include "platform/opengl/gl.h"
|
||||
#endif
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
#include <QIcon>
|
||||
|
||||
extern "C" {
|
||||
#include "gba/supervisor/thread.h"
|
||||
#include "platform/commandline.h"
|
||||
#include "util/nointro.h"
|
||||
#include "util/socket.h"
|
||||
|
|
|
@ -11,10 +11,6 @@
|
|||
|
||||
#include "ui_LogView.h"
|
||||
|
||||
extern "C" {
|
||||
#include "gba/supervisor/thread.h"
|
||||
}
|
||||
|
||||
namespace QGBA {
|
||||
|
||||
class LogController;
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#include "GameController.h"
|
||||
|
||||
extern "C" {
|
||||
#include "gba/supervisor/thread.h"
|
||||
#include "gba/gba.h"
|
||||
}
|
||||
|
||||
using namespace QGBA;
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <QFontDatabase>
|
||||
|
||||
extern "C" {
|
||||
#include "core/core.h"
|
||||
#include "gba/supervisor/export.h"
|
||||
#include "util/vfs.h"
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
#include "GameController.h"
|
||||
|
||||
extern "C" {
|
||||
#include "core/core.h"
|
||||
#include "gba/gba.h"
|
||||
#include "util/nointro.h"
|
||||
}
|
||||
|
||||
|
|
|
@ -10,10 +10,6 @@
|
|||
|
||||
#include "ui_ROMInfo.h"
|
||||
|
||||
extern "C" {
|
||||
#include "gba/supervisor/thread.h"
|
||||
}
|
||||
|
||||
namespace QGBA {
|
||||
|
||||
class GameController;
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#ifdef M_CORE_GBA
|
||||
#include "gba/core.h"
|
||||
#include "gba/gba.h"
|
||||
#include "gba/supervisor/thread.h"
|
||||
#include "gba/video.h"
|
||||
#endif
|
||||
#ifdef M_CORE_GB
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include "sdl-audio.h"
|
||||
|
||||
#include "core/thread.h"
|
||||
#include "gba/gba.h"
|
||||
#include "gba/supervisor/thread.h"
|
||||
|
||||
#include "third-party/blip_buf/blip_buf.h"
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include "gba/io.h"
|
||||
#include "gba/rr/rr.h"
|
||||
#include "gba/serialize.h"
|
||||
#include "gba/supervisor/thread.h"
|
||||
#include "gba/video.h"
|
||||
#include "gba/renderers/video-software.h"
|
||||
#include "util/configuration.h"
|
||||
|
@ -380,159 +379,6 @@ static void _pauseAfterFrame(struct mCoreThread* context) {
|
|||
mCoreThreadPauseFromThread(context);
|
||||
}
|
||||
|
||||
static void _mSDLHandleKeypressGBA(struct GBAThread* context, struct mSDLPlayer* sdlContext, const struct SDL_KeyboardEvent* event) {
|
||||
enum GBAKey key = GBA_KEY_NONE;
|
||||
if (!event->keysym.mod) {
|
||||
#if !defined(BUILD_PANDORA) && SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
key = mInputMapKey(sdlContext->bindings, SDL_BINDING_KEY, event->keysym.scancode);
|
||||
#else
|
||||
key = mInputMapKey(sdlContext->bindings, SDL_BINDING_KEY, event->keysym.sym);
|
||||
#endif
|
||||
}
|
||||
if (key != GBA_KEY_NONE) {
|
||||
if (event->type == SDL_KEYDOWN) {
|
||||
context->activeKeys |= 1 << key;
|
||||
} else {
|
||||
context->activeKeys &= ~(1 << key);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (event->keysym.sym == SDLK_TAB) {
|
||||
context->sync.audioWait = event->type != SDL_KEYDOWN;
|
||||
return;
|
||||
}
|
||||
if (event->type == SDL_KEYDOWN) {
|
||||
switch (event->keysym.sym) {
|
||||
case SDLK_F11:
|
||||
if (context->debugger) {
|
||||
DebuggerEnter(context->debugger, DEBUGGER_ENTER_MANUAL, 0);
|
||||
}
|
||||
return;
|
||||
#ifdef USE_PNG
|
||||
case SDLK_F12:
|
||||
GBAThreadInterrupt(context);
|
||||
GBAThreadTakeScreenshot(context);
|
||||
GBAThreadContinue(context);
|
||||
return;
|
||||
#endif
|
||||
case SDLK_BACKQUOTE:
|
||||
GBAThreadInterrupt(context);
|
||||
GBARewind(context, 10);
|
||||
GBAThreadContinue(context);
|
||||
return;
|
||||
#ifdef BUILD_PANDORA
|
||||
case SDLK_ESCAPE:
|
||||
GBAThreadEnd(context);
|
||||
return;
|
||||
#endif
|
||||
default:
|
||||
if ((event->keysym.mod & GUI_MOD) && (event->keysym.mod & GUI_MOD) == event->keysym.mod) {
|
||||
switch (event->keysym.sym) {
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
case SDLK_f:
|
||||
SDL_SetWindowFullscreen(sdlContext->window, sdlContext->fullscreen ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP);
|
||||
sdlContext->fullscreen = !sdlContext->fullscreen;
|
||||
sdlContext->windowUpdated = 1;
|
||||
break;
|
||||
#endif
|
||||
case SDLK_p:
|
||||
GBAThreadTogglePause(context);
|
||||
break;
|
||||
case SDLK_r:
|
||||
GBAThreadReset(context);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (event->keysym.mod & KMOD_SHIFT) {
|
||||
switch (event->keysym.sym) {
|
||||
case SDLK_F1:
|
||||
case SDLK_F2:
|
||||
case SDLK_F3:
|
||||
case SDLK_F4:
|
||||
case SDLK_F5:
|
||||
case SDLK_F6:
|
||||
case SDLK_F7:
|
||||
case SDLK_F8:
|
||||
case SDLK_F9:
|
||||
GBAThreadInterrupt(context);
|
||||
GBASaveState(context, context->dirs.state, event->keysym.sym - SDLK_F1 + 1, SAVESTATE_SCREENSHOT);
|
||||
GBAThreadContinue(context);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (event->keysym.sym) {
|
||||
case SDLK_F1:
|
||||
case SDLK_F2:
|
||||
case SDLK_F3:
|
||||
case SDLK_F4:
|
||||
case SDLK_F5:
|
||||
case SDLK_F6:
|
||||
case SDLK_F7:
|
||||
case SDLK_F8:
|
||||
case SDLK_F9:
|
||||
GBAThreadInterrupt(context);
|
||||
GBALoadState(context, context->dirs.state, event->keysym.sym - SDLK_F1 + 1, SAVESTATE_SCREENSHOT);
|
||||
GBAThreadContinue(context);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _mSDLHandleJoyButtonGBA(struct GBAThread* context, struct mSDLPlayer* sdlContext, const struct SDL_JoyButtonEvent* event) {
|
||||
enum GBAKey key = 0;
|
||||
key = mInputMapKey(sdlContext->bindings, SDL_BINDING_BUTTON, event->button);
|
||||
if (key == GBA_KEY_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event->type == SDL_JOYBUTTONDOWN) {
|
||||
context->activeKeys |= 1 << key;
|
||||
} else {
|
||||
context->activeKeys &= ~(1 << key);
|
||||
}
|
||||
}
|
||||
|
||||
static void _mSDLHandleJoyHatGBA(struct GBAThread* context, const struct SDL_JoyHatEvent* event) {
|
||||
enum GBAKey key = 0;
|
||||
|
||||
if (event->value & SDL_HAT_UP) {
|
||||
key |= 1 << GBA_KEY_UP;
|
||||
}
|
||||
if (event->value & SDL_HAT_LEFT) {
|
||||
key |= 1 << GBA_KEY_LEFT;
|
||||
}
|
||||
if (event->value & SDL_HAT_DOWN) {
|
||||
key |= 1 << GBA_KEY_DOWN;
|
||||
}
|
||||
if (event->value & SDL_HAT_RIGHT) {
|
||||
key |= 1 << GBA_KEY_RIGHT;
|
||||
}
|
||||
|
||||
context->activeKeys &= ~((1 << GBA_KEY_UP) | (1 << GBA_KEY_LEFT) | (1 << GBA_KEY_DOWN) | (1 << GBA_KEY_RIGHT));
|
||||
context->activeKeys |= key;
|
||||
}
|
||||
|
||||
static void _mSDLHandleJoyAxisGBA(struct GBAThread* context, struct mSDLPlayer* sdlContext, const struct SDL_JoyAxisEvent* event) {
|
||||
int keys = context->activeKeys;
|
||||
|
||||
keys = mInputClearAxis(sdlContext->bindings, SDL_BINDING_BUTTON, event->axis, keys);
|
||||
enum GBAKey key = mInputMapAxis(sdlContext->bindings, SDL_BINDING_BUTTON, event->axis, event->value);
|
||||
if (key != -1) {
|
||||
keys |= 1 << key;
|
||||
}
|
||||
|
||||
context->activeKeys = keys;
|
||||
}
|
||||
|
||||
static void _mSDLHandleKeypress(struct mCoreThread* context, struct mSDLPlayer* sdlContext, const struct SDL_KeyboardEvent* event) {
|
||||
int key = -1;
|
||||
if (!event->keysym.mod) {
|
||||
|
@ -681,33 +527,6 @@ static void _mSDLHandleWindowEvent(struct mSDLPlayer* sdlContext, const struct S
|
|||
}
|
||||
#endif
|
||||
|
||||
void mSDLHandleEventGBA(struct GBAThread* context, struct mSDLPlayer* sdlContext, const union SDL_Event* event) {
|
||||
switch (event->type) {
|
||||
case SDL_QUIT:
|
||||
GBAThreadEnd(context);
|
||||
break;
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
case SDL_WINDOWEVENT:
|
||||
_mSDLHandleWindowEvent(sdlContext, &event->window);
|
||||
break;
|
||||
#endif
|
||||
case SDL_KEYDOWN:
|
||||
case SDL_KEYUP:
|
||||
_mSDLHandleKeypressGBA(context, sdlContext, &event->key);
|
||||
break;
|
||||
case SDL_JOYBUTTONDOWN:
|
||||
case SDL_JOYBUTTONUP:
|
||||
_mSDLHandleJoyButtonGBA(context, sdlContext, &event->jbutton);
|
||||
break;
|
||||
case SDL_JOYHATMOTION:
|
||||
_mSDLHandleJoyHatGBA(context, &event->jhat);
|
||||
break;
|
||||
case SDL_JOYAXISMOTION:
|
||||
_mSDLHandleJoyAxisGBA(context, sdlContext, &event->jaxis);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void mSDLHandleEvent(struct mCoreThread* context, struct mSDLPlayer* sdlContext, const union SDL_Event* event) {
|
||||
switch (event->type) {
|
||||
case SDL_QUIT:
|
||||
|
|
|
@ -93,9 +93,7 @@ void mSDLUpdateJoysticks(struct mSDLEvents* events);
|
|||
void mSDLPlayerLoadConfig(struct mSDLPlayer*, const struct Configuration*);
|
||||
void mSDLPlayerSaveConfig(const struct mSDLPlayer*, struct Configuration*);
|
||||
|
||||
struct GBAThread;
|
||||
void mSDLInitBindingsGBA(struct mInputMap* inputMap);
|
||||
void mSDLHandleEventGBA(struct GBAThread* context, struct mSDLPlayer* sdlContext, const union SDL_Event* event);
|
||||
|
||||
struct mCoreThread;
|
||||
void mSDLHandleEvent(struct mCoreThread* context, struct mSDLPlayer* sdlContext, const union SDL_Event* event);
|
||||
|
|
Loading…
Reference in New Issue