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