all repos — mgba @ e0ae2e89063ec7139b89358cf476d4d79fdbcc8e

mGBA Game Boy Advance Emulator

src/ds/timer.c (view raw)

  1/* Copyright (c) 2013-2016 Jeffrey Pfau
  2 *
  3 * This Source Code Form is subject to the terms of the Mozilla Public
  4 * License, v. 2.0. If a copy of the MPL was not distributed with this
  5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6#include <mgba/internal/ds/timer.h>
  7
  8#include <mgba/internal/arm/arm.h>
  9#include <mgba/internal/ds/ds.h>
 10
 11void DSTimerUpdateRegister(struct DSTimer* timer, struct ARMCore* cpu, uint16_t* io) {
 12	if (DSTimerFlagsIsEnable(timer->flags) && !DSTimerFlagsIsCountUp(timer->flags)) {
 13		// Reading this takes two cycles (1N+1I), so let's remove them preemptively
 14		*io = timer->oldReload + ((cpu->cycles - timer->lastEvent - 2) >> DSTimerFlagsGetPrescaleBits(timer->flags));
 15	}
 16}
 17
 18void DSTimerWriteTMCNT_LO(struct DSTimer* timer, uint16_t reload) {
 19	timer->reload = reload;
 20	timer->overflowInterval = (0x10000 - timer->reload) << DSTimerFlagsGetPrescaleBits(timer->flags);
 21}
 22
 23void DSTimerWriteTMCNT_HI(struct DSTimer* timer, struct ARMCore* cpu, uint16_t* io, uint16_t control) {
 24	DSTimerUpdateRegister(timer, cpu, io);
 25
 26	unsigned oldPrescale = DSTimerFlagsGetPrescaleBits(timer->flags);
 27	switch (control & 0x0003) {
 28	case 0x0000:
 29		timer->flags = DSTimerFlagsSetPrescaleBits(timer->flags, 0);
 30		break;
 31	case 0x0001:
 32		timer->flags = DSTimerFlagsSetPrescaleBits(timer->flags, 6);
 33		break;
 34	case 0x0002:
 35		timer->flags = DSTimerFlagsSetPrescaleBits(timer->flags, 8);
 36		break;
 37	case 0x0003:
 38		timer->flags = DSTimerFlagsSetPrescaleBits(timer->flags, 10);
 39		break;
 40	}
 41	timer->flags = DSTimerFlagsTestFillCountUp(timer->flags, control & 0x0004);
 42	timer->flags = DSTimerFlagsTestFillDoIrq(timer->flags, control & 0x0040);
 43	timer->overflowInterval = (0x10000 - timer->reload) << DSTimerFlagsGetPrescaleBits(timer->flags);
 44	bool wasEnabled = DSTimerFlagsIsEnable(timer->flags);
 45	timer->flags = DSTimerFlagsTestFillEnable(timer->flags, control & 0x0080);
 46	if (!wasEnabled && DSTimerFlagsIsEnable(timer->flags)) {
 47		if (!DSTimerFlagsIsCountUp(timer->flags)) {
 48			timer->nextEvent = cpu->cycles + timer->overflowInterval;
 49		} else {
 50			timer->nextEvent = INT_MAX;
 51		}
 52		*io = timer->reload;
 53		timer->oldReload = timer->reload;
 54		timer->lastEvent = cpu->cycles;
 55	} else if (wasEnabled && !DSTimerFlagsIsEnable(timer->flags)) {
 56		if (!DSTimerFlagsIsCountUp(timer->flags)) {
 57			*io = timer->oldReload + ((cpu->cycles - timer->lastEvent) >> oldPrescale);
 58		}
 59	} else if (DSTimerFlagsGetPrescaleBits(timer->flags) != oldPrescale && !DSTimerFlagsIsCountUp(timer->flags)) {
 60		// FIXME: this might be before present
 61		timer->nextEvent = timer->lastEvent + timer->overflowInterval;
 62	}
 63
 64	if (timer->nextEvent < cpu->nextEvent) {
 65		cpu->nextEvent = timer->nextEvent;
 66	}
 67}
 68
 69int32_t DSTimersProcessEvents(struct DS* ds, int32_t cycles) {
 70	int32_t nextEvent = INT_MAX;
 71	if (!ds->timersEnabled7) {
 72		return nextEvent;
 73	}
 74
 75	struct DSTimer* timer;
 76	struct DSTimer* nextTimer;
 77
 78	int t;
 79	for (t = 0; t < 4; ++t) {
 80		timer = &ds->timers7[t];
 81		if (DSTimerFlagsIsEnable(timer->flags)) {
 82			timer->nextEvent -= cycles;
 83			timer->lastEvent -= cycles;
 84			while (timer->nextEvent <= 0) {
 85				timer->lastEvent = timer->nextEvent;
 86				timer->nextEvent += timer->overflowInterval;
 87				ds->memory.io7[(DS7_REG_TM0CNT_LO + (t << 2)) >> 1] = timer->reload;
 88				timer->oldReload = timer->reload;
 89
 90				if (DSTimerFlagsIsDoIrq(timer->flags)) {
 91					DSRaiseIRQ(ds->arm7, ds->memory.io7, DS_IRQ_TIMER0);
 92				}
 93
 94				if (t == 3) {
 95					break;
 96				}
 97
 98				nextTimer = &ds->timers7[t + 1];
 99				if (DSTimerFlagsIsCountUp(nextTimer->flags)) {
100					++ds->memory.io7[(DS7_REG_TM1CNT_LO + (t << 2)) >> 1];
101					if (!ds->memory.io7[(DS7_REG_TM1CNT_LO + (t << 2)) >> 1]) {
102						nextTimer->nextEvent = 0;
103					}
104				}
105			}
106			nextEvent = timer->nextEvent;
107		}
108	}
109	return nextEvent;
110}