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[DS7_REG_IPCSYNC >> 1] &= 0xFFF0;
14 remoteIo[DS7_REG_IPCSYNC >> 1] |= (value >> 8) & 0x0F;
15 if (value & 0x2000 && remoteIo[DS7_REG_IPCSYNC >> 1] & 0x4000) {
16 mLOG(DS_IO, STUB, "Unimplemented IPC IRQ");
17 UNUSED(remoteCpu);
18 }
19}
20
21void DS7IOInit(struct DS* ds) {
22 memset(ds->memory.io7, 0, sizeof(ds->memory.io7));
23}
24
25void DS7IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
26 switch (address) {
27 // Timers
28 case DS7_REG_TM0CNT_LO:
29 GBATimerWriteTMCNT_LO(&ds->timers7[0], value);
30 return;
31 case DS7_REG_TM1CNT_LO:
32 GBATimerWriteTMCNT_LO(&ds->timers7[1], value);
33 return;
34 case DS7_REG_TM2CNT_LO:
35 GBATimerWriteTMCNT_LO(&ds->timers7[2], value);
36 return;
37 case DS7_REG_TM3CNT_LO:
38 GBATimerWriteTMCNT_LO(&ds->timers7[3], value);
39 return;
40
41 case DS7_REG_TM0CNT_HI:
42 value &= 0x00C7;
43 DSTimerWriteTMCNT_HI(&ds->timers7[0], &ds->timing7, ds->arm7, &ds->memory.io7[DS7_REG_TM0CNT_LO >> 1], value);
44 break;
45 case DS7_REG_TM1CNT_HI:
46 value &= 0x00C7;
47 DSTimerWriteTMCNT_HI(&ds->timers7[1], &ds->timing7, ds->arm7, &ds->memory.io7[DS7_REG_TM1CNT_LO >> 1], value);
48 break;
49 case DS7_REG_TM2CNT_HI:
50 value &= 0x00C7;
51 DSTimerWriteTMCNT_HI(&ds->timers7[2], &ds->timing7, ds->arm7, &ds->memory.io7[DS7_REG_TM2CNT_LO >> 1], value);
52 break;
53 case DS7_REG_TM3CNT_HI:
54 value &= 0x00C7;
55 DSTimerWriteTMCNT_HI(&ds->timers7[3], &ds->timing7, ds->arm7, &ds->memory.io7[DS7_REG_TM3CNT_LO >> 1], value);
56 break;
57
58 case DS7_REG_IPCSYNC:
59 value &= 0x6F00;
60 value |= ds->memory.io7[address >> 1] & 0x000F;
61 _writeIPCSync(ds->arm9, ds->memory.io9, value);
62 break;
63 case DS7_REG_IME:
64 DSWriteIME(ds->arm7, ds->memory.io7, value);
65 break;
66 case DS7_REG_IF_LO:
67 case DS7_REG_IF_HI:
68 value = ds->memory.io7[address >> 1] & ~value;
69 break;
70 default:
71 mLOG(DS_IO, STUB, "Stub DS7 I/O register write: %06X:%04X", address, value);
72 if (address >= DS7_REG_MAX) {
73 mLOG(DS_IO, GAME_ERROR, "Write to unused DS7 I/O register: %06X:%04X", address, value);
74 return;
75 }
76 break;
77 }
78 ds->memory.io7[address >> 1] = value;
79}
80
81void DS7IOWrite8(struct DS* ds, uint32_t address, uint8_t value) {
82 if (address < DS7_REG_MAX) {
83 uint16_t value16 = value << (8 * (address & 1));
84 value16 |= (ds->memory.io7[(address & 0xFFF) >> 1]) & ~(0xFF << (8 * (address & 1)));
85 DS7IOWrite(ds, address & 0xFFFFFFFE, value16);
86 } else {
87 mLOG(DS, STUB, "Writing to unknown DS7 register: %08X:%02X", address, value);
88 }
89}
90
91void DS7IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
92 switch (address) {
93 case DS7_REG_IE_LO:
94 DSWriteIE(ds->arm7, ds->memory.io7, value);
95 break;
96 default:
97 DS7IOWrite(ds, address, value & 0xFFFF);
98 DS7IOWrite(ds, address | 2, value >> 16);
99 return;
100 }
101 ds->memory.io7[address >> 1] = value;
102 ds->memory.io7[(address >> 1) + 1] = value >> 16;
103}
104
105uint16_t DS7IORead(struct DS* ds, uint32_t address) {
106 switch (address) {
107 case DS7_REG_TM0CNT_LO:
108 GBATimerUpdateRegisterInternal(&ds->timers7[0], &ds->timing7, ds->arm7, &ds->memory.io7[address >> 1], 0);
109 break;
110 case DS7_REG_TM1CNT_LO:
111 GBATimerUpdateRegisterInternal(&ds->timers7[1], &ds->timing7, ds->arm7, &ds->memory.io7[address >> 1], 0);
112 break;
113 case DS7_REG_TM2CNT_LO:
114 GBATimerUpdateRegisterInternal(&ds->timers7[2], &ds->timing7, ds->arm7, &ds->memory.io7[address >> 1], 0);
115 break;
116 case DS7_REG_TM3CNT_LO:
117 GBATimerUpdateRegisterInternal(&ds->timers7[3], &ds->timing7, ds->arm7, &ds->memory.io7[address >> 1], 0);
118 break;
119
120 case DS7_REG_TM0CNT_HI:
121 case DS7_REG_TM1CNT_HI:
122 case DS7_REG_TM2CNT_HI:
123 case DS7_REG_TM3CNT_HI:
124 case DS7_REG_IPCSYNC:
125 case DS7_REG_IME:
126 case DS7_REG_IE_LO:
127 case DS7_REG_IE_HI:
128 case DS7_REG_IF_LO:
129 case DS7_REG_IF_HI:
130 // Handled transparently by the registers
131 break;
132 default:
133 mLOG(DS_IO, STUB, "Stub DS7 I/O register read: %06X", address);
134 }
135 if (address < DS7_REG_MAX) {
136 return ds->memory.io7[address >> 1];
137 }
138 return 0;
139}
140
141void DS9IOInit(struct DS* ds) {
142 memset(ds->memory.io9, 0, sizeof(ds->memory.io9));
143}
144
145void DS9IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
146 switch (address) {
147 case DS9_REG_IPCSYNC:
148 value &= 0x6F00;
149 value |= ds->memory.io9[address >> 1] & 0x000F;
150 _writeIPCSync(ds->arm7, ds->memory.io7, value);
151 break;
152 default:
153 mLOG(DS_IO, STUB, "Stub DS9 I/O register write: %06X:%04X", address, value);
154 if (address >= DS7_REG_MAX) {
155 mLOG(DS_IO, GAME_ERROR, "Write to unused DS9 I/O register: %06X:%04X", address, value);
156 return;
157 }
158 break;
159 }
160 ds->memory.io9[address >> 1] = value;
161}
162
163void DS9IOWrite8(struct DS* ds, uint32_t address, uint8_t value) {
164 if (address < DS9_REG_MAX) {
165 uint16_t value16 = value << (8 * (address & 1));
166 value16 |= (ds->memory.io9[(address & 0x1FFF) >> 1]) & ~(0xFF << (8 * (address & 1)));
167 DS9IOWrite(ds, address & 0xFFFFFFFE, value16);
168 } else {
169 mLOG(DS, STUB, "Writing to unknown DS9 register: %08X:%02X", address, value);
170 }
171}
172
173void DS9IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
174 switch (address) {
175 default:
176 DS9IOWrite(ds, address, value & 0xFFFF);
177 DS9IOWrite(ds, address | 2, value >> 16);
178 return;
179 }
180 ds->memory.io9[address >> 1] = value;
181 ds->memory.io9[(address >> 1) + 1] = value >> 16;
182}
183
184uint16_t DS9IORead(struct DS* ds, uint32_t address) {
185 switch (address) {
186 case DS9_REG_IPCSYNC:
187 // Handled transparently by the registers
188 break;
189 default:
190 mLOG(DS_IO, STUB, "Stub DS9 I/O register read: %06X", address);
191 }
192 if (address < DS9_REG_MAX) {
193 return ds->memory.io9[address >> 1];
194 }
195 return 0;
196}