all repos — mgba @ 2359a4e88689fd23f5cf2ef57f7aa38055158b52

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