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
11void GBTimerReset(struct GBTimer* timer) {
12 timer->nextDiv = GB_DMG_DIV_PERIOD; // TODO: GBC differences
13 timer->nextTima = INT_MAX;
14 timer->nextEvent = INT_MAX;
15}
16
17int32_t GBTimerProcessEvents(struct GBTimer* timer, int32_t cycles) {
18 if (timer->nextEvent == INT_MAX) {
19 return INT_MAX;
20 }
21 timer->eventDiff += cycles;
22 timer->nextEvent -= cycles;
23 if (timer->nextEvent < 0) {
24 timer->nextDiv -= timer->eventDiff;
25 if (timer->nextDiv <= 0) {
26 ++timer->p->memory.io[REG_DIV];
27 timer->nextDiv = GB_DMG_DIV_PERIOD;
28 }
29 timer->nextEvent = timer->nextDiv;
30
31 if (timer->nextTima != INT_MAX) {
32 timer->nextTima -= timer->eventDiff;
33 if (timer->nextTima <= 0) {
34 ++timer->p->memory.io[REG_TIMA];
35 if (!timer->p->memory.io[REG_TIMA]) {
36 timer->p->memory.io[REG_TIMA] = timer->p->memory.io[REG_TMA];
37 timer->p->memory.io[REG_IF] |= (1 << GB_IRQ_TIMER);
38 GBUpdateIRQs(timer->p);
39 }
40 timer->nextTima = timer->timaPeriod;
41 }
42 if (timer->nextTima < timer->nextEvent) {
43 timer->nextEvent = timer->nextTima;
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 // TODO: Do we need to reset the event?
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;
62 break;
63 case 1:
64 timer->timaPeriod = 16;
65 break;
66 case 2:
67 timer->timaPeriod = 64;
68 break;
69 case 3:
70 timer->timaPeriod = 256;
71 break;
72 }
73 timer->nextTima = timer->eventDiff + timer->timaPeriod;
74 if (timer->nextTima < timer->nextEvent) {
75 timer->nextEvent = timer->nextTima;
76 if (timer->nextEvent < timer->p->cpu->nextEvent) {
77 timer->p->cpu->nextEvent = timer->nextEvent;
78 }
79 }
80 } else {
81 timer->nextTima = INT_MAX;
82 }
83 return tac;
84}