all repos — mgba @ 2ab7289a059ab3055f00f0677f7c20bfed7591d5

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	int which;
 85	if (dscore == &dscore->p->ds9) {
 86		which = GBADMARegisterGetTiming9(info->reg);
 87	} else {
 88		which = GBADMARegisterGetTiming(info->reg);
 89	}
 90	switch (which) {
 91	case DS_DMA_TIMING_NOW:
 92		info->when = mTimingCurrentTime(&dscore->timing) + 3; // DMAs take 3 cycles to start
 93		info->nextCount = info->count;
 94		break;
 95	case DS_DMA_TIMING_VBLANK:
 96		// Handled implicitly
 97		return;
 98	case DS9_DMA_TIMING_SLOT1:
 99		DSSlot1ScheduleDMA(dscore, number, info);
100		return;
101	case DS_DMA_TIMING_HBLANK: // DS7_DMA_TIMING_SLOT1
102	default:
103		mLOG(DS_MEM, STUB, "Unimplemented DMA");
104	}
105	DSDMAUpdate(dscore);
106}
107
108void _dmaEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) {
109	UNUSED(timing);
110	UNUSED(cyclesLate);
111	struct DSCommon* dscore = context;
112	struct DSCoreMemory* memory = &dscore->memory;
113	struct GBADMA* dma = &memory->dma[memory->activeDMA];
114	if (dma->nextCount == dma->count) {
115		dma->when = mTimingCurrentTime(&dscore->timing);
116	}
117	if (dma->nextCount & 0xFFFFF) {
118		dscore->p->cpuBlocked = true; // TODO: Fix for ITCM
119		DSDMAService(dscore, memory->activeDMA, dma);
120	} else {
121		dma->nextCount = 0;
122		if (!GBADMARegisterIsRepeat(dma->reg) || GBADMARegisterGetTiming(dma->reg) == DMA_TIMING_NOW) {
123			dma->reg = GBADMARegisterClearEnable(dma->reg);
124
125			// Clear the enable bit in memory
126			memory->io[(DS_REG_DMA0CNT_HI + memory->activeDMA * (DS_REG_DMA1CNT_HI - DS_REG_DMA0CNT_HI)) >> 1] &= 0x7FFF;
127		}
128		if (GBADMARegisterGetDestControl(dma->reg) == DMA_INCREMENT_RELOAD) {
129			dma->nextDest = dma->dest;
130		}
131		if (GBADMARegisterIsDoIRQ(dma->reg)) {
132			DSRaiseIRQ(dscore->cpu, dscore->memory.io, DS_IRQ_DMA0 + memory->activeDMA);
133		}
134		DSDMAUpdate(dscore);
135	}
136}
137
138void DSDMAUpdate(struct DSCommon* dscore) {
139	int i;
140	struct DSCoreMemory* memory = &dscore->memory;
141	memory->activeDMA = -1;
142	uint32_t currentTime = mTimingCurrentTime(&dscore->timing);
143	for (i = 0; i < 4; ++i) {
144		struct GBADMA* dma = &memory->dma[i];
145		if (GBADMARegisterIsEnable(dma->reg) && dma->nextCount) {
146			memory->activeDMA = i;
147			break;
148		}
149	}
150
151	if (memory->activeDMA >= 0) {
152		mTimingDeschedule(&dscore->timing, &memory->dmaEvent);
153		mTimingSchedule(&dscore->timing, &memory->dmaEvent, memory->dma[memory->activeDMA].when - currentTime);
154	} else {
155		dscore->p->cpuBlocked = false;
156	}
157}
158
159void DSDMAService(struct DSCommon* dscore, int number, struct GBADMA* info) {
160	struct DSCoreMemory* memory = &dscore->memory;
161	struct ARMCore* cpu = dscore->cpu;
162	uint32_t width = 2 << GBADMARegisterGetWidth(info->reg);
163	int32_t wordsRemaining = info->nextCount;
164	uint32_t source = info->nextSource;
165	uint32_t dest = info->nextDest;
166	uint32_t sourceRegion = source >> DS_BASE_OFFSET;
167	uint32_t destRegion = dest >> DS_BASE_OFFSET;
168	int32_t cycles = 2;
169
170	if (info->count == info->nextCount) {
171		if (width == 4) {
172			cycles += dscore->memory.waitstatesNonseq32[sourceRegion] + dscore->memory.waitstatesNonseq32[destRegion];
173		} else {
174			cycles += dscore->memory.waitstatesNonseq16[sourceRegion] + dscore->memory.waitstatesNonseq16[destRegion];
175		}
176		source &= -width;
177		dest &= -width;
178	} else {
179		if (width == 4) {
180			cycles += dscore->memory.waitstatesSeq32[sourceRegion] + dscore->memory.waitstatesSeq32[destRegion];
181		} else {
182			cycles += dscore->memory.waitstatesSeq16[sourceRegion] + dscore->memory.waitstatesSeq16[destRegion];
183		}
184	}
185	info->when += cycles;
186
187	uint32_t word;
188	if (width == 4) {
189		word = cpu->memory.load32(cpu, source, 0);
190		cpu->memory.store32(cpu, dest, word, 0);
191	} else {
192		word = cpu->memory.load16(cpu, source, 0);
193		cpu->memory.store16(cpu, dest, word, 0);
194	}
195	int sourceOffset = DMA_OFFSET[GBADMARegisterGetSrcControl(info->reg)] * width;
196	int destOffset = DMA_OFFSET[GBADMARegisterGetDestControl(info->reg)] * width;
197	source += sourceOffset;
198	dest += destOffset;
199	--wordsRemaining;
200
201	info->nextCount = wordsRemaining;
202	info->nextSource = source;
203	info->nextDest = dest;
204	if (!wordsRemaining) {
205		info->nextCount |= 0x80000000;
206	}
207	DSDMAUpdate(dscore);
208}