mirror of https://github.com/mgba-emu/mgba.git
GBA Memory: Fix DMA timing
This commit is contained in:
parent
82a0088e1e
commit
a1689c80a7
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue