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
49void GBATimerUpdate(struct mTiming* timing, struct GBATimer* timer, uint16_t* io, uint32_t cyclesLate) {
50 if (GBATimerFlagsIsCountUp(timer->flags)) {
51 *io = timer->reload;
52 } else {
53 GBATimerUpdateRegisterInternal(timer, timing, io, TIMER_RELOAD_DELAY + cyclesLate);
54 }
55
56 if (GBATimerFlagsIsDoIrq(timer->flags)) {
57 timer->flags = GBATimerFlagsFillIrqPending(timer->flags);
58 if (!mTimingIsScheduled(timing, &timer->irq)) {
59 mTimingSchedule(timing, &timer->irq, TIMER_IRQ_DELAY - cyclesLate);
60 }
61 }
62}
63
64static void GBATimerUpdateAudio(struct GBA* gba, int timerId, uint32_t cyclesLate) {
65 if (!gba->audio.enable) {
66 return;
67 }
68 if ((gba->audio.chALeft || gba->audio.chARight) && gba->audio.chATimer == timerId) {
69 GBAAudioSampleFIFO(&gba->audio, 0, cyclesLate);
70 }
71
72 if ((gba->audio.chBLeft || gba->audio.chBRight) && gba->audio.chBTimer == timerId) {
73 GBAAudioSampleFIFO(&gba->audio, 1, cyclesLate);
74 }
75}
76
77void GBATimerUpdateCountUp(struct mTiming* timing, struct GBATimer* nextTimer, uint16_t* io, uint32_t cyclesLate) {
78 if (GBATimerFlagsIsCountUp(nextTimer->flags)) { // TODO: Does this increment while disabled?
79 ++*io;
80 if (!*io && GBATimerFlagsIsEnable(nextTimer->flags)) {
81 GBATimerUpdate(timing, nextTimer, io, cyclesLate);
82 }
83 }
84}
85
86static void GBATimerUpdate0(struct mTiming* timing, void* context, uint32_t cyclesLate) {
87 struct GBA* gba = context;
88 GBATimerUpdateAudio(gba, 0, cyclesLate);
89 GBATimerUpdate(timing, &gba->timers[0], &gba->memory.io[REG_TM0CNT_LO >> 1], cyclesLate);
90 GBATimerUpdateCountUp(timing, &gba->timers[1], &gba->memory.io[REG_TM1CNT_LO >> 1], cyclesLate);
91}
92
93static void GBATimerUpdate1(struct mTiming* timing, void* context, uint32_t cyclesLate) {
94 struct GBA* gba = context;
95 GBATimerUpdateAudio(gba, 1, cyclesLate);
96 GBATimerUpdate(timing, &gba->timers[1], &gba->memory.io[REG_TM1CNT_LO >> 1], cyclesLate);
97 GBATimerUpdateCountUp(timing, &gba->timers[2], &gba->memory.io[REG_TM2CNT_LO >> 1], cyclesLate);
98}
99
100static void GBATimerUpdate2(struct mTiming* timing, void* context, uint32_t cyclesLate) {
101 struct GBA* gba = context;
102 GBATimerUpdate(timing, &gba->timers[2], &gba->memory.io[REG_TM2CNT_LO >> 1], cyclesLate);
103 GBATimerUpdateCountUp(timing, &gba->timers[3], &gba->memory.io[REG_TM3CNT_LO >> 1], cyclesLate);
104}
105
106static void GBATimerUpdate3(struct mTiming* timing, void* context, uint32_t cyclesLate) {
107 struct GBA* gba = context;
108 GBATimerUpdate(timing, &gba->timers[3], &gba->memory.io[REG_TM3CNT_LO >> 1], cyclesLate);
109}
110
111void GBATimerInit(struct GBA* gba) {
112 memset(gba->timers, 0, sizeof(gba->timers));
113 gba->timers[0].event.name = "GBA Timer 0";
114 gba->timers[0].event.callback = GBATimerUpdate0;
115 gba->timers[0].event.context = gba;
116 gba->timers[0].event.priority = 0x20;
117 gba->timers[1].event.name = "GBA Timer 1";
118 gba->timers[1].event.callback = GBATimerUpdate1;
119 gba->timers[1].event.context = gba;
120 gba->timers[1].event.priority = 0x21;
121 gba->timers[2].event.name = "GBA Timer 2";
122 gba->timers[2].event.callback = GBATimerUpdate2;
123 gba->timers[2].event.context = gba;
124 gba->timers[2].event.priority = 0x22;
125 gba->timers[3].event.name = "GBA Timer 3";
126 gba->timers[3].event.callback = GBATimerUpdate3;
127 gba->timers[3].event.context = gba;
128 gba->timers[3].event.priority = 0x23;
129 gba->timers[0].irq.name = "GBA Timer 0 IRQ";
130 gba->timers[0].irq.callback = GBATimerIrq0;
131 gba->timers[0].irq.context = gba;
132 gba->timers[0].irq.priority = 0x28;
133 gba->timers[1].irq.name = "GBA Timer 1 IRQ";
134 gba->timers[1].irq.callback = GBATimerIrq1;
135 gba->timers[1].irq.context = gba;
136 gba->timers[1].irq.priority = 0x29;
137 gba->timers[2].irq.name = "GBA Timer 2 IRQ";
138 gba->timers[2].irq.callback = GBATimerIrq2;
139 gba->timers[2].irq.context = gba;
140 gba->timers[2].irq.priority = 0x2A;
141 gba->timers[3].irq.name = "GBA Timer 3 IRQ";
142 gba->timers[3].irq.callback = GBATimerIrq3;
143 gba->timers[3].irq.context = gba;
144 gba->timers[3].irq.priority = 0x2B;
145}
146
147void GBATimerUpdateRegister(struct GBA* gba, int timer, int32_t cyclesLate) {
148 struct GBATimer* currentTimer = &gba->timers[timer];
149 if (GBATimerFlagsIsEnable(currentTimer->flags) && !GBATimerFlagsIsCountUp(currentTimer->flags)) {
150 int32_t prefetchSkew = cyclesLate;
151 if (gba->memory.lastPrefetchedPc > (uint32_t) gba->cpu->gprs[ARM_PC]) {
152 prefetchSkew += ((gba->memory.lastPrefetchedPc - gba->cpu->gprs[ARM_PC]) * gba->cpu->memory.activeSeqCycles16) / WORD_SIZE_THUMB;
153 }
154 GBATimerUpdateRegisterInternal(currentTimer, &gba->timing, &gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1], prefetchSkew);
155 }
156}
157
158void GBATimerUpdateRegisterInternal(struct GBATimer* timer, struct mTiming* timing, uint16_t* io, int32_t skew) {
159 if (!GBATimerFlagsIsEnable(timer->flags) || GBATimerFlagsIsCountUp(timer->flags)) {
160 return;
161 }
162
163 // Align timer
164 int prescaleBits = GBATimerFlagsGetPrescaleBits(timer->flags);
165 int32_t currentTime = mTimingCurrentTime(timing) - skew;
166 int32_t tickMask = (1 << prescaleBits) - 1;
167 currentTime &= ~tickMask;
168
169 // Update register
170 int32_t tickIncrement = currentTime - timer->lastEvent;
171 timer->lastEvent = currentTime;
172 tickIncrement >>= prescaleBits;
173 tickIncrement += *io;
174 *io = tickIncrement;
175 while (tickIncrement >= 0x10000) {
176 tickIncrement -= 0x10000 - timer->reload;
177 }
178 *io = tickIncrement;
179
180 // Schedule next update
181 tickIncrement = (0x10000 - tickIncrement) << prescaleBits;
182 currentTime += tickIncrement;
183 currentTime &= ~tickMask;
184 currentTime -= mTimingCurrentTime(timing);
185 mTimingDeschedule(timing, &timer->event);
186 mTimingSchedule(timing, &timer->event, currentTime);
187}
188
189void GBATimerWriteTMCNT_LO(struct GBATimer* timer, uint16_t reload) {
190 timer->reload = reload;
191}
192
193void GBATimerWriteTMCNT_HI(struct GBATimer* timer, struct mTiming* timing, uint16_t* io, uint16_t control) {
194 GBATimerUpdateRegisterInternal(timer, timing, io, 0);
195
196 unsigned oldPrescale = GBATimerFlagsGetPrescaleBits(timer->flags);
197 unsigned prescaleBits;
198 switch (control & 0x0003) {
199 case 0x0000:
200 prescaleBits = 0;
201 break;
202 case 0x0001:
203 prescaleBits = 6;
204 break;
205 case 0x0002:
206 prescaleBits = 8;
207 break;
208 case 0x0003:
209 prescaleBits = 10;
210 break;
211 }
212 prescaleBits += timer->forcedPrescale;
213 timer->flags = GBATimerFlagsSetPrescaleBits(timer->flags, prescaleBits);
214 timer->flags = GBATimerFlagsTestFillCountUp(timer->flags, timer > 0 && (control & 0x0004));
215 timer->flags = GBATimerFlagsTestFillDoIrq(timer->flags, control & 0x0040);
216 bool wasEnabled = GBATimerFlagsIsEnable(timer->flags);
217 timer->flags = GBATimerFlagsTestFillEnable(timer->flags, control & 0x0080);
218 if (!wasEnabled && GBATimerFlagsIsEnable(timer->flags)) {
219 mTimingDeschedule(timing, &timer->event);
220 *io = timer->reload;
221 int32_t tickMask = (1 << prescaleBits) - 1;
222 timer->lastEvent = (mTimingCurrentTime(timing) - TIMER_STARTUP_DELAY) & ~tickMask;
223 GBATimerUpdateRegisterInternal(timer, timing, io, TIMER_STARTUP_DELAY);
224 } else if (wasEnabled && !GBATimerFlagsIsEnable(timer->flags)) {
225 mTimingDeschedule(timing, &timer->event);
226 } else if (GBATimerFlagsIsEnable(timer->flags) && GBATimerFlagsGetPrescaleBits(timer->flags) != oldPrescale && !GBATimerFlagsIsCountUp(timer->flags)) {
227 mTimingDeschedule(timing, &timer->event);
228 int32_t tickMask = (1 << prescaleBits) - 1;
229 timer->lastEvent = (mTimingCurrentTime(timing) - TIMER_STARTUP_DELAY) & ~tickMask;
230 GBATimerUpdateRegisterInternal(timer, timing, io, TIMER_STARTUP_DELAY);
231 }
232}