all repos — mgba @ a43c465f9fe18b5959121b0fe6dd5cfc358e83c2

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->nextTima = INT_MAX;
 15	timer->nextEvent = GB_DMG_DIV_PERIOD;
 16	timer->eventDiff = 0;
 17	timer->timaPeriod = 1024;
 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->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				if (!timer->p->memory.io[REG_TIMA]) {
 35					timer->p->memory.io[REG_TIMA] = timer->p->memory.io[REG_TMA];
 36					timer->p->memory.io[REG_IF] |= (1 << GB_IRQ_TIMER);
 37					GBUpdateIRQs(timer->p);
 38					timer->nextTima = timer->timaPeriod - 4;
 39				} else {
 40					++timer->p->memory.io[REG_TIMA];
 41					if (!timer->p->memory.io[REG_TIMA]) {
 42						timer->nextTima = 4;
 43					} else {
 44						timer->nextTima = timer->timaPeriod;
 45					}
 46				}
 47			}
 48			if (timer->nextTima < timer->nextEvent) {
 49				timer->nextEvent = timer->nextTima;
 50			}
 51		}
 52
 53		timer->eventDiff = 0;
 54	}
 55	return timer->nextEvent;
 56}
 57
 58void GBTimerDivReset(struct GBTimer* timer) {
 59	timer->p->memory.io[REG_DIV] = 0;
 60	timer->nextDiv = timer->eventDiff + timer->p->cpu->cycles + GB_DMG_DIV_PERIOD;
 61	if (timer->nextDiv - timer->eventDiff < timer->nextEvent) {
 62		timer->nextEvent = timer->nextDiv - timer->eventDiff;
 63		if (timer->nextEvent < timer->p->cpu->nextEvent) {
 64			timer->p->cpu->nextEvent = timer->nextEvent;
 65		}
 66	}
 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;
 74			break;
 75		case 1:
 76			timer->timaPeriod = 16;
 77			break;
 78		case 2:
 79			timer->timaPeriod = 64;
 80			break;
 81		case 3:
 82			timer->timaPeriod = 256;
 83			break;
 84		}
 85		GBTimerUpdateTIMA(timer);
 86	} else {
 87		timer->nextTima = INT_MAX;
 88	}
 89	return tac;
 90}
 91
 92void GBTimerUpdateTIMA(struct GBTimer* timer) {
 93	timer->nextTima = timer->eventDiff + timer->p->cpu->cycles + timer->timaPeriod;
 94	if (timer->nextTima - timer->eventDiff < timer->nextEvent) {
 95		timer->nextEvent = timer->nextTima - timer->eventDiff;
 96		if (timer->nextEvent < timer->p->cpu->nextEvent) {
 97			timer->p->cpu->nextEvent = timer->nextEvent;
 98		}
 99	}
100}
101
102void GBTimerSerialize(const struct GBTimer* timer, struct GBSerializedState* state) {
103	STORE_32LE(timer->nextEvent, 0, &state->timer.nextEvent);
104	STORE_32LE(timer->eventDiff, 0, &state->timer.eventDiff);
105	STORE_32LE(timer->nextDiv, 0, &state->timer.nextDiv);
106	STORE_32LE(timer->nextTima, 0, &state->timer.nextTima);
107	STORE_32LE(timer->timaPeriod, 0, &state->timer.timaPeriod);
108}
109
110void GBTimerDeserialize(struct GBTimer* timer, const struct GBSerializedState* state) {
111	LOAD_32LE(timer->nextEvent, 0, &state->timer.nextEvent);
112	LOAD_32LE(timer->eventDiff, 0, &state->timer.eventDiff);
113	LOAD_32LE(timer->nextDiv, 0, &state->timer.nextDiv);
114	LOAD_32LE(timer->nextTima, 0, &state->timer.nextTima);
115	LOAD_32LE(timer->timaPeriod, 0, &state->timer.timaPeriod);
116}