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 switch (GBADMARegisterGetTiming(info->reg)) {
85 case DS_DMA_TIMING_NOW:
86 info->when = mTimingCurrentTime(&dscore->timing) + 3; // DMAs take 3 cycles to start
87 info->nextCount = info->count;
88 break;
89 case DS_DMA_TIMING_HBLANK:
90 case DS_DMA_TIMING_VBLANK:
91 // Handled implicitly
92 return;
93 default:
94 mLOG(DS_MEM, STUB, "Unimplemented DMA");
95 }
96 DSDMAUpdate(dscore);
97}
98
99void _dmaEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) {
100 UNUSED(timing);
101 UNUSED(cyclesLate);
102 struct DSCommon* dscore = context;
103 struct DSCoreMemory* memory = &dscore->memory;
104 struct GBADMA* dma = &memory->dma[memory->activeDMA];
105 if (dma->nextCount == dma->count) {
106 dma->when = mTimingCurrentTime(&dscore->timing);
107 }
108 if (dma->nextCount & 0xFFFFF) {
109 dscore->p->cpuBlocked = true; // TODO: Fix for ITCM
110 DSDMAService(dscore, memory->activeDMA, dma);
111 } else {
112 dma->nextCount = 0;
113 if (!GBADMARegisterIsRepeat(dma->reg) || GBADMARegisterGetTiming(dma->reg) == DMA_TIMING_NOW) {
114 dma->reg = GBADMARegisterClearEnable(dma->reg);
115
116 // Clear the enable bit in memory
117 memory->io[(DS_REG_DMA0CNT_HI + memory->activeDMA * (DS_REG_DMA1CNT_HI - DS_REG_DMA0CNT_HI)) >> 1] &= 0x7FE0;
118 }
119 if (GBADMARegisterGetDestControl(dma->reg) == DMA_INCREMENT_RELOAD) {
120 dma->nextDest = dma->dest;
121 }
122 if (GBADMARegisterIsDoIRQ(dma->reg)) {
123 DSRaiseIRQ(dscore->cpu, dscore->memory.io, DS_IRQ_DMA0 + memory->activeDMA);
124 }
125 DSDMAUpdate(dscore);
126 }
127}
128
129void DSDMAUpdate(struct DSCommon* dscore) {
130 int i;
131 struct DSCoreMemory* memory = &dscore->memory;
132 memory->activeDMA = -1;
133 uint32_t currentTime = mTimingCurrentTime(&dscore->timing);
134 for (i = 0; i < 4; ++i) {
135 struct GBADMA* dma = &memory->dma[i];
136 if (GBADMARegisterIsEnable(dma->reg) && dma->nextCount) {
137 memory->activeDMA = i;
138 break;
139 }
140 }
141
142 if (memory->activeDMA >= 0) {
143 mTimingDeschedule(&dscore->timing, &memory->dmaEvent);
144 mTimingSchedule(&dscore->timing, &memory->dmaEvent, memory->dma[memory->activeDMA].when - currentTime);
145 } else {
146 dscore->p->cpuBlocked = false;
147 }
148}
149
150void DSDMAService(struct DSCommon* dscore, int number, struct GBADMA* info) {
151 struct DSCoreMemory* memory = &dscore->memory;
152 struct ARMCore* cpu = dscore->cpu;
153 uint32_t width = 2 << GBADMARegisterGetWidth(info->reg);
154 int32_t wordsRemaining = info->nextCount;
155 uint32_t source = info->nextSource;
156 uint32_t dest = info->nextDest;
157 uint32_t sourceRegion = source >> DS_BASE_OFFSET;
158 uint32_t destRegion = dest >> DS_BASE_OFFSET;
159 int32_t cycles = 2;
160
161 if (info->count == info->nextCount) {
162 if (width == 4) {
163 cycles += dscore->memory.waitstatesNonseq32[sourceRegion] + dscore->memory.waitstatesNonseq32[destRegion];
164 } else {
165 cycles += dscore->memory.waitstatesNonseq16[sourceRegion] + dscore->memory.waitstatesNonseq16[destRegion];
166 }
167 source &= -width;
168 dest &= -width;
169 } else {
170 if (width == 4) {
171 cycles += dscore->memory.waitstatesSeq32[sourceRegion] + dscore->memory.waitstatesSeq32[destRegion];
172 } else {
173 cycles += dscore->memory.waitstatesSeq16[sourceRegion] + dscore->memory.waitstatesSeq16[destRegion];
174 }
175 }
176 info->when += cycles;
177
178 uint32_t word;
179 if (width == 4) {
180 word = cpu->memory.load32(cpu, source, 0);
181 cpu->memory.store32(cpu, dest, word, 0);
182 } else {
183 word = cpu->memory.load16(cpu, source, 0);
184 cpu->memory.store16(cpu, dest, word, 0);
185 }
186 int sourceOffset = DMA_OFFSET[GBADMARegisterGetSrcControl(info->reg)] * width;
187 int destOffset = DMA_OFFSET[GBADMARegisterGetDestControl(info->reg)] * width;
188 source += sourceOffset;
189 dest += destOffset;
190 --wordsRemaining;
191
192 info->nextCount = wordsRemaining;
193 info->nextSource = source;
194 info->nextDest = dest;
195 if (!wordsRemaining) {
196 info->nextCount |= 0x80000000;
197 }
198 DSDMAUpdate(dscore);
199}