GBA Video: Refactor video routines to be in a more consistent state during callbacks

This commit is contained in:
Jeffrey Pfau 2015-01-24 01:02:09 -08:00
parent a7357df857
commit 19758d7115
6 changed files with 78 additions and 59 deletions

View File

@ -66,12 +66,12 @@ static bool _GBACLIDebuggerCustom(struct CLIDebuggerSystem* debugger) {
struct GBACLIDebugger* gbaDebugger = (struct GBACLIDebugger*) debugger;
if (gbaDebugger->frameAdvance) {
if (!gbaDebugger->inVblank && GBARegisterDISPSTATIsInVblank(gbaDebugger->context->gba->video.dispstat)) {
if (!gbaDebugger->inVblank && GBARegisterDISPSTATIsInVblank(gbaDebugger->context->gba->memory.io[REG_DISPSTAT >> 1])) {
ARMDebuggerEnter(&gbaDebugger->d.p->d, DEBUGGER_ENTER_BREAKPOINT);
gbaDebugger->frameAdvance = false;
return false;
}
gbaDebugger->inVblank = GBARegisterDISPSTATGetInVblank(gbaDebugger->context->gba->video.dispstat);
gbaDebugger->inVblank = GBARegisterDISPSTATGetInVblank(gbaDebugger->context->gba->memory.io[REG_DISPSTAT >> 1]);
return true;
}
return false;
@ -96,7 +96,7 @@ static void _frame(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
struct GBACLIDebugger* gbaDebugger = (struct GBACLIDebugger*) debugger->system;
gbaDebugger->frameAdvance = true;
gbaDebugger->inVblank = GBARegisterDISPSTATGetInVblank(gbaDebugger->context->gba->video.dispstat);
gbaDebugger->inVblank = GBARegisterDISPSTATGetInVblank(gbaDebugger->context->gba->memory.io[REG_DISPSTAT >> 1]);
}
static void _load(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {

View File

@ -616,25 +616,6 @@ void GBASyncPostFrame(struct GBASync* sync) {
} while (sync->videoFrameWait && sync->videoFramePending);
}
MutexUnlock(&sync->videoFrameMutex);
struct GBAThread* thread = GBAThreadGetContext();
if (!thread) {
return;
}
if (thread->rewindBuffer) {
--thread->rewindBufferNext;
if (thread->rewindBufferNext <= 0) {
thread->rewindBufferNext = thread->rewindBufferInterval;
GBARecordFrame(thread);
}
}
if (thread->stream) {
thread->stream->postVideoFrame(thread->stream, thread->renderer);
}
if (thread->frameCallback) {
thread->frameCallback(thread);
}
}
bool GBASyncWaitFrameStart(struct GBASync* sync, int frameskip) {

View File

@ -41,7 +41,6 @@ void GBAVideoInit(struct GBAVideo* video) {
}
void GBAVideoReset(struct GBAVideo* video) {
video->dispstat = 0;
video->vcount = VIDEO_VERTICAL_TOTAL_PIXELS - 1;
video->lastHblank = 0;
@ -96,53 +95,57 @@ int32_t GBAVideoProcessEvents(struct GBAVideo* video, int32_t cycles) {
video->nextHblank -= video->eventDiff;
video->nextHblankIRQ -= video->eventDiff;
video->nextVcounterIRQ -= video->eventDiff;
video->eventDiff = 0;
uint16_t dispstat = video->p->memory.io[REG_DISPSTAT >> 1];
if (GBARegisterDISPSTATIsInHblank(video->dispstat)) {
if (GBARegisterDISPSTATIsInHblank(dispstat)) {
// End Hblank
video->dispstat = GBARegisterDISPSTATClearInHblank(video->dispstat);
dispstat = GBARegisterDISPSTATClearInHblank(dispstat);
video->nextEvent = video->nextHblank;
++video->vcount;
if (video->vcount == VIDEO_VERTICAL_TOTAL_PIXELS) {
video->vcount = 0;
}
video->p->memory.io[REG_VCOUNT >> 1] = video->vcount;
if (video->vcount == GBARegisterDISPSTATGetVcountSetting(dispstat)) {
dispstat = GBARegisterDISPSTATFillVcounter(dispstat);
if (GBARegisterDISPSTATIsVcounterIRQ(dispstat)) {
GBARaiseIRQ(video->p, IRQ_VCOUNTER);
video->nextVcounterIRQ += VIDEO_TOTAL_LENGTH;
}
} else {
dispstat = GBARegisterDISPSTATClearVcounter(dispstat);
}
video->p->memory.io[REG_DISPSTAT >> 1] = dispstat;
// Note: state may be recorded during callbacks, so ensure it is consistent!
switch (video->vcount) {
case 0:
GBAFrameStarted(video->p);
break;
case VIDEO_VERTICAL_PIXELS:
video->dispstat = GBARegisterDISPSTATFillInVblank(video->dispstat);
video->p->memory.io[REG_DISPSTAT >> 1] = GBARegisterDISPSTATFillInVblank(dispstat);
if (GBASyncDrawingFrame(video->p->sync)) {
video->renderer->finishFrame(video->renderer);
}
video->nextVblankIRQ = video->nextEvent + VIDEO_TOTAL_LENGTH;
GBAMemoryRunVblankDMAs(video->p, lastEvent);
if (GBARegisterDISPSTATIsVblankIRQ(video->dispstat)) {
if (GBARegisterDISPSTATIsVblankIRQ(dispstat)) {
GBARaiseIRQ(video->p, IRQ_VBLANK);
}
GBAFrameEnded(video->p);
GBASyncPostFrame(video->p->sync);
++video->frameCounter;
break;
case VIDEO_VERTICAL_TOTAL_PIXELS - 1:
if (video->p->rr) {
GBARRNextFrame(video->p->rr);
}
video->dispstat = GBARegisterDISPSTATClearInVblank(video->dispstat);
video->p->memory.io[REG_DISPSTAT >> 1] = GBARegisterDISPSTATClearInVblank(dispstat);
break;
case VIDEO_VERTICAL_TOTAL_PIXELS:
video->vcount = 0;
video->p->memory.io[REG_VCOUNT >> 1] = 0;
break;
}
if (video->vcount == GBARegisterDISPSTATGetVcountSetting(video->dispstat)) {
video->dispstat = GBARegisterDISPSTATFillVcounter(video->dispstat);
if (GBARegisterDISPSTATIsVcounterIRQ(video->dispstat)) {
GBARaiseIRQ(video->p, IRQ_VCOUNTER);
video->nextVcounterIRQ += VIDEO_TOTAL_LENGTH;
}
} else {
video->dispstat = GBARegisterDISPSTATClearVcounter(video->dispstat);
}
} else {
// Begin Hblank
video->dispstat = GBARegisterDISPSTATFillInHblank(video->dispstat);
dispstat = GBARegisterDISPSTATFillInHblank(dispstat);
video->lastHblank = video->nextHblank;
video->nextEvent = video->lastHblank + VIDEO_HBLANK_LENGTH;
video->nextHblank = video->nextEvent + VIDEO_HDRAW_LENGTH;
@ -155,25 +158,24 @@ int32_t GBAVideoProcessEvents(struct GBAVideo* video, int32_t cycles) {
if (video->vcount < VIDEO_VERTICAL_PIXELS) {
GBAMemoryRunHblankDMAs(video->p, lastEvent);
}
if (GBARegisterDISPSTATIsHblankIRQ(video->dispstat)) {
if (GBARegisterDISPSTATIsHblankIRQ(dispstat)) {
GBARaiseIRQ(video->p, IRQ_HBLANK);
}
video->p->memory.io[REG_DISPSTAT >> 1] = dispstat;
}
video->eventDiff = 0;
}
video->p->memory.io[REG_DISPSTAT >> 1] &= 0xFFF8;
video->p->memory.io[REG_DISPSTAT >> 1] |= video->dispstat & 0x7;
return video->nextEvent;
}
void GBAVideoWriteDISPSTAT(struct GBAVideo* video, uint16_t value) {
video->dispstat &= 0x7;
video->dispstat |= value & 0xFFF8;
video->p->memory.io[REG_DISPSTAT >> 1] &= 0x7;
video->p->memory.io[REG_DISPSTAT >> 1] |= value & 0xFFF8;
if (GBARegisterDISPSTATIsVcounterIRQ(video->dispstat)) {
uint16_t dispstat = video->p->memory.io[REG_DISPSTAT >> 1];
if (GBARegisterDISPSTATIsVcounterIRQ(dispstat)) {
// FIXME: this can be too late if we're in the middle of an Hblank
video->nextVcounterIRQ = video->nextHblank + VIDEO_HBLANK_LENGTH + (GBARegisterDISPSTATGetVcountSetting(video->dispstat) - video->vcount) * VIDEO_HORIZONTAL_LENGTH;
video->nextVcounterIRQ = video->nextHblank + VIDEO_HBLANK_LENGTH + (GBARegisterDISPSTATGetVcountSetting(dispstat) - video->vcount) * VIDEO_HORIZONTAL_LENGTH;
if (video->nextVcounterIRQ < video->nextEvent) {
video->nextVcounterIRQ += VIDEO_TOTAL_LENGTH;
}
@ -237,7 +239,6 @@ void GBAVideoSerialize(struct GBAVideo* video, struct GBASerializedState* state)
memcpy(state->vram, video->renderer->vram, SIZE_VRAM);
memcpy(state->oam, video->oam.raw, SIZE_OAM);
memcpy(state->pram, video->palette, SIZE_PALETTE_RAM);
state->io[REG_DISPSTAT >> 1] = video->dispstat;
state->video.nextEvent = video->nextEvent;
state->video.eventDiff = video->eventDiff;
state->video.lastHblank = video->lastHblank;
@ -257,7 +258,6 @@ void GBAVideoDeserialize(struct GBAVideo* video, struct GBASerializedState* stat
for (i = 0; i < SIZE_PALETTE_RAM; i += 2) {
GBAStore16(video->p->cpu, BASE_PALETTE_RAM | i, state->pram[i >> 1], 0);
}
video->dispstat = state->io[REG_DISPSTAT >> 1];
video->nextEvent = state->video.nextEvent;
video->eventDiff = state->video.eventDiff;
video->lastHblank = state->video.lastHblank;

View File

@ -173,8 +173,6 @@ struct GBAVideo {
struct GBA* p;
struct GBAVideoRenderer* renderer;
GBARegisterDISPSTAT dispstat;
// VCOUNT
int vcount;

View File

@ -8,6 +8,7 @@
#include "gba-bios.h"
#include "gba-io.h"
#include "gba-rr.h"
#include "gba-serialize.h"
#include "gba-sio.h"
#include "gba-thread.h"
@ -634,3 +635,39 @@ void GBAIllegal(struct ARMCore* cpu, uint32_t opcode) {
ARMDebuggerEnter(gba->debugger, DEBUGGER_ENTER_ILLEGAL_OP);
}
}
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);
}
}
}
void GBAFrameEnded(struct GBA* gba) {
if (gba->rr) {
GBARRNextFrame(gba->rr);
}
struct GBAThread* thread = GBAThreadGetContext();
if (!thread) {
return;
}
if (thread->stream) {
thread->stream->postVideoFrame(thread->stream, thread->renderer);
}
if (thread->frameCallback) {
thread->frameCallback(thread);
}
}

View File

@ -181,6 +181,9 @@ bool GBAIsBIOS(struct VFile* vf);
void GBAGetGameCode(struct GBA* gba, char* out);
void GBAGetGameTitle(struct GBA* gba, char* out);
void GBAFrameStarted(struct GBA* gba);
void GBAFrameEnded(struct GBA* gba);
__attribute__((format (printf, 3, 4)))
void GBALog(struct GBA* gba, enum GBALogLevel level, const char* format, ...);