src/ds/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/ds/timer.h>
7
8#include <mgba/internal/arm/arm.h>
9#include <mgba/internal/ds/ds.h>
10
11void DSTimerUpdateRegister(struct DSTimer* timer, struct ARMCore* cpu, uint16_t* io) {
12 if (DSTimerFlagsIsEnable(timer->flags) && !DSTimerFlagsIsCountUp(timer->flags)) {
13 // Reading this takes two cycles (1N+1I), so let's remove them preemptively
14 *io = timer->oldReload + ((cpu->cycles - timer->lastEvent - 2) >> DSTimerFlagsGetPrescaleBits(timer->flags));
15 }
16}
17
18void DSTimerWriteTMCNT_LO(struct DSTimer* timer, uint16_t reload) {
19 timer->reload = reload;
20 timer->overflowInterval = (0x10000 - timer->reload) << DSTimerFlagsGetPrescaleBits(timer->flags);
21}
22
23void DSTimerWriteTMCNT_HI(struct DSTimer* timer, struct ARMCore* cpu, uint16_t* io, uint16_t control) {
24 DSTimerUpdateRegister(timer, cpu, io);
25
26 unsigned oldPrescale = DSTimerFlagsGetPrescaleBits(timer->flags);
27 switch (control & 0x0003) {
28 case 0x0000:
29 timer->flags = DSTimerFlagsSetPrescaleBits(timer->flags, 0);
30 break;
31 case 0x0001:
32 timer->flags = DSTimerFlagsSetPrescaleBits(timer->flags, 6);
33 break;
34 case 0x0002:
35 timer->flags = DSTimerFlagsSetPrescaleBits(timer->flags, 8);
36 break;
37 case 0x0003:
38 timer->flags = DSTimerFlagsSetPrescaleBits(timer->flags, 10);
39 break;
40 }
41 timer->flags = DSTimerFlagsTestFillCountUp(timer->flags, control & 0x0004);
42 timer->flags = DSTimerFlagsTestFillDoIrq(timer->flags, control & 0x0040);
43 timer->overflowInterval = (0x10000 - timer->reload) << DSTimerFlagsGetPrescaleBits(timer->flags);
44 bool wasEnabled = DSTimerFlagsIsEnable(timer->flags);
45 timer->flags = DSTimerFlagsTestFillEnable(timer->flags, control & 0x0080);
46 if (!wasEnabled && DSTimerFlagsIsEnable(timer->flags)) {
47 if (!DSTimerFlagsIsCountUp(timer->flags)) {
48 timer->nextEvent = cpu->cycles + timer->overflowInterval;
49 } else {
50 timer->nextEvent = INT_MAX;
51 }
52 *io = timer->reload;
53 timer->oldReload = timer->reload;
54 timer->lastEvent = cpu->cycles;
55 } else if (wasEnabled && !DSTimerFlagsIsEnable(timer->flags)) {
56 if (!DSTimerFlagsIsCountUp(timer->flags)) {
57 *io = timer->oldReload + ((cpu->cycles - timer->lastEvent) >> oldPrescale);
58 }
59 } else if (DSTimerFlagsGetPrescaleBits(timer->flags) != oldPrescale && !DSTimerFlagsIsCountUp(timer->flags)) {
60 // FIXME: this might be before present
61 timer->nextEvent = timer->lastEvent + timer->overflowInterval;
62 }
63
64 if (timer->nextEvent < cpu->nextEvent) {
65 cpu->nextEvent = timer->nextEvent;
66 }
67}
68
69int32_t DSTimersProcessEvents(struct DS* ds, int32_t cycles) {
70 int32_t nextEvent = INT_MAX;
71 if (!ds->timersEnabled7) {
72 return nextEvent;
73 }
74
75 struct DSTimer* timer;
76 struct DSTimer* nextTimer;
77
78 int t;
79 for (t = 0; t < 4; ++t) {
80 timer = &ds->timers7[t];
81 if (DSTimerFlagsIsEnable(timer->flags)) {
82 timer->nextEvent -= cycles;
83 timer->lastEvent -= cycles;
84 while (timer->nextEvent <= 0) {
85 timer->lastEvent = timer->nextEvent;
86 timer->nextEvent += timer->overflowInterval;
87 ds->memory.io7[(DS7_REG_TM0CNT_LO + (t << 2)) >> 1] = timer->reload;
88 timer->oldReload = timer->reload;
89
90 if (DSTimerFlagsIsDoIrq(timer->flags)) {
91 DSRaiseIRQ(ds->arm7, ds->memory.io7, DS_IRQ_TIMER0);
92 }
93
94 if (t == 3) {
95 break;
96 }
97
98 nextTimer = &ds->timers7[t + 1];
99 if (DSTimerFlagsIsCountUp(nextTimer->flags)) {
100 ++ds->memory.io7[(DS7_REG_TM1CNT_LO + (t << 2)) >> 1];
101 if (!ds->memory.io7[(DS7_REG_TM1CNT_LO + (t << 2)) >> 1]) {
102 nextTimer->nextEvent = 0;
103 }
104 }
105 }
106 nextEvent = timer->nextEvent;
107 }
108 }
109 return nextEvent;
110}