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