diff --git a/CHANGES b/CHANGES index bbcf3f53e..151863e1c 100644 --- a/CHANGES +++ b/CHANGES @@ -68,6 +68,7 @@ Misc: - Debugger: Add functions for read- or write-only watchpoints - GB Memory: Reset ROM bank when loading a ROM - GBA DMA: Refactor DMA out of memory.c + - GBA DMA: Move DMAs to using absolute timing 0.5.1: (2016-10-05) Bugfixes: diff --git a/src/gba/audio.c b/src/gba/audio.c index 0901e9ccf..baf87d3fe 100644 --- a/src/gba/audio.c +++ b/src/gba/audio.c @@ -109,6 +109,7 @@ void GBAAudioScheduleFifoDma(struct GBAAudio* audio, int number, struct GBADMA* return; } info->reg = GBADMARegisterSetDestControl(info->reg, DMA_FIXED); + info->reg = GBADMARegisterSetWidth(info->reg, 1); } void GBAAudioWriteSOUND1CNT_LO(struct GBAAudio* audio, uint16_t value) { @@ -232,11 +233,9 @@ void GBAAudioSampleFIFO(struct GBAAudio* audio, int fifoId, int32_t cycles) { if (CircleBufferSize(&channel->fifo) <= 4 * sizeof(int32_t) && channel->dmaSource > 0) { struct GBADMA* dma = &audio->p->memory.dma[channel->dmaSource]; if (GBADMARegisterGetTiming(dma->reg) == DMA_TIMING_CUSTOM) { + dma->when = mTimingCurrentTime(&audio->p->timing) - cycles; dma->nextCount = 4; - dma->nextEvent = 0; - dma->reg = GBADMARegisterSetWidth(dma->reg, 1); - dma->reg = GBADMARegisterSetDestControl(dma->reg, 2); - GBADMAUpdate(audio->p, -cycles); + GBADMASchedule(audio->p, channel->dmaSource, dma); } else { channel->dmaSource = 0; } diff --git a/src/gba/dma.c b/src/gba/dma.c index d49a1f313..335a93a96 100644 --- a/src/gba/dma.c +++ b/src/gba/dma.c @@ -26,7 +26,6 @@ void GBADMAReset(struct GBA* gba) { int i; for (i = 0; i < 4; ++i) { gba->memory.dma[i].count = 0x4000; - gba->memory.dma[i].nextEvent = INT_MAX; } gba->memory.dma[3].count = 0x10000; gba->memory.activeDMA = -1; @@ -83,7 +82,6 @@ uint16_t GBADMAWriteCNT_HI(struct GBA* gba, int dma, uint16_t control) { if (!wasEnabled && GBADMARegisterIsEnable(currentDma->reg)) { currentDma->nextSource = currentDma->source; currentDma->nextDest = currentDma->dest; - currentDma->nextCount = currentDma->count; GBADMASchedule(gba, dma, currentDma); } // If the DMA has already occurred, this value might have changed since the function started @@ -91,27 +89,20 @@ uint16_t GBADMAWriteCNT_HI(struct GBA* gba, int dma, uint16_t control) { }; void GBADMASchedule(struct GBA* gba, int number, struct GBADMA* info) { - info->hasStarted = 0; switch (GBADMARegisterGetTiming(info->reg)) { case DMA_TIMING_NOW: - info->nextEvent = 2 + 1; // XXX: Account for I cycle when writing - info->scheduledAt = mTimingCurrentTime(&gba->timing); - GBADMAUpdate(gba, 0); + info->when = mTimingCurrentTime(&gba->timing) + 2 + 1; // XXX: Account for I cycle when writing + info->nextCount = info->count; break; case DMA_TIMING_HBLANK: - // Handled implicitly - info->nextEvent = INT_MAX; - break; case DMA_TIMING_VBLANK: // Handled implicitly - info->nextEvent = INT_MAX; - break; + return; case DMA_TIMING_CUSTOM: - info->nextEvent = INT_MAX; switch (number) { case 0: mLOG(GBA_MEM, WARN, "Discarding invalid DMA0 scheduling"); - break; + return; case 1: case 2: GBAAudioScheduleFifoDma(&gba->audio, number, info); @@ -121,76 +112,64 @@ void GBADMASchedule(struct GBA* gba, int number, struct GBADMA* info) { break; } } + GBADMAUpdate(gba); } void GBADMARunHblank(struct GBA* gba, int32_t cycles) { struct GBAMemory* memory = &gba->memory; struct GBADMA* dma; - bool dmaSeen = false; - if (memory->activeDMA >= 0) { - GBADMAUpdate(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; + if (GBADMARegisterIsEnable(dma->reg) && GBADMARegisterGetTiming(dma->reg) == DMA_TIMING_HBLANK && !dma->hasStarted) { + dma->when = mTimingCurrentTime(&gba->timing) + 2 + cycles; + dma->nextCount = dma->count; } } - if (dmaSeen) { - GBADMAUpdate(gba, 0); - } + GBADMAUpdate(gba); } void GBADMARunVblank(struct GBA* gba, int32_t cycles) { struct GBAMemory* memory = &gba->memory; struct GBADMA* dma; - bool dmaSeen = false; - if (memory->activeDMA >= 0) { - GBADMAUpdate(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; + if (GBADMARegisterIsEnable(dma->reg) && GBADMARegisterGetTiming(dma->reg) == DMA_TIMING_VBLANK && !dma->hasStarted) { + dma->when = mTimingCurrentTime(&gba->timing) + 2 + cycles; + dma->nextCount = dma->count; } } - if (dmaSeen) { - GBADMAUpdate(gba, 0); - } + GBADMAUpdate(gba); } void _dmaEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) { UNUSED(timing); + UNUSED(cyclesLate); struct GBA* gba = context; struct GBAMemory* memory = &gba->memory; struct GBADMA* dma = &memory->dma[memory->activeDMA]; - dma->nextEvent = -cyclesLate; GBADMAService(gba, memory->activeDMA, dma); } -void GBADMAUpdate(struct GBA* gba, int32_t cycles) { +void GBADMAUpdate(struct GBA* gba) { int i; struct GBAMemory* memory = &gba->memory; memory->activeDMA = -1; + uint32_t currentTime = mTimingCurrentTime(&gba->timing); for (i = 3; i >= 0; --i) { struct GBADMA* dma = &memory->dma[i]; - if (dma->nextEvent != INT_MAX) { - dma->nextEvent -= cycles; - if (GBADMARegisterIsEnable(dma->reg)) { - memory->activeDMA = i; + if (GBADMARegisterIsEnable(dma->reg) && dma->nextCount) { + if (dma->when < currentTime) { + dma->when = currentTime; } + memory->activeDMA = i; } } if (memory->activeDMA >= 0) { mTimingDeschedule(&gba->timing, &memory->dmaEvent); - mTimingSchedule(&gba->timing, &memory->dmaEvent, memory->dma[memory->activeDMA].nextEvent); + mTimingSchedule(&gba->timing, &memory->dmaEvent, memory->dma[memory->activeDMA].when - currentTime); } else { gba->cpuBlocked = false; } @@ -218,10 +197,9 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) { cycles += memory->waitstatesNonseq16[sourceRegion] + memory->waitstatesNonseq16[destRegion]; } if (info->hasStarted < 1) { - info->hasStarted = wordsRemaining; - info->nextEvent = 0; - info->scheduledAt = mTimingCurrentTime(&gba->timing); - GBADMAUpdate(gba, -cycles); + info->hasStarted = info->count << 1; + info->when = mTimingCurrentTime(&gba->timing) + cycles; + GBADMAUpdate(gba); return; } info->hasStarted = 2; @@ -234,7 +212,7 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) { cycles += memory->waitstatesSeq16[sourceRegion] + memory->waitstatesSeq16[destRegion]; } } - info->nextEvent += cycles; + info->when += cycles; gba->performingDMA = 1 | (number << 1); uint32_t word; @@ -267,27 +245,23 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) { gba->performingDMA = 0; if (!wordsRemaining) { + info->hasStarted = 0; if (!GBADMARegisterIsRepeat(info->reg) || GBADMARegisterGetTiming(info->reg) == DMA_TIMING_NOW) { info->reg = GBADMARegisterClearEnable(info->reg); - info->nextEvent = INT_MAX; // Clear the enable bit in memory memory->io[(REG_DMA0CNT_HI + number * (REG_DMA1CNT_HI - REG_DMA0CNT_HI)) >> 1] &= 0x7FE0; - } else { - info->nextCount = info->count; - if (GBADMARegisterGetDestControl(info->reg) == DMA_INCREMENT_RELOAD) { - info->nextDest = info->dest; - } - GBADMASchedule(gba, number, info); + } + if (GBADMARegisterGetDestControl(info->reg) == DMA_INCREMENT_RELOAD) { + info->nextDest = info->dest; } if (GBADMARegisterIsDoIRQ(info->reg)) { GBARaiseIRQ(gba, IRQ_DMA0 + number); } } else { info->nextDest = dest; - info->nextCount = wordsRemaining; - info->scheduledAt = mTimingCurrentTime(&gba->timing); } + info->nextCount = wordsRemaining; info->nextSource = source; - GBADMAUpdate(gba, 0); + GBADMAUpdate(gba); } diff --git a/src/gba/dma.h b/src/gba/dma.h index b977d56fe..405a9e554 100644 --- a/src/gba/dma.h +++ b/src/gba/dma.h @@ -21,6 +21,6 @@ struct GBADMA; void GBADMASchedule(struct GBA* gba, int number, struct GBADMA* info); void GBADMARunHblank(struct GBA* gba, int32_t cycles); void GBADMARunVblank(struct GBA* gba, int32_t cycles); -void GBADMAUpdate(struct GBA* gba, int32_t cycles); +void GBADMAUpdate(struct GBA* gba); #endif diff --git a/src/gba/io.c b/src/gba/io.c index 1b3676fa5..e85101a30 100644 --- a/src/gba/io.c +++ b/src/gba/io.c @@ -918,7 +918,7 @@ void GBAIOSerialize(struct GBA* gba, struct GBASerializedState* state) { STORE_32(gba->memory.dma[i].nextSource, 0, &state->dma[i].nextSource); STORE_32(gba->memory.dma[i].nextDest, 0, &state->dma[i].nextDest); STORE_32(gba->memory.dma[i].nextCount, 0, &state->dma[i].nextCount); - STORE_32(gba->memory.dma[i].nextEvent, 0, &state->dma[i].nextEvent); + STORE_32(gba->memory.dma[i].when, 0, &state->dma[i].nextEvent); } GBAHardwareSerialize(&gba->memory.hw, state); @@ -951,12 +951,12 @@ void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) { LOAD_32(gba->memory.dma[i].nextSource, 0, &state->dma[i].nextSource); LOAD_32(gba->memory.dma[i].nextDest, 0, &state->dma[i].nextDest); LOAD_32(gba->memory.dma[i].nextCount, 0, &state->dma[i].nextCount); - LOAD_32(gba->memory.dma[i].nextEvent, 0, &state->dma[i].nextEvent); + LOAD_32(gba->memory.dma[i].when, 0, &state->dma[i].nextEvent); if (GBADMARegisterGetTiming(gba->memory.dma[i].reg) != DMA_TIMING_NOW) { GBADMASchedule(gba, i, &gba->memory.dma[i]); } } GBAAudioWriteSOUNDCNT_X(&gba->audio, gba->memory.io[REG_SOUNDCNT_X >> 1]); - GBADMAUpdate(gba, 0); + GBADMAUpdate(gba); GBAHardwareDeserialize(&gba->memory.hw, state); } diff --git a/src/gba/memory.h b/src/gba/memory.h index 35831fffe..7d478a80e 100644 --- a/src/gba/memory.h +++ b/src/gba/memory.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015 Jeffrey Pfau +/* Copyright (c) 2013-2016 Jeffrey Pfau * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -108,8 +108,7 @@ struct GBADMA { uint32_t nextSource; uint32_t nextDest; int32_t nextCount; - int32_t nextEvent; - uint32_t scheduledAt; + uint32_t when; int32_t hasStarted; };