all repos — mgba @ 1f156921732db400cfbb4a0ff3f8efc0faf5dd1e

mGBA Game Boy Advance Emulator

src/gba/timer.c (view raw)

  1/* Copyright (c) 2013-2018 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
 11#define TIMER_RELOAD_DELAY 0
 12#define TIMER_STARTUP_DELAY 2
 13
 14#define REG_TMCNT_LO(X) (REG_TM0CNT_LO + ((X) << 2))
 15
 16static void GBATimerIrq(struct GBA* gba, int timerId, uint32_t cyclesLate) {
 17	struct GBATimer* timer = &gba->timers[timerId];
 18	if (GBATimerFlagsIsDoIrq(timer->flags)) {
 19		GBARaiseIRQ(gba, IRQ_TIMER0 + timerId, cyclesLate);
 20	}
 21}
 22
 23void GBATimerUpdate(struct mTiming* timing, struct GBATimer* timer, uint16_t* io, uint32_t cyclesLate) {
 24	if (GBATimerFlagsIsCountUp(timer->flags)) {
 25		*io = timer->reload;
 26	} else {
 27		GBATimerUpdateRegisterInternal(timer, timing, io, TIMER_RELOAD_DELAY + cyclesLate);
 28	}
 29}
 30
 31static void GBATimerUpdateAudio(struct GBA* gba, int timerId, uint32_t cyclesLate) {
 32	if (!gba->audio.enable) {
 33		return;
 34	}
 35	if ((gba->audio.chALeft || gba->audio.chARight) && gba->audio.chATimer == timerId) {
 36		GBAAudioSampleFIFO(&gba->audio, 0, cyclesLate);
 37	}
 38
 39	if ((gba->audio.chBLeft || gba->audio.chBRight) && gba->audio.chBTimer == timerId) {
 40		GBAAudioSampleFIFO(&gba->audio, 1, cyclesLate);
 41	}
 42}
 43
 44bool GBATimerUpdateCountUp(struct mTiming* timing, struct GBATimer* nextTimer, uint16_t* io, uint32_t cyclesLate) {
 45	if (GBATimerFlagsIsCountUp(nextTimer->flags)) { // TODO: Does this increment while disabled?
 46		++*io;
 47		if (!*io && GBATimerFlagsIsEnable(nextTimer->flags)) {
 48			GBATimerUpdate(timing, nextTimer, io, cyclesLate);
 49			return true;
 50		}
 51	}
 52	return false;
 53}
 54
 55static void GBATimerUpdate0(struct mTiming* timing, void* context, uint32_t cyclesLate) {
 56	struct GBA* gba = context;
 57	GBATimerUpdateAudio(gba, 0, cyclesLate);
 58	GBATimerUpdate(timing, &gba->timers[0], &gba->memory.io[REG_TM0CNT_LO >> 1], cyclesLate);
 59	GBATimerIrq(gba, 0, cyclesLate);
 60	if (GBATimerUpdateCountUp(timing, &gba->timers[1], &gba->memory.io[REG_TM1CNT_LO >> 1], cyclesLate)) {
 61		GBATimerIrq(gba, 1, cyclesLate);
 62	}
 63}
 64
 65static void GBATimerUpdate1(struct mTiming* timing, void* context, uint32_t cyclesLate) {
 66	struct GBA* gba = context;
 67	GBATimerUpdateAudio(gba, 1, cyclesLate);
 68	GBATimerUpdate(timing, &gba->timers[1], &gba->memory.io[REG_TM1CNT_LO >> 1], cyclesLate);
 69	GBATimerIrq(gba, 1, cyclesLate);
 70	if (GBATimerUpdateCountUp(timing, &gba->timers[2], &gba->memory.io[REG_TM2CNT_LO >> 1], cyclesLate)) {
 71		GBATimerIrq(gba, 2, cyclesLate);
 72	}
 73}
 74
 75static void GBATimerUpdate2(struct mTiming* timing, void* context, uint32_t cyclesLate) {
 76	struct GBA* gba = context;
 77	GBATimerUpdate(timing, &gba->timers[2], &gba->memory.io[REG_TM2CNT_LO >> 1], cyclesLate);
 78	GBATimerIrq(gba, 2, cyclesLate);
 79	if (GBATimerUpdateCountUp(timing, &gba->timers[3], &gba->memory.io[REG_TM3CNT_LO >> 1], cyclesLate)) {
 80		GBATimerIrq(gba, 3, cyclesLate);
 81	}
 82}
 83
 84static void GBATimerUpdate3(struct mTiming* timing, void* context, uint32_t cyclesLate) {
 85	struct GBA* gba = context;
 86	GBATimerUpdate(timing, &gba->timers[3], &gba->memory.io[REG_TM3CNT_LO >> 1], cyclesLate);
 87	GBATimerIrq(gba, 3, cyclesLate);
 88}
 89
 90void GBATimerInit(struct GBA* gba) {
 91	memset(gba->timers, 0, sizeof(gba->timers));
 92	gba->timers[0].event.name = "GBA Timer 0";
 93	gba->timers[0].event.callback = GBATimerUpdate0;
 94	gba->timers[0].event.context = gba;
 95	gba->timers[0].event.priority = 0x20;
 96	gba->timers[1].event.name = "GBA Timer 1";
 97	gba->timers[1].event.callback = GBATimerUpdate1;
 98	gba->timers[1].event.context = gba;
 99	gba->timers[1].event.priority = 0x21;
100	gba->timers[2].event.name = "GBA Timer 2";
101	gba->timers[2].event.callback = GBATimerUpdate2;
102	gba->timers[2].event.context = gba;
103	gba->timers[2].event.priority = 0x22;
104	gba->timers[3].event.name = "GBA Timer 3";
105	gba->timers[3].event.callback = GBATimerUpdate3;
106	gba->timers[3].event.context = gba;
107	gba->timers[3].event.priority = 0x23;
108}
109
110void GBATimerUpdateRegister(struct GBA* gba, int timer, int32_t cyclesLate) {
111	struct GBATimer* currentTimer = &gba->timers[timer];
112	if (GBATimerFlagsIsEnable(currentTimer->flags) && !GBATimerFlagsIsCountUp(currentTimer->flags)) {
113		int32_t prefetchSkew = cyclesLate;
114		if (gba->memory.lastPrefetchedPc > (uint32_t) gba->cpu->gprs[ARM_PC]) {
115			prefetchSkew += ((gba->memory.lastPrefetchedPc - gba->cpu->gprs[ARM_PC]) * gba->cpu->memory.activeSeqCycles16) / WORD_SIZE_THUMB;
116		}
117		GBATimerUpdateRegisterInternal(currentTimer, &gba->timing, &gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1], prefetchSkew);
118	}
119}
120
121void GBATimerUpdateRegisterInternal(struct GBATimer* timer, struct mTiming* timing, uint16_t* io, int32_t skew) {
122	if (!GBATimerFlagsIsEnable(timer->flags) || GBATimerFlagsIsCountUp(timer->flags)) {
123		return;
124	}
125
126	// Align timer
127	int prescaleBits = GBATimerFlagsGetPrescaleBits(timer->flags);
128	int32_t currentTime = mTimingCurrentTime(timing) - skew;
129	int32_t tickMask = (1 << prescaleBits) - 1;
130	currentTime &= ~tickMask;
131
132	// Update register
133	int32_t tickIncrement = currentTime - timer->lastEvent;
134	timer->lastEvent = currentTime;
135	tickIncrement >>= prescaleBits;
136	tickIncrement += *io;
137	*io = tickIncrement;
138	while (tickIncrement >= 0x10000) {
139		tickIncrement -= 0x10000 - timer->reload;
140	}
141	*io = tickIncrement;
142
143	// Schedule next update
144	tickIncrement = (0x10000 - tickIncrement) << prescaleBits;
145	currentTime += tickIncrement;
146	currentTime &= ~tickMask;
147	currentTime -= mTimingCurrentTime(timing);
148	mTimingDeschedule(timing, &timer->event);
149	mTimingSchedule(timing, &timer->event, currentTime);
150}
151
152void GBATimerWriteTMCNT_LO(struct GBATimer* timer, uint16_t reload) {
153	timer->reload = reload;
154}
155
156void GBATimerWriteTMCNT_HI(struct GBATimer* timer, struct mTiming* timing, uint16_t* io, uint16_t control) {
157	GBATimerUpdateRegisterInternal(timer, timing, io, 0);
158
159	unsigned oldPrescale = GBATimerFlagsGetPrescaleBits(timer->flags);
160	unsigned prescaleBits;
161	switch (control & 0x0003) {
162	case 0x0000:
163		prescaleBits = 0;
164		break;
165	case 0x0001:
166		prescaleBits = 6;
167		break;
168	case 0x0002:
169		prescaleBits = 8;
170		break;
171	case 0x0003:
172		prescaleBits = 10;
173		break;
174	}
175	prescaleBits += timer->forcedPrescale;
176	timer->flags = GBATimerFlagsSetPrescaleBits(timer->flags, prescaleBits);
177	timer->flags = GBATimerFlagsTestFillCountUp(timer->flags, timer > 0 && (control & 0x0004));
178	timer->flags = GBATimerFlagsTestFillDoIrq(timer->flags, control & 0x0040);
179	bool wasEnabled = GBATimerFlagsIsEnable(timer->flags);
180	timer->flags = GBATimerFlagsTestFillEnable(timer->flags, control & 0x0080);
181	if (!wasEnabled && GBATimerFlagsIsEnable(timer->flags)) {
182		mTimingDeschedule(timing, &timer->event);
183		*io = timer->reload;
184		int32_t tickMask = (1 << prescaleBits) - 1;
185		timer->lastEvent = (mTimingCurrentTime(timing) - TIMER_STARTUP_DELAY) & ~tickMask;
186		GBATimerUpdateRegisterInternal(timer, timing, io, TIMER_STARTUP_DELAY);
187	} else if (wasEnabled && !GBATimerFlagsIsEnable(timer->flags)) {
188		mTimingDeschedule(timing, &timer->event);
189	} else if (GBATimerFlagsIsEnable(timer->flags) && GBATimerFlagsGetPrescaleBits(timer->flags) != oldPrescale && !GBATimerFlagsIsCountUp(timer->flags)) {
190		mTimingDeschedule(timing, &timer->event);
191		int32_t tickMask = (1 << prescaleBits) - 1;
192		timer->lastEvent = (mTimingCurrentTime(timing) - TIMER_STARTUP_DELAY) & ~tickMask;
193		GBATimerUpdateRegisterInternal(timer, timing, io, TIMER_STARTUP_DELAY);
194	}
195}