all repos — mgba @ de9bff4a29db25554e5234ff788617ed46a9436c

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