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/io.h>
10#include <mgba/internal/ds/memory.h>
11
12static void _dmaEvent(struct mTiming* timing, void* context, uint32_t cyclesLate);
13
14static void DSDMAService(struct DSCommon* dscore, int number, struct GBADMA* info);
15
16static const int DMA_OFFSET[] = { 1, -1, 0, 1 };
17
18void DSDMAInit(struct DS* ds) {
19 ds->ds7.memory.dmaEvent.name = "DS7 DMA";
20 ds->ds7.memory.dmaEvent.callback = _dmaEvent;
21 ds->ds7.memory.dmaEvent.context = &ds->ds7;
22 ds->ds7.memory.dmaEvent.priority = 0x40;
23 ds->ds9.memory.dmaEvent.name = "DS9 DMA";
24 ds->ds9.memory.dmaEvent.callback = _dmaEvent;
25 ds->ds9.memory.dmaEvent.context = &ds->ds9;
26 ds->ds9.memory.dmaEvent.priority = 0x40;
27}
28
29void DSDMAReset(struct DSCommon* dscore) {
30 memset(dscore->memory.dma, 0, sizeof(dscore->memory.dma));
31 int i;
32 for (i = 0; i < 4; ++i) {
33 // TODO: This is wrong for DS7
34 dscore->memory.dma[i].count = 0x200000;
35 }
36 dscore->memory.activeDMA = -1;
37}
38
39uint32_t DSDMAWriteSAD(struct DSCommon* dscore, int dma, uint32_t address) {
40 address &= 0x0FFFFFFE;
41 dscore->memory.dma[dma].source = address;
42 return dscore->memory.dma[dma].source;
43}
44
45uint32_t DSDMAWriteDAD(struct DSCommon* dscore, int dma, uint32_t address) {
46 address &= 0x0FFFFFFE;
47 dscore->memory.dma[dma].dest = address;
48 return dscore->memory.dma[dma].dest;
49}
50
51void DS7DMAWriteCNT(struct DSCommon* dscore, int dma, uint32_t value) {
52 struct DSCoreMemory* memory = &dscore->memory;
53 struct GBADMA* currentDma = &memory->dma[dma];
54 uint32_t count = value & 0xFFFF;
55 currentDma->count = count ? count : (dma == 3 ? 0x10000 : 0x4000);
56 int wasEnabled = GBADMARegisterIsEnable(currentDma->reg);
57 unsigned control = (value >> 16) & 0xF7E0;
58 currentDma->reg = control;
59
60 if (!wasEnabled && GBADMARegisterIsEnable(currentDma->reg)) {
61 currentDma->nextSource = currentDma->source;
62 currentDma->nextDest = currentDma->dest;
63 DSDMASchedule(dscore, dma, currentDma);
64 }
65}
66
67void DS9DMAWriteCNT(struct DSCommon* dscore, int dma, uint32_t value) {
68 struct DSCoreMemory* memory = &dscore->memory;
69 struct GBADMA* currentDma = &memory->dma[dma];
70 uint32_t count = value & 0x1FFFFF;
71 currentDma->count = count ? count : 0x200000;
72 int wasEnabled = GBADMARegisterIsEnable(currentDma->reg);
73 unsigned control = (value >> 16) & 0xFFE0;
74 currentDma->reg = control;
75
76 if (!wasEnabled && GBADMARegisterIsEnable(currentDma->reg)) {
77 currentDma->nextSource = currentDma->source;
78 currentDma->nextDest = currentDma->dest;
79 DSDMASchedule(dscore, dma, currentDma);
80 }
81}
82
83void DSDMASchedule(struct DSCommon* dscore, int number, struct GBADMA* info) {
84 int which;
85 if (dscore == &dscore->p->ds9) {
86 which = GBADMARegisterGetTiming9(info->reg);
87 } else {
88 which = GBADMARegisterGetTiming(info->reg);
89 }
90 switch (which) {
91 case DS_DMA_TIMING_NOW:
92 info->when = mTimingCurrentTime(&dscore->timing) + 3; // DMAs take 3 cycles to start
93 info->nextCount = info->count;
94 break;
95 case DS_DMA_TIMING_VBLANK:
96 // Handled implicitly
97 return;
98 case DS9_DMA_TIMING_SLOT1:
99 DSSlot1ScheduleDMA(dscore, number, info);
100 return;
101 case DS_DMA_TIMING_HBLANK: // DS7_DMA_TIMING_SLOT1
102 default:
103 mLOG(DS_MEM, STUB, "Unimplemented DMA");
104 }
105 DSDMAUpdate(dscore);
106}
107
108void _dmaEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) {
109 UNUSED(timing);
110 UNUSED(cyclesLate);
111 struct DSCommon* dscore = context;
112 struct DSCoreMemory* memory = &dscore->memory;
113 struct GBADMA* dma = &memory->dma[memory->activeDMA];
114 if (dma->nextCount == dma->count) {
115 dma->when = mTimingCurrentTime(&dscore->timing);
116 }
117 if (dma->nextCount & 0xFFFFF) {
118 dscore->p->cpuBlocked = true; // TODO: Fix for ITCM
119 DSDMAService(dscore, memory->activeDMA, dma);
120 } else {
121 dma->nextCount = 0;
122 if (!GBADMARegisterIsRepeat(dma->reg) || GBADMARegisterGetTiming(dma->reg) == DMA_TIMING_NOW) {
123 dma->reg = GBADMARegisterClearEnable(dma->reg);
124
125 // Clear the enable bit in memory
126 memory->io[(DS_REG_DMA0CNT_HI + memory->activeDMA * (DS_REG_DMA1CNT_HI - DS_REG_DMA0CNT_HI)) >> 1] &= 0x7FFF;
127 }
128 if (GBADMARegisterGetDestControl(dma->reg) == DMA_INCREMENT_RELOAD) {
129 dma->nextDest = dma->dest;
130 }
131 if (GBADMARegisterIsDoIRQ(dma->reg)) {
132 DSRaiseIRQ(dscore->cpu, dscore->memory.io, DS_IRQ_DMA0 + memory->activeDMA);
133 }
134 DSDMAUpdate(dscore);
135 }
136}
137
138void DSDMAUpdate(struct DSCommon* dscore) {
139 int i;
140 struct DSCoreMemory* memory = &dscore->memory;
141 memory->activeDMA = -1;
142 uint32_t currentTime = mTimingCurrentTime(&dscore->timing);
143 for (i = 0; i < 4; ++i) {
144 struct GBADMA* dma = &memory->dma[i];
145 if (GBADMARegisterIsEnable(dma->reg) && dma->nextCount) {
146 memory->activeDMA = i;
147 break;
148 }
149 }
150
151 if (memory->activeDMA >= 0) {
152 mTimingDeschedule(&dscore->timing, &memory->dmaEvent);
153 mTimingSchedule(&dscore->timing, &memory->dmaEvent, memory->dma[memory->activeDMA].when - currentTime);
154 } else {
155 dscore->p->cpuBlocked = false;
156 }
157}
158
159void DSDMAService(struct DSCommon* dscore, int number, struct GBADMA* info) {
160 struct DSCoreMemory* memory = &dscore->memory;
161 struct ARMCore* cpu = dscore->cpu;
162 uint32_t width = 2 << GBADMARegisterGetWidth(info->reg);
163 int32_t wordsRemaining = info->nextCount;
164 uint32_t source = info->nextSource;
165 uint32_t dest = info->nextDest;
166 uint32_t sourceRegion = source >> DS_BASE_OFFSET;
167 uint32_t destRegion = dest >> DS_BASE_OFFSET;
168 int32_t cycles = 2;
169
170 if (info->count == info->nextCount) {
171 if (width == 4) {
172 cycles += dscore->memory.waitstatesNonseq32[sourceRegion] + dscore->memory.waitstatesNonseq32[destRegion];
173 } else {
174 cycles += dscore->memory.waitstatesNonseq16[sourceRegion] + dscore->memory.waitstatesNonseq16[destRegion];
175 }
176 source &= -width;
177 dest &= -width;
178 } else {
179 if (width == 4) {
180 cycles += dscore->memory.waitstatesSeq32[sourceRegion] + dscore->memory.waitstatesSeq32[destRegion];
181 } else {
182 cycles += dscore->memory.waitstatesSeq16[sourceRegion] + dscore->memory.waitstatesSeq16[destRegion];
183 }
184 }
185 info->when += cycles;
186
187 uint32_t word;
188 if (width == 4) {
189 word = cpu->memory.load32(cpu, source, 0);
190 cpu->memory.store32(cpu, dest, word, 0);
191 } else {
192 word = cpu->memory.load16(cpu, source, 0);
193 cpu->memory.store16(cpu, dest, word, 0);
194 }
195 int sourceOffset = DMA_OFFSET[GBADMARegisterGetSrcControl(info->reg)] * width;
196 int destOffset = DMA_OFFSET[GBADMARegisterGetDestControl(info->reg)] * width;
197 source += sourceOffset;
198 dest += destOffset;
199 --wordsRemaining;
200
201 info->nextCount = wordsRemaining;
202 info->nextSource = source;
203 info->nextDest = dest;
204 if (!wordsRemaining) {
205 info->nextCount |= 0x80000000;
206 }
207 DSDMAUpdate(dscore);
208}