all repos — mgba @ 9b86abec09dc3f979b1e463fd42a76c9ce8f5273

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