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