all repos — mgba @ 3ccffdc29eab88906a6cbb9d686f474261da4e16

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