diff --git a/README.md b/README.md index 317c6d5..a79cd84 100644 --- a/README.md +++ b/README.md @@ -265,6 +265,7 @@ Name | Type | Default | Description `gameName` | **const char\*** | `""` | The game name that will be broadcasted in wireless sessions (max `14` characters). The string must be a null-terminated character array. The library uses this to only connect to servers from the same game. `cableOptions` | **LinkUniversal::CableOptions** | *same as LinkCable* | All the [👾 LinkCable](#-LinkCable) constructor parameters in one *struct*. `wirelessOptions` | **LinkUniversal::WirelessOptions** | *same as LinkWireless* | All the [📻 LinkWireless](#-LinkWireless) constructor parameters in one *struct*. +`randomSeed` | **int** | `123` | Random seed used for waits to prevent livelocks. If you use _libtonc_, pass `__qran_seed`. You can also change these compile-time constants: - `LINK_UNIVERSAL_MAX_PLAYERS`: to set a maximum number of players. The default value is `5`, but since LinkCable's limit is `4`, you might want to decrease it. diff --git a/examples/LinkCableMultiboot_demo/src/main.cpp b/examples/LinkCableMultiboot_demo/src/main.cpp index 80cb73e..86cfc2c 100644 --- a/examples/LinkCableMultiboot_demo/src/main.cpp +++ b/examples/LinkCableMultiboot_demo/src/main.cpp @@ -49,8 +49,9 @@ int main() { // Sender options if (isSenderMode) { if (result != LinkCableMultiboot::Result::SUCCESS) - log("LinkCableMultiboot_demo\n (v7.0.0)\n\nPress START to send the " - "ROM...\nPress B to set client mode..."); + log("LinkCableMultiboot_demo\n (v7.0.0)\n\n" + "Press START to send the ROM...\n" + "Press B to set client mode..."); if (keys & KEY_START) { log("Sending... (SELECT to cancel)"); diff --git a/examples/LinkCable_stress/src/main.cpp b/examples/LinkCable_stress/src/main.cpp index 861cc21..96fed59 100644 --- a/examples/LinkCable_stress/src/main.cpp +++ b/examples/LinkCable_stress/src/main.cpp @@ -52,7 +52,8 @@ LinkUniversal* linkUniversal = .remoteTimeout = LINK_WIRELESS_DEFAULT_REMOTE_TIMEOUT, .interval = LINK_WIRELESS_DEFAULT_INTERVAL, .sendTimerId = LINK_WIRELESS_DEFAULT_SEND_TIMER_ID, - .asyncACKTimerId = 0}); + .asyncACKTimerId = 0}, + __qran_seed); LinkUniversal* linkConnection = linkUniversal; #endif diff --git a/examples/LinkUniversal_basic/src/main.cpp b/examples/LinkUniversal_basic/src/main.cpp index e939c85..37c9740 100644 --- a/examples/LinkUniversal_basic/src/main.cpp +++ b/examples/LinkUniversal_basic/src/main.cpp @@ -1,13 +1,13 @@ -#include -#include -#include "../../_lib/interrupt.h" - // BASIC: // This example sends the pressed buttons to other players. // (0) Include the header #include "../../../lib/LinkUniversal.hpp" +#include +#include +#include "../../_lib/interrupt.h" + void log(std::string text); void waitFor(u16 key); @@ -21,11 +21,13 @@ void init() { int main() { init(); - log("LinkUniversal_basic (v7.0.0)\n\n\nPress A to start\n\n\nhold LEFT on " - "start:\n -> force cable\n\nhold RIGHT on start:\n -> force " - "wireless\n\nhold UP on start:\n -> force wireless server\n\nhold DOWN " - "on start:\n -> force wireless client\n\nhold B on start:\n -> set 2 " - "players (wireless)"); + log("LinkUniversal_basic (v7.0.0)\n\n\n" + "Press A to start\n\n\n" + "hold LEFT on start:\n -> force cable\n\n" + "hold RIGHT on start:\n -> force wireless\n\n" + "hold UP on start:\n -> force wireless server\n\n" + "hold DOWN on start:\n -> force wireless client\n\n" + "hold B on start:\n -> set 2 players (wireless)"); waitFor(KEY_A); u16 initialKeys = ~REG_KEYS & KEY_ANY; bool forceCable = initialKeys & KEY_LEFT; @@ -56,7 +58,8 @@ int main() { .remoteTimeout = LINK_WIRELESS_DEFAULT_REMOTE_TIMEOUT, .interval = LINK_WIRELESS_DEFAULT_INTERVAL, .sendTimerId = LINK_WIRELESS_DEFAULT_SEND_TIMER_ID, - .asyncACKTimerId = LINK_WIRELESS_DEFAULT_ASYNC_ACK_TIMER_ID}); + .asyncACKTimerId = LINK_WIRELESS_DEFAULT_ASYNC_ACK_TIMER_ID}, + __qran_seed); // (2) Add the required interrupt service routines interrupt_init(); diff --git a/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp b/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp index 53236e7..300f374 100644 --- a/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp +++ b/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp @@ -150,7 +150,7 @@ void MultibootScene::load() { log("B: toggle players"); log("UP/DOWN: scroll up/down"); log("L/R: scroll page up/down"); - log("UP+L/DOWN+R: scroll to top/bottom"); + log("UP+L/DOWN+R: scroll top/bottom"); log("SELECT: clear"); log("---"); log(""); diff --git a/examples/LinkWireless_demo/src/main.cpp b/examples/LinkWireless_demo/src/main.cpp index 3971e44..573a386 100644 --- a/examples/LinkWireless_demo/src/main.cpp +++ b/examples/LinkWireless_demo/src/main.cpp @@ -47,10 +47,12 @@ int main() { start: // Options - log("LinkWireless_demo (v7.0.0)\n\n\n\nPress A to start\n\n\n\n\nhold LEFT " - "on start:\n -> disable forwarding\n\nhold UP on start:\n -> disable " - "retransmission\n\nhold B on start:\n -> set 2 players\n\nhold START on " - "start:\n -> async ACK"); + log("LinkWireless_demo (v7.0.0)\n\n\n\n" + "Press A to start\n\n\n\n\n" + "hold LEFT on start:\n -> disable forwarding\n\n" + "hold UP on start:\n -> disable retransmission\n\n" + "hold B on start:\n -> set 2 players\n\n" + "hold START on start:\n -> async ACK"); waitFor(KEY_A); u16 initialKeys = ~REG_KEYS & KEY_ANY; forwarding = !(initialKeys & KEY_LEFT); diff --git a/lib/LinkUniversal.hpp b/lib/LinkUniversal.hpp index 80ea30f..87f5495 100644 --- a/lib/LinkUniversal.hpp +++ b/lib/LinkUniversal.hpp @@ -45,9 +45,8 @@ // (they mean 'disconnected' and 'no data' respectively) // -------------------------------------------------------------------------- -#include -#include -#include +#include "_link_common.hpp" + #include #include "LinkCable.hpp" #include "LinkWireless.hpp" @@ -58,23 +57,26 @@ // Game ID Filter. Default = 0 (no filter) #define LINK_UNIVERSAL_GAME_ID_FILTER 0 -#define LINK_UNIVERSAL_DISCONNECTED LINK_CABLE_DISCONNECTED -#define LINK_UNIVERSAL_NO_DATA LINK_CABLE_NO_DATA -#define LINK_UNIVERSAL_MAX_ROOM_NUMBER 32000 -#define LINK_UNIVERSAL_INIT_WAIT_FRAMES 10 -#define LINK_UNIVERSAL_SWITCH_WAIT_FRAMES 25 -#define LINK_UNIVERSAL_SWITCH_WAIT_FRAMES_RANDOM 10 -#define LINK_UNIVERSAL_BROADCAST_SEARCH_WAIT_FRAMES 10 -#define LINK_UNIVERSAL_SERVE_WAIT_FRAMES 60 -#define LINK_UNIVERSAL_SERVE_WAIT_FRAMES_RANDOM 30 - static volatile char LINK_UNIVERSAL_VERSION[] = "LinkUniversal/v7.0.0"; -void LINK_UNIVERSAL_ISR_VBLANK(); -void LINK_UNIVERSAL_ISR_SERIAL(); -void LINK_UNIVERSAL_ISR_TIMER(); +#define LINK_UNIVERSAL_DISCONNECTED LINK_CABLE_DISCONNECTED +#define LINK_UNIVERSAL_NO_DATA LINK_CABLE_NO_DATA class LinkUniversal { + private: + using u32 = unsigned int; + using u16 = unsigned short; + using u8 = unsigned char; + using s8 = signed char; + + static constexpr int MAX_ROOM_NUMBER = 32000; + static constexpr int INIT_WAIT_FRAMES = 10; + static constexpr int SWITCH_WAIT_FRAMES = 25; + static constexpr int SWITCH_WAIT_FRAMES_RANDOM = 10; + static constexpr int BROADCAST_SEARCH_WAIT_FRAMES = 10; + static constexpr int SERVE_WAIT_FRAMES = 60; + static constexpr int SERVE_WAIT_FRAMES_RANDOM = 30; + public: enum State { INITIALIZING, WAITING, CONNECTED }; enum Mode { LINK_CABLE, LINK_WIRELESS }; @@ -104,25 +106,29 @@ class LinkUniversal { s8 asyncACKTimerId; }; - explicit LinkUniversal( - Protocol protocol = AUTODETECT, - const char* gameName = "", - CableOptions cableOptions = - CableOptions{ - LinkCable::BaudRate::BAUD_RATE_1, LINK_CABLE_DEFAULT_TIMEOUT, - LINK_CABLE_DEFAULT_REMOTE_TIMEOUT, LINK_CABLE_DEFAULT_INTERVAL, - LINK_CABLE_DEFAULT_SEND_TIMER_ID}, - WirelessOptions wirelessOptions = WirelessOptions{ - true, LINK_UNIVERSAL_MAX_PLAYERS, LINK_WIRELESS_DEFAULT_TIMEOUT, - LINK_WIRELESS_DEFAULT_REMOTE_TIMEOUT, LINK_WIRELESS_DEFAULT_INTERVAL, - LINK_WIRELESS_DEFAULT_SEND_TIMER_ID, - LINK_WIRELESS_DEFAULT_ASYNC_ACK_TIMER_ID}) { + explicit LinkUniversal(Protocol protocol = AUTODETECT, + const char* gameName = "", + CableOptions cableOptions = + CableOptions{LinkCable::BaudRate::BAUD_RATE_1, + LINK_CABLE_DEFAULT_TIMEOUT, + LINK_CABLE_DEFAULT_REMOTE_TIMEOUT, + LINK_CABLE_DEFAULT_INTERVAL, + LINK_CABLE_DEFAULT_SEND_TIMER_ID}, + WirelessOptions wirelessOptions = + WirelessOptions{ + true, LINK_UNIVERSAL_MAX_PLAYERS, + LINK_WIRELESS_DEFAULT_TIMEOUT, + LINK_WIRELESS_DEFAULT_REMOTE_TIMEOUT, + LINK_WIRELESS_DEFAULT_INTERVAL, + LINK_WIRELESS_DEFAULT_SEND_TIMER_ID, + LINK_WIRELESS_DEFAULT_ASYNC_ACK_TIMER_ID}, + int randomSeed = 123) { this->linkCable = new LinkCable( cableOptions.baudRate, cableOptions.timeout, cableOptions.remoteTimeout, cableOptions.interval, cableOptions.sendTimerId); this->linkWireless = new LinkWireless( wirelessOptions.retransmission, true, - min(wirelessOptions.maxPlayers, LINK_UNIVERSAL_MAX_PLAYERS), + Link::_min(wirelessOptions.maxPlayers, LINK_UNIVERSAL_MAX_PLAYERS), wirelessOptions.timeout, wirelessOptions.remoteTimeout, wirelessOptions.interval, wirelessOptions.sendTimerId, wirelessOptions.asyncACKTimerId); @@ -131,7 +137,7 @@ class LinkUniversal { this->config.gameName = gameName; } - bool isActive() { return isEnabled; } + [[nodiscard]] bool isActive() { return isEnabled; } void activate() { reset(); @@ -146,16 +152,16 @@ class LinkUniversal { } void setProtocol(Protocol protocol) { this->config.protocol = protocol; } - Protocol getProtocol() { return this->config.protocol; } + [[nodiscard]] Protocol getProtocol() { return this->config.protocol; } - bool isConnected() { return state == CONNECTED; } + [[nodiscard]] bool isConnected() { return state == CONNECTED; } - u8 playerCount() { + [[nodiscard]] u8 playerCount() { return mode == LINK_CABLE ? linkCable->playerCount() : linkWireless->playerCount(); } - u8 currentPlayerId() { + [[nodiscard]] u8 currentPlayerId() { return mode == LINK_CABLE ? linkCable->currentPlayerId() : linkWireless->currentPlayerId(); } @@ -164,10 +170,10 @@ class LinkUniversal { if (!isEnabled) return; - u16 keys = ~REG_KEYS & KEY_ANY; - __qran_seed += keys; - __qran_seed += REG_RCNT; - __qran_seed += REG_SIOCNT; + u16 keys = ~Link::_REG_KEYS & Link::_KEY_ANY; + randomSeed += keys; + randomSeed += Link::_REG_RCNT; + randomSeed += Link::_REG_SIOCNT; if (mode == LINK_CABLE) linkCable->sync(); @@ -175,7 +181,7 @@ class LinkUniversal { switch (state) { case INITIALIZING: { waitCount++; - if (waitCount > LINK_UNIVERSAL_INIT_WAIT_FRAMES) + if (waitCount > INIT_WAIT_FRAMES) start(); break; }; @@ -242,18 +248,22 @@ class LinkUniversal { : linkWireless->config.sendTimerId; while (isConnected() && !canRead(playerId) && !cancel()) { - IntrWait(1, IRQ_SERIAL | LINK_CABLE_TIMER_IRQ_IDS[timerId]); + Link::_IntrWait(1, Link::_IRQ_SERIAL | Link::_TIMER_IRQ_IDS[timerId]); sync(); } return isConnected() && canRead(playerId); } - bool canRead(u8 playerId) { return !incomingMessages[playerId].isEmpty(); } + [[nodiscard]] bool canRead(u8 playerId) { + return !incomingMessages[playerId].isEmpty(); + } u16 read(u8 playerId) { return incomingMessages[playerId].pop(); } - u16 peek(u8 playerId) { return incomingMessages[playerId].peek(); } + [[nodiscard]] u16 peek(u8 playerId) { + return incomingMessages[playerId].peek(); + } bool send(u16 data) { if (data == LINK_CABLE_DISCONNECTED || data == LINK_CABLE_NO_DATA) @@ -267,17 +277,19 @@ class LinkUniversal { } } - State getState() { return state; } - Mode getMode() { return mode; } - LinkWireless::State getWirelessState() { return linkWireless->getState(); } + [[nodiscard]] State getState() { return state; } + [[nodiscard]] Mode getMode() { return mode; } + [[nodiscard]] LinkWireless::State getWirelessState() { + return linkWireless->getState(); + } ~LinkUniversal() { delete linkCable; delete linkWireless; } - u32 _getWaitCount() { return waitCount; } - u32 _getSubWaitCount() { return subWaitCount; } + [[nodiscard]] u32 _getWaitCount() { return waitCount; } + [[nodiscard]] u32 _getSubWaitCount() { return subWaitCount; } void _onVBlank() { if (mode == LINK_CABLE) @@ -322,13 +334,16 @@ class LinkUniversal { u32 switchWait = 0; u32 subWaitCount = 0; u32 serveWait = 0; + int randomSeed = 0; volatile bool isEnabled = false; void receiveCableMessages() { - int maxPlayers = - min(LINK_CABLE_MAX_PLAYERS, - LINK_UNIVERSAL_MAX_PLAYERS); // TODO: MOVE TO CONSTEXPR - for (u32 i = 0; i < maxPlayers; i++) { + static constexpr u32 MAX_PLAYERS = + LINK_CABLE_MAX_PLAYERS < LINK_UNIVERSAL_MAX_PLAYERS + ? LINK_CABLE_MAX_PLAYERS + : LINK_UNIVERSAL_MAX_PLAYERS; + + for (u32 i = 0; i < MAX_PLAYERS; i++) { while (linkCable->canRead(i)) incomingMessages[i].push(linkCable->read(i)); } @@ -360,7 +375,7 @@ class LinkUniversal { waitCount = 0; subWaitCount++; - if (subWaitCount >= LINK_UNIVERSAL_BROADCAST_SEARCH_WAIT_FRAMES) { + if (subWaitCount >= BROADCAST_SEARCH_WAIT_FRAMES) { if (!tryConnectOrServeWirelessSession()) return false; } @@ -407,8 +422,7 @@ class LinkUniversal { (LINK_UNIVERSAL_GAME_ID_FILTER == 0 || server.gameId == LINK_UNIVERSAL_GAME_ID_FILTER)) { u32 randomNumber = safeStoi(server.userName); - if (randomNumber > maxRandomNumber && - randomNumber < LINK_UNIVERSAL_MAX_ROOM_NUMBER) { + if (randomNumber > maxRandomNumber && randomNumber < MAX_ROOM_NUMBER) { maxRandomNumber = randomNumber; serverIndex = i; } @@ -423,9 +437,8 @@ class LinkUniversal { return false; subWaitCount = 0; - serveWait = LINK_UNIVERSAL_SERVE_WAIT_FRAMES + - qran_range(1, LINK_UNIVERSAL_SERVE_WAIT_FRAMES_RANDOM); - u32 randomNumber = qran_range(1, LINK_UNIVERSAL_MAX_ROOM_NUMBER); + serveWait = SERVE_WAIT_FRAMES + _qran_range(1, SERVE_WAIT_FRAMES_RANDOM); + u32 randomNumber = _qran_range(1, MAX_ROOM_NUMBER); char randomNumberStr[6]; std::snprintf(randomNumberStr, sizeof(randomNumberStr), "%d", randomNumber); @@ -503,8 +516,7 @@ class LinkUniversal { void resetState() { waitCount = 0; - switchWait = LINK_UNIVERSAL_SWITCH_WAIT_FRAMES + - qran_range(1, LINK_UNIVERSAL_SWITCH_WAIT_FRAMES_RANDOM); + switchWait = SWITCH_WAIT_FRAMES + _qran_range(1, SWITCH_WAIT_FRAMES_RANDOM); subWaitCount = 0; serveWait = 0; for (u32 i = 0; i < LINK_UNIVERSAL_MAX_PLAYERS; i++) @@ -524,6 +536,15 @@ class LinkUniversal { return num; } + + int _qran(void) { + randomSeed = 1664525 * randomSeed + 1013904223; + return (randomSeed >> 16) & 0x7FFF; + } + + int _qran_range(int min, int max) { + return (_qran() * (max - min) >> 15) + min; + } }; extern LinkUniversal* linkUniversal; diff --git a/lib/LinkWireless.hpp b/lib/LinkWireless.hpp index 02503e0..92b727b 100644 --- a/lib/LinkWireless.hpp +++ b/lib/LinkWireless.hpp @@ -106,6 +106,7 @@ static volatile char LINK_WIRELESS_VERSION[] = "LinkWireless/v7.0.0"; #define LINK_WIRELESS_MAX_PLAYERS 5 #define LINK_WIRELESS_MIN_PLAYERS 2 +#define LINK_WIRELESS_END 0 #define LINK_WIRELESS_MAX_COMMAND_TRANSFER_LENGTH 22 #define LINK_WIRELESS_MAX_COMMAND_RESPONSE_LENGTH 30 #define LINK_WIRELESS_BROADCAST_LENGTH 6 @@ -114,7 +115,9 @@ static volatile char LINK_WIRELESS_VERSION[] = "LinkWireless/v7.0.0"; #define LINK_WIRELESS_MAX_SERVERS \ (LINK_WIRELESS_MAX_COMMAND_RESPONSE_LENGTH / \ LINK_WIRELESS_BROADCAST_RESPONSE_LENGTH) -#define LINK_WIRELESS_END 0 +#define LINK_WIRELESS_MAX_GAME_ID 0x7fff +#define LINK_WIRELESS_MAX_GAME_NAME_LENGTH 14 +#define LINK_WIRELESS_MAX_USER_NAME_LENGTH 8 #define LINK_WIRELESS_DEFAULT_TIMEOUT 10 #define LINK_WIRELESS_DEFAULT_REMOTE_TIMEOUT 10 #define LINK_WIRELESS_DEFAULT_INTERVAL 50 @@ -153,9 +156,6 @@ class LinkWireless { static constexpr int TRANSFER_WAIT = 15; static constexpr int BROADCAST_SEARCH_WAIT_FRAMES = 60; static constexpr int CMD_TIMEOUT = 100; - static constexpr int MAX_GAME_ID = 0x7fff; - static constexpr int MAX_GAME_NAME_LENGTH = 14; - static constexpr int MAX_USER_NAME_LENGTH = 8; static constexpr int LOGIN_STEPS = 9; static constexpr int COMMAND_HEADER_VALUE = 0x9966; static constexpr int RESPONSE_ACK_VALUE = 0x80; @@ -233,8 +233,8 @@ class LinkWireless { struct Server { u16 id = 0; u16 gameId; - char gameName[MAX_GAME_NAME_LENGTH + 1]; - char userName[MAX_USER_NAME_LENGTH + 1]; + char gameName[LINK_WIRELESS_MAX_GAME_NAME_LENGTH + 1]; + char userName[LINK_WIRELESS_MAX_USER_NAME_LENGTH + 1]; u8 currentPlayerCount; bool isFull() { return currentPlayerCount == 0; } @@ -332,7 +332,7 @@ class LinkWireless { */ bool serve(const char* gameName = "", const char* userName = "", - u16 gameId = MAX_GAME_ID) { + u16 gameId = LINK_WIRELESS_MAX_GAME_ID) { LINK_WIRELESS_RESET_IF_NEEDED if (state != AUTHENTICATED && state != SERVING) { lastError = WRONG_STATE; @@ -342,25 +342,25 @@ class LinkWireless { lastError = BUSY_TRY_AGAIN; return false; } - if (std::strlen(gameName) > MAX_GAME_NAME_LENGTH) { + if (std::strlen(gameName) > LINK_WIRELESS_MAX_GAME_NAME_LENGTH) { lastError = GAME_NAME_TOO_LONG; return false; } - if (std::strlen(userName) > MAX_GAME_NAME_LENGTH) { + if (std::strlen(userName) > LINK_WIRELESS_MAX_GAME_NAME_LENGTH) { lastError = USER_NAME_TOO_LONG; return false; } - char finalGameName[MAX_GAME_NAME_LENGTH + 1]; - char finalUserName[MAX_USER_NAME_LENGTH + 1]; - copyName(finalGameName, gameName, MAX_GAME_NAME_LENGTH); - copyName(finalUserName, userName, MAX_USER_NAME_LENGTH); + char finalGameName[LINK_WIRELESS_MAX_GAME_NAME_LENGTH + 1]; + char finalUserName[LINK_WIRELESS_MAX_USER_NAME_LENGTH + 1]; + copyName(finalGameName, gameName, LINK_WIRELESS_MAX_GAME_NAME_LENGTH); + copyName(finalUserName, userName, LINK_WIRELESS_MAX_USER_NAME_LENGTH); if (state != SERVING) setup(config.maxPlayers); addData(buildU32(buildU16(finalGameName[1], finalGameName[0]), - gameId & MAX_GAME_ID), + gameId & LINK_WIRELESS_MAX_GAME_ID), true); addData(buildU32(buildU16(finalGameName[5], finalGameName[4]), buildU16(finalGameName[3], finalGameName[2]))); @@ -486,7 +486,7 @@ class LinkWireless { Server server; server.id = (u16)result.responses[start]; - server.gameId = result.responses[start + 1] & MAX_GAME_ID; + server.gameId = result.responses[start + 1] & LINK_WIRELESS_MAX_GAME_ID; u32 gameI = 0, userI = 0; recoverName(server.gameName, gameI, result.responses[start + 1], false); recoverName(server.gameName, gameI, result.responses[start + 2]); diff --git a/lib/_link_common.hpp b/lib/_link_common.hpp index 680b745..3e12c2a 100644 --- a/lib/_link_common.hpp +++ b/lib/_link_common.hpp @@ -75,15 +75,19 @@ inline volatile u16* const _REG_SIOMULTI = inline volatile u16& _REG_VCOUNT = *reinterpret_cast(_REG_BASE + 0x0006); +inline volatile u16& _REG_KEYS = + *reinterpret_cast(_REG_BASE + 0x0130); + inline volatile _TMR_REC* const _REG_TM = reinterpret_cast(_REG_BASE + 0x0100); -static constexpr u16 _TM_FREQ_1 = 0; // 1 cycle/tick (16.7 MHz) -static constexpr u16 _TM_FREQ_64 = 0x0001; // 64 cycles/tick (262 kHz) -static constexpr u16 _TM_FREQ_256 = 0x0002; // 256 cycles/tick (66 kHz) -static constexpr u16 _TM_FREQ_1024 = 0x0003; // 1024 cycles/tick (16 kHz) -static constexpr u16 _TM_IRQ = 0x0040; -static constexpr u16 _TM_ENABLE = 0x0080; +static constexpr u16 _KEY_ANY = 0x03FF; //!< Here's the Any key :) +static constexpr u16 _TM_FREQ_1 = 0; //!< 1 cycle/tick (16.7 MHz) +static constexpr u16 _TM_FREQ_64 = 0x0001; //!< 64 cycles/tick (262 kHz) +static constexpr u16 _TM_FREQ_256 = 0x0002; //!< 256 cycles/tick (66 kHz) +static constexpr u16 _TM_FREQ_1024 = 0x0003; //!< 1024 cycles/tick (16 kHz) +static constexpr u16 _TM_IRQ = 0x0040; //!< Enable timer irq +static constexpr u16 _TM_ENABLE = 0x0080; //!< Enable timer static constexpr u16 _IRQ_VBLANK = 0x0001; //!< Catch VBlank irq static constexpr u16 _IRQ_TIMER0 = 0x0008; //!< Catch timer 0 irq