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