all repos — mgba @ b4fa4fe77e23ed774e749608144fc0d1330b941c

mGBA Game Boy Advance Emulator

src/ds/dma.c (view raw)

  1/* Copyright (c) 2013-2015 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/ds/dma.h>
  7
  8#include <mgba/internal/ds/ds.h>
  9#include <mgba/internal/ds/io.h>
 10#include <mgba/internal/ds/memory.h>
 11
 12static void _dmaEvent(struct mTiming* timing, void* context, uint32_t cyclesLate);
 13
 14static void DSDMAService(struct DSCommon* dscore, int number, struct GBADMA* info);
 15
 16static const int DMA_OFFSET[] = { 1, -1, 0, 1 };
 17
 18void DSDMAInit(struct DS* ds) {
 19	ds->ds7.memory.dmaEvent.name = "DS7 DMA";
 20	ds->ds7.memory.dmaEvent.callback = _dmaEvent;
 21	ds->ds7.memory.dmaEvent.context = &ds->ds7;
 22	ds->ds7.memory.dmaEvent.priority = 0x40;
 23	ds->ds9.memory.dmaEvent.name = "DS9 DMA";
 24	ds->ds9.memory.dmaEvent.callback = _dmaEvent;
 25	ds->ds9.memory.dmaEvent.context = &ds->ds9;
 26	ds->ds9.memory.dmaEvent.priority = 0x40;
 27}
 28
 29void DSDMAReset(struct DSCommon* dscore) {
 30	memset(dscore->memory.dma, 0, sizeof(dscore->memory.dma));
 31	int i;
 32	for (i = 0; i < 4; ++i) {
 33		// TODO: This is wrong for DS7
 34		dscore->memory.dma[i].count = 0x200000;
 35	}
 36	dscore->memory.activeDMA = -1;
 37}
 38
 39uint32_t DSDMAWriteSAD(struct DSCommon* dscore, int dma, uint32_t address) {
 40	address &= 0x0FFFFFFE;
 41	dscore->memory.dma[dma].source = address;
 42	return dscore->memory.dma[dma].source;
 43}
 44
 45uint32_t DSDMAWriteDAD(struct DSCommon* dscore, int dma, uint32_t address) {
 46	address &= 0x0FFFFFFE;
 47	dscore->memory.dma[dma].dest = address;
 48	return dscore->memory.dma[dma].dest;
 49}
 50
 51void DS7DMAWriteCNT(struct DSCommon* dscore, int dma, uint32_t value) {
 52	struct DSCoreMemory* memory = &dscore->memory;
 53	struct GBADMA* currentDma = &memory->dma[dma];
 54	uint32_t count = value & 0xFFFF;
 55	currentDma->count = count ? count : (dma == 3 ? 0x10000 : 0x4000);
 56	int wasEnabled = GBADMARegisterIsEnable(currentDma->reg);
 57	unsigned control = (value >> 16) & 0xF7E0;
 58	currentDma->reg = control;
 59
 60	if (!wasEnabled && GBADMARegisterIsEnable(currentDma->reg)) {
 61		currentDma->nextSource = currentDma->source;
 62		currentDma->nextDest = currentDma->dest;
 63		DSDMASchedule(dscore, dma, currentDma);
 64	}
 65}
 66
 67void DS9DMAWriteCNT(struct DSCommon* dscore, int dma, uint32_t value) {
 68	struct DSCoreMemory* memory = &dscore->memory;
 69	struct GBADMA* currentDma = &memory->dma[dma];
 70	uint32_t count = value & 0x1FFFFF;
 71	currentDma->count = count ? count : 0x200000;
 72	int wasEnabled = GBADMARegisterIsEnable(currentDma->reg);
 73	unsigned control = (value >> 16) & 0xFFE0;
 74	currentDma->reg = control;
 75
 76	if (!wasEnabled && GBADMARegisterIsEnable(currentDma->reg)) {
 77		currentDma->nextSource = currentDma->source;
 78		currentDma->nextDest = currentDma->dest;
 79		DSDMASchedule(dscore, dma, currentDma);
 80	}
 81}
 82
 83void DSDMASchedule(struct DSCommon* dscore, int number, struct GBADMA* info) {
 84	switch (GBADMARegisterGetTiming(info->reg)) {
 85	case DS_DMA_TIMING_NOW:
 86		info->when = mTimingCurrentTime(&dscore->timing) + 3; // DMAs take 3 cycles to start
 87		info->nextCount = info->count;
 88		break;
 89	case DS_DMA_TIMING_HBLANK:
 90	case DS_DMA_TIMING_VBLANK:
 91		// Handled implicitly
 92		return;
 93	default:
 94		mLOG(DS_MEM, STUB, "Unimplemented DMA");
 95	}
 96	DSDMAUpdate(dscore);
 97}
 98
 99void _dmaEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) {
100	UNUSED(timing);
101	UNUSED(cyclesLate);
102	struct DSCommon* dscore = context;
103	struct DSCoreMemory* memory = &dscore->memory;
104	struct GBADMA* dma = &memory->dma[memory->activeDMA];
105	if (dma->nextCount == dma->count) {
106		dma->when = mTimingCurrentTime(&dscore->timing);
107	}
108	if (dma->nextCount & 0xFFFFF) {
109		dscore->p->cpuBlocked = true; // TODO: Fix for ITCM
110		DSDMAService(dscore, memory->activeDMA, dma);
111	} else {
112		dma->nextCount = 0;
113		if (!GBADMARegisterIsRepeat(dma->reg) || GBADMARegisterGetTiming(dma->reg) == DMA_TIMING_NOW) {
114			dma->reg = GBADMARegisterClearEnable(dma->reg);
115
116			// Clear the enable bit in memory
117			memory->io[(DS_REG_DMA0CNT_HI + memory->activeDMA * (DS_REG_DMA1CNT_HI - DS_REG_DMA0CNT_HI)) >> 1] &= 0x7FE0;
118		}
119		if (GBADMARegisterGetDestControl(dma->reg) == DMA_INCREMENT_RELOAD) {
120			dma->nextDest = dma->dest;
121		}
122		if (GBADMARegisterIsDoIRQ(dma->reg)) {
123			DSRaiseIRQ(dscore->cpu, dscore->memory.io, DS_IRQ_DMA0 + memory->activeDMA);
124		}
125		DSDMAUpdate(dscore);
126	}
127}
128
129void DSDMAUpdate(struct DSCommon* dscore) {
130	int i;
131	struct DSCoreMemory* memory = &dscore->memory;
132	memory->activeDMA = -1;
133	uint32_t currentTime = mTimingCurrentTime(&dscore->timing);
134	for (i = 0; i < 4; ++i) {
135		struct GBADMA* dma = &memory->dma[i];
136		if (GBADMARegisterIsEnable(dma->reg) && dma->nextCount) {
137			memory->activeDMA = i;
138			break;
139		}
140	}
141
142	if (memory->activeDMA >= 0) {
143		mTimingDeschedule(&dscore->timing, &memory->dmaEvent);
144		mTimingSchedule(&dscore->timing, &memory->dmaEvent, memory->dma[memory->activeDMA].when - currentTime);
145	} else {
146		dscore->p->cpuBlocked = false;
147	}
148}
149
150void DSDMAService(struct DSCommon* dscore, int number, struct GBADMA* info) {
151	struct DSCoreMemory* memory = &dscore->memory;
152	struct ARMCore* cpu = dscore->cpu;
153	uint32_t width = 2 << GBADMARegisterGetWidth(info->reg);
154	int32_t wordsRemaining = info->nextCount;
155	uint32_t source = info->nextSource;
156	uint32_t dest = info->nextDest;
157	uint32_t sourceRegion = source >> DS_BASE_OFFSET;
158	uint32_t destRegion = dest >> DS_BASE_OFFSET;
159	int32_t cycles = 2;
160
161	if (info->count == info->nextCount) {
162		if (width == 4) {
163			cycles += dscore->memory.waitstatesNonseq32[sourceRegion] + dscore->memory.waitstatesNonseq32[destRegion];
164		} else {
165			cycles += dscore->memory.waitstatesNonseq16[sourceRegion] + dscore->memory.waitstatesNonseq16[destRegion];
166		}
167		source &= -width;
168		dest &= -width;
169	} else {
170		if (width == 4) {
171			cycles += dscore->memory.waitstatesSeq32[sourceRegion] + dscore->memory.waitstatesSeq32[destRegion];
172		} else {
173			cycles += dscore->memory.waitstatesSeq16[sourceRegion] + dscore->memory.waitstatesSeq16[destRegion];
174		}
175	}
176	info->when += cycles;
177
178	uint32_t word;
179	if (width == 4) {
180		word = cpu->memory.load32(cpu, source, 0);
181		cpu->memory.store32(cpu, dest, word, 0);
182	} else {
183		word = cpu->memory.load16(cpu, source, 0);
184		cpu->memory.store16(cpu, dest, word, 0);
185	}
186	int sourceOffset = DMA_OFFSET[GBADMARegisterGetSrcControl(info->reg)] * width;
187	int destOffset = DMA_OFFSET[GBADMARegisterGetDestControl(info->reg)] * width;
188	source += sourceOffset;
189	dest += destOffset;
190	--wordsRemaining;
191
192	info->nextCount = wordsRemaining;
193	info->nextSource = source;
194	info->nextDest = dest;
195	if (!wordsRemaining) {
196		info->nextCount |= 0x80000000;
197	}
198	DSDMAUpdate(dscore);
199}