all repos — mgba @ c1147c9616c846d10673ce5fd37aa7e3ad7b068e

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