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