all repos — mgba @ 7c59350e9dfa22901b5c3e8d8f789f46bd843727

mGBA Game Boy Advance Emulator

GBA Timer: More timer improvements
Vicki Pfau vi@endrift.com
Wed, 05 Jul 2017 19:28:38 -0700
commit

7c59350e9dfa22901b5c3e8d8f789f46bd843727

parent

db08a75d9b031c3bfb6f510f6f52db6ace05c93e

M include/mgba/internal/gba/gba.hinclude/mgba/internal/gba/gba.h

@@ -79,7 +79,6 @@

uint32_t bus; int performingDMA; - struct mTimingEvent timerMaster; struct GBATimer timers[4]; int springIRQ;
M include/mgba/internal/gba/serialize.hinclude/mgba/internal/gba/serialize.h

@@ -264,10 +264,10 @@ } video;

struct { uint16_t reload; - uint16_t oldReload; + uint16_t reserved; uint32_t lastEvent; uint32_t nextEvent; - int32_t overflowInterval; + uint32_t nextIrq; GBATimerFlags flags; } timers[4];
M include/mgba/internal/gba/timer.hinclude/mgba/internal/gba/timer.h

@@ -17,12 +17,14 @@ DECL_BITS(GBATimerFlags, PrescaleBits, 0, 4);

DECL_BIT(GBATimerFlags, CountUp, 4); DECL_BIT(GBATimerFlags, DoIrq, 5); DECL_BIT(GBATimerFlags, Enable, 6); +DECL_BIT(GBATimerFlags, IrqPending, 7); struct GBA; struct GBATimer { uint16_t reload; int32_t lastEvent; struct mTimingEvent event; + struct mTimingEvent irq; GBATimerFlags flags; };
M src/gba/io.csrc/gba/io.c

@@ -928,6 +928,7 @@ STORE_16(gba->memory.io[(REG_DMA0CNT_LO + i * 12) >> 1], (REG_DMA0CNT_LO + i * 12), state->io);

STORE_16(gba->timers[i].reload, 0, &state->timers[i].reload); STORE_32(gba->timers[i].lastEvent - mTimingCurrentTime(&gba->timing), 0, &state->timers[i].lastEvent); STORE_32(gba->timers[i].event.when - mTimingCurrentTime(&gba->timing), 0, &state->timers[i].nextEvent); + STORE_32(gba->timers[i].irq.when - mTimingCurrentTime(&gba->timing), 0, &state->timers[i].nextIrq); STORE_32(gba->timers[i].flags, 0, &state->timers[i].flags); STORE_32(gba->memory.dma[i].nextSource, 0, &state->dma[i].nextSource); STORE_32(gba->memory.dma[i].nextDest, 0, &state->dma[i].nextDest);

@@ -951,10 +952,6 @@ }

} uint32_t when; - LOAD_32(when, 0, &state->masterCycles); - when += state->cpu.cycles; - when = 0x400 - (when & 0x3FF); - mTimingSchedule(&gba->timing, &gba->timerMaster, when); for (i = 0; i < 4; ++i) { LOAD_16(gba->timers[i].reload, 0, &state->timers[i].reload); LOAD_32(gba->timers[i].flags, 0, &state->timers[i].flags);

@@ -968,6 +965,10 @@ }

LOAD_32(when, 0, &state->timers[i].nextEvent); if (GBATimerFlagsIsEnable(gba->timers[i].flags)) { mTimingSchedule(&gba->timing, &gba->timers[i].event, when); + } + LOAD_32(when, 0, &state->timers[i].nextIrq); + if (GBATimerFlagsIsIrqPending(gba->timers[i].flags)) { + mTimingSchedule(&gba->timing, &gba->timers[i].irq, when); } LOAD_16(gba->memory.dma[i].reg, (REG_DMA0CNT_HI + i * 12), state->io);
M src/gba/timer.csrc/gba/timer.c

@@ -8,7 +8,39 @@

#include <mgba/internal/gba/gba.h> #include <mgba/internal/gba/io.h> -#define TIMER_MASTER 1024 +#define TIMER_IRQ_DELAY 7 + +static void GBATimerIrq(struct GBA* gba, int timerId) { + struct GBATimer* timer = &gba->timers[timerId]; + if (GBATimerFlagsIsIrqPending(timer->flags)) { + timer->flags = GBATimerFlagsClearIrqPending(timer->flags); + GBARaiseIRQ(gba, IRQ_TIMER0 + timerId); + } +} + +static void GBATimerIrq0(struct mTiming* timing, void* context, uint32_t cyclesLate) { + UNUSED(timing); + UNUSED(cyclesLate); + GBATimerIrq(context, 0); +} + +static void GBATimerIrq1(struct mTiming* timing, void* context, uint32_t cyclesLate) { + UNUSED(timing); + UNUSED(cyclesLate); + GBATimerIrq(context, 1); +} + +static void GBATimerIrq2(struct mTiming* timing, void* context, uint32_t cyclesLate) { + UNUSED(timing); + UNUSED(cyclesLate); + GBATimerIrq(context, 2); +} + +static void GBATimerIrq3(struct mTiming* timing, void* context, uint32_t cyclesLate) { + UNUSED(timing); + UNUSED(cyclesLate); + GBATimerIrq(context, 3); +} static void GBATimerUpdate(struct GBA* gba, int timerId, uint32_t cyclesLate) { struct GBATimer* timer = &gba->timers[timerId];

@@ -20,7 +52,10 @@ timer->lastEvent = currentTime;

GBATimerUpdateRegister(gba, timerId, 0); if (GBATimerFlagsIsDoIrq(timer->flags)) { - GBARaiseIRQ(gba, IRQ_TIMER0 + timerId); + timer->flags = GBATimerFlagsFillIrqPending(timer->flags); + if (!mTimingIsScheduled(&gba->timing, &timer->irq)) { + mTimingSchedule(&gba->timing, &timer->irq, TIMER_IRQ_DELAY - cyclesLate); + } } if (gba->audio.enable && timerId < 2) {

@@ -44,15 +79,6 @@ }

} } -static void GBATimerMasterUpdate(struct mTiming* timing, void* context, uint32_t cyclesLate) { - struct GBA* gba = context; - mTimingSchedule(timing, &gba->timerMaster, TIMER_MASTER - cyclesLate); - GBATimerUpdateRegister(gba, 0, cyclesLate); - GBATimerUpdateRegister(gba, 1, cyclesLate); - GBATimerUpdateRegister(gba, 2, cyclesLate); - GBATimerUpdateRegister(gba, 3, cyclesLate); -} - static void GBATimerUpdate0(struct mTiming* timing, void* context, uint32_t cyclesLate) { UNUSED(timing); GBATimerUpdate(context, 0, cyclesLate);

@@ -74,28 +100,39 @@ GBATimerUpdate(context, 3, cyclesLate);

} void GBATimerInit(struct GBA* gba) { - gba->timerMaster.name = "GBA Timer Master"; - gba->timerMaster.callback = GBATimerMasterUpdate; - gba->timerMaster.context = gba; - gba->timerMaster.priority = 0x20; - mTimingSchedule(&gba->timing, &gba->timerMaster, TIMER_MASTER); memset(gba->timers, 0, sizeof(gba->timers)); gba->timers[0].event.name = "GBA Timer 0"; gba->timers[0].event.callback = GBATimerUpdate0; gba->timers[0].event.context = gba; - gba->timers[0].event.priority = 0x21; + gba->timers[0].event.priority = 0x20; gba->timers[1].event.name = "GBA Timer 1"; gba->timers[1].event.callback = GBATimerUpdate1; gba->timers[1].event.context = gba; - gba->timers[1].event.priority = 0x22; + gba->timers[1].event.priority = 0x21; gba->timers[2].event.name = "GBA Timer 2"; gba->timers[2].event.callback = GBATimerUpdate2; gba->timers[2].event.context = gba; - gba->timers[2].event.priority = 0x23; + gba->timers[2].event.priority = 0x22; gba->timers[3].event.name = "GBA Timer 3"; gba->timers[3].event.callback = GBATimerUpdate3; gba->timers[3].event.context = gba; - gba->timers[3].event.priority = 0x24; + gba->timers[3].event.priority = 0x23; + gba->timers[0].irq.name = "GBA Timer 0 IRQ"; + gba->timers[0].irq.callback = GBATimerIrq0; + gba->timers[0].irq.context = gba; + gba->timers[0].irq.priority = 0x28; + gba->timers[1].irq.name = "GBA Timer 1 IRQ"; + gba->timers[1].irq.callback = GBATimerIrq1; + gba->timers[1].irq.context = gba; + gba->timers[1].irq.priority = 0x29; + gba->timers[2].irq.name = "GBA Timer 2 IRQ"; + gba->timers[2].irq.callback = GBATimerIrq2; + gba->timers[2].irq.context = gba; + gba->timers[2].irq.priority = 0x2A; + gba->timers[3].irq.name = "GBA Timer 3 IRQ"; + gba->timers[3].irq.callback = GBATimerIrq3; + gba->timers[3].irq.context = gba; + gba->timers[3].irq.priority = 0x2B; } void GBATimerUpdateRegister(struct GBA* gba, int timer, int32_t cyclesLate) {

@@ -117,17 +154,10 @@ currentTimer->lastEvent = currentTime;

tickIncrement >>= prescaleBits; tickIncrement += gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1]; gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = tickIncrement; - tickIncrement -= 0x10000; - mTimingDeschedule(&gba->timing, &currentTimer->event); - if (tickIncrement >= 0) { - mTimingSchedule(&gba->timing, &currentTimer->event, 7 - (tickIncrement << prescaleBits)); - } else { - int32_t nextIncrement = mTimingUntil(&gba->timing, &gba->timerMaster); - tickIncrement = -tickIncrement; - tickIncrement <<= prescaleBits; - if (nextIncrement - tickIncrement > 0) { - mTimingSchedule(&gba->timing, &currentTimer->event, 7 + tickIncrement); - } + if (!mTimingIsScheduled(&gba->timing, &currentTimer->event)) { + tickIncrement = (0x10000 - tickIncrement) << prescaleBits; + currentTime -= mTimingCurrentTime(&gba->timing) - cyclesLate; + mTimingSchedule(&gba->timing, &currentTimer->event, tickIncrement + currentTime); } }