all repos — mgba @ ad85acab75a674ec46850a2381323632dae0a83a

mGBA Game Boy Advance Emulator

src/gba/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 "dma.h"
  7
  8#include "gba/gba.h"
  9#include "gba/io.h"
 10
 11static void _dmaEvent(struct mTiming* timing, void* context, uint32_t cyclesLate);
 12
 13static void GBADMAService(struct GBA* gba, int number, struct GBADMA* info);
 14
 15static const int DMA_OFFSET[] = { 1, -1, 0, 1 };
 16
 17void GBADMAInit(struct GBA* gba) {
 18	gba->memory.dmaEvent.name = "GBA DMA";
 19	gba->memory.dmaEvent.callback = _dmaEvent;
 20	gba->memory.dmaEvent.context = gba;
 21	gba->memory.dmaEvent.priority = 0x40;
 22}
 23
 24void GBADMAReset(struct GBA* gba) {
 25	memset(gba->memory.dma, 0, sizeof(gba->memory.dma));
 26	int i;
 27	for (i = 0; i < 4; ++i) {
 28		gba->memory.dma[i].count = 0x4000;
 29		gba->memory.dma[i].nextEvent = INT_MAX;
 30	}
 31	gba->memory.dma[3].count = 0x10000;
 32	gba->memory.activeDMA = -1;
 33}
 34static bool _isValidDMASAD(int dma, uint32_t address) {
 35	if (dma == 0 && address >= BASE_CART0 && address < BASE_CART_SRAM) {
 36		return false;
 37	}
 38	return address >= BASE_WORKING_RAM;
 39}
 40
 41static bool _isValidDMADAD(int dma, uint32_t address) {
 42	return dma == 3 || address < BASE_CART0;
 43}
 44
 45uint32_t GBADMAWriteSAD(struct GBA* gba, int dma, uint32_t address) {
 46	struct GBAMemory* memory = &gba->memory;
 47	address &= 0x0FFFFFFE;
 48	if (_isValidDMASAD(dma, address)) {
 49		memory->dma[dma].source = address;
 50	}
 51	return memory->dma[dma].source;
 52}
 53
 54uint32_t GBADMAWriteDAD(struct GBA* gba, int dma, uint32_t address) {
 55	struct GBAMemory* memory = &gba->memory;
 56	address &= 0x0FFFFFFE;
 57	if (_isValidDMADAD(dma, address)) {
 58		memory->dma[dma].dest = address;
 59	}
 60	return memory->dma[dma].dest;
 61}
 62
 63void GBADMAWriteCNT_LO(struct GBA* gba, int dma, uint16_t count) {
 64	struct GBAMemory* memory = &gba->memory;
 65	memory->dma[dma].count = count ? count : (dma == 3 ? 0x10000 : 0x4000);
 66}
 67
 68uint16_t GBADMAWriteCNT_HI(struct GBA* gba, int dma, uint16_t control) {
 69	struct GBAMemory* memory = &gba->memory;
 70	struct GBADMA* currentDma = &memory->dma[dma];
 71	int wasEnabled = GBADMARegisterIsEnable(currentDma->reg);
 72	if (dma < 3) {
 73		control &= 0xF7E0;
 74	} else {
 75		control &= 0xFFE0;
 76	}
 77	currentDma->reg = control;
 78
 79	if (GBADMARegisterIsDRQ(currentDma->reg)) {
 80		mLOG(GBA_MEM, STUB, "DRQ not implemented");
 81	}
 82
 83	if (!wasEnabled && GBADMARegisterIsEnable(currentDma->reg)) {
 84		currentDma->nextSource = currentDma->source;
 85		currentDma->nextDest = currentDma->dest;
 86		currentDma->nextCount = currentDma->count;
 87		GBADMASchedule(gba, dma, currentDma);
 88	}
 89	// If the DMA has already occurred, this value might have changed since the function started
 90	return currentDma->reg;
 91};
 92
 93void GBADMASchedule(struct GBA* gba, int number, struct GBADMA* info) {
 94	info->hasStarted = 0;
 95	switch (GBADMARegisterGetTiming(info->reg)) {
 96	case DMA_TIMING_NOW:
 97		info->nextEvent = 2 + 1; // XXX: Account for I cycle when writing
 98		info->scheduledAt = mTimingCurrentTime(&gba->timing);
 99		GBADMAUpdate(gba, 0);
100		break;
101	case DMA_TIMING_HBLANK:
102		// Handled implicitly
103		info->nextEvent = INT_MAX;
104		break;
105	case DMA_TIMING_VBLANK:
106		// Handled implicitly
107		info->nextEvent = INT_MAX;
108		break;
109	case DMA_TIMING_CUSTOM:
110		info->nextEvent = INT_MAX;
111		switch (number) {
112		case 0:
113			mLOG(GBA_MEM, WARN, "Discarding invalid DMA0 scheduling");
114			break;
115		case 1:
116		case 2:
117			GBAAudioScheduleFifoDma(&gba->audio, number, info);
118			break;
119		case 3:
120			// GBAVideoScheduleVCaptureDma(dma, info);
121			break;
122		}
123	}
124}
125
126void GBADMARunHblank(struct GBA* gba, int32_t cycles) {
127	struct GBAMemory* memory = &gba->memory;
128	struct GBADMA* dma;
129	bool dmaSeen = false;
130	if (memory->activeDMA >= 0) {
131		GBADMAUpdate(gba, mTimingCurrentTime(&gba->timing) - memory->dma[memory->activeDMA].scheduledAt);
132	}
133	int i;
134	for (i = 0; i < 4; ++i) {
135		dma = &memory->dma[i];
136		if (GBADMARegisterIsEnable(dma->reg) && GBADMARegisterGetTiming(dma->reg) == DMA_TIMING_HBLANK) {
137			dma->nextEvent = 2 + cycles;
138			dma->scheduledAt = mTimingCurrentTime(&gba->timing);
139			dmaSeen = true;
140		}
141	}
142	if (dmaSeen) {
143		GBADMAUpdate(gba, 0);
144	}
145}
146
147void GBADMARunVblank(struct GBA* gba, int32_t cycles) {
148	struct GBAMemory* memory = &gba->memory;
149	struct GBADMA* dma;
150	bool dmaSeen = false;
151	if (memory->activeDMA >= 0) {
152		GBADMAUpdate(gba, mTimingCurrentTime(&gba->timing) - memory->dma[memory->activeDMA].scheduledAt);
153	}
154	int i;
155	for (i = 0; i < 4; ++i) {
156		dma = &memory->dma[i];
157		if (GBADMARegisterIsEnable(dma->reg) && GBADMARegisterGetTiming(dma->reg) == DMA_TIMING_VBLANK) {
158			dma->nextEvent = 2 + cycles;
159			dma->scheduledAt = mTimingCurrentTime(&gba->timing);
160			dmaSeen = true;
161		}
162	}
163	if (dmaSeen) {
164		GBADMAUpdate(gba, 0);
165	}
166}
167
168void _dmaEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) {
169	UNUSED(timing);
170	struct GBA* gba = context;
171	struct GBAMemory* memory = &gba->memory;
172	struct GBADMA* dma = &memory->dma[memory->activeDMA];
173	dma->nextEvent = -cyclesLate;
174	GBADMAService(gba, memory->activeDMA, dma);
175}
176
177void GBADMAUpdate(struct GBA* gba, int32_t cycles) {
178	int i;
179	struct GBAMemory* memory = &gba->memory;
180	memory->activeDMA = -1;
181	for (i = 3; i >= 0; --i) {
182		struct GBADMA* dma = &memory->dma[i];
183		if (dma->nextEvent != INT_MAX) {
184			dma->nextEvent -= cycles;
185			if (GBADMARegisterIsEnable(dma->reg)) {
186				memory->activeDMA = i;
187			}
188		}
189	}
190
191	if (memory->activeDMA >= 0) {
192		mTimingDeschedule(&gba->timing, &memory->dmaEvent);
193		mTimingSchedule(&gba->timing, &memory->dmaEvent, memory->dma[memory->activeDMA].nextEvent);
194	} else {
195		gba->cpuBlocked = false;
196	}
197}
198
199void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) {
200	struct GBAMemory* memory = &gba->memory;
201	struct ARMCore* cpu = gba->cpu;
202	uint32_t width = 2 << GBADMARegisterGetWidth(info->reg);
203	int32_t wordsRemaining = info->nextCount;
204	uint32_t source = info->nextSource;
205	uint32_t dest = info->nextDest;
206	uint32_t sourceRegion = source >> BASE_OFFSET;
207	uint32_t destRegion = dest >> BASE_OFFSET;
208	int32_t cycles = 2;
209
210	gba->cpuBlocked = true;
211	if (info->hasStarted < 2) {
212		if (sourceRegion < REGION_CART0 || destRegion < REGION_CART0) {
213			cycles += 2;
214		}
215		if (width == 4) {
216			cycles += memory->waitstatesNonseq32[sourceRegion] + memory->waitstatesNonseq32[destRegion];
217		} else {
218			cycles += memory->waitstatesNonseq16[sourceRegion] + memory->waitstatesNonseq16[destRegion];
219		}
220		if (info->hasStarted < 1) {
221			info->hasStarted = wordsRemaining;
222			info->nextEvent = 0;
223			info->scheduledAt = mTimingCurrentTime(&gba->timing);
224			GBADMAUpdate(gba, -cycles);
225			return;
226		}
227		info->hasStarted = 2;
228		source &= -width;
229		dest &= -width;
230	} else {
231		if (width == 4) {
232			cycles += memory->waitstatesSeq32[sourceRegion] + memory->waitstatesSeq32[destRegion];
233		} else {
234			cycles += memory->waitstatesSeq16[sourceRegion] + memory->waitstatesSeq16[destRegion];
235		}
236	}
237	info->nextEvent += cycles;
238
239	gba->performingDMA = 1 | (number << 1);
240	uint32_t word;
241	if (width == 4) {
242		word = cpu->memory.load32(cpu, source, 0);
243		gba->bus = word;
244		cpu->memory.store32(cpu, dest, word, 0);
245	} else {
246		if (sourceRegion == REGION_CART2_EX && memory->savedata.type == SAVEDATA_EEPROM) {
247			word = GBASavedataReadEEPROM(&memory->savedata);
248			cpu->memory.store16(cpu, dest, word, 0);
249		} else if (destRegion == REGION_CART2_EX) {
250			if (memory->savedata.type == SAVEDATA_AUTODETECT) {
251				mLOG(GBA_MEM, INFO, "Detected EEPROM savegame");
252				GBASavedataInitEEPROM(&memory->savedata, gba->realisticTiming);
253			}
254			word = cpu->memory.load16(cpu, source, 0);
255			GBASavedataWriteEEPROM(&memory->savedata, word, wordsRemaining);
256		} else {
257			word = cpu->memory.load16(cpu, source, 0);
258			cpu->memory.store16(cpu, dest, word, 0);
259		}
260		gba->bus = word | (word << 16);
261	}
262	int sourceOffset = DMA_OFFSET[GBADMARegisterGetSrcControl(info->reg)] * width;
263	int destOffset = DMA_OFFSET[GBADMARegisterGetDestControl(info->reg)] * width;
264	source += sourceOffset;
265	dest += destOffset;
266	--wordsRemaining;
267	gba->performingDMA = 0;
268
269	if (!wordsRemaining) {
270		if (!GBADMARegisterIsRepeat(info->reg) || GBADMARegisterGetTiming(info->reg) == DMA_TIMING_NOW) {
271			info->reg = GBADMARegisterClearEnable(info->reg);
272			info->nextEvent = INT_MAX;
273
274			// Clear the enable bit in memory
275			memory->io[(REG_DMA0CNT_HI + number * (REG_DMA1CNT_HI - REG_DMA0CNT_HI)) >> 1] &= 0x7FE0;
276		} else {
277			info->nextCount = info->count;
278			if (GBADMARegisterGetDestControl(info->reg) == DMA_INCREMENT_RELOAD) {
279				info->nextDest = info->dest;
280			}
281			GBADMASchedule(gba, number, info);
282		}
283		if (GBADMARegisterIsDoIRQ(info->reg)) {
284			GBARaiseIRQ(gba, IRQ_DMA0 + number);
285		}
286	} else {
287		info->nextDest = dest;
288		info->nextCount = wordsRemaining;
289		info->scheduledAt = mTimingCurrentTime(&gba->timing);
290	}
291	info->nextSource = source;
292	GBADMAUpdate(gba, 0);
293}