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