all repos — mgba @ c50cfc3b941963fbba6d48c9ec22ecd03b8c935c

mGBA Game Boy Advance Emulator

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