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 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 // VRAM control
437 case DS9_REG_VRAMCNT_A:
438 case DS9_REG_VRAMCNT_C:
439 case DS9_REG_VRAMCNT_E:
440 oldValue = ds->memory.io9[address >> 1];
441 value &= 0x9F9F;
442 DSVideoConfigureVRAM(ds, address - DS9_REG_VRAMCNT_A, value & 0xFF, oldValue & 0xFF);
443 DSVideoConfigureVRAM(ds, address - DS9_REG_VRAMCNT_A + 1, value >> 8, oldValue >> 8);
444 break;
445 case DS9_REG_VRAMCNT_G:
446 oldValue = ds->memory.io9[address >> 1];
447 value &= 0x039F;
448 DSVideoConfigureVRAM(ds, 6, value & 0xFF, oldValue & 0xFF);
449 DSConfigureWRAM(&ds->memory, value >> 8);
450 break;
451 case DS9_REG_VRAMCNT_H:
452 oldValue = ds->memory.io9[address >> 1];
453 value &= 0x9F9F;
454 DSVideoConfigureVRAM(ds, 7, value & 0xFF, oldValue & 0xFF);
455 DSVideoConfigureVRAM(ds, 8, value >> 8, oldValue >> 8);
456 break;
457
458 case DS9_REG_EXMEMCNT:
459 value &= 0xE8FF;
460 DSConfigureExternalMemory(ds, value);
461 break;
462
463 // Math
464 case DS9_REG_DIVCNT:
465 value = _scheduleDiv(ds, value);
466 break;
467 case DS9_REG_DIV_NUMER_0:
468 case DS9_REG_DIV_NUMER_1:
469 case DS9_REG_DIV_NUMER_2:
470 case DS9_REG_DIV_NUMER_3:
471 case DS9_REG_DIV_DENOM_0:
472 case DS9_REG_DIV_DENOM_1:
473 case DS9_REG_DIV_DENOM_2:
474 case DS9_REG_DIV_DENOM_3:
475 ds->memory.io9[DS9_REG_DIVCNT >> 1] = _scheduleDiv(ds, ds->memory.io9[DS9_REG_DIVCNT >> 1]);
476 break;
477 case DS9_REG_SQRTCNT:
478 value = _scheduleSqrt(ds, value);
479 break;
480 case DS9_REG_SQRT_PARAM_0:
481 case DS9_REG_SQRT_PARAM_1:
482 case DS9_REG_SQRT_PARAM_2:
483 case DS9_REG_SQRT_PARAM_3:
484 ds->memory.io9[DS9_REG_SQRTCNT >> 1] = _scheduleSqrt(ds, ds->memory.io9[DS9_REG_SQRTCNT >> 1]);
485 break;
486
487 // High Video
488 case DS9_REG_POWCNT1:
489 value = ds->video.renderer->writeVideoRegister(ds->video.renderer, address, value);
490 break;
491
492 default:
493 {
494 uint32_t v2 = DSIOWrite(&ds->ds9, address, value);
495 if (v2 & 0x10000) {
496 value = v2;
497 break;
498 } else if (v2 & 0x20000) {
499 return;
500 }
501 }
502 mLOG(DS_IO, STUB, "Stub DS9 I/O register write: %06X:%04X", address, value);
503 if (address >= DS7_REG_MAX) {
504 mLOG(DS_IO, GAME_ERROR, "Write to unused DS9 I/O register: %06X:%04X", address, value);
505 return;
506 }
507 break;
508 }
509 }
510 ds->memory.io9[address >> 1] = value;
511}
512
513void DS9IOWrite8(struct DS* ds, uint32_t address, uint8_t value) {
514 if (address < DS9_REG_MAX) {
515 uint16_t value16 = value << (8 * (address & 1));
516 value16 |= (ds->memory.io9[(address & 0x1FFF) >> 1]) & ~(0xFF << (8 * (address & 1)));
517 DS9IOWrite(ds, address & 0xFFFFFFFE, value16);
518 } else {
519 mLOG(DS, STUB, "Writing to unknown DS9 register: %08X:%02X", address, value);
520 }
521}
522
523void DS9IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
524 if ((address >= DS9_REG_RDLINES_COUNT && address <= DS9_REG_VECMTX_RESULT_12) || address == DS9_REG_DISP3DCNT) {
525 value = DSGXWriteRegister32(&ds->gx, address, value);
526 } else {
527 switch (address) {
528 case DS_REG_DMA0SAD_LO:
529 case DS_REG_DMA1SAD_LO:
530 case DS_REG_DMA2SAD_LO:
531 case DS_REG_DMA3SAD_LO:
532 case DS_REG_DMA0DAD_LO:
533 case DS_REG_DMA1DAD_LO:
534 case DS_REG_DMA2DAD_LO:
535 case DS_REG_DMA3DAD_LO:
536 case DS_REG_IPCFIFOSEND_LO:
537 case DS_REG_IE_LO:
538 value = DSIOWrite32(&ds->ds9, address, value);
539 break;
540
541 case DS_REG_DMA0CNT_LO:
542 DS9DMAWriteCNT(&ds->ds9, 0, value);
543 break;
544 case DS_REG_DMA1CNT_LO:
545 DS9DMAWriteCNT(&ds->ds9, 1, value);
546 break;
547 case DS_REG_DMA2CNT_LO:
548 DS9DMAWriteCNT(&ds->ds9, 2, value);
549 break;
550 case DS_REG_DMA3CNT_LO:
551 DS9DMAWriteCNT(&ds->ds9, 3, value);
552 break;
553
554 default:
555 DS9IOWrite(ds, address, value & 0xFFFF);
556 DS9IOWrite(ds, address | 2, value >> 16);
557 return;
558 }
559 }
560 ds->ds9.memory.io[address >> 1] = value;
561 ds->ds9.memory.io[(address >> 1) + 1] = value >> 16;
562}
563
564uint16_t DS9IORead(struct DS* ds, uint32_t address) {
565 switch (address) {
566 case DS_REG_TM0CNT_LO:
567 case DS_REG_TM1CNT_LO:
568 case DS_REG_TM2CNT_LO:
569 case DS_REG_TM3CNT_LO:
570 DSIOUpdateTimer(&ds->ds9, address);
571 break;
572 case DS_REG_KEYINPUT:
573 return DSIOReadKeyInput(ds);
574 case DS_REG_VCOUNT:
575 case DS_REG_DMA0FILL_LO:
576 case DS_REG_DMA0FILL_HI:
577 case DS_REG_DMA1FILL_LO:
578 case DS_REG_DMA1FILL_HI:
579 case DS_REG_DMA2FILL_LO:
580 case DS_REG_DMA2FILL_HI:
581 case DS_REG_DMA3FILL_LO:
582 case DS_REG_DMA3FILL_HI:
583 case DS_REG_TM0CNT_HI:
584 case DS_REG_TM1CNT_HI:
585 case DS_REG_TM2CNT_HI:
586 case DS_REG_TM3CNT_HI:
587 case DS_REG_IPCSYNC:
588 case DS_REG_IPCFIFOCNT:
589 case DS_REG_ROMCNT_LO:
590 case DS_REG_ROMCNT_HI:
591 case DS_REG_IME:
592 case 0x20A:
593 case DS_REG_IE_LO:
594 case DS_REG_IE_HI:
595 case DS_REG_IF_LO:
596 case DS_REG_IF_HI:
597 case DS9_REG_DIVCNT:
598 case DS9_REG_DIV_NUMER_0:
599 case DS9_REG_DIV_NUMER_1:
600 case DS9_REG_DIV_NUMER_2:
601 case DS9_REG_DIV_NUMER_3:
602 case DS9_REG_DIV_DENOM_0:
603 case DS9_REG_DIV_DENOM_1:
604 case DS9_REG_DIV_DENOM_2:
605 case DS9_REG_DIV_DENOM_3:
606 case DS9_REG_DIV_RESULT_0:
607 case DS9_REG_DIV_RESULT_1:
608 case DS9_REG_DIV_RESULT_2:
609 case DS9_REG_DIV_RESULT_3:
610 case DS9_REG_DIVREM_RESULT_0:
611 case DS9_REG_DIVREM_RESULT_1:
612 case DS9_REG_DIVREM_RESULT_2:
613 case DS9_REG_DIVREM_RESULT_3:
614 case DS9_REG_SQRTCNT:
615 case DS9_REG_SQRT_PARAM_0:
616 case DS9_REG_SQRT_PARAM_1:
617 case DS9_REG_SQRT_PARAM_2:
618 case DS9_REG_SQRT_PARAM_3:
619 case DS9_REG_SQRT_RESULT_LO:
620 case DS9_REG_SQRT_RESULT_HI:
621 case DS_REG_POSTFLG:
622 case DS9_REG_GXSTAT_LO:
623 case DS9_REG_GXSTAT_HI:
624 case DS9_REG_CLIPMTX_RESULT_00:
625 case DS9_REG_CLIPMTX_RESULT_01:
626 case DS9_REG_CLIPMTX_RESULT_02:
627 case DS9_REG_CLIPMTX_RESULT_03:
628 case DS9_REG_CLIPMTX_RESULT_04:
629 case DS9_REG_CLIPMTX_RESULT_05:
630 case DS9_REG_CLIPMTX_RESULT_06:
631 case DS9_REG_CLIPMTX_RESULT_07:
632 case DS9_REG_CLIPMTX_RESULT_08:
633 case DS9_REG_CLIPMTX_RESULT_09:
634 case DS9_REG_CLIPMTX_RESULT_0A:
635 case DS9_REG_CLIPMTX_RESULT_0B:
636 case DS9_REG_CLIPMTX_RESULT_0C:
637 case DS9_REG_CLIPMTX_RESULT_0D:
638 case DS9_REG_CLIPMTX_RESULT_0E:
639 case DS9_REG_CLIPMTX_RESULT_0F:
640 case DS9_REG_CLIPMTX_RESULT_10:
641 case DS9_REG_CLIPMTX_RESULT_11:
642 case DS9_REG_CLIPMTX_RESULT_12:
643 case DS9_REG_CLIPMTX_RESULT_13:
644 case DS9_REG_CLIPMTX_RESULT_14:
645 case DS9_REG_CLIPMTX_RESULT_15:
646 case DS9_REG_CLIPMTX_RESULT_16:
647 case DS9_REG_CLIPMTX_RESULT_17:
648 case DS9_REG_CLIPMTX_RESULT_18:
649 case DS9_REG_CLIPMTX_RESULT_19:
650 case DS9_REG_CLIPMTX_RESULT_1A:
651 case DS9_REG_CLIPMTX_RESULT_1B:
652 case DS9_REG_CLIPMTX_RESULT_1C:
653 case DS9_REG_CLIPMTX_RESULT_1D:
654 case DS9_REG_CLIPMTX_RESULT_1E:
655 case DS9_REG_CLIPMTX_RESULT_1F:
656 // Handled transparently by the registers
657 break;
658 case DS_REG_AUXSPICNT:
659 case DS_REG_AUXSPIDATA:
660 if (ds->ds9.memory.slot1Access) {
661 break;
662 } else {
663 mLOG(DS_IO, GAME_ERROR, "Invalid cart access");
664 return 0;
665 }
666 default:
667 mLOG(DS_IO, STUB, "Stub DS9 I/O register read: %06X", address);
668 }
669 if (address < DS9_REG_MAX) {
670 return ds->ds9.memory.io[address >> 1];
671 }
672 return 0;
673}
674
675uint32_t DS9IORead32(struct DS* ds, uint32_t address) {
676 switch (address) {
677 case DS_REG_IPCFIFORECV_LO:
678 return DSIPCReadFIFO(&ds->ds9);
679 case DS_REG_ROMDATA_0:
680 if (ds->ds9.memory.slot1Access) {
681 return DSSlot1Read(ds);
682 } else {
683 mLOG(DS_IO, GAME_ERROR, "Invalid cart access");
684 return 0;
685 }
686 default:
687 return DS9IORead(ds, address & 0x00FFFFFC) | (DS9IORead(ds, (address & 0x00FFFFFC) | 2) << 16);
688 }
689}