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