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
12mLOG_DEFINE_CATEGORY(DS_IO, "DS I/O");
13
14static void _DSHaltCNT(struct DSCommon* dscore, uint8_t value) {
15 switch (value >> 6) {
16 case 0:
17 default:
18 break;
19 case 1:
20 mLOG(DS_IO, STUB, "Enter GBA mode not supported");
21 break;
22 case 2:
23 ARMHalt(dscore->cpu);
24 break;
25 case 3:
26 mLOG(DS_IO, STUB, "Enter sleep mode not supported");
27 break;
28 }
29}
30
31static uint16_t _scheduleDiv(struct DS* ds, uint16_t control) {
32 mTimingDeschedule(&ds->ds9.timing, &ds->divEvent);
33 mTimingSchedule(&ds->ds9.timing, &ds->divEvent, (control & 3) ? 36 : 68);
34 return control | 0x8000;
35}
36
37static uint16_t _scheduleSqrt(struct DS* ds, uint16_t control) {
38 mTimingDeschedule(&ds->ds9.timing, &ds->sqrtEvent);
39 mTimingSchedule(&ds->ds9.timing, &ds->sqrtEvent, 26);
40 return control | 0x8000;
41}
42
43static uint32_t DSIOWrite(struct DSCommon* dscore, uint32_t address, uint16_t value) {
44 switch (address) {
45 // Video
46 case DS_REG_DISPSTAT:
47 DSVideoWriteDISPSTAT(dscore, value);
48 break;
49
50 // DMA Fill
51 case DS_REG_DMA0FILL_LO:
52 case DS_REG_DMA0FILL_HI:
53 case DS_REG_DMA1FILL_LO:
54 case DS_REG_DMA1FILL_HI:
55 case DS_REG_DMA2FILL_LO:
56 case DS_REG_DMA2FILL_HI:
57 case DS_REG_DMA3FILL_LO:
58 case DS_REG_DMA3FILL_HI:
59 break;
60
61 // Timers
62 case DS_REG_TM0CNT_LO:
63 GBATimerWriteTMCNT_LO(&dscore->timers[0], value);
64 return 0x20000;
65 case DS_REG_TM1CNT_LO:
66 GBATimerWriteTMCNT_LO(&dscore->timers[1], value);
67 return 0x20000;
68 case DS_REG_TM2CNT_LO:
69 GBATimerWriteTMCNT_LO(&dscore->timers[2], value);
70 return 0x20000;
71 case DS_REG_TM3CNT_LO:
72 GBATimerWriteTMCNT_LO(&dscore->timers[3], value);
73 return 0x20000;
74
75 case DS_REG_TM0CNT_HI:
76 value &= 0x00C7;
77 DSTimerWriteTMCNT_HI(&dscore->timers[0], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM0CNT_LO >> 1], value);
78 break;
79 case DS_REG_TM1CNT_HI:
80 value &= 0x00C7;
81 DSTimerWriteTMCNT_HI(&dscore->timers[1], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM1CNT_LO >> 1], value);
82 break;
83 case DS_REG_TM2CNT_HI:
84 value &= 0x00C7;
85 DSTimerWriteTMCNT_HI(&dscore->timers[2], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM2CNT_LO >> 1], value);
86 break;
87 case DS_REG_TM3CNT_HI:
88 value &= 0x00C7;
89 DSTimerWriteTMCNT_HI(&dscore->timers[3], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM3CNT_LO >> 1], value);
90 break;
91
92 // IPC
93 case DS_REG_IPCSYNC:
94 value &= 0x6F00;
95 value |= dscore->memory.io[address >> 1] & 0x000F;
96 DSIPCWriteSYNC(dscore->ipc->cpu, dscore->ipc->memory.io, value);
97 break;
98 case DS_REG_IPCFIFOCNT:
99 value = DSIPCWriteFIFOCNT(dscore, value);
100 break;
101
102 // Cart bus
103 case DS_REG_SLOT1CNT_LO:
104 mLOG(DS_IO, STUB, "ROM control not implemented");
105 value &= 0x7FFF;
106 break;
107
108 // Interrupts
109 case DS_REG_IME:
110 DSWriteIME(dscore->cpu, dscore->memory.io, value);
111 break;
112 case 0x20A:
113 value = 0;
114 // Some bad interrupt libraries will write to this
115 break;
116 case DS_REG_IF_LO:
117 case DS_REG_IF_HI:
118 value = dscore->memory.io[address >> 1] & ~value;
119 break;
120 default:
121 return 0;
122 }
123 return value | 0x10000;
124}
125
126static uint16_t DSIOReadKeyInput(struct DS* ds) {
127 uint16_t input = 0x3FF;
128 if (ds->keyCallback) {
129 input = ds->keyCallback->readKeys(ds->keyCallback);
130 } else if (ds->keySource) {
131 input = *ds->keySource;
132 }
133 // TODO: Put back
134 /*if (!dscore->p->allowOpposingDirections) {
135 unsigned rl = input & 0x030;
136 unsigned ud = input & 0x0C0;
137 input &= 0x30F;
138 if (rl != 0x030) {
139 input |= rl;
140 }
141 if (ud != 0x0C0) {
142 input |= ud;
143 }
144 }*/
145 return 0x3FF ^ input;
146}
147
148static void DSIOUpdateTimer(struct DSCommon* dscore, uint32_t address) {
149 switch (address) {
150 case DS_REG_TM0CNT_LO:
151 GBATimerUpdateRegisterInternal(&dscore->timers[0], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0);
152 break;
153 case DS_REG_TM1CNT_LO:
154 GBATimerUpdateRegisterInternal(&dscore->timers[1], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0);
155 break;
156 case DS_REG_TM2CNT_LO:
157 GBATimerUpdateRegisterInternal(&dscore->timers[2], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0);
158 break;
159 case DS_REG_TM3CNT_LO:
160 GBATimerUpdateRegisterInternal(&dscore->timers[3], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0);
161 break;
162 }
163}
164
165void DS7IOInit(struct DS* ds) {
166 memset(ds->memory.io7, 0, sizeof(ds->memory.io7));
167}
168
169void DS7IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
170 switch (address) {
171 default:
172 {
173 uint32_t v2 = DSIOWrite(&ds->ds7, address, value);
174 if (v2 & 0x10000) {
175 value = v2;
176 break;
177 } else if (v2 & 0x20000) {
178 return;
179 }
180 }
181 mLOG(DS_IO, STUB, "Stub DS7 I/O register write: %06X:%04X", address, value);
182 if (address >= DS7_REG_MAX) {
183 mLOG(DS_IO, GAME_ERROR, "Write to unused DS7 I/O register: %06X:%04X", address, value);
184 return;
185 }
186 break;
187 }
188 ds->memory.io7[address >> 1] = value;
189}
190
191void DS7IOWrite8(struct DS* ds, uint32_t address, uint8_t value) {
192 if (address == DS7_REG_HALTCNT) {
193 _DSHaltCNT(&ds->ds7, value);
194 return;
195 }
196 if (address < DS7_REG_MAX) {
197 uint16_t value16 = value << (8 * (address & 1));
198 value16 |= (ds->ds7.memory.io[(address & 0xFFF) >> 1]) & ~(0xFF << (8 * (address & 1)));
199 DS7IOWrite(ds, address & 0xFFFFFFFE, value16);
200 } else {
201 mLOG(DS, STUB, "Writing to unknown DS7 register: %08X:%02X", address, value);
202 }
203}
204
205void DS7IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
206 switch (address) {
207 case DS_REG_DMA0SAD_LO:
208 value = DSDMAWriteSAD(&ds->ds7, 0, value);
209 break;
210 case DS_REG_DMA1SAD_LO:
211 value = DSDMAWriteSAD(&ds->ds7, 1, value);
212 break;
213 case DS_REG_DMA2SAD_LO:
214 value = DSDMAWriteSAD(&ds->ds7, 2, value);
215 break;
216 case DS_REG_DMA3SAD_LO:
217 value = DSDMAWriteSAD(&ds->ds7, 3, value);
218 break;
219
220 case DS_REG_DMA0DAD_LO:
221 value = DSDMAWriteDAD(&ds->ds7, 0, value);
222 break;
223 case DS_REG_DMA1DAD_LO:
224 value = DSDMAWriteDAD(&ds->ds7, 1, value);
225 break;
226 case DS_REG_DMA2DAD_LO:
227 value = DSDMAWriteDAD(&ds->ds7, 2, value);
228 break;
229 case DS_REG_DMA3DAD_LO:
230 value = DSDMAWriteDAD(&ds->ds7, 3, value);
231 break;
232
233 case DS_REG_DMA0CNT_LO:
234 DS7DMAWriteCNT(&ds->ds7, 0, value);
235 break;
236 case DS_REG_DMA1CNT_LO:
237 DS7DMAWriteCNT(&ds->ds7, 1, value);
238 break;
239 case DS_REG_DMA2CNT_LO:
240 DS7DMAWriteCNT(&ds->ds7, 2, value);
241 break;
242 case DS_REG_DMA3CNT_LO:
243 DS7DMAWriteCNT(&ds->ds7, 3, value);
244 break;
245
246 case DS_REG_IPCFIFOSEND_LO:
247 DSIPCWriteFIFO(&ds->ds7, value);
248 break;
249 case DS_REG_IE_LO:
250 DSWriteIE(ds->ds7.cpu, ds->ds7.memory.io, value);
251 break;
252 default:
253 DS7IOWrite(ds, address, value & 0xFFFF);
254 DS7IOWrite(ds, address | 2, value >> 16);
255 return;
256 }
257 ds->ds7.memory.io[address >> 1] = value;
258 ds->ds7.memory.io[(address >> 1) + 1] = value >> 16;
259}
260
261uint16_t DS7IORead(struct DS* ds, uint32_t address) {
262 switch (address) {
263 case DS_REG_TM0CNT_LO:
264 case DS_REG_TM1CNT_LO:
265 case DS_REG_TM2CNT_LO:
266 case DS_REG_TM3CNT_LO:
267 DSIOUpdateTimer(&ds->ds7, address);
268 break;
269 case DS_REG_KEYINPUT:
270 return DSIOReadKeyInput(ds);
271 case DS_REG_DMA0FILL_LO:
272 case DS_REG_DMA0FILL_HI:
273 case DS_REG_DMA1FILL_LO:
274 case DS_REG_DMA1FILL_HI:
275 case DS_REG_DMA2FILL_LO:
276 case DS_REG_DMA2FILL_HI:
277 case DS_REG_DMA3FILL_LO:
278 case DS_REG_DMA3FILL_HI:
279 case DS_REG_TM0CNT_HI:
280 case DS_REG_TM1CNT_HI:
281 case DS_REG_TM2CNT_HI:
282 case DS_REG_TM3CNT_HI:
283 case DS_REG_IPCSYNC:
284 case DS_REG_IPCFIFOCNT:
285 case DS_REG_IME:
286 case 0x20A:
287 case DS_REG_IE_LO:
288 case DS_REG_IE_HI:
289 case DS_REG_IF_LO:
290 case DS_REG_IF_HI:
291 // Handled transparently by the registers
292 break;
293 default:
294 mLOG(DS_IO, STUB, "Stub DS7 I/O register read: %06X", address);
295 }
296 if (address < DS7_REG_MAX) {
297 return ds->memory.io7[address >> 1];
298 }
299 return 0;
300}
301
302uint32_t DS7IORead32(struct DS* ds, uint32_t address) {
303 switch (address) {
304 case DS_REG_IPCFIFORECV_LO:
305 return DSIPCReadFIFO(&ds->ds7);
306 default:
307 return DS7IORead(ds, address & 0x00FFFFFC) | (DS7IORead(ds, (address & 0x00FFFFFC) | 2) << 16);
308 }
309}
310
311void DS9IOInit(struct DS* ds) {
312 memset(ds->memory.io9, 0, sizeof(ds->memory.io9));
313}
314
315void DS9IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
316 switch (address) {
317 // VRAM control
318 case DS9_REG_VRAMCNT_A:
319 case DS9_REG_VRAMCNT_C:
320 case DS9_REG_VRAMCNT_E:
321 case DS9_REG_VRAMCNT_G:
322 DSVideoConfigureVRAM(&ds->memory, address - DS9_REG_VRAMCNT_A + 1, value & 0xFF);
323 // Fall through
324 case DS9_REG_VRAMCNT_I:
325 DSVideoConfigureVRAM(&ds->memory, address - DS9_REG_VRAMCNT_A, value >> 8);
326 break;
327
328 // Math
329 case DS9_REG_DIVCNT:
330 value = _scheduleDiv(ds, value);
331 break;
332 case DS9_REG_DIV_NUMER_0:
333 case DS9_REG_DIV_NUMER_1:
334 case DS9_REG_DIV_NUMER_2:
335 case DS9_REG_DIV_NUMER_3:
336 case DS9_REG_DIV_DENOM_0:
337 case DS9_REG_DIV_DENOM_1:
338 case DS9_REG_DIV_DENOM_2:
339 case DS9_REG_DIV_DENOM_3:
340 ds->memory.io9[DS9_REG_DIVCNT >> 1] = _scheduleDiv(ds, ds->memory.io9[DS9_REG_DIVCNT >> 1]);
341 break;
342 case DS9_REG_SQRTCNT:
343 value = _scheduleSqrt(ds, value);
344 break;
345 case DS9_REG_SQRT_PARAM_0:
346 case DS9_REG_SQRT_PARAM_1:
347 case DS9_REG_SQRT_PARAM_2:
348 case DS9_REG_SQRT_PARAM_3:
349 ds->memory.io9[DS9_REG_SQRTCNT >> 1] = _scheduleSqrt(ds, ds->memory.io9[DS9_REG_SQRTCNT >> 1]);
350 break;
351
352 default:
353 {
354 uint32_t v2 = DSIOWrite(&ds->ds9, address, value);
355 if (v2 & 0x10000) {
356 value = v2;
357 break;
358 } else if (v2 & 0x20000) {
359 return;
360 }
361 }
362 mLOG(DS_IO, STUB, "Stub DS9 I/O register write: %06X:%04X", address, value);
363 if (address >= DS7_REG_MAX) {
364 mLOG(DS_IO, GAME_ERROR, "Write to unused DS9 I/O register: %06X:%04X", address, value);
365 return;
366 }
367 break;
368 }
369 ds->memory.io9[address >> 1] = value;
370}
371
372void DS9IOWrite8(struct DS* ds, uint32_t address, uint8_t value) {
373 if (address < DS9_REG_MAX) {
374 uint16_t value16 = value << (8 * (address & 1));
375 value16 |= (ds->memory.io9[(address & 0x1FFF) >> 1]) & ~(0xFF << (8 * (address & 1)));
376 DS9IOWrite(ds, address & 0xFFFFFFFE, value16);
377 } else {
378 mLOG(DS, STUB, "Writing to unknown DS9 register: %08X:%02X", address, value);
379 }
380}
381
382void DS9IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
383 switch (address) {
384 case DS_REG_DMA0SAD_LO:
385 value = DSDMAWriteSAD(&ds->ds9, 0, value);
386 break;
387 case DS_REG_DMA1SAD_LO:
388 value = DSDMAWriteSAD(&ds->ds9, 1, value);
389 break;
390 case DS_REG_DMA2SAD_LO:
391 value = DSDMAWriteSAD(&ds->ds9, 2, value);
392 break;
393 case DS_REG_DMA3SAD_LO:
394 value = DSDMAWriteSAD(&ds->ds9, 3, value);
395 break;
396
397 case DS_REG_DMA0DAD_LO:
398 value = DSDMAWriteDAD(&ds->ds9, 0, value);
399 break;
400 case DS_REG_DMA1DAD_LO:
401 value = DSDMAWriteDAD(&ds->ds9, 1, value);
402 break;
403 case DS_REG_DMA2DAD_LO:
404 value = DSDMAWriteDAD(&ds->ds9, 2, value);
405 break;
406 case DS_REG_DMA3DAD_LO:
407 value = DSDMAWriteDAD(&ds->ds9, 3, value);
408 break;
409
410 case DS_REG_DMA0CNT_LO:
411 DS9DMAWriteCNT(&ds->ds9, 0, value);
412 break;
413 case DS_REG_DMA1CNT_LO:
414 DS9DMAWriteCNT(&ds->ds9, 1, value);
415 break;
416 case DS_REG_DMA2CNT_LO:
417 DS9DMAWriteCNT(&ds->ds9, 2, value);
418 break;
419 case DS_REG_DMA3CNT_LO:
420 DS9DMAWriteCNT(&ds->ds9, 3, value);
421 break;
422
423 case DS_REG_IPCFIFOSEND_LO:
424 DSIPCWriteFIFO(&ds->ds9, value);
425 break;
426 case DS_REG_IE_LO:
427 DSWriteIE(ds->ds9.cpu, ds->ds9.memory.io, value);
428 break;
429 default:
430 DS9IOWrite(ds, address, value & 0xFFFF);
431 DS9IOWrite(ds, address | 2, value >> 16);
432 return;
433 }
434 ds->ds9.memory.io[address >> 1] = value;
435 ds->ds9.memory.io[(address >> 1) + 1] = value >> 16;
436}
437
438uint16_t DS9IORead(struct DS* ds, uint32_t address) {
439 switch (address) {
440 case DS_REG_TM0CNT_LO:
441 case DS_REG_TM1CNT_LO:
442 case DS_REG_TM2CNT_LO:
443 case DS_REG_TM3CNT_LO:
444 DSIOUpdateTimer(&ds->ds9, address);
445 break;
446 case DS_REG_KEYINPUT:
447 return DSIOReadKeyInput(ds);
448 case DS_REG_DMA0FILL_LO:
449 case DS_REG_DMA0FILL_HI:
450 case DS_REG_DMA1FILL_LO:
451 case DS_REG_DMA1FILL_HI:
452 case DS_REG_DMA2FILL_LO:
453 case DS_REG_DMA2FILL_HI:
454 case DS_REG_DMA3FILL_LO:
455 case DS_REG_DMA3FILL_HI:
456 case DS_REG_TM0CNT_HI:
457 case DS_REG_TM1CNT_HI:
458 case DS_REG_TM2CNT_HI:
459 case DS_REG_TM3CNT_HI:
460 case DS_REG_IPCSYNC:
461 case DS_REG_IPCFIFOCNT:
462 case DS_REG_IME:
463 case 0x20A:
464 case DS_REG_IE_LO:
465 case DS_REG_IE_HI:
466 case DS_REG_IF_LO:
467 case DS_REG_IF_HI:
468 case DS9_REG_DIVCNT:
469 case DS9_REG_DIV_NUMER_0:
470 case DS9_REG_DIV_NUMER_1:
471 case DS9_REG_DIV_NUMER_2:
472 case DS9_REG_DIV_NUMER_3:
473 case DS9_REG_DIV_DENOM_0:
474 case DS9_REG_DIV_DENOM_1:
475 case DS9_REG_DIV_DENOM_2:
476 case DS9_REG_DIV_DENOM_3:
477 case DS9_REG_DIV_RESULT_0:
478 case DS9_REG_DIV_RESULT_1:
479 case DS9_REG_DIV_RESULT_2:
480 case DS9_REG_DIV_RESULT_3:
481 case DS9_REG_DIVREM_RESULT_0:
482 case DS9_REG_DIVREM_RESULT_1:
483 case DS9_REG_DIVREM_RESULT_2:
484 case DS9_REG_DIVREM_RESULT_3:
485 case DS9_REG_SQRTCNT:
486 case DS9_REG_SQRT_PARAM_0:
487 case DS9_REG_SQRT_PARAM_1:
488 case DS9_REG_SQRT_PARAM_2:
489 case DS9_REG_SQRT_PARAM_3:
490 case DS9_REG_SQRT_RESULT_LO:
491 case DS9_REG_SQRT_RESULT_HI:
492 // Handled transparently by the registers
493 break;
494 default:
495 mLOG(DS_IO, STUB, "Stub DS9 I/O register read: %06X", address);
496 }
497 if (address < DS9_REG_MAX) {
498 return ds->ds9.memory.io[address >> 1];
499 }
500 return 0;
501}
502
503uint32_t DS9IORead32(struct DS* ds, uint32_t address) {
504 switch (address) {
505 case DS_REG_IPCFIFORECV_LO:
506 return DSIPCReadFIFO(&ds->ds9);
507 default:
508 return DS9IORead(ds, address & 0x00FFFFFC) | (DS9IORead(ds, (address & 0x00FFFFFC) | 2) << 16);
509 }
510}