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