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
40static bool _isValidDMADAD(int dma, uint32_t address) {
41 UNUSED(dma);
42 return address >= DS_BASE_RAM;
43}
44
45uint32_t DSDMAWriteSAD(struct DSCommon* dscore, int dma, uint32_t address) {
46 address &= 0x0FFFFFFE;
47 dscore->memory.dma[dma].source = address;
48 return dscore->memory.dma[dma].source;
49}
50
51uint32_t DSDMAWriteDAD(struct DSCommon* dscore, int dma, uint32_t address) {
52 address &= 0x0FFFFFFE;
53 if (_isValidDMADAD(dma, address)) {
54 dscore->memory.dma[dma].dest = address;
55 }
56 return dscore->memory.dma[dma].dest;
57}
58
59void DS7DMAWriteCNT(struct DSCommon* dscore, int dma, uint32_t value) {
60 struct DSCoreMemory* memory = &dscore->memory;
61 struct GBADMA* currentDma = &memory->dma[dma];
62 uint32_t count = value & 0xFFFF;
63 currentDma->count = count ? count : (dma == 3 ? 0x10000 : 0x4000);
64 int wasEnabled = GBADMARegisterIsEnable(currentDma->reg);
65 unsigned control = (value >> 16) & 0xF7E0;
66 currentDma->reg = control;
67
68 if (!wasEnabled && GBADMARegisterIsEnable(currentDma->reg)) {
69 currentDma->nextSource = currentDma->source;
70 currentDma->nextDest = currentDma->dest;
71 DSDMASchedule(dscore, dma, currentDma);
72 }
73}
74
75void DS9DMAWriteCNT(struct DSCommon* dscore, int dma, uint32_t value) {
76 struct DSCoreMemory* memory = &dscore->memory;
77 struct GBADMA* currentDma = &memory->dma[dma];
78 uint32_t count = value & 0x1FFFFF;
79 currentDma->count = count ? count : 0x200000;
80 int wasEnabled = GBADMARegisterIsEnable(currentDma->reg);
81 unsigned control = (value >> 16) & 0xFFE0;
82 currentDma->reg = control;
83
84 if (!wasEnabled && GBADMARegisterIsEnable(currentDma->reg)) {
85 currentDma->nextSource = currentDma->source;
86 currentDma->nextDest = currentDma->dest;
87 DSDMASchedule(dscore, dma, currentDma);
88 }
89}
90
91void DSDMASchedule(struct DSCommon* dscore, int number, struct GBADMA* info) {
92 int which;
93 if (dscore == &dscore->p->ds9) {
94 which = GBADMARegisterGetTiming9(info->reg);
95 } else {
96 which = GBADMARegisterGetTiming(info->reg);
97 }
98 switch (which) {
99 case DS_DMA_TIMING_NOW:
100 info->when = mTimingCurrentTime(&dscore->timing) + 3; // DMAs take 3 cycles to start
101 info->nextCount = info->count;
102 break;
103 case DS_DMA_TIMING_VBLANK:
104 // Handled implicitly
105 return;
106 case DS9_DMA_TIMING_SLOT1:
107 DSSlot1ScheduleDMA(dscore, number, info);
108 return;
109 case DS_DMA_TIMING_GEOM_FIFO:
110 DSGXScheduleDMA(dscore, number, info);
111 return;
112 case DS_DMA_TIMING_HBLANK: // DS7_DMA_TIMING_SLOT1
113 if (dscore == &dscore->p->ds9) {
114 // Handled implicitly
115 return;
116 }
117 default:
118 mLOG(DS_MEM, STUB, "Unimplemented DMA");
119 }
120 DSDMAUpdate(dscore);
121}
122
123void _dmaEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) {
124 UNUSED(timing);
125 UNUSED(cyclesLate);
126 struct DSCommon* dscore = context;
127 struct DSCoreMemory* memory = &dscore->memory;
128 struct GBADMA* dma = &memory->dma[memory->activeDMA];
129 if (dma->nextCount == dma->count) {
130 dma->when = mTimingCurrentTime(&dscore->timing);
131 }
132 if (dma->nextCount & 0xFFFFF) {
133 if (dscore->p->cpuBlocked & ~DS_CPU_BLOCK_DMA) {
134 // Delay DMA until after the CPU unblocks
135 dma->when = mTimingCurrentTime(&dscore->timing) + mTimingNextEvent(&dscore->timing) + 1;
136 DSDMAUpdate(dscore);
137 } else {
138 dscore->p->cpuBlocked |= DS_CPU_BLOCK_DMA; // TODO: Fix for ITCM
139 DSDMAService(dscore, memory->activeDMA, dma);
140 }
141 } else {
142 dma->nextCount = 0;
143 if (!GBADMARegisterIsRepeat(dma->reg) || GBADMARegisterGetTiming(dma->reg) == DMA_TIMING_NOW) {
144 dma->reg = GBADMARegisterClearEnable(dma->reg);
145
146 // Clear the enable bit in memory
147 memory->io[(DS_REG_DMA0CNT_HI + memory->activeDMA * (DS_REG_DMA1CNT_HI - DS_REG_DMA0CNT_HI)) >> 1] &= 0x7FFF;
148 }
149 if (GBADMARegisterGetDestControl(dma->reg) == DMA_INCREMENT_RELOAD) {
150 dma->nextDest = dma->dest;
151 }
152 if (GBADMARegisterIsDoIRQ(dma->reg)) {
153 DSRaiseIRQ(dscore->cpu, dscore->memory.io, DS_IRQ_DMA0 + memory->activeDMA);
154 }
155 DSDMAUpdate(dscore);
156 }
157}
158
159void DSDMARunVblank(struct DSCommon* dscore, int32_t cycles) {
160 struct DSCoreMemory* memory = &dscore->memory;
161 struct GBADMA* dma;
162 int i;
163 for (i = 0; i < 4; ++i) {
164 dma = &memory->dma[i];
165 if (GBADMARegisterIsEnable(dma->reg) && GBADMARegisterGetTiming(dma->reg) == DS_DMA_TIMING_VBLANK && !dma->nextCount) {
166 dma->when = mTimingCurrentTime(&dscore->timing) + 3 + cycles;
167 dma->nextCount = dma->count;
168 }
169 }
170 DSDMAUpdate(dscore);
171}
172
173void DSDMARunHblank(struct DSCommon* dscore, int32_t cycles) {
174 struct DSCoreMemory* memory = &dscore->memory;
175 struct GBADMA* dma;
176 int i;
177 for (i = 0; i < 4; ++i) {
178 dma = &memory->dma[i];
179 if (GBADMARegisterIsEnable(dma->reg) && GBADMARegisterGetTiming9(dma->reg) == DS_DMA_TIMING_HBLANK && !dma->nextCount) {
180 dma->when = mTimingCurrentTime(&dscore->timing) + 3 + cycles;
181 dma->nextCount = dma->count;
182 }
183 }
184 DSDMAUpdate(dscore);
185}
186
187void DSDMAUpdate(struct DSCommon* dscore) {
188 int i;
189 struct DSCoreMemory* memory = &dscore->memory;
190 memory->activeDMA = -1;
191 uint32_t currentTime = mTimingCurrentTime(&dscore->timing);
192 for (i = 0; i < 4; ++i) {
193 struct GBADMA* dma = &memory->dma[i];
194 if (GBADMARegisterIsEnable(dma->reg) && dma->nextCount) {
195 memory->activeDMA = i;
196 break;
197 }
198 }
199
200 if (memory->activeDMA >= 0) {
201 mTimingDeschedule(&dscore->timing, &memory->dmaEvent);
202 mTimingSchedule(&dscore->timing, &memory->dmaEvent, memory->dma[memory->activeDMA].when - currentTime);
203 } else {
204 dscore->p->cpuBlocked &= ~DS_CPU_BLOCK_DMA;
205 }
206}
207
208void DSDMAService(struct DSCommon* dscore, int number, struct GBADMA* info) {
209 UNUSED(number);
210 struct ARMCore* cpu = dscore->cpu;
211 uint32_t width = 2 << GBADMARegisterGetWidth(info->reg);
212 int32_t wordsRemaining = info->nextCount;
213 uint32_t source = info->nextSource;
214 uint32_t dest = info->nextDest;
215 uint32_t sourceRegion = source >> DS_BASE_OFFSET;
216 uint32_t destRegion = dest >> DS_BASE_OFFSET;
217 int32_t cycles = 2;
218
219 if (info->count == info->nextCount) {
220 if (width == 4) {
221 cycles += dscore->memory.waitstatesNonseq32[sourceRegion] + dscore->memory.waitstatesNonseq32[destRegion];
222 } else {
223 cycles += dscore->memory.waitstatesNonseq16[sourceRegion] + dscore->memory.waitstatesNonseq16[destRegion];
224 }
225 source &= -width;
226 dest &= -width;
227 } else {
228 if (width == 4) {
229 cycles += dscore->memory.waitstatesSeq32[sourceRegion] + dscore->memory.waitstatesSeq32[destRegion];
230 } else {
231 cycles += dscore->memory.waitstatesSeq16[sourceRegion] + dscore->memory.waitstatesSeq16[destRegion];
232 }
233 }
234 info->when += cycles;
235
236 uint32_t word;
237 if (width == 4) {
238 word = cpu->memory.load32(cpu, source, 0);
239 cpu->memory.store32(cpu, dest, word, 0);
240 } else {
241 word = cpu->memory.load16(cpu, source, 0);
242 cpu->memory.store16(cpu, dest, word, 0);
243 }
244 int sourceOffset = DMA_OFFSET[GBADMARegisterGetSrcControl(info->reg)] * width;
245 int destOffset = DMA_OFFSET[GBADMARegisterGetDestControl(info->reg)] * width;
246 source += sourceOffset;
247 dest += destOffset;
248 --wordsRemaining;
249
250 info->nextCount = wordsRemaining;
251 info->nextSource = source;
252 info->nextDest = dest;
253 if (!wordsRemaining) {
254 info->nextCount |= 0x80000000;
255 }
256 DSDMAUpdate(dscore);
257}