all repos — mgba @ 3201c984e8d62d72125263b743d59e95aa4a1649

mGBA Game Boy Advance Emulator

src/platform/qt/MultiplayerController.cpp (view raw)

  1/* Copyright (c) 2013-2015 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 "MultiplayerController.h"
  7
  8#include "CoreController.h"
  9
 10#ifdef M_CORE_GBA
 11#include <mgba/internal/gba/gba.h>
 12#endif
 13#ifdef M_CORE_GB
 14#include <mgba/internal/gb/gb.h>
 15#endif
 16
 17using namespace QGBA;
 18
 19#ifdef M_CORE_GB
 20MultiplayerController::Player::Player(CoreController* coreController, GBSIOLockstepNode* node)
 21	: controller(coreController)
 22	, gbNode(node)
 23{
 24}
 25#endif
 26
 27#ifdef M_CORE_GBA
 28MultiplayerController::Player::Player(CoreController* coreController, GBASIOLockstepNode* node)
 29	: controller(coreController)
 30	, gbaNode(node)
 31{
 32}
 33#endif
 34
 35MultiplayerController::MultiplayerController() {
 36	mLockstepInit(&m_lockstep);
 37	m_lockstep.context = this;
 38	m_lockstep.lock = [](mLockstep* lockstep) {
 39		MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
 40		controller->m_lock.lock();
 41	};
 42	m_lockstep.unlock = [](mLockstep* lockstep) {
 43		MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
 44		controller->m_lock.unlock();
 45	};
 46	m_lockstep.signal = [](mLockstep* lockstep, unsigned mask) {
 47		MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
 48		Player* player = &controller->m_players[0];
 49		bool woke = false;
 50		player->waitMask &= ~mask;
 51		if (!player->waitMask && player->awake < 1) {
 52			mCoreThreadStopWaiting(player->controller->thread());
 53			player->awake = 1;
 54			woke = true;
 55		}
 56		return woke;
 57	};
 58	m_lockstep.wait = [](mLockstep* lockstep, unsigned mask) {
 59		MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
 60		Player* player = &controller->m_players[0];
 61		bool slept = false;
 62		player->waitMask |= mask;
 63		if (player->awake > 0) {
 64			mCoreThreadWaitFromThread(player->controller->thread());
 65			player->awake = 0;
 66			slept = true;
 67		}
 68		return slept;
 69	};
 70	m_lockstep.addCycles = [](mLockstep* lockstep, int id, int32_t cycles) {
 71		if (cycles < 0) {
 72			abort();
 73		}
 74		MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
 75		if (!id) {
 76			for (int i = 1; i < controller->m_players.count(); ++i) {
 77				Player* player = &controller->m_players[i];
 78#ifdef M_CORE_GBA
 79				if (player->controller->platform() == PLATFORM_GBA && player->gbaNode->d.p->mode != controller->m_players[0].gbaNode->d.p->mode) {
 80					player->controller->setSync(true);
 81					continue;
 82				}
 83#endif
 84				player->controller->setSync(false);
 85				player->cyclesPosted += cycles;
 86				if (player->awake < 1) {
 87					switch (player->controller->platform()) {
 88#ifdef M_CORE_GBA
 89					case PLATFORM_GBA:
 90						player->gbaNode->nextEvent += player->cyclesPosted;
 91						break;
 92#endif
 93#ifdef M_CORE_GB
 94					case PLATFORM_GB:
 95						player->gbNode->nextEvent += player->cyclesPosted;
 96						break;
 97#endif
 98					default:
 99						break;
100					}
101					mCoreThreadStopWaiting(player->controller->thread());
102					player->awake = 1;
103				}
104			}
105		} else {
106			controller->m_players[id].controller->setSync(true);
107			controller->m_players[id].cyclesPosted += cycles;
108		}
109	};
110	m_lockstep.useCycles = [](mLockstep* lockstep, int id, int32_t cycles) {
111		MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
112		Player* player = &controller->m_players[id];
113		player->cyclesPosted -= cycles;
114		if (player->cyclesPosted <= 0) {
115			mCoreThreadWaitFromThread(player->controller->thread());
116			player->awake = 0;
117		}
118		cycles = player->cyclesPosted;
119		return cycles;
120	};
121	m_lockstep.unusedCycles= [](mLockstep* lockstep, int id) {
122		MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
123		Player* player = &controller->m_players[id];
124		auto cycles = player->cyclesPosted;
125		return cycles;
126	};
127	m_lockstep.unload = [](mLockstep* lockstep, int id) {
128		MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
129		if (id) {
130			Player* player = &controller->m_players[id];
131			player->controller->setSync(true);
132			player->cyclesPosted = 0;
133
134			// release master GBA if it is waiting for this GBA
135			player = &controller->m_players[0];
136			player->waitMask &= ~(1 << id);
137			if (!player->waitMask && player->awake < 1) {
138				mCoreThreadStopWaiting(player->controller->thread());
139				player->awake = 1;
140			}
141		} else {
142			for (int i = 1; i < controller->m_players.count(); ++i) {
143				Player* player = &controller->m_players[i];
144				player->controller->setSync(true);
145				switch (player->controller->platform()) {
146#ifdef M_CORE_GBA
147				case PLATFORM_GBA:
148					player->cyclesPosted += reinterpret_cast<GBASIOLockstep*>(lockstep)->players[0]->eventDiff;
149					break;
150#endif
151#ifdef M_CORE_GB
152				case PLATFORM_GB:
153					player->cyclesPosted += reinterpret_cast<GBSIOLockstep*>(lockstep)->players[0]->eventDiff;
154					break;
155#endif
156				default:
157					break;
158				}
159				if (player->awake < 1) {
160					switch (player->controller->platform()) {
161#ifdef M_CORE_GBA
162					case PLATFORM_GBA:
163						player->gbaNode->nextEvent += player->cyclesPosted;
164						break;
165#endif
166#ifdef M_CORE_GB
167					case PLATFORM_GB:
168						player->gbNode->nextEvent += player->cyclesPosted;
169						break;
170#endif
171					default:
172						break;
173					}
174					mCoreThreadStopWaiting(player->controller->thread());
175					player->awake = 1;
176				}
177			}
178		}
179	};
180}
181
182MultiplayerController::~MultiplayerController() {
183	mLockstepDeinit(&m_lockstep);
184}
185
186bool MultiplayerController::attachGame(CoreController* controller) {
187	if (m_lockstep.attached == MAX_GBAS) {
188		return false;
189	}
190
191	if (m_lockstep.attached == 0) {
192		switch (controller->platform()) {
193#ifdef M_CORE_GBA
194		case PLATFORM_GBA:
195			GBASIOLockstepInit(&m_gbaLockstep);
196			break;
197#endif
198#ifdef M_CORE_GB
199		case PLATFORM_GB:
200			GBSIOLockstepInit(&m_gbLockstep);
201			break;
202#endif
203		default:
204			return false;
205		}
206	}
207
208	mCoreThread* thread = controller->thread();
209	if (!thread) {
210		return false;
211	}
212
213	switch (controller->platform()) {
214#ifdef M_CORE_GBA
215	case PLATFORM_GBA: {
216		GBA* gba = static_cast<GBA*>(thread->core->board);
217
218		GBASIOLockstepNode* node = new GBASIOLockstepNode;
219		GBASIOLockstepNodeCreate(node);
220		GBASIOLockstepAttachNode(&m_gbaLockstep, node);
221		m_players.append({controller, node});
222
223		GBASIOSetDriver(&gba->sio, &node->d, SIO_MULTI);
224
225		emit gameAttached();
226		return true;
227	}
228#endif
229#ifdef M_CORE_GB
230	case PLATFORM_GB: {
231		GB* gb = static_cast<GB*>(thread->core->board);
232
233		GBSIOLockstepNode* node = new GBSIOLockstepNode;
234		GBSIOLockstepNodeCreate(node);
235		GBSIOLockstepAttachNode(&m_gbLockstep, node);
236		m_players.append({controller, node});
237
238		GBSIOSetDriver(&gb->sio, &node->d);
239
240		emit gameAttached();
241		return true;
242	}
243#endif
244	default:
245		break;
246	}
247
248	return false;
249}
250
251void MultiplayerController::detachGame(CoreController* controller) {
252	if (m_players.empty()) {
253		return;
254	}
255	mCoreThread* thread = controller->thread();
256	if (!thread) {
257		return;
258	}
259	QList<CoreController::Interrupter> interrupters;
260
261	for (int i = 0; i < m_players.count(); ++i) {
262		interrupters.append(m_players[i].controller);
263	}
264	switch (controller->platform()) {
265#ifdef M_CORE_GBA
266	case PLATFORM_GBA: {
267		GBA* gba = static_cast<GBA*>(thread->core->board);
268		GBASIOLockstepNode* node = reinterpret_cast<GBASIOLockstepNode*>(gba->sio.drivers.multiplayer);
269		GBASIOSetDriver(&gba->sio, nullptr, SIO_MULTI);
270		if (node) {
271			GBASIOLockstepDetachNode(&m_gbaLockstep, node);
272			delete node;
273		}
274		break;
275	}
276#endif
277#ifdef M_CORE_GB
278	case PLATFORM_GB: {
279		GB* gb = static_cast<GB*>(thread->core->board);
280		GBSIOLockstepNode* node = reinterpret_cast<GBSIOLockstepNode*>(gb->sio.driver);
281		GBSIOSetDriver(&gb->sio, nullptr);
282		if (node) {
283			GBSIOLockstepDetachNode(&m_gbLockstep, node);
284			delete node;
285		}
286		break;
287	}
288#endif
289	default:
290		break;
291	}
292
293	for (int i = 0; i < m_players.count(); ++i) {
294		if (m_players[i].controller == controller) {
295			m_players.removeAt(i);
296			break;
297		}
298	}
299	emit gameDetached();
300}
301
302int MultiplayerController::playerId(CoreController* controller) {
303	for (int i = 0; i < m_players.count(); ++i) {
304		if (m_players[i].controller == controller) {
305			return i;
306		}
307	}
308	return -1;
309}
310
311int MultiplayerController::attached() {
312	int num;
313	num = m_lockstep.attached;
314	return num;
315}