all repos — mgba @ 79e0a0da49c24c166792285baea795edd232a4f5

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
 11static void GBATimerUpdateAudio(struct GBA* gba, int timerId, uint32_t cyclesLate) {
 12	if (!gba->audio.enable) {
 13		return;
 14	}
 15	if ((gba->audio.chALeft || gba->audio.chARight) && gba->audio.chATimer == timerId) {
 16		GBAAudioSampleFIFO(&gba->audio, 0, cyclesLate);
 17	}
 18
 19	if ((gba->audio.chBLeft || gba->audio.chBRight) && gba->audio.chBTimer == timerId) {
 20		GBAAudioSampleFIFO(&gba->audio, 1, cyclesLate);
 21	}
 22}
 23
 24void GBATimerUpdateCountUp(struct mTiming* timing, struct GBATimer* nextTimer, uint16_t* io, uint32_t cyclesLate) {
 25	if (GBATimerFlagsIsCountUp(nextTimer->flags)) { // TODO: Does this increment while disabled?
 26		++*io;
 27		if (!*io && GBATimerFlagsIsEnable(nextTimer->flags)) {
 28			mTimingSchedule(timing, &nextTimer->event, -cyclesLate);
 29		}
 30	}
 31}
 32
 33void GBATimerUpdate(struct mTiming* timing, struct GBATimer* timer, uint16_t* io, uint32_t cyclesLate) {
 34	*io = timer->reload;
 35	timer->oldReload = timer->reload;
 36	timer->lastEvent = timing->masterCycles - cyclesLate;
 37
 38	if (!GBATimerFlagsIsCountUp(timer->flags)) {
 39		uint32_t nextEvent = timer->overflowInterval - cyclesLate;
 40		mTimingSchedule(timing, &timer->event, nextEvent);
 41	}
 42}
 43
 44static void GBATimerUpdate0(struct mTiming* timing, void* context, uint32_t cyclesLate) {
 45	struct GBA* gba = context;
 46	struct GBATimer* timer = &gba->timers[0];
 47	if (GBATimerFlagsIsDoIrq(timer->flags)) {
 48		GBARaiseIRQ(gba, IRQ_TIMER0);
 49	}
 50	GBATimerUpdateAudio(gba, 0, cyclesLate);
 51	GBATimerUpdate(timing, &gba->timers[0], &gba->memory.io[REG_TM0CNT_LO >> 1], cyclesLate);
 52	GBATimerUpdateCountUp(timing, &gba->timers[1], &gba->memory.io[REG_TM1CNT_LO >> 1], cyclesLate);
 53}
 54
 55static void GBATimerUpdate1(struct mTiming* timing, void* context, uint32_t cyclesLate) {
 56	struct GBA* gba = context;
 57	struct GBATimer* timer = &gba->timers[1];
 58	if (GBATimerFlagsIsDoIrq(timer->flags)) {
 59		GBARaiseIRQ(gba, IRQ_TIMER1);
 60	}
 61	GBATimerUpdateAudio(gba, 1, cyclesLate);
 62	GBATimerUpdate(timing, &gba->timers[1], &gba->memory.io[REG_TM1CNT_LO >> 1], cyclesLate);
 63	GBATimerUpdateCountUp(timing, &gba->timers[2], &gba->memory.io[REG_TM2CNT_LO >> 1], cyclesLate);
 64}
 65
 66static void GBATimerUpdate2(struct mTiming* timing, void* context, uint32_t cyclesLate) {
 67	struct GBA* gba = context;
 68	struct GBATimer* timer = &gba->timers[2];
 69	if (GBATimerFlagsIsDoIrq(timer->flags)) {
 70		GBARaiseIRQ(gba, IRQ_TIMER2);
 71	}
 72	GBATimerUpdate(timing, &gba->timers[2], &gba->memory.io[REG_TM2CNT_LO >> 1], cyclesLate);
 73	GBATimerUpdateCountUp(timing, &gba->timers[3], &gba->memory.io[REG_TM3CNT_LO >> 1], cyclesLate);
 74}
 75
 76static void GBATimerUpdate3(struct mTiming* timing, void* context, uint32_t cyclesLate) {
 77	struct GBA* gba = context;
 78	struct GBATimer* timer = &gba->timers[3];
 79	if (GBATimerFlagsIsDoIrq(timer->flags)) {
 80		GBARaiseIRQ(gba, IRQ_TIMER3);
 81	}
 82	GBATimerUpdate(timing, &gba->timers[3], &gba->memory.io[REG_TM3CNT_LO >> 1], 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) {
106	struct GBATimer* currentTimer = &gba->timers[timer];
107	if (GBATimerFlagsIsEnable(currentTimer->flags) && !GBATimerFlagsIsCountUp(currentTimer->flags)) {
108		int32_t prefetchSkew = 0;
109		if (gba->memory.lastPrefetchedPc >= (uint32_t) gba->cpu->gprs[ARM_PC]) {
110			prefetchSkew = (gba->memory.lastPrefetchedPc - gba->cpu->gprs[ARM_PC]) * (gba->cpu->memory.activeSeqCycles16 + 1) / WORD_SIZE_THUMB;
111		}
112		GBATimerUpdateRegisterInternal(currentTimer, &gba->timing, gba->cpu, &gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1], prefetchSkew);
113	}
114}
115
116void GBATimerUpdateRegisterInternal(struct GBATimer* timer, struct mTiming* timing, struct ARMCore* cpu, uint16_t* io, int32_t skew) {
117	// Reading this takes two cycles (1N+1I), so let's remove them preemptively
118	int32_t diff = cpu->cycles - (timer->lastEvent - timing->masterCycles);
119	*io = timer->oldReload + ((diff - 2 + skew) >> GBATimerFlagsGetPrescaleBits(timer->flags));
120}
121
122void GBATimerWriteTMCNT_LO(struct GBATimer* timer, uint16_t reload) {
123	timer->reload = reload;
124	timer->overflowInterval = (0x10000 - timer->reload) << GBATimerFlagsGetPrescaleBits(timer->flags);
125}
126
127void GBATimerWriteTMCNT_HI(struct GBATimer* timer, struct mTiming* timing, struct ARMCore* cpu, uint16_t* io, uint16_t control) {
128	unsigned oldPrescale = GBATimerFlagsGetPrescaleBits(timer->flags);
129	switch (control & 0x0003) {
130	case 0x0000:
131		timer->flags = GBATimerFlagsSetPrescaleBits(timer->flags, 0);
132		break;
133	case 0x0001:
134		timer->flags = GBATimerFlagsSetPrescaleBits(timer->flags, 6);
135		break;
136	case 0x0002:
137		timer->flags = GBATimerFlagsSetPrescaleBits(timer->flags, 8);
138		break;
139	case 0x0003:
140		timer->flags = GBATimerFlagsSetPrescaleBits(timer->flags, 10);
141		break;
142	}
143	timer->flags = GBATimerFlagsTestFillCountUp(timer->flags, timer > 0 && (control & 0x0004));
144	timer->flags = GBATimerFlagsTestFillDoIrq(timer->flags, control & 0x0040);
145	timer->overflowInterval = (0x10000 - timer->reload) << GBATimerFlagsGetPrescaleBits(timer->flags);
146	bool wasEnabled = GBATimerFlagsIsEnable(timer->flags);
147	timer->flags = GBATimerFlagsTestFillEnable(timer->flags, control & 0x0080);
148	if (!wasEnabled && GBATimerFlagsIsEnable(timer->flags)) {
149		mTimingDeschedule(timing, &timer->event);
150		if (!GBATimerFlagsIsCountUp(timer->flags)) {
151			mTimingSchedule(timing, &timer->event, timer->overflowInterval);
152		}
153		*io = timer->reload;
154		timer->oldReload = timer->reload;
155		timer->lastEvent = timing->masterCycles + cpu->cycles;
156	} else if (wasEnabled && !GBATimerFlagsIsEnable(timer->flags)) {
157		mTimingDeschedule(timing, &timer->event);
158		if (!GBATimerFlagsIsCountUp(timer->flags)) {
159			*io = timer->oldReload + ((cpu->cycles - timer->lastEvent) >> oldPrescale);
160		}
161	} else if (GBATimerFlagsIsEnable(timer->flags) && GBATimerFlagsGetPrescaleBits(timer->flags) != oldPrescale && !GBATimerFlagsIsCountUp(timer->flags)) {
162		mTimingDeschedule(timing, &timer->event);
163		mTimingSchedule(timing, &timer->event, timer->overflowInterval - timer->lastEvent);
164	}
165}