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_IME:
211 case DS_REG_IE_LO:
212 case DS_REG_IE_HI:
213 case DS_REG_IF_LO:
214 case DS_REG_IF_HI:
215 // Handled transparently by the registers
216 break;
217 default:
218 mLOG(DS_IO, STUB, "Stub DS7 I/O register read: %06X", address);
219 }
220 if (address < DS7_REG_MAX) {
221 return ds->memory.io7[address >> 1];
222 }
223 return 0;
224}
225
226void DS9IOInit(struct DS* ds) {
227 memset(ds->memory.io9, 0, sizeof(ds->memory.io9));
228}
229
230void DS9IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
231 switch (address) {
232 default:
233 {
234 uint32_t v2 = DSIOWrite(&ds->ds9, address, value);
235 if (v2 & 0x10000) {
236 value = v2;
237 break;
238 } else if (v2 & 0x20000) {
239 return;
240 }
241 }
242 mLOG(DS_IO, STUB, "Stub DS9 I/O register write: %06X:%04X", address, value);
243 if (address >= DS7_REG_MAX) {
244 mLOG(DS_IO, GAME_ERROR, "Write to unused DS9 I/O register: %06X:%04X", address, value);
245 return;
246 }
247 break;
248 }
249 ds->memory.io9[address >> 1] = value;
250}
251
252void DS9IOWrite8(struct DS* ds, uint32_t address, uint8_t value) {
253 if (address < DS9_REG_MAX) {
254 uint16_t value16 = value << (8 * (address & 1));
255 value16 |= (ds->memory.io9[(address & 0x1FFF) >> 1]) & ~(0xFF << (8 * (address & 1)));
256 DS9IOWrite(ds, address & 0xFFFFFFFE, value16);
257 } else {
258 mLOG(DS, STUB, "Writing to unknown DS9 register: %08X:%02X", address, value);
259 }
260}
261
262void DS9IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
263 switch (address) {
264 case DS_REG_DMA0SAD_LO:
265 value = DSDMAWriteSAD(&ds->ds9, 0, value);
266 break;
267 case DS_REG_DMA1SAD_LO:
268 value = DSDMAWriteSAD(&ds->ds9, 1, value);
269 break;
270 case DS_REG_DMA2SAD_LO:
271 value = DSDMAWriteSAD(&ds->ds9, 2, value);
272 break;
273 case DS_REG_DMA3SAD_LO:
274 value = DSDMAWriteSAD(&ds->ds9, 3, value);
275 break;
276
277 case DS_REG_DMA0DAD_LO:
278 value = DSDMAWriteDAD(&ds->ds9, 0, value);
279 break;
280 case DS_REG_DMA1DAD_LO:
281 value = DSDMAWriteDAD(&ds->ds9, 1, value);
282 break;
283 case DS_REG_DMA2DAD_LO:
284 value = DSDMAWriteDAD(&ds->ds9, 2, value);
285 break;
286 case DS_REG_DMA3DAD_LO:
287 value = DSDMAWriteDAD(&ds->ds9, 3, value);
288 break;
289
290 case DS_REG_DMA0CNT_LO:
291 DS9DMAWriteCNT(&ds->ds9, 0, value);
292 break;
293 case DS_REG_DMA1CNT_LO:
294 DS9DMAWriteCNT(&ds->ds9, 1, value);
295 break;
296 case DS_REG_DMA2CNT_LO:
297 DS9DMAWriteCNT(&ds->ds9, 2, value);
298 break;
299 case DS_REG_DMA3CNT_LO:
300 DS9DMAWriteCNT(&ds->ds9, 3, value);
301 break;
302
303 case DS_REG_IPCFIFOSEND_LO:
304 DSIPCWriteFIFO(&ds->ds9, value);
305 break;
306 case DS_REG_IE_LO:
307 DSWriteIE(ds->ds9.cpu, ds->ds9.memory.io, value);
308 break;
309 default:
310 DS9IOWrite(ds, address, value & 0xFFFF);
311 DS9IOWrite(ds, address | 2, value >> 16);
312 return;
313 }
314 ds->ds9.memory.io[address >> 1] = value;
315 ds->ds9.memory.io[(address >> 1) + 1] = value >> 16;
316}
317
318uint16_t DS9IORead(struct DS* ds, uint32_t address) {
319 switch (address) {
320 case DS_REG_TM0CNT_LO:
321 case DS_REG_TM1CNT_LO:
322 case DS_REG_TM2CNT_LO:
323 case DS_REG_TM3CNT_LO:
324 DSIOUpdateTimer(&ds->ds9, address);
325 break;
326 case DS_REG_TM0CNT_HI:
327 case DS_REG_TM1CNT_HI:
328 case DS_REG_TM2CNT_HI:
329 case DS_REG_TM3CNT_HI:
330 case DS_REG_IPCSYNC:
331 case DS_REG_IME:
332 case DS_REG_IE_LO:
333 case DS_REG_IE_HI:
334 case DS_REG_IF_LO:
335 case DS_REG_IF_HI:
336 // Handled transparently by the registers
337 break;
338 default:
339 mLOG(DS_IO, STUB, "Stub DS9 I/O register read: %06X", address);
340 }
341 if (address < DS9_REG_MAX) {
342 return ds->ds9.memory.io[address >> 1];
343 }
344 return 0;
345}