mirror of https://github.com/mgba-emu/mgba.git
GB Video: Convert to mTiming
This commit is contained in:
parent
6243b7fd3b
commit
bf9be29ad5
|
@ -36,6 +36,17 @@ void mTimingSchedule(struct mTiming* timing, struct mTimingEvent* event, int32_t
|
||||||
*mTimingEventListAppend(&timing->events) = event;
|
*mTimingEventListAppend(&timing->events) = event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void mTimingDeschedule(struct mTiming* timing, struct mTimingEvent* event) {
|
||||||
|
size_t e;
|
||||||
|
for (e = 0; e < mTimingEventListSize(&timing->events); ++e) {
|
||||||
|
struct mTimingEvent* next = *mTimingEventListGetPointer(&timing->events, e);
|
||||||
|
if (next == event) {
|
||||||
|
mTimingEventListShift(&timing->events, e, 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void mTimingTick(struct mTiming* timing, int32_t cycles) {
|
void mTimingTick(struct mTiming* timing, int32_t cycles) {
|
||||||
timing->masterCycles += cycles;
|
timing->masterCycles += cycles;
|
||||||
while (mTimingEventListSize(&timing->events)) {
|
while (mTimingEventListSize(&timing->events)) {
|
||||||
|
|
|
@ -29,6 +29,7 @@ void mTimingInit(struct mTiming* timing);
|
||||||
void mTimingDeinit(struct mTiming* timing);
|
void mTimingDeinit(struct mTiming* timing);
|
||||||
void mTimingClear(struct mTiming* timing);
|
void mTimingClear(struct mTiming* timing);
|
||||||
void mTimingSchedule(struct mTiming* timing, struct mTimingEvent*, int32_t when);
|
void mTimingSchedule(struct mTiming* timing, struct mTimingEvent*, int32_t when);
|
||||||
|
void mTimingDeschedule(struct mTiming* timing, struct mTimingEvent*);
|
||||||
void mTimingTick(struct mTiming* timing, int32_t cycles);
|
void mTimingTick(struct mTiming* timing, int32_t cycles);
|
||||||
int32_t mTimingNextEvent(struct mTiming* timing);
|
int32_t mTimingNextEvent(struct mTiming* timing);
|
||||||
|
|
||||||
|
|
|
@ -549,14 +549,6 @@ void GBProcessEvents(struct LR35902Core* cpu) {
|
||||||
nextEvent = testEvent;
|
nextEvent = testEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
testEvent = GBVideoProcessEvents(&gb->video, cycles >> gb->doubleSpeed);
|
|
||||||
if (testEvent != INT_MAX) {
|
|
||||||
testEvent <<= gb->doubleSpeed;
|
|
||||||
if (testEvent < nextEvent) {
|
|
||||||
nextEvent = testEvent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
testEvent = GBAudioProcessEvents(&gb->audio, cycles >> gb->doubleSpeed);
|
testEvent = GBAudioProcessEvents(&gb->audio, cycles >> gb->doubleSpeed);
|
||||||
if (testEvent != INT_MAX) {
|
if (testEvent != INT_MAX) {
|
||||||
testEvent <<= gb->doubleSpeed;
|
testEvent <<= gb->doubleSpeed;
|
||||||
|
|
344
src/gb/video.c
344
src/gb/video.c
|
@ -27,6 +27,12 @@ static void GBVideoDummyRendererPutPixels(struct GBVideoRenderer* renderer, size
|
||||||
|
|
||||||
static void _cleanOAM(struct GBVideo* video, int y);
|
static void _cleanOAM(struct GBVideo* video, int y);
|
||||||
|
|
||||||
|
static void _endMode0(struct mTiming* timing, void* context, uint32_t cyclesLate);
|
||||||
|
static void _endMode1(struct mTiming* timing, void* context, uint32_t cyclesLate);
|
||||||
|
static void _endMode2(struct mTiming* timing, void* context, uint32_t cyclesLate);
|
||||||
|
static void _endMode3(struct mTiming* timing, void* context, uint32_t cyclesLate);
|
||||||
|
static void _updateFrameCount(struct mTiming* timing, void* context, uint32_t cyclesLate);
|
||||||
|
|
||||||
static struct GBVideoRenderer dummyRenderer = {
|
static struct GBVideoRenderer dummyRenderer = {
|
||||||
.init = GBVideoDummyRendererInit,
|
.init = GBVideoDummyRendererInit,
|
||||||
.deinit = GBVideoDummyRendererDeinit,
|
.deinit = GBVideoDummyRendererDeinit,
|
||||||
|
@ -45,6 +51,13 @@ void GBVideoInit(struct GBVideo* video) {
|
||||||
video->renderer->cache = NULL;
|
video->renderer->cache = NULL;
|
||||||
video->vram = 0;
|
video->vram = 0;
|
||||||
video->frameskip = 0;
|
video->frameskip = 0;
|
||||||
|
|
||||||
|
video->modeEvent.context = video;
|
||||||
|
video->modeEvent.name = "GB Video Mode";
|
||||||
|
video->modeEvent.callback = NULL;
|
||||||
|
video->frameEvent.context = video;
|
||||||
|
video->frameEvent.name = "GB Video Frame";
|
||||||
|
video->frameEvent.callback = _updateFrameCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBVideoReset(struct GBVideo* video) {
|
void GBVideoReset(struct GBVideo* video) {
|
||||||
|
@ -53,13 +66,6 @@ void GBVideoReset(struct GBVideo* video) {
|
||||||
video->mode = 1;
|
video->mode = 1;
|
||||||
video->stat = 1;
|
video->stat = 1;
|
||||||
|
|
||||||
video->nextEvent = INT_MAX;
|
|
||||||
video->eventDiff = 0;
|
|
||||||
|
|
||||||
video->nextMode = INT_MAX;
|
|
||||||
video->dotCounter = INT_MIN;
|
|
||||||
video->nextFrame = INT_MAX;
|
|
||||||
|
|
||||||
video->frameCounter = 0;
|
video->frameCounter = 0;
|
||||||
video->frameskipCounter = 0;
|
video->frameskipCounter = 0;
|
||||||
|
|
||||||
|
@ -90,155 +96,158 @@ void GBVideoAssociateRenderer(struct GBVideo* video, struct GBVideoRenderer* ren
|
||||||
video->renderer->init(video->renderer, video->p->model);
|
video->renderer->init(video->renderer, video->p->model);
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t GBVideoProcessEvents(struct GBVideo* video, int32_t cycles) {
|
void _endMode0(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||||
video->eventDiff += cycles;
|
struct GBVideo* video = context;
|
||||||
if (video->nextEvent != INT_MAX) {
|
if (video->frameskipCounter <= 0) {
|
||||||
video->nextEvent -= cycles;
|
video->renderer->finishScanline(video->renderer, video->ly);
|
||||||
}
|
}
|
||||||
if (video->nextEvent <= 0) {
|
int lyc = video->p->memory.io[REG_LYC];
|
||||||
if (video->nextMode != INT_MAX) {
|
int32_t next;
|
||||||
video->nextMode -= video->eventDiff;
|
++video->ly;
|
||||||
|
video->p->memory.io[REG_LY] = video->ly;
|
||||||
|
video->stat = GBRegisterSTATSetLYC(video->stat, lyc == video->ly);
|
||||||
|
if (video->ly < GB_VIDEO_VERTICAL_PIXELS) {
|
||||||
|
// TODO: Cache SCX & 7 in case it changes during mode 2
|
||||||
|
next = GB_VIDEO_MODE_2_LENGTH + (video->p->memory.io[REG_SCX] & 7);
|
||||||
|
video->mode = 2;
|
||||||
|
video->modeEvent.callback = _endMode2;
|
||||||
|
if (!GBRegisterSTATIsHblankIRQ(video->stat) && GBRegisterSTATIsOAMIRQ(video->stat)) {
|
||||||
|
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
||||||
}
|
}
|
||||||
if (video->nextFrame != INT_MAX) {
|
} else {
|
||||||
video->nextFrame -= video->eventDiff;
|
next = GB_VIDEO_HORIZONTAL_LENGTH;
|
||||||
|
video->mode = 1;
|
||||||
|
video->modeEvent.callback = _endMode1;
|
||||||
|
|
||||||
|
_updateFrameCount(timing, video, cyclesLate);
|
||||||
|
|
||||||
|
if (GBRegisterSTATIsVblankIRQ(video->stat) || GBRegisterSTATIsOAMIRQ(video->stat)) {
|
||||||
|
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
||||||
}
|
}
|
||||||
video->nextEvent = INT_MAX;
|
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_VBLANK);
|
||||||
GBVideoProcessDots(video);
|
|
||||||
if (video->nextMode <= 0) {
|
|
||||||
int lyc = video->p->memory.io[REG_LYC];
|
|
||||||
switch (video->mode) {
|
|
||||||
case 0:
|
|
||||||
if (video->frameskipCounter <= 0) {
|
|
||||||
video->renderer->finishScanline(video->renderer, video->ly);
|
|
||||||
}
|
|
||||||
++video->ly;
|
|
||||||
video->p->memory.io[REG_LY] = video->ly;
|
|
||||||
video->stat = GBRegisterSTATSetLYC(video->stat, lyc == video->ly);
|
|
||||||
if (video->ly < GB_VIDEO_VERTICAL_PIXELS) {
|
|
||||||
video->nextMode = GB_VIDEO_MODE_2_LENGTH + (video->p->memory.io[REG_SCX] & 7);
|
|
||||||
video->mode = 2;
|
|
||||||
if (!GBRegisterSTATIsHblankIRQ(video->stat) && GBRegisterSTATIsOAMIRQ(video->stat)) {
|
|
||||||
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
video->nextMode = GB_VIDEO_HORIZONTAL_LENGTH;
|
|
||||||
video->mode = 1;
|
|
||||||
|
|
||||||
if (video->nextFrame != 0) {
|
struct mCoreCallbacks* callbacks = video->p->coreCallbacks;
|
||||||
video->nextFrame = 0;
|
if (callbacks && callbacks->videoFrameEnded) {
|
||||||
}
|
callbacks->videoFrameEnded(callbacks->context);
|
||||||
|
|
||||||
if (GBRegisterSTATIsVblankIRQ(video->stat) || GBRegisterSTATIsOAMIRQ(video->stat)) {
|
|
||||||
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
|
||||||
}
|
|
||||||
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_VBLANK);
|
|
||||||
|
|
||||||
struct mCoreCallbacks* callbacks = video->p->coreCallbacks;
|
|
||||||
if (callbacks && callbacks->videoFrameEnded) {
|
|
||||||
callbacks->videoFrameEnded(callbacks->context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!GBRegisterSTATIsHblankIRQ(video->stat) && GBRegisterSTATIsLYCIRQ(video->stat) && lyc == video->ly) {
|
|
||||||
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
|
||||||
}
|
|
||||||
GBUpdateIRQs(video->p);
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
// TODO: One M-cycle delay
|
|
||||||
++video->ly;
|
|
||||||
if (video->ly == GB_VIDEO_VERTICAL_TOTAL_PIXELS + 1) {
|
|
||||||
video->ly = 0;
|
|
||||||
video->p->memory.io[REG_LY] = video->ly;
|
|
||||||
// TODO: Cache SCX & 7 in case it changes during mode 2
|
|
||||||
video->nextMode = GB_VIDEO_MODE_2_LENGTH + (video->p->memory.io[REG_SCX] & 7);
|
|
||||||
video->mode = 2;
|
|
||||||
if (GBRegisterSTATIsOAMIRQ(video->stat)) {
|
|
||||||
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
|
||||||
GBUpdateIRQs(video->p);
|
|
||||||
}
|
|
||||||
video->renderer->finishFrame(video->renderer);
|
|
||||||
if (video->p->memory.mbcType == GB_MBC7 && video->p->memory.rotation && video->p->memory.rotation->sample) {
|
|
||||||
video->p->memory.rotation->sample(video->p->memory.rotation);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
} else if (video->ly == GB_VIDEO_VERTICAL_TOTAL_PIXELS) {
|
|
||||||
video->p->memory.io[REG_LY] = 0;
|
|
||||||
video->nextMode = GB_VIDEO_HORIZONTAL_LENGTH - 8;
|
|
||||||
} else if (video->ly == GB_VIDEO_VERTICAL_TOTAL_PIXELS - 1) {
|
|
||||||
video->p->memory.io[REG_LY] = video->ly;
|
|
||||||
video->nextMode = 8;
|
|
||||||
} else {
|
|
||||||
video->p->memory.io[REG_LY] = video->ly;
|
|
||||||
video->nextMode = GB_VIDEO_HORIZONTAL_LENGTH;
|
|
||||||
}
|
|
||||||
|
|
||||||
video->stat = GBRegisterSTATSetLYC(video->stat, lyc == video->p->memory.io[REG_LY]);
|
|
||||||
if (GBRegisterSTATIsLYCIRQ(video->stat) && lyc == video->p->memory.io[REG_LY]) {
|
|
||||||
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
|
||||||
GBUpdateIRQs(video->p);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
_cleanOAM(video, video->ly);
|
|
||||||
video->dotCounter = 0;
|
|
||||||
video->nextEvent = GB_VIDEO_HORIZONTAL_LENGTH;
|
|
||||||
video->x = 0;
|
|
||||||
// TODO: Estimate sprite timings better
|
|
||||||
video->nextMode = GB_VIDEO_MODE_3_LENGTH_BASE + video->objMax * 11 - (video->p->memory.io[REG_SCX] & 7);
|
|
||||||
video->mode = 3;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
video->nextMode = GB_VIDEO_MODE_0_LENGTH_BASE - video->objMax * 11;
|
|
||||||
video->mode = 0;
|
|
||||||
if (GBRegisterSTATIsHblankIRQ(video->stat)) {
|
|
||||||
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
|
||||||
GBUpdateIRQs(video->p);
|
|
||||||
}
|
|
||||||
if (video->ly < GB_VIDEO_VERTICAL_PIXELS && video->p->memory.isHdma && video->p->memory.io[REG_HDMA5] != 0xFF) {
|
|
||||||
video->p->memory.hdmaRemaining = 0x10;
|
|
||||||
mTimingSchedule(&video->p->timing, &video->p->memory.hdmaEvent, 0);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
video->stat = GBRegisterSTATSetMode(video->stat, video->mode);
|
|
||||||
video->p->memory.io[REG_STAT] = video->stat;
|
|
||||||
}
|
}
|
||||||
if (video->nextFrame <= 0) {
|
|
||||||
if (video->p->cpu->executionState == LR35902_CORE_FETCH) {
|
|
||||||
GBFrameEnded(video->p);
|
|
||||||
video->nextFrame = GB_VIDEO_TOTAL_LENGTH;
|
|
||||||
video->nextEvent = GB_VIDEO_TOTAL_LENGTH;
|
|
||||||
|
|
||||||
--video->frameskipCounter;
|
|
||||||
if (video->frameskipCounter < 0) {
|
|
||||||
mCoreSyncPostFrame(video->p->sync);
|
|
||||||
video->frameskipCounter = video->frameskip;
|
|
||||||
}
|
|
||||||
++video->frameCounter;
|
|
||||||
|
|
||||||
if (video->p->stream && video->p->stream->postVideoFrame) {
|
|
||||||
const color_t* pixels;
|
|
||||||
size_t stride;
|
|
||||||
video->renderer->getPixels(video->renderer, &stride, (const void**) &pixels);
|
|
||||||
video->p->stream->postVideoFrame(video->p->stream, pixels, stride);
|
|
||||||
}
|
|
||||||
struct mCoreCallbacks* callbacks = video->p->coreCallbacks;
|
|
||||||
if (callbacks && callbacks->videoFrameStarted) {
|
|
||||||
callbacks->videoFrameStarted(callbacks->context);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
video->nextFrame = 4 - ((video->p->cpu->executionState + 1) & 3);
|
|
||||||
if (video->nextFrame < video->nextEvent) {
|
|
||||||
video->nextEvent = video->nextFrame;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (video->nextMode < video->nextEvent) {
|
|
||||||
video->nextEvent = video->nextMode;
|
|
||||||
}
|
|
||||||
video->eventDiff = 0;
|
|
||||||
}
|
}
|
||||||
return video->nextEvent;
|
if (!GBRegisterSTATIsHblankIRQ(video->stat) && GBRegisterSTATIsLYCIRQ(video->stat) && lyc == video->ly) {
|
||||||
|
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
||||||
|
}
|
||||||
|
GBUpdateIRQs(video->p);
|
||||||
|
video->stat = GBRegisterSTATSetMode(video->stat, video->mode);
|
||||||
|
video->p->memory.io[REG_STAT] = video->stat;
|
||||||
|
mTimingSchedule(timing, &video->modeEvent, (next << video->p->doubleSpeed) - cyclesLate);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _endMode1(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||||
|
struct GBVideo* video = context;
|
||||||
|
if (!GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int lyc = video->p->memory.io[REG_LYC];
|
||||||
|
// TODO: One M-cycle delay
|
||||||
|
++video->ly;
|
||||||
|
int32_t next;
|
||||||
|
if (video->ly == GB_VIDEO_VERTICAL_TOTAL_PIXELS + 1) {
|
||||||
|
video->ly = 0;
|
||||||
|
video->p->memory.io[REG_LY] = video->ly;
|
||||||
|
next = GB_VIDEO_MODE_2_LENGTH + (video->p->memory.io[REG_SCX] & 7);
|
||||||
|
video->mode = 2;
|
||||||
|
video->modeEvent.callback = _endMode2;
|
||||||
|
if (GBRegisterSTATIsOAMIRQ(video->stat)) {
|
||||||
|
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
||||||
|
GBUpdateIRQs(video->p);
|
||||||
|
}
|
||||||
|
video->renderer->finishFrame(video->renderer);
|
||||||
|
if (video->p->memory.mbcType == GB_MBC7 && video->p->memory.rotation && video->p->memory.rotation->sample) {
|
||||||
|
video->p->memory.rotation->sample(video->p->memory.rotation);
|
||||||
|
}
|
||||||
|
} else if (video->ly == GB_VIDEO_VERTICAL_TOTAL_PIXELS) {
|
||||||
|
video->p->memory.io[REG_LY] = 0;
|
||||||
|
next = GB_VIDEO_HORIZONTAL_LENGTH - 8;
|
||||||
|
} else if (video->ly == GB_VIDEO_VERTICAL_TOTAL_PIXELS - 1) {
|
||||||
|
video->p->memory.io[REG_LY] = video->ly;
|
||||||
|
next = 8;
|
||||||
|
} else {
|
||||||
|
video->p->memory.io[REG_LY] = video->ly;
|
||||||
|
next = GB_VIDEO_HORIZONTAL_LENGTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
video->stat = GBRegisterSTATSetMode(video->stat, video->mode);
|
||||||
|
video->stat = GBRegisterSTATSetLYC(video->stat, lyc == video->p->memory.io[REG_LY]);
|
||||||
|
if (video->ly && GBRegisterSTATIsLYCIRQ(video->stat) && lyc == video->p->memory.io[REG_LY]) {
|
||||||
|
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
||||||
|
GBUpdateIRQs(video->p);
|
||||||
|
}
|
||||||
|
video->p->memory.io[REG_STAT] = video->stat;
|
||||||
|
mTimingSchedule(timing, &video->modeEvent, (next << video->p->doubleSpeed) - cyclesLate);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _endMode2(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||||
|
struct GBVideo* video = context;
|
||||||
|
_cleanOAM(video, video->ly);
|
||||||
|
video->x = 0;
|
||||||
|
video->dotClock = timing->masterCycles - cyclesLate;
|
||||||
|
int32_t next = GB_VIDEO_MODE_3_LENGTH_BASE + video->objMax * 11 - (video->p->memory.io[REG_SCX] & 7);
|
||||||
|
video->mode = 3;
|
||||||
|
video->modeEvent.callback = _endMode3;
|
||||||
|
video->stat = GBRegisterSTATSetMode(video->stat, video->mode);
|
||||||
|
video->p->memory.io[REG_STAT] = video->stat;
|
||||||
|
mTimingSchedule(timing, &video->modeEvent, (next << video->p->doubleSpeed) - cyclesLate);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _endMode3(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||||
|
struct GBVideo* video = context;
|
||||||
|
GBVideoProcessDots(video);
|
||||||
|
if (GBRegisterSTATIsHblankIRQ(video->stat)) {
|
||||||
|
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
||||||
|
GBUpdateIRQs(video->p);
|
||||||
|
}
|
||||||
|
if (video->ly < GB_VIDEO_VERTICAL_PIXELS && video->p->memory.isHdma && video->p->memory.io[REG_HDMA5] != 0xFF) {
|
||||||
|
video->p->memory.hdmaRemaining = 0x10;
|
||||||
|
mTimingSchedule(timing, &video->p->memory.hdmaEvent, 0);
|
||||||
|
}
|
||||||
|
video->mode = 0;
|
||||||
|
video->modeEvent.callback = _endMode0;
|
||||||
|
video->stat = GBRegisterSTATSetMode(video->stat, video->mode);
|
||||||
|
video->p->memory.io[REG_STAT] = video->stat;
|
||||||
|
int32_t next = GB_VIDEO_MODE_0_LENGTH_BASE - video->objMax * 11;
|
||||||
|
mTimingSchedule(timing, &video->modeEvent, (next << video->p->doubleSpeed) - cyclesLate);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _updateFrameCount(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||||
|
UNUSED(cyclesLate);
|
||||||
|
struct GBVideo* video = context;
|
||||||
|
if (video->p->cpu->executionState != LR35902_CORE_FETCH) {
|
||||||
|
mTimingSchedule(timing, &video->frameEvent, 4 - ((video->p->cpu->executionState + 1) & 3));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GBFrameEnded(video->p);
|
||||||
|
--video->frameskipCounter;
|
||||||
|
if (video->frameskipCounter < 0) {
|
||||||
|
mCoreSyncPostFrame(video->p->sync);
|
||||||
|
video->frameskipCounter = video->frameskip;
|
||||||
|
}
|
||||||
|
++video->frameCounter;
|
||||||
|
|
||||||
|
// TODO: Move to common code
|
||||||
|
if (video->p->stream && video->p->stream->postVideoFrame) {
|
||||||
|
const color_t* pixels;
|
||||||
|
size_t stride;
|
||||||
|
video->renderer->getPixels(video->renderer, &stride, (const void**) &pixels);
|
||||||
|
video->p->stream->postVideoFrame(video->p->stream, pixels, stride);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct mCoreCallbacks* callbacks = video->p->coreCallbacks;
|
||||||
|
if (callbacks && callbacks->videoFrameStarted) {
|
||||||
|
callbacks->videoFrameStarted(callbacks->context);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC])) {
|
||||||
|
mTimingSchedule(timing, &video->frameEvent, GB_VIDEO_TOTAL_LENGTH);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _cleanOAM(struct GBVideo* video, int y) {
|
static void _cleanOAM(struct GBVideo* video, int y) {
|
||||||
|
@ -267,20 +276,17 @@ static void _cleanOAM(struct GBVideo* video, int y) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBVideoProcessDots(struct GBVideo* video) {
|
void GBVideoProcessDots(struct GBVideo* video) {
|
||||||
if (video->mode != 3 || video->dotCounter < 0) {
|
if (video->mode != 3) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int oldX = video->x;
|
int oldX = video->x;
|
||||||
video->x = video->dotCounter + video->eventDiff + (video->p->cpu->cycles >> video->p->doubleSpeed);
|
video->x = (video->p->timing.masterCycles - video->dotClock + video->p->cpu->cycles) >> video->p->doubleSpeed;
|
||||||
if (video->x > GB_VIDEO_HORIZONTAL_PIXELS) {
|
if (video->x > GB_VIDEO_HORIZONTAL_PIXELS) {
|
||||||
video->x = GB_VIDEO_HORIZONTAL_PIXELS;
|
video->x = GB_VIDEO_HORIZONTAL_PIXELS;
|
||||||
} else if (video->x < 0) {
|
} else if (video->x < 0) {
|
||||||
mLOG(GB, FATAL, "Video dot clock went negative!");
|
mLOG(GB, FATAL, "Video dot clock went negative!");
|
||||||
video->x = oldX;
|
video->x = oldX;
|
||||||
}
|
}
|
||||||
if (video->x == GB_VIDEO_HORIZONTAL_PIXELS) {
|
|
||||||
video->dotCounter = INT_MIN;
|
|
||||||
}
|
|
||||||
if (video->frameskipCounter <= 0) {
|
if (video->frameskipCounter <= 0) {
|
||||||
video->renderer->drawRange(video->renderer, oldX, video->x, video->ly, video->objThisLine, video->objMax);
|
video->renderer->drawRange(video->renderer, oldX, video->x, video->ly, video->objThisLine, video->objMax);
|
||||||
}
|
}
|
||||||
|
@ -289,9 +295,10 @@ void GBVideoProcessDots(struct GBVideo* video) {
|
||||||
void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) {
|
void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) {
|
||||||
if (!GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC]) && GBRegisterLCDCIsEnable(value)) {
|
if (!GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC]) && GBRegisterLCDCIsEnable(value)) {
|
||||||
video->mode = 2;
|
video->mode = 2;
|
||||||
video->nextMode = GB_VIDEO_MODE_2_LENGTH - 5; // TODO: Why is this fudge factor needed? Might be related to T-cycles for load/store differing
|
video->modeEvent.callback = _endMode2;
|
||||||
video->nextEvent = video->nextMode;
|
int32_t next = GB_VIDEO_MODE_2_LENGTH - 5; // TODO: Why is this fudge factor needed? Might be related to T-cycles for load/store differing
|
||||||
video->eventDiff = -video->p->cpu->cycles >> video->p->doubleSpeed;
|
mTimingSchedule(&video->p->timing, &video->modeEvent, next << video->p->doubleSpeed);
|
||||||
|
|
||||||
video->ly = 0;
|
video->ly = 0;
|
||||||
video->p->memory.io[REG_LY] = 0;
|
video->p->memory.io[REG_LY] = 0;
|
||||||
// TODO: Does this read as 0 for 4 T-cycles?
|
// TODO: Does this read as 0 for 4 T-cycles?
|
||||||
|
@ -302,21 +309,16 @@ void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) {
|
||||||
GBUpdateIRQs(video->p);
|
GBUpdateIRQs(video->p);
|
||||||
}
|
}
|
||||||
video->p->memory.io[REG_STAT] = video->stat;
|
video->p->memory.io[REG_STAT] = video->stat;
|
||||||
|
mTimingDeschedule(&video->p->timing, &video->frameEvent);
|
||||||
if (video->p->cpu->cycles + (video->nextEvent << video->p->doubleSpeed) < video->p->cpu->nextEvent) {
|
|
||||||
video->p->cpu->nextEvent = video->p->cpu->cycles + (video->nextEvent << video->p->doubleSpeed);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC]) && !GBRegisterLCDCIsEnable(value)) {
|
if (GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC]) && !GBRegisterLCDCIsEnable(value)) {
|
||||||
video->mode = 0;
|
video->stat = GBRegisterSTATSetMode(video->stat, 0);
|
||||||
video->nextMode = INT_MAX;
|
|
||||||
video->nextEvent = video->nextFrame;
|
|
||||||
video->stat = GBRegisterSTATSetMode(video->stat, video->mode);
|
|
||||||
video->p->memory.io[REG_STAT] = video->stat;
|
video->p->memory.io[REG_STAT] = video->stat;
|
||||||
video->ly = 0;
|
video->ly = 0;
|
||||||
video->p->memory.io[REG_LY] = 0;
|
video->p->memory.io[REG_LY] = 0;
|
||||||
|
mTimingSchedule(&video->p->timing, &video->frameEvent, GB_VIDEO_TOTAL_LENGTH);
|
||||||
}
|
}
|
||||||
|
video->p->memory.io[REG_STAT] = video->stat;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBVideoWriteSTAT(struct GBVideo* video, GBRegisterSTAT value) {
|
void GBVideoWriteSTAT(struct GBVideo* video, GBRegisterSTAT value) {
|
||||||
|
@ -486,10 +488,6 @@ static void GBVideoDummyRendererPutPixels(struct GBVideoRenderer* renderer, size
|
||||||
void GBVideoSerialize(const struct GBVideo* video, struct GBSerializedState* state) {
|
void GBVideoSerialize(const struct GBVideo* video, struct GBSerializedState* state) {
|
||||||
STORE_16LE(video->x, 0, &state->video.x);
|
STORE_16LE(video->x, 0, &state->video.x);
|
||||||
STORE_16LE(video->ly, 0, &state->video.ly);
|
STORE_16LE(video->ly, 0, &state->video.ly);
|
||||||
STORE_32LE(video->nextEvent, 0, &state->video.nextEvent);
|
|
||||||
STORE_32LE(video->eventDiff, 0, &state->video.eventDiff);
|
|
||||||
STORE_32LE(video->nextMode, 0, &state->video.nextMode);
|
|
||||||
STORE_32LE(video->dotCounter, 0, &state->video.dotCounter);
|
|
||||||
STORE_32LE(video->frameCounter, 0, &state->video.frameCounter);
|
STORE_32LE(video->frameCounter, 0, &state->video.frameCounter);
|
||||||
state->video.vramCurrentBank = video->vramCurrentBank;
|
state->video.vramCurrentBank = video->vramCurrentBank;
|
||||||
|
|
||||||
|
@ -513,10 +511,6 @@ void GBVideoSerialize(const struct GBVideo* video, struct GBSerializedState* sta
|
||||||
void GBVideoDeserialize(struct GBVideo* video, const struct GBSerializedState* state) {
|
void GBVideoDeserialize(struct GBVideo* video, const struct GBSerializedState* state) {
|
||||||
LOAD_16LE(video->x, 0, &state->video.x);
|
LOAD_16LE(video->x, 0, &state->video.x);
|
||||||
LOAD_16LE(video->ly, 0, &state->video.ly);
|
LOAD_16LE(video->ly, 0, &state->video.ly);
|
||||||
LOAD_32LE(video->nextEvent, 0, &state->video.nextEvent);
|
|
||||||
LOAD_32LE(video->eventDiff, 0, &state->video.eventDiff);
|
|
||||||
LOAD_32LE(video->nextMode, 0, &state->video.nextMode);
|
|
||||||
LOAD_32LE(video->dotCounter, 0, &state->video.dotCounter);
|
|
||||||
LOAD_32LE(video->frameCounter, 0, &state->video.frameCounter);
|
LOAD_32LE(video->frameCounter, 0, &state->video.frameCounter);
|
||||||
video->vramCurrentBank = state->video.vramCurrentBank;
|
video->vramCurrentBank = state->video.vramCurrentBank;
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "util/common.h"
|
#include "util/common.h"
|
||||||
|
|
||||||
#include "core/interface.h"
|
#include "core/interface.h"
|
||||||
|
#include "core/timing.h"
|
||||||
#include "gb/interface.h"
|
#include "gb/interface.h"
|
||||||
#include "gb/memory.h"
|
#include "gb/memory.h"
|
||||||
|
|
||||||
|
@ -101,13 +102,10 @@ struct GBVideo {
|
||||||
|
|
||||||
int mode;
|
int mode;
|
||||||
|
|
||||||
int32_t nextEvent;
|
struct mTimingEvent modeEvent;
|
||||||
int32_t eventDiff;
|
struct mTimingEvent frameEvent;
|
||||||
|
|
||||||
int32_t nextMode;
|
uint32_t dotClock;
|
||||||
int32_t dotCounter;
|
|
||||||
|
|
||||||
int32_t nextFrame;
|
|
||||||
|
|
||||||
uint8_t* vram;
|
uint8_t* vram;
|
||||||
uint8_t* vramBank;
|
uint8_t* vramBank;
|
||||||
|
|
Loading…
Reference in New Issue