GBA Memory: Fix DMA timing

This commit is contained in:
Jeffrey Pfau 2016-12-13 18:04:12 -08:00
parent 82a0088e1e
commit a1689c80a7
4 changed files with 29 additions and 4 deletions

View File

@ -68,6 +68,10 @@ int32_t mTimingTick(struct mTiming* timing, int32_t cycles) {
return *timing->nextEvent; return *timing->nextEvent;
} }
int32_t mTimingCurrentTime(struct mTiming* timing) {
return timing->masterCycles + *timing->relativeCycles;
}
int32_t mTimingNextEvent(struct mTiming* timing) { int32_t mTimingNextEvent(struct mTiming* timing) {
struct mTimingEvent* next = timing->root; struct mTimingEvent* next = timing->root;
if (!next) { if (!next) {

View File

@ -32,6 +32,7 @@ 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 mTimingDeschedule(struct mTiming* timing, struct mTimingEvent*);
int32_t mTimingTick(struct mTiming* timing, int32_t cycles); int32_t mTimingTick(struct mTiming* timing, int32_t cycles);
int32_t mTimingCurrentTime(struct mTiming* timing);
int32_t mTimingNextEvent(struct mTiming* timing); int32_t mTimingNextEvent(struct mTiming* timing);
#endif #endif

View File

@ -1555,8 +1555,9 @@ void GBAMemoryScheduleDMA(struct GBA* gba, int number, struct GBADMA* info) {
info->hasStarted = 0; info->hasStarted = 0;
switch (GBADMARegisterGetTiming(info->reg)) { switch (GBADMARegisterGetTiming(info->reg)) {
case DMA_TIMING_NOW: case DMA_TIMING_NOW:
info->nextEvent = 2; info->nextEvent = 2 + 1; // XXX: Account for I cycle when writing
GBAMemoryUpdateDMAs(gba, -1); info->scheduledAt = mTimingCurrentTime(&gba->timing);
GBAMemoryUpdateDMAs(gba, 0);
break; break;
case DMA_TIMING_HBLANK: case DMA_TIMING_HBLANK:
// Handled implicitly // Handled implicitly
@ -1586,27 +1587,43 @@ void GBAMemoryScheduleDMA(struct GBA* gba, int number, struct GBADMA* info) {
void GBAMemoryRunHblankDMAs(struct GBA* gba, int32_t cycles) { void GBAMemoryRunHblankDMAs(struct GBA* gba, int32_t cycles) {
struct GBAMemory* memory = &gba->memory; struct GBAMemory* memory = &gba->memory;
struct GBADMA* dma; struct GBADMA* dma;
bool dmaSeen = false;
if (memory->activeDMA >= 0) {
GBAMemoryUpdateDMAs(gba, mTimingCurrentTime(&gba->timing) - memory->dma[memory->activeDMA].scheduledAt);
}
int i; int i;
for (i = 0; i < 4; ++i) { for (i = 0; i < 4; ++i) {
dma = &memory->dma[i]; dma = &memory->dma[i];
if (GBADMARegisterIsEnable(dma->reg) && GBADMARegisterGetTiming(dma->reg) == DMA_TIMING_HBLANK) { if (GBADMARegisterIsEnable(dma->reg) && GBADMARegisterGetTiming(dma->reg) == DMA_TIMING_HBLANK) {
dma->nextEvent = 2 + cycles; 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) { void GBAMemoryRunVblankDMAs(struct GBA* gba, int32_t cycles) {
struct GBAMemory* memory = &gba->memory; struct GBAMemory* memory = &gba->memory;
struct GBADMA* dma; struct GBADMA* dma;
bool dmaSeen = false;
if (memory->activeDMA >= 0) {
GBAMemoryUpdateDMAs(gba, mTimingCurrentTime(&gba->timing) - memory->dma[memory->activeDMA].scheduledAt);
}
int i; int i;
for (i = 0; i < 4; ++i) { for (i = 0; i < 4; ++i) {
dma = &memory->dma[i]; dma = &memory->dma[i];
if (GBADMARegisterIsEnable(dma->reg) && GBADMARegisterGetTiming(dma->reg) == DMA_TIMING_VBLANK) { if (GBADMARegisterIsEnable(dma->reg) && GBADMARegisterGetTiming(dma->reg) == DMA_TIMING_VBLANK) {
dma->nextEvent = 2 + cycles; 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) { void _dmaEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) {
@ -1664,6 +1681,7 @@ void GBAMemoryServiceDMA(struct GBA* gba, int number, struct GBADMA* info) {
if (info->hasStarted < 1) { if (info->hasStarted < 1) {
info->hasStarted = wordsRemaining; info->hasStarted = wordsRemaining;
info->nextEvent = 0; info->nextEvent = 0;
info->scheduledAt = mTimingCurrentTime(&gba->timing);
GBAMemoryUpdateDMAs(gba, -cycles); GBAMemoryUpdateDMAs(gba, -cycles);
return; return;
} }
@ -1729,6 +1747,7 @@ void GBAMemoryServiceDMA(struct GBA* gba, int number, struct GBADMA* info) {
} else { } else {
info->nextDest = dest; info->nextDest = dest;
info->nextCount = wordsRemaining; info->nextCount = wordsRemaining;
info->scheduledAt = mTimingCurrentTime(&gba->timing);
} }
info->nextSource = source; info->nextSource = source;
GBAMemoryUpdateDMAs(gba, 0); GBAMemoryUpdateDMAs(gba, 0);

View File

@ -109,6 +109,7 @@ struct GBADMA {
uint32_t nextDest; uint32_t nextDest;
int32_t nextCount; int32_t nextCount;
int32_t nextEvent; int32_t nextEvent;
uint32_t scheduledAt;
int32_t hasStarted; int32_t hasStarted;
}; };