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 while (timer->nextDiv <= 0) {
33 if ((timer->internalDiv & 15) == 15) {
34 ++timer->p->memory.io[REG_DIV];
35 }
36 timer->nextDiv += GB_DMG_DIV_PERIOD;
37
38 // Make sure to trigger when the correct bit is a falling edge
39 if (timer->timaPeriod > 0 && (timer->internalDiv & (timer->timaPeriod - 1)) == timer->timaPeriod - 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 ++timer->internalDiv;
47 }
48 if (timer->nextEvent <= 0) {
49 // Batch div increments
50 int divsToGo = 16 - (timer->internalDiv & 15);
51 int timaToGo = INT_MAX;
52 if (timer->timaPeriod) {
53 timaToGo = timer->timaPeriod - (timer->internalDiv & (timer->timaPeriod - 1));
54 }
55 if (timaToGo < divsToGo) {
56 divsToGo = timaToGo;
57 }
58 timer->nextEvent += GB_DMG_DIV_PERIOD * divsToGo;
59 }
60 timer->eventDiff = 0;
61 }
62 return timer->nextEvent;
63}
64
65void GBTimerDivReset(struct GBTimer* timer) {
66 timer->p->memory.io[REG_DIV] = 0;
67}
68
69uint8_t GBTimerUpdateTAC(struct GBTimer* timer, GBRegisterTAC tac) {
70 if (GBRegisterTACIsRun(tac)) {
71 switch (GBRegisterTACGetClock(tac)) {
72 case 0:
73 timer->timaPeriod = 1024 >> 4;
74 break;
75 case 1:
76 timer->timaPeriod = 16 >> 4;
77 break;
78 case 2:
79 timer->timaPeriod = 64 >> 4;
80 break;
81 case 3:
82 timer->timaPeriod = 256 >> 4;
83 break;
84 }
85 } else {
86 timer->timaPeriod = 0;
87 }
88 return tac;
89}
90void GBTimerSerialize(const struct GBTimer* timer, struct GBSerializedState* state) {
91 STORE_32LE(timer->nextEvent, 0, &state->timer.nextEvent);
92 STORE_32LE(timer->eventDiff, 0, &state->timer.eventDiff);
93 STORE_32LE(timer->nextDiv, 0, &state->timer.nextDiv);
94 STORE_32LE(timer->internalDiv, 0, &state->timer.internalDiv);
95 STORE_32LE(timer->timaPeriod, 0, &state->timer.timaPeriod);
96
97 GBSerializedTimerFlags flags = 0;
98 flags = GBSerializedTimerFlagsSetIrqPending(flags, state->timer.flags);
99 state->timer.flags = flags;
100}
101
102void GBTimerDeserialize(struct GBTimer* timer, const struct GBSerializedState* state) {
103 LOAD_32LE(timer->nextEvent, 0, &state->timer.nextEvent);
104 LOAD_32LE(timer->eventDiff, 0, &state->timer.eventDiff);
105 LOAD_32LE(timer->nextDiv, 0, &state->timer.nextDiv);
106 LOAD_32LE(timer->internalDiv, 0, &state->timer.internalDiv);
107 LOAD_32LE(timer->timaPeriod, 0, &state->timer.timaPeriod);
108
109 GBSerializedTimerFlags flags = state->timer.flags ;
110 timer->irqPending = GBSerializedTimerFlagsIsIrqPending(flags);
111}