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