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}