all repos — mgba @ 605d5ddcfd1ba1cbf81c98fb4333099a0bbc58d5

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