all repos — mgba @ e0ae2e89063ec7139b89358cf476d4d79fdbcc8e

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[DS7_REG_IPCSYNC >> 1] &= 0xFFF0;
 14	remoteIo[DS7_REG_IPCSYNC >> 1] |= (value >> 8) & 0x0F;
 15	if (value & 0x2000 && remoteIo[DS7_REG_IPCSYNC >> 1] & 0x4000) {
 16		mLOG(DS_IO, STUB, "Unimplemented IPC IRQ");
 17		UNUSED(remoteCpu);
 18	}
 19}
 20
 21void DS7IOInit(struct DS* ds) {
 22	memset(ds->memory.io7, 0, sizeof(ds->memory.io7));
 23}
 24
 25void DS7IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
 26	switch (address) {
 27	// Timers
 28	case DS7_REG_TM0CNT_LO:
 29		DSTimerWriteTMCNT_LO(&ds->timers7[0], value);
 30		return;
 31	case DS7_REG_TM1CNT_LO:
 32		DSTimerWriteTMCNT_LO(&ds->timers7[1], value);
 33		return;
 34	case DS7_REG_TM2CNT_LO:
 35		DSTimerWriteTMCNT_LO(&ds->timers7[2], value);
 36		return;
 37	case DS7_REG_TM3CNT_LO:
 38		DSTimerWriteTMCNT_LO(&ds->timers7[3], value);
 39		return;
 40
 41	case DS7_REG_TM0CNT_HI:
 42		value &= 0x00C7;
 43		DSTimerWriteTMCNT_HI(&ds->timers7[0], ds->arm7, &ds->memory.io7[(address - 2) >> 1], value);
 44		ds->timersEnabled7 &= ~1;
 45		ds->timersEnabled7 |= DSTimerFlagsGetEnable(ds->timers7[0].flags);
 46		break;
 47	case DS7_REG_TM1CNT_HI:
 48		value &= 0x00C7;
 49		DSTimerWriteTMCNT_HI(&ds->timers7[1], ds->arm7, &ds->memory.io7[(address - 2) >> 1], value);
 50		ds->timersEnabled7 &= ~2;
 51		ds->timersEnabled7 |= DSTimerFlagsGetEnable(ds->timers7[1].flags) << 1;
 52		break;
 53	case DS7_REG_TM2CNT_HI:
 54		value &= 0x00C7;
 55		DSTimerWriteTMCNT_HI(&ds->timers7[2], ds->arm7, &ds->memory.io7[(address - 2) >> 1], value);
 56		ds->timersEnabled7 &= ~4;
 57		ds->timersEnabled7 |= DSTimerFlagsGetEnable(ds->timers7[2].flags) << 2;
 58		break;
 59	case DS7_REG_TM3CNT_HI:
 60		value &= 0x00C7;
 61		DSTimerWriteTMCNT_HI(&ds->timers7[3], ds->arm7, &ds->memory.io7[(address - 2) >> 1], value);
 62		ds->timersEnabled7 &= ~8;
 63		ds->timersEnabled7 |= DSTimerFlagsGetEnable(ds->timers7[3].flags) << 3;
 64		break;
 65
 66	case DS7_REG_IPCSYNC:
 67		value &= 0x6F00;
 68		value |= ds->memory.io7[address >> 1] & 0x000F;
 69		_writeIPCSync(ds->arm9, ds->memory.io9, value);
 70		break;
 71	case DS7_REG_IME:
 72		DSWriteIME(ds->arm7, ds->memory.io7, value);
 73		break;
 74	case DS7_REG_IF_LO:
 75	case DS7_REG_IF_HI:
 76		value = ds->memory.io7[address >> 1] & ~value;
 77		break;
 78	default:
 79		mLOG(DS_IO, STUB, "Stub DS7 I/O register write: %06X:%04X", address, value);
 80		if (address >= DS7_REG_MAX) {
 81			mLOG(DS_IO, GAME_ERROR, "Write to unused DS7 I/O register: %06X:%04X", address, value);
 82			return;
 83		}
 84		break;
 85	}
 86	ds->memory.io7[address >> 1] = value;
 87}
 88
 89void DS7IOWrite8(struct DS* ds, uint32_t address, uint8_t value) {
 90	if (address < DS7_REG_MAX) {
 91		uint16_t value16 = value << (8 * (address & 1));
 92		value16 |= (ds->memory.io7[(address & 0xFFF) >> 1]) & ~(0xFF << (8 * (address & 1)));
 93		DS7IOWrite(ds, address & 0xFFFFFFFE, value16);
 94	} else {
 95		mLOG(DS, STUB, "Writing to unknown DS7 register: %08X:%02X", address, value);
 96	}
 97}
 98
 99void DS7IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
100	switch (address) {
101	case DS7_REG_IE_LO:
102		DSWriteIE(ds->arm7, ds->memory.io7, value);
103		break;
104	default:
105		DS7IOWrite(ds, address, value & 0xFFFF);
106		DS7IOWrite(ds, address | 2, value >> 16);
107		return;
108	}
109	ds->memory.io7[address >> 1] = value;
110	ds->memory.io7[(address >> 1) + 1] = value >> 16;
111}
112
113uint16_t DS7IORead(struct DS* ds, uint32_t address) {
114	switch (address) {
115	case DS7_REG_TM0CNT_LO:
116		DSTimerUpdateRegister(&ds->timers7[0], ds->arm7, &ds->memory.io7[address >> 1]);
117		break;
118	case DS7_REG_TM1CNT_LO:
119		DSTimerUpdateRegister(&ds->timers7[1], ds->arm7, &ds->memory.io7[address >> 1]);
120		break;
121	case DS7_REG_TM2CNT_LO:
122		DSTimerUpdateRegister(&ds->timers7[2], ds->arm7, &ds->memory.io7[address >> 1]);
123		break;
124	case DS7_REG_TM3CNT_LO:
125		DSTimerUpdateRegister(&ds->timers7[3], ds->arm7, &ds->memory.io7[address >> 1]);
126		break;
127
128	case DS7_REG_TM0CNT_HI:
129	case DS7_REG_TM1CNT_HI:
130	case DS7_REG_TM2CNT_HI:
131	case DS7_REG_TM3CNT_HI:
132	case DS7_REG_IPCSYNC:
133	case DS7_REG_IME:
134	case DS7_REG_IE_LO:
135	case DS7_REG_IE_HI:
136	case DS7_REG_IF_LO:
137	case DS7_REG_IF_HI:
138		// Handled transparently by the registers
139		break;
140	default:
141		mLOG(DS_IO, STUB, "Stub DS7 I/O register read: %06X", address);
142	}
143	if (address < DS7_REG_MAX) {
144		return ds->memory.io7[address >> 1];
145	}
146	return 0;
147}
148
149void DS9IOInit(struct DS* ds) {
150	memset(ds->memory.io9, 0, sizeof(ds->memory.io9));
151}
152
153void DS9IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
154	switch (address) {
155	case DS9_REG_IPCSYNC:
156		value &= 0x6F00;
157		value |= ds->memory.io9[address >> 1] & 0x000F;
158		_writeIPCSync(ds->arm7, ds->memory.io7, value);
159		break;
160	default:
161		mLOG(DS_IO, STUB, "Stub DS9 I/O register write: %06X:%04X", address, value);
162		if (address >= DS7_REG_MAX) {
163			mLOG(DS_IO, GAME_ERROR, "Write to unused DS9 I/O register: %06X:%04X", address, value);
164			return;
165		}
166		break;
167	}
168	ds->memory.io9[address >> 1] = value;
169}
170
171void DS9IOWrite8(struct DS* ds, uint32_t address, uint8_t value) {
172	if (address < DS9_REG_MAX) {
173		uint16_t value16 = value << (8 * (address & 1));
174		value16 |= (ds->memory.io9[(address & 0x1FFF) >> 1]) & ~(0xFF << (8 * (address & 1)));
175		DS9IOWrite(ds, address & 0xFFFFFFFE, value16);
176	} else {
177		mLOG(DS, STUB, "Writing to unknown DS9 register: %08X:%02X", address, value);
178	}
179}
180
181void DS9IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
182	switch (address) {
183	default:
184		DS9IOWrite(ds, address, value & 0xFFFF);
185		DS9IOWrite(ds, address | 2, value >> 16);
186		return;
187	}
188	ds->memory.io9[address >> 1] = value;
189	ds->memory.io9[(address >> 1) + 1] = value >> 16;
190}
191
192uint16_t DS9IORead(struct DS* ds, uint32_t address) {
193	switch (address) {
194	case DS9_REG_IPCSYNC:
195		// Handled transparently by the registers
196		break;
197	default:
198		mLOG(DS_IO, STUB, "Stub DS9 I/O register read: %06X", address);
199	}
200	if (address < DS9_REG_MAX) {
201		return ds->memory.io9[address >> 1];
202	}
203	return 0;
204}