all repos — mgba @ 8b4b63498923c80d52454fb0dbb9f3f7263866d0

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