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