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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "gba/gba.h"
|
|
||||||
#include "util/formatting.h"
|
#include "util/formatting.h"
|
||||||
#include "util/string.h"
|
#include "util/string.h"
|
||||||
#include "util/vfs.h"
|
#include "util/vfs.h"
|
||||||
|
|
|
@ -5,10 +5,10 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
#include "audio.h"
|
#include "audio.h"
|
||||||
|
|
||||||
|
#include "core/sync.h"
|
||||||
#include "gba/gba.h"
|
#include "gba/gba.h"
|
||||||
#include "gba/io.h"
|
#include "gba/io.h"
|
||||||
#include "gba/serialize.h"
|
#include "gba/serialize.h"
|
||||||
#include "gba/supervisor/thread.h"
|
|
||||||
#include "gba/video.h"
|
#include "gba/video.h"
|
||||||
|
|
||||||
const unsigned GBA_AUDIO_SAMPLES = 2048;
|
const unsigned GBA_AUDIO_SAMPLES = 2048;
|
||||||
|
|
|
@ -5,14 +5,16 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
#include "gba.h"
|
#include "gba.h"
|
||||||
|
|
||||||
|
#include "core/thread.h"
|
||||||
|
|
||||||
#include "arm/decoder.h"
|
#include "arm/decoder.h"
|
||||||
#include "arm/isa-inlines.h"
|
#include "arm/isa-inlines.h"
|
||||||
|
|
||||||
#include "gba/bios.h"
|
#include "gba/bios.h"
|
||||||
#include "gba/cheats.h"
|
#include "gba/cheats.h"
|
||||||
|
#include "gba/context/overrides.h"
|
||||||
#include "gba/io.h"
|
#include "gba/io.h"
|
||||||
#include "gba/rr/rr.h"
|
#include "gba/rr/rr.h"
|
||||||
#include "gba/supervisor/thread.h"
|
|
||||||
#include "gba/serialize.h"
|
#include "gba/serialize.h"
|
||||||
#include "gba/sio.h"
|
#include "gba/sio.h"
|
||||||
|
|
||||||
|
@ -90,8 +92,6 @@ static void GBAInit(void* cpu, struct mCPUComponent* component) {
|
||||||
gba->romVf = 0;
|
gba->romVf = 0;
|
||||||
gba->biosVf = 0;
|
gba->biosVf = 0;
|
||||||
|
|
||||||
gba->logHandler = 0;
|
|
||||||
gba->logLevel = GBA_LOG_WARN | GBA_LOG_ERROR | GBA_LOG_FATAL;
|
|
||||||
gba->stream = 0;
|
gba->stream = 0;
|
||||||
gba->keyCallback = 0;
|
gba->keyCallback = 0;
|
||||||
gba->stopCallback = 0;
|
gba->stopCallback = 0;
|
||||||
|
@ -650,80 +650,12 @@ void GBAStop(struct GBA* gba) {
|
||||||
gba->stopCallback->stop(gba->stopCallback);
|
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, ...) {
|
void GBALog(struct GBA* gba, enum GBALogLevel level, const char* format, ...) {
|
||||||
va_list args;
|
// TODO: Kill GBALog
|
||||||
va_start(args, format);
|
|
||||||
_GBAVLog(gba, level, format, args);
|
|
||||||
va_end(args);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBADebuggerLogShim(struct Debugger* debugger, enum DebuggerLogLevel level, const char* format, ...) {
|
void GBADebuggerLogShim(struct Debugger* debugger, enum DebuggerLogLevel level, const char* format, ...) {
|
||||||
struct GBA* gba = 0;
|
// TODO: Kill GBADebuggerLogShim
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GBAIsROM(struct VFile* vf) {
|
bool GBAIsROM(struct VFile* vf) {
|
||||||
|
@ -879,18 +811,7 @@ void GBABreakpoint(struct ARMCore* cpu, int immediate) {
|
||||||
void GBAFrameStarted(struct GBA* gba) {
|
void GBAFrameStarted(struct GBA* gba) {
|
||||||
UNUSED(gba);
|
UNUSED(gba);
|
||||||
|
|
||||||
struct GBAThread* thread = GBAThreadGetContext();
|
// TODO: Put back rewind
|
||||||
if (!thread) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (thread->rewindBuffer) {
|
|
||||||
--thread->rewindBufferNext;
|
|
||||||
if (thread->rewindBufferNext <= 0) {
|
|
||||||
thread->rewindBufferNext = thread->rewindBufferInterval;
|
|
||||||
GBARecordFrame(thread);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBAFrameEnded(struct GBA* gba) {
|
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) {
|
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);
|
GBASetBreakpoint((struct GBA*) debugger->cpu->master, &debugger->d, address, mode, opcode);
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -56,7 +56,6 @@ enum {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GBA;
|
struct GBA;
|
||||||
struct GBAThread;
|
|
||||||
struct Patch;
|
struct Patch;
|
||||||
struct VFile;
|
struct VFile;
|
||||||
|
|
||||||
|
@ -114,8 +113,6 @@ struct GBA {
|
||||||
|
|
||||||
const char* activeFile;
|
const char* activeFile;
|
||||||
|
|
||||||
GBALogHandler logHandler;
|
|
||||||
enum GBALogLevel logLevel;
|
|
||||||
struct mAVStream* stream;
|
struct mAVStream* stream;
|
||||||
struct mKeyCallback* keyCallback;
|
struct mKeyCallback* keyCallback;
|
||||||
struct mStopCallback* stopCallback;
|
struct mStopCallback* stopCallback;
|
||||||
|
|
|
@ -37,11 +37,8 @@ enum GBASIOMode {
|
||||||
struct GBA;
|
struct GBA;
|
||||||
struct GBAAudio;
|
struct GBAAudio;
|
||||||
struct GBASIO;
|
struct GBASIO;
|
||||||
struct GBAThread;
|
|
||||||
struct GBAVideoRenderer;
|
struct GBAVideoRenderer;
|
||||||
|
|
||||||
typedef void (*GBALogHandler)(struct GBAThread*, enum GBALogLevel, const char* format, va_list args);
|
|
||||||
|
|
||||||
extern const int GBA_LUX_LEVELS[10];
|
extern const int GBA_LUX_LEVELS[10];
|
||||||
|
|
||||||
struct GBALuminanceSource {
|
struct GBALuminanceSource {
|
||||||
|
|
|
@ -5,11 +5,11 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
#include "serialize.h"
|
#include "serialize.h"
|
||||||
|
|
||||||
|
#include "core/sync.h"
|
||||||
#include "gba/audio.h"
|
#include "gba/audio.h"
|
||||||
#include "gba/cheats.h"
|
#include "gba/cheats.h"
|
||||||
#include "gba/io.h"
|
#include "gba/io.h"
|
||||||
#include "gba/rr/rr.h"
|
#include "gba/rr/rr.h"
|
||||||
#include "gba/supervisor/thread.h"
|
|
||||||
#include "gba/video.h"
|
#include "gba/video.h"
|
||||||
|
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
|
@ -356,66 +356,6 @@ static struct GBASerializedState* _loadPNGState(struct VFile* vf, struct GBAExtd
|
||||||
}
|
}
|
||||||
#endif
|
#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) {
|
bool GBASaveStateNamed(struct GBA* gba, struct VFile* vf, int flags) {
|
||||||
struct GBAExtdata extdata;
|
struct GBAExtdata extdata;
|
||||||
GBAExtdataInit(&extdata);
|
GBAExtdataInit(&extdata);
|
||||||
|
@ -671,82 +611,7 @@ void GBADeallocateState(struct GBASerializedState* state) {
|
||||||
mappedMemoryFree(state, sizeof(struct GBASerializedState));
|
mappedMemoryFree(state, sizeof(struct GBASerializedState));
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBARecordFrame(struct GBAThread* thread) {
|
// TODO: Put back rewind
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GBATakeScreenshot(struct GBA* gba, struct VDir* dir) {
|
void GBATakeScreenshot(struct GBA* gba, struct VDir* dir) {
|
||||||
#ifdef USE_PNG
|
#ifdef USE_PNG
|
||||||
|
|
|
@ -358,13 +358,10 @@ struct GBAExtdata {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VDir;
|
struct VDir;
|
||||||
struct GBAThread;
|
|
||||||
|
|
||||||
void GBASerialize(struct GBA* gba, struct GBASerializedState* state);
|
void GBASerialize(struct GBA* gba, struct GBASerializedState* state);
|
||||||
bool GBADeserialize(struct GBA* gba, const 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);
|
struct VFile* GBAGetState(struct GBA* gba, struct VDir* dir, int slot, bool write);
|
||||||
void GBADeleteState(struct GBA* thread, struct VDir* dir, int slot);
|
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);
|
struct GBASerializedState* GBAAllocateState(void);
|
||||||
void GBADeallocateState(struct GBASerializedState* state);
|
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);
|
void GBATakeScreenshot(struct GBA* gba, struct VDir* dir);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
|
|
||||||
#include "gba/io.h"
|
#include "gba/io.h"
|
||||||
#include "gba/serialize.h"
|
#include "gba/serialize.h"
|
||||||
#include "gba/supervisor/thread.h"
|
|
||||||
|
|
||||||
#ifdef USE_CLI_DEBUGGER
|
#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>
|
#include <QResizeEvent>
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "gba/supervisor/thread.h"
|
#include "core/core.h"
|
||||||
|
#include "core/thread.h"
|
||||||
#ifdef BUILD_GL
|
#ifdef BUILD_GL
|
||||||
#include "platform/opengl/gl.h"
|
#include "platform/opengl/gl.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "gba/supervisor/thread.h"
|
|
||||||
#include "platform/commandline.h"
|
#include "platform/commandline.h"
|
||||||
#include "util/nointro.h"
|
#include "util/nointro.h"
|
||||||
#include "util/socket.h"
|
#include "util/socket.h"
|
||||||
|
|
|
@ -11,10 +11,6 @@
|
||||||
|
|
||||||
#include "ui_LogView.h"
|
#include "ui_LogView.h"
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
#include "gba/supervisor/thread.h"
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace QGBA {
|
namespace QGBA {
|
||||||
|
|
||||||
class LogController;
|
class LogController;
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
#include "GameController.h"
|
#include "GameController.h"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "gba/supervisor/thread.h"
|
#include "gba/gba.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
using namespace QGBA;
|
using namespace QGBA;
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include <QFontDatabase>
|
#include <QFontDatabase>
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
#include "core/core.h"
|
||||||
#include "gba/supervisor/export.h"
|
#include "gba/supervisor/export.h"
|
||||||
#include "util/vfs.h"
|
#include "util/vfs.h"
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
#include "GameController.h"
|
#include "GameController.h"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
#include "core/core.h"
|
||||||
|
#include "gba/gba.h"
|
||||||
#include "util/nointro.h"
|
#include "util/nointro.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,10 +10,6 @@
|
||||||
|
|
||||||
#include "ui_ROMInfo.h"
|
#include "ui_ROMInfo.h"
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
#include "gba/supervisor/thread.h"
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace QGBA {
|
namespace QGBA {
|
||||||
|
|
||||||
class GameController;
|
class GameController;
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
#ifdef M_CORE_GBA
|
#ifdef M_CORE_GBA
|
||||||
#include "gba/core.h"
|
#include "gba/core.h"
|
||||||
#include "gba/gba.h"
|
#include "gba/gba.h"
|
||||||
#include "gba/supervisor/thread.h"
|
|
||||||
#include "gba/video.h"
|
#include "gba/video.h"
|
||||||
#endif
|
#endif
|
||||||
#ifdef M_CORE_GB
|
#ifdef M_CORE_GB
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
#include "sdl-audio.h"
|
#include "sdl-audio.h"
|
||||||
|
|
||||||
|
#include "core/thread.h"
|
||||||
#include "gba/gba.h"
|
#include "gba/gba.h"
|
||||||
#include "gba/supervisor/thread.h"
|
|
||||||
|
|
||||||
#include "third-party/blip_buf/blip_buf.h"
|
#include "third-party/blip_buf/blip_buf.h"
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
#include "gba/io.h"
|
#include "gba/io.h"
|
||||||
#include "gba/rr/rr.h"
|
#include "gba/rr/rr.h"
|
||||||
#include "gba/serialize.h"
|
#include "gba/serialize.h"
|
||||||
#include "gba/supervisor/thread.h"
|
|
||||||
#include "gba/video.h"
|
#include "gba/video.h"
|
||||||
#include "gba/renderers/video-software.h"
|
#include "gba/renderers/video-software.h"
|
||||||
#include "util/configuration.h"
|
#include "util/configuration.h"
|
||||||
|
@ -380,159 +379,6 @@ static void _pauseAfterFrame(struct mCoreThread* context) {
|
||||||
mCoreThreadPauseFromThread(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) {
|
static void _mSDLHandleKeypress(struct mCoreThread* context, struct mSDLPlayer* sdlContext, const struct SDL_KeyboardEvent* event) {
|
||||||
int key = -1;
|
int key = -1;
|
||||||
if (!event->keysym.mod) {
|
if (!event->keysym.mod) {
|
||||||
|
@ -681,33 +527,6 @@ static void _mSDLHandleWindowEvent(struct mSDLPlayer* sdlContext, const struct S
|
||||||
}
|
}
|
||||||
#endif
|
#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) {
|
void mSDLHandleEvent(struct mCoreThread* context, struct mSDLPlayer* sdlContext, const union SDL_Event* event) {
|
||||||
switch (event->type) {
|
switch (event->type) {
|
||||||
case SDL_QUIT:
|
case SDL_QUIT:
|
||||||
|
|
|
@ -93,9 +93,7 @@ void mSDLUpdateJoysticks(struct mSDLEvents* events);
|
||||||
void mSDLPlayerLoadConfig(struct mSDLPlayer*, const struct Configuration*);
|
void mSDLPlayerLoadConfig(struct mSDLPlayer*, const struct Configuration*);
|
||||||
void mSDLPlayerSaveConfig(const struct mSDLPlayer*, struct Configuration*);
|
void mSDLPlayerSaveConfig(const struct mSDLPlayer*, struct Configuration*);
|
||||||
|
|
||||||
struct GBAThread;
|
|
||||||
void mSDLInitBindingsGBA(struct mInputMap* inputMap);
|
void mSDLInitBindingsGBA(struct mInputMap* inputMap);
|
||||||
void mSDLHandleEventGBA(struct GBAThread* context, struct mSDLPlayer* sdlContext, const union SDL_Event* event);
|
|
||||||
|
|
||||||
struct mCoreThread;
|
struct mCoreThread;
|
||||||
void mSDLHandleEvent(struct mCoreThread* context, struct mSDLPlayer* sdlContext, const union SDL_Event* event);
|
void mSDLHandleEvent(struct mCoreThread* context, struct mSDLPlayer* sdlContext, const union SDL_Event* event);
|
||||||
|
|
Loading…
Reference in New Issue