Skip to content

Modern Rust async/await support for Game Boy Advance development using the Embassy executor integrated with the agb library.

License

Notifications You must be signed in to change notification settings

zpg6/embassy-agb

Repository files navigation

Embassy AGB - Async Rust Support for Game Boy Advance Development

Crates.io Version Crates.io Downloads

This repository provides async/await support for Game Boy Advance development using the embassy executor integrated with the agb library.

Example

Features

  • Async/await support: Write GBA games using modern async Rust
  • Embassy executor integration: Leverage embassy's powerful task scheduling
  • Configurable time driver: Precise timing using any of GBA's 4 hardware timers
  • Async APIs: Async wrappers for display, input, and sound operations
  • Full agb compatibility: Works alongside existing agb code

Requirements

  • Rust nightly toolchain (for build-std)
  • thumbv4t-none-eabi target: rustup target add thumbv4t-none-eabi
  • mGBA or another GBA emulator for testing

Quick Start

Add the dependencies:

cargo add [email protected]
cargo add embassy-agb
cargo add [email protected]

Or manually add to your Cargo.toml:

[dependencies]
agb = "0.22.6"
embassy-agb = "0.1"
embassy-executor = "0.9"

Timer Selection

Embassy-agb supports using any of the GBA's 4 hardware timers for the time driver. Timer2 is the default. To use a different timer:

cargo add embassy-agb --no-default-features --features executor,time-driver-timer0

Available timer options:

  • time-driver-timer0 - Timer0 (used by sound system)
  • time-driver-timer1 - Timer1 (used by sound system)
  • time-driver-timer2 - Timer2 (default, available for general use)
  • time-driver-timer3 - Timer3 (available for general use)

Note: Timer0 and Timer1 are also used by agb's sound system. Using Timer2 or Timer3 avoids potential conflicts.

Project Setup

Create a rust-toolchain.toml in your project root:

[toolchain]
channel = "nightly"
components = ["rust-src", "clippy"]

Optionally, create .cargo/config.toml to set the default target:

[build]
target = "thumbv4t-none-eabi"

[unstable]
build-std = ["core", "alloc"]

Create an async GBA application:

#![no_std]
#![no_main]

use embassy_agb::{time::Timer, Spawner};

#[embassy_agb::main]
async fn main(spawner: Spawner) {
    let mut gba = embassy_agb::init(Default::default());

    // Spawn background tasks
    spawner.must_spawn(display_task(gba.display()));
    spawner.must_spawn(audio_task(gba.mixer()));

    // Main game loop
    let mut input = gba.input();
    loop {
        // Wait for input asynchronously
        let (button, event) = input.wait_for_any_button_press().await;

        // Handle input...

        // Run at 60 FPS
        Timer::after_millis(16).await;
    }
}

#[embassy_executor::task]
async fn display_task(mut display: embassy_agb::display::AsyncDisplay<'_>) {
    loop {
        // Wait for VBlank and render frame
        let mut frame = display.frame().await;
        // Rendering code...
    }
}

#[embassy_executor::task]
async fn audio_task(mut mixer: embassy_agb::sound::AsyncMixer<'_>) {
    mixer.init(agb::sound::mixer::Frequency::Hz32768);

    loop {
        // Process audio frame
        mixer.frame().await;
    }
}

Architecture

Embassy-agb integrates the embassy async executor with agb's hardware abstraction:

  • Executor: Uses embassy's arch-spin executor optimized for the GBA's ARM7TDMI processor
  • Time Driver: Implements embassy's time driver interface using any of GBA's 4 timers (configurable via feature flags)
  • Async APIs: Provides async wrappers around agb's display, input, and sound systems
  • Task Management: Supports spawning multiple concurrent tasks for different game systems

Timing and Performance

  • Time Resolution: 32.768kHz tick rate for precise timing
  • Frame Rate: Designed for 60 FPS game loops
  • Power Efficiency: Uses halt() instruction when no tasks are ready
  • Memory Overhead: Minimal overhead over synchronous agb code

Compatibility

Embassy-agb is designed to be fully compatible with existing agb code:

  • Use gba.agb() to access the underlying agb::Gba instance
  • Mix async and sync code as needed
  • Existing agb examples can be gradually migrated to async

Resources

License

MIT

Contributing

Contributions are welcome! Whether it's bug fixes, feature additions, or documentation improvements, we appreciate your help in making this project better. For major changes or new features, please open an issue first to discuss what you would like to change.

About

Modern Rust async/await support for Game Boy Advance development using the Embassy executor integrated with the agb library.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages