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/core/interface.h>
9#include <mgba/internal/ds/ds.h>
10#include <mgba/internal/ds/ipc.h>
11#include <mgba/internal/ds/spi.h>
12
13mLOG_DEFINE_CATEGORY(DS_IO, "DS I/O");
14
15static void _DSHaltCNT(struct DSCommon* dscore, uint8_t value) {
16 switch (value >> 6) {
17 case 0:
18 default:
19 break;
20 case 1:
21 mLOG(DS_IO, STUB, "Enter GBA mode not supported");
22 break;
23 case 2:
24 ARMHalt(dscore->cpu);
25 break;
26 case 3:
27 mLOG(DS_IO, STUB, "Enter sleep mode not supported");
28 break;
29 }
30}
31
32static uint16_t _scheduleDiv(struct DS* ds, uint16_t control) {
33 mTimingDeschedule(&ds->ds9.timing, &ds->divEvent);
34 mTimingSchedule(&ds->ds9.timing, &ds->divEvent, (control & 3) ? 36 : 68);
35 return control | 0x8000;
36}
37
38static uint16_t _scheduleSqrt(struct DS* ds, uint16_t control) {
39 mTimingDeschedule(&ds->ds9.timing, &ds->sqrtEvent);
40 mTimingSchedule(&ds->ds9.timing, &ds->sqrtEvent, 26);
41 return control | 0x8000;
42}
43
44static uint32_t DSIOWrite(struct DSCommon* dscore, uint32_t address, uint16_t value) {
45 switch (address) {
46 // Video
47 case DS_REG_DISPSTAT:
48 DSVideoWriteDISPSTAT(dscore, value);
49 break;
50
51 // DMA Fill
52 case DS_REG_DMA0FILL_LO:
53 case DS_REG_DMA0FILL_HI:
54 case DS_REG_DMA1FILL_LO:
55 case DS_REG_DMA1FILL_HI:
56 case DS_REG_DMA2FILL_LO:
57 case DS_REG_DMA2FILL_HI:
58 case DS_REG_DMA3FILL_LO:
59 case DS_REG_DMA3FILL_HI:
60 break;
61
62 // Timers
63 case DS_REG_TM0CNT_LO:
64 GBATimerWriteTMCNT_LO(&dscore->timers[0], value);
65 return 0x20000;
66 case DS_REG_TM1CNT_LO:
67 GBATimerWriteTMCNT_LO(&dscore->timers[1], value);
68 return 0x20000;
69 case DS_REG_TM2CNT_LO:
70 GBATimerWriteTMCNT_LO(&dscore->timers[2], value);
71 return 0x20000;
72 case DS_REG_TM3CNT_LO:
73 GBATimerWriteTMCNT_LO(&dscore->timers[3], value);
74 return 0x20000;
75
76 case DS_REG_TM0CNT_HI:
77 value &= 0x00C7;
78 DSTimerWriteTMCNT_HI(&dscore->timers[0], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM0CNT_LO >> 1], value);
79 break;
80 case DS_REG_TM1CNT_HI:
81 value &= 0x00C7;
82 DSTimerWriteTMCNT_HI(&dscore->timers[1], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM1CNT_LO >> 1], value);
83 break;
84 case DS_REG_TM2CNT_HI:
85 value &= 0x00C7;
86 DSTimerWriteTMCNT_HI(&dscore->timers[2], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM2CNT_LO >> 1], value);
87 break;
88 case DS_REG_TM3CNT_HI:
89 value &= 0x00C7;
90 DSTimerWriteTMCNT_HI(&dscore->timers[3], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM3CNT_LO >> 1], value);
91 break;
92
93 // IPC
94 case DS_REG_IPCSYNC:
95 value &= 0x6F00;
96 value |= dscore->memory.io[address >> 1] & 0x000F;
97 DSIPCWriteSYNC(dscore->ipc->cpu, dscore->ipc->memory.io, value);
98 break;
99 case DS_REG_IPCFIFOCNT:
100 value = DSIPCWriteFIFOCNT(dscore, value);
101 break;
102
103 // Cart bus
104 case DS_REG_SLOT1CNT_LO:
105 mLOG(DS_IO, STUB, "ROM control not implemented");
106 value &= 0x7FFF;
107 break;
108
109 // Interrupts
110 case DS_REG_IME:
111 DSWriteIME(dscore->cpu, dscore->memory.io, value);
112 break;
113 case 0x20A:
114 value = 0;
115 // Some bad interrupt libraries will write to this
116 break;
117 case DS_REG_IF_LO:
118 case DS_REG_IF_HI:
119 value = dscore->memory.io[address >> 1] & ~value;
120 break;
121 default:
122 return 0;
123 }
124 return value | 0x10000;
125}
126
127static uint16_t DSIOReadKeyInput(struct DS* ds) {
128 uint16_t input = 0x3FF;
129 if (ds->keyCallback) {
130 input = ds->keyCallback->readKeys(ds->keyCallback);
131 } else if (ds->keySource) {
132 input = *ds->keySource;
133 }
134 // TODO: Put back
135 /*if (!dscore->p->allowOpposingDirections) {
136 unsigned rl = input & 0x030;
137 unsigned ud = input & 0x0C0;
138 input &= 0x30F;
139 if (rl != 0x030) {
140 input |= rl;
141 }
142 if (ud != 0x0C0) {
143 input |= ud;
144 }
145 }*/
146 return 0x3FF ^ input;
147}
148
149static void DSIOUpdateTimer(struct DSCommon* dscore, uint32_t address) {
150 switch (address) {
151 case DS_REG_TM0CNT_LO:
152 GBATimerUpdateRegisterInternal(&dscore->timers[0], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0);
153 break;
154 case DS_REG_TM1CNT_LO:
155 GBATimerUpdateRegisterInternal(&dscore->timers[1], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0);
156 break;
157 case DS_REG_TM2CNT_LO:
158 GBATimerUpdateRegisterInternal(&dscore->timers[2], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0);
159 break;
160 case DS_REG_TM3CNT_LO:
161 GBATimerUpdateRegisterInternal(&dscore->timers[3], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0);
162 break;
163 }
164}
165
166void DS7IOInit(struct DS* ds) {
167 memset(ds->memory.io7, 0, sizeof(ds->memory.io7));
168 ds->memory.io7[DS_REG_IPCFIFOCNT >> 1] = 0x0101;
169 ds->memory.io7[DS_REG_POSTFLG >> 1] = 0x0001;
170}
171
172void DS7IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
173 switch (address) {
174 case DS7_REG_SPICNT:
175 value &= 0xCF83;
176 value = DSSPIWriteControl(ds, value);
177 break;
178 case DS7_REG_SPIDATA:
179 DSSPIWrite(ds, value);
180 return;
181 default:
182 {
183 uint32_t v2 = DSIOWrite(&ds->ds7, address, value);
184 if (v2 & 0x10000) {
185 value = v2;
186 break;
187 } else if (v2 & 0x20000) {
188 return;
189 }
190 }
191 mLOG(DS_IO, STUB, "Stub DS7 I/O register write: %06X:%04X", address, value);
192 if (address >= DS7_REG_MAX) {
193 mLOG(DS_IO, GAME_ERROR, "Write to unused DS7 I/O register: %06X:%04X", address, value);
194 return;
195 }
196 break;
197 }
198 ds->memory.io7[address >> 1] = value;
199}
200
201void DS7IOWrite8(struct DS* ds, uint32_t address, uint8_t value) {
202 if (address == DS7_REG_HALTCNT) {
203 _DSHaltCNT(&ds->ds7, value);
204 return;
205 }
206 if (address < DS7_REG_MAX) {
207 uint16_t value16 = value << (8 * (address & 1));
208 value16 |= (ds->ds7.memory.io[(address & 0xFFF) >> 1]) & ~(0xFF << (8 * (address & 1)));
209 DS7IOWrite(ds, address & 0xFFFFFFFE, value16);
210 } else {
211 mLOG(DS, STUB, "Writing to unknown DS7 register: %08X:%02X", address, value);
212 }
213}
214
215void DS7IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
216 switch (address) {
217 case DS_REG_DMA0SAD_LO:
218 value = DSDMAWriteSAD(&ds->ds7, 0, value);
219 break;
220 case DS_REG_DMA1SAD_LO:
221 value = DSDMAWriteSAD(&ds->ds7, 1, value);
222 break;
223 case DS_REG_DMA2SAD_LO:
224 value = DSDMAWriteSAD(&ds->ds7, 2, value);
225 break;
226 case DS_REG_DMA3SAD_LO:
227 value = DSDMAWriteSAD(&ds->ds7, 3, value);
228 break;
229
230 case DS_REG_DMA0DAD_LO:
231 value = DSDMAWriteDAD(&ds->ds7, 0, value);
232 break;
233 case DS_REG_DMA1DAD_LO:
234 value = DSDMAWriteDAD(&ds->ds7, 1, value);
235 break;
236 case DS_REG_DMA2DAD_LO:
237 value = DSDMAWriteDAD(&ds->ds7, 2, value);
238 break;
239 case DS_REG_DMA3DAD_LO:
240 value = DSDMAWriteDAD(&ds->ds7, 3, value);
241 break;
242
243 case DS_REG_DMA0CNT_LO:
244 DS7DMAWriteCNT(&ds->ds7, 0, value);
245 break;
246 case DS_REG_DMA1CNT_LO:
247 DS7DMAWriteCNT(&ds->ds7, 1, value);
248 break;
249 case DS_REG_DMA2CNT_LO:
250 DS7DMAWriteCNT(&ds->ds7, 2, value);
251 break;
252 case DS_REG_DMA3CNT_LO:
253 DS7DMAWriteCNT(&ds->ds7, 3, value);
254 break;
255
256 case DS_REG_IPCFIFOSEND_LO:
257 DSIPCWriteFIFO(&ds->ds7, value);
258 break;
259 case DS_REG_IE_LO:
260 DSWriteIE(ds->ds7.cpu, ds->ds7.memory.io, value);
261 break;
262 default:
263 DS7IOWrite(ds, address, value & 0xFFFF);
264 DS7IOWrite(ds, address | 2, value >> 16);
265 return;
266 }
267 ds->ds7.memory.io[address >> 1] = value;
268 ds->ds7.memory.io[(address >> 1) + 1] = value >> 16;
269}
270
271uint16_t DS7IORead(struct DS* ds, uint32_t address) {
272 switch (address) {
273 case DS_REG_TM0CNT_LO:
274 case DS_REG_TM1CNT_LO:
275 case DS_REG_TM2CNT_LO:
276 case DS_REG_TM3CNT_LO:
277 DSIOUpdateTimer(&ds->ds7, address);
278 break;
279 case DS_REG_KEYINPUT:
280 return DSIOReadKeyInput(ds);
281 case DS_REG_DMA0FILL_LO:
282 case DS_REG_DMA0FILL_HI:
283 case DS_REG_DMA1FILL_LO:
284 case DS_REG_DMA1FILL_HI:
285 case DS_REG_DMA2FILL_LO:
286 case DS_REG_DMA2FILL_HI:
287 case DS_REG_DMA3FILL_LO:
288 case DS_REG_DMA3FILL_HI:
289 case DS_REG_TM0CNT_HI:
290 case DS_REG_TM1CNT_HI:
291 case DS_REG_TM2CNT_HI:
292 case DS_REG_TM3CNT_HI:
293 case DS7_REG_SPICNT:
294 case DS7_REG_SPIDATA:
295 case DS_REG_IPCSYNC:
296 case DS_REG_IPCFIFOCNT:
297 case DS_REG_IME:
298 case 0x20A:
299 case DS_REG_IE_LO:
300 case DS_REG_IE_HI:
301 case DS_REG_IF_LO:
302 case DS_REG_IF_HI:
303 // Handled transparently by the registers
304 break;
305 default:
306 mLOG(DS_IO, STUB, "Stub DS7 I/O register read: %06X", address);
307 }
308 if (address < DS7_REG_MAX) {
309 return ds->memory.io7[address >> 1];
310 }
311 return 0;
312}
313
314uint32_t DS7IORead32(struct DS* ds, uint32_t address) {
315 switch (address) {
316 case DS_REG_IPCFIFORECV_LO:
317 return DSIPCReadFIFO(&ds->ds7);
318 default:
319 return DS7IORead(ds, address & 0x00FFFFFC) | (DS7IORead(ds, (address & 0x00FFFFFC) | 2) << 16);
320 }
321}
322
323void DS9IOInit(struct DS* ds) {
324 memset(ds->memory.io9, 0, sizeof(ds->memory.io9));
325 ds->memory.io9[DS_REG_IPCFIFOCNT >> 1] = 0x0101;
326 ds->memory.io9[DS_REG_POSTFLG >> 1] = 0x0001;
327 ds->memory.io9[DS9_REG_VRAMCNT_G >> 1] = 0x0300;
328}
329
330void DS9IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
331 switch (address) {
332 // VRAM control
333 case DS9_REG_VRAMCNT_A:
334 case DS9_REG_VRAMCNT_C:
335 case DS9_REG_VRAMCNT_E:
336 value &= 0x9F9F;
337 DSVideoConfigureVRAM(&ds->memory, address - DS9_REG_VRAMCNT_A, value & 0xFF);
338 DSVideoConfigureVRAM(&ds->memory, address - DS9_REG_VRAMCNT_A + 1, value >> 8);
339 break;
340 case DS9_REG_VRAMCNT_G:
341 value &= 0x9F03;
342 DSVideoConfigureVRAM(&ds->memory, 6, value & 0xFF);
343 DSConfigureWRAM(&ds->memory, value >> 8);
344 break;
345 case DS9_REG_VRAMCNT_H:
346 value &= 0x9F9F;
347 DSVideoConfigureVRAM(&ds->memory, 7, value & 0xFF);
348 DSVideoConfigureVRAM(&ds->memory, 8, value >> 8);
349 break;
350
351 // Math
352 case DS9_REG_DIVCNT:
353 value = _scheduleDiv(ds, value);
354 break;
355 case DS9_REG_DIV_NUMER_0:
356 case DS9_REG_DIV_NUMER_1:
357 case DS9_REG_DIV_NUMER_2:
358 case DS9_REG_DIV_NUMER_3:
359 case DS9_REG_DIV_DENOM_0:
360 case DS9_REG_DIV_DENOM_1:
361 case DS9_REG_DIV_DENOM_2:
362 case DS9_REG_DIV_DENOM_3:
363 ds->memory.io9[DS9_REG_DIVCNT >> 1] = _scheduleDiv(ds, ds->memory.io9[DS9_REG_DIVCNT >> 1]);
364 break;
365 case DS9_REG_SQRTCNT:
366 value = _scheduleSqrt(ds, value);
367 break;
368 case DS9_REG_SQRT_PARAM_0:
369 case DS9_REG_SQRT_PARAM_1:
370 case DS9_REG_SQRT_PARAM_2:
371 case DS9_REG_SQRT_PARAM_3:
372 ds->memory.io9[DS9_REG_SQRTCNT >> 1] = _scheduleSqrt(ds, ds->memory.io9[DS9_REG_SQRTCNT >> 1]);
373 break;
374
375 default:
376 {
377 uint32_t v2 = DSIOWrite(&ds->ds9, address, value);
378 if (v2 & 0x10000) {
379 value = v2;
380 break;
381 } else if (v2 & 0x20000) {
382 return;
383 }
384 }
385 mLOG(DS_IO, STUB, "Stub DS9 I/O register write: %06X:%04X", address, value);
386 if (address >= DS7_REG_MAX) {
387 mLOG(DS_IO, GAME_ERROR, "Write to unused DS9 I/O register: %06X:%04X", address, value);
388 return;
389 }
390 break;
391 }
392 ds->memory.io9[address >> 1] = value;
393}
394
395void DS9IOWrite8(struct DS* ds, uint32_t address, uint8_t value) {
396 if (address < DS9_REG_MAX) {
397 uint16_t value16 = value << (8 * (address & 1));
398 value16 |= (ds->memory.io9[(address & 0x1FFF) >> 1]) & ~(0xFF << (8 * (address & 1)));
399 DS9IOWrite(ds, address & 0xFFFFFFFE, value16);
400 } else {
401 mLOG(DS, STUB, "Writing to unknown DS9 register: %08X:%02X", address, value);
402 }
403}
404
405void DS9IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
406 switch (address) {
407 case DS_REG_DMA0SAD_LO:
408 value = DSDMAWriteSAD(&ds->ds9, 0, value);
409 break;
410 case DS_REG_DMA1SAD_LO:
411 value = DSDMAWriteSAD(&ds->ds9, 1, value);
412 break;
413 case DS_REG_DMA2SAD_LO:
414 value = DSDMAWriteSAD(&ds->ds9, 2, value);
415 break;
416 case DS_REG_DMA3SAD_LO:
417 value = DSDMAWriteSAD(&ds->ds9, 3, value);
418 break;
419
420 case DS_REG_DMA0DAD_LO:
421 value = DSDMAWriteDAD(&ds->ds9, 0, value);
422 break;
423 case DS_REG_DMA1DAD_LO:
424 value = DSDMAWriteDAD(&ds->ds9, 1, value);
425 break;
426 case DS_REG_DMA2DAD_LO:
427 value = DSDMAWriteDAD(&ds->ds9, 2, value);
428 break;
429 case DS_REG_DMA3DAD_LO:
430 value = DSDMAWriteDAD(&ds->ds9, 3, value);
431 break;
432
433 case DS_REG_DMA0CNT_LO:
434 DS9DMAWriteCNT(&ds->ds9, 0, value);
435 break;
436 case DS_REG_DMA1CNT_LO:
437 DS9DMAWriteCNT(&ds->ds9, 1, value);
438 break;
439 case DS_REG_DMA2CNT_LO:
440 DS9DMAWriteCNT(&ds->ds9, 2, value);
441 break;
442 case DS_REG_DMA3CNT_LO:
443 DS9DMAWriteCNT(&ds->ds9, 3, value);
444 break;
445
446 case DS_REG_IPCFIFOSEND_LO:
447 DSIPCWriteFIFO(&ds->ds9, value);
448 break;
449 case DS_REG_IE_LO:
450 DSWriteIE(ds->ds9.cpu, ds->ds9.memory.io, value);
451 break;
452 default:
453 DS9IOWrite(ds, address, value & 0xFFFF);
454 DS9IOWrite(ds, address | 2, value >> 16);
455 return;
456 }
457 ds->ds9.memory.io[address >> 1] = value;
458 ds->ds9.memory.io[(address >> 1) + 1] = value >> 16;
459}
460
461uint16_t DS9IORead(struct DS* ds, uint32_t address) {
462 switch (address) {
463 case DS_REG_TM0CNT_LO:
464 case DS_REG_TM1CNT_LO:
465 case DS_REG_TM2CNT_LO:
466 case DS_REG_TM3CNT_LO:
467 DSIOUpdateTimer(&ds->ds9, address);
468 break;
469 case DS_REG_KEYINPUT:
470 return DSIOReadKeyInput(ds);
471 case DS_REG_DMA0FILL_LO:
472 case DS_REG_DMA0FILL_HI:
473 case DS_REG_DMA1FILL_LO:
474 case DS_REG_DMA1FILL_HI:
475 case DS_REG_DMA2FILL_LO:
476 case DS_REG_DMA2FILL_HI:
477 case DS_REG_DMA3FILL_LO:
478 case DS_REG_DMA3FILL_HI:
479 case DS_REG_TM0CNT_HI:
480 case DS_REG_TM1CNT_HI:
481 case DS_REG_TM2CNT_HI:
482 case DS_REG_TM3CNT_HI:
483 case DS_REG_IPCSYNC:
484 case DS_REG_IPCFIFOCNT:
485 case DS_REG_IME:
486 case 0x20A:
487 case DS_REG_IE_LO:
488 case DS_REG_IE_HI:
489 case DS_REG_IF_LO:
490 case DS_REG_IF_HI:
491 case DS9_REG_DIVCNT:
492 case DS9_REG_DIV_NUMER_0:
493 case DS9_REG_DIV_NUMER_1:
494 case DS9_REG_DIV_NUMER_2:
495 case DS9_REG_DIV_NUMER_3:
496 case DS9_REG_DIV_DENOM_0:
497 case DS9_REG_DIV_DENOM_1:
498 case DS9_REG_DIV_DENOM_2:
499 case DS9_REG_DIV_DENOM_3:
500 case DS9_REG_DIV_RESULT_0:
501 case DS9_REG_DIV_RESULT_1:
502 case DS9_REG_DIV_RESULT_2:
503 case DS9_REG_DIV_RESULT_3:
504 case DS9_REG_DIVREM_RESULT_0:
505 case DS9_REG_DIVREM_RESULT_1:
506 case DS9_REG_DIVREM_RESULT_2:
507 case DS9_REG_DIVREM_RESULT_3:
508 case DS9_REG_SQRTCNT:
509 case DS9_REG_SQRT_PARAM_0:
510 case DS9_REG_SQRT_PARAM_1:
511 case DS9_REG_SQRT_PARAM_2:
512 case DS9_REG_SQRT_PARAM_3:
513 case DS9_REG_SQRT_RESULT_LO:
514 case DS9_REG_SQRT_RESULT_HI:
515 // Handled transparently by the registers
516 break;
517 default:
518 mLOG(DS_IO, STUB, "Stub DS9 I/O register read: %06X", address);
519 }
520 if (address < DS9_REG_MAX) {
521 return ds->ds9.memory.io[address >> 1];
522 }
523 return 0;
524}
525
526uint32_t DS9IORead32(struct DS* ds, uint32_t address) {
527 switch (address) {
528 case DS_REG_IPCFIFORECV_LO:
529 return DSIPCReadFIFO(&ds->ds9);
530 default:
531 return DS9IORead(ds, address & 0x00FFFFFC) | (DS9IORead(ds, (address & 0x00FFFFFC) | 2) << 16);
532 }
533}