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, TIMER_RELOAD_DELAY + cyclesLate);
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 < 3) {
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 int prescaleBits = GBATimerFlagsGetPrescaleBits(currentTimer->flags);
147 int32_t currentTime = mTimingCurrentTime(&gba->timing) - cyclesLate;
148 int32_t tickMask = (1 << prescaleBits) - 1;
149 currentTime &= ~tickMask;
150 int32_t tickIncrement = currentTime - currentTimer->lastEvent;
151 currentTimer->lastEvent = currentTime;
152 tickIncrement >>= prescaleBits;
153 tickIncrement += gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1];
154 gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = tickIncrement;
155 if (!mTimingIsScheduled(&gba->timing, ¤tTimer->event)) {
156 tickIncrement = (0x10000 - tickIncrement) << prescaleBits;
157 currentTime -= mTimingCurrentTime(&gba->timing) - cyclesLate;
158 mTimingSchedule(&gba->timing, ¤tTimer->event, tickIncrement + currentTime);
159 }
160}
161
162void GBATimerWriteTMCNT_LO(struct GBA* gba, int timer, uint16_t reload) {
163 gba->timers[timer].reload = reload;
164}
165
166void GBATimerWriteTMCNT_HI(struct GBA* gba, int timer, uint16_t control) {
167 struct GBATimer* currentTimer = &gba->timers[timer];
168 GBATimerUpdateRegister(gba, timer, 0);
169
170 unsigned oldPrescale = GBATimerFlagsGetPrescaleBits(currentTimer->flags);
171 unsigned prescaleBits;
172 switch (control & 0x0003) {
173 case 0x0000:
174 prescaleBits = 0;
175 break;
176 case 0x0001:
177 prescaleBits = 6;
178 break;
179 case 0x0002:
180 prescaleBits = 8;
181 break;
182 case 0x0003:
183 prescaleBits = 10;
184 break;
185 }
186 currentTimer->flags = GBATimerFlagsSetPrescaleBits(currentTimer->flags, prescaleBits);
187 currentTimer->flags = GBATimerFlagsTestFillCountUp(currentTimer->flags, timer > 0 && (control & 0x0004));
188 currentTimer->flags = GBATimerFlagsTestFillDoIrq(currentTimer->flags, control & 0x0040);
189 bool wasEnabled = GBATimerFlagsIsEnable(currentTimer->flags);
190 currentTimer->flags = GBATimerFlagsTestFillEnable(currentTimer->flags, control & 0x0080);
191 if (!wasEnabled && GBATimerFlagsIsEnable(currentTimer->flags)) {
192 mTimingDeschedule(&gba->timing, ¤tTimer->event);
193 gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->reload;
194 int32_t tickMask = (1 << prescaleBits) - 1;
195 currentTimer->lastEvent = (mTimingCurrentTime(&gba->timing) - TIMER_STARTUP_DELAY) & ~tickMask;
196 GBATimerUpdateRegister(gba, timer, TIMER_STARTUP_DELAY);
197 } else if (wasEnabled && !GBATimerFlagsIsEnable(currentTimer->flags)) {
198 mTimingDeschedule(&gba->timing, ¤tTimer->event);
199 } else if (GBATimerFlagsIsEnable(currentTimer->flags) && GBATimerFlagsGetPrescaleBits(currentTimer->flags) != oldPrescale && !GBATimerFlagsIsCountUp(currentTimer->flags)) {
200 mTimingDeschedule(&gba->timing, ¤tTimer->event);
201 int32_t tickMask = (1 << prescaleBits) - 1;
202 currentTimer->lastEvent = (mTimingCurrentTime(&gba->timing) - TIMER_STARTUP_DELAY) & ~tickMask;
203 GBATimerUpdateRegister(gba, timer, TIMER_STARTUP_DELAY);
204 }
205}