mirror of
https://github.com/italicsjenga/agb.git
synced 2025-01-10 00:51:34 +11:00
add amplitude
This commit is contained in:
parent
3ec9bd2610
commit
e78a6624ff
14
examples/amplitude/.cargo/config.toml
Normal file
14
examples/amplitude/.cargo/config.toml
Normal file
|
@ -0,0 +1,14 @@
|
|||
[unstable]
|
||||
build-std = ["core", "alloc"]
|
||||
build-std-features = ["compiler-builtins-mem"]
|
||||
|
||||
[build]
|
||||
target = "thumbv4t-none-eabi"
|
||||
|
||||
[target.thumbv4t-none-eabi]
|
||||
rustflags = ["-Clink-arg=-Tgba.ld", "-Ctarget-cpu=arm7tdmi"]
|
||||
runner = "mgba-qt"
|
||||
|
||||
[target.armv4t-none-eabi]
|
||||
rustflags = ["-Clink-arg=-Tgba.ld", "-Ctarget-cpu=arm7tdmi"]
|
||||
runner = "mgba-qt"
|
407
examples/amplitude/Cargo.lock
generated
Normal file
407
examples/amplitude/Cargo.lock
generated
Normal file
|
@ -0,0 +1,407 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "adler32"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
|
||||
|
||||
[[package]]
|
||||
name = "agb"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"agb_fixnum",
|
||||
"agb_hashmap",
|
||||
"agb_image_converter",
|
||||
"agb_macros",
|
||||
"agb_sound_converter",
|
||||
"bare-metal",
|
||||
"bitflags 2.1.0",
|
||||
"modular-bitfield",
|
||||
"rustc-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "agb_fixnum"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"agb_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "agb_hashmap"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"rustc-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "agb_image_converter"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"asefile",
|
||||
"fontdue",
|
||||
"image",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "agb_macros"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "agb_sound_converter"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"hound",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "amplitude"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"agb",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "asefile"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10a71de7aecd2d0a76ec90fde2c443d12667c737d92de76bd187f101eca37891"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"byteorder",
|
||||
"flate2",
|
||||
"image",
|
||||
"log",
|
||||
"nohash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "bare-metal"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c70beb79cbb5ce9c4f8e20849978f34225931f665bb49efa6982875a4d5facb3"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "color_quant"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deflate"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174"
|
||||
dependencies = [
|
||||
"adler32",
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide 0.6.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fontdue"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0793f5137567643cf65ea42043a538804ff0fbf288649e2141442b602d81f9bc"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
"ttf-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hound"
|
||||
version = "3.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d13cdbd5dbb29f9c88095bbdc2590c9cba0d0a1269b983fef6b2cdd7e9f4db1"
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.23.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"byteorder",
|
||||
"color_quant",
|
||||
"num-iter",
|
||||
"num-rational",
|
||||
"num-traits",
|
||||
"png",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435"
|
||||
dependencies = [
|
||||
"adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa"
|
||||
dependencies = [
|
||||
"adler",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "modular-bitfield"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a53d79ba8304ac1c4f9eb3b9d281f21f7be9d4626f72ce7df4ad8fbde4f38a74"
|
||||
dependencies = [
|
||||
"modular-bitfield-impl",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "modular-bitfield-impl"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nohash"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0f889fb66f7acdf83442c35775764b51fed3c606ab9cee51500dbde2cf528ca"
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-iter"
|
||||
version = "0.1.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-rational"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.16.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"crc32fast",
|
||||
"deflate",
|
||||
"miniz_oxide 0.3.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ttf-parser"
|
||||
version = "0.15.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b3e06c9b9d80ed6b745c7159c40b311ad2916abb34a49e9be2653b90db0d8dd"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
20
examples/amplitude/Cargo.toml
Normal file
20
examples/amplitude/Cargo.toml
Normal file
|
@ -0,0 +1,20 @@
|
|||
[package]
|
||||
name = "amplitude"
|
||||
version = "0.1.0"
|
||||
authors = [""]
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
agb = { version = "0.14.0", path = "../../agb" }
|
||||
|
||||
[profile.dev]
|
||||
opt-level = 2
|
||||
debug = true
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
lto = true
|
||||
debug = true
|
||||
codegen-units = 1
|
72
examples/amplitude/README.md
Normal file
72
examples/amplitude/README.md
Normal file
|
@ -0,0 +1,72 @@
|
|||
# AGBRS template
|
||||
|
||||
## A basic template example for agb projects
|
||||
|
||||
This makes getting started with a new project for the Game Boy Advance in rust really simple, by providing
|
||||
all the boiler plate files for you.
|
||||
|
||||
## Building
|
||||
|
||||
### Prerequisites
|
||||
|
||||
You will need the following installed in order to build and run this project:
|
||||
|
||||
* A recent version of `rustup`. See the [rust website](https://www.rust-lang.org/tools/install) for instructions for your operating system
|
||||
* `arm-none-eabi-binutils` for assembling and linking
|
||||
* Windows: [GNU Arm Embedded Toolchain](https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads).
|
||||
Make sure you select "Add path to environment variable" during the install
|
||||
* Debian and derivatives (e.g. Ubuntu, raspberry pi OS, linux mint): `sudo apt install binutils-arm-none-eabi`
|
||||
* Arch linux and derivatives: `sudo pacman -S arm-none-eabi-binutils`
|
||||
|
||||
You will also want to install an emulator. The best support in agb is with [mgba](https://mgba.io), with
|
||||
`println!` support via `agb::println!` but any emulator should work. You'll get the best experience if
|
||||
`mgba-qt` is in your `PATH`.
|
||||
|
||||
If you want to run your game on real hardware, you will also need to install `agb-gbafix` which you can do after installing
|
||||
rust with the following: `cargo install agb-gbafix`. This is not required if you are only running your game in an emulator.
|
||||
|
||||
### Running in an emulator
|
||||
|
||||
Once you have the prerequisites installed, you should be able to build using
|
||||
|
||||
```sh
|
||||
cargo build
|
||||
```
|
||||
|
||||
or in release mode (recommended for the final version to ship to players)
|
||||
|
||||
```sh
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
The resulting file will be in `target/thumbv4t-none-eabi/debug/<your game>` or `target/thumbv4t-none-eabi/release/<your game>` depending on
|
||||
whether you did a release or debug build.
|
||||
|
||||
If you have `mgba-qt` in your path, you will be able to run your game with
|
||||
|
||||
```sh
|
||||
cargo run
|
||||
```
|
||||
|
||||
or in release mode
|
||||
|
||||
```sh
|
||||
cargo run --release
|
||||
```
|
||||
|
||||
## Starting development
|
||||
|
||||
You can find the documentation for agb [here](https://docs.rs/agb/latest/agb/).
|
||||
|
||||
You may also want to change the package name and version in `Cargo.toml` before you start.
|
||||
|
||||
## Shipping a .gba file for real hardware
|
||||
|
||||
To make a game run on real hardware, you will need to convert the built file into a file suitable for
|
||||
running on the real thing.
|
||||
|
||||
First build the binary in release mode using the instructions above, then do the following:
|
||||
|
||||
```sh
|
||||
agb-gbafix target/thumbv4t-none-eabi/release/<your game> -o <your game>.gba
|
||||
```
|
117
examples/amplitude/gba.ld
Normal file
117
examples/amplitude/gba.ld
Normal file
|
@ -0,0 +1,117 @@
|
|||
OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
|
||||
OUTPUT_ARCH(arm)
|
||||
|
||||
ENTRY(__start)
|
||||
EXTERN(__RUST_INTERRUPT_HANDLER)
|
||||
|
||||
EXTERN(__agbabi_memset)
|
||||
EXTERN(__agbabi_memcpy)
|
||||
|
||||
MEMORY {
|
||||
ewram (w!x) : ORIGIN = 0x02000000, LENGTH = 256K
|
||||
iwram (w!x) : ORIGIN = 0x03000000, LENGTH = 32K
|
||||
rom (rx) : ORIGIN = 0x08000000, LENGTH = 32M
|
||||
}
|
||||
|
||||
__text_start = ORIGIN(rom);
|
||||
|
||||
INPUT (agb.a)
|
||||
|
||||
SECTIONS {
|
||||
. = __text_start;
|
||||
|
||||
|
||||
.text : {
|
||||
KEEP(*(.crt0));
|
||||
*(.crt0 .crt0*);
|
||||
*(.text .text*);
|
||||
. = ALIGN(4);
|
||||
} > rom
|
||||
__text_end = .;
|
||||
|
||||
.rodata : {
|
||||
*(.rodata .rodata.*);
|
||||
. = ALIGN(4);
|
||||
} > rom
|
||||
|
||||
__iwram_rom_start = .;
|
||||
.iwram : {
|
||||
__iwram_data_start = ABSOLUTE(.);
|
||||
|
||||
*(.iwram .iwram.*);
|
||||
. = ALIGN(4);
|
||||
|
||||
*(.text_iwram .text_iwram.*);
|
||||
. = ALIGN(4);
|
||||
|
||||
__iwram_data_end = ABSOLUTE(.);
|
||||
} > iwram AT>rom
|
||||
|
||||
. = __iwram_rom_start + (__iwram_data_end - __iwram_data_start);
|
||||
|
||||
__ewram_rom_start = .;
|
||||
.ewram : {
|
||||
__ewram_data_start = ABSOLUTE(.);
|
||||
|
||||
*(.ewram .ewram.*);
|
||||
. = ALIGN(4);
|
||||
|
||||
*(.data .data.*);
|
||||
. = ALIGN(4);
|
||||
|
||||
__ewram_data_end = ABSOLUTE(.);
|
||||
} > ewram AT>rom
|
||||
|
||||
.bss : {
|
||||
*(.bss .bss.*);
|
||||
. = ALIGN(4);
|
||||
__iwram_end = ABSOLUTE(.);
|
||||
} > iwram
|
||||
|
||||
__iwram_rom_length_bytes = __iwram_data_end - __iwram_data_start;
|
||||
__iwram_rom_length_halfwords = (__iwram_rom_length_bytes + 1) / 2;
|
||||
|
||||
__ewram_rom_length_bytes = __ewram_data_end - __ewram_data_start;
|
||||
__ewram_rom_length_halfwords = (__ewram_rom_length_bytes + 1) / 2;
|
||||
|
||||
.shstrtab : {
|
||||
*(.shstrtab)
|
||||
}
|
||||
|
||||
/* debugging sections */
|
||||
/* Stabs */
|
||||
.stab 0 : { *(.stab) }
|
||||
.stabstr 0 : { *(.stabstr) }
|
||||
.stab.excl 0 : { *(.stab.excl) }
|
||||
.stab.exclstr 0 : { *(.stab.exclstr) }
|
||||
.stab.index 0 : { *(.stab.index) }
|
||||
.stab.indexstr 0 : { *(.stab.indexstr) }
|
||||
.comment 0 : { *(.comment) }
|
||||
/* DWARF 1 */
|
||||
.debug 0 : { *(.debug) }
|
||||
.line 0 : { *(.line) }
|
||||
/* GNU DWARF 1 extensions */
|
||||
.debug_srcinfo 0 : { *(.debug_srcinfo) }
|
||||
.debug_sfnames 0 : { *(.debug_sfnames) }
|
||||
/* DWARF 1.1 and DWARF 2 */
|
||||
.debug_aranges 0 : { *(.debug_aranges) }
|
||||
.debug_pubnames 0 : { *(.debug_pubnames) }
|
||||
/* DWARF 2 */
|
||||
.debug_info 0 : { *(.debug_info) }
|
||||
.debug_abbrev 0 : { *(.debug_abbrev) }
|
||||
.debug_line 0 : { *(.debug_line) }
|
||||
.debug_frame 0 : { *(.debug_frame) }
|
||||
.debug_str 0 : { *(.debug_str) }
|
||||
.debug_loc 0 : { *(.debug_loc) }
|
||||
.debug_macinfo 0 : { *(.debug_macinfo) }
|
||||
/* SGI/MIPS DWARF 2 extensions */
|
||||
.debug_weaknames 0 : { *(.debug_weaknames) }
|
||||
.debug_funcnames 0 : { *(.debug_funcnames) }
|
||||
.debug_typenames 0 : { *(.debug_typenames) }
|
||||
.debug_varnames 0 : { *(.debug_varnames) }
|
||||
|
||||
.debug_ranges 0 : { *(.debug_ranges) }
|
||||
|
||||
/* discard anything not already mentioned */
|
||||
/DISCARD/ : { *(*) }
|
||||
}
|
115
examples/amplitude/gba_mb.ld
Normal file
115
examples/amplitude/gba_mb.ld
Normal file
|
@ -0,0 +1,115 @@
|
|||
OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
|
||||
OUTPUT_ARCH(arm)
|
||||
|
||||
ENTRY(__start)
|
||||
EXTERN(__RUST_INTERRUPT_HANDLER)
|
||||
|
||||
EXTERN(__agbabi_memset)
|
||||
EXTERN(__agbabi_memcpy)
|
||||
|
||||
MEMORY {
|
||||
ewram (w!x) : ORIGIN = 0x02000000, LENGTH = 256K
|
||||
iwram (w!x) : ORIGIN = 0x03000000, LENGTH = 32K
|
||||
}
|
||||
|
||||
__text_start = ORIGIN(ewram);
|
||||
|
||||
INPUT (agb.a)
|
||||
|
||||
SECTIONS {
|
||||
. = __text_start;
|
||||
|
||||
.text : {
|
||||
KEEP(*(.crt0));
|
||||
*(.crt0 .crt0*);
|
||||
*(.text .text*);
|
||||
. = ALIGN(4);
|
||||
} > rom
|
||||
__text_end = .;
|
||||
|
||||
.rodata : {
|
||||
*(.rodata .rodata.*);
|
||||
. = ALIGN(4);
|
||||
} > ewram
|
||||
|
||||
__iwram_rom_start = .;
|
||||
.iwram : {
|
||||
__iwram_data_start = ABSOLUTE(.);
|
||||
|
||||
*(.iwram .iwram.*);
|
||||
. = ALIGN(4);
|
||||
|
||||
*(.text_iwram .text_iwram.*);
|
||||
. = ALIGN(4);
|
||||
|
||||
__iwram_data_end = ABSOLUTE(.);
|
||||
} > iwram AT>ewram
|
||||
|
||||
. = __iwram_rom_start + (__iwram_data_end - __iwram_data_start);
|
||||
|
||||
__ewram_rom_start = .;
|
||||
.ewram : {
|
||||
__ewram_data_start = ABSOLUTE(.);
|
||||
|
||||
*(.ewram .ewram.*);
|
||||
. = ALIGN(4);
|
||||
|
||||
*(.data .data.*);
|
||||
. = ALIGN(4);
|
||||
|
||||
__ewram_data_end = ABSOLUTE(.);
|
||||
} > ewram AT>ewram
|
||||
|
||||
.bss : {
|
||||
*(.bss .bss.*);
|
||||
. = ALIGN(4);
|
||||
__iwram_end = ABSOLUTE(.);
|
||||
} > iwram
|
||||
|
||||
__iwram_rom_length_bytes = __iwram_data_end - __iwram_data_start;
|
||||
__iwram_rom_length_halfwords = (__iwram_rom_length_bytes + 1) / 2;
|
||||
|
||||
__ewram_rom_length_bytes = __ewram_data_end - __ewram_data_start;
|
||||
__ewram_rom_length_halfwords = (__ewram_rom_length_bytes + 1) / 2;
|
||||
|
||||
.shstrtab : {
|
||||
*(.shstrtab)
|
||||
}
|
||||
|
||||
/* debugging sections */
|
||||
/* Stabs */
|
||||
.stab 0 : { *(.stab) }
|
||||
.stabstr 0 : { *(.stabstr) }
|
||||
.stab.excl 0 : { *(.stab.excl) }
|
||||
.stab.exclstr 0 : { *(.stab.exclstr) }
|
||||
.stab.index 0 : { *(.stab.index) }
|
||||
.stab.indexstr 0 : { *(.stab.indexstr) }
|
||||
.comment 0 : { *(.comment) }
|
||||
/* DWARF 1 */
|
||||
.debug 0 : { *(.debug) }
|
||||
.line 0 : { *(.line) }
|
||||
/* GNU DWARF 1 extensions */
|
||||
.debug_srcinfo 0 : { *(.debug_srcinfo) }
|
||||
.debug_sfnames 0 : { *(.debug_sfnames) }
|
||||
/* DWARF 1.1 and DWARF 2 */
|
||||
.debug_aranges 0 : { *(.debug_aranges) }
|
||||
.debug_pubnames 0 : { *(.debug_pubnames) }
|
||||
/* DWARF 2 */
|
||||
.debug_info 0 : { *(.debug_info) }
|
||||
.debug_abbrev 0 : { *(.debug_abbrev) }
|
||||
.debug_line 0 : { *(.debug_line) }
|
||||
.debug_frame 0 : { *(.debug_frame) }
|
||||
.debug_str 0 : { *(.debug_str) }
|
||||
.debug_loc 0 : { *(.debug_loc) }
|
||||
.debug_macinfo 0 : { *(.debug_macinfo) }
|
||||
/* SGI/MIPS DWARF 2 extensions */
|
||||
.debug_weaknames 0 : { *(.debug_weaknames) }
|
||||
.debug_funcnames 0 : { *(.debug_funcnames) }
|
||||
.debug_typenames 0 : { *(.debug_typenames) }
|
||||
.debug_varnames 0 : { *(.debug_varnames) }
|
||||
|
||||
.debug_ranges 0 : { *(.debug_ranges) }
|
||||
|
||||
/* discard anything not already mentioned */
|
||||
/DISCARD/ : { *(*) }
|
||||
}
|
BIN
examples/amplitude/gfx/circles.aseprite
Normal file
BIN
examples/amplitude/gfx/circles.aseprite
Normal file
Binary file not shown.
BIN
examples/amplitude/gfx/numbers.aseprite
Normal file
BIN
examples/amplitude/gfx/numbers.aseprite
Normal file
Binary file not shown.
BIN
examples/amplitude/gfx/saw.aseprite
Normal file
BIN
examples/amplitude/gfx/saw.aseprite
Normal file
Binary file not shown.
3
examples/amplitude/rust-toolchain.toml
Normal file
3
examples/amplitude/rust-toolchain.toml
Normal file
|
@ -0,0 +1,3 @@
|
|||
[toolchain]
|
||||
channel = "nightly"
|
||||
components = ["rust-src", "clippy", "rustfmt"]
|
373
examples/amplitude/src/lib.rs
Normal file
373
examples/amplitude/src/lib.rs
Normal file
|
@ -0,0 +1,373 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
#![cfg_attr(test, feature(custom_test_frameworks))]
|
||||
#![cfg_attr(test, reexport_test_harness_main = "test_main")]
|
||||
#![cfg_attr(test, test_runner(agb::test_runner::test_runner))]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use agb::{
|
||||
display::{
|
||||
self,
|
||||
affine::{self, AffineMatrix},
|
||||
object::{
|
||||
AffineMatrixInstance, AffineMode, Graphics, OamIterator, ObjectUnmanaged, Sprite,
|
||||
SpriteLoader, SpriteVram, Tag, TagMap,
|
||||
},
|
||||
palette16::Palette16,
|
||||
},
|
||||
fixnum::{num, Num, Vector2D},
|
||||
include_aseprite,
|
||||
input::{Button, ButtonController},
|
||||
rng,
|
||||
};
|
||||
use alloc::{boxed::Box, collections::VecDeque, vec::Vec};
|
||||
|
||||
type Number = Num<i32, 8>;
|
||||
|
||||
struct Saw {
|
||||
object: ObjectUnmanaged,
|
||||
position: Vector2D<Number>,
|
||||
angle: Number,
|
||||
rotation_speed: Number,
|
||||
}
|
||||
|
||||
enum Colour {
|
||||
Red,
|
||||
Blue,
|
||||
}
|
||||
|
||||
struct Circle {
|
||||
colour: Colour,
|
||||
position: Vector2D<Number>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct SpriteCache {
|
||||
saw: SpriteVram,
|
||||
blue: SpriteVram,
|
||||
red: SpriteVram,
|
||||
numbers: Box<[SpriteVram]>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
enum DrawDirection {
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
fn draw_number(
|
||||
mut number: u32,
|
||||
position: Vector2D<i32>,
|
||||
oam: &mut OamIterator,
|
||||
direction: DrawDirection,
|
||||
sprite_cache: &SpriteCache,
|
||||
) {
|
||||
let mut digits = Vec::new();
|
||||
if number == 0 {
|
||||
digits.push(0);
|
||||
}
|
||||
|
||||
while number != 0 {
|
||||
digits.push(number % 10);
|
||||
number /= 10;
|
||||
}
|
||||
|
||||
let mut current_position = if direction == DrawDirection::Right {
|
||||
position + (4 * (digits.len() - 1) as i32, 0).into()
|
||||
} else {
|
||||
position
|
||||
};
|
||||
|
||||
for digit in digits {
|
||||
let mut obj = ObjectUnmanaged::new(sprite_cache.numbers[digit as usize].clone());
|
||||
obj.show().set_position(current_position);
|
||||
|
||||
if let Some(slot) = oam.next() {
|
||||
slot.set(&obj);
|
||||
}
|
||||
|
||||
current_position -= (4, 0).into();
|
||||
}
|
||||
}
|
||||
|
||||
impl SpriteCache {
|
||||
fn new(loader: &mut SpriteLoader) -> Self {
|
||||
const SPRITES: &Graphics = include_aseprite!(
|
||||
"gfx/circles.aseprite",
|
||||
"gfx/saw.aseprite",
|
||||
"gfx/numbers.aseprite"
|
||||
);
|
||||
|
||||
const NUMBERS: &Tag = SPRITES.tags().get("numbers");
|
||||
const BLUE_CIRCLE: &Sprite = SPRITES.tags().get("Blue").sprite(0);
|
||||
const RED_CIRCLE: &Sprite = SPRITES.tags().get("Red").sprite(0);
|
||||
const SAW: &Sprite = SPRITES.tags().get("Saw").sprite(0);
|
||||
|
||||
Self {
|
||||
saw: loader.get_vram_sprite(SAW),
|
||||
blue: loader.get_vram_sprite(BLUE_CIRCLE),
|
||||
red: loader.get_vram_sprite(RED_CIRCLE),
|
||||
numbers: (0..10)
|
||||
.map(|x| NUMBERS.sprite(x))
|
||||
.map(|x| loader.get_vram_sprite(x))
|
||||
.collect::<Vec<_>>()
|
||||
.into_boxed_slice(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Game {
|
||||
settings: FinalisedSettings,
|
||||
circles: VecDeque<Circle>,
|
||||
saws: VecDeque<Saw>,
|
||||
head_position: Vector2D<Number>,
|
||||
phase_time: Number,
|
||||
input: ButtonController,
|
||||
energy: Number,
|
||||
frame_since_last_saw: i32,
|
||||
alive_frames: u32,
|
||||
}
|
||||
|
||||
enum GameState {
|
||||
Continue,
|
||||
Loss(u32),
|
||||
}
|
||||
|
||||
impl Game {
|
||||
fn from_settings(settings: Settings) -> Self {
|
||||
let finalised = settings.to_finalised_settings();
|
||||
|
||||
let mut circles = VecDeque::with_capacity(finalised.number_of_circles);
|
||||
for idx in 0..finalised.number_of_circles {
|
||||
circles.push_back(Circle {
|
||||
colour: Colour::Red,
|
||||
position: Vector2D::new(
|
||||
finalised.speed * idx as i32 - 4,
|
||||
settings.head_start_position.y,
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
Game {
|
||||
energy: 100.into(),
|
||||
input: agb::input::ButtonController::new(),
|
||||
settings: finalised,
|
||||
circles,
|
||||
saws: VecDeque::new(),
|
||||
head_position: settings.head_start_position,
|
||||
phase_time: 0.into(),
|
||||
frame_since_last_saw: 0,
|
||||
alive_frames: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn frame(&mut self, sprite_cache: &SpriteCache) -> GameState {
|
||||
self.input.update();
|
||||
|
||||
let (height, colour) = if self.input.is_pressed(Button::A) {
|
||||
(self.settings.wave_height_ability, Colour::Blue)
|
||||
} else {
|
||||
(self.settings.wave_height_normal, Colour::Red)
|
||||
};
|
||||
|
||||
let next_phase_time = self.phase_time + self.settings.phase_speed;
|
||||
|
||||
let this_frame_y_delta = next_phase_time.cos() - self.phase_time.cos();
|
||||
self.phase_time = next_phase_time % num!(1.);
|
||||
let this_frame_y_delta = this_frame_y_delta * height;
|
||||
self.head_position.y += this_frame_y_delta;
|
||||
|
||||
// update circles
|
||||
for circle in self.circles.iter_mut() {
|
||||
circle.position.x -= self.settings.speed;
|
||||
}
|
||||
|
||||
self.circles.pop_front();
|
||||
|
||||
// generate circle
|
||||
let circle = Circle {
|
||||
colour,
|
||||
position: self.head_position,
|
||||
};
|
||||
|
||||
self.circles.push_back(circle);
|
||||
|
||||
// update saws + check for death
|
||||
let mut saw_has_hit_head = false;
|
||||
let mut number_of_saws_to_pop = 0;
|
||||
for (idx, saw) in self.saws.iter_mut().enumerate() {
|
||||
saw.position.x -= self.settings.speed;
|
||||
if saw.position.x < (-32).into() {
|
||||
number_of_saws_to_pop = idx + 1;
|
||||
}
|
||||
saw.angle += saw.rotation_speed;
|
||||
|
||||
let angle_affine_matrix = AffineMatrix::from_rotation(saw.angle);
|
||||
|
||||
saw.object.set_affine_matrix(AffineMatrixInstance::new(
|
||||
angle_affine_matrix.to_object_wrapping(),
|
||||
));
|
||||
saw.object.show_affine(AffineMode::Affine);
|
||||
|
||||
saw.object
|
||||
.set_position(saw.position.floor() - (16, 16).into());
|
||||
|
||||
if (saw.position - self.head_position).magnitude_squared()
|
||||
< ((16 + 4) * (16 + 4)).into()
|
||||
{
|
||||
saw_has_hit_head = true;
|
||||
}
|
||||
}
|
||||
|
||||
// destroy saws
|
||||
for _ in 0..number_of_saws_to_pop {
|
||||
self.saws.pop_front();
|
||||
}
|
||||
|
||||
// create saw
|
||||
self.frame_since_last_saw -= 1;
|
||||
if self.frame_since_last_saw <= 0 {
|
||||
self.frame_since_last_saw = self.settings.frames_between_saws;
|
||||
let mut rotation_direction = rng::gen().signum();
|
||||
if rotation_direction == 0 {
|
||||
rotation_direction = 1;
|
||||
}
|
||||
|
||||
let rotation_magnitude =
|
||||
Number::from_raw(rng::gen().abs() % (1 << 8)) % num!(0.02) + num!(0.005);
|
||||
|
||||
let rotation_speed = rotation_magnitude * rotation_direction;
|
||||
let saw = Saw {
|
||||
object: ObjectUnmanaged::new(sprite_cache.saw.clone()),
|
||||
position: (300, rng::gen().rem_euclid(display::HEIGHT)).into(),
|
||||
angle: 0.into(),
|
||||
rotation_speed,
|
||||
};
|
||||
|
||||
self.saws.push_back(saw);
|
||||
}
|
||||
|
||||
self.alive_frames += 1;
|
||||
|
||||
let out_of_bounds_death = self.head_position.y.floor() < -4
|
||||
|| (self.head_position.y + 1).floor() > display::HEIGHT + 4;
|
||||
|
||||
if saw_has_hit_head || out_of_bounds_death {
|
||||
GameState::Loss(self.alive_frames)
|
||||
} else {
|
||||
GameState::Continue
|
||||
}
|
||||
}
|
||||
|
||||
fn render(&self, oam: &mut OamIterator, sprite_cache: &SpriteCache) {
|
||||
for saw in self.saws.iter() {
|
||||
if let Some(slot) = oam.next() {
|
||||
slot.set(&saw.object);
|
||||
}
|
||||
}
|
||||
|
||||
for circle in self.circles.iter() {
|
||||
if let Some(slot) = oam.next() {
|
||||
let mut object = ObjectUnmanaged::new(match circle.colour {
|
||||
Colour::Red => sprite_cache.red.clone(),
|
||||
Colour::Blue => sprite_cache.blue.clone(),
|
||||
});
|
||||
|
||||
object
|
||||
.show()
|
||||
.set_position(circle.position.floor() - (4, 4).into());
|
||||
|
||||
slot.set(&object);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Settings {
|
||||
phase_speed: Number,
|
||||
frames_between_saws: i32,
|
||||
speed: Number,
|
||||
head_start_position: Vector2D<Number>,
|
||||
wave_height_normal: Number,
|
||||
wave_height_ability: Number,
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
fn to_finalised_settings(&self) -> FinalisedSettings {
|
||||
FinalisedSettings {
|
||||
number_of_circles: ((self.head_start_position.x + 4) / self.speed + 1)
|
||||
.floor()
|
||||
.try_into()
|
||||
.expect("number should be positive"),
|
||||
speed: self.speed,
|
||||
phase_speed: self.phase_speed,
|
||||
frames_between_saws: self.frames_between_saws,
|
||||
wave_height_ability: self.wave_height_ability,
|
||||
wave_height_normal: self.wave_height_normal,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct FinalisedSettings {
|
||||
wave_height_normal: Number,
|
||||
wave_height_ability: Number,
|
||||
phase_speed: Number,
|
||||
frames_between_saws: i32,
|
||||
speed: Number,
|
||||
number_of_circles: usize,
|
||||
}
|
||||
|
||||
pub fn main(mut gba: agb::Gba) -> ! {
|
||||
let (mut unmanaged, mut sprites) = gba.display.object.get_unmanaged();
|
||||
let sprite_cache = SpriteCache::new(&mut sprites);
|
||||
|
||||
let (_background, mut vram) = gba.display.video.tiled0();
|
||||
|
||||
vram.set_background_palettes(&[Palette16::new([u16::MAX; 16])]);
|
||||
|
||||
let vblank = agb::interrupt::VBlank::get();
|
||||
|
||||
let mut max_score = 0;
|
||||
|
||||
loop {
|
||||
let mut game = Game::from_settings(Settings {
|
||||
phase_speed: num!(0.02),
|
||||
frames_between_saws: 60,
|
||||
speed: num!(1.),
|
||||
head_start_position: (40, 100).into(),
|
||||
wave_height_normal: 20.into(),
|
||||
wave_height_ability: 5.into(),
|
||||
});
|
||||
loop {
|
||||
let state = game.frame(&sprite_cache);
|
||||
if game.alive_frames > max_score {
|
||||
max_score = game.alive_frames;
|
||||
}
|
||||
vblank.wait_for_vblank();
|
||||
let oam_frame = &mut unmanaged.iter();
|
||||
draw_number(
|
||||
max_score,
|
||||
(display::WIDTH - 4, 1).into(),
|
||||
oam_frame,
|
||||
DrawDirection::Left,
|
||||
&sprite_cache,
|
||||
);
|
||||
draw_number(
|
||||
game.alive_frames,
|
||||
(1, 1).into(),
|
||||
oam_frame,
|
||||
DrawDirection::Right,
|
||||
&sprite_cache,
|
||||
);
|
||||
game.render(oam_frame, &sprite_cache);
|
||||
|
||||
if let GameState::Loss(score) = state {
|
||||
for _ in 0..30 {
|
||||
vblank.wait_for_vblank();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
10
examples/amplitude/src/main.rs
Normal file
10
examples/amplitude/src/main.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
#![cfg_attr(test, feature(custom_test_frameworks))]
|
||||
#![cfg_attr(test, reexport_test_harness_main = "test_main")]
|
||||
#![cfg_attr(test, test_runner(agb::test_runner::test_runner))]
|
||||
|
||||
#[agb::entry]
|
||||
fn main(mut gba: agb::Gba) -> ! {
|
||||
amplitude::main(gba)
|
||||
}
|
Loading…
Reference in a new issue