all repos — mgba @ 03817e5293cff053edbdace6deedd4be9ff8165e

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