all repos — mgba @ db08a75d9b031c3bfb6f510f6f52db6ace05c93e

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