all repos — mgba @ 793cad8f301e4f42bcf682ff5d324b63c4561ee7

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_IPCFIFOCNT:
211	case DS_REG_IME:
212	case DS_REG_IE_LO:
213	case DS_REG_IE_HI:
214	case DS_REG_IF_LO:
215	case DS_REG_IF_HI:
216		// Handled transparently by the registers
217		break;
218	default:
219		mLOG(DS_IO, STUB, "Stub DS7 I/O register read: %06X", address);
220	}
221	if (address < DS7_REG_MAX) {
222		return ds->memory.io7[address >> 1];
223	}
224	return 0;
225}
226
227uint32_t DS7IORead32(struct DS* ds, uint32_t address) {
228	switch (address) {
229	case DS_REG_IPCFIFORECV_LO:
230		return DSIPCReadFIFO(&ds->ds7);
231	default:
232		return DS7IORead(ds, address & 0x00FFFFFC) | (DS7IORead(ds, (address & 0x00FFFFFC) | 2) << 16);
233	}
234}
235
236void DS9IOInit(struct DS* ds) {
237	memset(ds->memory.io9, 0, sizeof(ds->memory.io9));
238}
239
240void DS9IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
241	switch (address) {
242	default:
243		{
244			uint32_t v2 = DSIOWrite(&ds->ds9, address, value);
245			if (v2 & 0x10000) {
246				value = v2;
247				break;
248			} else if (v2 & 0x20000) {
249				return;
250			}
251		}
252		mLOG(DS_IO, STUB, "Stub DS9 I/O register write: %06X:%04X", address, value);
253		if (address >= DS7_REG_MAX) {
254			mLOG(DS_IO, GAME_ERROR, "Write to unused DS9 I/O register: %06X:%04X", address, value);
255			return;
256		}
257		break;
258	}
259	ds->memory.io9[address >> 1] = value;
260}
261
262void DS9IOWrite8(struct DS* ds, uint32_t address, uint8_t value) {
263	if (address < DS9_REG_MAX) {
264		uint16_t value16 = value << (8 * (address & 1));
265		value16 |= (ds->memory.io9[(address & 0x1FFF) >> 1]) & ~(0xFF << (8 * (address & 1)));
266		DS9IOWrite(ds, address & 0xFFFFFFFE, value16);
267	} else {
268		mLOG(DS, STUB, "Writing to unknown DS9 register: %08X:%02X", address, value);
269	}
270}
271
272void DS9IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
273	switch (address) {
274	case DS_REG_DMA0SAD_LO:
275		value = DSDMAWriteSAD(&ds->ds9, 0, value);
276		break;
277	case DS_REG_DMA1SAD_LO:
278		value = DSDMAWriteSAD(&ds->ds9, 1, value);
279		break;
280	case DS_REG_DMA2SAD_LO:
281		value = DSDMAWriteSAD(&ds->ds9, 2, value);
282		break;
283	case DS_REG_DMA3SAD_LO:
284		value = DSDMAWriteSAD(&ds->ds9, 3, value);
285		break;
286
287	case DS_REG_DMA0DAD_LO:
288		value = DSDMAWriteDAD(&ds->ds9, 0, value);
289		break;
290	case DS_REG_DMA1DAD_LO:
291		value = DSDMAWriteDAD(&ds->ds9, 1, value);
292		break;
293	case DS_REG_DMA2DAD_LO:
294		value = DSDMAWriteDAD(&ds->ds9, 2, value);
295		break;
296	case DS_REG_DMA3DAD_LO:
297		value = DSDMAWriteDAD(&ds->ds9, 3, value);
298		break;
299
300	case DS_REG_DMA0CNT_LO:
301		DS9DMAWriteCNT(&ds->ds9, 0, value);
302		break;
303	case DS_REG_DMA1CNT_LO:
304		DS9DMAWriteCNT(&ds->ds9, 1, value);
305		break;
306	case DS_REG_DMA2CNT_LO:
307		DS9DMAWriteCNT(&ds->ds9, 2, value);
308		break;
309	case DS_REG_DMA3CNT_LO:
310		DS9DMAWriteCNT(&ds->ds9, 3, value);
311		break;
312
313	case DS_REG_IPCFIFOSEND_LO:
314		DSIPCWriteFIFO(&ds->ds9, value);
315		break;
316	case DS_REG_IE_LO:
317		DSWriteIE(ds->ds9.cpu, ds->ds9.memory.io, value);
318		break;
319	default:
320		DS9IOWrite(ds, address, value & 0xFFFF);
321		DS9IOWrite(ds, address | 2, value >> 16);
322		return;
323	}
324	ds->ds9.memory.io[address >> 1] = value;
325	ds->ds9.memory.io[(address >> 1) + 1] = value >> 16;
326}
327
328uint16_t DS9IORead(struct DS* ds, uint32_t address) {
329	switch (address) {
330	case DS_REG_TM0CNT_LO:
331	case DS_REG_TM1CNT_LO:
332	case DS_REG_TM2CNT_LO:
333	case DS_REG_TM3CNT_LO:
334		DSIOUpdateTimer(&ds->ds9, address);
335		break;
336	case DS_REG_TM0CNT_HI:
337	case DS_REG_TM1CNT_HI:
338	case DS_REG_TM2CNT_HI:
339	case DS_REG_TM3CNT_HI:
340	case DS_REG_IPCSYNC:
341	case DS_REG_IPCFIFOCNT:
342	case DS_REG_IME:
343	case DS_REG_IE_LO:
344	case DS_REG_IE_HI:
345	case DS_REG_IF_LO:
346	case DS_REG_IF_HI:
347		// Handled transparently by the registers
348		break;
349	default:
350		mLOG(DS_IO, STUB, "Stub DS9 I/O register read: %06X", address);
351	}
352	if (address < DS9_REG_MAX) {
353		return ds->ds9.memory.io[address >> 1];
354	}
355	return 0;
356}
357
358uint32_t DS9IORead32(struct DS* ds, uint32_t address) {
359	switch (address) {
360	case DS_REG_IPCFIFORECV_LO:
361		return DSIPCReadFIFO(&ds->ds9);
362	default:
363		return DS9IORead(ds, address & 0x00FFFFFC) | (DS9IORead(ds, (address & 0x00FFFFFC) | 2) << 16);
364	}
365}