all repos — mgba @ 0ff5f43eb0f14409614282fb27d540eec1fb516f

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	default:
107		mLOG(DS_MEM, STUB, "Unimplemented DMA");
108	}
109	DSDMAUpdate(dscore);
110}
111
112void _dmaEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) {
113	UNUSED(timing);
114	UNUSED(cyclesLate);
115	struct DSCommon* dscore = context;
116	struct DSCoreMemory* memory = &dscore->memory;
117	struct GBADMA* dma = &memory->dma[memory->activeDMA];
118	if (dma->nextCount == dma->count) {
119		dma->when = mTimingCurrentTime(&dscore->timing);
120	}
121	if (dma->nextCount & 0xFFFFF) {
122		if (dscore->p->cpuBlocked & ~DS_CPU_BLOCK_DMA) {
123			// Delay DMA until after the CPU unblocks
124			dma->when = mTimingCurrentTime(&dscore->timing) + mTimingNextEvent(&dscore->timing) + 1;
125			DSDMAUpdate(dscore);
126		} else {
127			dscore->p->cpuBlocked |= DS_CPU_BLOCK_DMA; // TODO: Fix for ITCM
128			DSDMAService(dscore, memory->activeDMA, dma);
129		}
130	} else {
131		dma->nextCount = 0;
132		if (!GBADMARegisterIsRepeat(dma->reg) || GBADMARegisterGetTiming(dma->reg) == DMA_TIMING_NOW) {
133			dma->reg = GBADMARegisterClearEnable(dma->reg);
134
135			// Clear the enable bit in memory
136			memory->io[(DS_REG_DMA0CNT_HI + memory->activeDMA * (DS_REG_DMA1CNT_HI - DS_REG_DMA0CNT_HI)) >> 1] &= 0x7FFF;
137		}
138		if (GBADMARegisterGetDestControl(dma->reg) == DMA_INCREMENT_RELOAD) {
139			dma->nextDest = dma->dest;
140		}
141		if (GBADMARegisterIsDoIRQ(dma->reg)) {
142			DSRaiseIRQ(dscore->cpu, dscore->memory.io, DS_IRQ_DMA0 + memory->activeDMA);
143		}
144		DSDMAUpdate(dscore);
145	}
146}
147
148void DSDMAUpdate(struct DSCommon* dscore) {
149	int i;
150	struct DSCoreMemory* memory = &dscore->memory;
151	memory->activeDMA = -1;
152	uint32_t currentTime = mTimingCurrentTime(&dscore->timing);
153	for (i = 0; i < 4; ++i) {
154		struct GBADMA* dma = &memory->dma[i];
155		if (GBADMARegisterIsEnable(dma->reg) && dma->nextCount) {
156			memory->activeDMA = i;
157			break;
158		}
159	}
160
161	if (memory->activeDMA >= 0) {
162		mTimingDeschedule(&dscore->timing, &memory->dmaEvent);
163		mTimingSchedule(&dscore->timing, &memory->dmaEvent, memory->dma[memory->activeDMA].when - currentTime);
164	} else {
165		dscore->p->cpuBlocked &= ~DS_CPU_BLOCK_DMA;
166	}
167}
168
169void DSDMAService(struct DSCommon* dscore, int number, struct GBADMA* info) {
170	struct DSCoreMemory* memory = &dscore->memory;
171	struct ARMCore* cpu = dscore->cpu;
172	uint32_t width = 2 << GBADMARegisterGetWidth(info->reg);
173	int32_t wordsRemaining = info->nextCount;
174	uint32_t source = info->nextSource;
175	uint32_t dest = info->nextDest;
176	uint32_t sourceRegion = source >> DS_BASE_OFFSET;
177	uint32_t destRegion = dest >> DS_BASE_OFFSET;
178	int32_t cycles = 2;
179
180	if (info->count == info->nextCount) {
181		if (width == 4) {
182			cycles += dscore->memory.waitstatesNonseq32[sourceRegion] + dscore->memory.waitstatesNonseq32[destRegion];
183		} else {
184			cycles += dscore->memory.waitstatesNonseq16[sourceRegion] + dscore->memory.waitstatesNonseq16[destRegion];
185		}
186		source &= -width;
187		dest &= -width;
188	} else {
189		if (width == 4) {
190			cycles += dscore->memory.waitstatesSeq32[sourceRegion] + dscore->memory.waitstatesSeq32[destRegion];
191		} else {
192			cycles += dscore->memory.waitstatesSeq16[sourceRegion] + dscore->memory.waitstatesSeq16[destRegion];
193		}
194	}
195	info->when += cycles;
196
197	uint32_t word;
198	if (width == 4) {
199		word = cpu->memory.load32(cpu, source, 0);
200		cpu->memory.store32(cpu, dest, word, 0);
201	} else {
202		word = cpu->memory.load16(cpu, source, 0);
203		cpu->memory.store16(cpu, dest, word, 0);
204	}
205	int sourceOffset = DMA_OFFSET[GBADMARegisterGetSrcControl(info->reg)] * width;
206	int destOffset = DMA_OFFSET[GBADMARegisterGetDestControl(info->reg)] * width;
207	source += sourceOffset;
208	dest += destOffset;
209	--wordsRemaining;
210
211	info->nextCount = wordsRemaining;
212	info->nextSource = source;
213	info->nextDest = dest;
214	if (!wordsRemaining) {
215		info->nextCount |= 0x80000000;
216	}
217	DSDMAUpdate(dscore);
218}