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