all repos — mgba @ 5ebf24b621c50c167213fd6b8a0174ae88e7c160

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