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