all repos — mgba @ 4b6c00289c8d4d7b3a2a7f011503b02e0d67b053

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/core/interface.h>
  9#include <mgba/internal/ds/ds.h>
 10#include <mgba/internal/ds/ipc.h>
 11
 12mLOG_DEFINE_CATEGORY(DS_IO, "DS I/O");
 13
 14static void _DSHaltCNT(struct DSCommon* dscore, uint8_t value) {
 15	switch (value >> 6) {
 16	case 0:
 17	default:
 18		break;
 19	case 1:
 20		mLOG(DS_IO, STUB, "Enter GBA mode not supported");
 21		break;
 22	case 2:
 23		ARMHalt(dscore->cpu);
 24		break;
 25	case 3:
 26		mLOG(DS_IO, STUB, "Enter sleep mode not supported");
 27		break;
 28	}
 29}
 30
 31static uint32_t DSIOWrite(struct DSCommon* dscore, uint32_t address, uint16_t value) {
 32	switch (address) {
 33	// Video
 34	case DS_REG_DISPSTAT:
 35		DSVideoWriteDISPSTAT(dscore, value);
 36		break;
 37
 38	// DMA Fill
 39	case DS_REG_DMA0FILL_LO:
 40	case DS_REG_DMA0FILL_HI:
 41	case DS_REG_DMA1FILL_LO:
 42	case DS_REG_DMA1FILL_HI:
 43	case DS_REG_DMA2FILL_LO:
 44	case DS_REG_DMA2FILL_HI:
 45	case DS_REG_DMA3FILL_LO:
 46	case DS_REG_DMA3FILL_HI:
 47		break;
 48
 49	// Timers
 50	case DS_REG_TM0CNT_LO:
 51		GBATimerWriteTMCNT_LO(&dscore->timers[0], value);
 52		return 0x20000;
 53	case DS_REG_TM1CNT_LO:
 54		GBATimerWriteTMCNT_LO(&dscore->timers[1], value);
 55		return 0x20000;
 56	case DS_REG_TM2CNT_LO:
 57		GBATimerWriteTMCNT_LO(&dscore->timers[2], value);
 58		return 0x20000;
 59	case DS_REG_TM3CNT_LO:
 60		GBATimerWriteTMCNT_LO(&dscore->timers[3], value);
 61		return 0x20000;
 62
 63	case DS_REG_TM0CNT_HI:
 64		value &= 0x00C7;
 65		DSTimerWriteTMCNT_HI(&dscore->timers[0], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM0CNT_LO >> 1], value);
 66		break;
 67	case DS_REG_TM1CNT_HI:
 68		value &= 0x00C7;
 69		DSTimerWriteTMCNT_HI(&dscore->timers[1], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM1CNT_LO >> 1], value);
 70		break;
 71	case DS_REG_TM2CNT_HI:
 72		value &= 0x00C7;
 73		DSTimerWriteTMCNT_HI(&dscore->timers[2], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM2CNT_LO >> 1], value);
 74		break;
 75	case DS_REG_TM3CNT_HI:
 76		value &= 0x00C7;
 77		DSTimerWriteTMCNT_HI(&dscore->timers[3], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM3CNT_LO >> 1], value);
 78		break;
 79
 80	case DS_REG_IPCSYNC:
 81		value &= 0x6F00;
 82		value |= dscore->memory.io[address >> 1] & 0x000F;
 83		DSIPCWriteSYNC(dscore->ipc->cpu, dscore->ipc->memory.io, value);
 84		break;
 85	case DS_REG_IPCFIFOCNT:
 86		value = DSIPCWriteFIFOCNT(dscore, value);
 87		break;
 88	case DS_REG_IME:
 89		DSWriteIME(dscore->cpu, dscore->memory.io, value);
 90		break;
 91	case DS_REG_IF_LO:
 92	case DS_REG_IF_HI:
 93		value = dscore->memory.io[address >> 1] & ~value;
 94		break;
 95	default:
 96		return 0;
 97	}
 98	return value | 0x10000;
 99}
100
101static uint16_t DSIOReadKeyInput(struct DS* ds) {
102	uint16_t input = 0x3FF;
103	if (ds->keyCallback) {
104		input = ds->keyCallback->readKeys(ds->keyCallback);
105	} else if (ds->keySource) {
106		input = *ds->keySource;
107	}
108	// TODO: Put back
109	/*if (!dscore->p->allowOpposingDirections) {
110		unsigned rl = input & 0x030;
111		unsigned ud = input & 0x0C0;
112		input &= 0x30F;
113		if (rl != 0x030) {
114			input |= rl;
115		}
116		if (ud != 0x0C0) {
117			input |= ud;
118		}
119	}*/
120	return 0x3FF ^ input;
121}
122
123static void DSIOUpdateTimer(struct DSCommon* dscore, uint32_t address) {
124	switch (address) {
125	case DS_REG_TM0CNT_LO:
126		GBATimerUpdateRegisterInternal(&dscore->timers[0], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0);
127		break;
128	case DS_REG_TM1CNT_LO:
129		GBATimerUpdateRegisterInternal(&dscore->timers[1], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0);
130		break;
131	case DS_REG_TM2CNT_LO:
132		GBATimerUpdateRegisterInternal(&dscore->timers[2], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0);
133		break;
134	case DS_REG_TM3CNT_LO:
135		GBATimerUpdateRegisterInternal(&dscore->timers[3], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0);
136		break;
137	}
138}
139
140void DS7IOInit(struct DS* ds) {
141	memset(ds->memory.io7, 0, sizeof(ds->memory.io7));
142}
143
144void DS7IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
145	switch (address) {
146	default:
147		{
148			uint32_t v2 = DSIOWrite(&ds->ds7, address, value);
149			if (v2 & 0x10000) {
150				value = v2;
151				break;
152			} else if (v2 & 0x20000) {
153				return;
154			}
155		}
156		mLOG(DS_IO, STUB, "Stub DS7 I/O register write: %06X:%04X", address, value);
157		if (address >= DS7_REG_MAX) {
158			mLOG(DS_IO, GAME_ERROR, "Write to unused DS7 I/O register: %06X:%04X", address, value);
159			return;
160		}
161		break;
162	}
163	ds->memory.io7[address >> 1] = value;
164}
165
166void DS7IOWrite8(struct DS* ds, uint32_t address, uint8_t value) {
167	if (address == DS7_REG_HALTCNT) {
168		_DSHaltCNT(&ds->ds7, value);
169		return;
170	}
171	if (address < DS7_REG_MAX) {
172		uint16_t value16 = value << (8 * (address & 1));
173		value16 |= (ds->ds7.memory.io[(address & 0xFFF) >> 1]) & ~(0xFF << (8 * (address & 1)));
174		DS7IOWrite(ds, address & 0xFFFFFFFE, value16);
175	} else {
176		mLOG(DS, STUB, "Writing to unknown DS7 register: %08X:%02X", address, value);
177	}
178}
179
180void DS7IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
181	switch (address) {
182	case DS_REG_DMA0SAD_LO:
183		value = DSDMAWriteSAD(&ds->ds7, 0, value);
184		break;
185	case DS_REG_DMA1SAD_LO:
186		value = DSDMAWriteSAD(&ds->ds7, 1, value);
187		break;
188	case DS_REG_DMA2SAD_LO:
189		value = DSDMAWriteSAD(&ds->ds7, 2, value);
190		break;
191	case DS_REG_DMA3SAD_LO:
192		value = DSDMAWriteSAD(&ds->ds7, 3, value);
193		break;
194
195	case DS_REG_DMA0DAD_LO:
196		value = DSDMAWriteDAD(&ds->ds7, 0, value);
197		break;
198	case DS_REG_DMA1DAD_LO:
199		value = DSDMAWriteDAD(&ds->ds7, 1, value);
200		break;
201	case DS_REG_DMA2DAD_LO:
202		value = DSDMAWriteDAD(&ds->ds7, 2, value);
203		break;
204	case DS_REG_DMA3DAD_LO:
205		value = DSDMAWriteDAD(&ds->ds7, 3, value);
206		break;
207
208	case DS_REG_DMA0CNT_LO:
209		DS7DMAWriteCNT(&ds->ds7, 0, value);
210		break;
211	case DS_REG_DMA1CNT_LO:
212		DS7DMAWriteCNT(&ds->ds7, 1, value);
213		break;
214	case DS_REG_DMA2CNT_LO:
215		DS7DMAWriteCNT(&ds->ds7, 2, value);
216		break;
217	case DS_REG_DMA3CNT_LO:
218		DS7DMAWriteCNT(&ds->ds7, 3, value);
219		break;
220
221	case DS_REG_IPCFIFOSEND_LO:
222		DSIPCWriteFIFO(&ds->ds7, value);
223		break;
224	case DS_REG_IE_LO:
225		DSWriteIE(ds->ds7.cpu, ds->ds7.memory.io, value);
226		break;
227	default:
228		DS7IOWrite(ds, address, value & 0xFFFF);
229		DS7IOWrite(ds, address | 2, value >> 16);
230		return;
231	}
232	ds->ds7.memory.io[address >> 1] = value;
233	ds->ds7.memory.io[(address >> 1) + 1] = value >> 16;
234}
235
236uint16_t DS7IORead(struct DS* ds, uint32_t address) {
237	switch (address) {
238	case DS_REG_TM0CNT_LO:
239	case DS_REG_TM1CNT_LO:
240	case DS_REG_TM2CNT_LO:
241	case DS_REG_TM3CNT_LO:
242		DSIOUpdateTimer(&ds->ds7, address);
243		break;
244	case DS_REG_KEYINPUT:
245		return DSIOReadKeyInput(ds);
246	case DS_REG_DMA0FILL_LO:
247	case DS_REG_DMA0FILL_HI:
248	case DS_REG_DMA1FILL_LO:
249	case DS_REG_DMA1FILL_HI:
250	case DS_REG_DMA2FILL_LO:
251	case DS_REG_DMA2FILL_HI:
252	case DS_REG_DMA3FILL_LO:
253	case DS_REG_DMA3FILL_HI:
254	case DS_REG_TM0CNT_HI:
255	case DS_REG_TM1CNT_HI:
256	case DS_REG_TM2CNT_HI:
257	case DS_REG_TM3CNT_HI:
258	case DS_REG_IPCSYNC:
259	case DS_REG_IPCFIFOCNT:
260	case DS_REG_IME:
261	case DS_REG_IE_LO:
262	case DS_REG_IE_HI:
263	case DS_REG_IF_LO:
264	case DS_REG_IF_HI:
265		// Handled transparently by the registers
266		break;
267	default:
268		mLOG(DS_IO, STUB, "Stub DS7 I/O register read: %06X", address);
269	}
270	if (address < DS7_REG_MAX) {
271		return ds->memory.io7[address >> 1];
272	}
273	return 0;
274}
275
276uint32_t DS7IORead32(struct DS* ds, uint32_t address) {
277	switch (address) {
278	case DS_REG_IPCFIFORECV_LO:
279		return DSIPCReadFIFO(&ds->ds7);
280	default:
281		return DS7IORead(ds, address & 0x00FFFFFC) | (DS7IORead(ds, (address & 0x00FFFFFC) | 2) << 16);
282	}
283}
284
285void DS9IOInit(struct DS* ds) {
286	memset(ds->memory.io9, 0, sizeof(ds->memory.io9));
287}
288
289void DS9IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
290	switch (address) {
291	case DS9_REG_VRAMCNT_A:
292	case DS9_REG_VRAMCNT_C:
293	case DS9_REG_VRAMCNT_E:
294	case DS9_REG_VRAMCNT_G:
295		DSVideoConfigureVRAM(&ds->memory, address - DS9_REG_VRAMCNT_A + 1, value & 0xFF);
296		// Fall through
297	case DS9_REG_VRAMCNT_I:
298		DSVideoConfigureVRAM(&ds->memory, address - DS9_REG_VRAMCNT_A, value >> 8);
299		break;
300	default:
301		{
302			uint32_t v2 = DSIOWrite(&ds->ds9, address, value);
303			if (v2 & 0x10000) {
304				value = v2;
305				break;
306			} else if (v2 & 0x20000) {
307				return;
308			}
309		}
310		mLOG(DS_IO, STUB, "Stub DS9 I/O register write: %06X:%04X", address, value);
311		if (address >= DS7_REG_MAX) {
312			mLOG(DS_IO, GAME_ERROR, "Write to unused DS9 I/O register: %06X:%04X", address, value);
313			return;
314		}
315		break;
316	}
317	ds->memory.io9[address >> 1] = value;
318}
319
320void DS9IOWrite8(struct DS* ds, uint32_t address, uint8_t value) {
321	if (address < DS9_REG_MAX) {
322		uint16_t value16 = value << (8 * (address & 1));
323		value16 |= (ds->memory.io9[(address & 0x1FFF) >> 1]) & ~(0xFF << (8 * (address & 1)));
324		DS9IOWrite(ds, address & 0xFFFFFFFE, value16);
325	} else {
326		mLOG(DS, STUB, "Writing to unknown DS9 register: %08X:%02X", address, value);
327	}
328}
329
330void DS9IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
331	switch (address) {
332	case DS_REG_DMA0SAD_LO:
333		value = DSDMAWriteSAD(&ds->ds9, 0, value);
334		break;
335	case DS_REG_DMA1SAD_LO:
336		value = DSDMAWriteSAD(&ds->ds9, 1, value);
337		break;
338	case DS_REG_DMA2SAD_LO:
339		value = DSDMAWriteSAD(&ds->ds9, 2, value);
340		break;
341	case DS_REG_DMA3SAD_LO:
342		value = DSDMAWriteSAD(&ds->ds9, 3, value);
343		break;
344
345	case DS_REG_DMA0DAD_LO:
346		value = DSDMAWriteDAD(&ds->ds9, 0, value);
347		break;
348	case DS_REG_DMA1DAD_LO:
349		value = DSDMAWriteDAD(&ds->ds9, 1, value);
350		break;
351	case DS_REG_DMA2DAD_LO:
352		value = DSDMAWriteDAD(&ds->ds9, 2, value);
353		break;
354	case DS_REG_DMA3DAD_LO:
355		value = DSDMAWriteDAD(&ds->ds9, 3, value);
356		break;
357
358	case DS_REG_DMA0CNT_LO:
359		DS9DMAWriteCNT(&ds->ds9, 0, value);
360		break;
361	case DS_REG_DMA1CNT_LO:
362		DS9DMAWriteCNT(&ds->ds9, 1, value);
363		break;
364	case DS_REG_DMA2CNT_LO:
365		DS9DMAWriteCNT(&ds->ds9, 2, value);
366		break;
367	case DS_REG_DMA3CNT_LO:
368		DS9DMAWriteCNT(&ds->ds9, 3, value);
369		break;
370
371	case DS_REG_IPCFIFOSEND_LO:
372		DSIPCWriteFIFO(&ds->ds9, value);
373		break;
374	case DS_REG_IE_LO:
375		DSWriteIE(ds->ds9.cpu, ds->ds9.memory.io, value);
376		break;
377	default:
378		DS9IOWrite(ds, address, value & 0xFFFF);
379		DS9IOWrite(ds, address | 2, value >> 16);
380		return;
381	}
382	ds->ds9.memory.io[address >> 1] = value;
383	ds->ds9.memory.io[(address >> 1) + 1] = value >> 16;
384}
385
386uint16_t DS9IORead(struct DS* ds, uint32_t address) {
387	switch (address) {
388	case DS_REG_TM0CNT_LO:
389	case DS_REG_TM1CNT_LO:
390	case DS_REG_TM2CNT_LO:
391	case DS_REG_TM3CNT_LO:
392		DSIOUpdateTimer(&ds->ds9, address);
393		break;
394	case DS_REG_KEYINPUT:
395		return DSIOReadKeyInput(ds);
396	case DS_REG_DMA0FILL_LO:
397	case DS_REG_DMA0FILL_HI:
398	case DS_REG_DMA1FILL_LO:
399	case DS_REG_DMA1FILL_HI:
400	case DS_REG_DMA2FILL_LO:
401	case DS_REG_DMA2FILL_HI:
402	case DS_REG_DMA3FILL_LO:
403	case DS_REG_DMA3FILL_HI:
404	case DS_REG_TM0CNT_HI:
405	case DS_REG_TM1CNT_HI:
406	case DS_REG_TM2CNT_HI:
407	case DS_REG_TM3CNT_HI:
408	case DS_REG_IPCSYNC:
409	case DS_REG_IPCFIFOCNT:
410	case DS_REG_IME:
411	case DS_REG_IE_LO:
412	case DS_REG_IE_HI:
413	case DS_REG_IF_LO:
414	case DS_REG_IF_HI:
415		// Handled transparently by the registers
416		break;
417	default:
418		mLOG(DS_IO, STUB, "Stub DS9 I/O register read: %06X", address);
419	}
420	if (address < DS9_REG_MAX) {
421		return ds->ds9.memory.io[address >> 1];
422	}
423	return 0;
424}
425
426uint32_t DS9IORead32(struct DS* ds, uint32_t address) {
427	switch (address) {
428	case DS_REG_IPCFIFORECV_LO:
429		return DSIPCReadFIFO(&ds->ds9);
430	default:
431		return DS9IORead(ds, address & 0x00FFFFFC) | (DS9IORead(ds, (address & 0x00FFFFFC) | 2) << 16);
432	}
433}