all repos — mgba @ 2347bc4a5232f7e9adc5421103c2cec381d47205

mGBA Game Boy Advance Emulator

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}