all repos — mgba @ c037dada3e13727bb41ce807fe9a6caa8fd4cf95

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
 10mLOG_DEFINE_CATEGORY(DS_IO, "DS I/O");
 11
 12static void _writeIPCSync(struct ARMCore* remoteCpu, uint16_t* remoteIo, int16_t value) {
 13	remoteIo[DS_REG_IPCSYNC >> 1] &= 0xFFF0;
 14	remoteIo[DS_REG_IPCSYNC >> 1] |= (value >> 8) & 0x0F;
 15	if (value & 0x2000 && remoteIo[DS_REG_IPCSYNC >> 1] & 0x4000) {
 16		mLOG(DS_IO, STUB, "Unimplemented IPC IRQ");
 17		UNUSED(remoteCpu);
 18	}
 19}
 20
 21static bool DSIOWrite(struct DSCommon* dscore, uint32_t address, uint16_t value) {
 22	switch (address) {
 23	// Timers
 24	case DS_REG_TM0CNT_LO:
 25		GBATimerWriteTMCNT_LO(&dscore->timers[0], value);
 26		return true;
 27	case DS_REG_TM1CNT_LO:
 28		GBATimerWriteTMCNT_LO(&dscore->timers[1], value);
 29		return true;
 30	case DS_REG_TM2CNT_LO:
 31		GBATimerWriteTMCNT_LO(&dscore->timers[2], value);
 32		return true;
 33	case DS_REG_TM3CNT_LO:
 34		GBATimerWriteTMCNT_LO(&dscore->timers[3], value);
 35		return true;
 36
 37	case DS_REG_TM0CNT_HI:
 38		value &= 0x00C7;
 39		DSTimerWriteTMCNT_HI(&dscore->timers[0], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM0CNT_LO >> 1], value);
 40		break;
 41	case DS_REG_TM1CNT_HI:
 42		value &= 0x00C7;
 43		DSTimerWriteTMCNT_HI(&dscore->timers[1], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM1CNT_LO >> 1], value);
 44		break;
 45	case DS_REG_TM2CNT_HI:
 46		value &= 0x00C7;
 47		DSTimerWriteTMCNT_HI(&dscore->timers[2], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM2CNT_LO >> 1], value);
 48		break;
 49	case DS_REG_TM3CNT_HI:
 50		value &= 0x00C7;
 51		DSTimerWriteTMCNT_HI(&dscore->timers[3], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM3CNT_LO >> 1], value);
 52		break;
 53
 54	case DS_REG_IPCSYNC:
 55		value &= 0x6F00;
 56		value |= dscore->memory.io[address >> 1] & 0x000F;
 57		_writeIPCSync(dscore->ipc->cpu, dscore->ipc->memory.io, value);
 58		break;
 59	case DS_REG_IME:
 60		DSWriteIME(dscore->cpu, dscore->memory.io, value);
 61		break;
 62	case DS_REG_IF_LO:
 63	case DS_REG_IF_HI:
 64		value = dscore->memory.io[address >> 1] & ~value;
 65		break;
 66	default:
 67		return false;
 68	}
 69	return true;
 70}
 71
 72static void DSIOUpdateTimer(struct DSCommon* dscore, uint32_t address) {
 73	switch (address) {
 74	case DS_REG_TM0CNT_LO:
 75		GBATimerUpdateRegisterInternal(&dscore->timers[0], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0);
 76		break;
 77	case DS_REG_TM1CNT_LO:
 78		GBATimerUpdateRegisterInternal(&dscore->timers[1], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0);
 79		break;
 80	case DS_REG_TM2CNT_LO:
 81		GBATimerUpdateRegisterInternal(&dscore->timers[2], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0);
 82		break;
 83	case DS_REG_TM3CNT_LO:
 84		GBATimerUpdateRegisterInternal(&dscore->timers[3], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0);
 85		break;
 86	}
 87}
 88
 89void DS7IOInit(struct DS* ds) {
 90	memset(ds->memory.io7, 0, sizeof(ds->memory.io7));
 91}
 92
 93void DS7IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
 94	switch (address) {
 95	default:
 96		if (DSIOWrite(&ds->ds7, address, value)) {
 97			break;
 98		}
 99		mLOG(DS_IO, STUB, "Stub DS7 I/O register write: %06X:%04X", address, value);
100		if (address >= DS7_REG_MAX) {
101			mLOG(DS_IO, GAME_ERROR, "Write to unused DS7 I/O register: %06X:%04X", address, value);
102			return;
103		}
104		break;
105	}
106	ds->memory.io7[address >> 1] = value;
107}
108
109void DS7IOWrite8(struct DS* ds, uint32_t address, uint8_t value) {
110	if (address < DS7_REG_MAX) {
111		uint16_t value16 = value << (8 * (address & 1));
112		value16 |= (ds->ds7.memory.io[(address & 0xFFF) >> 1]) & ~(0xFF << (8 * (address & 1)));
113		DS7IOWrite(ds, address & 0xFFFFFFFE, value16);
114	} else {
115		mLOG(DS, STUB, "Writing to unknown DS7 register: %08X:%02X", address, value);
116	}
117}
118
119void DS7IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
120	switch (address) {
121	case DS_REG_IE_LO:
122		DSWriteIE(ds->ds7.cpu, ds->ds7.memory.io, value);
123		break;
124	default:
125		DS7IOWrite(ds, address, value & 0xFFFF);
126		DS7IOWrite(ds, address | 2, value >> 16);
127		return;
128	}
129	ds->ds7.memory.io[address >> 1] = value;
130	ds->ds7.memory.io[(address >> 1) + 1] = value >> 16;
131}
132
133uint16_t DS7IORead(struct DS* ds, uint32_t address) {
134	switch (address) {
135	case DS_REG_TM0CNT_LO:
136	case DS_REG_TM1CNT_LO:
137	case DS_REG_TM2CNT_LO:
138	case DS_REG_TM3CNT_LO:
139		DSIOUpdateTimer(&ds->ds7, address);
140		break;
141	case DS_REG_TM0CNT_HI:
142	case DS_REG_TM1CNT_HI:
143	case DS_REG_TM2CNT_HI:
144	case DS_REG_TM3CNT_HI:
145	case DS_REG_IPCSYNC:
146	case DS_REG_IME:
147	case DS_REG_IE_LO:
148	case DS_REG_IE_HI:
149	case DS_REG_IF_LO:
150	case DS_REG_IF_HI:
151		// Handled transparently by the registers
152		break;
153	default:
154		mLOG(DS_IO, STUB, "Stub DS7 I/O register read: %06X", address);
155	}
156	if (address < DS7_REG_MAX) {
157		return ds->memory.io7[address >> 1];
158	}
159	return 0;
160}
161
162void DS9IOInit(struct DS* ds) {
163	memset(ds->memory.io9, 0, sizeof(ds->memory.io9));
164}
165
166void DS9IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
167	switch (address) {
168	default:
169		if (DSIOWrite(&ds->ds9, address, value)) {
170			break;
171		}
172		mLOG(DS_IO, STUB, "Stub DS9 I/O register write: %06X:%04X", address, value);
173		if (address >= DS7_REG_MAX) {
174			mLOG(DS_IO, GAME_ERROR, "Write to unused DS9 I/O register: %06X:%04X", address, value);
175			return;
176		}
177		break;
178	}
179	ds->memory.io9[address >> 1] = value;
180}
181
182void DS9IOWrite8(struct DS* ds, uint32_t address, uint8_t value) {
183	if (address < DS9_REG_MAX) {
184		uint16_t value16 = value << (8 * (address & 1));
185		value16 |= (ds->memory.io9[(address & 0x1FFF) >> 1]) & ~(0xFF << (8 * (address & 1)));
186		DS9IOWrite(ds, address & 0xFFFFFFFE, value16);
187	} else {
188		mLOG(DS, STUB, "Writing to unknown DS9 register: %08X:%02X", address, value);
189	}
190}
191
192void DS9IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
193	switch (address) {
194	default:
195		DS9IOWrite(ds, address, value & 0xFFFF);
196		DS9IOWrite(ds, address | 2, value >> 16);
197		return;
198	}
199	ds->ds9.memory.io[address >> 1] = value;
200	ds->ds9.memory.io[(address >> 1) + 1] = value >> 16;
201}
202
203uint16_t DS9IORead(struct DS* ds, uint32_t address) {
204	switch (address) {
205	case DS_REG_TM0CNT_LO:
206	case DS_REG_TM1CNT_LO:
207	case DS_REG_TM2CNT_LO:
208	case DS_REG_TM3CNT_LO:
209		DSIOUpdateTimer(&ds->ds9, address);
210		break;
211	case DS_REG_TM0CNT_HI:
212	case DS_REG_TM1CNT_HI:
213	case DS_REG_TM2CNT_HI:
214	case DS_REG_TM3CNT_HI:
215	case DS_REG_IPCSYNC:
216	case DS_REG_IME:
217	case DS_REG_IE_LO:
218	case DS_REG_IE_HI:
219	case DS_REG_IF_LO:
220	case DS_REG_IF_HI:
221		// Handled transparently by the registers
222		break;
223	default:
224		mLOG(DS_IO, STUB, "Stub DS9 I/O register read: %06X", address);
225	}
226	if (address < DS9_REG_MAX) {
227		return ds->ds9.memory.io[address >> 1];
228	}
229	return 0;
230}