all repos — mgba @ 20754b772e23b4aafa0c24c626c773ea2f027c03

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