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_IPCFIFOSEND_LO:
117 DSIPCWriteFIFO(&ds->ds9, value);
118 break;
119 case DS_REG_IE_LO:
120 DSWriteIE(ds->ds7.cpu, ds->ds7.memory.io, value);
121 break;
122 default:
123 DS7IOWrite(ds, address, value & 0xFFFF);
124 DS7IOWrite(ds, address | 2, value >> 16);
125 return;
126 }
127 ds->ds7.memory.io[address >> 1] = value;
128 ds->ds7.memory.io[(address >> 1) + 1] = value >> 16;
129}
130
131uint16_t DS7IORead(struct DS* ds, uint32_t address) {
132 switch (address) {
133 case DS_REG_TM0CNT_LO:
134 case DS_REG_TM1CNT_LO:
135 case DS_REG_TM2CNT_LO:
136 case DS_REG_TM3CNT_LO:
137 DSIOUpdateTimer(&ds->ds7, address);
138 break;
139 case DS_REG_TM0CNT_HI:
140 case DS_REG_TM1CNT_HI:
141 case DS_REG_TM2CNT_HI:
142 case DS_REG_TM3CNT_HI:
143 case DS_REG_IPCSYNC:
144 case DS_REG_IME:
145 case DS_REG_IE_LO:
146 case DS_REG_IE_HI:
147 case DS_REG_IF_LO:
148 case DS_REG_IF_HI:
149 // Handled transparently by the registers
150 break;
151 default:
152 mLOG(DS_IO, STUB, "Stub DS7 I/O register read: %06X", address);
153 }
154 if (address < DS7_REG_MAX) {
155 return ds->memory.io7[address >> 1];
156 }
157 return 0;
158}
159
160void DS9IOInit(struct DS* ds) {
161 memset(ds->memory.io9, 0, sizeof(ds->memory.io9));
162}
163
164void DS9IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
165 switch (address) {
166 default:
167 if (DSIOWrite(&ds->ds9, address, value)) {
168 break;
169 }
170 mLOG(DS_IO, STUB, "Stub DS9 I/O register write: %06X:%04X", address, value);
171 if (address >= DS7_REG_MAX) {
172 mLOG(DS_IO, GAME_ERROR, "Write to unused DS9 I/O register: %06X:%04X", address, value);
173 return;
174 }
175 break;
176 }
177 ds->memory.io9[address >> 1] = value;
178}
179
180void DS9IOWrite8(struct DS* ds, uint32_t address, uint8_t value) {
181 if (address < DS9_REG_MAX) {
182 uint16_t value16 = value << (8 * (address & 1));
183 value16 |= (ds->memory.io9[(address & 0x1FFF) >> 1]) & ~(0xFF << (8 * (address & 1)));
184 DS9IOWrite(ds, address & 0xFFFFFFFE, value16);
185 } else {
186 mLOG(DS, STUB, "Writing to unknown DS9 register: %08X:%02X", address, value);
187 }
188}
189
190void DS9IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
191 switch (address) {
192 case DS_REG_IPCFIFOSEND_LO:
193 DSIPCWriteFIFO(&ds->ds9, value);
194 break;
195 case DS_REG_IE_LO:
196 DSWriteIE(ds->ds9.cpu, ds->ds9.memory.io, value);
197 break;
198 default:
199 DS9IOWrite(ds, address, value & 0xFFFF);
200 DS9IOWrite(ds, address | 2, value >> 16);
201 return;
202 }
203 ds->ds9.memory.io[address >> 1] = value;
204 ds->ds9.memory.io[(address >> 1) + 1] = value >> 16;
205}
206
207uint16_t DS9IORead(struct DS* ds, uint32_t address) {
208 switch (address) {
209 case DS_REG_TM0CNT_LO:
210 case DS_REG_TM1CNT_LO:
211 case DS_REG_TM2CNT_LO:
212 case DS_REG_TM3CNT_LO:
213 DSIOUpdateTimer(&ds->ds9, address);
214 break;
215 case DS_REG_TM0CNT_HI:
216 case DS_REG_TM1CNT_HI:
217 case DS_REG_TM2CNT_HI:
218 case DS_REG_TM3CNT_HI:
219 case DS_REG_IPCSYNC:
220 case DS_REG_IME:
221 case DS_REG_IE_LO:
222 case DS_REG_IE_HI:
223 case DS_REG_IF_LO:
224 case DS_REG_IF_HI:
225 // Handled transparently by the registers
226 break;
227 default:
228 mLOG(DS_IO, STUB, "Stub DS9 I/O register read: %06X", address);
229 }
230 if (address < DS9_REG_MAX) {
231 return ds->ds9.memory.io[address >> 1];
232 }
233 return 0;
234}