diff --git a/README.md b/README.md index 553c87d..c88afa7 100644 --- a/README.md +++ b/README.md @@ -43,10 +43,12 @@ Name | Type | Default | Description `baudRate` | **BaudRate** | `BAUD_RATE_1` | Sets a specific baud rate. `timeout` | **u32** | `3` | Number of *frames* without an `II_SERIAL` IRQ to reset the connection. `remoteTimeout` | **u32** | `5` | Number of *messages* with `0xFFFF` to mark a player as disconnected. -`bufferSize` | **u32** | `30` | Number of *messages* that the queues will be able to store. `interval` | **u16** | `50` | Number of *1024cycles* (61.04μs) ticks between messages *(50 = 3.052ms)*. It's the interval of Timer #`sendTimerId`. `sendTimerId` | **u8** *(0~3)* | `3` | GBA Timer to use for sending. +You can also change these compile-time constants: +- `LINK_CABLE_QUEUE_SIZE`: to set a custom buffer size (how many incoming and outcoming messages the queues can store at max). The default value is `30`, which seems fine for most games. + ## Methods Name | Return type | Description diff --git a/lib/LinkCable.h b/lib/LinkCable.h index 2b517ea..044671a 100644 --- a/lib/LinkCable.h +++ b/lib/LinkCable.h @@ -37,14 +37,15 @@ // -------------------------------------------------------------------------- #include -#include + +// Buffer size +#define LINK_CABLE_QUEUE_SIZE 30 #define LINK_CABLE_MAX_PLAYERS 4 #define LINK_CABLE_DISCONNECTED 0xFFFF #define LINK_CABLE_NO_DATA 0x0 #define LINK_CABLE_DEFAULT_TIMEOUT 3 #define LINK_CABLE_DEFAULT_REMOTE_TIMEOUT 5 -#define LINK_CABLE_DEFAULT_BUFFER_SIZE 30 #define LINK_CABLE_DEFAULT_INTERVAL 50 #define LINK_CABLE_DEFAULT_SEND_TIMER_ID 3 #define LINK_CABLE_BASE_FREQUENCY TM_FREQ_1024 @@ -67,8 +68,6 @@ static volatile char LINK_CABLE_VERSION[] = "LinkCable/v5.0.1"; void LINK_CABLE_ISR_VBLANK(); void LINK_CABLE_ISR_SERIAL(); void LINK_CABLE_ISR_TIMER(); -u16 LINK_CABLE_QUEUE_POP(std::queue& q); -void LINK_CABLE_QUEUE_CLEAR(std::queue& q); const u16 LINK_CABLE_TIMER_IRQ_IDS[] = {IRQ_TIMER0, IRQ_TIMER1, IRQ_TIMER2, IRQ_TIMER3}; @@ -81,16 +80,52 @@ class LinkCable { BAUD_RATE_3 // 115200 bps }; + class U16Queue { + public: + void push(u16 item) { + if (isFull()) + pop(); + + rear = (rear + 1) % LINK_CABLE_QUEUE_SIZE; + arr[rear] = item; + count++; + } + + u16 pop() { + if (isEmpty()) + return LINK_CABLE_NO_DATA; + + auto x = arr[front]; + front = (front + 1) % LINK_CABLE_QUEUE_SIZE; + count--; + + return x; + } + + void clear() { + while (!isEmpty()) + pop(); + } + + int size() { return count; } + bool isEmpty() { return size() == 0; } + bool isFull() { return size() == LINK_CABLE_QUEUE_SIZE; } + + private: + u16 arr[LINK_CABLE_QUEUE_SIZE]; + vs32 front = 0; + vs32 rear = -1; + vu32 count = 0; + }; + explicit LinkCable(BaudRate baudRate = BAUD_RATE_1, u32 timeout = LINK_CABLE_DEFAULT_TIMEOUT, u32 remoteTimeout = LINK_CABLE_DEFAULT_REMOTE_TIMEOUT, - u32 bufferSize = LINK_CABLE_DEFAULT_BUFFER_SIZE, u16 interval = LINK_CABLE_DEFAULT_INTERVAL, u8 sendTimerId = LINK_CABLE_DEFAULT_SEND_TIMER_ID) { this->config.baudRate = baudRate; this->config.timeout = timeout; this->config.remoteTimeout = remoteTimeout; - this->config.bufferSize = bufferSize; this->config.interval = interval; this->config.sendTimerId = sendTimerId; } @@ -125,7 +160,7 @@ class LinkCable { LINK_CABLE_BARRIER; - return !$state.incomingMessages[playerId].empty(); + return !$state.incomingMessages[playerId].isEmpty(); } u16 read(u8 playerId) { @@ -134,7 +169,7 @@ class LinkCable { LINK_CABLE_BARRIER; - return LINK_CABLE_QUEUE_POP($state.incomingMessages[playerId]); + return $state.incomingMessages[playerId].pop(); } void consume() { isStateConsumed = true; } @@ -147,14 +182,14 @@ class LinkCable { isAddingMessage = true; LINK_CABLE_BARRIER; - push(_state.outgoingMessages, data); + _state.outgoingMessages.push(data); LINK_CABLE_BARRIER; isAddingMessage = false; LINK_CABLE_BARRIER; if (isResetting) { - LINK_CABLE_QUEUE_CLEAR(_state.outgoingMessages); + _state.outgoingMessages.clear(); isResetting = false; } } @@ -189,14 +224,14 @@ class LinkCable { if (data != LINK_CABLE_DISCONNECTED) { if (data != LINK_CABLE_NO_DATA && i != state.currentPlayerId) - push(state.incomingMessages[i], data); + state.incomingMessages[i].push(data); newPlayerCount++; _state.timeouts[i] = 0; } else if (_state.timeouts[i] > LINK_CABLE_REMOTE_TIMEOUT_OFFLINE) { _state.timeouts[i]++; if (_state.timeouts[i] >= (int)config.remoteTimeout) { - LINK_CABLE_QUEUE_CLEAR(state.incomingMessages[i]); + state.incomingMessages[i].clear(); _state.timeouts[i] = LINK_CABLE_REMOTE_TIMEOUT_OFFLINE; } else newPlayerCount++; @@ -235,19 +270,18 @@ class LinkCable { BaudRate baudRate; u32 timeout; u32 remoteTimeout; - u32 bufferSize; u32 interval; u8 sendTimerId; }; struct ExternalState { - std::queue incomingMessages[LINK_CABLE_MAX_PLAYERS]; + U16Queue incomingMessages[LINK_CABLE_MAX_PLAYERS]; u8 playerCount; u8 currentPlayerId; }; struct InternalState { - std::queue outgoingMessages; + U16Queue outgoingMessages; int timeouts[LINK_CABLE_MAX_PLAYERS]; bool IRQFlag; u32 IRQTimeout; @@ -275,7 +309,7 @@ class LinkCable { LINK_CABLE_BARRIER; - transfer(LINK_CABLE_QUEUE_POP(_state.outgoingMessages)); + transfer(_state.outgoingMessages.pop()); } void transfer(u16 data) { @@ -304,7 +338,7 @@ class LinkCable { state.playerCount = 0; state.currentPlayerId = 0; for (u32 i = 0; i < LINK_CABLE_MAX_PLAYERS; i++) { - LINK_CABLE_QUEUE_CLEAR(state.incomingMessages[i]); + state.incomingMessages[i].clear(); _state.timeouts[i] = LINK_CABLE_REMOTE_TIMEOUT_OFFLINE; } _state.IRQFlag = false; @@ -313,7 +347,7 @@ class LinkCable { if (isAddingMessage || isResetting) isResetting = true; else - LINK_CABLE_QUEUE_CLEAR(_state.outgoingMessages); + _state.outgoingMessages.clear(); } void stop() { @@ -352,8 +386,9 @@ class LinkCable { $state.playerCount = state.playerCount; $state.currentPlayerId = state.currentPlayerId; for (u32 i = 0; i < LINK_CABLE_MAX_PLAYERS; i++) { - $state.incomingMessages[i].swap(state.incomingMessages[i]); - LINK_CABLE_QUEUE_CLEAR(state.incomingMessages[i]); + $state.incomingMessages[i].clear(); + while (!state.incomingMessages[i].isEmpty()) + $state.incomingMessages[i].push(state.incomingMessages[i].pop()); } LINK_CABLE_BARRIER; isStateReady = true; @@ -361,13 +396,6 @@ class LinkCable { LINK_CABLE_BARRIER; } - void push(std::queue& q, u16 value) { - if (q.size() >= config.bufferSize) - LINK_CABLE_QUEUE_POP(q); - - q.push(value); - } - bool isBitHigh(u8 bit) { return (REG_SIOCNT >> bit) & 1; } void setBitHigh(u8 bit) { LINK_CABLE_SET_HIGH(REG_SIOCNT, bit); } void setBitLow(u8 bit) { LINK_CABLE_SET_LOW(REG_SIOCNT, bit); } @@ -387,18 +415,4 @@ inline void LINK_CABLE_ISR_TIMER() { linkCable->_onTimer(); } -inline u16 LINK_CABLE_QUEUE_POP(std::queue& q) { - if (q.empty()) - return LINK_CABLE_NO_DATA; - - u16 value = q.front(); - q.pop(); - return value; -} - -inline void LINK_CABLE_QUEUE_CLEAR(std::queue& q) { - while (!q.empty()) - LINK_CABLE_QUEUE_POP(q); -} - #endif // LINK_CABLE_H diff --git a/lib/LinkUniversal.h b/lib/LinkUniversal.h index 348d4af..296e123 100644 --- a/lib/LinkUniversal.h +++ b/lib/LinkUniversal.h @@ -37,14 +37,12 @@ // -------------------------------------------------------------------------- #include -#include #include "LinkCable.h" #include "LinkWireless.h" #define LINK_UNIVERSAL_MAX_PLAYERS LINK_CABLE_MAX_PLAYERS #define LINK_UNIVERSAL_DISCONNECTED LINK_CABLE_DISCONNECTED #define LINK_UNIVERSAL_NO_DATA LINK_CABLE_NO_DATA -#define LINK_UNIVERSAL_BUFFER_SIZE LINK_WIRELESS_QUEUE_SIZE #define LINK_UNIVERSAL_MAX_ROOM_NUMBER 32000 #define LINK_UNIVERSAL_INIT_WAIT_FRAMES 10 #define LINK_UNIVERSAL_SWITCH_WAIT_FRAMES 25 @@ -94,10 +92,9 @@ class LinkUniversal { true, LINK_WIRELESS_MAX_PLAYERS, LINK_WIRELESS_DEFAULT_TIMEOUT, LINK_WIRELESS_DEFAULT_REMOTE_TIMEOUT, LINK_WIRELESS_DEFAULT_INTERVAL, LINK_WIRELESS_DEFAULT_SEND_TIMER_ID}) { - this->linkCable = - new LinkCable(cableOptions.baudRate, cableOptions.timeout, - cableOptions.remoteTimeout, LINK_UNIVERSAL_BUFFER_SIZE, - cableOptions.interval, cableOptions.sendTimerId); + this->linkCable = new LinkCable( + cableOptions.baudRate, cableOptions.timeout, cableOptions.remoteTimeout, + cableOptions.interval, cableOptions.sendTimerId); this->linkWireless = new LinkWireless( wirelessOptions.retransmission, true, wirelessOptions.maxPlayers, wirelessOptions.timeout, wirelessOptions.remoteTimeout, @@ -203,11 +200,9 @@ class LinkUniversal { linkCable->consume(); } - bool canRead(u8 playerId) { return !incomingMessages[playerId].empty(); } + bool canRead(u8 playerId) { return !incomingMessages[playerId].isEmpty(); } - u16 read(u8 playerId) { - return LINK_CABLE_QUEUE_POP(incomingMessages[playerId]); - } + u16 read(u8 playerId) { return incomingMessages[playerId].pop(); } void send(u16 data) { if (data == LINK_CABLE_DISCONNECTED || data == LINK_CABLE_NO_DATA) @@ -258,7 +253,7 @@ class LinkUniversal { std::string gameName; }; - std::queue incomingMessages[LINK_UNIVERSAL_MAX_PLAYERS]; + LinkCable::U16Queue incomingMessages[LINK_UNIVERSAL_MAX_PLAYERS]; LinkCable* linkCable; LinkWireless* linkWireless; Config config; @@ -273,7 +268,7 @@ class LinkUniversal { void receiveCableMessages() { for (u32 i = 0; i < LINK_UNIVERSAL_MAX_PLAYERS; i++) { while (linkCable->canRead(i)) - push(incomingMessages[i], linkCable->read(i)); + incomingMessages[i].push(linkCable->read(i)); } } @@ -286,7 +281,7 @@ class LinkUniversal { if (message.packetId == LINK_WIRELESS_END) break; - push(incomingMessages[message.playerId], message.data); + incomingMessages[message.playerId].push(message.data); } } @@ -438,14 +433,7 @@ class LinkUniversal { subWaitCount = 0; serveWait = 0; for (u32 i = 0; i < LINK_UNIVERSAL_MAX_PLAYERS; i++) - LINK_CABLE_QUEUE_CLEAR(incomingMessages[i]); - } - - void push(std::queue& q, u16 value) { - if (q.size() >= LINK_UNIVERSAL_BUFFER_SIZE) - LINK_CABLE_QUEUE_POP(q); - - q.push(value); + incomingMessages[i].clear(); } };