all repos — mgba @ bb7bda6f80de86ad76448357394b8343635b839b

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 uint32_t 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 0x20000;
 19	case DS_REG_TM1CNT_LO:
 20		GBATimerWriteTMCNT_LO(&dscore->timers[1], value);
 21		return 0x20000;
 22	case DS_REG_TM2CNT_LO:
 23		GBATimerWriteTMCNT_LO(&dscore->timers[2], value);
 24		return 0x20000;
 25	case DS_REG_TM3CNT_LO:
 26		GBATimerWriteTMCNT_LO(&dscore->timers[3], value);
 27		return 0x20000;
 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 0;
 63	}
 64	return value | 0x10000;
 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		{
 92			uint32_t v2 = DSIOWrite(&ds->ds7, address, value);
 93			if (v2 & 0x10000) {
 94				value = v2;
 95				break;
 96			} else if (v2 & 0x20000) {
 97				return;
 98			}
 99		}
100		mLOG(DS_IO, STUB, "Stub DS7 I/O register write: %06X:%04X", address, value);
101		if (address >= DS7_REG_MAX) {
102			mLOG(DS_IO, GAME_ERROR, "Write to unused DS7 I/O register: %06X:%04X", address, value);
103			return;
104		}
105		break;
106	}
107	ds->memory.io7[address >> 1] = value;
108}
109
110void DS7IOWrite8(struct DS* ds, uint32_t address, uint8_t value) {
111	if (address < DS7_REG_MAX) {
112		uint16_t value16 = value << (8 * (address & 1));
113		value16 |= (ds->ds7.memory.io[(address & 0xFFF) >> 1]) & ~(0xFF << (8 * (address & 1)));
114		DS7IOWrite(ds, address & 0xFFFFFFFE, value16);
115	} else {
116		mLOG(DS, STUB, "Writing to unknown DS7 register: %08X:%02X", address, value);
117	}
118}
119
120void DS7IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
121	switch (address) {
122	case DS_REG_DMA0SAD_LO:
123		value = DSDMAWriteSAD(&ds->ds7, 0, value);
124		break;
125	case DS_REG_DMA1SAD_LO:
126		value = DSDMAWriteSAD(&ds->ds7, 1, value);
127		break;
128	case DS_REG_DMA2SAD_LO:
129		value = DSDMAWriteSAD(&ds->ds7, 2, value);
130		break;
131	case DS_REG_DMA3SAD_LO:
132		value = DSDMAWriteSAD(&ds->ds7, 3, value);
133		break;
134
135	case DS_REG_DMA0DAD_LO:
136		value = DSDMAWriteDAD(&ds->ds7, 0, value);
137		break;
138	case DS_REG_DMA1DAD_LO:
139		value = DSDMAWriteDAD(&ds->ds7, 1, value);
140		break;
141	case DS_REG_DMA2DAD_LO:
142		value = DSDMAWriteDAD(&ds->ds7, 2, value);
143		break;
144	case DS_REG_DMA3DAD_LO:
145		value = DSDMAWriteDAD(&ds->ds7, 3, value);
146		break;
147
148	case DS_REG_DMA0CNT_LO:
149		DS7DMAWriteCNT(&ds->ds7, 0, value);
150		break;
151	case DS_REG_DMA1CNT_LO:
152		DS7DMAWriteCNT(&ds->ds7, 1, value);
153		break;
154	case DS_REG_DMA2CNT_LO:
155		DS7DMAWriteCNT(&ds->ds7, 2, value);
156		break;
157	case DS_REG_DMA3CNT_LO:
158		DS7DMAWriteCNT(&ds->ds7, 3, value);
159		break;
160
161	case DS_REG_IPCFIFOSEND_LO:
162		DSIPCWriteFIFO(&ds->ds7, value);
163		break;
164	case DS_REG_IE_LO:
165		DSWriteIE(ds->ds7.cpu, ds->ds7.memory.io, value);
166		break;
167	default:
168		DS7IOWrite(ds, address, value & 0xFFFF);
169		DS7IOWrite(ds, address | 2, value >> 16);
170		return;
171	}
172	ds->ds7.memory.io[address >> 1] = value;
173	ds->ds7.memory.io[(address >> 1) + 1] = value >> 16;
174}
175
176uint16_t DS7IORead(struct DS* ds, uint32_t address) {
177	switch (address) {
178	case DS_REG_TM0CNT_LO:
179	case DS_REG_TM1CNT_LO:
180	case DS_REG_TM2CNT_LO:
181	case DS_REG_TM3CNT_LO:
182		DSIOUpdateTimer(&ds->ds7, address);
183		break;
184	case DS_REG_TM0CNT_HI:
185	case DS_REG_TM1CNT_HI:
186	case DS_REG_TM2CNT_HI:
187	case DS_REG_TM3CNT_HI:
188	case DS_REG_IPCSYNC:
189	case DS_REG_IME:
190	case DS_REG_IE_LO:
191	case DS_REG_IE_HI:
192	case DS_REG_IF_LO:
193	case DS_REG_IF_HI:
194		// Handled transparently by the registers
195		break;
196	default:
197		mLOG(DS_IO, STUB, "Stub DS7 I/O register read: %06X", address);
198	}
199	if (address < DS7_REG_MAX) {
200		return ds->memory.io7[address >> 1];
201	}
202	return 0;
203}
204
205void DS9IOInit(struct DS* ds) {
206	memset(ds->memory.io9, 0, sizeof(ds->memory.io9));
207}
208
209void DS9IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
210	switch (address) {
211	default:
212		{
213			uint32_t v2 = DSIOWrite(&ds->ds9, address, value);
214			if (v2 & 0x10000) {
215				value = v2;
216				break;
217			} else if (v2 & 0x20000) {
218				return;
219			}
220		}
221		mLOG(DS_IO, STUB, "Stub DS9 I/O register write: %06X:%04X", address, value);
222		if (address >= DS7_REG_MAX) {
223			mLOG(DS_IO, GAME_ERROR, "Write to unused DS9 I/O register: %06X:%04X", address, value);
224			return;
225		}
226		break;
227	}
228	ds->memory.io9[address >> 1] = value;
229}
230
231void DS9IOWrite8(struct DS* ds, uint32_t address, uint8_t value) {
232	if (address < DS9_REG_MAX) {
233		uint16_t value16 = value << (8 * (address & 1));
234		value16 |= (ds->memory.io9[(address & 0x1FFF) >> 1]) & ~(0xFF << (8 * (address & 1)));
235		DS9IOWrite(ds, address & 0xFFFFFFFE, value16);
236	} else {
237		mLOG(DS, STUB, "Writing to unknown DS9 register: %08X:%02X", address, value);
238	}
239}
240
241void DS9IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
242	switch (address) {
243	case DS_REG_DMA0SAD_LO:
244		value = DSDMAWriteSAD(&ds->ds9, 0, value);
245		break;
246	case DS_REG_DMA1SAD_LO:
247		value = DSDMAWriteSAD(&ds->ds9, 1, value);
248		break;
249	case DS_REG_DMA2SAD_LO:
250		value = DSDMAWriteSAD(&ds->ds9, 2, value);
251		break;
252	case DS_REG_DMA3SAD_LO:
253		value = DSDMAWriteSAD(&ds->ds9, 3, value);
254		break;
255
256	case DS_REG_DMA0DAD_LO:
257		value = DSDMAWriteDAD(&ds->ds9, 0, value);
258		break;
259	case DS_REG_DMA1DAD_LO:
260		value = DSDMAWriteDAD(&ds->ds9, 1, value);
261		break;
262	case DS_REG_DMA2DAD_LO:
263		value = DSDMAWriteDAD(&ds->ds9, 2, value);
264		break;
265	case DS_REG_DMA3DAD_LO:
266		value = DSDMAWriteDAD(&ds->ds9, 3, value);
267		break;
268
269	case DS_REG_DMA0CNT_LO:
270		DS9DMAWriteCNT(&ds->ds9, 0, value);
271		break;
272	case DS_REG_DMA1CNT_LO:
273		DS9DMAWriteCNT(&ds->ds9, 1, value);
274		break;
275	case DS_REG_DMA2CNT_LO:
276		DS9DMAWriteCNT(&ds->ds9, 2, value);
277		break;
278	case DS_REG_DMA3CNT_LO:
279		DS9DMAWriteCNT(&ds->ds9, 3, value);
280		break;
281
282	case DS_REG_IPCFIFOSEND_LO:
283		DSIPCWriteFIFO(&ds->ds9, value);
284		break;
285	case DS_REG_IE_LO:
286		DSWriteIE(ds->ds9.cpu, ds->ds9.memory.io, value);
287		break;
288	default:
289		DS9IOWrite(ds, address, value & 0xFFFF);
290		DS9IOWrite(ds, address | 2, value >> 16);
291		return;
292	}
293	ds->ds9.memory.io[address >> 1] = value;
294	ds->ds9.memory.io[(address >> 1) + 1] = value >> 16;
295}
296
297uint16_t DS9IORead(struct DS* ds, uint32_t address) {
298	switch (address) {
299	case DS_REG_TM0CNT_LO:
300	case DS_REG_TM1CNT_LO:
301	case DS_REG_TM2CNT_LO:
302	case DS_REG_TM3CNT_LO:
303		DSIOUpdateTimer(&ds->ds9, address);
304		break;
305	case DS_REG_TM0CNT_HI:
306	case DS_REG_TM1CNT_HI:
307	case DS_REG_TM2CNT_HI:
308	case DS_REG_TM3CNT_HI:
309	case DS_REG_IPCSYNC:
310	case DS_REG_IME:
311	case DS_REG_IE_LO:
312	case DS_REG_IE_HI:
313	case DS_REG_IF_LO:
314	case DS_REG_IF_HI:
315		// Handled transparently by the registers
316		break;
317	default:
318		mLOG(DS_IO, STUB, "Stub DS9 I/O register read: %06X", address);
319	}
320	if (address < DS9_REG_MAX) {
321		return ds->ds9.memory.io[address >> 1];
322	}
323	return 0;
324}