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 bool 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 true;
19 case DS_REG_TM1CNT_LO:
20 GBATimerWriteTMCNT_LO(&dscore->timers[1], value);
21 return true;
22 case DS_REG_TM2CNT_LO:
23 GBATimerWriteTMCNT_LO(&dscore->timers[2], value);
24 return true;
25 case DS_REG_TM3CNT_LO:
26 GBATimerWriteTMCNT_LO(&dscore->timers[3], value);
27 return true;
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 false;
63 }
64 return true;
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 if (DSIOWrite(&ds->ds7, address, value)) {
92 break;
93 }
94 mLOG(DS_IO, STUB, "Stub DS7 I/O register write: %06X:%04X", address, value);
95 if (address >= DS7_REG_MAX) {
96 mLOG(DS_IO, GAME_ERROR, "Write to unused DS7 I/O register: %06X:%04X", address, value);
97 return;
98 }
99 break;
100 }
101 ds->memory.io7[address >> 1] = value;
102}
103
104void DS7IOWrite8(struct DS* ds, uint32_t address, uint8_t value) {
105 if (address < DS7_REG_MAX) {
106 uint16_t value16 = value << (8 * (address & 1));
107 value16 |= (ds->ds7.memory.io[(address & 0xFFF) >> 1]) & ~(0xFF << (8 * (address & 1)));
108 DS7IOWrite(ds, address & 0xFFFFFFFE, value16);
109 } else {
110 mLOG(DS, STUB, "Writing to unknown DS7 register: %08X:%02X", address, value);
111 }
112}
113
114void DS7IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
115 switch (address) {
116 case DS_REG_DMA0SAD_LO:
117 value = DSDMAWriteSAD(&ds->ds7, 0, value);
118 break;
119 case DS_REG_DMA1SAD_LO:
120 value = DSDMAWriteSAD(&ds->ds7, 1, value);
121 break;
122 case DS_REG_DMA2SAD_LO:
123 value = DSDMAWriteSAD(&ds->ds7, 2, value);
124 break;
125 case DS_REG_DMA3SAD_LO:
126 value = DSDMAWriteSAD(&ds->ds7, 3, value);
127 break;
128
129 case DS_REG_DMA0DAD_LO:
130 value = DSDMAWriteDAD(&ds->ds7, 0, value);
131 break;
132 case DS_REG_DMA1DAD_LO:
133 value = DSDMAWriteDAD(&ds->ds7, 1, value);
134 break;
135 case DS_REG_DMA2DAD_LO:
136 value = DSDMAWriteDAD(&ds->ds7, 2, value);
137 break;
138 case DS_REG_DMA3DAD_LO:
139 value = DSDMAWriteDAD(&ds->ds7, 3, value);
140 break;
141
142 case DS_REG_DMA0CNT_LO:
143 DS7DMAWriteCNT(&ds->ds7, 0, value);
144 break;
145 case DS_REG_DMA1CNT_LO:
146 DS7DMAWriteCNT(&ds->ds7, 1, value);
147 break;
148 case DS_REG_DMA2CNT_LO:
149 DS7DMAWriteCNT(&ds->ds7, 2, value);
150 break;
151 case DS_REG_DMA3CNT_LO:
152 DS7DMAWriteCNT(&ds->ds7, 3, value);
153 break;
154
155 case DS_REG_IPCFIFOSEND_LO:
156 DSIPCWriteFIFO(&ds->ds7, value);
157 break;
158 case DS_REG_IE_LO:
159 DSWriteIE(ds->ds7.cpu, ds->ds7.memory.io, value);
160 break;
161 default:
162 DS7IOWrite(ds, address, value & 0xFFFF);
163 DS7IOWrite(ds, address | 2, value >> 16);
164 return;
165 }
166 ds->ds7.memory.io[address >> 1] = value;
167 ds->ds7.memory.io[(address >> 1) + 1] = value >> 16;
168}
169
170uint16_t DS7IORead(struct DS* ds, uint32_t address) {
171 switch (address) {
172 case DS_REG_TM0CNT_LO:
173 case DS_REG_TM1CNT_LO:
174 case DS_REG_TM2CNT_LO:
175 case DS_REG_TM3CNT_LO:
176 DSIOUpdateTimer(&ds->ds7, address);
177 break;
178 case DS_REG_TM0CNT_HI:
179 case DS_REG_TM1CNT_HI:
180 case DS_REG_TM2CNT_HI:
181 case DS_REG_TM3CNT_HI:
182 case DS_REG_IPCSYNC:
183 case DS_REG_IME:
184 case DS_REG_IE_LO:
185 case DS_REG_IE_HI:
186 case DS_REG_IF_LO:
187 case DS_REG_IF_HI:
188 // Handled transparently by the registers
189 break;
190 default:
191 mLOG(DS_IO, STUB, "Stub DS7 I/O register read: %06X", address);
192 }
193 if (address < DS7_REG_MAX) {
194 return ds->memory.io7[address >> 1];
195 }
196 return 0;
197}
198
199void DS9IOInit(struct DS* ds) {
200 memset(ds->memory.io9, 0, sizeof(ds->memory.io9));
201}
202
203void DS9IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
204 switch (address) {
205 default:
206 if (DSIOWrite(&ds->ds9, address, value)) {
207 break;
208 }
209 mLOG(DS_IO, STUB, "Stub DS9 I/O register write: %06X:%04X", address, value);
210 if (address >= DS7_REG_MAX) {
211 mLOG(DS_IO, GAME_ERROR, "Write to unused DS9 I/O register: %06X:%04X", address, value);
212 return;
213 }
214 break;
215 }
216 ds->memory.io9[address >> 1] = value;
217}
218
219void DS9IOWrite8(struct DS* ds, uint32_t address, uint8_t value) {
220 if (address < DS9_REG_MAX) {
221 uint16_t value16 = value << (8 * (address & 1));
222 value16 |= (ds->memory.io9[(address & 0x1FFF) >> 1]) & ~(0xFF << (8 * (address & 1)));
223 DS9IOWrite(ds, address & 0xFFFFFFFE, value16);
224 } else {
225 mLOG(DS, STUB, "Writing to unknown DS9 register: %08X:%02X", address, value);
226 }
227}
228
229void DS9IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
230 switch (address) {
231 case DS_REG_DMA0SAD_LO:
232 value = DSDMAWriteSAD(&ds->ds9, 0, value);
233 break;
234 case DS_REG_DMA1SAD_LO:
235 value = DSDMAWriteSAD(&ds->ds9, 1, value);
236 break;
237 case DS_REG_DMA2SAD_LO:
238 value = DSDMAWriteSAD(&ds->ds9, 2, value);
239 break;
240 case DS_REG_DMA3SAD_LO:
241 value = DSDMAWriteSAD(&ds->ds9, 3, value);
242 break;
243
244 case DS_REG_DMA0DAD_LO:
245 value = DSDMAWriteDAD(&ds->ds9, 0, value);
246 break;
247 case DS_REG_DMA1DAD_LO:
248 value = DSDMAWriteDAD(&ds->ds9, 1, value);
249 break;
250 case DS_REG_DMA2DAD_LO:
251 value = DSDMAWriteDAD(&ds->ds9, 2, value);
252 break;
253 case DS_REG_DMA3DAD_LO:
254 value = DSDMAWriteDAD(&ds->ds9, 3, value);
255 break;
256
257 case DS_REG_DMA0CNT_LO:
258 DS9DMAWriteCNT(&ds->ds9, 0, value);
259 break;
260 case DS_REG_DMA1CNT_LO:
261 DS9DMAWriteCNT(&ds->ds9, 1, value);
262 break;
263 case DS_REG_DMA2CNT_LO:
264 DS9DMAWriteCNT(&ds->ds9, 2, value);
265 break;
266 case DS_REG_DMA3CNT_LO:
267 DS9DMAWriteCNT(&ds->ds9, 3, value);
268 break;
269
270 case DS_REG_IPCFIFOSEND_LO:
271 DSIPCWriteFIFO(&ds->ds9, value);
272 break;
273 case DS_REG_IE_LO:
274 DSWriteIE(ds->ds9.cpu, ds->ds9.memory.io, value);
275 break;
276 default:
277 DS9IOWrite(ds, address, value & 0xFFFF);
278 DS9IOWrite(ds, address | 2, value >> 16);
279 return;
280 }
281 ds->ds9.memory.io[address >> 1] = value;
282 ds->ds9.memory.io[(address >> 1) + 1] = value >> 16;
283}
284
285uint16_t DS9IORead(struct DS* ds, uint32_t address) {
286 switch (address) {
287 case DS_REG_TM0CNT_LO:
288 case DS_REG_TM1CNT_LO:
289 case DS_REG_TM2CNT_LO:
290 case DS_REG_TM3CNT_LO:
291 DSIOUpdateTimer(&ds->ds9, address);
292 break;
293 case DS_REG_TM0CNT_HI:
294 case DS_REG_TM1CNT_HI:
295 case DS_REG_TM2CNT_HI:
296 case DS_REG_TM3CNT_HI:
297 case DS_REG_IPCSYNC:
298 case DS_REG_IME:
299 case DS_REG_IE_LO:
300 case DS_REG_IE_HI:
301 case DS_REG_IF_LO:
302 case DS_REG_IF_HI:
303 // Handled transparently by the registers
304 break;
305 default:
306 mLOG(DS_IO, STUB, "Stub DS9 I/O register read: %06X", address);
307 }
308 if (address < DS9_REG_MAX) {
309 return ds->ds9.memory.io[address >> 1];
310 }
311 return 0;
312}