GBA SIO: Dolphin connectivity

This commit is contained in:
Vicki Pfau 2017-07-14 09:04:40 -07:00
parent 66093288e2
commit b1828dbc59
8 changed files with 282 additions and 3 deletions

View File

@ -78,9 +78,6 @@ struct GBASIODriver {
uint16_t (*writeRegister)(struct GBASIODriver* driver, uint32_t address, uint16_t value);
};
void GBASIOJOYCreate(struct GBASIODriver* sio);
int GBASIOJOYSendCommand(struct GBASIODriver* sio, enum GBASIOJOYCommand command, uint8_t* data);
enum GBASIOBattleChipGateFlavor {
GBA_FLAVOR_BATTLECHIP_GATE = 4,
GBA_FLAVOR_PROGRESS_GATE = 5,

View File

@ -78,6 +78,9 @@ void GBASIOWriteRCNT(struct GBASIO* sio, uint16_t value);
void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value);
uint16_t GBASIOWriteRegister(struct GBASIO* sio, uint32_t address, uint16_t value);
void GBASIOJOYCreate(struct GBASIODriver* sio);
int GBASIOJOYSendCommand(struct GBASIODriver* sio, enum GBASIOJOYCommand command, uint8_t* data);
CXX_GUARD_END
#endif

View File

@ -0,0 +1,40 @@
/* Copyright (c) 2013-2017 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef SIO_DOLPHIN_H
#define SIO_DOLPHIN_H
#include <mgba-util/common.h>
CXX_GUARD_START
#include <mgba/core/timing.h>
#include <mgba/internal/gba/sio.h>
#include <mgba-util/socket.h>
extern const uint16_t DOLPHIN_CLOCK_PORT;
extern const uint16_t DOLPHIN_DATA_PORT;
struct GBASIODolphin {
struct GBASIODriver d;
struct mTimingEvent event;
Socket data;
Socket clock;
int32_t clockSlice;
int state;
};
void GBASIODolphinCreate(struct GBASIODolphin*);
void GBASIODolphinDestroy(struct GBASIODolphin*);
bool GBASIODolphinConnect(struct GBASIODolphin*, const struct Address* address, short dataPort, short clockPort);
bool GBASIODolphinIsConnected(struct GBASIODolphin*);
CXX_GUARD_END
#endif

View File

@ -34,6 +34,7 @@ set(SOURCE_FILES
video.c)
set(SIO_FILES
sio/dolphin.c
sio/joybus.c
sio/lockstep.c)

190
src/gba/sio/dolphin.c Normal file
View File

@ -0,0 +1,190 @@
/* Copyright (c) 2013-2017 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba/internal/gba/sio/dolphin.h>
#include <mgba/internal/gba/gba.h>
#include <mgba/internal/gba/io.h>
#define BITS_PER_SECOND 115200 // This is wrong, but we need to maintain compat for the time being
#define CYCLES_PER_BIT (GBA_ARM7TDMI_FREQUENCY / BITS_PER_SECOND)
#define CLOCK_GRAIN (CYCLES_PER_BIT * 8)
#define CLOCK_WAIT 500
const uint16_t DOLPHIN_CLOCK_PORT = 49420;
const uint16_t DOLPHIN_DATA_PORT = 54970;
enum {
CMD_RESET = 0xFF,
CMD_POLL = 0x00,
CMD_TRANS = 0x14,
CMD_RECV = 0x15,
CMD_NONE = 0x80
};
enum {
WAIT_FOR_FIRST_CLOCK = 0,
WAIT_FOR_CLOCK,
WAIT_FOR_COMMAND,
};
static bool GBASIODolphinLoad(struct GBASIODriver* driver);
static void GBASIODolphinProcessEvents(struct mTiming* timing, void* context, uint32_t cyclesLate);
static int32_t _processCommand(struct GBASIODolphin* dol, uint32_t cyclesLate);
void GBASIODolphinCreate(struct GBASIODolphin* dol) {
GBASIOJOYCreate(&dol->d);
dol->d.load = GBASIODolphinLoad;
dol->event.context = dol;
dol->event.name = "GB SIO Lockstep";
dol->event.callback = GBASIODolphinProcessEvents;
dol->event.priority = 0x80;
dol->data = INVALID_SOCKET;
dol->clock = INVALID_SOCKET;
}
void GBASIODolphinDestroy(struct GBASIODolphin* dol) {
if (!SOCKET_FAILED(dol->data)) {
SocketClose(dol->data);
dol->data = INVALID_SOCKET;
}
if (!SOCKET_FAILED(dol->clock)) {
SocketClose(dol->clock);
dol->clock = INVALID_SOCKET;
}
}
bool GBASIODolphinConnect(struct GBASIODolphin* dol, const struct Address* address, short dataPort, short clockPort) {
if (!SOCKET_FAILED(dol->data)) {
SocketClose(dol->data);
dol->data = INVALID_SOCKET;
}
if (!dataPort) {
dataPort = DOLPHIN_DATA_PORT;
}
if (!SOCKET_FAILED(dol->clock)) {
SocketClose(dol->clock);
dol->clock = INVALID_SOCKET;
}
if (!clockPort) {
clockPort = DOLPHIN_CLOCK_PORT;
}
dol->data = SocketConnectTCP(dataPort, address);
if (SOCKET_FAILED(dol->data)) {
return false;
}
dol->clock = SocketConnectTCP(clockPort, address);
if (SOCKET_FAILED(dol->clock)) {
SocketClose(dol->data);
dol->data = INVALID_SOCKET;
return false;
}
SocketSetBlocking(dol->data, false);
SocketSetBlocking(dol->clock, false);
SocketSetTCPPush(dol->data, true);
return true;
}
static bool GBASIODolphinLoad(struct GBASIODriver* driver) {
struct GBASIODolphin* dol = (struct GBASIODolphin*) driver;
dol->clockSlice = 0;
dol->state = WAIT_FOR_FIRST_CLOCK;
mTimingDeschedule(&dol->d.p->p->timing, &dol->event);
mTimingSchedule(&dol->d.p->p->timing, &dol->event, 0);
return true;
}
void GBASIODolphinProcessEvents(struct mTiming* timing, void* context, uint32_t cyclesLate) {
struct GBASIODolphin* dol = context;
dol->clockSlice -= cyclesLate;
int32_t clockSlice;
int32_t nextEvent = CLOCK_GRAIN;
switch (dol->state) {
case WAIT_FOR_FIRST_CLOCK:
dol->clockSlice = 0;
// Fall through
case WAIT_FOR_CLOCK:
if (dol->clockSlice < 0) {
Socket r = dol->clock;
SocketPoll(1, &r, 0, 0, CLOCK_WAIT);
}
if (SocketRecv(dol->clock, &clockSlice, 4) == 4) {
clockSlice = ntohl(clockSlice);
dol->clockSlice += clockSlice;
dol->state = WAIT_FOR_COMMAND;
nextEvent = 0;
}
// Fall through
case WAIT_FOR_COMMAND:
if (dol->clockSlice < -VIDEO_TOTAL_LENGTH * 4) {
Socket r = dol->data;
SocketPoll(1, &r, 0, 0, CLOCK_WAIT);
}
if (_processCommand(dol, cyclesLate)) {
dol->state = WAIT_FOR_CLOCK;
nextEvent = CLOCK_GRAIN;
}
break;
}
dol->clockSlice -= nextEvent;
mTimingSchedule(timing, &dol->event, nextEvent);
}
int32_t _processCommand(struct GBASIODolphin* dol, uint32_t cyclesLate) {
// This does not include the stop bits due to compatibility reasons
int bitsOnLine = 8;
uint8_t buffer[6];
int gotten = SocketRecv(dol->data, &buffer, 5);
if (gotten < 1) {
return 0;
}
switch (buffer[0]) {
case CMD_RESET:
case CMD_POLL:
bitsOnLine += 24;
break;
case CMD_RECV:
mLOG(GBA_SIO, DEBUG, "JOY <: %02X%02X%02X%02X", buffer[1], buffer[2], buffer[3], buffer[4]);
// Fall through
case CMD_TRANS:
bitsOnLine += 40;
break;
}
int sent = GBASIOJOYSendCommand(&dol->d, buffer[0], &buffer[1]);
SocketSend(dol->data, &buffer[1], sent);
switch (buffer[0]) {
case CMD_TRANS:
mLOG(GBA_SIO, DEBUG, "JOY >: %02X%02X%02X%02X:%02X", buffer[1], buffer[2], buffer[3], buffer[4], buffer[5]);
break;
case CMD_RECV:
mLOG(GBA_SIO, DEBUG, "JOY <: %02X", buffer[1]);
break;
case CMD_RESET:
mLOG(GBA_SIO, DEBUG, "JOY !: %02X", buffer[3]);
break;
case CMD_POLL:
mLOG(GBA_SIO, DEBUG, "JOY ?: %02X", buffer[3]);
break;
}
return bitsOnLine * CYCLES_PER_BIT - cyclesLate;
}
bool GBASIODolphinIsConnected(struct GBASIODolphin* dol) {
return dol->data != INVALID_SOCKET;
}

View File

@ -42,6 +42,10 @@ CoreController::CoreController(mCore* core, QObject* parent)
m_threadContext.userData = this;
updateROMInfo();
#ifdef M_CORE_GBA
GBASIODolphinCreate(&m_dolphin);
#endif
m_resetActions.append([this]() {
if (m_autoload) {
mCoreLoadState(m_threadContext.core, 0, m_loadStateFlags);
@ -113,6 +117,7 @@ CoreController::CoreController(mCore* core, QObject* parent)
}
controller->clearMultiplayerController();
controller->detachDolphin();
QMetaObject::invokeMethod(controller, "stopping");
};
@ -357,6 +362,29 @@ mCacheSet* CoreController::graphicCaches() {
return m_cacheSet.get();
}
bool CoreController::connectDolphin(uint32_t ipv4) {
if (platform() != mPLATFORM_GBA) {
return false;
}
Address ipaddr;
ipaddr.version = IPV4;
ipaddr.ipv4 = htonl(ipv4);
if (GBASIODolphinConnect(&m_dolphin, &ipaddr, 0, 0)) {
GBA* gba = static_cast<GBA*>(m_threadContext.core->board);
GBASIOSetDriver(&gba->sio, &m_dolphin.d, SIO_JOYBUS);
return true;
}
return false;
}
void CoreController::detachDolphin() {
if (platform() == mPLATFORM_GBA) {
GBA* gba = static_cast<GBA*>(m_threadContext.core->board);
GBASIOSetDriver(&gba->sio, nullptr, SIO_JOYBUS);
}
GBASIODolphinDestroy(&m_dolphin);
}
void CoreController::setOverride(std::unique_ptr<Override> override) {
Interrupter interrupter(this);
m_override = std::move(override);

View File

@ -25,6 +25,9 @@
#ifdef M_CORE_GB
#include <mgba/internal/gb/sio/printer.h>
#endif
#ifdef M_CORE_GBA
#include <mgba/internal/gba/sio/dolphin.h>
#endif
#ifdef M_CORE_GBA
#include <mgba/gba/interface.h>
@ -105,6 +108,9 @@ public:
void clearMultiplayerController();
MultiplayerController* multiplayerController() { return m_multiplayer; }
bool connectDolphin(uint32_t ipv4);
void detachDolphin();
mCacheSet* graphicCaches();
int stateSlot() const { return m_stateSlot; }
@ -269,6 +275,9 @@ private:
InputController* m_inputController = nullptr;
LogController* m_log = nullptr;
MultiplayerController* m_multiplayer = nullptr;
#ifdef M_CORE_GBA
GBASIODolphin m_dolphin;
#endif
mVideoLogContext* m_vl = nullptr;
VFile* m_vlVf = nullptr;

View File

@ -1192,6 +1192,17 @@ void Window::setupMenu(QMenuBar* menubar) {
GBAApp::app()->newWindow();
}, "file");
Action* dolphin = m_actions.addAction(tr("Connect to Dolphin"), "connectDolphin", [this]() {
CoreController::Interrupter interrupter;
if (m_controller) {
interrupter.interrupt(m_controller);
} else {
setController(m_manager->loadBIOS(mPLATFORM_GBA, m_config->getOption("gba.bios")), QString());
}
m_controller->connectDolphin(0x0100007F);
}, "file");
m_platformActions.insert(mPLATFORM_GBA, dolphin);
#ifndef Q_OS_MAC
m_actions.addSeparator("file");
#endif