all repos — mgba @ 3a03d180d2f7c7081f42d2ada5582c17ad2032cd

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