GBA Video: Fix Hblank timing
Vicki Pfau vi@endrift.com
Mon, 27 Jan 2020 18:27:03 -0800
4 files changed,
51 insertions(+),
13 deletions(-)
M
CHANGES
→
CHANGES
@@ -9,6 +9,7 @@ - GBA Serialize: Fix audio serialization for desynced FIFOs
- GBA Serialize: Fix audio DMA timing deserialization - GBA Video: Fix OAM not invalidating after reset (fixes mgba.io/i/1630) - GBA Video: Latch scanline at end of Hblank (fixes mgba.io/i/1319) + - GBA Video: Fix Hblank timing Other fixes: - Qt: Only dynamically reset video scale if a game is running - Qt: Fix race condition with proxied video events
M
include/mgba/internal/gba/serialize.h
→
include/mgba/internal/gba/serialize.h
@@ -92,7 +92,8 @@ * | bit 5: Has channel 1 sweep occurred?
* | bits 6 - 7: Reserved * 0x001E0 - 0x001FF: Video miscellaneous state * | 0x001E0 - 0x001E3: Next event - * | 0x001E4 - 0x001FB: Reserved + * | 0x001E4 - 0x001F7: Reserved + * | 0x001F8 - 0x001FB: Miscellaneous flags * | 0x001FC - 0x001FF: Frame counter * 0x00200 - 0x00213: Timer 0 * | 0x00200 - 0x00201: Reload value@@ -210,6 +211,9 @@ * 0x21000 - 0x60FFF: WRAM
* Total size: 0x61000 (397,312) bytes */ +DECL_BITFIELD(GBASerializedVideoFlags, uint32_t); +DECL_BITS(GBASerializedVideoFlags, Mode, 0, 2); + DECL_BITFIELD(GBASerializedHWFlags1, uint16_t); DECL_BIT(GBASerializedHWFlags1, ReadWrite, 0); DECL_BIT(GBASerializedHWFlags1, GyroEdge, 1);@@ -267,7 +271,8 @@ } audio;
struct { int32_t nextEvent; - int32_t reserved[6]; + int32_t reserved[5]; + GBASerializedVideoFlags flags; int32_t frameCounter; } video;
M
include/mgba/internal/gba/video.h
→
include/mgba/internal/gba/video.h
@@ -18,8 +18,9 @@ mLOG_DECLARE_CATEGORY(GBA_VIDEO);
enum { VIDEO_HBLANK_PIXELS = 68, - VIDEO_HDRAW_LENGTH = 1006, - VIDEO_HBLANK_LENGTH = 226, + VIDEO_HDRAW_LENGTH = 960, + VIDEO_HBLANK_LENGTH = 272, + VIDEO_HBLANK_FLIP = 46, VIDEO_HORIZONTAL_LENGTH = 1232, VIDEO_VBLANK_PIXELS = 68,
M
src/gba/video.c
→
src/gba/video.c
@@ -31,6 +31,7 @@ static void GBAVideoDummyRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels);
static void GBAVideoDummyRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels); static void _startHblank(struct mTiming*, void* context, uint32_t cyclesLate); +static void _midHblank(struct mTiming*, void* context, uint32_t cyclesLate); static void _startHdraw(struct mTiming*, void* context, uint32_t cyclesLate); MGBA_EXPORT const int GBAVideoObjSizes[16][2] = {@@ -116,10 +117,17 @@ renderer->oam = &video->oam;
video->renderer->init(video->renderer); } -void _startHdraw(struct mTiming* timing, void* context, uint32_t cyclesLate) { +void _midHblank(struct mTiming* timing, void* context, uint32_t cyclesLate) { struct GBAVideo* video = context; GBARegisterDISPSTAT dispstat = video->p->memory.io[REG_DISPSTAT >> 1]; dispstat = GBARegisterDISPSTATClearInHblank(dispstat); + video->p->memory.io[REG_DISPSTAT >> 1] = dispstat; + video->event.callback = _startHdraw; + mTimingSchedule(timing, &video->event, VIDEO_HBLANK_FLIP - cyclesLate); +} + +void _startHdraw(struct mTiming* timing, void* context, uint32_t cyclesLate) { + struct GBAVideo* video = context; video->event.callback = _startHblank; mTimingSchedule(timing, &video->event, VIDEO_HDRAW_LENGTH - cyclesLate);@@ -133,6 +141,7 @@ if (video->vcount < GBA_VIDEO_VERTICAL_PIXELS && video->frameskipCounter <= 0) {
video->renderer->drawScanline(video->renderer, video->vcount); } + GBARegisterDISPSTAT dispstat = video->p->memory.io[REG_DISPSTAT >> 1]; if (video->vcount == GBARegisterDISPSTATGetVcountSetting(dispstat)) { dispstat = GBARegisterDISPSTATFillVcounter(dispstat); if (GBARegisterDISPSTATIsVcounterIRQ(dispstat)) {@@ -173,12 +182,11 @@ }
void _startHblank(struct mTiming* timing, void* context, uint32_t cyclesLate) { struct GBAVideo* video = context; - GBARegisterDISPSTAT dispstat = video->p->memory.io[REG_DISPSTAT >> 1]; - dispstat = GBARegisterDISPSTATFillInHblank(dispstat); - video->event.callback = _startHdraw; - mTimingSchedule(timing, &video->event, VIDEO_HBLANK_LENGTH - cyclesLate); + video->event.callback = _midHblank; + mTimingSchedule(timing, &video->event, VIDEO_HBLANK_LENGTH - VIDEO_HBLANK_FLIP - cyclesLate); // Begin Hblank + GBARegisterDISPSTAT dispstat = video->p->memory.io[REG_DISPSTAT >> 1]; dispstat = GBARegisterDISPSTATFillInHblank(dispstat); if (video->vcount < GBA_VIDEO_VERTICAL_PIXELS) {@@ -304,6 +312,15 @@ memcpy(state->vram, video->vram, SIZE_VRAM);
memcpy(state->oam, video->oam.raw, SIZE_OAM); memcpy(state->pram, video->palette, SIZE_PALETTE_RAM); STORE_32(video->event.when - mTimingCurrentTime(&video->p->timing), 0, &state->video.nextEvent); + int32_t flags = 0; + if (video->event.callback == _startHdraw) { + flags = GBASerializedVideoFlagsSetMode(flags, 1); + } else if (video->event.callback == _startHblank) { + flags = GBASerializedVideoFlagsSetMode(flags, 2); + } else if (video->event.callback == _midHblank) { + flags = GBASerializedVideoFlagsSetMode(flags, 3); + } + STORE_32(flags, 0, &state->video.flags); STORE_32(video->frameCounter, 0, &state->video.frameCounter); }@@ -321,14 +338,28 @@ GBAStore16(video->p->cpu, BASE_PALETTE_RAM | i, value, 0);
} LOAD_32(video->frameCounter, 0, &state->video.frameCounter); - uint32_t when; - LOAD_32(when, 0, &state->video.nextEvent); + int32_t flags; + LOAD_32(flags, 0, &state->video.flags); GBARegisterDISPSTAT dispstat = state->io[REG_DISPSTAT >> 1]; - if (GBARegisterDISPSTATIsInHblank(dispstat)) { + switch (GBASerializedVideoFlagsGetMode(flags)) { + case 0: + if (GBARegisterDISPSTATIsInHblank(dispstat)) { + video->event.callback = _startHdraw; + } else { + video->event.callback = _startHblank; + } + case 1: video->event.callback = _startHdraw; - } else { + break; + case 2: video->event.callback = _startHblank; + break; + case 3: + video->event.callback = _midHblank; + break; } + uint32_t when; + LOAD_32(when, 0, &state->video.nextEvent); mTimingSchedule(&video->p->timing, &video->event, when); LOAD_16(video->vcount, REG_VCOUNT, state->io);