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
10mLOG_DEFINE_CATEGORY(DS_IO, "DS I/O");
11
12static void _writeIPCSync(struct ARMCore* remoteCpu, uint16_t* remoteIo, int16_t value) {
13 remoteIo[DS_REG_IPCSYNC >> 1] &= 0xFFF0;
14 remoteIo[DS_REG_IPCSYNC >> 1] |= (value >> 8) & 0x0F;
15 if (value & 0x2000 && remoteIo[DS_REG_IPCSYNC >> 1] & 0x4000) {
16 mLOG(DS_IO, STUB, "Unimplemented IPC IRQ");
17 UNUSED(remoteCpu);
18 }
19}
20
21static bool DSIOWrite(struct DSCommon* dscore, uint32_t address, uint16_t value) {
22 switch (address) {
23 // Timers
24 case DS_REG_TM0CNT_LO:
25 GBATimerWriteTMCNT_LO(&dscore->timers[0], value);
26 return true;
27 case DS_REG_TM1CNT_LO:
28 GBATimerWriteTMCNT_LO(&dscore->timers[1], value);
29 return true;
30 case DS_REG_TM2CNT_LO:
31 GBATimerWriteTMCNT_LO(&dscore->timers[2], value);
32 return true;
33 case DS_REG_TM3CNT_LO:
34 GBATimerWriteTMCNT_LO(&dscore->timers[3], value);
35 return true;
36
37 case DS_REG_TM0CNT_HI:
38 value &= 0x00C7;
39 DSTimerWriteTMCNT_HI(&dscore->timers[0], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM0CNT_LO >> 1], value);
40 break;
41 case DS_REG_TM1CNT_HI:
42 value &= 0x00C7;
43 DSTimerWriteTMCNT_HI(&dscore->timers[1], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM1CNT_LO >> 1], value);
44 break;
45 case DS_REG_TM2CNT_HI:
46 value &= 0x00C7;
47 DSTimerWriteTMCNT_HI(&dscore->timers[2], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM2CNT_LO >> 1], value);
48 break;
49 case DS_REG_TM3CNT_HI:
50 value &= 0x00C7;
51 DSTimerWriteTMCNT_HI(&dscore->timers[3], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM3CNT_LO >> 1], value);
52 break;
53
54 case DS_REG_IPCSYNC:
55 value &= 0x6F00;
56 value |= dscore->memory.io[address >> 1] & 0x000F;
57 _writeIPCSync(dscore->ipc->cpu, dscore->ipc->memory.io, value);
58 break;
59 case DS_REG_IME:
60 DSWriteIME(dscore->cpu, dscore->memory.io, value);
61 break;
62 case DS_REG_IF_LO:
63 case DS_REG_IF_HI:
64 value = dscore->memory.io[address >> 1] & ~value;
65 break;
66 default:
67 return false;
68 }
69 return true;
70}
71
72static void DSIOUpdateTimer(struct DSCommon* dscore, uint32_t address) {
73 switch (address) {
74 case DS_REG_TM0CNT_LO:
75 GBATimerUpdateRegisterInternal(&dscore->timers[0], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0);
76 break;
77 case DS_REG_TM1CNT_LO:
78 GBATimerUpdateRegisterInternal(&dscore->timers[1], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0);
79 break;
80 case DS_REG_TM2CNT_LO:
81 GBATimerUpdateRegisterInternal(&dscore->timers[2], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0);
82 break;
83 case DS_REG_TM3CNT_LO:
84 GBATimerUpdateRegisterInternal(&dscore->timers[3], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0);
85 break;
86 }
87}
88
89void DS7IOInit(struct DS* ds) {
90 memset(ds->memory.io7, 0, sizeof(ds->memory.io7));
91}
92
93void DS7IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
94 switch (address) {
95 default:
96 if (DSIOWrite(&ds->ds7, address, value)) {
97 break;
98 }
99 mLOG(DS_IO, STUB, "Stub DS7 I/O register write: %06X:%04X", address, value);
100 if (address >= DS7_REG_MAX) {
101 mLOG(DS_IO, GAME_ERROR, "Write to unused DS7 I/O register: %06X:%04X", address, value);
102 return;
103 }
104 break;
105 }
106 ds->memory.io7[address >> 1] = value;
107}
108
109void DS7IOWrite8(struct DS* ds, uint32_t address, uint8_t value) {
110 if (address < DS7_REG_MAX) {
111 uint16_t value16 = value << (8 * (address & 1));
112 value16 |= (ds->ds7.memory.io[(address & 0xFFF) >> 1]) & ~(0xFF << (8 * (address & 1)));
113 DS7IOWrite(ds, address & 0xFFFFFFFE, value16);
114 } else {
115 mLOG(DS, STUB, "Writing to unknown DS7 register: %08X:%02X", address, value);
116 }
117}
118
119void DS7IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
120 switch (address) {
121 case DS_REG_IE_LO:
122 DSWriteIE(ds->ds7.cpu, ds->ds7.memory.io, value);
123 break;
124 default:
125 DS7IOWrite(ds, address, value & 0xFFFF);
126 DS7IOWrite(ds, address | 2, value >> 16);
127 return;
128 }
129 ds->ds7.memory.io[address >> 1] = value;
130 ds->ds7.memory.io[(address >> 1) + 1] = value >> 16;
131}
132
133uint16_t DS7IORead(struct DS* ds, uint32_t address) {
134 switch (address) {
135 case DS_REG_TM0CNT_LO:
136 case DS_REG_TM1CNT_LO:
137 case DS_REG_TM2CNT_LO:
138 case DS_REG_TM3CNT_LO:
139 DSIOUpdateTimer(&ds->ds7, address);
140 break;
141 case DS_REG_TM0CNT_HI:
142 case DS_REG_TM1CNT_HI:
143 case DS_REG_TM2CNT_HI:
144 case DS_REG_TM3CNT_HI:
145 case DS_REG_IPCSYNC:
146 case DS_REG_IME:
147 case DS_REG_IE_LO:
148 case DS_REG_IE_HI:
149 case DS_REG_IF_LO:
150 case DS_REG_IF_HI:
151 // Handled transparently by the registers
152 break;
153 default:
154 mLOG(DS_IO, STUB, "Stub DS7 I/O register read: %06X", address);
155 }
156 if (address < DS7_REG_MAX) {
157 return ds->memory.io7[address >> 1];
158 }
159 return 0;
160}
161
162void DS9IOInit(struct DS* ds) {
163 memset(ds->memory.io9, 0, sizeof(ds->memory.io9));
164}
165
166void DS9IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
167 switch (address) {
168 default:
169 if (DSIOWrite(&ds->ds9, address, value)) {
170 break;
171 }
172 mLOG(DS_IO, STUB, "Stub DS9 I/O register write: %06X:%04X", address, value);
173 if (address >= DS7_REG_MAX) {
174 mLOG(DS_IO, GAME_ERROR, "Write to unused DS9 I/O register: %06X:%04X", address, value);
175 return;
176 }
177 break;
178 }
179 ds->memory.io9[address >> 1] = value;
180}
181
182void DS9IOWrite8(struct DS* ds, uint32_t address, uint8_t value) {
183 if (address < DS9_REG_MAX) {
184 uint16_t value16 = value << (8 * (address & 1));
185 value16 |= (ds->memory.io9[(address & 0x1FFF) >> 1]) & ~(0xFF << (8 * (address & 1)));
186 DS9IOWrite(ds, address & 0xFFFFFFFE, value16);
187 } else {
188 mLOG(DS, STUB, "Writing to unknown DS9 register: %08X:%02X", address, value);
189 }
190}
191
192void DS9IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
193 switch (address) {
194 default:
195 DS9IOWrite(ds, address, value & 0xFFFF);
196 DS9IOWrite(ds, address | 2, value >> 16);
197 return;
198 }
199 ds->ds9.memory.io[address >> 1] = value;
200 ds->ds9.memory.io[(address >> 1) + 1] = value >> 16;
201}
202
203uint16_t DS9IORead(struct DS* ds, uint32_t address) {
204 switch (address) {
205 case DS_REG_TM0CNT_LO:
206 case DS_REG_TM1CNT_LO:
207 case DS_REG_TM2CNT_LO:
208 case DS_REG_TM3CNT_LO:
209 DSIOUpdateTimer(&ds->ds9, address);
210 break;
211 case DS_REG_TM0CNT_HI:
212 case DS_REG_TM1CNT_HI:
213 case DS_REG_TM2CNT_HI:
214 case DS_REG_TM3CNT_HI:
215 case DS_REG_IPCSYNC:
216 case DS_REG_IME:
217 case DS_REG_IE_LO:
218 case DS_REG_IE_HI:
219 case DS_REG_IF_LO:
220 case DS_REG_IF_HI:
221 // Handled transparently by the registers
222 break;
223 default:
224 mLOG(DS_IO, STUB, "Stub DS9 I/O register read: %06X", address);
225 }
226 if (address < DS9_REG_MAX) {
227 return ds->ds9.memory.io[address >> 1];
228 }
229 return 0;
230}