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