all repos — mgba @ 35e2b73301a0198c45d3c77abaf28534d9575a67

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