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