src/gb/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/gb/timer.h>
7
8#include <mgba/internal/lr35902/lr35902.h>
9#include <mgba/internal/gb/gb.h>
10#include <mgba/internal/gb/io.h>
11#include <mgba/internal/gb/serialize.h>
12
13void _GBTimerIRQ(struct mTiming* timing, void* context, uint32_t cyclesLate) {
14 UNUSED(timing);
15 UNUSED(cyclesLate);
16 struct GBTimer* timer = context;
17 timer->p->memory.io[REG_TIMA] = timer->p->memory.io[REG_TMA];
18 timer->p->memory.io[REG_IF] |= (1 << GB_IRQ_TIMER);
19 GBUpdateIRQs(timer->p);
20}
21
22static void _GBTimerDivIncrement(struct GBTimer* timer, uint32_t cyclesLate) {
23 while (timer->nextDiv >= GB_DMG_DIV_PERIOD) {
24 timer->nextDiv -= GB_DMG_DIV_PERIOD;
25
26 // Make sure to trigger when the correct bit is a falling edge
27 if (timer->timaPeriod > 0 && (timer->internalDiv & (timer->timaPeriod - 1)) == timer->timaPeriod - 1) {
28 ++timer->p->memory.io[REG_TIMA];
29 if (!timer->p->memory.io[REG_TIMA]) {
30 mTimingSchedule(&timer->p->timing, &timer->irq, 7 - ((timer->p->cpu->executionState - cyclesLate) & 3));
31 }
32 }
33 int timingFactor = 0x3FF >> !timer->p->doubleSpeed;
34 if ((timer->internalDiv & timingFactor) == timingFactor) {
35 GBAudioUpdateFrame(&timer->p->audio, &timer->p->timing);
36 }
37 ++timer->internalDiv;
38 timer->p->memory.io[REG_DIV] = timer->internalDiv >> 4;
39 }
40}
41
42void _GBTimerUpdate(struct mTiming* timing, void* context, uint32_t cyclesLate) {
43 struct GBTimer* timer = context;
44 timer->nextDiv += cyclesLate;
45 _GBTimerDivIncrement(timer, cyclesLate);
46 // Batch div increments
47 int divsToGo = 16 - (timer->internalDiv & 15);
48 int timaToGo = INT_MAX;
49 if (timer->timaPeriod) {
50 timaToGo = timer->timaPeriod - (timer->internalDiv & (timer->timaPeriod - 1));
51 }
52 if (timaToGo < divsToGo) {
53 divsToGo = timaToGo;
54 }
55 timer->nextDiv = GB_DMG_DIV_PERIOD * divsToGo;
56 mTimingSchedule(timing, &timer->event, timer->nextDiv - cyclesLate);
57}
58
59void GBTimerReset(struct GBTimer* timer) {
60 timer->event.context = timer;
61 timer->event.name = "GB Timer";
62 timer->event.callback = _GBTimerUpdate;
63 timer->event.priority = 0x20;
64 timer->irq.context = timer;
65 timer->irq.name = "GB Timer IRQ";
66 timer->irq.callback = _GBTimerIRQ;
67 timer->event.priority = 0x21;
68
69 timer->nextDiv = GB_DMG_DIV_PERIOD; // TODO: GBC differences
70 timer->timaPeriod = 1024 >> 4;
71}
72
73void GBTimerDivReset(struct GBTimer* timer) {
74 timer->nextDiv -= mTimingUntil(&timer->p->timing, &timer->event);
75 mTimingDeschedule(&timer->p->timing, &timer->event);
76 _GBTimerDivIncrement(timer, 0);
77 if (((timer->internalDiv << 1) | ((timer->nextDiv >> 3) & 1)) & timer->timaPeriod) {
78 ++timer->p->memory.io[REG_TIMA];
79 if (!timer->p->memory.io[REG_TIMA]) {
80 mTimingSchedule(&timer->p->timing, &timer->irq, 7 - (timer->p->cpu->executionState & 3));
81 }
82 }
83 int timingFactor = 0x200 >> !timer->p->doubleSpeed;
84 if (timer->internalDiv & 0x200) {
85 GBAudioUpdateFrame(&timer->p->audio, &timer->p->timing);
86 }
87 timer->p->memory.io[REG_DIV] = 0;
88 timer->internalDiv = 0;
89 timer->nextDiv = GB_DMG_DIV_PERIOD;
90 mTimingSchedule(&timer->p->timing, &timer->event, timer->nextDiv - ((timer->p->cpu->executionState + 1) & 3));
91}
92
93uint8_t GBTimerUpdateTAC(struct GBTimer* timer, GBRegisterTAC tac) {
94 if (GBRegisterTACIsRun(tac)) {
95 switch (GBRegisterTACGetClock(tac)) {
96 case 0:
97 timer->timaPeriod = 1024 >> 4;
98 break;
99 case 1:
100 timer->timaPeriod = 16 >> 4;
101 break;
102 case 2:
103 timer->timaPeriod = 64 >> 4;
104 break;
105 case 3:
106 timer->timaPeriod = 256 >> 4;
107 break;
108 }
109
110 timer->nextDiv -= mTimingUntil(&timer->p->timing, &timer->event);
111 mTimingDeschedule(&timer->p->timing, &timer->event);
112 _GBTimerDivIncrement(timer, (timer->p->cpu->executionState + 2) & 3);
113 timer->nextDiv += GB_DMG_DIV_PERIOD;
114 mTimingSchedule(&timer->p->timing, &timer->event, timer->nextDiv);
115 } else {
116 timer->timaPeriod = 0;
117 }
118 return tac;
119}
120
121void GBTimerSerialize(const struct GBTimer* timer, struct GBSerializedState* state) {
122 STORE_32LE(timer->nextDiv, 0, &state->timer.nextDiv);
123 STORE_32LE(timer->internalDiv, 0, &state->timer.internalDiv);
124 STORE_32LE(timer->timaPeriod, 0, &state->timer.timaPeriod);
125 STORE_32LE(timer->event.when - mTimingCurrentTime(&timer->p->timing), 0, &state->timer.nextEvent);
126 STORE_32LE(timer->irq.when - mTimingCurrentTime(&timer->p->timing), 0, &state->timer.nextIRQ);
127 GBSerializedTimerFlags flags = GBSerializedTimerFlagsSetIrqPending(0, mTimingIsScheduled(&timer->p->timing, &timer->irq));
128 state->timer.flags = flags;
129}
130
131void GBTimerDeserialize(struct GBTimer* timer, const struct GBSerializedState* state) {
132 LOAD_32LE(timer->nextDiv, 0, &state->timer.nextDiv);
133 LOAD_32LE(timer->internalDiv, 0, &state->timer.internalDiv);
134 LOAD_32LE(timer->timaPeriod, 0, &state->timer.timaPeriod);
135
136 uint32_t when;
137 LOAD_32LE(when, 0, &state->timer.nextEvent);
138 mTimingSchedule(&timer->p->timing, &timer->event, when);
139
140 GBSerializedTimerFlags flags = state->timer.flags;
141
142 if (GBSerializedTimerFlagsIsIrqPending(flags)) {
143 LOAD_32LE(when, 0, &state->timer.nextIRQ);
144 mTimingSchedule(&timer->p->timing, &timer->irq, when);
145 }
146}