mirror of
https://github.com/libretro/mgba.git
synced 2024-11-24 00:20:05 +00:00
GBA Memory: Lazily evaluate prefetch
This commit is contained in:
parent
bddebef90b
commit
aed62605cf
@ -176,8 +176,9 @@ void GBASkipBIOS(struct ARMCore* cpu) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void GBAProcessEvents(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 cycles = cpu->nextEvent;
|
||||||
int32_t nextEvent = INT_MAX;
|
int32_t nextEvent = INT_MAX;
|
||||||
int32_t testEvent;
|
int32_t testEvent;
|
||||||
@ -223,7 +224,7 @@ static void GBAProcessEvents(struct ARMCore* cpu) {
|
|||||||
if (cpu->halted) {
|
if (cpu->halted) {
|
||||||
cpu->cycles = cpu->nextEvent;
|
cpu->cycles = cpu->nextEvent;
|
||||||
}
|
}
|
||||||
} while (cpu->cycles >= cpu->nextEvent);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t GBATimersProcessEvents(struct GBA* gba, int32_t cycles) {
|
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) {
|
void GBATimerUpdateRegister(struct GBA* gba, int timer) {
|
||||||
struct GBATimer* currentTimer = &gba->timers[timer];
|
struct GBATimer* currentTimer = &gba->timers[timer];
|
||||||
|
GBAMemoryInvalidatePrefetch(gba);
|
||||||
if (currentTimer->enable && !currentTimer->countUp) {
|
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
|
// 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,6 +114,8 @@ void GBAMemoryReset(struct GBA* gba) {
|
|||||||
gba->memory.eventDiff = 0;
|
gba->memory.eventDiff = 0;
|
||||||
|
|
||||||
gba->memory.prefetch = false;
|
gba->memory.prefetch = false;
|
||||||
|
gba->memory.prefetchCycles = 0;
|
||||||
|
gba->memory.prefetchStalls = 0;
|
||||||
gba->memory.lastPrefetchedPc = 0;
|
gba->memory.lastPrefetchedPc = 0;
|
||||||
|
|
||||||
if (!gba->memory.wram || !gba->memory.iwram) {
|
if (!gba->memory.wram || !gba->memory.iwram) {
|
||||||
@ -235,6 +237,7 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
gba->lastJump = address;
|
gba->lastJump = address;
|
||||||
|
GBAMemoryInvalidatePrefetch(gba);
|
||||||
memory->lastPrefetchedPc = 0;
|
memory->lastPrefetchedPc = 0;
|
||||||
memory->lastPrefetchedLoads = 0;
|
memory->lastPrefetchedLoads = 0;
|
||||||
if (newRegion == memory->activeRegion && (newRegion < REGION_CART0 || (address & (SIZE_CART0 - 1)) < memory->romSize)) {
|
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;
|
return wait;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t s = cpu->memory.activeSeqCycles16 + 1;
|
// Offload the prefetch timing until the next event, which will happen too early.
|
||||||
int32_t n2s = cpu->memory.activeNonseqCycles16 - cpu->memory.activeSeqCycles16 + 1;
|
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
|
// Figure out how many sequential loads we can jam in
|
||||||
int32_t stall = s;
|
int32_t loads = (waited - 1) / s;
|
||||||
int32_t loads = 1;
|
int32_t diff = waited - loads * s;
|
||||||
int32_t previousLoads = 0;
|
|
||||||
|
|
||||||
// Don't prefetch too much if we're overlapping with a previous prefetch
|
// The next |loads|S waitstates disappear entirely, so long as they're all in a row.
|
||||||
uint32_t dist = (memory->lastPrefetchedPc - cpu->gprs[ARM_PC]) >> 1;
|
// Each instruction that waited has an N cycle that was converted into an S, so those
|
||||||
if (dist < memory->lastPrefetchedLoads) {
|
// disappear as well.
|
||||||
previousLoads = dist;
|
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) {
|
gba->cpu->cycles -= toRemove;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBAMemorySerialize(const struct GBAMemory* memory, struct GBASerializedState* state) {
|
void GBAMemorySerialize(const struct GBAMemory* memory, struct GBASerializedState* state) {
|
||||||
|
@ -130,9 +130,13 @@ struct GBAMemory {
|
|||||||
char waitstatesPrefetchNonseq32[16];
|
char waitstatesPrefetchNonseq32[16];
|
||||||
char waitstatesPrefetchNonseq16[16];
|
char waitstatesPrefetchNonseq16[16];
|
||||||
int activeRegion;
|
int activeRegion;
|
||||||
|
|
||||||
bool prefetch;
|
bool prefetch;
|
||||||
uint32_t lastPrefetchedPc;
|
uint32_t lastPrefetchedPc;
|
||||||
uint32_t lastPrefetchedLoads;
|
uint32_t lastPrefetchedLoads;
|
||||||
|
int32_t prefetchCycles;
|
||||||
|
int prefetchStalls;
|
||||||
|
|
||||||
uint32_t biosPrefetch;
|
uint32_t biosPrefetch;
|
||||||
|
|
||||||
struct GBADMA dma[4];
|
struct GBADMA dma[4];
|
||||||
@ -176,6 +180,8 @@ 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);
|
int32_t GBAMemoryRunDMAs(struct GBA* gba, int32_t cycles);
|
||||||
|
|
||||||
|
void GBAMemoryInvalidatePrefetch(struct GBA* gba);
|
||||||
|
|
||||||
struct GBASerializedState;
|
struct GBASerializedState;
|
||||||
void GBAMemorySerialize(const struct GBAMemory* memory, struct GBASerializedState* state);
|
void GBAMemorySerialize(const struct GBAMemory* memory, struct GBASerializedState* state);
|
||||||
void GBAMemoryDeserialize(struct GBAMemory* memory, const struct GBASerializedState* state);
|
void GBAMemoryDeserialize(struct GBAMemory* memory, const struct GBASerializedState* state);
|
||||||
|
Loading…
Reference in New Issue
Block a user