all repos — mgba @ 59922fe2e931ef876f10e4e4cbad92b5d76255fb

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