all repos — mgba @ 41975eb338e689370d278a494053a71e0cf73520

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#include <mgba/internal/ds/spi.h>
 12
 13mLOG_DEFINE_CATEGORY(DS_IO, "DS I/O");
 14
 15static void _DSHaltCNT(struct DSCommon* dscore, uint8_t value) {
 16	switch (value >> 6) {
 17	case 0:
 18	default:
 19		break;
 20	case 1:
 21		mLOG(DS_IO, STUB, "Enter GBA mode not supported");
 22		break;
 23	case 2:
 24		ARMHalt(dscore->cpu);
 25		break;
 26	case 3:
 27		mLOG(DS_IO, STUB, "Enter sleep mode not supported");
 28		break;
 29	}
 30}
 31
 32static uint16_t _scheduleDiv(struct DS* ds, uint16_t control) {
 33	mTimingDeschedule(&ds->ds9.timing, &ds->divEvent);
 34	mTimingSchedule(&ds->ds9.timing, &ds->divEvent, (control & 3) ? 36 : 68);
 35	return control | 0x8000;
 36}
 37
 38static uint16_t _scheduleSqrt(struct DS* ds, uint16_t control) {
 39	mTimingDeschedule(&ds->ds9.timing, &ds->sqrtEvent);
 40	mTimingSchedule(&ds->ds9.timing, &ds->sqrtEvent, 26);
 41	return control | 0x8000;
 42}
 43
 44static uint32_t DSIOWrite(struct DSCommon* dscore, uint32_t address, uint16_t value) {
 45	switch (address) {
 46	// Video
 47	case DS_REG_DISPSTAT:
 48		DSVideoWriteDISPSTAT(dscore, value);
 49		break;
 50
 51	// DMA Fill
 52	case DS_REG_DMA0FILL_LO:
 53	case DS_REG_DMA0FILL_HI:
 54	case DS_REG_DMA1FILL_LO:
 55	case DS_REG_DMA1FILL_HI:
 56	case DS_REG_DMA2FILL_LO:
 57	case DS_REG_DMA2FILL_HI:
 58	case DS_REG_DMA3FILL_LO:
 59	case DS_REG_DMA3FILL_HI:
 60		break;
 61
 62	// Timers
 63	case DS_REG_TM0CNT_LO:
 64		GBATimerWriteTMCNT_LO(&dscore->timers[0], value);
 65		return 0x20000;
 66	case DS_REG_TM1CNT_LO:
 67		GBATimerWriteTMCNT_LO(&dscore->timers[1], value);
 68		return 0x20000;
 69	case DS_REG_TM2CNT_LO:
 70		GBATimerWriteTMCNT_LO(&dscore->timers[2], value);
 71		return 0x20000;
 72	case DS_REG_TM3CNT_LO:
 73		GBATimerWriteTMCNT_LO(&dscore->timers[3], value);
 74		return 0x20000;
 75
 76	case DS_REG_TM0CNT_HI:
 77		value &= 0x00C7;
 78		DSTimerWriteTMCNT_HI(&dscore->timers[0], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM0CNT_LO >> 1], value);
 79		break;
 80	case DS_REG_TM1CNT_HI:
 81		value &= 0x00C7;
 82		DSTimerWriteTMCNT_HI(&dscore->timers[1], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM1CNT_LO >> 1], value);
 83		break;
 84	case DS_REG_TM2CNT_HI:
 85		value &= 0x00C7;
 86		DSTimerWriteTMCNT_HI(&dscore->timers[2], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM2CNT_LO >> 1], value);
 87		break;
 88	case DS_REG_TM3CNT_HI:
 89		value &= 0x00C7;
 90		DSTimerWriteTMCNT_HI(&dscore->timers[3], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM3CNT_LO >> 1], value);
 91		break;
 92
 93	// IPC
 94	case DS_REG_IPCSYNC:
 95		value &= 0x6F00;
 96		value |= dscore->memory.io[address >> 1] & 0x000F;
 97		DSIPCWriteSYNC(dscore->ipc->cpu, dscore->ipc->memory.io, value);
 98		break;
 99	case DS_REG_IPCFIFOCNT:
100		value = DSIPCWriteFIFOCNT(dscore, value);
101		break;
102
103	// Cart bus
104	case DS_REG_SLOT1CNT_LO:
105		mLOG(DS_IO, STUB, "ROM control not implemented");
106		value &= 0x7FFF;
107		break;
108
109	// Interrupts
110	case DS_REG_IME:
111		DSWriteIME(dscore->cpu, dscore->memory.io, value);
112		break;
113	case 0x20A:
114		value = 0;
115		// Some bad interrupt libraries will write to this
116		break;
117	case DS_REG_IF_LO:
118	case DS_REG_IF_HI:
119		value = dscore->memory.io[address >> 1] & ~value;
120		break;
121	default:
122		return 0;
123	}
124	return value | 0x10000;
125}
126
127static uint16_t DSIOReadKeyInput(struct DS* ds) {
128	uint16_t input = 0x3FF;
129	if (ds->keyCallback) {
130		input = ds->keyCallback->readKeys(ds->keyCallback);
131	} else if (ds->keySource) {
132		input = *ds->keySource;
133	}
134	// TODO: Put back
135	/*if (!dscore->p->allowOpposingDirections) {
136		unsigned rl = input & 0x030;
137		unsigned ud = input & 0x0C0;
138		input &= 0x30F;
139		if (rl != 0x030) {
140			input |= rl;
141		}
142		if (ud != 0x0C0) {
143			input |= ud;
144		}
145	}*/
146	return 0x3FF ^ input;
147}
148
149static void DSIOUpdateTimer(struct DSCommon* dscore, uint32_t address) {
150	switch (address) {
151	case DS_REG_TM0CNT_LO:
152		GBATimerUpdateRegisterInternal(&dscore->timers[0], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0);
153		break;
154	case DS_REG_TM1CNT_LO:
155		GBATimerUpdateRegisterInternal(&dscore->timers[1], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0);
156		break;
157	case DS_REG_TM2CNT_LO:
158		GBATimerUpdateRegisterInternal(&dscore->timers[2], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0);
159		break;
160	case DS_REG_TM3CNT_LO:
161		GBATimerUpdateRegisterInternal(&dscore->timers[3], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0);
162		break;
163	}
164}
165
166void DS7IOInit(struct DS* ds) {
167	memset(ds->memory.io7, 0, sizeof(ds->memory.io7));
168	ds->memory.io7[DS_REG_IPCFIFOCNT >> 1] = 0x0101;
169	ds->memory.io7[DS_REG_POSTFLG >> 1] = 0x0001;
170}
171
172void DS7IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
173	switch (address) {
174	case DS7_REG_SPICNT:
175		value &= 0xCF83;
176		value = DSSPIWriteControl(ds, value);
177		break;
178	case DS7_REG_SPIDATA:
179		DSSPIWrite(ds, value);
180		return;
181	default:
182		{
183			uint32_t v2 = DSIOWrite(&ds->ds7, address, value);
184			if (v2 & 0x10000) {
185				value = v2;
186				break;
187			} else if (v2 & 0x20000) {
188				return;
189			}
190		}
191		mLOG(DS_IO, STUB, "Stub DS7 I/O register write: %06X:%04X", address, value);
192		if (address >= DS7_REG_MAX) {
193			mLOG(DS_IO, GAME_ERROR, "Write to unused DS7 I/O register: %06X:%04X", address, value);
194			return;
195		}
196		break;
197	}
198	ds->memory.io7[address >> 1] = value;
199}
200
201void DS7IOWrite8(struct DS* ds, uint32_t address, uint8_t value) {
202	if (address == DS7_REG_HALTCNT) {
203		_DSHaltCNT(&ds->ds7, value);
204		return;
205	}
206	if (address < DS7_REG_MAX) {
207		uint16_t value16 = value << (8 * (address & 1));
208		value16 |= (ds->ds7.memory.io[(address & 0xFFF) >> 1]) & ~(0xFF << (8 * (address & 1)));
209		DS7IOWrite(ds, address & 0xFFFFFFFE, value16);
210	} else {
211		mLOG(DS, STUB, "Writing to unknown DS7 register: %08X:%02X", address, value);
212	}
213}
214
215void DS7IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
216	switch (address) {
217	case DS_REG_DMA0SAD_LO:
218		value = DSDMAWriteSAD(&ds->ds7, 0, value);
219		break;
220	case DS_REG_DMA1SAD_LO:
221		value = DSDMAWriteSAD(&ds->ds7, 1, value);
222		break;
223	case DS_REG_DMA2SAD_LO:
224		value = DSDMAWriteSAD(&ds->ds7, 2, value);
225		break;
226	case DS_REG_DMA3SAD_LO:
227		value = DSDMAWriteSAD(&ds->ds7, 3, value);
228		break;
229
230	case DS_REG_DMA0DAD_LO:
231		value = DSDMAWriteDAD(&ds->ds7, 0, value);
232		break;
233	case DS_REG_DMA1DAD_LO:
234		value = DSDMAWriteDAD(&ds->ds7, 1, value);
235		break;
236	case DS_REG_DMA2DAD_LO:
237		value = DSDMAWriteDAD(&ds->ds7, 2, value);
238		break;
239	case DS_REG_DMA3DAD_LO:
240		value = DSDMAWriteDAD(&ds->ds7, 3, value);
241		break;
242
243	case DS_REG_DMA0CNT_LO:
244		DS7DMAWriteCNT(&ds->ds7, 0, value);
245		break;
246	case DS_REG_DMA1CNT_LO:
247		DS7DMAWriteCNT(&ds->ds7, 1, value);
248		break;
249	case DS_REG_DMA2CNT_LO:
250		DS7DMAWriteCNT(&ds->ds7, 2, value);
251		break;
252	case DS_REG_DMA3CNT_LO:
253		DS7DMAWriteCNT(&ds->ds7, 3, value);
254		break;
255
256	case DS_REG_IPCFIFOSEND_LO:
257		DSIPCWriteFIFO(&ds->ds7, value);
258		break;
259	case DS_REG_IE_LO:
260		DSWriteIE(ds->ds7.cpu, ds->ds7.memory.io, value);
261		break;
262	default:
263		DS7IOWrite(ds, address, value & 0xFFFF);
264		DS7IOWrite(ds, address | 2, value >> 16);
265		return;
266	}
267	ds->ds7.memory.io[address >> 1] = value;
268	ds->ds7.memory.io[(address >> 1) + 1] = value >> 16;
269}
270
271uint16_t DS7IORead(struct DS* ds, uint32_t address) {
272	switch (address) {
273	case DS_REG_TM0CNT_LO:
274	case DS_REG_TM1CNT_LO:
275	case DS_REG_TM2CNT_LO:
276	case DS_REG_TM3CNT_LO:
277		DSIOUpdateTimer(&ds->ds7, address);
278		break;
279	case DS_REG_KEYINPUT:
280		return DSIOReadKeyInput(ds);
281	case DS_REG_DMA0FILL_LO:
282	case DS_REG_DMA0FILL_HI:
283	case DS_REG_DMA1FILL_LO:
284	case DS_REG_DMA1FILL_HI:
285	case DS_REG_DMA2FILL_LO:
286	case DS_REG_DMA2FILL_HI:
287	case DS_REG_DMA3FILL_LO:
288	case DS_REG_DMA3FILL_HI:
289	case DS_REG_TM0CNT_HI:
290	case DS_REG_TM1CNT_HI:
291	case DS_REG_TM2CNT_HI:
292	case DS_REG_TM3CNT_HI:
293	case DS7_REG_SPICNT:
294	case DS7_REG_SPIDATA:
295	case DS_REG_IPCSYNC:
296	case DS_REG_IPCFIFOCNT:
297	case DS_REG_IME:
298	case 0x20A:
299	case DS_REG_IE_LO:
300	case DS_REG_IE_HI:
301	case DS_REG_IF_LO:
302	case DS_REG_IF_HI:
303		// Handled transparently by the registers
304		break;
305	default:
306		mLOG(DS_IO, STUB, "Stub DS7 I/O register read: %06X", address);
307	}
308	if (address < DS7_REG_MAX) {
309		return ds->memory.io7[address >> 1];
310	}
311	return 0;
312}
313
314uint32_t DS7IORead32(struct DS* ds, uint32_t address) {
315	switch (address) {
316	case DS_REG_IPCFIFORECV_LO:
317		return DSIPCReadFIFO(&ds->ds7);
318	default:
319		return DS7IORead(ds, address & 0x00FFFFFC) | (DS7IORead(ds, (address & 0x00FFFFFC) | 2) << 16);
320	}
321}
322
323void DS9IOInit(struct DS* ds) {
324	memset(ds->memory.io9, 0, sizeof(ds->memory.io9));
325	ds->memory.io9[DS_REG_IPCFIFOCNT >> 1] = 0x0101;
326	ds->memory.io9[DS_REG_POSTFLG >> 1] = 0x0001;
327	ds->memory.io9[DS9_REG_VRAMCNT_G >> 1] = 0x0300;
328}
329
330void DS9IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
331	switch (address) {
332	// VRAM control
333	case DS9_REG_VRAMCNT_A:
334	case DS9_REG_VRAMCNT_C:
335	case DS9_REG_VRAMCNT_E:
336		value &= 0x9F9F;
337		DSVideoConfigureVRAM(&ds->memory, address - DS9_REG_VRAMCNT_A, value & 0xFF);
338		DSVideoConfigureVRAM(&ds->memory, address - DS9_REG_VRAMCNT_A + 1, value >> 8);
339		break;
340	case DS9_REG_VRAMCNT_G:
341		value &= 0x9F03;
342		DSVideoConfigureVRAM(&ds->memory, 6, value & 0xFF);
343		DSConfigureWRAM(&ds->memory, value >> 8);
344		break;
345	case DS9_REG_VRAMCNT_H:
346		value &= 0x9F9F;
347		DSVideoConfigureVRAM(&ds->memory, 7, value & 0xFF);
348		DSVideoConfigureVRAM(&ds->memory, 8, value >> 8);
349		break;
350
351	// Math
352	case DS9_REG_DIVCNT:
353		value = _scheduleDiv(ds, value);
354		break;
355	case DS9_REG_DIV_NUMER_0:
356	case DS9_REG_DIV_NUMER_1:
357	case DS9_REG_DIV_NUMER_2:
358	case DS9_REG_DIV_NUMER_3:
359	case DS9_REG_DIV_DENOM_0:
360	case DS9_REG_DIV_DENOM_1:
361	case DS9_REG_DIV_DENOM_2:
362	case DS9_REG_DIV_DENOM_3:
363		ds->memory.io9[DS9_REG_DIVCNT >> 1] = _scheduleDiv(ds, ds->memory.io9[DS9_REG_DIVCNT >> 1]);
364		break;
365	case DS9_REG_SQRTCNT:
366		value = _scheduleSqrt(ds, value);
367		break;
368	case DS9_REG_SQRT_PARAM_0:
369	case DS9_REG_SQRT_PARAM_1:
370	case DS9_REG_SQRT_PARAM_2:
371	case DS9_REG_SQRT_PARAM_3:
372		ds->memory.io9[DS9_REG_SQRTCNT >> 1] = _scheduleSqrt(ds, ds->memory.io9[DS9_REG_SQRTCNT >> 1]);
373		break;
374
375	default:
376		{
377			uint32_t v2 = DSIOWrite(&ds->ds9, address, value);
378			if (v2 & 0x10000) {
379				value = v2;
380				break;
381			} else if (v2 & 0x20000) {
382				return;
383			}
384		}
385		mLOG(DS_IO, STUB, "Stub DS9 I/O register write: %06X:%04X", address, value);
386		if (address >= DS7_REG_MAX) {
387			mLOG(DS_IO, GAME_ERROR, "Write to unused DS9 I/O register: %06X:%04X", address, value);
388			return;
389		}
390		break;
391	}
392	ds->memory.io9[address >> 1] = value;
393}
394
395void DS9IOWrite8(struct DS* ds, uint32_t address, uint8_t value) {
396	if (address < DS9_REG_MAX) {
397		uint16_t value16 = value << (8 * (address & 1));
398		value16 |= (ds->memory.io9[(address & 0x1FFF) >> 1]) & ~(0xFF << (8 * (address & 1)));
399		DS9IOWrite(ds, address & 0xFFFFFFFE, value16);
400	} else {
401		mLOG(DS, STUB, "Writing to unknown DS9 register: %08X:%02X", address, value);
402	}
403}
404
405void DS9IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
406	switch (address) {
407	case DS_REG_DMA0SAD_LO:
408		value = DSDMAWriteSAD(&ds->ds9, 0, value);
409		break;
410	case DS_REG_DMA1SAD_LO:
411		value = DSDMAWriteSAD(&ds->ds9, 1, value);
412		break;
413	case DS_REG_DMA2SAD_LO:
414		value = DSDMAWriteSAD(&ds->ds9, 2, value);
415		break;
416	case DS_REG_DMA3SAD_LO:
417		value = DSDMAWriteSAD(&ds->ds9, 3, value);
418		break;
419
420	case DS_REG_DMA0DAD_LO:
421		value = DSDMAWriteDAD(&ds->ds9, 0, value);
422		break;
423	case DS_REG_DMA1DAD_LO:
424		value = DSDMAWriteDAD(&ds->ds9, 1, value);
425		break;
426	case DS_REG_DMA2DAD_LO:
427		value = DSDMAWriteDAD(&ds->ds9, 2, value);
428		break;
429	case DS_REG_DMA3DAD_LO:
430		value = DSDMAWriteDAD(&ds->ds9, 3, value);
431		break;
432
433	case DS_REG_DMA0CNT_LO:
434		DS9DMAWriteCNT(&ds->ds9, 0, value);
435		break;
436	case DS_REG_DMA1CNT_LO:
437		DS9DMAWriteCNT(&ds->ds9, 1, value);
438		break;
439	case DS_REG_DMA2CNT_LO:
440		DS9DMAWriteCNT(&ds->ds9, 2, value);
441		break;
442	case DS_REG_DMA3CNT_LO:
443		DS9DMAWriteCNT(&ds->ds9, 3, value);
444		break;
445
446	case DS_REG_IPCFIFOSEND_LO:
447		DSIPCWriteFIFO(&ds->ds9, value);
448		break;
449	case DS_REG_IE_LO:
450		DSWriteIE(ds->ds9.cpu, ds->ds9.memory.io, value);
451		break;
452	default:
453		DS9IOWrite(ds, address, value & 0xFFFF);
454		DS9IOWrite(ds, address | 2, value >> 16);
455		return;
456	}
457	ds->ds9.memory.io[address >> 1] = value;
458	ds->ds9.memory.io[(address >> 1) + 1] = value >> 16;
459}
460
461uint16_t DS9IORead(struct DS* ds, uint32_t address) {
462	switch (address) {
463	case DS_REG_TM0CNT_LO:
464	case DS_REG_TM1CNT_LO:
465	case DS_REG_TM2CNT_LO:
466	case DS_REG_TM3CNT_LO:
467		DSIOUpdateTimer(&ds->ds9, address);
468		break;
469	case DS_REG_KEYINPUT:
470		return DSIOReadKeyInput(ds);
471	case DS_REG_DMA0FILL_LO:
472	case DS_REG_DMA0FILL_HI:
473	case DS_REG_DMA1FILL_LO:
474	case DS_REG_DMA1FILL_HI:
475	case DS_REG_DMA2FILL_LO:
476	case DS_REG_DMA2FILL_HI:
477	case DS_REG_DMA3FILL_LO:
478	case DS_REG_DMA3FILL_HI:
479	case DS_REG_TM0CNT_HI:
480	case DS_REG_TM1CNT_HI:
481	case DS_REG_TM2CNT_HI:
482	case DS_REG_TM3CNT_HI:
483	case DS_REG_IPCSYNC:
484	case DS_REG_IPCFIFOCNT:
485	case DS_REG_IME:
486	case 0x20A:
487	case DS_REG_IE_LO:
488	case DS_REG_IE_HI:
489	case DS_REG_IF_LO:
490	case DS_REG_IF_HI:
491	case DS9_REG_DIVCNT:
492	case DS9_REG_DIV_NUMER_0:
493	case DS9_REG_DIV_NUMER_1:
494	case DS9_REG_DIV_NUMER_2:
495	case DS9_REG_DIV_NUMER_3:
496	case DS9_REG_DIV_DENOM_0:
497	case DS9_REG_DIV_DENOM_1:
498	case DS9_REG_DIV_DENOM_2:
499	case DS9_REG_DIV_DENOM_3:
500	case DS9_REG_DIV_RESULT_0:
501	case DS9_REG_DIV_RESULT_1:
502	case DS9_REG_DIV_RESULT_2:
503	case DS9_REG_DIV_RESULT_3:
504	case DS9_REG_DIVREM_RESULT_0:
505	case DS9_REG_DIVREM_RESULT_1:
506	case DS9_REG_DIVREM_RESULT_2:
507	case DS9_REG_DIVREM_RESULT_3:
508	case DS9_REG_SQRTCNT:
509	case DS9_REG_SQRT_PARAM_0:
510	case DS9_REG_SQRT_PARAM_1:
511	case DS9_REG_SQRT_PARAM_2:
512	case DS9_REG_SQRT_PARAM_3:
513	case DS9_REG_SQRT_RESULT_LO:
514	case DS9_REG_SQRT_RESULT_HI:
515		// Handled transparently by the registers
516		break;
517	default:
518		mLOG(DS_IO, STUB, "Stub DS9 I/O register read: %06X", address);
519	}
520	if (address < DS9_REG_MAX) {
521		return ds->ds9.memory.io[address >> 1];
522	}
523	return 0;
524}
525
526uint32_t DS9IORead32(struct DS* ds, uint32_t address) {
527	switch (address) {
528	case DS_REG_IPCFIFORECV_LO:
529		return DSIPCReadFIFO(&ds->ds9);
530	default:
531		return DS9IORead(ds, address & 0x00FFFFFC) | (DS9IORead(ds, (address & 0x00FFFFFC) | 2) << 16);
532	}
533}