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
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 default:
107 mLOG(DS_MEM, STUB, "Unimplemented DMA");
108 }
109 DSDMAUpdate(dscore);
110}
111
112void _dmaEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) {
113 UNUSED(timing);
114 UNUSED(cyclesLate);
115 struct DSCommon* dscore = context;
116 struct DSCoreMemory* memory = &dscore->memory;
117 struct GBADMA* dma = &memory->dma[memory->activeDMA];
118 if (dma->nextCount == dma->count) {
119 dma->when = mTimingCurrentTime(&dscore->timing);
120 }
121 if (dma->nextCount & 0xFFFFF) {
122 if (dscore->p->cpuBlocked & ~DS_CPU_BLOCK_DMA) {
123 // Delay DMA until after the CPU unblocks
124 dma->when = mTimingCurrentTime(&dscore->timing) + mTimingNextEvent(&dscore->timing) + 1;
125 DSDMAUpdate(dscore);
126 } else {
127 dscore->p->cpuBlocked |= DS_CPU_BLOCK_DMA; // TODO: Fix for ITCM
128 DSDMAService(dscore, memory->activeDMA, dma);
129 }
130 } else {
131 dma->nextCount = 0;
132 if (!GBADMARegisterIsRepeat(dma->reg) || GBADMARegisterGetTiming(dma->reg) == DMA_TIMING_NOW) {
133 dma->reg = GBADMARegisterClearEnable(dma->reg);
134
135 // Clear the enable bit in memory
136 memory->io[(DS_REG_DMA0CNT_HI + memory->activeDMA * (DS_REG_DMA1CNT_HI - DS_REG_DMA0CNT_HI)) >> 1] &= 0x7FFF;
137 }
138 if (GBADMARegisterGetDestControl(dma->reg) == DMA_INCREMENT_RELOAD) {
139 dma->nextDest = dma->dest;
140 }
141 if (GBADMARegisterIsDoIRQ(dma->reg)) {
142 DSRaiseIRQ(dscore->cpu, dscore->memory.io, DS_IRQ_DMA0 + memory->activeDMA);
143 }
144 DSDMAUpdate(dscore);
145 }
146}
147
148void DSDMARunVblank(struct DSCommon* dscore, int32_t cycles) {
149 struct DSCoreMemory* memory = &dscore->memory;
150 struct GBADMA* dma;
151 int i;
152 for (i = 0; i < 4; ++i) {
153 dma = &memory->dma[i];
154 if (GBADMARegisterIsEnable(dma->reg) && GBADMARegisterGetTiming(dma->reg) == DMA_TIMING_VBLANK && !dma->nextCount) {
155 dma->when = mTimingCurrentTime(&dscore->timing) + 3 + cycles;
156 dma->nextCount = dma->count;
157 }
158 }
159 DSDMAUpdate(dscore);
160}
161
162void DSDMAUpdate(struct DSCommon* dscore) {
163 int i;
164 struct DSCoreMemory* memory = &dscore->memory;
165 memory->activeDMA = -1;
166 uint32_t currentTime = mTimingCurrentTime(&dscore->timing);
167 for (i = 0; i < 4; ++i) {
168 struct GBADMA* dma = &memory->dma[i];
169 if (GBADMARegisterIsEnable(dma->reg) && dma->nextCount) {
170 memory->activeDMA = i;
171 break;
172 }
173 }
174
175 if (memory->activeDMA >= 0) {
176 mTimingDeschedule(&dscore->timing, &memory->dmaEvent);
177 mTimingSchedule(&dscore->timing, &memory->dmaEvent, memory->dma[memory->activeDMA].when - currentTime);
178 } else {
179 dscore->p->cpuBlocked &= ~DS_CPU_BLOCK_DMA;
180 }
181}
182
183void DSDMAService(struct DSCommon* dscore, int number, struct GBADMA* info) {
184 struct DSCoreMemory* memory = &dscore->memory;
185 struct ARMCore* cpu = dscore->cpu;
186 uint32_t width = 2 << GBADMARegisterGetWidth(info->reg);
187 int32_t wordsRemaining = info->nextCount;
188 uint32_t source = info->nextSource;
189 uint32_t dest = info->nextDest;
190 uint32_t sourceRegion = source >> DS_BASE_OFFSET;
191 uint32_t destRegion = dest >> DS_BASE_OFFSET;
192 int32_t cycles = 2;
193
194 if (info->count == info->nextCount) {
195 if (width == 4) {
196 cycles += dscore->memory.waitstatesNonseq32[sourceRegion] + dscore->memory.waitstatesNonseq32[destRegion];
197 } else {
198 cycles += dscore->memory.waitstatesNonseq16[sourceRegion] + dscore->memory.waitstatesNonseq16[destRegion];
199 }
200 source &= -width;
201 dest &= -width;
202 } else {
203 if (width == 4) {
204 cycles += dscore->memory.waitstatesSeq32[sourceRegion] + dscore->memory.waitstatesSeq32[destRegion];
205 } else {
206 cycles += dscore->memory.waitstatesSeq16[sourceRegion] + dscore->memory.waitstatesSeq16[destRegion];
207 }
208 }
209 info->when += cycles;
210
211 uint32_t word;
212 if (width == 4) {
213 word = cpu->memory.load32(cpu, source, 0);
214 cpu->memory.store32(cpu, dest, word, 0);
215 } else {
216 word = cpu->memory.load16(cpu, source, 0);
217 cpu->memory.store16(cpu, dest, word, 0);
218 }
219 int sourceOffset = DMA_OFFSET[GBADMARegisterGetSrcControl(info->reg)] * width;
220 int destOffset = DMA_OFFSET[GBADMARegisterGetDestControl(info->reg)] * width;
221 source += sourceOffset;
222 dest += destOffset;
223 --wordsRemaining;
224
225 info->nextCount = wordsRemaining;
226 info->nextSource = source;
227 info->nextDest = dest;
228 if (!wordsRemaining) {
229 info->nextCount |= 0x80000000;
230 }
231 DSDMAUpdate(dscore);
232}