all repos — mgba @ c483a402e9306b5c4dab7fe3e88f153b74de5219

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