From aed62605cf22974807b93fb4f219c0a74a570cc6 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Tue, 30 Jun 2015 21:38:09 -0700 Subject: [PATCH] GBA Memory: Lazily evaluate prefetch --- src/gba/gba.c | 14 +++++------ src/gba/memory.c | 64 +++++++++++++++++++++++++----------------------- src/gba/memory.h | 6 +++++ 3 files changed, 45 insertions(+), 39 deletions(-) diff --git a/src/gba/gba.c b/src/gba/gba.c index 1be44c506..bf9cd2c10 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -176,8 +176,9 @@ void GBASkipBIOS(struct ARMCore* cpu) { } static void GBAProcessEvents(struct ARMCore* cpu) { - do { - struct GBA* gba = (struct GBA*) cpu->master; + struct GBA* gba = (struct GBA*) cpu->master; + GBAMemoryInvalidatePrefetch(gba); + while (cpu->cycles >= cpu->nextEvent) { int32_t cycles = cpu->nextEvent; int32_t nextEvent = INT_MAX; int32_t testEvent; @@ -223,7 +224,7 @@ static void GBAProcessEvents(struct ARMCore* cpu) { if (cpu->halted) { cpu->cycles = cpu->nextEvent; } - } while (cpu->cycles >= cpu->nextEvent); + } } static int32_t GBATimersProcessEvents(struct GBA* gba, int32_t cycles) { @@ -450,13 +451,10 @@ void GBAApplyPatch(struct GBA* gba, struct Patch* patch) { void GBATimerUpdateRegister(struct GBA* gba, int timer) { struct GBATimer* currentTimer = &gba->timers[timer]; + GBAMemoryInvalidatePrefetch(gba); if (currentTimer->enable && !currentTimer->countUp) { - int32_t prefetchSkew = 0; - if (gba->memory.lastPrefetchedPc - gba->memory.lastPrefetchedLoads * WORD_SIZE_THUMB >= (uint32_t) gba->cpu->gprs[ARM_PC]) { - prefetchSkew = (gba->memory.lastPrefetchedPc - gba->cpu->gprs[ARM_PC]) * (gba->cpu->memory.activeSeqCycles16 + 1) / WORD_SIZE_THUMB; - } // Reading this takes two cycles (1N+1I), so let's remove them preemptively - gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->oldReload + ((gba->cpu->cycles - currentTimer->lastEvent - 2 + prefetchSkew) >> currentTimer->prescaleBits); + gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->oldReload + ((gba->cpu->cycles - currentTimer->lastEvent - 2) >> currentTimer->prescaleBits); } } diff --git a/src/gba/memory.c b/src/gba/memory.c index 32fa3f917..af97a869a 100644 --- a/src/gba/memory.c +++ b/src/gba/memory.c @@ -114,6 +114,8 @@ void GBAMemoryReset(struct GBA* gba) { gba->memory.eventDiff = 0; gba->memory.prefetch = false; + gba->memory.prefetchCycles = 0; + gba->memory.prefetchStalls = 0; gba->memory.lastPrefetchedPc = 0; if (!gba->memory.wram || !gba->memory.iwram) { @@ -235,6 +237,7 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) { } gba->lastJump = address; + GBAMemoryInvalidatePrefetch(gba); memory->lastPrefetchedPc = 0; memory->lastPrefetchedLoads = 0; if (newRegion == memory->activeRegion && (newRegion < REGION_CART0 || (address & (SIZE_CART0 - 1)) < memory->romSize)) { @@ -1534,41 +1537,40 @@ int32_t GBAMemoryStall(struct ARMCore* cpu, int32_t wait) { return wait; } - int32_t s = cpu->memory.activeSeqCycles16 + 1; - int32_t n2s = cpu->memory.activeNonseqCycles16 - cpu->memory.activeSeqCycles16 + 1; + // Offload the prefetch timing until the next event, which will happen too early. + memory->prefetchCycles += wait; + ++memory->prefetchStalls; + return wait; +} + +void GBAMemoryInvalidatePrefetch(struct GBA* gba) { + int32_t waited = gba->memory.prefetchCycles; + int32_t nWaits = gba->memory.prefetchStalls; + gba->memory.prefetchCycles = 0; + gba->memory.prefetchStalls = 0; + + if (!waited) { + return; + } + + int32_t s = gba->cpu->memory.activeSeqCycles16 + 1; + int32_t n2s = gba->cpu->memory.activeNonseqCycles16 - gba->cpu->memory.activeSeqCycles16; // Figure out how many sequential loads we can jam in - int32_t stall = s; - int32_t loads = 1; - int32_t previousLoads = 0; + int32_t loads = (waited - 1) / s; + int32_t diff = waited - loads * s; - // Don't prefetch too much if we're overlapping with a previous prefetch - uint32_t dist = (memory->lastPrefetchedPc - cpu->gprs[ARM_PC]) >> 1; - if (dist < memory->lastPrefetchedLoads) { - previousLoads = dist; + // The next |loads|S waitstates disappear entirely, so long as they're all in a row. + // Each instruction that waited has an N cycle that was converted into an S, so those + // disappear as well. + int32_t toRemove = (s - 1) * loads + n2s * nWaits + diff; + if (toRemove > gba->cpu->cycles) { + // We have to delay invalidating... + gba->memory.prefetchCycles = gba->memory.prefetchCycles; + gba->memory.prefetchStalls = gba->memory.prefetchStalls; + return; } - while (stall < wait) { - stall += s; - ++loads; - } - if (loads + previousLoads > 8) { - int diff = (loads + previousLoads) - 8; - loads -= diff; - stall -= s * diff; - } else if (stall > wait && loads == 1) { - // We might need to stall a bit extra if we haven't finished the first S cycle - wait = stall; - } - // This instruction used to have an N, convert it to an S. - wait -= n2s; - - // TODO: Invalidate prefetch on branch - memory->lastPrefetchedLoads = loads; - memory->lastPrefetchedPc = cpu->gprs[ARM_PC] + WORD_SIZE_THUMB * loads; - - // The next |loads|S waitstates disappear entirely, so long as they're all in a row - cpu->cycles -= (s - 1) * loads; - return wait; + gba->cpu->cycles -= toRemove; } void GBAMemorySerialize(const struct GBAMemory* memory, struct GBASerializedState* state) { diff --git a/src/gba/memory.h b/src/gba/memory.h index dd9d4066b..c263f3928 100644 --- a/src/gba/memory.h +++ b/src/gba/memory.h @@ -130,9 +130,13 @@ struct GBAMemory { char waitstatesPrefetchNonseq32[16]; char waitstatesPrefetchNonseq16[16]; int activeRegion; + bool prefetch; uint32_t lastPrefetchedPc; uint32_t lastPrefetchedLoads; + int32_t prefetchCycles; + int prefetchStalls; + uint32_t biosPrefetch; struct GBADMA dma[4]; @@ -176,6 +180,8 @@ void GBAMemoryRunVblankDMAs(struct GBA* gba, int32_t cycles); void GBAMemoryUpdateDMAs(struct GBA* gba, int32_t cycles); int32_t GBAMemoryRunDMAs(struct GBA* gba, int32_t cycles); +void GBAMemoryInvalidatePrefetch(struct GBA* gba); + struct GBASerializedState; void GBAMemorySerialize(const struct GBAMemory* memory, struct GBASerializedState* state); void GBAMemoryDeserialize(struct GBAMemory* memory, const struct GBASerializedState* state);