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