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