all repos — mgba @ 66db559f5b4c10957180687f4c4f1502bf30707d

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