all repos — mgba @ 896749ede946fd761d327829e16cf70a47c76c0b

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