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