all repos — mgba @ 0bf0975a5d040bcc00741b25ebe8f18110ba08a0

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 _GBTimerIRQ(struct mTiming* timing, void* context, uint32_t cyclesLate) {
 13	UNUSED(timing);
 14	UNUSED(cyclesLate);
 15	struct GBTimer* timer = context;
 16	timer->p->memory.io[REG_TIMA] = timer->p->memory.io[REG_TMA];
 17	timer->p->memory.io[REG_IF] |= (1 << GB_IRQ_TIMER);
 18	GBUpdateIRQs(timer->p);
 19}
 20
 21void _GBTimerIncrement(struct mTiming* timing, void* context, uint32_t cyclesLate) {
 22	struct GBTimer* timer = context;
 23	timer->nextDiv += cyclesLate;
 24	while (timer->nextDiv > 0) {
 25		timer->nextDiv -= GB_DMG_DIV_PERIOD;
 26
 27		// Make sure to trigger when the correct bit is a falling edge
 28		if (timer->timaPeriod > 0 && (timer->internalDiv & (timer->timaPeriod - 1)) == timer->timaPeriod - 1) {
 29			++timer->p->memory.io[REG_TIMA];
 30			if (!timer->p->memory.io[REG_TIMA]) {
 31				mTimingSchedule(timing, &timer->irq, 4 - cyclesLate);
 32			}
 33		}
 34		++timer->internalDiv;
 35		timer->p->memory.io[REG_DIV] = timer->internalDiv >> 4;
 36	}
 37	// Batch div increments
 38	int divsToGo = 16 - (timer->internalDiv & 15);
 39	int timaToGo = INT_MAX;
 40	if (timer->timaPeriod) {
 41		timaToGo = timer->timaPeriod - (timer->internalDiv & (timer->timaPeriod - 1));
 42	}
 43	if (timaToGo < divsToGo) {
 44		divsToGo = timaToGo;
 45	}
 46	timer->nextDiv = GB_DMG_DIV_PERIOD * divsToGo;
 47	mTimingSchedule(timing, &timer->event, timer->nextDiv - cyclesLate);
 48}
 49
 50void GBTimerReset(struct GBTimer* timer) {
 51	timer->event.context = timer;
 52	timer->event.name = "GB Timer";
 53	timer->event.callback = _GBTimerIncrement;
 54	timer->event.priority = 0x20;
 55	timer->irq.context = timer;
 56	timer->irq.name = "GB Timer IRQ";
 57	timer->irq.callback = _GBTimerIRQ;
 58	timer->event.priority = 0x21;
 59
 60	timer->nextDiv = GB_DMG_DIV_PERIOD; // TODO: GBC differences
 61	timer->timaPeriod = 1024 >> 4;
 62	timer->internalDiv = 0;
 63}
 64
 65void GBTimerDivReset(struct GBTimer* timer) {
 66	timer->p->memory.io[REG_DIV] = 0;
 67	timer->internalDiv = 0;
 68	timer->nextDiv = GB_DMG_DIV_PERIOD;
 69	mTimingDeschedule(&timer->p->timing, &timer->event);
 70	mTimingSchedule(&timer->p->timing, &timer->event, timer->nextDiv);
 71}
 72
 73uint8_t GBTimerUpdateTAC(struct GBTimer* timer, GBRegisterTAC tac) {
 74	if (GBRegisterTACIsRun(tac)) {
 75		switch (GBRegisterTACGetClock(tac)) {
 76		case 0:
 77			timer->timaPeriod = 1024 >> 4;
 78			break;
 79		case 1:
 80			timer->timaPeriod = 16 >> 4;
 81			break;
 82		case 2:
 83			timer->timaPeriod = 64 >> 4;
 84			break;
 85		case 3:
 86			timer->timaPeriod = 256 >> 4;
 87			break;
 88		}
 89	} else {
 90		timer->timaPeriod = 0;
 91	}
 92	return tac;
 93}
 94
 95void GBTimerSerialize(const struct GBTimer* timer, struct GBSerializedState* state) {
 96	STORE_32LE(timer->nextDiv, 0, &state->timer.nextDiv);
 97	STORE_32LE(timer->internalDiv, 0, &state->timer.internalDiv);
 98	STORE_32LE(timer->timaPeriod, 0, &state->timer.timaPeriod);
 99	STORE_32LE(timer->event.when - mTimingCurrentTime(&timer->p->timing), 0, &state->timer.nextEvent);
100	STORE_32LE(timer->irq.when - mTimingCurrentTime(&timer->p->timing), 0, &state->timer.nextIRQ);
101	GBSerializedTimerFlags flags = GBSerializedTimerFlagsSetIrqPending(0, mTimingIsScheduled(&timer->p->timing, &timer->irq));
102	STORE_32LE(flags, 0, &state->timer.flags);
103}
104
105void GBTimerDeserialize(struct GBTimer* timer, const struct GBSerializedState* state) {
106	LOAD_32LE(timer->nextDiv, 0, &state->timer.nextDiv);
107	LOAD_32LE(timer->internalDiv, 0, &state->timer.internalDiv);
108	LOAD_32LE(timer->timaPeriod, 0, &state->timer.timaPeriod);
109
110	uint32_t when;
111	LOAD_32LE(when, 0, &state->timer.nextEvent);
112	mTimingDeschedule(&timer->p->timing, &timer->event);
113	mTimingSchedule(&timer->p->timing, &timer->event, when);
114
115	GBSerializedTimerFlags flags;
116	LOAD_32LE(flags, 0, &state->timer.flags);
117
118	mTimingDeschedule(&timer->p->timing, &timer->irq);
119	if (GBSerializedTimerFlagsIsIrqPending(flags)) {
120		LOAD_32LE(when, 0, &state->timer.nextIRQ);
121		mTimingSchedule(&timer->p->timing, &timer->irq, when);
122	}
123}