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_VCOUNT:
345 case DS_REG_DMA0FILL_LO:
346 case DS_REG_DMA0FILL_HI:
347 case DS_REG_DMA1FILL_LO:
348 case DS_REG_DMA1FILL_HI:
349 case DS_REG_DMA2FILL_LO:
350 case DS_REG_DMA2FILL_HI:
351 case DS_REG_DMA3FILL_LO:
352 case DS_REG_DMA3FILL_HI:
353 case DS_REG_TM0CNT_HI:
354 case DS_REG_TM1CNT_HI:
355 case DS_REG_TM2CNT_HI:
356 case DS_REG_TM3CNT_HI:
357 case DS7_REG_SPICNT:
358 case DS7_REG_SPIDATA:
359 case DS_REG_IPCSYNC:
360 case DS_REG_IPCFIFOCNT:
361 case DS_REG_ROMCNT_LO:
362 case DS_REG_ROMCNT_HI:
363 case DS_REG_IME:
364 case 0x20A:
365 case DS_REG_IE_LO:
366 case DS_REG_IE_HI:
367 case DS_REG_IF_LO:
368 case DS_REG_IF_HI:
369 case DS_REG_POSTFLG:
370 // Handled transparently by the registers
371 break;
372 default:
373 mLOG(DS_IO, STUB, "Stub DS7 I/O register read: %06X", address);
374 }
375 if (address < DS7_REG_MAX) {
376 return ds->memory.io7[address >> 1];
377 }
378 return 0;
379}
380
381uint32_t DS7IORead32(struct DS* ds, uint32_t address) {
382 switch (address) {
383 case DS_REG_IPCFIFORECV_LO:
384 return DSIPCReadFIFO(&ds->ds7);
385 case DS_REG_ROMDATA_0:
386 if (ds->ds7.memory.slot1Access) {
387 return DSSlot1Read(ds);
388 } else {
389 mLOG(DS_IO, GAME_ERROR, "Invalid cart access");
390 return 0;
391 }
392 default:
393 return DS7IORead(ds, address & 0x00FFFFFC) | (DS7IORead(ds, (address & 0x00FFFFFC) | 2) << 16);
394 }
395}
396
397void DS9IOInit(struct DS* ds) {
398 memset(ds->memory.io9, 0, sizeof(ds->memory.io9));
399 ds->memory.io9[DS_REG_IPCFIFOCNT >> 1] = 0x0101;
400 ds->memory.io9[DS_REG_POSTFLG >> 1] = 0x0001;
401 DS9IOWrite(ds, DS9_REG_VRAMCNT_G, 0x0300);
402}
403
404void DS9IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
405 if (address <= DS9_REG_A_BLDY && (address > DS_REG_VCOUNT || address == DS9_REG_A_DISPCNT_LO || address == DS9_REG_A_DISPCNT_HI)) {
406 value = ds->video.renderer->writeVideoRegister(ds->video.renderer, address, value);
407 } else if (address >= DS9_REG_B_DISPCNT_LO && address <= DS9_REG_B_BLDY) {
408 value = ds->video.renderer->writeVideoRegister(ds->video.renderer, address, value);
409 } else {
410 switch (address) {
411 // VRAM control
412 case DS9_REG_VRAMCNT_A:
413 case DS9_REG_VRAMCNT_C:
414 case DS9_REG_VRAMCNT_E:
415 value &= 0x9F9F;
416 DSVideoConfigureVRAM(ds, address - DS9_REG_VRAMCNT_A, value & 0xFF);
417 DSVideoConfigureVRAM(ds, address - DS9_REG_VRAMCNT_A + 1, value >> 8);
418 break;
419 case DS9_REG_VRAMCNT_G:
420 value &= 0x9F03;
421 DSVideoConfigureVRAM(ds, 6, value & 0xFF);
422 DSConfigureWRAM(&ds->memory, value >> 8);
423 break;
424 case DS9_REG_VRAMCNT_H:
425 value &= 0x9F9F;
426 DSVideoConfigureVRAM(ds, 7, value & 0xFF);
427 DSVideoConfigureVRAM(ds, 8, value >> 8);
428 break;
429
430 case DS9_REG_EXMEMCNT:
431 value &= 0xE8FF;
432 DSConfigureExternalMemory(ds, value);
433 break;
434
435 // Math
436 case DS9_REG_DIVCNT:
437 value = _scheduleDiv(ds, value);
438 break;
439 case DS9_REG_DIV_NUMER_0:
440 case DS9_REG_DIV_NUMER_1:
441 case DS9_REG_DIV_NUMER_2:
442 case DS9_REG_DIV_NUMER_3:
443 case DS9_REG_DIV_DENOM_0:
444 case DS9_REG_DIV_DENOM_1:
445 case DS9_REG_DIV_DENOM_2:
446 case DS9_REG_DIV_DENOM_3:
447 ds->memory.io9[DS9_REG_DIVCNT >> 1] = _scheduleDiv(ds, ds->memory.io9[DS9_REG_DIVCNT >> 1]);
448 break;
449 case DS9_REG_SQRTCNT:
450 value = _scheduleSqrt(ds, value);
451 break;
452 case DS9_REG_SQRT_PARAM_0:
453 case DS9_REG_SQRT_PARAM_1:
454 case DS9_REG_SQRT_PARAM_2:
455 case DS9_REG_SQRT_PARAM_3:
456 ds->memory.io9[DS9_REG_SQRTCNT >> 1] = _scheduleSqrt(ds, ds->memory.io9[DS9_REG_SQRTCNT >> 1]);
457 break;
458
459 // High Video
460 case DS9_REG_POWCNT1:
461 value = ds->video.renderer->writeVideoRegister(ds->video.renderer, address, value);
462 break;
463
464 default:
465 {
466 uint32_t v2 = DSIOWrite(&ds->ds9, address, value);
467 if (v2 & 0x10000) {
468 value = v2;
469 break;
470 } else if (v2 & 0x20000) {
471 return;
472 }
473 }
474 mLOG(DS_IO, STUB, "Stub DS9 I/O register write: %06X:%04X", address, value);
475 if (address >= DS7_REG_MAX) {
476 mLOG(DS_IO, GAME_ERROR, "Write to unused DS9 I/O register: %06X:%04X", address, value);
477 return;
478 }
479 break;
480 }
481 }
482 ds->memory.io9[address >> 1] = value;
483}
484
485void DS9IOWrite8(struct DS* ds, uint32_t address, uint8_t value) {
486 if (address < DS9_REG_MAX) {
487 uint16_t value16 = value << (8 * (address & 1));
488 value16 |= (ds->memory.io9[(address & 0x1FFF) >> 1]) & ~(0xFF << (8 * (address & 1)));
489 DS9IOWrite(ds, address & 0xFFFFFFFE, value16);
490 } else {
491 mLOG(DS, STUB, "Writing to unknown DS9 register: %08X:%02X", address, value);
492 }
493}
494
495void DS9IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
496 switch (address) {
497 case DS_REG_DMA0SAD_LO:
498 case DS_REG_DMA1SAD_LO:
499 case DS_REG_DMA2SAD_LO:
500 case DS_REG_DMA3SAD_LO:
501 case DS_REG_DMA0DAD_LO:
502 case DS_REG_DMA1DAD_LO:
503 case DS_REG_DMA2DAD_LO:
504 case DS_REG_DMA3DAD_LO:
505 case DS_REG_IPCFIFOSEND_LO:
506 case DS_REG_IE_LO:
507 value = DSIOWrite32(&ds->ds9, address, value);
508 break;
509
510 case DS_REG_DMA0CNT_LO:
511 DS9DMAWriteCNT(&ds->ds9, 0, value);
512 break;
513 case DS_REG_DMA1CNT_LO:
514 DS9DMAWriteCNT(&ds->ds9, 1, value);
515 break;
516 case DS_REG_DMA2CNT_LO:
517 DS9DMAWriteCNT(&ds->ds9, 2, value);
518 break;
519 case DS_REG_DMA3CNT_LO:
520 DS9DMAWriteCNT(&ds->ds9, 3, value);
521 break;
522
523 default:
524 DS9IOWrite(ds, address, value & 0xFFFF);
525 DS9IOWrite(ds, address | 2, value >> 16);
526 return;
527 }
528 ds->ds9.memory.io[address >> 1] = value;
529 ds->ds9.memory.io[(address >> 1) + 1] = value >> 16;
530}
531
532uint16_t DS9IORead(struct DS* ds, uint32_t address) {
533 switch (address) {
534 case DS_REG_TM0CNT_LO:
535 case DS_REG_TM1CNT_LO:
536 case DS_REG_TM2CNT_LO:
537 case DS_REG_TM3CNT_LO:
538 DSIOUpdateTimer(&ds->ds9, address);
539 break;
540 case DS_REG_KEYINPUT:
541 return DSIOReadKeyInput(ds);
542 case DS_REG_VCOUNT:
543 case DS_REG_DMA0FILL_LO:
544 case DS_REG_DMA0FILL_HI:
545 case DS_REG_DMA1FILL_LO:
546 case DS_REG_DMA1FILL_HI:
547 case DS_REG_DMA2FILL_LO:
548 case DS_REG_DMA2FILL_HI:
549 case DS_REG_DMA3FILL_LO:
550 case DS_REG_DMA3FILL_HI:
551 case DS_REG_TM0CNT_HI:
552 case DS_REG_TM1CNT_HI:
553 case DS_REG_TM2CNT_HI:
554 case DS_REG_TM3CNT_HI:
555 case DS_REG_IPCSYNC:
556 case DS_REG_IPCFIFOCNT:
557 case DS_REG_ROMCNT_LO:
558 case DS_REG_ROMCNT_HI:
559 case DS_REG_IME:
560 case 0x20A:
561 case DS_REG_IE_LO:
562 case DS_REG_IE_HI:
563 case DS_REG_IF_LO:
564 case DS_REG_IF_HI:
565 case DS9_REG_DIVCNT:
566 case DS9_REG_DIV_NUMER_0:
567 case DS9_REG_DIV_NUMER_1:
568 case DS9_REG_DIV_NUMER_2:
569 case DS9_REG_DIV_NUMER_3:
570 case DS9_REG_DIV_DENOM_0:
571 case DS9_REG_DIV_DENOM_1:
572 case DS9_REG_DIV_DENOM_2:
573 case DS9_REG_DIV_DENOM_3:
574 case DS9_REG_DIV_RESULT_0:
575 case DS9_REG_DIV_RESULT_1:
576 case DS9_REG_DIV_RESULT_2:
577 case DS9_REG_DIV_RESULT_3:
578 case DS9_REG_DIVREM_RESULT_0:
579 case DS9_REG_DIVREM_RESULT_1:
580 case DS9_REG_DIVREM_RESULT_2:
581 case DS9_REG_DIVREM_RESULT_3:
582 case DS9_REG_SQRTCNT:
583 case DS9_REG_SQRT_PARAM_0:
584 case DS9_REG_SQRT_PARAM_1:
585 case DS9_REG_SQRT_PARAM_2:
586 case DS9_REG_SQRT_PARAM_3:
587 case DS9_REG_SQRT_RESULT_LO:
588 case DS9_REG_SQRT_RESULT_HI:
589 case DS_REG_POSTFLG:
590 // Handled transparently by the registers
591 break;
592 default:
593 mLOG(DS_IO, STUB, "Stub DS9 I/O register read: %06X", address);
594 }
595 if (address < DS9_REG_MAX) {
596 return ds->ds9.memory.io[address >> 1];
597 }
598 return 0;
599}
600
601uint32_t DS9IORead32(struct DS* ds, uint32_t address) {
602 switch (address) {
603 case DS_REG_IPCFIFORECV_LO:
604 return DSIPCReadFIFO(&ds->ds9);
605 case DS_REG_ROMDATA_0:
606 if (ds->ds9.memory.slot1Access) {
607 return DSSlot1Read(ds);
608 } else {
609 mLOG(DS_IO, GAME_ERROR, "Invalid cart access");
610 return 0;
611 }
612 default:
613 return DS9IORead(ds, address & 0x00FFFFFC) | (DS9IORead(ds, (address & 0x00FFFFFC) | 2) << 16);
614 }
615}