all repos — mgba @ d7ce8f9f0660cfab0990cc1c1e7b1fc43dae9b3d

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