all repos — mgba @ f6755a6e1b7b0cf2b944cd8ca842746f11d6bf82

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