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