all repos — mgba @ 11354ac23ea6cbc41df04f2132e3b44d38c1494a

mGBA Game Boy Advance Emulator

src/gba/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/gba/timer.h>
  7
  8#include <mgba/internal/gba/gba.h>
  9#include <mgba/internal/gba/io.h>
 10
 11static void GBATimerUpdate(struct mTiming* timing, struct GBA* gba, int timerId, uint32_t cyclesLate) {
 12	struct GBATimer* timer = &gba->timers[timerId];
 13	gba->memory.io[(REG_TM0CNT_LO >> 1) + (timerId << 1)] = timer->reload;
 14	timer->oldReload = timer->reload;
 15	timer->lastEvent = timing->masterCycles - cyclesLate;
 16
 17	if (GBATimerFlagsIsDoIrq(timer->flags)) {
 18		GBARaiseIRQ(gba, IRQ_TIMER0 + timerId);
 19	}
 20
 21	if (gba->audio.enable && timerId < 2) {
 22		if ((gba->audio.chALeft || gba->audio.chARight) && gba->audio.chATimer == timerId) {
 23			GBAAudioSampleFIFO(&gba->audio, 0, cyclesLate);
 24		}
 25
 26		if ((gba->audio.chBLeft || gba->audio.chBRight) && gba->audio.chBTimer == timerId) {
 27			GBAAudioSampleFIFO(&gba->audio, 1, cyclesLate);
 28		}
 29	}
 30
 31	if (timerId < 4) {
 32		struct GBATimer* nextTimer = &gba->timers[timerId + 1];
 33		if (GBATimerFlagsIsCountUp(nextTimer->flags)) { // TODO: Does this increment while disabled?
 34			++gba->memory.io[(REG_TM1CNT_LO >> 1) + (timerId << 1)];
 35			if (!gba->memory.io[(REG_TM1CNT_LO >> 1) + (timerId << 1)] && GBATimerFlagsIsEnable(nextTimer->flags)) {
 36				mTimingSchedule(timing, &nextTimer->event, -cyclesLate);
 37			}
 38		}
 39	}
 40
 41	if (!GBATimerFlagsIsCountUp(timer->flags)) {
 42		uint32_t nextEvent = timer->overflowInterval - cyclesLate;
 43		mTimingSchedule(timing, &timer->event, nextEvent);
 44	}
 45}
 46
 47static void GBATimerUpdate0(struct mTiming* timing, void* context, uint32_t cyclesLate) {
 48	GBATimerUpdate(timing, context, 0, cyclesLate);
 49}
 50
 51static void GBATimerUpdate1(struct mTiming* timing, void* context, uint32_t cyclesLate) {
 52	GBATimerUpdate(timing, context, 1, cyclesLate);
 53}
 54
 55static void GBATimerUpdate2(struct mTiming* timing, void* context, uint32_t cyclesLate) {
 56	GBATimerUpdate(timing, context, 2, cyclesLate);
 57}
 58
 59static void GBATimerUpdate3(struct mTiming* timing, void* context, uint32_t cyclesLate) {
 60	GBATimerUpdate(timing, context, 3, cyclesLate);
 61}
 62
 63void GBATimerInit(struct GBA* gba) {
 64	memset(gba->timers, 0, sizeof(gba->timers));
 65	gba->timers[0].event.name = "GBA Timer 0";
 66	gba->timers[0].event.callback = GBATimerUpdate0;
 67	gba->timers[0].event.context = gba;
 68	gba->timers[0].event.priority = 0x20;
 69	gba->timers[1].event.name = "GBA Timer 1";
 70	gba->timers[1].event.callback = GBATimerUpdate1;
 71	gba->timers[1].event.context = gba;
 72	gba->timers[1].event.priority = 0x21;
 73	gba->timers[2].event.name = "GBA Timer 2";
 74	gba->timers[2].event.callback = GBATimerUpdate2;
 75	gba->timers[2].event.context = gba;
 76	gba->timers[2].event.priority = 0x22;
 77	gba->timers[3].event.name = "GBA Timer 3";
 78	gba->timers[3].event.callback = GBATimerUpdate3;
 79	gba->timers[3].event.context = gba;
 80	gba->timers[3].event.priority = 0x23;
 81}
 82
 83void GBATimerUpdateRegister(struct GBA* gba, int timer) {
 84	struct GBATimer* currentTimer = &gba->timers[timer];
 85	if (GBATimerFlagsIsEnable(currentTimer->flags) && !GBATimerFlagsIsCountUp(currentTimer->flags)) {
 86		int32_t prefetchSkew = -2;
 87		if (gba->memory.lastPrefetchedPc > (uint32_t) gba->cpu->gprs[ARM_PC]) {
 88			prefetchSkew += ((gba->memory.lastPrefetchedPc - gba->cpu->gprs[ARM_PC]) * gba->cpu->memory.activeSeqCycles16) / WORD_SIZE_THUMB;
 89		}
 90		// Reading this takes two cycles (1N+1I), so let's remove them preemptively
 91		int32_t diff = gba->cpu->cycles - (currentTimer->lastEvent - gba->timing.masterCycles);
 92		gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->oldReload + ((diff + prefetchSkew) >> GBATimerFlagsGetPrescaleBits(currentTimer->flags));
 93	}
 94}
 95
 96void GBATimerWriteTMCNT_LO(struct GBA* gba, int timer, uint16_t reload) {
 97	gba->timers[timer].reload = reload;
 98	gba->timers[timer].overflowInterval = (0x10000 - gba->timers[timer].reload) << GBATimerFlagsGetPrescaleBits(gba->timers[timer].flags);
 99}
100
101void GBATimerWriteTMCNT_HI(struct GBA* gba, int timer, uint16_t control) {
102	struct GBATimer* currentTimer = &gba->timers[timer];
103	GBATimerUpdateRegister(gba, timer);
104
105	unsigned oldPrescale = GBATimerFlagsGetPrescaleBits(currentTimer->flags);
106	switch (control & 0x0003) {
107	case 0x0000:
108		currentTimer->flags = GBATimerFlagsSetPrescaleBits(currentTimer->flags, 0);
109		break;
110	case 0x0001:
111		currentTimer->flags = GBATimerFlagsSetPrescaleBits(currentTimer->flags, 6);
112		break;
113	case 0x0002:
114		currentTimer->flags = GBATimerFlagsSetPrescaleBits(currentTimer->flags, 8);
115		break;
116	case 0x0003:
117		currentTimer->flags = GBATimerFlagsSetPrescaleBits(currentTimer->flags, 10);
118		break;
119	}
120	currentTimer->flags = GBATimerFlagsTestFillCountUp(currentTimer->flags, timer > 0 && (control & 0x0004));
121	currentTimer->flags = GBATimerFlagsTestFillDoIrq(currentTimer->flags, control & 0x0040);
122	currentTimer->overflowInterval = (0x10000 - currentTimer->reload) << GBATimerFlagsGetPrescaleBits(currentTimer->flags);
123	bool wasEnabled = GBATimerFlagsIsEnable(currentTimer->flags);
124	currentTimer->flags = GBATimerFlagsTestFillEnable(currentTimer->flags, control & 0x0080);
125	if (!wasEnabled && GBATimerFlagsIsEnable(currentTimer->flags)) {
126		mTimingDeschedule(&gba->timing, &currentTimer->event);
127		if (!GBATimerFlagsIsCountUp(currentTimer->flags)) {
128			mTimingSchedule(&gba->timing, &currentTimer->event, currentTimer->overflowInterval);
129		}
130		gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->reload;
131		currentTimer->oldReload = currentTimer->reload;
132		currentTimer->lastEvent = gba->timing.masterCycles + gba->cpu->cycles;
133	} else if (wasEnabled && !GBATimerFlagsIsEnable(currentTimer->flags)) {
134		mTimingDeschedule(&gba->timing, &currentTimer->event);
135	} else if (GBATimerFlagsIsEnable(currentTimer->flags) && GBATimerFlagsGetPrescaleBits(currentTimer->flags) != oldPrescale && !GBATimerFlagsIsCountUp(currentTimer->flags)) {
136		mTimingDeschedule(&gba->timing, &currentTimer->event);
137		mTimingSchedule(&gba->timing, &currentTimer->event, currentTimer->overflowInterval - currentTimer->lastEvent);
138	}
139}