mirror of https://github.com/mgba-emu/mgba.git
GBA Video: Fix Hblank timing
This commit is contained in:
parent
c73ad72d41
commit
befef8ee26
1
CHANGES
1
CHANGES
|
@ -9,6 +9,7 @@ Emulation fixes:
|
||||||
- GBA Serialize: Fix audio DMA timing deserialization
|
- GBA Serialize: Fix audio DMA timing deserialization
|
||||||
- GBA Video: Fix OAM not invalidating after reset (fixes mgba.io/i/1630)
|
- 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: Latch scanline at end of Hblank (fixes mgba.io/i/1319)
|
||||||
|
- GBA Video: Fix Hblank timing
|
||||||
Other fixes:
|
Other fixes:
|
||||||
- Qt: Only dynamically reset video scale if a game is running
|
- Qt: Only dynamically reset video scale if a game is running
|
||||||
- Qt: Fix race condition with proxied video events
|
- Qt: Fix race condition with proxied video events
|
||||||
|
|
|
@ -92,7 +92,8 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
|
||||||
* | bits 6 - 7: Reserved
|
* | bits 6 - 7: Reserved
|
||||||
* 0x001E0 - 0x001FF: Video miscellaneous state
|
* 0x001E0 - 0x001FF: Video miscellaneous state
|
||||||
* | 0x001E0 - 0x001E3: Next event
|
* | 0x001E0 - 0x001E3: Next event
|
||||||
* | 0x001E4 - 0x001FB: Reserved
|
* | 0x001E4 - 0x001F7: Reserved
|
||||||
|
* | 0x001F8 - 0x001FB: Miscellaneous flags
|
||||||
* | 0x001FC - 0x001FF: Frame counter
|
* | 0x001FC - 0x001FF: Frame counter
|
||||||
* 0x00200 - 0x00213: Timer 0
|
* 0x00200 - 0x00213: Timer 0
|
||||||
* | 0x00200 - 0x00201: Reload value
|
* | 0x00200 - 0x00201: Reload value
|
||||||
|
@ -210,6 +211,9 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
|
||||||
* Total size: 0x61000 (397,312) bytes
|
* Total size: 0x61000 (397,312) bytes
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
DECL_BITFIELD(GBASerializedVideoFlags, uint32_t);
|
||||||
|
DECL_BITS(GBASerializedVideoFlags, Mode, 0, 2);
|
||||||
|
|
||||||
DECL_BITFIELD(GBASerializedHWFlags1, uint16_t);
|
DECL_BITFIELD(GBASerializedHWFlags1, uint16_t);
|
||||||
DECL_BIT(GBASerializedHWFlags1, ReadWrite, 0);
|
DECL_BIT(GBASerializedHWFlags1, ReadWrite, 0);
|
||||||
DECL_BIT(GBASerializedHWFlags1, GyroEdge, 1);
|
DECL_BIT(GBASerializedHWFlags1, GyroEdge, 1);
|
||||||
|
@ -267,7 +271,8 @@ struct GBASerializedState {
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
int32_t nextEvent;
|
int32_t nextEvent;
|
||||||
int32_t reserved[6];
|
int32_t reserved[5];
|
||||||
|
GBASerializedVideoFlags flags;
|
||||||
int32_t frameCounter;
|
int32_t frameCounter;
|
||||||
} video;
|
} video;
|
||||||
|
|
||||||
|
|
|
@ -18,8 +18,9 @@ mLOG_DECLARE_CATEGORY(GBA_VIDEO);
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
VIDEO_HBLANK_PIXELS = 68,
|
VIDEO_HBLANK_PIXELS = 68,
|
||||||
VIDEO_HDRAW_LENGTH = 1006,
|
VIDEO_HDRAW_LENGTH = 960,
|
||||||
VIDEO_HBLANK_LENGTH = 226,
|
VIDEO_HBLANK_LENGTH = 272,
|
||||||
|
VIDEO_HBLANK_FLIP = 46,
|
||||||
VIDEO_HORIZONTAL_LENGTH = 1232,
|
VIDEO_HORIZONTAL_LENGTH = 1232,
|
||||||
|
|
||||||
VIDEO_VBLANK_PIXELS = 68,
|
VIDEO_VBLANK_PIXELS = 68,
|
||||||
|
|
|
@ -31,6 +31,7 @@ static void GBAVideoDummyRendererGetPixels(struct GBAVideoRenderer* renderer, si
|
||||||
static void GBAVideoDummyRendererPutPixels(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 _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);
|
static void _startHdraw(struct mTiming*, void* context, uint32_t cyclesLate);
|
||||||
|
|
||||||
MGBA_EXPORT const int GBAVideoObjSizes[16][2] = {
|
MGBA_EXPORT const int GBAVideoObjSizes[16][2] = {
|
||||||
|
@ -116,10 +117,17 @@ void GBAVideoAssociateRenderer(struct GBAVideo* video, struct GBAVideoRenderer*
|
||||||
video->renderer->init(video->renderer);
|
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;
|
struct GBAVideo* video = context;
|
||||||
GBARegisterDISPSTAT dispstat = video->p->memory.io[REG_DISPSTAT >> 1];
|
GBARegisterDISPSTAT dispstat = video->p->memory.io[REG_DISPSTAT >> 1];
|
||||||
dispstat = GBARegisterDISPSTATClearInHblank(dispstat);
|
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;
|
video->event.callback = _startHblank;
|
||||||
mTimingSchedule(timing, &video->event, VIDEO_HDRAW_LENGTH - cyclesLate);
|
mTimingSchedule(timing, &video->event, VIDEO_HDRAW_LENGTH - cyclesLate);
|
||||||
|
|
||||||
|
@ -133,6 +141,7 @@ void _startHdraw(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||||
video->renderer->drawScanline(video->renderer, video->vcount);
|
video->renderer->drawScanline(video->renderer, video->vcount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GBARegisterDISPSTAT dispstat = video->p->memory.io[REG_DISPSTAT >> 1];
|
||||||
if (video->vcount == GBARegisterDISPSTATGetVcountSetting(dispstat)) {
|
if (video->vcount == GBARegisterDISPSTATGetVcountSetting(dispstat)) {
|
||||||
dispstat = GBARegisterDISPSTATFillVcounter(dispstat);
|
dispstat = GBARegisterDISPSTATFillVcounter(dispstat);
|
||||||
if (GBARegisterDISPSTATIsVcounterIRQ(dispstat)) {
|
if (GBARegisterDISPSTATIsVcounterIRQ(dispstat)) {
|
||||||
|
@ -173,12 +182,11 @@ void _startHdraw(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||||
|
|
||||||
void _startHblank(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
void _startHblank(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||||
struct GBAVideo* video = context;
|
struct GBAVideo* video = context;
|
||||||
GBARegisterDISPSTAT dispstat = video->p->memory.io[REG_DISPSTAT >> 1];
|
video->event.callback = _midHblank;
|
||||||
dispstat = GBARegisterDISPSTATFillInHblank(dispstat);
|
mTimingSchedule(timing, &video->event, VIDEO_HBLANK_LENGTH - VIDEO_HBLANK_FLIP - cyclesLate);
|
||||||
video->event.callback = _startHdraw;
|
|
||||||
mTimingSchedule(timing, &video->event, VIDEO_HBLANK_LENGTH - cyclesLate);
|
|
||||||
|
|
||||||
// Begin Hblank
|
// Begin Hblank
|
||||||
|
GBARegisterDISPSTAT dispstat = video->p->memory.io[REG_DISPSTAT >> 1];
|
||||||
dispstat = GBARegisterDISPSTATFillInHblank(dispstat);
|
dispstat = GBARegisterDISPSTATFillInHblank(dispstat);
|
||||||
|
|
||||||
if (video->vcount < GBA_VIDEO_VERTICAL_PIXELS) {
|
if (video->vcount < GBA_VIDEO_VERTICAL_PIXELS) {
|
||||||
|
@ -304,6 +312,15 @@ void GBAVideoSerialize(const struct GBAVideo* video, struct GBASerializedState*
|
||||||
memcpy(state->oam, video->oam.raw, SIZE_OAM);
|
memcpy(state->oam, video->oam.raw, SIZE_OAM);
|
||||||
memcpy(state->pram, video->palette, SIZE_PALETTE_RAM);
|
memcpy(state->pram, video->palette, SIZE_PALETTE_RAM);
|
||||||
STORE_32(video->event.when - mTimingCurrentTime(&video->p->timing), 0, &state->video.nextEvent);
|
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);
|
STORE_32(video->frameCounter, 0, &state->video.frameCounter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,14 +338,28 @@ void GBAVideoDeserialize(struct GBAVideo* video, const struct GBASerializedState
|
||||||
}
|
}
|
||||||
LOAD_32(video->frameCounter, 0, &state->video.frameCounter);
|
LOAD_32(video->frameCounter, 0, &state->video.frameCounter);
|
||||||
|
|
||||||
|
int32_t flags;
|
||||||
|
LOAD_32(flags, 0, &state->video.flags);
|
||||||
|
GBARegisterDISPSTAT dispstat = state->io[REG_DISPSTAT >> 1];
|
||||||
|
switch (GBASerializedVideoFlagsGetMode(flags)) {
|
||||||
|
case 0:
|
||||||
|
if (GBARegisterDISPSTATIsInHblank(dispstat)) {
|
||||||
|
video->event.callback = _startHdraw;
|
||||||
|
} else {
|
||||||
|
video->event.callback = _startHblank;
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
video->event.callback = _startHdraw;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
video->event.callback = _startHblank;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
video->event.callback = _midHblank;
|
||||||
|
break;
|
||||||
|
}
|
||||||
uint32_t when;
|
uint32_t when;
|
||||||
LOAD_32(when, 0, &state->video.nextEvent);
|
LOAD_32(when, 0, &state->video.nextEvent);
|
||||||
GBARegisterDISPSTAT dispstat = state->io[REG_DISPSTAT >> 1];
|
|
||||||
if (GBARegisterDISPSTATIsInHblank(dispstat)) {
|
|
||||||
video->event.callback = _startHdraw;
|
|
||||||
} else {
|
|
||||||
video->event.callback = _startHblank;
|
|
||||||
}
|
|
||||||
mTimingSchedule(&video->p->timing, &video->event, when);
|
mTimingSchedule(&video->p->timing, &video->event, when);
|
||||||
|
|
||||||
LOAD_16(video->vcount, REG_VCOUNT, state->io);
|
LOAD_16(video->vcount, REG_VCOUNT, state->io);
|
||||||
|
|
Loading…
Reference in New Issue