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