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
11static void GBATimerUpdateAudio(struct GBA* gba, int timerId, uint32_t cyclesLate) {
12 if (!gba->audio.enable) {
13 return;
14 }
15 if ((gba->audio.chALeft || gba->audio.chARight) && gba->audio.chATimer == timerId) {
16 GBAAudioSampleFIFO(&gba->audio, 0, cyclesLate);
17 }
18
19 if ((gba->audio.chBLeft || gba->audio.chBRight) && gba->audio.chBTimer == timerId) {
20 GBAAudioSampleFIFO(&gba->audio, 1, cyclesLate);
21 }
22}
23
24void GBATimerUpdateCountUp(struct mTiming* timing, struct GBATimer* nextTimer, uint16_t* io, uint32_t cyclesLate) {
25 if (GBATimerFlagsIsCountUp(nextTimer->flags)) { // TODO: Does this increment while disabled?
26 ++*io;
27 if (!*io && GBATimerFlagsIsEnable(nextTimer->flags)) {
28 mTimingSchedule(timing, &nextTimer->event, -cyclesLate);
29 }
30 }
31}
32
33void GBATimerUpdate(struct mTiming* timing, struct GBATimer* timer, uint16_t* io, uint32_t cyclesLate) {
34 *io = timer->reload;
35 timer->oldReload = timer->reload;
36 timer->lastEvent = timing->masterCycles - cyclesLate;
37
38 if (!GBATimerFlagsIsCountUp(timer->flags)) {
39 uint32_t nextEvent = timer->overflowInterval - cyclesLate;
40 mTimingSchedule(timing, &timer->event, nextEvent);
41 }
42}
43
44static void GBATimerUpdate0(struct mTiming* timing, void* context, uint32_t cyclesLate) {
45 struct GBA* gba = context;
46 struct GBATimer* timer = &gba->timers[0];
47 if (GBATimerFlagsIsDoIrq(timer->flags)) {
48 GBARaiseIRQ(gba, IRQ_TIMER0);
49 }
50 GBATimerUpdateAudio(gba, 0, cyclesLate);
51 GBATimerUpdate(timing, &gba->timers[0], &gba->memory.io[REG_TM0CNT_LO >> 1], cyclesLate);
52 GBATimerUpdateCountUp(timing, &gba->timers[1], &gba->memory.io[REG_TM1CNT_LO >> 1], cyclesLate);
53}
54
55static void GBATimerUpdate1(struct mTiming* timing, void* context, uint32_t cyclesLate) {
56 struct GBA* gba = context;
57 struct GBATimer* timer = &gba->timers[1];
58 if (GBATimerFlagsIsDoIrq(timer->flags)) {
59 GBARaiseIRQ(gba, IRQ_TIMER1);
60 }
61 GBATimerUpdateAudio(gba, 1, cyclesLate);
62 GBATimerUpdate(timing, &gba->timers[1], &gba->memory.io[REG_TM1CNT_LO >> 1], cyclesLate);
63 GBATimerUpdateCountUp(timing, &gba->timers[2], &gba->memory.io[REG_TM2CNT_LO >> 1], cyclesLate);
64}
65
66static void GBATimerUpdate2(struct mTiming* timing, void* context, uint32_t cyclesLate) {
67 struct GBA* gba = context;
68 struct GBATimer* timer = &gba->timers[2];
69 if (GBATimerFlagsIsDoIrq(timer->flags)) {
70 GBARaiseIRQ(gba, IRQ_TIMER2);
71 }
72 GBATimerUpdate(timing, &gba->timers[2], &gba->memory.io[REG_TM2CNT_LO >> 1], cyclesLate);
73 GBATimerUpdateCountUp(timing, &gba->timers[3], &gba->memory.io[REG_TM3CNT_LO >> 1], cyclesLate);
74}
75
76static void GBATimerUpdate3(struct mTiming* timing, void* context, uint32_t cyclesLate) {
77 struct GBA* gba = context;
78 struct GBATimer* timer = &gba->timers[3];
79 if (GBATimerFlagsIsDoIrq(timer->flags)) {
80 GBARaiseIRQ(gba, IRQ_TIMER3);
81 }
82 GBATimerUpdate(timing, &gba->timers[3], &gba->memory.io[REG_TM3CNT_LO >> 1], 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) {
106 struct GBATimer* currentTimer = &gba->timers[timer];
107 if (GBATimerFlagsIsEnable(currentTimer->flags) && !GBATimerFlagsIsCountUp(currentTimer->flags)) {
108 int32_t prefetchSkew = 0;
109 if (gba->memory.lastPrefetchedPc >= (uint32_t) gba->cpu->gprs[ARM_PC]) {
110 prefetchSkew = (gba->memory.lastPrefetchedPc - gba->cpu->gprs[ARM_PC]) * (gba->cpu->memory.activeSeqCycles16 + 1) / WORD_SIZE_THUMB;
111 }
112 GBATimerUpdateRegisterInternal(currentTimer, &gba->timing, gba->cpu, &gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1], prefetchSkew);
113 }
114}
115
116void GBATimerUpdateRegisterInternal(struct GBATimer* timer, struct mTiming* timing, struct ARMCore* cpu, uint16_t* io, int32_t skew) {
117 // Reading this takes two cycles (1N+1I), so let's remove them preemptively
118 int32_t diff = cpu->cycles - (timer->lastEvent - timing->masterCycles);
119 *io = timer->oldReload + ((diff - 2 + skew) >> GBATimerFlagsGetPrescaleBits(timer->flags));
120}
121
122void GBATimerWriteTMCNT_LO(struct GBATimer* timer, uint16_t reload) {
123 timer->reload = reload;
124 timer->overflowInterval = (0x10000 - timer->reload) << GBATimerFlagsGetPrescaleBits(timer->flags);
125}
126
127void GBATimerWriteTMCNT_HI(struct GBATimer* timer, struct mTiming* timing, struct ARMCore* cpu, uint16_t* io, uint16_t control) {
128 unsigned oldPrescale = GBATimerFlagsGetPrescaleBits(timer->flags);
129 switch (control & 0x0003) {
130 case 0x0000:
131 timer->flags = GBATimerFlagsSetPrescaleBits(timer->flags, 0);
132 break;
133 case 0x0001:
134 timer->flags = GBATimerFlagsSetPrescaleBits(timer->flags, 6);
135 break;
136 case 0x0002:
137 timer->flags = GBATimerFlagsSetPrescaleBits(timer->flags, 8);
138 break;
139 case 0x0003:
140 timer->flags = GBATimerFlagsSetPrescaleBits(timer->flags, 10);
141 break;
142 }
143 timer->flags = GBATimerFlagsTestFillCountUp(timer->flags, timer > 0 && (control & 0x0004));
144 timer->flags = GBATimerFlagsTestFillDoIrq(timer->flags, control & 0x0040);
145 timer->overflowInterval = (0x10000 - timer->reload) << GBATimerFlagsGetPrescaleBits(timer->flags);
146 bool wasEnabled = GBATimerFlagsIsEnable(timer->flags);
147 timer->flags = GBATimerFlagsTestFillEnable(timer->flags, control & 0x0080);
148 if (!wasEnabled && GBATimerFlagsIsEnable(timer->flags)) {
149 mTimingDeschedule(timing, &timer->event);
150 if (!GBATimerFlagsIsCountUp(timer->flags)) {
151 mTimingSchedule(timing, &timer->event, timer->overflowInterval);
152 }
153 *io = timer->reload;
154 timer->oldReload = timer->reload;
155 timer->lastEvent = timing->masterCycles + cpu->cycles;
156 } else if (wasEnabled && !GBATimerFlagsIsEnable(timer->flags)) {
157 mTimingDeschedule(timing, &timer->event);
158 } else if (GBATimerFlagsIsEnable(timer->flags) && GBATimerFlagsGetPrescaleBits(timer->flags) != oldPrescale && !GBATimerFlagsIsCountUp(timer->flags)) {
159 mTimingDeschedule(timing, &timer->event);
160 mTimingSchedule(timing, &timer->event, timer->overflowInterval - timer->lastEvent);
161 }
162}