mirror of https://github.com/mgba-emu/mgba.git
GBA DMA: Move DMAs to using absolute timing
This commit is contained in:
parent
ad85acab75
commit
1c93b75b7e
1
CHANGES
1
CHANGES
|
@ -68,6 +68,7 @@ Misc:
|
||||||
- Debugger: Add functions for read- or write-only watchpoints
|
- Debugger: Add functions for read- or write-only watchpoints
|
||||||
- GB Memory: Reset ROM bank when loading a ROM
|
- GB Memory: Reset ROM bank when loading a ROM
|
||||||
- GBA DMA: Refactor DMA out of memory.c
|
- GBA DMA: Refactor DMA out of memory.c
|
||||||
|
- GBA DMA: Move DMAs to using absolute timing
|
||||||
|
|
||||||
0.5.1: (2016-10-05)
|
0.5.1: (2016-10-05)
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
|
|
|
@ -109,6 +109,7 @@ void GBAAudioScheduleFifoDma(struct GBAAudio* audio, int number, struct GBADMA*
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
info->reg = GBADMARegisterSetDestControl(info->reg, DMA_FIXED);
|
info->reg = GBADMARegisterSetDestControl(info->reg, DMA_FIXED);
|
||||||
|
info->reg = GBADMARegisterSetWidth(info->reg, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBAAudioWriteSOUND1CNT_LO(struct GBAAudio* audio, uint16_t value) {
|
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) {
|
if (CircleBufferSize(&channel->fifo) <= 4 * sizeof(int32_t) && channel->dmaSource > 0) {
|
||||||
struct GBADMA* dma = &audio->p->memory.dma[channel->dmaSource];
|
struct GBADMA* dma = &audio->p->memory.dma[channel->dmaSource];
|
||||||
if (GBADMARegisterGetTiming(dma->reg) == DMA_TIMING_CUSTOM) {
|
if (GBADMARegisterGetTiming(dma->reg) == DMA_TIMING_CUSTOM) {
|
||||||
|
dma->when = mTimingCurrentTime(&audio->p->timing) - cycles;
|
||||||
dma->nextCount = 4;
|
dma->nextCount = 4;
|
||||||
dma->nextEvent = 0;
|
GBADMASchedule(audio->p, channel->dmaSource, dma);
|
||||||
dma->reg = GBADMARegisterSetWidth(dma->reg, 1);
|
|
||||||
dma->reg = GBADMARegisterSetDestControl(dma->reg, 2);
|
|
||||||
GBADMAUpdate(audio->p, -cycles);
|
|
||||||
} else {
|
} else {
|
||||||
channel->dmaSource = 0;
|
channel->dmaSource = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,6 @@ void GBADMAReset(struct GBA* gba) {
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < 4; ++i) {
|
for (i = 0; i < 4; ++i) {
|
||||||
gba->memory.dma[i].count = 0x4000;
|
gba->memory.dma[i].count = 0x4000;
|
||||||
gba->memory.dma[i].nextEvent = INT_MAX;
|
|
||||||
}
|
}
|
||||||
gba->memory.dma[3].count = 0x10000;
|
gba->memory.dma[3].count = 0x10000;
|
||||||
gba->memory.activeDMA = -1;
|
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)) {
|
if (!wasEnabled && GBADMARegisterIsEnable(currentDma->reg)) {
|
||||||
currentDma->nextSource = currentDma->source;
|
currentDma->nextSource = currentDma->source;
|
||||||
currentDma->nextDest = currentDma->dest;
|
currentDma->nextDest = currentDma->dest;
|
||||||
currentDma->nextCount = currentDma->count;
|
|
||||||
GBADMASchedule(gba, dma, currentDma);
|
GBADMASchedule(gba, dma, currentDma);
|
||||||
}
|
}
|
||||||
// If the DMA has already occurred, this value might have changed since the function started
|
// 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) {
|
void GBADMASchedule(struct GBA* gba, int number, struct GBADMA* info) {
|
||||||
info->hasStarted = 0;
|
|
||||||
switch (GBADMARegisterGetTiming(info->reg)) {
|
switch (GBADMARegisterGetTiming(info->reg)) {
|
||||||
case DMA_TIMING_NOW:
|
case DMA_TIMING_NOW:
|
||||||
info->nextEvent = 2 + 1; // XXX: Account for I cycle when writing
|
info->when = mTimingCurrentTime(&gba->timing) + 2 + 1; // XXX: Account for I cycle when writing
|
||||||
info->scheduledAt = mTimingCurrentTime(&gba->timing);
|
info->nextCount = info->count;
|
||||||
GBADMAUpdate(gba, 0);
|
|
||||||
break;
|
break;
|
||||||
case DMA_TIMING_HBLANK:
|
case DMA_TIMING_HBLANK:
|
||||||
// Handled implicitly
|
|
||||||
info->nextEvent = INT_MAX;
|
|
||||||
break;
|
|
||||||
case DMA_TIMING_VBLANK:
|
case DMA_TIMING_VBLANK:
|
||||||
// Handled implicitly
|
// Handled implicitly
|
||||||
info->nextEvent = INT_MAX;
|
return;
|
||||||
break;
|
|
||||||
case DMA_TIMING_CUSTOM:
|
case DMA_TIMING_CUSTOM:
|
||||||
info->nextEvent = INT_MAX;
|
|
||||||
switch (number) {
|
switch (number) {
|
||||||
case 0:
|
case 0:
|
||||||
mLOG(GBA_MEM, WARN, "Discarding invalid DMA0 scheduling");
|
mLOG(GBA_MEM, WARN, "Discarding invalid DMA0 scheduling");
|
||||||
break;
|
return;
|
||||||
case 1:
|
case 1:
|
||||||
case 2:
|
case 2:
|
||||||
GBAAudioScheduleFifoDma(&gba->audio, number, info);
|
GBAAudioScheduleFifoDma(&gba->audio, number, info);
|
||||||
|
@ -121,76 +112,64 @@ void GBADMASchedule(struct GBA* gba, int number, struct GBADMA* info) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
GBADMAUpdate(gba);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBADMARunHblank(struct GBA* gba, int32_t cycles) {
|
void GBADMARunHblank(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) {
|
|
||||||
GBADMAUpdate(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->hasStarted) {
|
||||||
dma->nextEvent = 2 + cycles;
|
dma->when = mTimingCurrentTime(&gba->timing) + 2 + cycles;
|
||||||
dma->scheduledAt = mTimingCurrentTime(&gba->timing);
|
dma->nextCount = dma->count;
|
||||||
dmaSeen = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (dmaSeen) {
|
GBADMAUpdate(gba);
|
||||||
GBADMAUpdate(gba, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBADMARunVblank(struct GBA* gba, int32_t cycles) {
|
void GBADMARunVblank(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) {
|
|
||||||
GBADMAUpdate(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->hasStarted) {
|
||||||
dma->nextEvent = 2 + cycles;
|
dma->when = mTimingCurrentTime(&gba->timing) + 2 + cycles;
|
||||||
dma->scheduledAt = mTimingCurrentTime(&gba->timing);
|
dma->nextCount = dma->count;
|
||||||
dmaSeen = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (dmaSeen) {
|
GBADMAUpdate(gba);
|
||||||
GBADMAUpdate(gba, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _dmaEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
void _dmaEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||||
UNUSED(timing);
|
UNUSED(timing);
|
||||||
|
UNUSED(cyclesLate);
|
||||||
struct GBA* gba = context;
|
struct GBA* gba = context;
|
||||||
struct GBAMemory* memory = &gba->memory;
|
struct GBAMemory* memory = &gba->memory;
|
||||||
struct GBADMA* dma = &memory->dma[memory->activeDMA];
|
struct GBADMA* dma = &memory->dma[memory->activeDMA];
|
||||||
dma->nextEvent = -cyclesLate;
|
|
||||||
GBADMAService(gba, memory->activeDMA, dma);
|
GBADMAService(gba, memory->activeDMA, dma);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBADMAUpdate(struct GBA* gba, int32_t cycles) {
|
void GBADMAUpdate(struct GBA* gba) {
|
||||||
int i;
|
int i;
|
||||||
struct GBAMemory* memory = &gba->memory;
|
struct GBAMemory* memory = &gba->memory;
|
||||||
memory->activeDMA = -1;
|
memory->activeDMA = -1;
|
||||||
|
uint32_t currentTime = mTimingCurrentTime(&gba->timing);
|
||||||
for (i = 3; i >= 0; --i) {
|
for (i = 3; i >= 0; --i) {
|
||||||
struct GBADMA* dma = &memory->dma[i];
|
struct GBADMA* dma = &memory->dma[i];
|
||||||
if (dma->nextEvent != INT_MAX) {
|
if (GBADMARegisterIsEnable(dma->reg) && dma->nextCount) {
|
||||||
dma->nextEvent -= cycles;
|
if (dma->when < currentTime) {
|
||||||
if (GBADMARegisterIsEnable(dma->reg)) {
|
dma->when = currentTime;
|
||||||
memory->activeDMA = i;
|
|
||||||
}
|
}
|
||||||
|
memory->activeDMA = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (memory->activeDMA >= 0) {
|
if (memory->activeDMA >= 0) {
|
||||||
mTimingDeschedule(&gba->timing, &memory->dmaEvent);
|
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 {
|
} else {
|
||||||
gba->cpuBlocked = false;
|
gba->cpuBlocked = false;
|
||||||
}
|
}
|
||||||
|
@ -218,10 +197,9 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) {
|
||||||
cycles += memory->waitstatesNonseq16[sourceRegion] + memory->waitstatesNonseq16[destRegion];
|
cycles += memory->waitstatesNonseq16[sourceRegion] + memory->waitstatesNonseq16[destRegion];
|
||||||
}
|
}
|
||||||
if (info->hasStarted < 1) {
|
if (info->hasStarted < 1) {
|
||||||
info->hasStarted = wordsRemaining;
|
info->hasStarted = info->count << 1;
|
||||||
info->nextEvent = 0;
|
info->when = mTimingCurrentTime(&gba->timing) + cycles;
|
||||||
info->scheduledAt = mTimingCurrentTime(&gba->timing);
|
GBADMAUpdate(gba);
|
||||||
GBADMAUpdate(gba, -cycles);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
info->hasStarted = 2;
|
info->hasStarted = 2;
|
||||||
|
@ -234,7 +212,7 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) {
|
||||||
cycles += memory->waitstatesSeq16[sourceRegion] + memory->waitstatesSeq16[destRegion];
|
cycles += memory->waitstatesSeq16[sourceRegion] + memory->waitstatesSeq16[destRegion];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
info->nextEvent += cycles;
|
info->when += cycles;
|
||||||
|
|
||||||
gba->performingDMA = 1 | (number << 1);
|
gba->performingDMA = 1 | (number << 1);
|
||||||
uint32_t word;
|
uint32_t word;
|
||||||
|
@ -267,27 +245,23 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) {
|
||||||
gba->performingDMA = 0;
|
gba->performingDMA = 0;
|
||||||
|
|
||||||
if (!wordsRemaining) {
|
if (!wordsRemaining) {
|
||||||
|
info->hasStarted = 0;
|
||||||
if (!GBADMARegisterIsRepeat(info->reg) || GBADMARegisterGetTiming(info->reg) == DMA_TIMING_NOW) {
|
if (!GBADMARegisterIsRepeat(info->reg) || GBADMARegisterGetTiming(info->reg) == DMA_TIMING_NOW) {
|
||||||
info->reg = GBADMARegisterClearEnable(info->reg);
|
info->reg = GBADMARegisterClearEnable(info->reg);
|
||||||
info->nextEvent = INT_MAX;
|
|
||||||
|
|
||||||
// Clear the enable bit in memory
|
// Clear the enable bit in memory
|
||||||
memory->io[(REG_DMA0CNT_HI + number * (REG_DMA1CNT_HI - REG_DMA0CNT_HI)) >> 1] &= 0x7FE0;
|
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) {
|
||||||
if (GBADMARegisterGetDestControl(info->reg) == DMA_INCREMENT_RELOAD) {
|
info->nextDest = info->dest;
|
||||||
info->nextDest = info->dest;
|
|
||||||
}
|
|
||||||
GBADMASchedule(gba, number, info);
|
|
||||||
}
|
}
|
||||||
if (GBADMARegisterIsDoIRQ(info->reg)) {
|
if (GBADMARegisterIsDoIRQ(info->reg)) {
|
||||||
GBARaiseIRQ(gba, IRQ_DMA0 + number);
|
GBARaiseIRQ(gba, IRQ_DMA0 + number);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
info->nextDest = dest;
|
info->nextDest = dest;
|
||||||
info->nextCount = wordsRemaining;
|
|
||||||
info->scheduledAt = mTimingCurrentTime(&gba->timing);
|
|
||||||
}
|
}
|
||||||
|
info->nextCount = wordsRemaining;
|
||||||
info->nextSource = source;
|
info->nextSource = source;
|
||||||
GBADMAUpdate(gba, 0);
|
GBADMAUpdate(gba);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,6 @@ struct GBADMA;
|
||||||
void GBADMASchedule(struct GBA* gba, int number, struct GBADMA* info);
|
void GBADMASchedule(struct GBA* gba, int number, struct GBADMA* info);
|
||||||
void GBADMARunHblank(struct GBA* gba, int32_t cycles);
|
void GBADMARunHblank(struct GBA* gba, int32_t cycles);
|
||||||
void GBADMARunVblank(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
|
#endif
|
||||||
|
|
|
@ -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].nextSource, 0, &state->dma[i].nextSource);
|
||||||
STORE_32(gba->memory.dma[i].nextDest, 0, &state->dma[i].nextDest);
|
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].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);
|
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].nextSource, 0, &state->dma[i].nextSource);
|
||||||
LOAD_32(gba->memory.dma[i].nextDest, 0, &state->dma[i].nextDest);
|
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].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) {
|
if (GBADMARegisterGetTiming(gba->memory.dma[i].reg) != DMA_TIMING_NOW) {
|
||||||
GBADMASchedule(gba, i, &gba->memory.dma[i]);
|
GBADMASchedule(gba, i, &gba->memory.dma[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GBAAudioWriteSOUNDCNT_X(&gba->audio, gba->memory.io[REG_SOUNDCNT_X >> 1]);
|
GBAAudioWriteSOUNDCNT_X(&gba->audio, gba->memory.io[REG_SOUNDCNT_X >> 1]);
|
||||||
GBADMAUpdate(gba, 0);
|
GBADMAUpdate(gba);
|
||||||
GBAHardwareDeserialize(&gba->memory.hw, state);
|
GBAHardwareDeserialize(&gba->memory.hw, state);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
* 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
|
* 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 nextSource;
|
||||||
uint32_t nextDest;
|
uint32_t nextDest;
|
||||||
int32_t nextCount;
|
int32_t nextCount;
|
||||||
int32_t nextEvent;
|
uint32_t when;
|
||||||
uint32_t scheduledAt;
|
|
||||||
int32_t hasStarted;
|
int32_t hasStarted;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue