all repos — mgba @ a1689c80a71813c3506eca2a87a4add5dbf3f53e

mGBA Game Boy Advance Emulator

GBA Memory: Fix DMA timing
Jeffrey Pfau jeffrey@endrift.com
Tue, 13 Dec 2016 18:04:12 -0800
commit

a1689c80a71813c3506eca2a87a4add5dbf3f53e

parent

82a0088e1ec4a1fe13f9eff96c9c72d22995013e

4 files changed, 29 insertions(+), 4 deletions(-)

jump to
M src/core/timing.csrc/core/timing.c

@@ -68,6 +68,10 @@ }

return *timing->nextEvent; } +int32_t mTimingCurrentTime(struct mTiming* timing) { + return timing->masterCycles + *timing->relativeCycles; +} + int32_t mTimingNextEvent(struct mTiming* timing) { struct mTimingEvent* next = timing->root; if (!next) {
M src/core/timing.hsrc/core/timing.h

@@ -32,6 +32,7 @@ void mTimingClear(struct mTiming* timing);

void mTimingSchedule(struct mTiming* timing, struct mTimingEvent*, int32_t when); void mTimingDeschedule(struct mTiming* timing, struct mTimingEvent*); int32_t mTimingTick(struct mTiming* timing, int32_t cycles); +int32_t mTimingCurrentTime(struct mTiming* timing); int32_t mTimingNextEvent(struct mTiming* timing); #endif
M src/gba/memory.csrc/gba/memory.c

@@ -1555,8 +1555,9 @@ void GBAMemoryScheduleDMA(struct GBA* gba, int number, struct GBADMA* info) {

info->hasStarted = 0; switch (GBADMARegisterGetTiming(info->reg)) { case DMA_TIMING_NOW: - info->nextEvent = 2; - GBAMemoryUpdateDMAs(gba, -1); + info->nextEvent = 2 + 1; // XXX: Account for I cycle when writing + info->scheduledAt = mTimingCurrentTime(&gba->timing); + GBAMemoryUpdateDMAs(gba, 0); break; case DMA_TIMING_HBLANK: // Handled implicitly

@@ -1586,27 +1587,43 @@

void GBAMemoryRunHblankDMAs(struct GBA* gba, int32_t cycles) { struct GBAMemory* memory = &gba->memory; struct GBADMA* dma; + bool dmaSeen = false; + if (memory->activeDMA >= 0) { + GBAMemoryUpdateDMAs(gba, mTimingCurrentTime(&gba->timing) - memory->dma[memory->activeDMA].scheduledAt); + } int i; for (i = 0; i < 4; ++i) { dma = &memory->dma[i]; if (GBADMARegisterIsEnable(dma->reg) && GBADMARegisterGetTiming(dma->reg) == DMA_TIMING_HBLANK) { dma->nextEvent = 2 + cycles; + dma->scheduledAt = mTimingCurrentTime(&gba->timing); + dmaSeen = true; } } - GBAMemoryUpdateDMAs(gba, 0); + if (dmaSeen) { + GBAMemoryUpdateDMAs(gba, 0); + } } void GBAMemoryRunVblankDMAs(struct GBA* gba, int32_t cycles) { struct GBAMemory* memory = &gba->memory; struct GBADMA* dma; + bool dmaSeen = false; + if (memory->activeDMA >= 0) { + GBAMemoryUpdateDMAs(gba, mTimingCurrentTime(&gba->timing) - memory->dma[memory->activeDMA].scheduledAt); + } int i; for (i = 0; i < 4; ++i) { dma = &memory->dma[i]; if (GBADMARegisterIsEnable(dma->reg) && GBADMARegisterGetTiming(dma->reg) == DMA_TIMING_VBLANK) { dma->nextEvent = 2 + cycles; + dma->scheduledAt = mTimingCurrentTime(&gba->timing); + dmaSeen = true; } } - GBAMemoryUpdateDMAs(gba, 0); + if (dmaSeen) { + GBAMemoryUpdateDMAs(gba, 0); + } } void _dmaEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) {

@@ -1664,6 +1681,7 @@ }

if (info->hasStarted < 1) { info->hasStarted = wordsRemaining; info->nextEvent = 0; + info->scheduledAt = mTimingCurrentTime(&gba->timing); GBAMemoryUpdateDMAs(gba, -cycles); return; }

@@ -1729,6 +1747,7 @@ }

} else { info->nextDest = dest; info->nextCount = wordsRemaining; + info->scheduledAt = mTimingCurrentTime(&gba->timing); } info->nextSource = source; GBAMemoryUpdateDMAs(gba, 0);
M src/gba/memory.hsrc/gba/memory.h

@@ -109,6 +109,7 @@ uint32_t nextSource;

uint32_t nextDest; int32_t nextCount; int32_t nextEvent; + uint32_t scheduledAt; int32_t hasStarted; };