all repos — mgba @ 7a5190e95e517f8b1030340e72ae0288bb601f29

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