mirror of https://github.com/AxioDL/jbus.git
Add Doxygen comments
This commit is contained in:
parent
a3fe0a3a25
commit
4699542f93
|
@ -0,0 +1,9 @@
|
|||
JBus Documentation {#mainpage}
|
||||
==============================
|
||||
|
||||
JBus functions as a server for acceping connections from GBA emulator clients.
|
||||
The jbus::Listener class may be constructed and [started](@ref jbus::Listener::start)
|
||||
to enque incoming jbus::Endpoint instances.
|
||||
|
||||
Once an Endpoint has been [accepted](@ref jbus::Listener::accept), it's ready to use.
|
||||
Refer to the jbus::Endpoint class for the main interface.
|
|
@ -0,0 +1,22 @@
|
|||
The MIT License
|
||||
|
||||
Copyright (c) 2016-2017 JBus Contributors
|
||||
Original Authors: Jack Andersen
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
11
README.md
11
README.md
|
@ -1,8 +1,17 @@
|
|||
## JBus
|
||||
|
||||
This is a library for communicating with emulated GameBoy Advance instances
|
||||
using the JoyBus protocol, linked over TCP.
|
||||
using the JoyBus protocol linked over TCP.
|
||||
|
||||
Currently, only [VBA-M](https://github.com/visualboyadvance-m/visualboyadvance-m)
|
||||
is known to function. It uses the same networking method as the
|
||||
[Dolphin](https://github.com/dolphin-emu/dolphin) GameCube emulator.
|
||||
|
||||
### Documentation
|
||||
|
||||
[Doxygen docs are available!](http://axiodl.github.io/jbus)
|
||||
|
||||
### Example
|
||||
|
||||
[Refer to the `joyboot.cpp`](https://github.com/AxioDL/jbus/blob/master/tools/joyboot.cpp)
|
||||
implementation for a good usage example.
|
||||
|
|
|
@ -17,6 +17,8 @@ using u32 = uint32_t;
|
|||
using s64 = int64_t;
|
||||
using u64 = uint64_t;
|
||||
|
||||
#ifndef DOXYGEN_SHOULD_SKIP_THIS
|
||||
|
||||
#undef bswap16
|
||||
#undef bswap32
|
||||
#undef bswap64
|
||||
|
@ -139,6 +141,8 @@ static inline double SBig(double val) {return val;}
|
|||
class Endpoint;
|
||||
class ThreadLocalEndpoint;
|
||||
|
||||
#endif
|
||||
|
||||
enum EJStatFlags
|
||||
{
|
||||
GBA_JSTAT_MASK = 0x3a,
|
||||
|
@ -159,11 +163,24 @@ enum EJoyReturn
|
|||
GBA_JOYBOOT_ERR_INVALID = 4
|
||||
};
|
||||
|
||||
/** @brief Standard callback for asynchronous jbus::Endpoint APIs.
|
||||
* @param endpoint Thread-local Endpoint interface for optionally issuing next command in sequence.
|
||||
* @param status GBA_READY if connection is still open, GBA_NOT_READY if connection lost. */
|
||||
using FGBACallback = std::function<void(ThreadLocalEndpoint& endpoint, EJoyReturn status)>;
|
||||
|
||||
/** @brief Get host system's timebase scaled into Dolphin ticks.
|
||||
* @return Scaled ticks from host timebase. */
|
||||
u64 GetGCTicks();
|
||||
|
||||
/** @brief Wait an approximate Dolphin tick duration (avoid using, it's rather inaccurate).
|
||||
* @param ticks CPU ticks to wait. */
|
||||
void WaitGCTicks(u64 ticks);
|
||||
|
||||
/** @brief Obtain CPU ticks per second of Dolphin hardware (clock speed).
|
||||
* @return 486Mhz - always. */
|
||||
static constexpr u64 GetGCTicksPerSec() { return 486000000ull; }
|
||||
|
||||
/** @brief Initialize platform specifics of JBus library */
|
||||
void Initialize();
|
||||
|
||||
}
|
||||
|
|
|
@ -11,118 +11,124 @@
|
|||
namespace jbus
|
||||
{
|
||||
|
||||
/** Self-contained class for solving Kawasedo's GBA BootROM challenge.
|
||||
* GBA will boot client_pad.bin code on completion. */
|
||||
class KawasedoChallenge
|
||||
{
|
||||
/** DSP-hosted public-key unwrap and initial message crypt
|
||||
* Reference: https://github.com/dolphin-emu/dolphin/blob/master/Source/Core/Core/HW/DSPHLE/UCodes/GBA.cpp */
|
||||
struct DSPSecParms
|
||||
{
|
||||
/* Nonce challenge (first read from GBA, hence already little-endian) */
|
||||
u32 x0_gbaChallenge;
|
||||
|
||||
/* Palette of pulsing logo on GBA during transmission [0,6] */
|
||||
u32 x4_logoPalette;
|
||||
|
||||
/* Speed and direction of palette interpolation [-4,4] */
|
||||
u32 x8_logoSpeed;
|
||||
|
||||
/* Length of JoyBoot program to upload */
|
||||
u32 xc_progLength;
|
||||
|
||||
/* Unwrapped public key */
|
||||
u32 x20_publicKey;
|
||||
|
||||
/* Message authentication code */
|
||||
u32 x24_authInitCode;
|
||||
|
||||
void ProcessGBACrypto()
|
||||
{
|
||||
/* Unwrap key from challenge using 'sedo' magic number (to encrypt JoyBoot program) */
|
||||
x20_publicKey = x0_gbaChallenge ^ 0x6f646573;
|
||||
|
||||
/* Pack palette parameters */
|
||||
u16 paletteSpeedCoded;
|
||||
s16 logoSpeed = static_cast<s8>(x8_logoSpeed);
|
||||
if (logoSpeed < 0)
|
||||
paletteSpeedCoded = ((-logoSpeed + 2) * 2) | (x4_logoPalette << 4);
|
||||
else if (logoSpeed == 0)
|
||||
paletteSpeedCoded = (x4_logoPalette * 2) | 0x70;
|
||||
else /* logo_speed > 0 */
|
||||
paletteSpeedCoded = ((logoSpeed - 1) * 2) | (x4_logoPalette << 4);
|
||||
|
||||
/* JoyBoot ROMs start with a padded header; this is the length beyond that header */
|
||||
s32 lengthNoHeader = ROUND_UP_8(xc_progLength) - 0x200;
|
||||
|
||||
/* The JoyBus protocol transmits in 4-byte packets while flipping a state flag;
|
||||
* so the GBA BIOS counts the program length in 8-byte packet-pairs */
|
||||
u16 packetPairCount = (lengthNoHeader < 0) ? 0 : lengthNoHeader / 8;
|
||||
paletteSpeedCoded |= (packetPairCount & 0x4000) >> 14;
|
||||
|
||||
/* Pack together encoded transmission parameters */
|
||||
u32 t1 = (((packetPairCount << 16) | 0x3f80) & 0x3f80ffff) * 2;
|
||||
t1 += (static_cast<s16>(static_cast<s8>(t1 >> 8)) & packetPairCount) << 16;
|
||||
u32 t2 = ((paletteSpeedCoded & 0xff) << 16) + (t1 & 0xff0000) + ((t1 >> 8) & 0xffff00);
|
||||
u32 t3 = paletteSpeedCoded << 16 | ((t2 << 8) & 0xff000000) | (t1 >> 16) | 0x80808080;
|
||||
|
||||
/* Wrap with 'Kawa' or 'sedo' (Kawasedo is the author of the BIOS cipher) */
|
||||
x24_authInitCode = t3 ^ ((t3 & 0x200) != 0 ? 0x6f646573 : 0x6177614b);
|
||||
}
|
||||
} xf8_dspHmac;
|
||||
|
||||
s32 x0_pColor;
|
||||
s32 x4_pSpeed;
|
||||
u8* x8_progPtr;
|
||||
u32 xc_progLen;
|
||||
u8* x10_statusPtr;
|
||||
FGBACallback x14_callback;
|
||||
u8 x18_readBuf[4];
|
||||
u8 x1c_writeBuf[4];
|
||||
s32 x20_byteInWindow;
|
||||
u64 x28_ticksAfterXf;
|
||||
u32 x30_justStarted;
|
||||
u32 x34_bytesSent;
|
||||
u32 x38_crc;
|
||||
u32 x3c_checkStore[7];
|
||||
s32 x58_currentKey;
|
||||
s32 x5c_initMessage;
|
||||
s32 x60_gameId;
|
||||
u32 x64_totalBytes;
|
||||
bool m_started = true;
|
||||
|
||||
void F23(ThreadLocalEndpoint& endpoint, EJoyReturn status);
|
||||
void F25(ThreadLocalEndpoint& endpoint, EJoyReturn status);
|
||||
void F27(ThreadLocalEndpoint& endpoint, EJoyReturn status);
|
||||
void F29(ThreadLocalEndpoint& endpoint, EJoyReturn status);
|
||||
void GBAX02();
|
||||
void GBAX01(ThreadLocalEndpoint& endpoint);
|
||||
void F31(ThreadLocalEndpoint& endpoint, EJoyReturn status);
|
||||
void F33(ThreadLocalEndpoint& endpoint, EJoyReturn status);
|
||||
void F35(ThreadLocalEndpoint& endpoint, EJoyReturn status);
|
||||
void F37(ThreadLocalEndpoint& endpoint, EJoyReturn status);
|
||||
void F39(ThreadLocalEndpoint& endpoint, EJoyReturn status);
|
||||
|
||||
auto bindThis(void(KawasedoChallenge::*ptmf)(ThreadLocalEndpoint&, EJoyReturn))
|
||||
{
|
||||
return std::bind(ptmf, this, std::placeholders::_1, std::placeholders::_2);
|
||||
}
|
||||
|
||||
public:
|
||||
KawasedoChallenge(Endpoint& endpoint, s32 paletteColor, s32 paletteSpeed,
|
||||
u8* programp, s32 length, u8* status, FGBACallback&& callback);
|
||||
bool started() const { return m_started; }
|
||||
u8 percentComplete() const
|
||||
{
|
||||
if (!x64_totalBytes)
|
||||
return 0;
|
||||
return x34_bytesSent * 100 / x64_totalBytes;
|
||||
}
|
||||
bool isDone() const { return !x14_callback; }
|
||||
};
|
||||
|
||||
/** Main class for performing JoyBoot and subsequent JoyBus I/O operations.
|
||||
* Instances should be obtained though the jbus::Listener::accept method. */
|
||||
class Endpoint
|
||||
{
|
||||
/** Self-contained class for solving Kawasedo's GBA BootROM challenge.
|
||||
* GBA will boot client_pad.bin code on completion.
|
||||
*
|
||||
* This class shouldn't be used directly. JoyBoot operations are started
|
||||
* via jbus::Endpoint::GBAJoyBootAsync. The JoyBoot status may be obtained
|
||||
* via jbus::Endpoint::GBAGetProcessStatus. */
|
||||
class KawasedoChallenge
|
||||
{
|
||||
/** DSP-hosted public-key unwrap and initial message crypt
|
||||
* Reference: https://github.com/dolphin-emu/dolphin/blob/master/Source/Core/Core/HW/DSPHLE/UCodes/GBA.cpp */
|
||||
struct DSPSecParms
|
||||
{
|
||||
/* Nonce challenge (first read from GBA, hence already little-endian) */
|
||||
u32 x0_gbaChallenge;
|
||||
|
||||
/* Palette of pulsing logo on GBA during transmission [0,6] */
|
||||
u32 x4_logoPalette;
|
||||
|
||||
/* Speed and direction of palette interpolation [-4,4] */
|
||||
u32 x8_logoSpeed;
|
||||
|
||||
/* Length of JoyBoot program to upload */
|
||||
u32 xc_progLength;
|
||||
|
||||
/* Unwrapped public key */
|
||||
u32 x20_publicKey;
|
||||
|
||||
/* Message authentication code */
|
||||
u32 x24_authInitCode;
|
||||
|
||||
void ProcessGBACrypto()
|
||||
{
|
||||
/* Unwrap key from challenge using 'sedo' magic number (to encrypt JoyBoot program) */
|
||||
x20_publicKey = x0_gbaChallenge ^ 0x6f646573;
|
||||
|
||||
/* Pack palette parameters */
|
||||
u16 paletteSpeedCoded;
|
||||
s16 logoSpeed = static_cast<s8>(x8_logoSpeed);
|
||||
if (logoSpeed < 0)
|
||||
paletteSpeedCoded = ((-logoSpeed + 2) * 2) | (x4_logoPalette << 4);
|
||||
else if (logoSpeed == 0)
|
||||
paletteSpeedCoded = (x4_logoPalette * 2) | 0x70;
|
||||
else /* logo_speed > 0 */
|
||||
paletteSpeedCoded = ((logoSpeed - 1) * 2) | (x4_logoPalette << 4);
|
||||
|
||||
/* JoyBoot ROMs start with a padded header; this is the length beyond that header */
|
||||
s32 lengthNoHeader = ROUND_UP_8(xc_progLength) - 0x200;
|
||||
|
||||
/* The JoyBus protocol transmits in 4-byte packets while flipping a state flag;
|
||||
* so the GBA BIOS counts the program length in 8-byte packet-pairs */
|
||||
u16 packetPairCount = (lengthNoHeader < 0) ? 0 : lengthNoHeader / 8;
|
||||
paletteSpeedCoded |= (packetPairCount & 0x4000) >> 14;
|
||||
|
||||
/* Pack together encoded transmission parameters */
|
||||
u32 t1 = (((packetPairCount << 16) | 0x3f80) & 0x3f80ffff) * 2;
|
||||
t1 += (static_cast<s16>(static_cast<s8>(t1 >> 8)) & packetPairCount) << 16;
|
||||
u32 t2 = ((paletteSpeedCoded & 0xff) << 16) + (t1 & 0xff0000) + ((t1 >> 8) & 0xffff00);
|
||||
u32 t3 = paletteSpeedCoded << 16 | ((t2 << 8) & 0xff000000) | (t1 >> 16) | 0x80808080;
|
||||
|
||||
/* Wrap with 'Kawa' or 'sedo' (Kawasedo is the author of the BIOS cipher) */
|
||||
x24_authInitCode = t3 ^ ((t3 & 0x200) != 0 ? 0x6f646573 : 0x6177614b);
|
||||
}
|
||||
} xf8_dspHmac;
|
||||
|
||||
s32 x0_pColor;
|
||||
s32 x4_pSpeed;
|
||||
const u8* x8_progPtr;
|
||||
u32 xc_progLen;
|
||||
u8* x10_statusPtr;
|
||||
FGBACallback x14_callback;
|
||||
u8 x18_readBuf[4];
|
||||
u8 x1c_writeBuf[4];
|
||||
s32 x20_byteInWindow;
|
||||
u64 x28_ticksAfterXf;
|
||||
u32 x30_justStarted;
|
||||
u32 x34_bytesSent;
|
||||
u32 x38_crc;
|
||||
u32 x3c_checkStore[7];
|
||||
s32 x58_currentKey;
|
||||
s32 x5c_initMessage;
|
||||
s32 x60_gameId;
|
||||
u32 x64_totalBytes;
|
||||
bool m_started = true;
|
||||
|
||||
void _0Reset(ThreadLocalEndpoint& endpoint, EJoyReturn status);
|
||||
void _1GetStatus(ThreadLocalEndpoint& endpoint, EJoyReturn status);
|
||||
void _2ReadChallenge(ThreadLocalEndpoint& endpoint, EJoyReturn status);
|
||||
void _3DSPCrypto(ThreadLocalEndpoint& endpoint, EJoyReturn status);
|
||||
void _DSPCryptoInit();
|
||||
void _DSPCryptoDone(ThreadLocalEndpoint& endpoint);
|
||||
void _4TransmitProgram(ThreadLocalEndpoint& endpoint, EJoyReturn status);
|
||||
void _5StartBootPoll(ThreadLocalEndpoint& endpoint, EJoyReturn status);
|
||||
void _6BootPoll(ThreadLocalEndpoint& endpoint, EJoyReturn status);
|
||||
void _7BootAcknowledge(ThreadLocalEndpoint& endpoint, EJoyReturn status);
|
||||
void _8BootDone(ThreadLocalEndpoint& endpoint, EJoyReturn status);
|
||||
|
||||
auto bindThis(void(KawasedoChallenge::*ptmf)(ThreadLocalEndpoint&, EJoyReturn))
|
||||
{
|
||||
return std::bind(ptmf, this, std::placeholders::_1, std::placeholders::_2);
|
||||
}
|
||||
|
||||
public:
|
||||
KawasedoChallenge(Endpoint& endpoint, s32 paletteColor, s32 paletteSpeed,
|
||||
const u8* programp, s32 length, u8* status, FGBACallback&& callback);
|
||||
bool started() const { return m_started; }
|
||||
u8 percentComplete() const
|
||||
{
|
||||
if (!x64_totalBytes)
|
||||
return 0;
|
||||
return x34_bytesSent * 100 / x64_totalBytes;
|
||||
}
|
||||
bool isDone() const { return !x14_callback; }
|
||||
};
|
||||
|
||||
friend class ThreadLocalEndpoint;
|
||||
|
||||
enum EJoybusCmds
|
||||
|
@ -154,7 +160,6 @@ class Endpoint
|
|||
bool m_cmdIssued = false;
|
||||
bool m_running = true;
|
||||
|
||||
static u64 getTransferTime(u8 cmd);
|
||||
void clockSync();
|
||||
void send(const u8* buffer);
|
||||
size_t receive(u8* buffer);
|
||||
|
@ -170,34 +175,122 @@ class Endpoint
|
|||
}
|
||||
|
||||
public:
|
||||
/** @brief Request stop of I/O thread and block until joined.
|
||||
* Further use of this Endpoint is undefined behavior.
|
||||
* The destructor calls this implicitly. */
|
||||
void stop();
|
||||
EJoyReturn GBAGetProcessStatus(u8* percentp);
|
||||
|
||||
/** @brief Get status of last asynchronous operation.
|
||||
* @param percentOut Reference to output transfer percent of GBAJoyBootAsync.
|
||||
* @return GBA_READY when idle, or GBA_BUSY when operation in progress. */
|
||||
EJoyReturn GBAGetProcessStatus(u8& percentOut);
|
||||
|
||||
/** @brief Get JOYSTAT register from GBA asynchronously.
|
||||
* @param status Destination pointer for EJStatFlags.
|
||||
* @param callback Functor to execute when operation completes.
|
||||
* @return GBA_READY if submitted, or GBA_NOT_READY if another operation in progress. */
|
||||
EJoyReturn GBAGetStatusAsync(u8* status, FGBACallback&& callback);
|
||||
|
||||
/** @brief Get JOYSTAT register from GBA synchronously.
|
||||
* @param status Destination pointer for EJStatFlags.
|
||||
* @return GBA_READY if submitted, or GBA_NOT_READY if another operation in progress. */
|
||||
EJoyReturn GBAGetStatus(u8* status);
|
||||
|
||||
/** @brief Send RESET command to GBA asynchronously.
|
||||
* @param status Destination pointer for EJStatFlags.
|
||||
* @param callback Functor to execute when operation completes.
|
||||
* @return GBA_READY if submitted, or GBA_NOT_READY if another operation in progress. */
|
||||
EJoyReturn GBAResetAsync(u8* status, FGBACallback&& callback);
|
||||
|
||||
/** @brief Send RESET command to GBA synchronously.
|
||||
* @param status Destination pointer for EJStatFlags.
|
||||
* @return GBA_READY if submitted, or GBA_NOT_READY if another operation in progress. */
|
||||
EJoyReturn GBAReset(u8* status);
|
||||
|
||||
/** @brief Send READ command to GBA asynchronously.
|
||||
* @param dst Destination pointer for 4-byte packet of data.
|
||||
* @param status Destination pointer for EJStatFlags.
|
||||
* @param callback Functor to execute when operation completes.
|
||||
* @return GBA_READY if submitted, or GBA_NOT_READY if another operation in progress. */
|
||||
EJoyReturn GBAReadAsync(u8* dst, u8* status, FGBACallback&& callback);
|
||||
|
||||
/** @brief Send READ command to GBA synchronously.
|
||||
* @param dst Destination pointer for 4-byte packet of data.
|
||||
* @param status Destination pointer for EJStatFlags.
|
||||
* @return GBA_READY if submitted, or GBA_NOT_READY if another operation in progress. */
|
||||
EJoyReturn GBARead(u8* dst, u8* status);
|
||||
|
||||
/** @brief Send WRITE command to GBA asynchronously.
|
||||
* @param src Source pointer for 4-byte packet of data. It is not required to keep resident.
|
||||
* @param status Destination pointer for EJStatFlags.
|
||||
* @param callback Functor to execute when operation completes.
|
||||
* @return GBA_READY if submitted, or GBA_NOT_READY if another operation in progress. */
|
||||
EJoyReturn GBAWriteAsync(const u8* src, u8* status, FGBACallback&& callback);
|
||||
|
||||
/** @brief Send WRITE command to GBA synchronously.
|
||||
* @param src Source pointer for 4-byte packet of data. It is not required to keep resident.
|
||||
* @param status Destination pointer for EJStatFlags.
|
||||
* @return GBA_READY if submitted, or GBA_NOT_READY if another operation in progress. */
|
||||
EJoyReturn GBAWrite(const u8* src, u8* status);
|
||||
|
||||
/** @brief Initiate JoyBoot sequence on this endpoint.
|
||||
* @param paletteColor Palette for displaying logo in ROM header [0,6].
|
||||
* @param paletteSpeed Palette interpolation speed for displaying logo in ROM header [-4,4].
|
||||
* @param programp Pointer to program ROM data.
|
||||
* @param length Length of program ROM data.
|
||||
* @param status Destination pointer for EJStatFlags.
|
||||
* @param callback Functor to execute when operation completes.
|
||||
* @return GBA_READY if submitted, or GBA_NOT_READY if another operation in progress. */
|
||||
EJoyReturn GBAJoyBootAsync(s32 paletteColor, s32 paletteSpeed,
|
||||
u8* programp, s32 length, u8* status,
|
||||
const u8* programp, s32 length, u8* status,
|
||||
FGBACallback&& callback);
|
||||
|
||||
/** @brief Get virtual SI channel assigned to this endpoint.
|
||||
* @return SI channel */
|
||||
int GetChan() const { return m_chan; }
|
||||
|
||||
Endpoint(u8 chan, net::Socket&& data, net::Socket&& clock);
|
||||
~Endpoint();
|
||||
};
|
||||
|
||||
/** Lockless wrapper interface for jbus::Endpoint.
|
||||
* This class is constructed internally and supplied as a callback argument.
|
||||
* It should not be constructed directly. */
|
||||
class ThreadLocalEndpoint
|
||||
{
|
||||
friend class Endpoint;
|
||||
Endpoint& m_ep;
|
||||
ThreadLocalEndpoint(Endpoint& ep) : m_ep(ep) {}
|
||||
|
||||
public:
|
||||
/** @brief Get JOYSTAT register from GBA asynchronously.
|
||||
* @param status Destination pointer for EJStatFlags.
|
||||
* @param callback Functor to execute when operation completes.
|
||||
* @return GBA_READY if submitted, or GBA_NOT_READY if another operation in progress. */
|
||||
EJoyReturn GBAGetStatusAsync(u8* status, FGBACallback&& callback);
|
||||
|
||||
/** @brief Send RESET command to GBA asynchronously.
|
||||
* @param status Destination pointer for EJStatFlags.
|
||||
* @param callback Functor to execute when operation completes.
|
||||
* @return GBA_READY if submitted, or GBA_NOT_READY if another operation in progress. */
|
||||
EJoyReturn GBAResetAsync(u8* status, FGBACallback&& callback);
|
||||
|
||||
/** @brief Send READ command to GBA asynchronously.
|
||||
* @param dst Destination pointer for 4-byte packet of data.
|
||||
* @param status Destination pointer for EJStatFlags.
|
||||
* @param callback Functor to execute when operation completes.
|
||||
* @return GBA_READY if submitted, or GBA_NOT_READY if another operation in progress. */
|
||||
EJoyReturn GBAReadAsync(u8* dst, u8* status, FGBACallback&& callback);
|
||||
|
||||
/** @brief Send WRITE command to GBA asynchronously.
|
||||
* @param src Source pointer for 4-byte packet of data. It is not required to keep resident.
|
||||
* @param status Destination pointer for EJStatFlags.
|
||||
* @param callback Functor to execute when operation completes.
|
||||
* @return GBA_READY if submitted, or GBA_NOT_READY if another operation in progress. */
|
||||
EJoyReturn GBAWriteAsync(const u8* src, u8* status, FGBACallback&& callback);
|
||||
|
||||
/** @brief Get virtual SI channel assigned to this endpoint.
|
||||
* @return SI channel */
|
||||
int GetChan() const { return m_ep.GetChan(); }
|
||||
};
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
namespace jbus
|
||||
{
|
||||
|
||||
/** Server interface for accepting incoming connections from GBA emulator instances. */
|
||||
class Listener
|
||||
{
|
||||
net::Socket m_dataServer = {false};
|
||||
|
@ -25,9 +26,16 @@ class Listener
|
|||
void listenerProc();
|
||||
|
||||
public:
|
||||
/** @brief Start listener thread. */
|
||||
void start();
|
||||
|
||||
/** @brief Request stop of listener thread and block until joined. */
|
||||
void stop();
|
||||
|
||||
/** @brief Pop jbus::Endpoint off Listener's queue.
|
||||
* @return Endpoint instance, ready to issue commands. */
|
||||
std::unique_ptr<Endpoint> accept();
|
||||
|
||||
~Listener();
|
||||
};
|
||||
|
||||
|
|
|
@ -5,11 +5,11 @@
|
|||
namespace jbus
|
||||
{
|
||||
|
||||
void KawasedoChallenge::F23(ThreadLocalEndpoint& endpoint, EJoyReturn status)
|
||||
void Endpoint::KawasedoChallenge::_0Reset(ThreadLocalEndpoint& endpoint, EJoyReturn status)
|
||||
{
|
||||
if (status != GBA_READY ||
|
||||
(status = endpoint.GBAResetAsync(x10_statusPtr,
|
||||
bindThis(&KawasedoChallenge::F25))) != GBA_READY)
|
||||
bindThis(&KawasedoChallenge::_1GetStatus))) != GBA_READY)
|
||||
{
|
||||
x28_ticksAfterXf = 0;
|
||||
if (x14_callback)
|
||||
|
@ -20,7 +20,7 @@ void KawasedoChallenge::F23(ThreadLocalEndpoint& endpoint, EJoyReturn status)
|
|||
}
|
||||
}
|
||||
|
||||
void KawasedoChallenge::F25(ThreadLocalEndpoint& endpoint, EJoyReturn status)
|
||||
void Endpoint::KawasedoChallenge::_1GetStatus(ThreadLocalEndpoint& endpoint, EJoyReturn status)
|
||||
{
|
||||
if (status == GBA_READY)
|
||||
if (*x10_statusPtr != GBA_JSTAT_SEND)
|
||||
|
@ -28,7 +28,7 @@ void KawasedoChallenge::F25(ThreadLocalEndpoint& endpoint, EJoyReturn status)
|
|||
|
||||
if (status != GBA_READY ||
|
||||
(status = endpoint.GBAGetStatusAsync(x10_statusPtr,
|
||||
bindThis(&KawasedoChallenge::F27))) != GBA_READY)
|
||||
bindThis(&KawasedoChallenge::_2ReadChallenge))) != GBA_READY)
|
||||
{
|
||||
x28_ticksAfterXf = 0;
|
||||
if (x14_callback)
|
||||
|
@ -39,7 +39,7 @@ void KawasedoChallenge::F25(ThreadLocalEndpoint& endpoint, EJoyReturn status)
|
|||
}
|
||||
}
|
||||
|
||||
void KawasedoChallenge::F27(ThreadLocalEndpoint& endpoint, EJoyReturn status)
|
||||
void Endpoint::KawasedoChallenge::_2ReadChallenge(ThreadLocalEndpoint& endpoint, EJoyReturn status)
|
||||
{
|
||||
if (status == GBA_READY)
|
||||
if (*x10_statusPtr != (GBA_JSTAT_PSF0 | GBA_JSTAT_SEND))
|
||||
|
@ -47,7 +47,7 @@ void KawasedoChallenge::F27(ThreadLocalEndpoint& endpoint, EJoyReturn status)
|
|||
|
||||
if (status != GBA_READY ||
|
||||
(status = endpoint.GBAReadAsync(x18_readBuf, x10_statusPtr,
|
||||
bindThis(&KawasedoChallenge::F29))) != GBA_READY)
|
||||
bindThis(&KawasedoChallenge::_3DSPCrypto))) != GBA_READY)
|
||||
{
|
||||
x28_ticksAfterXf = 0;
|
||||
if (x14_callback)
|
||||
|
@ -58,7 +58,7 @@ void KawasedoChallenge::F27(ThreadLocalEndpoint& endpoint, EJoyReturn status)
|
|||
}
|
||||
}
|
||||
|
||||
void KawasedoChallenge::F29(ThreadLocalEndpoint& endpoint, EJoyReturn status)
|
||||
void Endpoint::KawasedoChallenge::_3DSPCrypto(ThreadLocalEndpoint& endpoint, EJoyReturn status)
|
||||
{
|
||||
if (status != GBA_READY)
|
||||
{
|
||||
|
@ -71,12 +71,12 @@ void KawasedoChallenge::F29(ThreadLocalEndpoint& endpoint, EJoyReturn status)
|
|||
}
|
||||
else
|
||||
{
|
||||
GBAX02();
|
||||
GBAX01(endpoint);
|
||||
_DSPCryptoInit();
|
||||
_DSPCryptoDone(endpoint);
|
||||
}
|
||||
}
|
||||
|
||||
void KawasedoChallenge::GBAX02()
|
||||
void Endpoint::KawasedoChallenge::_DSPCryptoInit()
|
||||
{
|
||||
xf8_dspHmac.x0_gbaChallenge = reinterpret_cast<u32&>(x18_readBuf);
|
||||
xf8_dspHmac.x4_logoPalette = x0_pColor;
|
||||
|
@ -85,7 +85,7 @@ void KawasedoChallenge::GBAX02()
|
|||
xf8_dspHmac.ProcessGBACrypto();
|
||||
}
|
||||
|
||||
void KawasedoChallenge::GBAX01(ThreadLocalEndpoint& endpoint)
|
||||
void Endpoint::KawasedoChallenge::_DSPCryptoDone(ThreadLocalEndpoint& endpoint)
|
||||
{
|
||||
x58_currentKey = xf8_dspHmac.x20_publicKey;
|
||||
x5c_initMessage = xf8_dspHmac.x24_authInitCode;
|
||||
|
@ -107,7 +107,7 @@ void KawasedoChallenge::GBAX01(ThreadLocalEndpoint& endpoint)
|
|||
|
||||
EJoyReturn status;
|
||||
if ((status = endpoint.GBAWriteAsync(x1c_writeBuf, x10_statusPtr,
|
||||
bindThis(&KawasedoChallenge::F31))) != GBA_READY)
|
||||
bindThis(&KawasedoChallenge::_4TransmitProgram))) != GBA_READY)
|
||||
{
|
||||
x28_ticksAfterXf = 0;
|
||||
if (x14_callback)
|
||||
|
@ -118,7 +118,7 @@ void KawasedoChallenge::GBAX01(ThreadLocalEndpoint& endpoint)
|
|||
}
|
||||
}
|
||||
|
||||
void KawasedoChallenge::F31(ThreadLocalEndpoint& endpoint, EJoyReturn status)
|
||||
void Endpoint::KawasedoChallenge::_4TransmitProgram(ThreadLocalEndpoint& endpoint, EJoyReturn status)
|
||||
{
|
||||
if (status != GBA_READY)
|
||||
{
|
||||
|
@ -240,7 +240,7 @@ void KawasedoChallenge::F31(ThreadLocalEndpoint& endpoint, EJoyReturn status)
|
|||
}
|
||||
|
||||
if ((status = endpoint.GBAWriteAsync(x1c_writeBuf, x10_statusPtr,
|
||||
bindThis(&KawasedoChallenge::F31))) != GBA_READY)
|
||||
bindThis(&KawasedoChallenge::_4TransmitProgram))) != GBA_READY)
|
||||
{
|
||||
x28_ticksAfterXf = 0;
|
||||
if (x14_callback)
|
||||
|
@ -253,7 +253,7 @@ void KawasedoChallenge::F31(ThreadLocalEndpoint& endpoint, EJoyReturn status)
|
|||
else // x34_bytesWritten > x64_totalBytes
|
||||
{
|
||||
if ((status = endpoint.GBAReadAsync(x18_readBuf, x10_statusPtr,
|
||||
bindThis(&KawasedoChallenge::F33))) != GBA_READY)
|
||||
bindThis(&KawasedoChallenge::_5StartBootPoll))) != GBA_READY)
|
||||
{
|
||||
x28_ticksAfterXf = 0;
|
||||
if (x14_callback)
|
||||
|
@ -265,11 +265,11 @@ void KawasedoChallenge::F31(ThreadLocalEndpoint& endpoint, EJoyReturn status)
|
|||
}
|
||||
}
|
||||
|
||||
void KawasedoChallenge::F33(ThreadLocalEndpoint& endpoint, EJoyReturn status)
|
||||
void Endpoint::KawasedoChallenge::_5StartBootPoll(ThreadLocalEndpoint& endpoint, EJoyReturn status)
|
||||
{
|
||||
if (status != GBA_READY ||
|
||||
(status = endpoint.GBAGetStatusAsync(x10_statusPtr,
|
||||
bindThis(&KawasedoChallenge::F35))) != GBA_READY)
|
||||
bindThis(&KawasedoChallenge::_6BootPoll))) != GBA_READY)
|
||||
{
|
||||
x28_ticksAfterXf = 0;
|
||||
if (x14_callback)
|
||||
|
@ -280,7 +280,7 @@ void KawasedoChallenge::F33(ThreadLocalEndpoint& endpoint, EJoyReturn status)
|
|||
}
|
||||
}
|
||||
|
||||
void KawasedoChallenge::F35(ThreadLocalEndpoint& endpoint, EJoyReturn status)
|
||||
void Endpoint::KawasedoChallenge::_6BootPoll(ThreadLocalEndpoint& endpoint, EJoyReturn status)
|
||||
{
|
||||
if (status == GBA_READY)
|
||||
if (*x10_statusPtr & (GBA_JSTAT_FLAGS_MASK | GBA_JSTAT_RECV))
|
||||
|
@ -300,7 +300,7 @@ void KawasedoChallenge::F35(ThreadLocalEndpoint& endpoint, EJoyReturn status)
|
|||
if (*x10_statusPtr != GBA_JSTAT_SEND)
|
||||
{
|
||||
if ((status = endpoint.GBAGetStatusAsync(x10_statusPtr,
|
||||
bindThis(&KawasedoChallenge::F35))) != GBA_READY)
|
||||
bindThis(&KawasedoChallenge::_6BootPoll))) != GBA_READY)
|
||||
{
|
||||
x28_ticksAfterXf = 0;
|
||||
if (x14_callback)
|
||||
|
@ -313,7 +313,7 @@ void KawasedoChallenge::F35(ThreadLocalEndpoint& endpoint, EJoyReturn status)
|
|||
}
|
||||
|
||||
if ((status = endpoint.GBAReadAsync(x18_readBuf, x10_statusPtr,
|
||||
bindThis(&KawasedoChallenge::F37))) != GBA_READY)
|
||||
bindThis(&KawasedoChallenge::_7BootAcknowledge))) != GBA_READY)
|
||||
{
|
||||
x28_ticksAfterXf = 0;
|
||||
if (x14_callback)
|
||||
|
@ -324,11 +324,11 @@ void KawasedoChallenge::F35(ThreadLocalEndpoint& endpoint, EJoyReturn status)
|
|||
}
|
||||
}
|
||||
|
||||
void KawasedoChallenge::F37(ThreadLocalEndpoint& endpoint, EJoyReturn status)
|
||||
void Endpoint::KawasedoChallenge::_7BootAcknowledge(ThreadLocalEndpoint& endpoint, EJoyReturn status)
|
||||
{
|
||||
if (status != GBA_READY ||
|
||||
(status = endpoint.GBAWriteAsync(x18_readBuf, x10_statusPtr,
|
||||
bindThis(&KawasedoChallenge::F39))) != GBA_READY)
|
||||
bindThis(&KawasedoChallenge::_8BootDone))) != GBA_READY)
|
||||
{
|
||||
x28_ticksAfterXf = 0;
|
||||
if (x14_callback)
|
||||
|
@ -339,7 +339,7 @@ void KawasedoChallenge::F37(ThreadLocalEndpoint& endpoint, EJoyReturn status)
|
|||
}
|
||||
}
|
||||
|
||||
void KawasedoChallenge::F39(ThreadLocalEndpoint& endpoint, EJoyReturn status)
|
||||
void Endpoint::KawasedoChallenge::_8BootDone(ThreadLocalEndpoint& endpoint, EJoyReturn status)
|
||||
{
|
||||
if (status == GBA_READY)
|
||||
*x10_statusPtr = 0;
|
||||
|
@ -353,50 +353,19 @@ void KawasedoChallenge::F39(ThreadLocalEndpoint& endpoint, EJoyReturn status)
|
|||
}
|
||||
}
|
||||
|
||||
KawasedoChallenge::KawasedoChallenge(Endpoint& endpoint, s32 paletteColor, s32 paletteSpeed,
|
||||
u8* programp, s32 length, u8* status, FGBACallback&& callback)
|
||||
Endpoint::KawasedoChallenge::KawasedoChallenge(Endpoint& endpoint, s32 paletteColor, s32 paletteSpeed,
|
||||
const u8* programp, s32 length, u8* status, FGBACallback&& callback)
|
||||
: x0_pColor(paletteColor), x4_pSpeed(paletteSpeed), x8_progPtr(programp), xc_progLen(length),
|
||||
x10_statusPtr(status), x14_callback(std::move(callback)), x34_bytesSent(0)
|
||||
{
|
||||
if (endpoint.GBAGetStatusAsync(x10_statusPtr,
|
||||
bindThis(&KawasedoChallenge::F23)) != GBA_READY)
|
||||
bindThis(&KawasedoChallenge::_0Reset)) != GBA_READY)
|
||||
{
|
||||
x14_callback = {};
|
||||
m_started = false;
|
||||
}
|
||||
}
|
||||
|
||||
u64 Endpoint::getTransferTime(u8 cmd)
|
||||
{
|
||||
u64 bytes = 0;
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case CMD_RESET:
|
||||
case CMD_STATUS:
|
||||
{
|
||||
bytes = 4;
|
||||
break;
|
||||
}
|
||||
case CMD_READ:
|
||||
{
|
||||
bytes = 6;
|
||||
break;
|
||||
}
|
||||
case CMD_WRITE:
|
||||
{
|
||||
bytes = 1;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
bytes = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return bytes * GetGCTicksPerSec() / BYTES_PER_SECOND;
|
||||
}
|
||||
|
||||
void Endpoint::clockSync()
|
||||
{
|
||||
if (!m_clockSocket)
|
||||
|
@ -598,12 +567,12 @@ void Endpoint::stop()
|
|||
m_transferThread.join();
|
||||
}
|
||||
|
||||
EJoyReturn Endpoint::GBAGetProcessStatus(u8* percentp)
|
||||
EJoyReturn Endpoint::GBAGetProcessStatus(u8& percentOut)
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(m_syncLock);
|
||||
if (m_joyBoot)
|
||||
{
|
||||
*percentp = m_joyBoot->percentComplete();
|
||||
percentOut = m_joyBoot->percentComplete();
|
||||
if (!m_joyBoot->isDone())
|
||||
return GBA_BUSY;
|
||||
}
|
||||
|
@ -753,7 +722,7 @@ EJoyReturn Endpoint::GBAWrite(const u8* src, u8* status)
|
|||
}
|
||||
|
||||
EJoyReturn Endpoint::GBAJoyBootAsync(s32 paletteColor, s32 paletteSpeed,
|
||||
u8* programp, s32 length, u8* status,
|
||||
const u8* programp, s32 length, u8* status,
|
||||
FGBACallback&& callback)
|
||||
{
|
||||
if (m_chan > 3)
|
||||
|
|
|
@ -95,7 +95,7 @@ int main(int argc, char** argv)
|
|||
jbus::s64 start = jbus::GetGCTicks();
|
||||
jbus::u8 percent = 0;
|
||||
jbus::u8 lastpercent = 0;
|
||||
while (endpoint->GBAGetProcessStatus(&percent) == jbus::GBA_BUSY)
|
||||
while (endpoint->GBAGetProcessStatus(percent) == jbus::GBA_BUSY)
|
||||
{
|
||||
if (percent != lastpercent)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue