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