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