all repos — mgba @ a506f6cd9d6fd025410f62e3b663b0aaea3f0b82

mGBA Game Boy Advance Emulator

GBA: Convert timers to mTiming
Jeffrey Pfau jeffrey@endrift.com
Fri, 23 Sep 2016 22:56:45 -0700
commit

a506f6cd9d6fd025410f62e3b663b0aaea3f0b82

parent

e423cd45e5805f0d45fceb032eee8bee6d00f1c6

6 files changed, 189 insertions(+), 252 deletions(-)

jump to
M src/gb/timer.csrc/gb/timer.c

@@ -64,6 +64,7 @@ void GBTimerDivReset(struct GBTimer* timer) {

timer->p->memory.io[REG_DIV] = 0; timer->internalDiv = 0; timer->nextDiv = GB_DMG_DIV_PERIOD; + mTimingDeschedule(&timer->p->timing, &timer->event); mTimingSchedule(&timer->p->timing, &timer->event, timer->nextDiv); }
M src/gba/gba.csrc/gba/gba.c

@@ -18,6 +18,7 @@ #include "gba/overrides.h"

#include "gba/rr/rr.h" #include "gba/serialize.h" #include "gba/sio.h" +#include "gba/timer.h" #include "gba/vfame.h" #include "util/crc32.h"

@@ -39,7 +40,6 @@

static void GBAInit(void* cpu, struct mCPUComponent* component); static void GBAInterruptHandlerInit(struct ARMInterruptHandler* irqh); static void GBAProcessEvents(struct ARMCore* cpu); -static int32_t GBATimersProcessEvents(struct GBA* gba, int32_t cycles); static void GBAHitStub(struct ARMCore* cpu, uint32_t opcode); static void GBAIllegal(struct ARMCore* cpu, uint32_t opcode); static void GBABreakpoint(struct ARMCore* cpu, int immediate);

@@ -80,9 +80,6 @@

gba->sio.p = gba; GBASIOInit(&gba->sio); - gba->timersEnabled = 0; - memset(gba->timers, 0, sizeof(gba->timers)); - gba->springIRQ = 0; gba->keySource = 0; gba->rotationSource = 0;

@@ -114,6 +111,8 @@

gba->pristineRom = 0; gba->pristineRomSize = 0; gba->yankedRomSize = 0; + + mTimingInit(&gba->timing, &gba->cpu->cycles); } void GBAUnloadROM(struct GBA* gba) {

@@ -156,6 +155,7 @@ GBAVideoDeinit(&gba->video);

GBAAudioDeinit(&gba->audio); GBASIODeinit(&gba->sio); gba->rr = 0; + mTimingDeinit(&gba->timing); } void GBAInterruptHandlerInit(struct ARMInterruptHandler* irqh) {

@@ -192,11 +192,9 @@ GBAMemoryReset(gba);

GBAVideoReset(&gba->video); GBAAudioReset(&gba->audio); GBAIOInit(gba); + GBATimerInit(gba); GBASIOReset(&gba->sio); - - gba->timersEnabled = 0; - memset(gba->timers, 0, sizeof(gba->timers)); gba->lastJump = 0; gba->haltPending = false;

@@ -239,12 +237,21 @@ do {

int32_t cycles = cpu->nextEvent; int32_t nextEvent = INT_MAX; int32_t testEvent; + + cpu->cycles -= cycles; + #ifndef NDEBUG if (cycles < 0) { mLOG(GBA, FATAL, "Negative cycles passed: %i", cycles); } #endif + mTimingTick(&gba->timing, cycles); + testEvent = mTimingNextEvent(&gba->timing); + if (testEvent < nextEvent) { + nextEvent = testEvent; + } + testEvent = GBAVideoProcessEvents(&gba->video, cycles); if (testEvent < nextEvent) { #ifndef NDEBUG

@@ -260,16 +267,6 @@ if (testEvent < nextEvent) {

#ifndef NDEBUG if (testEvent == 0) { mLOG(GBA, ERROR, "Audio requiring 0 cycles"); - } -#endif - nextEvent = testEvent; - } - - testEvent = GBATimersProcessEvents(gba, cycles); - if (testEvent < nextEvent) { -#ifndef NDEBUG - if (testEvent == 0) { - mLOG(GBA, ERROR, "Timers requiring 0 cycles"); } #endif nextEvent = testEvent;

@@ -290,7 +286,6 @@ if (testEvent < nextEvent) {

nextEvent = testEvent; } - cpu->cycles -= cycles; cpu->nextEvent = nextEvent; if (cpu->halted) {

@@ -310,145 +305,6 @@ #endif

} while (cpu->cycles >= cpu->nextEvent); } -static int32_t GBATimersProcessEvents(struct GBA* gba, int32_t cycles) { - int32_t nextEvent = INT_MAX; - if (gba->timersEnabled) { - struct GBATimer* timer; - struct GBATimer* nextTimer; - - timer = &gba->timers[0]; - if (GBATimerFlagsIsEnable(timer->flags) && timer->nextEvent != INT_MAX) { - timer->nextEvent -= cycles; - timer->lastEvent -= cycles; - while (timer->nextEvent <= 0) { - timer->lastEvent = timer->nextEvent; - timer->nextEvent += timer->overflowInterval; - gba->memory.io[REG_TM0CNT_LO >> 1] = timer->reload; - timer->oldReload = timer->reload; - - if (GBATimerFlagsIsDoIrq(timer->flags)) { - GBARaiseIRQ(gba, IRQ_TIMER0); - } - - if (gba->audio.enable) { - if ((gba->audio.chALeft || gba->audio.chARight) && gba->audio.chATimer == 0) { - GBAAudioSampleFIFO(&gba->audio, 0, timer->lastEvent); - } - - if ((gba->audio.chBLeft || gba->audio.chBRight) && gba->audio.chBTimer == 0) { - GBAAudioSampleFIFO(&gba->audio, 1, timer->lastEvent); - } - } - - nextTimer = &gba->timers[1]; - if (GBATimerFlagsIsCountUp(nextTimer->flags)) { - ++gba->memory.io[REG_TM1CNT_LO >> 1]; - if (!gba->memory.io[REG_TM1CNT_LO >> 1]) { - nextTimer->nextEvent = cycles; - } - } - } - nextEvent = timer->nextEvent; - } - - timer = &gba->timers[1]; - if (GBATimerFlagsIsEnable(timer->flags) && timer->nextEvent != INT_MAX) { - timer->nextEvent -= cycles; - timer->lastEvent -= cycles; - if (timer->nextEvent <= 0) { - timer->lastEvent = timer->nextEvent; - timer->nextEvent += timer->overflowInterval; - gba->memory.io[REG_TM1CNT_LO >> 1] = timer->reload; - timer->oldReload = timer->reload; - - if (GBATimerFlagsIsDoIrq(timer->flags)) { - GBARaiseIRQ(gba, IRQ_TIMER1); - } - - if (gba->audio.enable) { - if ((gba->audio.chALeft || gba->audio.chARight) && gba->audio.chATimer == 1) { - GBAAudioSampleFIFO(&gba->audio, 0, timer->lastEvent); - } - - if ((gba->audio.chBLeft || gba->audio.chBRight) && gba->audio.chBTimer == 1) { - GBAAudioSampleFIFO(&gba->audio, 1, timer->lastEvent); - } - } - - if (GBATimerFlagsIsCountUp(timer->flags)) { - timer->nextEvent = INT_MAX; - } - - nextTimer = &gba->timers[2]; - if (GBATimerFlagsIsCountUp(nextTimer->flags)) { - ++gba->memory.io[REG_TM2CNT_LO >> 1]; - if (!gba->memory.io[REG_TM2CNT_LO >> 1]) { - nextTimer->nextEvent = cycles; - } - } - } - if (timer->nextEvent < nextEvent) { - nextEvent = timer->nextEvent; - } - } - - timer = &gba->timers[2]; - if (GBATimerFlagsIsEnable(timer->flags) && timer->nextEvent != INT_MAX) { - timer->nextEvent -= cycles; - timer->lastEvent -= cycles; - if (timer->nextEvent <= 0) { - timer->lastEvent = timer->nextEvent; - timer->nextEvent += timer->overflowInterval; - gba->memory.io[REG_TM2CNT_LO >> 1] = timer->reload; - timer->oldReload = timer->reload; - - if (GBATimerFlagsIsDoIrq(timer->flags)) { - GBARaiseIRQ(gba, IRQ_TIMER2); - } - - if (GBATimerFlagsIsCountUp(timer->flags)) { - timer->nextEvent = INT_MAX; - } - - nextTimer = &gba->timers[3]; - if (GBATimerFlagsIsCountUp(nextTimer->flags)) { - ++gba->memory.io[REG_TM3CNT_LO >> 1]; - if (!gba->memory.io[REG_TM3CNT_LO >> 1]) { - nextTimer->nextEvent = cycles; - } - } - } - if (timer->nextEvent < nextEvent) { - nextEvent = timer->nextEvent; - } - } - - timer = &gba->timers[3]; - if (GBATimerFlagsIsEnable(timer->flags) && timer->nextEvent != INT_MAX) { - timer->nextEvent -= cycles; - timer->lastEvent -= cycles; - if (timer->nextEvent <= 0) { - timer->lastEvent = timer->nextEvent; - timer->nextEvent += timer->overflowInterval; - gba->memory.io[REG_TM3CNT_LO >> 1] = timer->reload; - timer->oldReload = timer->reload; - - if (GBATimerFlagsIsDoIrq(timer->flags)) { - GBARaiseIRQ(gba, IRQ_TIMER3); - } - - if (GBATimerFlagsIsCountUp(timer->flags)) { - timer->nextEvent = INT_MAX; - } - } - if (timer->nextEvent < nextEvent) { - nextEvent = timer->nextEvent; - } - } - } - return nextEvent; -} - void GBAAttachDebugger(struct GBA* gba, struct mDebugger* debugger) { gba->debugger = (struct ARMDebugger*) debugger->platform; gba->debugger->setSoftwareBreakpoint = _setSoftwareBreakpoint;

@@ -579,72 +435,6 @@ gba->memory.romSize = patchedSize;

gba->memory.romMask = SIZE_CART0 - 1; gba->romCrc32 = doCrc32(gba->memory.rom, gba->memory.romSize); } - -void GBATimerUpdateRegister(struct GBA* gba, int timer) { - struct GBATimer* currentTimer = &gba->timers[timer]; - if (GBATimerFlagsIsEnable(currentTimer->flags) && !GBATimerFlagsIsCountUp(currentTimer->flags)) { - int32_t prefetchSkew = 0; - if (gba->memory.lastPrefetchedPc >= (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) >> GBATimerFlagsGetPrescaleBits(currentTimer->flags)); - } -} - -void GBATimerWriteTMCNT_LO(struct GBA* gba, int timer, uint16_t reload) { - gba->timers[timer].reload = reload; - gba->timers[timer].overflowInterval = (0x10000 - gba->timers[timer].reload) << GBATimerFlagsGetPrescaleBits(gba->timers[timer].flags); -} - -void GBATimerWriteTMCNT_HI(struct GBA* gba, int timer, uint16_t control) { - struct GBATimer* currentTimer = &gba->timers[timer]; - GBATimerUpdateRegister(gba, timer); - - unsigned oldPrescale = GBATimerFlagsGetPrescaleBits(currentTimer->flags); - switch (control & 0x0003) { - case 0x0000: - currentTimer->flags = GBATimerFlagsSetPrescaleBits(currentTimer->flags, 0); - break; - case 0x0001: - currentTimer->flags = GBATimerFlagsSetPrescaleBits(currentTimer->flags, 6); - break; - case 0x0002: - currentTimer->flags = GBATimerFlagsSetPrescaleBits(currentTimer->flags, 8); - break; - case 0x0003: - currentTimer->flags = GBATimerFlagsSetPrescaleBits(currentTimer->flags, 10); - break; - } - currentTimer->flags = GBATimerFlagsTestFillCountUp(currentTimer->flags, timer > 0 && (control & 0x0004)); - currentTimer->flags = GBATimerFlagsTestFillDoIrq(currentTimer->flags, control & 0x0040); - currentTimer->overflowInterval = (0x10000 - currentTimer->reload) << GBATimerFlagsGetPrescaleBits(currentTimer->flags); - bool wasEnabled = GBATimerFlagsIsEnable(currentTimer->flags); - currentTimer->flags = GBATimerFlagsTestFillEnable(currentTimer->flags, control & 0x0080); - if (!wasEnabled && GBATimerFlagsIsEnable(currentTimer->flags)) { - if (!GBATimerFlagsIsCountUp(currentTimer->flags)) { - currentTimer->nextEvent = gba->cpu->cycles + currentTimer->overflowInterval; - } else { - currentTimer->nextEvent = INT_MAX; - } - gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->reload; - currentTimer->oldReload = currentTimer->reload; - currentTimer->lastEvent = gba->cpu->cycles; - gba->timersEnabled |= 1 << timer; - } else if (wasEnabled && !GBATimerFlagsIsEnable(currentTimer->flags)) { - if (!GBATimerFlagsIsCountUp(currentTimer->flags)) { - gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->oldReload + ((gba->cpu->cycles - currentTimer->lastEvent) >> oldPrescale); - } - gba->timersEnabled &= ~(1 << timer); - } else if (GBATimerFlagsGetPrescaleBits(currentTimer->flags) != oldPrescale && !GBATimerFlagsIsCountUp(currentTimer->flags)) { - // FIXME: this might be before present - currentTimer->nextEvent = currentTimer->lastEvent + currentTimer->overflowInterval; - } - - if (currentTimer->nextEvent < gba->cpu->nextEvent) { - gba->cpu->nextEvent = currentTimer->nextEvent; - } -}; void GBAWriteIE(struct GBA* gba, uint16_t value) { if (value & (1 << IRQ_KEYPAD)) {
M src/gba/gba.hsrc/gba/gba.h

@@ -1,4 +1,4 @@

-/* Copyright (c) 2013-2015 Jeffrey Pfau +/* Copyright (c) 2013-2016 Jeffrey Pfau * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this

@@ -10,12 +10,14 @@ #include "util/common.h"

#include "arm/arm.h" #include "core/log.h" +#include "core/timing.h" #include "gba/interface.h" #include "gba/memory.h" #include "gba/video.h" #include "gba/audio.h" #include "gba/sio.h" +#include "gba/timer.h" #define GBA_ARM7TDMI_FREQUENCY 0x1000000U

@@ -55,25 +57,10 @@

mLOG_DECLARE_CATEGORY(GBA); mLOG_DECLARE_CATEGORY(GBA_DEBUG); -DECL_BITFIELD(GBATimerFlags, uint32_t); -DECL_BITS(GBATimerFlags, PrescaleBits, 0, 4); -DECL_BIT(GBATimerFlags, CountUp, 4); -DECL_BIT(GBATimerFlags, DoIrq, 5); -DECL_BIT(GBATimerFlags, Enable, 6); - DECL_BITFIELD(GBADebugFlags, uint16_t); DECL_BITS(GBADebugFlags, Level, 0, 3); DECL_BIT(GBADebugFlags, Send, 8); -struct GBATimer { - uint16_t reload; - uint16_t oldReload; - int32_t lastEvent; - int32_t nextEvent; - int32_t overflowInterval; - GBATimerFlags flags; -}; - struct GBA { struct mCPUComponent d;

@@ -84,13 +71,13 @@ struct GBAAudio audio;

struct GBASIO sio; struct mCoreSync* sync; + struct mTiming timing; struct ARMDebugger* debugger; uint32_t bus; int performingDMA; - int timersEnabled; struct GBATimer timers[4]; int springIRQ;

@@ -152,10 +139,6 @@ void GBADestroy(struct GBA* gba);

void GBAReset(struct ARMCore* cpu); void GBASkipBIOS(struct GBA* gba); - -void GBATimerUpdateRegister(struct GBA* gba, int timer); -void GBATimerWriteTMCNT_LO(struct GBA* gba, int timer, uint16_t value); -void GBATimerWriteTMCNT_HI(struct GBA* gba, int timer, uint16_t value); void GBAWriteIE(struct GBA* gba, uint16_t value); void GBAWriteIME(struct GBA* gba, uint16_t value);
M src/gba/io.csrc/gba/io.c

@@ -912,7 +912,6 @@ 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_16(gba->timers[i].oldReload, 0, &state->timers[i].oldReload); STORE_32(gba->timers[i].lastEvent, 0, &state->timers[i].lastEvent); - STORE_32(gba->timers[i].nextEvent, 0, &state->timers[i].nextEvent); STORE_32(gba->timers[i].overflowInterval, 0, &state->timers[i].overflowInterval); STORE_32(gba->timers[i].flags, 0, &state->timers[i].flags); STORE_32(gba->memory.dma[i].nextSource, 0, &state->dma[i].nextSource);

@@ -936,7 +935,6 @@ GBAIOWrite(gba, i, reg);

} } - gba->timersEnabled = 0; for (i = 0; i < 4; ++i) { LOAD_16(gba->timers[i].reload, 0, &state->timers[i].reload); LOAD_16(gba->timers[i].oldReload, 0, &state->timers[i].oldReload);

@@ -945,10 +943,8 @@ LOAD_32(gba->timers[i].flags, 0, &state->timers[i].flags);

if (i > 0 && GBATimerFlagsIsCountUp(gba->timers[i].flags)) { // Overwrite invalid values in savestate gba->timers[i].lastEvent = 0; - gba->timers[i].nextEvent = INT_MAX; } else { LOAD_32(gba->timers[i].lastEvent, 0, &state->timers[i].lastEvent); - LOAD_32(gba->timers[i].nextEvent, 0, &state->timers[i].nextEvent); } LOAD_16(gba->memory.dma[i].reg, (REG_DMA0CNT_HI + i * 12), state->io); LOAD_32(gba->memory.dma[i].nextSource, 0, &state->dma[i].nextSource);

@@ -957,10 +953,6 @@ LOAD_32(gba->memory.dma[i].nextCount, 0, &state->dma[i].nextCount);

LOAD_32(gba->memory.dma[i].nextEvent, 0, &state->dma[i].nextEvent); if (GBADMARegisterGetTiming(gba->memory.dma[i].reg) != DMA_TIMING_NOW) { GBAMemoryScheduleDMA(gba, i, &gba->memory.dma[i]); - } - - if (GBATimerFlagsIsEnable(gba->timers[i].flags)) { - gba->timersEnabled |= 1 << i; } } GBAAudioWriteSOUNDCNT_X(&gba->audio, gba->memory.io[REG_SOUNDCNT_X >> 1]);
A src/gba/timer.c

@@ -0,0 +1,137 @@

+/* Copyright (c) 2013-2016 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "timer.h" + +#include "gba/gba.h" +#include "gba/io.h" + +static void GBATimerUpdate(struct mTiming* timing, struct GBA* gba, int timerId, uint32_t cyclesLate) { + struct GBATimer* timer = &gba->timers[timerId]; + gba->memory.io[(REG_TM0CNT_LO >> 1) + (timerId << 1)] = timer->reload; + timer->oldReload = timer->reload; + timer->lastEvent = timing->masterCycles - cyclesLate; + + if (GBATimerFlagsIsDoIrq(timer->flags)) { + GBARaiseIRQ(gba, IRQ_TIMER0 + timerId); + } + + if (gba->audio.enable && timerId < 2) { + if ((gba->audio.chALeft || gba->audio.chARight) && gba->audio.chATimer == timerId) { + GBAAudioSampleFIFO(&gba->audio, 0, cyclesLate); + } + + if ((gba->audio.chBLeft || gba->audio.chBRight) && gba->audio.chBTimer == timerId) { + GBAAudioSampleFIFO(&gba->audio, 1, cyclesLate); + } + } + + if (timerId < 4) { + struct GBATimer* nextTimer = &gba->timers[timerId + 1]; + if (GBATimerFlagsIsCountUp(nextTimer->flags)) { // TODO: Does this increment while disabled? + ++gba->memory.io[(REG_TM1CNT_LO >> 1) + (timerId << 1)]; + if (!gba->memory.io[(REG_TM1CNT_LO >> 1) + (timerId << 1)] && GBATimerFlagsIsEnable(nextTimer->flags)) { + mTimingSchedule(timing, &nextTimer->event, -cyclesLate); + } + } + } + + if (!GBATimerFlagsIsCountUp(timer->flags)) { + uint32_t nextEvent = timer->overflowInterval - cyclesLate; + mTimingSchedule(timing, &timer->event, nextEvent); + } +} + +static void GBATimerUpdate0(struct mTiming* timing, void* context, uint32_t cyclesLate) { + GBATimerUpdate(timing, context, 0, cyclesLate); +} + +static void GBATimerUpdate1(struct mTiming* timing, void* context, uint32_t cyclesLate) { + GBATimerUpdate(timing, context, 1, cyclesLate); +} + +static void GBATimerUpdate2(struct mTiming* timing, void* context, uint32_t cyclesLate) { + GBATimerUpdate(timing, context, 2, cyclesLate); +} + +static void GBATimerUpdate3(struct mTiming* timing, void* context, uint32_t cyclesLate) { + GBATimerUpdate(timing, context, 3, cyclesLate); +} + +void GBATimerInit(struct GBA* gba) { + 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[1].event.name = "GBA Timer 1"; + gba->timers[1].event.callback = GBATimerUpdate1; + gba->timers[1].event.context = gba; + gba->timers[2].event.name = "GBA Timer 2"; + gba->timers[2].event.callback = GBATimerUpdate2; + gba->timers[2].event.context = gba; + gba->timers[3].event.name = "GBA Timer 3"; + gba->timers[3].event.callback = GBATimerUpdate3; + gba->timers[3].event.context = gba; +} + +void GBATimerUpdateRegister(struct GBA* gba, int timer) { + struct GBATimer* currentTimer = &gba->timers[timer]; + if (GBATimerFlagsIsEnable(currentTimer->flags) && !GBATimerFlagsIsCountUp(currentTimer->flags)) { + int32_t prefetchSkew = 0; + if (gba->memory.lastPrefetchedPc >= (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 + int32_t diff = gba->cpu->cycles - (currentTimer->lastEvent - gba->timing.masterCycles); + gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->oldReload + ((diff - 2 + prefetchSkew) >> GBATimerFlagsGetPrescaleBits(currentTimer->flags)); + } +} + +void GBATimerWriteTMCNT_LO(struct GBA* gba, int timer, uint16_t reload) { + gba->timers[timer].reload = reload; + gba->timers[timer].overflowInterval = (0x10000 - gba->timers[timer].reload) << GBATimerFlagsGetPrescaleBits(gba->timers[timer].flags); +} + +void GBATimerWriteTMCNT_HI(struct GBA* gba, int timer, uint16_t control) { + struct GBATimer* currentTimer = &gba->timers[timer]; + GBATimerUpdateRegister(gba, timer); + + unsigned oldPrescale = GBATimerFlagsGetPrescaleBits(currentTimer->flags); + switch (control & 0x0003) { + case 0x0000: + currentTimer->flags = GBATimerFlagsSetPrescaleBits(currentTimer->flags, 0); + break; + case 0x0001: + currentTimer->flags = GBATimerFlagsSetPrescaleBits(currentTimer->flags, 6); + break; + case 0x0002: + currentTimer->flags = GBATimerFlagsSetPrescaleBits(currentTimer->flags, 8); + break; + case 0x0003: + currentTimer->flags = GBATimerFlagsSetPrescaleBits(currentTimer->flags, 10); + break; + } + currentTimer->flags = GBATimerFlagsTestFillCountUp(currentTimer->flags, timer > 0 && (control & 0x0004)); + currentTimer->flags = GBATimerFlagsTestFillDoIrq(currentTimer->flags, control & 0x0040); + currentTimer->overflowInterval = (0x10000 - currentTimer->reload) << GBATimerFlagsGetPrescaleBits(currentTimer->flags); + bool wasEnabled = GBATimerFlagsIsEnable(currentTimer->flags); + currentTimer->flags = GBATimerFlagsTestFillEnable(currentTimer->flags, control & 0x0080); + if (!wasEnabled && GBATimerFlagsIsEnable(currentTimer->flags)) { + mTimingDeschedule(&gba->timing, &currentTimer->event); + if (!GBATimerFlagsIsCountUp(currentTimer->flags)) { + mTimingSchedule(&gba->timing, &currentTimer->event, currentTimer->overflowInterval); + } + gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->reload; + currentTimer->oldReload = currentTimer->reload; + currentTimer->lastEvent = gba->timing.masterCycles + gba->cpu->cycles; + } else if (wasEnabled && !GBATimerFlagsIsEnable(currentTimer->flags)) { + if (!GBATimerFlagsIsCountUp(currentTimer->flags)) { + gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->oldReload + ((gba->cpu->cycles - currentTimer->lastEvent) >> oldPrescale); + } + } else if (GBATimerFlagsIsEnable(currentTimer->flags) && GBATimerFlagsGetPrescaleBits(currentTimer->flags) != oldPrescale && !GBATimerFlagsIsCountUp(currentTimer->flags)) { + mTimingDeschedule(&gba->timing, &currentTimer->event); + mTimingSchedule(&gba->timing, &currentTimer->event, currentTimer->overflowInterval - currentTimer->lastEvent); + } +}
A src/gba/timer.h

@@ -0,0 +1,33 @@

+/* Copyright (c) 2013-2016 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef GBA_TIMER_H +#define GBA_TIMER_H + +#include "util/common.h" +#include "core/timing.h" + +DECL_BITFIELD(GBATimerFlags, uint32_t); +DECL_BITS(GBATimerFlags, PrescaleBits, 0, 4); +DECL_BIT(GBATimerFlags, CountUp, 4); +DECL_BIT(GBATimerFlags, DoIrq, 5); +DECL_BIT(GBATimerFlags, Enable, 6); + +struct GBA; +struct GBATimer { + uint16_t reload; + uint16_t oldReload; + uint32_t lastEvent; + struct mTimingEvent event; + int32_t overflowInterval; + GBATimerFlags flags; +}; + +void GBATimerInit(struct GBA* gba); +void GBATimerUpdateRegister(struct GBA* gba, int timer); +void GBATimerWriteTMCNT_LO(struct GBA* gba, int timer, uint16_t value); +void GBATimerWriteTMCNT_HI(struct GBA* gba, int timer, uint16_t value); + +#endif