src/ds/io.c (view raw)
1/* Copyright (c) 2013-2016 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/io.h>
7
8#include <mgba/internal/ds/ds.h>
9#include <mgba/internal/ds/ipc.h>
10
11mLOG_DEFINE_CATEGORY(DS_IO, "DS I/O");
12
13static void _DSHaltCNT(struct DSCommon* dscore, uint8_t value) {
14 switch (value >> 6) {
15 case 0:
16 default:
17 break;
18 case 1:
19 mLOG(DS_IO, STUB, "Enter GBA mode not supported");
20 break;
21 case 2:
22 ARMHalt(dscore->cpu);
23 break;
24 case 3:
25 mLOG(DS_IO, STUB, "Enter sleep mode not supported");
26 break;
27 }
28}
29
30static uint32_t DSIOWrite(struct DSCommon* dscore, uint32_t address, uint16_t value) {
31 switch (address) {
32 // DMA Fill
33 case DS_REG_DMA0FILL_LO:
34 case DS_REG_DMA0FILL_HI:
35 case DS_REG_DMA1FILL_LO:
36 case DS_REG_DMA1FILL_HI:
37 case DS_REG_DMA2FILL_LO:
38 case DS_REG_DMA2FILL_HI:
39 case DS_REG_DMA3FILL_LO:
40 case DS_REG_DMA3FILL_HI:
41 break;
42
43 // Timers
44 case DS_REG_TM0CNT_LO:
45 GBATimerWriteTMCNT_LO(&dscore->timers[0], value);
46 return 0x20000;
47 case DS_REG_TM1CNT_LO:
48 GBATimerWriteTMCNT_LO(&dscore->timers[1], value);
49 return 0x20000;
50 case DS_REG_TM2CNT_LO:
51 GBATimerWriteTMCNT_LO(&dscore->timers[2], value);
52 return 0x20000;
53 case DS_REG_TM3CNT_LO:
54 GBATimerWriteTMCNT_LO(&dscore->timers[3], value);
55 return 0x20000;
56
57 case DS_REG_TM0CNT_HI:
58 value &= 0x00C7;
59 DSTimerWriteTMCNT_HI(&dscore->timers[0], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM0CNT_LO >> 1], value);
60 break;
61 case DS_REG_TM1CNT_HI:
62 value &= 0x00C7;
63 DSTimerWriteTMCNT_HI(&dscore->timers[1], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM1CNT_LO >> 1], value);
64 break;
65 case DS_REG_TM2CNT_HI:
66 value &= 0x00C7;
67 DSTimerWriteTMCNT_HI(&dscore->timers[2], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM2CNT_LO >> 1], value);
68 break;
69 case DS_REG_TM3CNT_HI:
70 value &= 0x00C7;
71 DSTimerWriteTMCNT_HI(&dscore->timers[3], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM3CNT_LO >> 1], value);
72 break;
73
74 case DS_REG_IPCSYNC:
75 value &= 0x6F00;
76 value |= dscore->memory.io[address >> 1] & 0x000F;
77 DSIPCWriteSYNC(dscore->ipc->cpu, dscore->ipc->memory.io, value);
78 break;
79 case DS_REG_IPCFIFOCNT:
80 value = DSIPCWriteFIFOCNT(dscore, value);
81 break;
82 case DS_REG_IME:
83 DSWriteIME(dscore->cpu, dscore->memory.io, value);
84 break;
85 case DS_REG_IF_LO:
86 case DS_REG_IF_HI:
87 value = dscore->memory.io[address >> 1] & ~value;
88 break;
89 default:
90 return 0;
91 }
92 return value | 0x10000;
93}
94
95static void DSIOUpdateTimer(struct DSCommon* dscore, uint32_t address) {
96 switch (address) {
97 case DS_REG_TM0CNT_LO:
98 GBATimerUpdateRegisterInternal(&dscore->timers[0], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0);
99 break;
100 case DS_REG_TM1CNT_LO:
101 GBATimerUpdateRegisterInternal(&dscore->timers[1], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0);
102 break;
103 case DS_REG_TM2CNT_LO:
104 GBATimerUpdateRegisterInternal(&dscore->timers[2], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0);
105 break;
106 case DS_REG_TM3CNT_LO:
107 GBATimerUpdateRegisterInternal(&dscore->timers[3], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0);
108 break;
109 }
110}
111
112void DS7IOInit(struct DS* ds) {
113 memset(ds->memory.io7, 0, sizeof(ds->memory.io7));
114}
115
116void DS7IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
117 switch (address) {
118 default:
119 {
120 uint32_t v2 = DSIOWrite(&ds->ds7, address, value);
121 if (v2 & 0x10000) {
122 value = v2;
123 break;
124 } else if (v2 & 0x20000) {
125 return;
126 }
127 }
128 mLOG(DS_IO, STUB, "Stub DS7 I/O register write: %06X:%04X", address, value);
129 if (address >= DS7_REG_MAX) {
130 mLOG(DS_IO, GAME_ERROR, "Write to unused DS7 I/O register: %06X:%04X", address, value);
131 return;
132 }
133 break;
134 }
135 ds->memory.io7[address >> 1] = value;
136}
137
138void DS7IOWrite8(struct DS* ds, uint32_t address, uint8_t value) {
139 if (address == DS7_REG_HALTCNT) {
140 _DSHaltCNT(&ds->ds7, value);
141 return;
142 }
143 if (address < DS7_REG_MAX) {
144 uint16_t value16 = value << (8 * (address & 1));
145 value16 |= (ds->ds7.memory.io[(address & 0xFFF) >> 1]) & ~(0xFF << (8 * (address & 1)));
146 DS7IOWrite(ds, address & 0xFFFFFFFE, value16);
147 } else {
148 mLOG(DS, STUB, "Writing to unknown DS7 register: %08X:%02X", address, value);
149 }
150}
151
152void DS7IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
153 switch (address) {
154 case DS_REG_DMA0SAD_LO:
155 value = DSDMAWriteSAD(&ds->ds7, 0, value);
156 break;
157 case DS_REG_DMA1SAD_LO:
158 value = DSDMAWriteSAD(&ds->ds7, 1, value);
159 break;
160 case DS_REG_DMA2SAD_LO:
161 value = DSDMAWriteSAD(&ds->ds7, 2, value);
162 break;
163 case DS_REG_DMA3SAD_LO:
164 value = DSDMAWriteSAD(&ds->ds7, 3, value);
165 break;
166
167 case DS_REG_DMA0DAD_LO:
168 value = DSDMAWriteDAD(&ds->ds7, 0, value);
169 break;
170 case DS_REG_DMA1DAD_LO:
171 value = DSDMAWriteDAD(&ds->ds7, 1, value);
172 break;
173 case DS_REG_DMA2DAD_LO:
174 value = DSDMAWriteDAD(&ds->ds7, 2, value);
175 break;
176 case DS_REG_DMA3DAD_LO:
177 value = DSDMAWriteDAD(&ds->ds7, 3, value);
178 break;
179
180 case DS_REG_DMA0CNT_LO:
181 DS7DMAWriteCNT(&ds->ds7, 0, value);
182 break;
183 case DS_REG_DMA1CNT_LO:
184 DS7DMAWriteCNT(&ds->ds7, 1, value);
185 break;
186 case DS_REG_DMA2CNT_LO:
187 DS7DMAWriteCNT(&ds->ds7, 2, value);
188 break;
189 case DS_REG_DMA3CNT_LO:
190 DS7DMAWriteCNT(&ds->ds7, 3, value);
191 break;
192
193 case DS_REG_IPCFIFOSEND_LO:
194 DSIPCWriteFIFO(&ds->ds7, value);
195 break;
196 case DS_REG_IE_LO:
197 DSWriteIE(ds->ds7.cpu, ds->ds7.memory.io, value);
198 break;
199 default:
200 DS7IOWrite(ds, address, value & 0xFFFF);
201 DS7IOWrite(ds, address | 2, value >> 16);
202 return;
203 }
204 ds->ds7.memory.io[address >> 1] = value;
205 ds->ds7.memory.io[(address >> 1) + 1] = value >> 16;
206}
207
208uint16_t DS7IORead(struct DS* ds, uint32_t address) {
209 switch (address) {
210 case DS_REG_TM0CNT_LO:
211 case DS_REG_TM1CNT_LO:
212 case DS_REG_TM2CNT_LO:
213 case DS_REG_TM3CNT_LO:
214 DSIOUpdateTimer(&ds->ds7, address);
215 break;
216 case DS_REG_DMA0FILL_LO:
217 case DS_REG_DMA0FILL_HI:
218 case DS_REG_DMA1FILL_LO:
219 case DS_REG_DMA1FILL_HI:
220 case DS_REG_DMA2FILL_LO:
221 case DS_REG_DMA2FILL_HI:
222 case DS_REG_DMA3FILL_LO:
223 case DS_REG_DMA3FILL_HI:
224 case DS_REG_TM0CNT_HI:
225 case DS_REG_TM1CNT_HI:
226 case DS_REG_TM2CNT_HI:
227 case DS_REG_TM3CNT_HI:
228 case DS_REG_IPCSYNC:
229 case DS_REG_IPCFIFOCNT:
230 case DS_REG_IME:
231 case DS_REG_IE_LO:
232 case DS_REG_IE_HI:
233 case DS_REG_IF_LO:
234 case DS_REG_IF_HI:
235 // Handled transparently by the registers
236 break;
237 default:
238 mLOG(DS_IO, STUB, "Stub DS7 I/O register read: %06X", address);
239 }
240 if (address < DS7_REG_MAX) {
241 return ds->memory.io7[address >> 1];
242 }
243 return 0;
244}
245
246uint32_t DS7IORead32(struct DS* ds, uint32_t address) {
247 switch (address) {
248 case DS_REG_IPCFIFORECV_LO:
249 return DSIPCReadFIFO(&ds->ds7);
250 default:
251 return DS7IORead(ds, address & 0x00FFFFFC) | (DS7IORead(ds, (address & 0x00FFFFFC) | 2) << 16);
252 }
253}
254
255void DS9IOInit(struct DS* ds) {
256 memset(ds->memory.io9, 0, sizeof(ds->memory.io9));
257}
258
259void DS9IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
260 switch (address) {
261 default:
262 {
263 uint32_t v2 = DSIOWrite(&ds->ds9, address, value);
264 if (v2 & 0x10000) {
265 value = v2;
266 break;
267 } else if (v2 & 0x20000) {
268 return;
269 }
270 }
271 mLOG(DS_IO, STUB, "Stub DS9 I/O register write: %06X:%04X", address, value);
272 if (address >= DS7_REG_MAX) {
273 mLOG(DS_IO, GAME_ERROR, "Write to unused DS9 I/O register: %06X:%04X", address, value);
274 return;
275 }
276 break;
277 }
278 ds->memory.io9[address >> 1] = value;
279}
280
281void DS9IOWrite8(struct DS* ds, uint32_t address, uint8_t value) {
282 if (address < DS9_REG_MAX) {
283 uint16_t value16 = value << (8 * (address & 1));
284 value16 |= (ds->memory.io9[(address & 0x1FFF) >> 1]) & ~(0xFF << (8 * (address & 1)));
285 DS9IOWrite(ds, address & 0xFFFFFFFE, value16);
286 } else {
287 mLOG(DS, STUB, "Writing to unknown DS9 register: %08X:%02X", address, value);
288 }
289}
290
291void DS9IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
292 switch (address) {
293 case DS_REG_DMA0SAD_LO:
294 value = DSDMAWriteSAD(&ds->ds9, 0, value);
295 break;
296 case DS_REG_DMA1SAD_LO:
297 value = DSDMAWriteSAD(&ds->ds9, 1, value);
298 break;
299 case DS_REG_DMA2SAD_LO:
300 value = DSDMAWriteSAD(&ds->ds9, 2, value);
301 break;
302 case DS_REG_DMA3SAD_LO:
303 value = DSDMAWriteSAD(&ds->ds9, 3, value);
304 break;
305
306 case DS_REG_DMA0DAD_LO:
307 value = DSDMAWriteDAD(&ds->ds9, 0, value);
308 break;
309 case DS_REG_DMA1DAD_LO:
310 value = DSDMAWriteDAD(&ds->ds9, 1, value);
311 break;
312 case DS_REG_DMA2DAD_LO:
313 value = DSDMAWriteDAD(&ds->ds9, 2, value);
314 break;
315 case DS_REG_DMA3DAD_LO:
316 value = DSDMAWriteDAD(&ds->ds9, 3, value);
317 break;
318
319 case DS_REG_DMA0CNT_LO:
320 DS9DMAWriteCNT(&ds->ds9, 0, value);
321 break;
322 case DS_REG_DMA1CNT_LO:
323 DS9DMAWriteCNT(&ds->ds9, 1, value);
324 break;
325 case DS_REG_DMA2CNT_LO:
326 DS9DMAWriteCNT(&ds->ds9, 2, value);
327 break;
328 case DS_REG_DMA3CNT_LO:
329 DS9DMAWriteCNT(&ds->ds9, 3, value);
330 break;
331
332 case DS_REG_IPCFIFOSEND_LO:
333 DSIPCWriteFIFO(&ds->ds9, value);
334 break;
335 case DS_REG_IE_LO:
336 DSWriteIE(ds->ds9.cpu, ds->ds9.memory.io, value);
337 break;
338 default:
339 DS9IOWrite(ds, address, value & 0xFFFF);
340 DS9IOWrite(ds, address | 2, value >> 16);
341 return;
342 }
343 ds->ds9.memory.io[address >> 1] = value;
344 ds->ds9.memory.io[(address >> 1) + 1] = value >> 16;
345}
346
347uint16_t DS9IORead(struct DS* ds, uint32_t address) {
348 switch (address) {
349 case DS_REG_TM0CNT_LO:
350 case DS_REG_TM1CNT_LO:
351 case DS_REG_TM2CNT_LO:
352 case DS_REG_TM3CNT_LO:
353 DSIOUpdateTimer(&ds->ds9, address);
354 break;
355 case DS_REG_DMA0FILL_LO:
356 case DS_REG_DMA0FILL_HI:
357 case DS_REG_DMA1FILL_LO:
358 case DS_REG_DMA1FILL_HI:
359 case DS_REG_DMA2FILL_LO:
360 case DS_REG_DMA2FILL_HI:
361 case DS_REG_DMA3FILL_LO:
362 case DS_REG_DMA3FILL_HI:
363 case DS_REG_TM0CNT_HI:
364 case DS_REG_TM1CNT_HI:
365 case DS_REG_TM2CNT_HI:
366 case DS_REG_TM3CNT_HI:
367 case DS_REG_IPCSYNC:
368 case DS_REG_IPCFIFOCNT:
369 case DS_REG_IME:
370 case DS_REG_IE_LO:
371 case DS_REG_IE_HI:
372 case DS_REG_IF_LO:
373 case DS_REG_IF_HI:
374 // Handled transparently by the registers
375 break;
376 default:
377 mLOG(DS_IO, STUB, "Stub DS9 I/O register read: %06X", address);
378 }
379 if (address < DS9_REG_MAX) {
380 return ds->ds9.memory.io[address >> 1];
381 }
382 return 0;
383}
384
385uint32_t DS9IORead32(struct DS* ds, uint32_t address) {
386 switch (address) {
387 case DS_REG_IPCFIFORECV_LO:
388 return DSIPCReadFIFO(&ds->ds9);
389 default:
390 return DS9IORead(ds, address & 0x00FFFFFC) | (DS9IORead(ds, (address & 0x00FFFFFC) | 2) << 16);
391 }
392}