GBA Memory: Fix DMA timing
Jeffrey Pfau jeffrey@endrift.com
Tue, 13 Dec 2016 18:04:12 -0800
4 files changed,
29 insertions(+),
4 deletions(-)
M
src/core/timing.c
→
src/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.h
→
src/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.c
→
src/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.h
→
src/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; };