mirror of https://github.com/mgba-emu/mgba.git
GBA Memory: Convert DMAs to mTiming
This commit is contained in:
parent
74bb02065d
commit
c056acb98f
|
@ -183,6 +183,7 @@ void GBAReset(struct ARMCore* cpu) {
|
||||||
GBASavedataUnmask(&gba->memory.savedata);
|
GBASavedataUnmask(&gba->memory.savedata);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gba->cpuBlocked = false;
|
||||||
if (gba->yankedRomSize) {
|
if (gba->yankedRomSize) {
|
||||||
gba->memory.romSize = gba->yankedRomSize;
|
gba->memory.romSize = gba->yankedRomSize;
|
||||||
gba->memory.romMask = toPow2(gba->memory.romSize) - 1;
|
gba->memory.romMask = toPow2(gba->memory.romSize) - 1;
|
||||||
|
@ -234,9 +235,9 @@ static void GBAProcessEvents(struct ARMCore* cpu) {
|
||||||
gba->springIRQ = 0;
|
gba->springIRQ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int32_t nextEvent;
|
||||||
do {
|
do {
|
||||||
int32_t cycles = cpu->cycles;
|
int32_t cycles = cpu->cycles;
|
||||||
int32_t nextEvent;
|
|
||||||
int32_t testEvent;
|
int32_t testEvent;
|
||||||
|
|
||||||
cpu->cycles = 0;
|
cpu->cycles = 0;
|
||||||
|
@ -247,9 +248,11 @@ static void GBAProcessEvents(struct ARMCore* cpu) {
|
||||||
mLOG(GBA, FATAL, "Negative cycles passed: %i", cycles);
|
mLOG(GBA, FATAL, "Negative cycles passed: %i", cycles);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
nextEvent = cycles;
|
||||||
mTimingTick(&gba->timing, cycles);
|
do {
|
||||||
nextEvent = cpu->nextEvent;
|
mTimingTick(&gba->timing, nextEvent);
|
||||||
|
nextEvent = cpu->nextEvent;
|
||||||
|
} while (gba->cpuBlocked);
|
||||||
|
|
||||||
testEvent = GBAVideoProcessEvents(&gba->video, cycles);
|
testEvent = GBAVideoProcessEvents(&gba->video, cycles);
|
||||||
if (testEvent < nextEvent) {
|
if (testEvent < nextEvent) {
|
||||||
|
@ -271,16 +274,6 @@ static void GBAProcessEvents(struct ARMCore* cpu) {
|
||||||
nextEvent = testEvent;
|
nextEvent = testEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
testEvent = GBAMemoryRunDMAs(gba, cycles);
|
|
||||||
if (testEvent < nextEvent) {
|
|
||||||
#ifndef NDEBUG
|
|
||||||
if (testEvent == 0) {
|
|
||||||
mLOG(GBA, ERROR, "DMAs requiring 0 cycles");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
nextEvent = testEvent;
|
|
||||||
}
|
|
||||||
|
|
||||||
testEvent = GBASIOProcessEvents(&gba->sio, cycles);
|
testEvent = GBASIOProcessEvents(&gba->sio, cycles);
|
||||||
if (testEvent < nextEvent) {
|
if (testEvent < nextEvent) {
|
||||||
nextEvent = testEvent;
|
nextEvent = testEvent;
|
||||||
|
@ -288,21 +281,21 @@ static void GBAProcessEvents(struct ARMCore* cpu) {
|
||||||
|
|
||||||
cpu->nextEvent = nextEvent;
|
cpu->nextEvent = nextEvent;
|
||||||
|
|
||||||
|
if (nextEvent == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (cpu->halted) {
|
if (cpu->halted) {
|
||||||
cpu->cycles = cpu->nextEvent;
|
cpu->cycles = nextEvent;
|
||||||
if (!gba->memory.io[REG_IME >> 1] || !gba->memory.io[REG_IE >> 1]) {
|
if (!gba->memory.io[REG_IME >> 1] || !gba->memory.io[REG_IE >> 1]) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (nextEvent == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
else if (nextEvent < 0) {
|
else if (nextEvent < 0) {
|
||||||
mLOG(GBA, FATAL, "Negative cycles will pass: %i", nextEvent);
|
mLOG(GBA, FATAL, "Negative cycles will pass: %i", nextEvent);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
} while (cpu->cycles >= cpu->nextEvent);
|
} while (cpu->cycles >= nextEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBAAttachDebugger(struct GBA* gba, struct mDebugger* debugger) {
|
void GBAAttachDebugger(struct GBA* gba, struct mDebugger* debugger) {
|
||||||
|
|
|
@ -105,6 +105,7 @@ struct GBA {
|
||||||
uint32_t idleLoop;
|
uint32_t idleLoop;
|
||||||
uint32_t lastJump;
|
uint32_t lastJump;
|
||||||
bool haltPending;
|
bool haltPending;
|
||||||
|
bool cpuBlocked;
|
||||||
int idleDetectionStep;
|
int idleDetectionStep;
|
||||||
int idleDetectionFailures;
|
int idleDetectionFailures;
|
||||||
int32_t cachedRegisters[16];
|
int32_t cachedRegisters[16];
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
mLOG_DEFINE_CATEGORY(GBA_MEM, "GBA Memory");
|
mLOG_DEFINE_CATEGORY(GBA_MEM, "GBA Memory");
|
||||||
|
|
||||||
static void _pristineCow(struct GBA* gba);
|
static void _pristineCow(struct GBA* gba);
|
||||||
|
static void _dmaEvent(struct mTiming* timing, void* context, uint32_t cyclesLate);
|
||||||
static uint32_t _deadbeef[1] = { 0xE710B710 }; // Illegal instruction on both ARM and Thumb
|
static uint32_t _deadbeef[1] = { 0xE710B710 }; // Illegal instruction on both ARM and Thumb
|
||||||
|
|
||||||
static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t region);
|
static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t region);
|
||||||
|
@ -83,6 +84,10 @@ void GBAMemoryInit(struct GBA* gba) {
|
||||||
gba->memory.biosPrefetch = 0;
|
gba->memory.biosPrefetch = 0;
|
||||||
gba->memory.mirroring = false;
|
gba->memory.mirroring = false;
|
||||||
|
|
||||||
|
gba->memory.dmaEvent.name = "GBA DMA";
|
||||||
|
gba->memory.dmaEvent.callback = _dmaEvent;
|
||||||
|
gba->memory.dmaEvent.context = gba;
|
||||||
|
|
||||||
GBAVFameInit(&gba->memory.vfame);
|
GBAVFameInit(&gba->memory.vfame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,8 +128,6 @@ void GBAMemoryReset(struct GBA* gba) {
|
||||||
}
|
}
|
||||||
gba->memory.dma[3].count = 0x10000;
|
gba->memory.dma[3].count = 0x10000;
|
||||||
gba->memory.activeDMA = -1;
|
gba->memory.activeDMA = -1;
|
||||||
gba->memory.nextDMA = INT_MAX;
|
|
||||||
gba->memory.eventDiff = 0;
|
|
||||||
|
|
||||||
gba->memory.prefetch = false;
|
gba->memory.prefetch = false;
|
||||||
gba->memory.lastPrefetchedPc = 0;
|
gba->memory.lastPrefetchedPc = 0;
|
||||||
|
@ -1549,10 +1552,10 @@ uint16_t GBAMemoryWriteDMACNT_HI(struct GBA* gba, int dma, uint16_t control) {
|
||||||
};
|
};
|
||||||
|
|
||||||
void GBAMemoryScheduleDMA(struct GBA* gba, int number, struct GBADMA* info) {
|
void GBAMemoryScheduleDMA(struct GBA* gba, int number, struct GBADMA* info) {
|
||||||
struct ARMCore* cpu = gba->cpu;
|
info->hasStarted = 0;
|
||||||
switch (GBADMARegisterGetTiming(info->reg)) {
|
switch (GBADMARegisterGetTiming(info->reg)) {
|
||||||
case DMA_TIMING_NOW:
|
case DMA_TIMING_NOW:
|
||||||
info->nextEvent = cpu->cycles + 2;
|
info->nextEvent = 2;
|
||||||
GBAMemoryUpdateDMAs(gba, -1);
|
GBAMemoryUpdateDMAs(gba, -1);
|
||||||
break;
|
break;
|
||||||
case DMA_TIMING_HBLANK:
|
case DMA_TIMING_HBLANK:
|
||||||
|
@ -1587,7 +1590,7 @@ void GBAMemoryRunHblankDMAs(struct GBA* gba, int32_t cycles) {
|
||||||
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 = cycles;
|
dma->nextEvent = 2 + cycles;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GBAMemoryUpdateDMAs(gba, 0);
|
GBAMemoryUpdateDMAs(gba, 0);
|
||||||
|
@ -1600,46 +1603,40 @@ void GBAMemoryRunVblankDMAs(struct GBA* gba, int32_t cycles) {
|
||||||
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 = cycles;
|
dma->nextEvent = 2 + cycles;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GBAMemoryUpdateDMAs(gba, 0);
|
GBAMemoryUpdateDMAs(gba, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t GBAMemoryRunDMAs(struct GBA* gba, int32_t cycles) {
|
void _dmaEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||||
|
UNUSED(timing);
|
||||||
|
struct GBA* gba = context;
|
||||||
struct GBAMemory* memory = &gba->memory;
|
struct GBAMemory* memory = &gba->memory;
|
||||||
if (memory->nextDMA == INT_MAX) {
|
struct GBADMA* dma = &memory->dma[memory->activeDMA];
|
||||||
return INT_MAX;
|
dma->nextEvent = -cyclesLate;
|
||||||
}
|
GBAMemoryServiceDMA(gba, memory->activeDMA, dma);
|
||||||
memory->nextDMA -= cycles;
|
|
||||||
memory->eventDiff += cycles;
|
|
||||||
while (memory->nextDMA <= 0) {
|
|
||||||
struct GBADMA* dma = &memory->dma[memory->activeDMA];
|
|
||||||
GBAMemoryServiceDMA(gba, memory->activeDMA, dma);
|
|
||||||
GBAMemoryUpdateDMAs(gba, memory->eventDiff);
|
|
||||||
memory->eventDiff = 0;
|
|
||||||
}
|
|
||||||
return memory->nextDMA;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBAMemoryUpdateDMAs(struct GBA* gba, int32_t cycles) {
|
void GBAMemoryUpdateDMAs(struct GBA* gba, int32_t cycles) {
|
||||||
int i;
|
int i;
|
||||||
struct GBAMemory* memory = &gba->memory;
|
struct GBAMemory* memory = &gba->memory;
|
||||||
struct ARMCore* cpu = gba->cpu;
|
|
||||||
memory->activeDMA = -1;
|
memory->activeDMA = -1;
|
||||||
memory->nextDMA = INT_MAX;
|
|
||||||
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 (dma->nextEvent != INT_MAX) {
|
||||||
dma->nextEvent -= cycles;
|
dma->nextEvent -= cycles;
|
||||||
if (GBADMARegisterIsEnable(dma->reg)) {
|
if (GBADMARegisterIsEnable(dma->reg)) {
|
||||||
memory->activeDMA = i;
|
memory->activeDMA = i;
|
||||||
memory->nextDMA = dma->nextEvent;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (memory->nextDMA < cpu->nextEvent) {
|
|
||||||
cpu->nextEvent = memory->nextDMA;
|
if (memory->activeDMA >= 0) {
|
||||||
|
mTimingDeschedule(&gba->timing, &memory->dmaEvent);
|
||||||
|
mTimingSchedule(&gba->timing, &memory->dmaEvent, memory->dma[memory->activeDMA].nextEvent);
|
||||||
|
} else {
|
||||||
|
gba->cpuBlocked = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1656,7 +1653,8 @@ void GBAMemoryServiceDMA(struct GBA* gba, int number, struct GBADMA* info) {
|
||||||
uint32_t destRegion = dest >> BASE_OFFSET;
|
uint32_t destRegion = dest >> BASE_OFFSET;
|
||||||
int32_t cycles = 2;
|
int32_t cycles = 2;
|
||||||
|
|
||||||
if (source == info->source && dest == info->dest && wordsRemaining == info->count) {
|
gba->cpuBlocked = true;
|
||||||
|
if (info->hasStarted < 2) {
|
||||||
if (sourceRegion < REGION_CART0 || destRegion < REGION_CART0) {
|
if (sourceRegion < REGION_CART0 || destRegion < REGION_CART0) {
|
||||||
cycles += 2;
|
cycles += 2;
|
||||||
}
|
}
|
||||||
|
@ -1667,6 +1665,13 @@ void GBAMemoryServiceDMA(struct GBA* gba, int number, struct GBADMA* info) {
|
||||||
} else {
|
} else {
|
||||||
cycles += memory->waitstatesNonseq16[sourceRegion] + memory->waitstatesNonseq16[destRegion];
|
cycles += memory->waitstatesNonseq16[sourceRegion] + memory->waitstatesNonseq16[destRegion];
|
||||||
}
|
}
|
||||||
|
if (info->hasStarted < 1) {
|
||||||
|
info->hasStarted = wordsRemaining;
|
||||||
|
info->nextEvent = 0;
|
||||||
|
GBAMemoryUpdateDMAs(gba, -cycles);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
info->hasStarted = 2;
|
||||||
} else {
|
} else {
|
||||||
if (width == 4) {
|
if (width == 4) {
|
||||||
cycles += memory->waitstatesSeq32[sourceRegion] + memory->waitstatesSeq32[destRegion];
|
cycles += memory->waitstatesSeq32[sourceRegion] + memory->waitstatesSeq32[destRegion];
|
||||||
|
@ -1740,7 +1745,7 @@ void GBAMemoryServiceDMA(struct GBA* gba, int number, struct GBADMA* info) {
|
||||||
if (info->nextEvent != INT_MAX) {
|
if (info->nextEvent != INT_MAX) {
|
||||||
info->nextEvent += cycles;
|
info->nextEvent += cycles;
|
||||||
}
|
}
|
||||||
cpu->cycles += cycles;
|
GBAMemoryUpdateDMAs(gba, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t GBAMemoryStall(struct ARMCore* cpu, int32_t wait) {
|
int32_t GBAMemoryStall(struct ARMCore* cpu, int32_t wait) {
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "util/common.h"
|
#include "util/common.h"
|
||||||
|
|
||||||
#include "arm/arm.h"
|
#include "arm/arm.h"
|
||||||
|
#include "core/timing.h"
|
||||||
|
|
||||||
#include "gba/hardware.h"
|
#include "gba/hardware.h"
|
||||||
#include "gba/savedata.h"
|
#include "gba/savedata.h"
|
||||||
|
@ -108,6 +109,7 @@ struct GBADMA {
|
||||||
uint32_t nextDest;
|
uint32_t nextDest;
|
||||||
int32_t nextCount;
|
int32_t nextCount;
|
||||||
int32_t nextEvent;
|
int32_t nextEvent;
|
||||||
|
int32_t hasStarted;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GBAMemory {
|
struct GBAMemory {
|
||||||
|
@ -139,9 +141,8 @@ struct GBAMemory {
|
||||||
uint32_t biosPrefetch;
|
uint32_t biosPrefetch;
|
||||||
|
|
||||||
struct GBADMA dma[4];
|
struct GBADMA dma[4];
|
||||||
|
struct mTimingEvent dmaEvent;
|
||||||
int activeDMA;
|
int activeDMA;
|
||||||
int32_t nextDMA;
|
|
||||||
int32_t eventDiff;
|
|
||||||
|
|
||||||
bool mirroring;
|
bool mirroring;
|
||||||
};
|
};
|
||||||
|
@ -185,7 +186,6 @@ 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);
|
||||||
void GBAMemoryRunVblankDMAs(struct GBA* gba, int32_t cycles);
|
void GBAMemoryRunVblankDMAs(struct GBA* gba, int32_t cycles);
|
||||||
void GBAMemoryUpdateDMAs(struct GBA* gba, int32_t cycles);
|
void GBAMemoryUpdateDMAs(struct GBA* gba, int32_t cycles);
|
||||||
int32_t GBAMemoryRunDMAs(struct GBA* gba, int32_t cycles);
|
|
||||||
|
|
||||||
struct GBASerializedState;
|
struct GBASerializedState;
|
||||||
void GBAMemorySerialize(const struct GBAMemory* memory, struct GBASerializedState* state);
|
void GBAMemorySerialize(const struct GBAMemory* memory, struct GBASerializedState* state);
|
||||||
|
|
Loading…
Reference in New Issue