Rewriting LinkWireless to support async transfers

This commit is contained in:
Rodrigo Alfonso 2023-02-08 18:58:44 -03:00
parent 5c6dffe7bc
commit af5a892e40
7 changed files with 619 additions and 367 deletions

View File

@ -17,7 +17,7 @@ void init() {
REG_DISPCNT = DCNT_MODE0 | DCNT_BG0;
tte_init_se_default(0, BG_CBB(0) | BG_SBB(31));
// (2) Add the interrupt service routines
// (2) Add the required interrupt service routines
interrupt_init();
interrupt_set_handler(INTR_VBLANK, LINK_CABLE_ISR_VBLANK);
interrupt_enable(INTR_VBLANK);

View File

@ -32,7 +32,7 @@ int main() {
// (1) Create a LinkUniversal instance
linkUniversal = new LinkUniversal(protocol);
// (2) Add the interrupt service routines
// (2) Add the required interrupt service routines
interrupt_init();
interrupt_set_handler(INTR_VBLANK, LINK_UNIVERSAL_ISR_VBLANK);
interrupt_enable(INTR_VBLANK);

View File

@ -1,14 +1,14 @@
#include <tonc.h>
#include <functional>
#include <string>
#include "../../_lib/interrupt.h"
// (0) Include the header
#include "../../../lib/LinkWireless.h"
#define TRANSFERS_PER_FRAME 4
#define CHECK_ERRORS(MESSAGE) \
if ((lastError = linkWireless->getLastError())) { \
if ((lastError = linkWireless->getLastError()) || \
linkWireless->getState() == LinkWireless::State::NEEDS_RESET) { \
log(std::string(MESSAGE) + " (" + std::to_string(lastError) + ") [" + \
std::to_string(linkWireless->getState()) + "]"); \
hang(); \
@ -26,32 +26,41 @@ void hang();
LinkWireless::Error lastError;
LinkWireless* linkWireless = NULL;
bool forwarding = true;
bool retransmission = true;
void init() {
REG_DISPCNT = DCNT_MODE0 | DCNT_BG0;
tte_init_se_default(0, BG_CBB(0) | BG_SBB(31));
irq_init(NULL);
irq_add(II_VBLANK, NULL);
}
int main() {
init();
bool firstTime = true;
start:
// Options
log("Press A to start\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nhold LEFT on start:\n -> "
"disable forwarding\n\nhold UP on start:\n -> disable retransmission");
waitFor(KEY_A);
u16 initialKeys = ~REG_KEYS & KEY_ANY;
forwarding = !(initialKeys & KEY_LEFT);
retransmission = !(initialKeys & KEY_UP);
bool forwarding = !(initialKeys & KEY_LEFT);
bool retransmission = !(initialKeys & KEY_UP);
// (1) Create a LinkWireless instance
linkWireless = new LinkWireless(forwarding, retransmission);
if (firstTime) {
// (2) Add the required interrupt service routines
interrupt_init();
interrupt_set_handler(INTR_VBLANK, LINK_WIRELESS_ISR_VBLANK);
interrupt_enable(INTR_VBLANK);
interrupt_set_handler(INTR_SERIAL, LINK_WIRELESS_ISR_SERIAL);
interrupt_enable(INTR_SERIAL);
interrupt_set_handler(INTR_TIMER3, LINK_WIRELESS_ISR_TIMER);
interrupt_enable(INTR_TIMER3);
firstTime = false;
}
// (2) Initialize the library
linkWireless->activate();
@ -131,14 +140,13 @@ void serve() {
u16 keys = ~REG_KEYS & KEY_ANY;
if (keys & KEY_SELECT) {
log("Canceled!");
linkWireless->disconnect();
linkWireless->activate();
hang();
return;
}
linkWireless->acceptConnections();
CHECK_ERRORS("Accept failed :(")
} while (linkWireless->getPlayerCount() <= 1);
} while (linkWireless->getState() == LinkWireless::State::SERVING &&
!linkWireless->isConnected());
CHECK_ERRORS("Accept failed :(")
log("Connection accepted!");
@ -181,7 +189,7 @@ void connect() {
waitFor(KEY_START | KEY_SELECT);
if ((~REG_KEYS & KEY_ANY) & KEY_SELECT) {
linkWireless->disconnect();
linkWireless->activate();
return;
}
@ -192,7 +200,7 @@ void connect() {
u16 keys = ~REG_KEYS & KEY_ANY;
if (keys & KEY_SELECT) {
log("Canceled!");
linkWireless->disconnect();
linkWireless->activate();
hang();
return;
}
@ -201,7 +209,7 @@ void connect() {
CHECK_ERRORS("Finish failed :(")
}
log("Connected! " + std::to_string(linkWireless->getPlayerId()));
log("Connected! " + std::to_string(linkWireless->currentPlayerId()));
messageLoop();
}
@ -223,6 +231,7 @@ void messageLoop() {
u32 lastLostPacketReceived = 0;
while (true) {
CHECK_ERRORS("Error :(")
u16 keys = ~REG_KEYS & KEY_ANY;
// (5) Send data
@ -232,9 +241,9 @@ void messageLoop() {
sending = true;
again:
counters[linkWireless->getPlayerId()]++;
counters[linkWireless->currentPlayerId()]++;
linkWireless->send(
std::vector<u32>{counters[linkWireless->getPlayerId()]});
std::vector<u32>{counters[linkWireless->currentPlayerId()]});
CHECK_ERRORS("Send failed :(")
if (!doubleSend && (keys & KEY_LEFT) && linkWireless->canSend()) {
@ -246,18 +255,7 @@ void messageLoop() {
sending = false;
// (6) Receive data
std::vector<LinkWireless::Message> messages;
if (retransmission) {
// (exchanging data 4 times, just for speed purposes)
linkWireless->receiveMany(messages, TRANSFERS_PER_FRAME, []() {
u16 keys = ~REG_KEYS & KEY_ANY;
return keys & KEY_SELECT;
});
} else {
// (exchanging data one time)
linkWireless->receive(messages);
}
CHECK_ERRORS("Receive failed :(")
std::vector<LinkWireless::Message> messages = linkWireless->receive();
if (messages.size() > 0) {
for (auto& message : messages) {
u32 expected = counters[message.playerId] + 1;
@ -274,19 +272,9 @@ void messageLoop() {
}
}
// Accept new connections
if (linkWireless->getState() == LinkWireless::State::SERVING) {
linkWireless->acceptConnections();
CHECK_ERRORS("Accept failed :(")
}
// (7) Disconnect
if ((keys & KEY_SELECT)) {
if (!linkWireless->disconnect()) {
log("Disconn failed :(");
hang();
return;
}
linkWireless->activate();
return;
}
@ -305,12 +293,12 @@ void messageLoop() {
switching = false;
std::string output =
"Player #" + std::to_string(linkWireless->getPlayerId()) + " (" +
std::to_string(linkWireless->getPlayerCount()) + " total)" +
"Player #" + std::to_string(linkWireless->currentPlayerId()) + " (" +
std::to_string(linkWireless->playerCount()) + " total)" +
"\n\n(press A to increment counter)\n(hold B to do it "
"continuously)\n(hold LEFT for double send)\n\nPacket loss check: " +
(packetLossCheck ? "ON" : "OFF") + "\n(switch with UP)\n\n";
for (u32 i = 0; i < linkWireless->getPlayerCount(); i++) {
for (u32 i = 0; i < linkWireless->playerCount(); i++) {
output +=
"p" + std::to_string(i) + ": " + std::to_string(counters[i]) + "\n";
}

View File

@ -31,7 +31,7 @@
// That can cause packet loss. You might want to use libugba's instead.
// (see examples)
// --------------------------------------------------------------------------
// `data` restrictions:
// `send(...)` restrictions:
// - 0xFFFF and 0x0 are reserved values, so don't use them!
// (they mean 'disconnected' and 'no data' respectively)
// --------------------------------------------------------------------------
@ -65,8 +65,8 @@
static volatile char LINK_CABLE_VERSION[] = "LinkCable/v4.3.0";
void LINK_CABLE_ISR_VBLANK();
void LINK_CABLE_ISR_TIMER();
void LINK_CABLE_ISR_SERIAL();
void LINK_CABLE_ISR_TIMER();
u16 LINK_CABLE_QUEUE_POP(std::queue<u16>& q);
void LINK_CABLE_QUEUE_CLEAR(std::queue<u16>& q);
const u16 LINK_CABLE_TIMER_IRQ_IDS[] = {IRQ_TIMER0, IRQ_TIMER1, IRQ_TIMER2,
@ -240,7 +240,7 @@ class LinkCable {
u8 sendTimerId;
};
struct PublicState {
struct ExternalState {
std::queue<u16> incomingMessages[LINK_CABLE_MAX_PLAYERS];
u8 playerCount;
u8 currentPlayerId;
@ -253,8 +253,8 @@ class LinkCable {
u32 IRQTimeout;
};
PublicState state; // (updated state / back buffer)
PublicState $state; // (visible state / front buffer)
ExternalState state; // (updated state / back buffer)
ExternalState $state; // (visible state / front buffer)
InternalState _state; // (internal state)
Config config;
bool isEnabled = false;

View File

@ -7,7 +7,7 @@
// Usage:
// - 1) Include this header in your main.cpp file and add:
// LinkSPI* linkSPI = new LinkSPI();
// - 2) (Optional) Add the required interrupt service routines:
// - 2) (Optional) Add the interrupt service routines:
// irq_init(NULL);
// irq_add(II_SERIAL, LINK_SPI_ISR_SERIAL);
// // (this is only required for `transferAsync`)
@ -171,11 +171,13 @@ class LinkSPI {
bool isWaitModeActive() { return waitMode; }
AsyncState getAsyncState() { return asyncState; }
void _onSerial() {
void _onSerial(bool _customAck = false) {
if (!isEnabled || asyncState != WAITING)
return;
disableTransfer();
if (!_customAck)
disableTransfer();
setInterruptsOff();
asyncState = READY;
asyncData = getData();

View File

@ -31,7 +31,7 @@
// That can cause packet loss. You might want to use libugba's instead.
// (see examples)
// --------------------------------------------------------------------------
// `data` restrictions:
// `send(...)` restrictions:
// - 0xFFFF and 0x0 are reserved values, so don't use them!
// (they mean 'disconnected' and 'no data' respectively)
// --------------------------------------------------------------------------

File diff suppressed because it is too large Load Diff