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