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 "timer.h"
7
8#include "gb/gb.h"
9#include "gb/io.h"
10#include "gb/serialize.h"
11
12void GBTimerReset(struct GBTimer* timer) {
13 timer->nextDiv = GB_DMG_DIV_PERIOD; // TODO: GBC differences
14 timer->nextEvent = GB_DMG_DIV_PERIOD;
15 timer->eventDiff = 0;
16 timer->timaPeriod = 1024 >> 4;
17 timer->internalDiv = 0;
18}
19
20int32_t GBTimerProcessEvents(struct GBTimer* timer, int32_t cycles) {
21 timer->eventDiff += cycles;
22 timer->nextEvent -= cycles;
23 if (timer->nextEvent <= 0) {
24 timer->nextDiv -= timer->eventDiff;
25 if (timer->irqPending) {
26 timer->p->memory.io[REG_TIMA] = timer->p->memory.io[REG_TMA];
27 timer->p->memory.io[REG_IF] |= (1 << GB_IRQ_TIMER);
28 GBUpdateIRQs(timer->p);
29 timer->irqPending = false;
30 timer->nextEvent = timer->nextDiv;
31 }
32 if (timer->nextDiv <= 0) {
33 ++timer->internalDiv;
34 timer->p->memory.io[REG_DIV] = timer->internalDiv >> 4;
35 timer->nextDiv = GB_DMG_DIV_PERIOD;
36 timer->nextEvent = timer->nextDiv;
37
38 // Make sure to trigger when the correct bit is a falling edge
39 if (timer->timaPeriod == 1 || (timer->internalDiv & (timer->timaPeriod - 1)) == (timer->timaPeriod >> 1) - 1) {
40 ++timer->p->memory.io[REG_TIMA];
41 if (!timer->p->memory.io[REG_TIMA]) {
42 timer->irqPending = true;
43 timer->nextEvent = 4;
44 }
45 }
46 }
47 timer->eventDiff = 0;
48 }
49 return timer->nextEvent;
50}
51
52void GBTimerDivReset(struct GBTimer* timer) {
53 timer->p->memory.io[REG_DIV] = 0;
54 timer->internalDiv = 0;
55}
56
57uint8_t GBTimerUpdateTAC(struct GBTimer* timer, GBRegisterTAC tac) {
58 if (GBRegisterTACIsRun(tac)) {
59 switch (GBRegisterTACGetClock(tac)) {
60 case 0:
61 timer->timaPeriod = 1024 >> 4;
62 break;
63 case 1:
64 timer->timaPeriod = 16 >> 4;
65 break;
66 case 2:
67 timer->timaPeriod = 64 >> 4;
68 break;
69 case 3:
70 timer->timaPeriod = 256 >> 4;
71 break;
72 }
73 } else {
74 timer->timaPeriod = 0;
75 }
76 return tac;
77}
78void GBTimerSerialize(const struct GBTimer* timer, struct GBSerializedState* state) {
79 STORE_32LE(timer->nextEvent, 0, &state->timer.nextEvent);
80 STORE_32LE(timer->eventDiff, 0, &state->timer.eventDiff);
81 STORE_32LE(timer->nextDiv, 0, &state->timer.nextDiv);
82 STORE_32LE(timer->internalDiv, 0, &state->timer.internalDiv);
83 STORE_32LE(timer->timaPeriod, 0, &state->timer.timaPeriod);
84
85 GBSerializedTimerFlags flags = 0;
86 flags = GBSerializedTimerFlagsSetIrqPending(flags, state->timer.flags);
87 state->timer.flags = flags;
88}
89
90void GBTimerDeserialize(struct GBTimer* timer, const struct GBSerializedState* state) {
91 LOAD_32LE(timer->nextEvent, 0, &state->timer.nextEvent);
92 LOAD_32LE(timer->eventDiff, 0, &state->timer.eventDiff);
93 LOAD_32LE(timer->nextDiv, 0, &state->timer.nextDiv);
94 LOAD_32LE(timer->internalDiv, 0, &state->timer.internalDiv);
95 LOAD_32LE(timer->timaPeriod, 0, &state->timer.timaPeriod);
96
97 GBSerializedTimerFlags flags = state->timer.flags ;
98 timer->irqPending = GBSerializedTimerFlagsIsIrqPending(flags);
99}