Skip to content

Commit

Permalink
Merge pull request #16 from afska/raw
Browse files Browse the repository at this point in the history
🔧👾 LinkRawCable, 🔧📻 LinkRawWireless
  • Loading branch information
afska authored Jan 15, 2024
2 parents 396ac8b + 560a1c6 commit 7b1d457
Show file tree
Hide file tree
Showing 24 changed files with 3,006 additions and 52 deletions.
49 changes: 46 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
A set of Game Boy Advance (GBA) C++ libraries to interact with the Serial Port. Its main purpose is to provide multiplayer support to homebrew games.

- [👾](#-LinkCable) [LinkCable.hpp](lib/LinkCable.hpp): The classic 16-bit **Multi-Play mode** (up to 4 players) using a GBA Link Cable!
- [🔧👾](#-LinkRawCable) [LinkRawCable.hpp](lib/LinkRawCable.hpp): A **minimal** low-level API for the 16-bit Multi-Play mode.
- [💻](#-LinkCableMultiboot) [LinkCableMultiboot.hpp](lib/LinkCableMultiboot.hpp): ‍Send **Multiboot software** (small 256KiB ROMs) to other GBAs with no cartridge!
- [🔌](#-LinkGPIO) [LinkGPIO.hpp](lib/LinkGPIO.hpp): Use the Link Port however you want to control **any device** (like LEDs, rumble motors, and that kind of stuff)!
- [🔗](#-LinkSPI) [LinkSPI.hpp](lib/LinkSPI.hpp): Connect with a PC (like a **Raspberry Pi**) or another GBA (with a GBC Link Cable) using this mode. Transfer up to 2Mbit/s!
- [📻](#-LinkWireless) [LinkWireless.hpp](lib/LinkWireless.hpp): Connect up to 5 consoles with the **Wireless Adapter**!
- [🔧📻](#-LinkRawWireless) [LinkRawWireless.hpp](lib/LinkRawWireless.hpp): A **minimal** low-level API for the Wireless Adapter.
- [🌎](#-LinkUniversal) [LinkUniversal.hpp](lib/LinkUniversal.hpp): Add multiplayer support to your game, both with 👾 *Link Cables* and 📻 *Wireless Adapters*, using the **same API**.

*(click on the emojis for documentation)*
Expand All @@ -15,9 +17,9 @@ A set of Game Boy Advance (GBA) C++ libraries to interact with the Serial Port.
## Usage

- Include the library you want (e.g. [LinkCable.hpp](lib/LinkCable.hpp)) in your game code, and refer to its comments for instructions.
- Include the library you want (e.g. [LinkCable.hpp](lib/LinkCable.hpp)) in your game code, and refer to its comments for instructions. Most of these libraries are provided as single header files for simplicity. The only external dependency is **libtonc**, which comes preinstalled with *devkitPro*.
- Check out the [examples](examples) folder.
* Builds are available in *Releases*.
* Builds are available in [Releases](https://github.com/afska/gba-link-connection/releases).
* They can be tested on real GBAs or using emulators (*mGBA*, *NO$GBA*, or *VBA-M*).
* For `LinkCable`/`LinkWireless`/`LinkUniversal` there are stress tests that you can use to tweak your configuration.

Expand Down Expand Up @@ -195,7 +197,7 @@ Name | Return type | Description
--- | --- | ---
`isActive()` | **bool** | Returns whether the library is active or not.
`activate()` | **bool** | Activates the library. When an adapter is connected, it changes the state to `AUTHENTICATED`. It can also be used to disconnect or reset the adapter.
`deactivate()` | **bool** | Puts the adapter into a low consumption mode and then deactivates the library. It returns a boolean indicating whether the transition to low consumption mode was successful.
`deactivate()` | **bool** | Puts the adapter into a low consumption mode and then deactivates the library. It returns a boolean indicating whether the transition to low consumption mode was successful. To ensure that the transition works, before calling this, reset the adapter by calling `activate()` again, and then `deactivate()`.
`serve([gameName], [userName], [gameId])` | **bool** | Starts broadcasting a server and changes the state to `SERVING`. You can, optionally, provide a `gameName` (max `14` characters), a `userName` (max `8` characters), and a `gameId` *(0 ~ 0x7FFF)* that games will be able to read. If the adapter is already serving, this method only updates the broadcast data.
`getServers(servers, [onWait])` | **bool** | Fills the `servers` array with all the currently broadcasting servers. This action takes 1 second to complete, but you can optionally provide an `onWait()` function which will be invoked each time VBlank starts.
`getServersAsyncStart()` | **bool** | Starts looking for broadcasting servers and changes the state to `SEARCHING`. After this, call `getServersAsyncEnd(...)` 1 second later.
Expand Down Expand Up @@ -241,3 +243,44 @@ Name | Return type | Description
`getProtocol()` | **LinkUniversal::Protocol** | Returns the active protocol (one of `LinkUniversal::Protocol::AUTODETECT`, `LinkUniversal::Protocol::CABLE`, `LinkUniversal::Protocol::WIRELESS_AUTO`, `LinkUniversal::Protocol::WIRELESS_SERVER`, or `LinkUniversal::Protocol::WIRELESS_CLIENT`).
`setProtocol(protocol)` | - | Sets the active `protocol`.
`getWirelessState()` | **LinkWireless::State** | Returns the wireless state (same as [📻 LinkWireless](#methods-4)'s `getState()`).

# 🔧👾 LinkRawCable

- This is a minimal hardware wrapper designed for the *Multi-Play mode*.
- It doesn't include any of the features of [👾 LinkCable](#-LinkCable), so it's not well suited for games.
- Its demo (`LinkRawCable_demo`) can help emulator developers in enhancing accuracy.

![screenshot](https://github.com/afska/gba-link-connection/assets/1631752/29a25bf7-211e-49d6-a32a-566c72a44973)

## Methods

Name | Return type | Description
--- | --- | ---
`isActive()` | **bool** | Returns whether the library is active or not.
`activate(baudRate = BAUD_RATE_1)` | - | Activates the library in a specific `baudRate` (`LinkRawCable::BaudRate`).
`deactivate()` | - | Deactivates the library.
`transfer(data)` | **LinkRawCable::Response** | Exchanges `data` with the other end. Returns the received data, including the assigned player id.
`transfer(data, cancel)` | **LinkRawCable::Response** | Like `transfer(data)` but accepts a `cancel()` function. The library will continuously invoke it, and abort the transfer if it returns `true`.
`transferAsync(data)` | - | Schedules a `data` transfer and returns. After this, call `getAsyncState()` and `getAsyncData()`. Note that until you retrieve the async data, normal `transfer(...)`s won't do anything!
`getAsyncState()` | **LinkRawCable::AsyncState** | Returns the state of the last async transfer (one of `LinkRawCable::AsyncState::IDLE`, `LinkRawCable::AsyncState::WAITING`, or `LinkRawCable::AsyncState::READY`).
`getAsyncData()` | **LinkRawCable::Response** | If the async state is `READY`, returns the remote data and switches the state back to `IDLE`.
`isMaster()` | **bool** | Returns whether the console is connected as master or not. Returns garbage when the cable is not properly connected.
`isReady()` | **bool** | Returns whether all connected consoles have entered the multiplayer mode. Returns garbage when the cable is not properly connected.
`getBaudRate()` | **LinkRawCable::BaudRate** | Returns the current `baudRate`.

- don't send `0xFFFF`, it's a reserved value that means *disconnected client*
- only `transfer(...)` if `isReady()`

# 🔧📻 LinkRawWireless

- This is a minimal hardware wrapper designed for the *Wireless Adapter*.
- It doesn't include any of the features of [📻 LinkWireless](#-LinkWireless), so it's not well suited for games.
- Its demo (`LinkRawWireless_demo`) can help emulator developers in enhancing accuracy.

![screenshot](https://github.com/afska/gba-link-connection/assets/1631752/bc7e5a7d-a1bd-46a5-8318-98160c1229ae)


## Methods

- There's one method for every supported wireless adapter command.
- Use `sendCommand(...)` to send arbitrary commands.
3 changes: 1 addition & 2 deletions docs/wireless_adapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,6 @@ Both Pokemon games and the multiboot ROM that the adapter sends when no cartridg

If we analyze whether a command ID throws an 'invalid command' error (`0x996601ee` with error code `2`), we can know that there are **more commands**. I don't know what these commands do.

- `0x18`
- `0x18`
- `0x32`
- `0x33`
Expand All @@ -524,7 +523,7 @@ Waiting
* The GBA then sends the response back (e.g. `0x996600A8` as `0x28` + `0x80` = `0xA8`).
* After this, control of the clock returns to the GBA, and it can start sending commands back again. For example this might be receiving the command sent by the other device using [ReceiveData](#receivedata---0x26).

⌚ This timeouts after 500ms of the adapter not having anything to tell the GBA about. In this case, the adapter sends `0x99660027`.
⌚ This timeouts after 500ms of the adapter not having anything to tell the GBA about. In this case, the adapter sends `0x99660027`. **This is only true if the console has used the Setup command before**. The value that most games use (`0x003C0420`) seems to contain this timeout value, but the default is zero (no timeout).

✅ When there's new data available, the adapter sends to the GBA a `0x99660028`.

Expand Down
1 change: 1 addition & 0 deletions examples/LinkCable_full/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ inline void setUpInterrupts() {
// A+B+START+SELECT
REG_KEYCNT = 0b1100000000001111;
interrupt_set_handler(INTR_KEYPAD, ISR_reset);
interrupt_enable(INTR_KEYPAD);
}

void printTutorial() {
Expand Down
14 changes: 0 additions & 14 deletions examples/LinkCable_full/src/utils/BackgroundUtils.h

This file was deleted.

15 changes: 10 additions & 5 deletions examples/LinkCable_full/src/utils/InputHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@

class InputHandler {
public:
InputHandler() { this->isPressed = false; }
InputHandler() {
this->isPressed = false;
this->isWaiting = true;
}

inline bool getIsPressed() { return isPressed; }

Expand All @@ -16,9 +19,10 @@ class InputHandler {
inline void setHandledFlag(bool value) { handledFlag = value; }

inline void setIsPressed(bool isPressed) {
bool isNewPressEvent = !this->isPressed && isPressed;
bool isNewReleaseEvent = this->isPressed && !isPressed;
bool isNewPressEvent = !this->isWaiting && !this->isPressed && isPressed;
bool isNewReleaseEvent = !this->isWaiting && this->isPressed && !isPressed;
this->isPressed = isPressed;
this->isWaiting = this->isWaiting && isPressed;

this->isNewPressEvent = isNewPressEvent;
this->isNewReleaseEvent = isNewReleaseEvent;
Expand All @@ -27,8 +31,9 @@ class InputHandler {
protected:
bool isPressed = false;
bool isNewPressEvent = false;
bool isNewReleaseEvent;
bool isNewReleaseEvent = false;
bool handledFlag = false;
bool isWaiting = false;
};

#endif // INPUT_HANDLER_H
#endif // INPUT_HANDLER_H
17 changes: 13 additions & 4 deletions examples/LinkCable_full/src/utils/SceneUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@
#define SCENE_UTILS_H

#include <libgba-sprite-engine/background/text_stream.h>

#include <tonc_memdef.h>
#include <tonc_memmap.h>
#include <string>

#include "BackgroundUtils.h"
#include "SpriteUtils.h"

const u32 TEXT_MIDDLE_COL = 12;

extern int DEBULOG_LINE;
Expand All @@ -21,6 +19,17 @@ inline bool isBitHigh(u16 data, u8 bit) {
return (data >> bit) & 1;
}

inline void BACKGROUND_enable(bool bg0, bool bg1, bool bg2, bool bg3) {
REG_DISPCNT = bg0 ? REG_DISPCNT | DCNT_BG0 : REG_DISPCNT & ~DCNT_BG0;
REG_DISPCNT = bg1 ? REG_DISPCNT | DCNT_BG1 : REG_DISPCNT & ~DCNT_BG1;
REG_DISPCNT = bg2 ? REG_DISPCNT | DCNT_BG2 : REG_DISPCNT & ~DCNT_BG2;
REG_DISPCNT = bg3 ? REG_DISPCNT | DCNT_BG3 : REG_DISPCNT & ~DCNT_BG3;
}

inline void SPRITE_disable() {
REG_DISPCNT = REG_DISPCNT & ~DCNT_OBJ;
}

inline void SCENE_init() {
TextStream::instance().clear();
TextStream::instance().scroll(0, 0);
Expand Down
11 changes: 0 additions & 11 deletions examples/LinkCable_full/src/utils/SpriteUtils.h

This file was deleted.

Loading

0 comments on commit 7b1d457

Please sign in to comment.