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