mirror of
https://github.com/italicsjenga/gba.git
synced 2024-12-24 03:11:29 +11:00
commit
5b327dce6f
|
@ -14,7 +14,8 @@ before_script:
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- cargo check && cargo check --release
|
- cargo check && cargo check --release
|
||||||
# at the moment we DO NOT run "cargo test" because of lang-item issues
|
# Only run a test build for the library itself
|
||||||
|
- cargo test --lib && cargo test --lib --release
|
||||||
- cd book && mdbook build
|
- cd book && mdbook build
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "gba"
|
name = "gba"
|
||||||
description = "A crate (and book) for making GBA games with Rust."
|
description = "A crate (and book) for making GBA games with Rust."
|
||||||
version = "0.2.0"
|
version = "0.3.0"
|
||||||
authors = ["Lokathor <zefria@gmail.com>", "Ketsuban"]
|
authors = ["Lokathor <zefria@gmail.com>", "Ketsuban"]
|
||||||
repository = "https://github.com/rust-console/gba"
|
repository = "https://github.com/rust-console/gba"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
@ -9,8 +9,10 @@ keywords = ["gba"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
|
|
||||||
|
publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
gba-proc-macro = "0.1.1"
|
gba-proc-macro = "0.2.1"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
lto = true
|
||||||
|
|
63
Makefile.toml
Normal file
63
Makefile.toml
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
[config]
|
||||||
|
skip_core_tasks = true
|
||||||
|
|
||||||
|
[tasks.create-target-dir]
|
||||||
|
script_runner = "@rust"
|
||||||
|
script = [
|
||||||
|
'''
|
||||||
|
fn main() {
|
||||||
|
std::fs::DirBuilder::new().recursive(true).create("./target/").unwrap();
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
]
|
||||||
|
|
||||||
|
[tasks.assemble]
|
||||||
|
dependencies = ["create-target-dir"]
|
||||||
|
command = "arm-none-eabi-as"
|
||||||
|
args = ["crt0.s", "-o", "target/crt0.o"]
|
||||||
|
|
||||||
|
[tasks.build-examples-debug]
|
||||||
|
dependencies = ["assemble"]
|
||||||
|
command = "cargo"
|
||||||
|
args = ["xbuild", "--examples", "--target", "thumbv4-none-agb.json"]
|
||||||
|
|
||||||
|
[tasks.build-examples-release]
|
||||||
|
dependencies = ["assemble"]
|
||||||
|
command = "cargo"
|
||||||
|
args = ["xbuild", "--examples", "--target", "thumbv4-none-agb.json", "--release"]
|
||||||
|
|
||||||
|
[tasks.pack-roms]
|
||||||
|
script_runner = "@rust"
|
||||||
|
script = [
|
||||||
|
'''
|
||||||
|
fn main() -> std::io::Result<()> {
|
||||||
|
for entry in std::fs::read_dir("examples/")? {
|
||||||
|
let entry = entry?;
|
||||||
|
let mut path = entry.path();
|
||||||
|
if path.is_dir() {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
path.set_extension("");
|
||||||
|
let name = path.file_name().unwrap().to_str().unwrap();
|
||||||
|
println!("{:?}", name);
|
||||||
|
std::process::Command::new("arm-none-eabi-objcopy").args(
|
||||||
|
&["-O", "binary",
|
||||||
|
&format!("target/thumbv4-none-agb/release/examples/{}",name),
|
||||||
|
&format!("target/example-{}.gba",name)])
|
||||||
|
.output().expect("failed to objcopy!");
|
||||||
|
std::process::Command::new("gbafix").args(
|
||||||
|
&[&format!("target/example-{}.gba",name)])
|
||||||
|
.output().expect("failed to gbafix!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
]
|
||||||
|
|
||||||
|
[tasks.build]
|
||||||
|
dependencies = ["build-examples-debug", "build-examples-release", "pack-roms"]
|
||||||
|
|
||||||
|
[tasks.test]
|
||||||
|
command = "cargo"
|
||||||
|
args = ["test", "--lib"]
|
|
@ -1,4 +1,9 @@
|
||||||
[![License:Apache2](https://img.shields.io/badge/License-Apache2-green.svg)](https://www.apache.org/licenses/LICENSE-2.0)
|
[![License:Apache2](https://img.shields.io/badge/License-Apache2-green.svg)](https://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
[![travis.ci](https://travis-ci.org/rust-console/gba.svg?branch=master)](https://travis-ci.org/rust-console/gba)
|
||||||
|
[![crates.io](https://img.shields.io/crates/v/gba.svg)](https://crates.io/crates/gba)
|
||||||
|
[![docs.rs](https://docs.rs/gba/badge.svg)](https://docs.rs/gba/latest/gba/)
|
||||||
|
|
||||||
|
* [![Built with cargo-make](https://sagiegurari.github.io/cargo-make/assets/badges/cargo-make.svg)](https://sagiegurari.github.io/cargo-make)
|
||||||
|
|
||||||
# gba
|
# gba
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
[book]
|
[book]
|
||||||
title = "Rust GBA Tutorials"
|
title = "Rust GBA Guide"
|
||||||
authors = ["Lokathor"]
|
authors = ["Lokathor"]
|
||||||
#description = "Rust GBA Tutorials."
|
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
build-dir = "../docs"
|
build-dir = "../docs"
|
||||||
|
|
|
@ -2,14 +2,21 @@
|
||||||
# Rust GBA Tutorials
|
# Rust GBA Tutorials
|
||||||
|
|
||||||
* [Introduction](introduction.md)
|
* [Introduction](introduction.md)
|
||||||
* [Ch 0: Development Setup](ch0/index.md)
|
* [Ch 0: Development Setup](ch00/index.md)
|
||||||
* [Ch 1: Hello GBA](ch1/index.md)
|
* [Ch 1: Hello GBA](ch01/index.md)
|
||||||
* [hello1](ch1/hello1.md)
|
* [hello1](ch01/hello1.md)
|
||||||
* [IO Registers](ch1/io_registers.md)
|
* [Volatile](ch01/volatile.md)
|
||||||
* [The Display Control Register](ch1/the_display_control_register.md)
|
* [IO Registers](ch01/io_registers.md)
|
||||||
* [Video Memory Intro](ch1/video_memory_intro.md)
|
* [The Display Control Register](ch01/the_display_control_register.md)
|
||||||
* [hello2](ch1/hello2.md)
|
* [Video Memory Intro](ch01/video_memory_intro.md)
|
||||||
* [Ch 2: User Input](ch2/index.md)
|
* [hello2](ch01/hello2.md)
|
||||||
* [The Key Input Register](ch2/the_key_input_register.md)
|
* [Ch 2: User Input](ch02/index.md)
|
||||||
* [The VCount Register](ch2/the_vcount_register.md)
|
* [The Key Input Register](ch02/the_key_input_register.md)
|
||||||
* [light_cycle](ch2/light_cycle.md)
|
* [The VCount Register](ch02/the_vcount_register.md)
|
||||||
|
* [light_cycle](ch02/light_cycle.md)
|
||||||
|
* [Ch 3: Memory and Objects](ch03/index.md)
|
||||||
|
* [GBA Memory](ch03/gba_memory.md)
|
||||||
|
* [Tiled Backgrounds](ch03/tiled_backgrounds.md)
|
||||||
|
* [Object Basics](ch03/object_basics.md)
|
||||||
|
* [GBA RNG](ch03/gba_rng.md)
|
||||||
|
* [memory_game](ch03/memory_game.md)
|
||||||
|
|
|
@ -4,54 +4,58 @@ Before you can build a GBA game you'll have to follow some special steps to
|
||||||
setup the development environment. Perhaps unfortunately, there's enough detail
|
setup the development environment. Perhaps unfortunately, there's enough detail
|
||||||
here to warrant a mini-chapter all on its own.
|
here to warrant a mini-chapter all on its own.
|
||||||
|
|
||||||
Before we begin I'd like to give a special thanks to **Ketsuban**, who is the
|
Once again, extra special thanks to **Ketsuban**, who first dove into how to
|
||||||
wizard that arranged for all of this to be able to happen and laid out the
|
make this all work with rust and then shared it with the world.
|
||||||
details of the plan to the rest of the world.
|
|
||||||
|
|
||||||
## Per System Setup
|
## Per System Setup
|
||||||
|
|
||||||
Obviously you need your computer to have a working rust installation. However,
|
Obviously you need your computer to have a [working rust
|
||||||
you'll also need to ensure that you're using a nightly toolchain. You can run
|
installation](https://rustup.rs/). However, you'll also need to ensure that
|
||||||
`rustup default nightly` to set nightly as the system wide default toolchain, or
|
you're using a nightly toolchain (we will need it for inline assembly, among
|
||||||
you can use a [toolchain
|
other potential useful features). You can run `rustup default nightly` to set
|
||||||
|
nightly as the system wide default toolchain, or you can use a [toolchain
|
||||||
file](https://github.com/rust-lang-nursery/rustup.rs#the-toolchain-file) to use
|
file](https://github.com/rust-lang-nursery/rustup.rs#the-toolchain-file) to use
|
||||||
nightly just on a specific project, but either way we'll be assuming nightly
|
nightly just on a specific project, but either way we'll be assuming the use of
|
||||||
from now on.
|
nightly from now on. You'll also need the `rust-src` component so that
|
||||||
|
`cargo-xbuild` will be able to compile the core crate for us in a bit, so run
|
||||||
|
`rustup component add rust-src`.
|
||||||
|
|
||||||
Next you need [devkitpro](https://devkitpro.org/wiki/Getting_Started). They've
|
Next, you need [devkitpro](https://devkitpro.org/wiki/Getting_Started). They've
|
||||||
got a graphical installer for Windows, and `pacman` support on Linux. We'll be
|
got a graphical installer for Windows that runs nicely, and I guess `pacman`
|
||||||
using a few of their binutils for the `arm-none-eabi` target, and we'll also be
|
support on Linux (I'm on Windows so I haven't tried the Linux install myself).
|
||||||
using some of their tools that are specific to GBA development, so _even if_ you
|
We'll be using a few of their general binutils for the `arm-none-eabi` target,
|
||||||
already have the right binutils for whatever reason, you'll still want devkitpro
|
and we'll also be using some of their tools that are specific to GBA
|
||||||
for the `gbafix` utility.
|
development, so _even if_ you already have the right binutils for whatever
|
||||||
|
reason, you'll still want devkitpro for the `gbafix` utility.
|
||||||
|
|
||||||
* On Windows you'll want something like `C:\devkitpro\devkitARM\bin` and
|
* On Windows you'll want something like `C:\devkitpro\devkitARM\bin` and
|
||||||
`C:\devkitpro\tools\bin` to be [added to your
|
`C:\devkitpro\tools\bin` to be [added to your
|
||||||
PATH](https://stackoverflow.com/q/44272416/455232), depending on where you
|
PATH](https://stackoverflow.com/q/44272416/455232), depending on where you
|
||||||
installed it to and such.
|
installed it to and such.
|
||||||
* On Linux you'll also want it to be added to your path, but if you're using
|
* On Linux you'll also want it to be added to your path, but if you're using
|
||||||
Linux I'll just assume you know how to do all that.
|
Linux I'll just assume you know how to do all that. I'm told that the default
|
||||||
|
installation path is `/opt/devkitpro/devkitARM/bin`, so look there first if
|
||||||
|
you didn't select some other place.
|
||||||
|
|
||||||
Finally, you'll need `cargo-xbuild`. Just run `cargo install cargo-xbuild` and
|
Finally, you'll need `cargo-xbuild`. Just run `cargo install cargo-xbuild` and
|
||||||
cargo will figure it all out for you.
|
cargo will figure it all out for you.
|
||||||
|
|
||||||
## Per Project Setup
|
## Per Project Setup
|
||||||
|
|
||||||
Now you'll need some particular files each time you want to start a new project.
|
Once the system wide tools are ready, you'll need some particular files each
|
||||||
You can find them in the root of the [rust-console/gba
|
time you want to start a new project. You can find them in the root of the
|
||||||
repo](https://github.com/rust-console/gba).
|
[rust-console/gba repo](https://github.com/rust-console/gba).
|
||||||
|
|
||||||
* `thumbv4-none-agb.json` describes the overall GBA to cargo-xbuild so it knows
|
* `thumbv4-none-agb.json` describes the overall GBA to cargo-xbuild (and LLVM)
|
||||||
what to do. This is actually a somewhat made up target name since there's no
|
so it knows what to do. Technically the GBA is `thumbv4-none-eabi`, but we
|
||||||
official target name. The GBA is essentially the same as a normal
|
change the `eabi` to `agb` so that we can distinguish it from other `eabi`
|
||||||
`thumbv4-none-eabi` device, but we give it the "agb" signifier so that later
|
devices when using `cfg` flags.
|
||||||
on we'll be able to use rust's `cfg` ability to allow our code to know if it's
|
|
||||||
specifically targeting a GBA or some other similar device (like an NDS).
|
|
||||||
* `crt0.s` describes some ASM startup stuff. If you have more ASM to place here
|
* `crt0.s` describes some ASM startup stuff. If you have more ASM to place here
|
||||||
later on this is where you can put it. You also need to build it into a
|
later on this is where you can put it. You also need to build it into a
|
||||||
`crt0.o` file before it can actually be used, but we'll cover that below.
|
`crt0.o` file before it can actually be used, but we'll cover that below.
|
||||||
* `linker.ld` tells the linker more critical info about the layout expectations
|
* `linker.ld` tells the linker all the critical info about the layout
|
||||||
that the GBA has about our program.
|
expectations that the GBA has about our program, and that it should also
|
||||||
|
include the `crt0.o` file with our compiled rust code.
|
||||||
|
|
||||||
## Compiling
|
## Compiling
|
||||||
|
|
||||||
|
@ -72,13 +76,13 @@ Once you've got something to build, you perform the following steps:
|
||||||
as `--release`, and options, such as `--bin foo` or `--examples`, that you'd
|
as `--release`, and options, such as `--bin foo` or `--examples`, that you'd
|
||||||
expect `cargo` to accept.
|
expect `cargo` to accept.
|
||||||
* You **can not** build and run tests this way, because they require `std`,
|
* You **can not** build and run tests this way, because they require `std`,
|
||||||
which the GBA doesn't have. You can still run some of your project's tests
|
which the GBA doesn't have. If you want you can still run some of your
|
||||||
with `cargo test`, but that builds for your local machine, so anything
|
project's tests with `cargo test --lib` or similar, but that builds for your
|
||||||
specific to the GBA (such as reading and writing registers) won't be
|
local machine, so anything specific to the GBA (such as reading and writing
|
||||||
testable that way. If you want to isolate and try out some piece code
|
registers) won't be testable that way. If you want to isolate and try out
|
||||||
running on the GBA you'll unfortunately have to make a demo for it in your
|
some piece code running on the GBA you'll unfortunately have to make a demo
|
||||||
`examples/` directory and then run the demo in an emulator and see if it
|
for it in your `examples/` directory and then run the demo in an emulator
|
||||||
does what you expect.
|
and see if it does what you expect.
|
||||||
* The file extension is important. `cargo xbuild` takes it as a flag to
|
* The file extension is important. `cargo xbuild` takes it as a flag to
|
||||||
compile dependencies with the same sysroot, so you can include crates
|
compile dependencies with the same sysroot, so you can include crates
|
||||||
normally. Well, creates that work in the GBA's limited environment, but you
|
normally. Well, creates that work in the GBA's limited environment, but you
|
||||||
|
@ -132,3 +136,6 @@ transfer to a flash cart there's a little more to do.
|
||||||
And you're finally done!
|
And you're finally done!
|
||||||
|
|
||||||
Of course, you probably want to make a script for all that, but it's up to you.
|
Of course, you probably want to make a script for all that, but it's up to you.
|
||||||
|
On our own project we have it mostly set up within a `Makefile.toml` which runs
|
||||||
|
using the [cargo-make](https://github.com/sagiegurari/cargo-make) plugin. It's
|
||||||
|
not really the best plugin, but it's what's available.
|
|
@ -1,5 +1,6 @@
|
||||||
# hello1
|
# hello1
|
||||||
|
|
||||||
|
Our first example will be a totally minimal, full magic number crazy town.
|
||||||
Ready? Here goes:
|
Ready? Here goes:
|
||||||
|
|
||||||
`hello1.rs`
|
`hello1.rs`
|
||||||
|
@ -8,7 +9,6 @@ Ready? Here goes:
|
||||||
#![feature(start)]
|
#![feature(start)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
#[cfg(not(test))]
|
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||||
loop {}
|
loop {}
|
||||||
|
@ -26,17 +26,18 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Throw that into your project, build the program (as described back in Chapter
|
Throw that into your project skeleton, build the program (as described back in
|
||||||
0), and give it a run. You should see a red, green, and blue dot close-ish to
|
Chapter 0), and give it a run in your emulator. You should see a red, green, and
|
||||||
the middle of the screen. If you don't, something already went wrong. Double
|
blue dot close-ish to the middle of the screen. If you don't, something already
|
||||||
check things, phone a friend, write your senators, try asking Ketsuban on the
|
went wrong. Double check things, phone a friend, write your senators, try asking
|
||||||
[Rust Community Discord](https://discordapp.com/invite/aVESxV8), until you're
|
Ketsuban on the [Rust Community Discord](https://discordapp.com/invite/aVESxV8),
|
||||||
able to get your three dots going.
|
until you're able to get your three dots going.
|
||||||
|
|
||||||
## Explaining hello1
|
## A basic hello1 explanation
|
||||||
|
|
||||||
So, what just happened? Even if you're used to Rust that might look pretty
|
So, what just happened? Even if you're used to Rust that might look pretty
|
||||||
strange. We'll go over each part extra carefully.
|
strange. We'll go over most of the little parts right here, and then bigger
|
||||||
|
parts will get their own sections.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
#![feature(start)]
|
#![feature(start)]
|
||||||
|
@ -60,7 +61,6 @@ There's no standard library available on the GBA, so we'll have to live a core
|
||||||
only life.
|
only life.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
#[cfg(not(test))]
|
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||||
loop {}
|
loop {}
|
||||||
|
@ -74,10 +74,6 @@ However, right now we don't know how to get any sort of message out to the user
|
||||||
so... we do nothing at all. We _can't even return_ from here, so we just sit in
|
so... we do nothing at all. We _can't even return_ from here, so we just sit in
|
||||||
an infinite loop. The player will have to reset the universe from the outside.
|
an infinite loop. The player will have to reset the universe from the outside.
|
||||||
|
|
||||||
The `#[cfg(not(test))]` part makes this item only exist in the program when
|
|
||||||
we're _not_ in a test build. This is so that `cargo test` and such work right as
|
|
||||||
much as possible.
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
#[start]
|
#[start]
|
||||||
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||||
|
@ -148,46 +144,5 @@ magic numbers mean or do.
|
||||||
* `0x06000000` is the start of Video RAM.
|
* `0x06000000` is the start of Video RAM.
|
||||||
|
|
||||||
So we write some magic to the display control register once, then we write some
|
So we write some magic to the display control register once, then we write some
|
||||||
other magic to three locations of magic to the Video RAM. We get three dots,
|
other magic to three magic locations in the Video RAM. Somehow that shows three
|
||||||
each in their own location... so that second part makes sense at least.
|
dots. Gotta read on to find out why!
|
||||||
|
|
||||||
We'll get into the magic number details in the other sections of this chapter.
|
|
||||||
|
|
||||||
## Sidebar: Volatile
|
|
||||||
|
|
||||||
We'll get into what all that is in a moment, but first let's ask ourselves: Why
|
|
||||||
are we doing _volatile_ writes? You've probably never used it before at all.
|
|
||||||
What is volatile anyway?
|
|
||||||
|
|
||||||
Well, the optimizer is pretty aggressive some of the time, and so it'll skip
|
|
||||||
reads and writes when it thinks can. Like if you write to a pointer once, and
|
|
||||||
then again a moment later, and it didn't see any other reads in between, it'll
|
|
||||||
think that it can just skip doing that first write since it'll get overwritten
|
|
||||||
anyway. Sometimes that's right, but sometimes it's wrong.
|
|
||||||
|
|
||||||
Marking a read or write as _volatile_ tells the compiler that it really must do
|
|
||||||
that action, and in the exact order that we wrote it out. It says that there
|
|
||||||
might even be special hardware side effects going on that the compiler isn't
|
|
||||||
aware of. In this case, the write to the display control register sets a video
|
|
||||||
mode, and the writes to the Video RAM set pixels that will show up on the
|
|
||||||
screen.
|
|
||||||
|
|
||||||
Similar to "atomic" operations you might have heard about, all volatile
|
|
||||||
operations are enforced to happen in the exact order that you specify them, but
|
|
||||||
only relative to other volatile operations. So something like
|
|
||||||
|
|
||||||
```rust
|
|
||||||
c.volatile_write(5);
|
|
||||||
a += b;
|
|
||||||
d.volatile_write(7);
|
|
||||||
```
|
|
||||||
|
|
||||||
might end up changing `a` either before or after the change to `c` (since the
|
|
||||||
value of `a` doesn't affect the write to `c`), but the write to `d` will
|
|
||||||
_always_ happen after the write to `c` even though the compiler doesn't see any
|
|
||||||
direct data dependency there.
|
|
||||||
|
|
||||||
If you ever use volatile stuff on other platforms it's important to note that
|
|
||||||
volatile doesn't make things thread-safe, you still need atomic for that.
|
|
||||||
However, the GBA doesn't have threads, so we don't have to worry about thread
|
|
||||||
safety concerns.
|
|
|
@ -8,7 +8,6 @@ Okay so let's have a look again:
|
||||||
#![feature(start)]
|
#![feature(start)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
#[cfg(not(test))]
|
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||||
loop {}
|
loop {}
|
||||||
|
@ -28,10 +27,11 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||||
|
|
||||||
Now let's clean this up so that it's clearer what's going on.
|
Now let's clean this up so that it's clearer what's going on.
|
||||||
|
|
||||||
First we'll label that display control stuff:
|
First we'll label that display control stuff, including using the `VolatilePtr`
|
||||||
|
type from the volatile explanation:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
pub const DISPCNT: *mut u16 = 0x04000000 as *mut u16;
|
pub const DISPCNT: VolatilePtr<u16> = VolatilePtr(0x04000000 as *mut u16);
|
||||||
pub const MODE3: u16 = 3;
|
pub const MODE3: u16 = 3;
|
||||||
pub const BG2: u16 = 0b100_0000_0000;
|
pub const BG2: u16 = 0b100_0000_0000;
|
||||||
```
|
```
|
||||||
|
@ -43,9 +43,12 @@ pub const VRAM: usize = 0x06000000;
|
||||||
pub const SCREEN_WIDTH: isize = 240;
|
pub const SCREEN_WIDTH: isize = 240;
|
||||||
```
|
```
|
||||||
|
|
||||||
And then we want a small helper function for putting together a color value.
|
Note that VRAM has to be interpreted in different ways depending on mode, so we
|
||||||
|
just leave it as `usize` and we'll cast it into the right form closer to the
|
||||||
|
actual use.
|
||||||
|
|
||||||
Happily, this one can even be declared as a const function. At the time of
|
Next we want a small helper function for putting together a color value.
|
||||||
|
Happily, this one can even be declared as a `const` function. At the time of
|
||||||
writing, we've got the "minimal const fn" support in nightly. It really is quite
|
writing, we've got the "minimal const fn" support in nightly. It really is quite
|
||||||
limited, but I'm happy to let rustc and LLVM pre-compute as much as they can
|
limited, but I'm happy to let rustc and LLVM pre-compute as much as they can
|
||||||
when it comes to the GBA's tiny CPU.
|
when it comes to the GBA's tiny CPU.
|
||||||
|
@ -62,7 +65,7 @@ usually helps you think about it a lot better.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
pub unsafe fn mode3_pixel(col: isize, row: isize, color: u16) {
|
pub unsafe fn mode3_pixel(col: isize, row: isize, color: u16) {
|
||||||
(VRAM as *mut u16).offset(col + row * SCREEN_WIDTH).write_volatile(color);
|
VolatilePtr(VRAM as *mut u16).offset(col + row * SCREEN_WIDTH).write(color);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -74,7 +77,6 @@ So now we've got this:
|
||||||
#![feature(start)]
|
#![feature(start)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
#[cfg(not(test))]
|
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||||
loop {}
|
loop {}
|
||||||
|
@ -83,7 +85,7 @@ fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||||
#[start]
|
#[start]
|
||||||
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||||
unsafe {
|
unsafe {
|
||||||
DISPCNT.write_volatile(MODE3 | BG2);
|
DISPCNT.write(MODE3 | BG2);
|
||||||
mode3_pixel(120, 80, rgb16(31, 0, 0));
|
mode3_pixel(120, 80, rgb16(31, 0, 0));
|
||||||
mode3_pixel(136, 80, rgb16(0, 31, 0));
|
mode3_pixel(136, 80, rgb16(0, 31, 0));
|
||||||
mode3_pixel(120, 96, rgb16(0, 0, 31));
|
mode3_pixel(120, 96, rgb16(0, 0, 31));
|
||||||
|
@ -91,7 +93,22 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const DISPCNT: *mut u16 = 0x04000000 as *mut u16;
|
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct VolatilePtr<T>(pub *mut T);
|
||||||
|
impl<T> VolatilePtr<T> {
|
||||||
|
pub unsafe fn read(&self) -> T {
|
||||||
|
core::ptr::read_volatile(self.0)
|
||||||
|
}
|
||||||
|
pub unsafe fn write(&self, data: T) {
|
||||||
|
core::ptr::write_volatile(self.0, data);
|
||||||
|
}
|
||||||
|
pub unsafe fn offset(self, count: isize) -> Self {
|
||||||
|
VolatilePtr(self.0.wrapping_offset(count))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const DISPCNT: VolatilePtr<u16> = VolatilePtr(0x04000000 as *mut u16);
|
||||||
pub const MODE3: u16 = 3;
|
pub const MODE3: u16 = 3;
|
||||||
pub const BG2: u16 = 0b100_0000_0000;
|
pub const BG2: u16 = 0b100_0000_0000;
|
||||||
|
|
||||||
|
@ -103,12 +120,13 @@ pub const fn rgb16(red: u16, green: u16, blue: u16) -> u16 {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn mode3_pixel(col: isize, row: isize, color: u16) {
|
pub unsafe fn mode3_pixel(col: isize, row: isize, color: u16) {
|
||||||
(VRAM as *mut u16).offset(col + row * SCREEN_WIDTH).write_volatile(color);
|
VolatilePtr(VRAM as *mut u16).offset(col + row * SCREEN_WIDTH).write(color);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Exact same program that we started with, but much easier to read.
|
Exact same program that we started with, but much easier to read.
|
||||||
|
|
||||||
Of course, in the full `gba` crate that this book is a part of we have these and
|
Of course, in the full `gba` crate that this book is a part of we have these and
|
||||||
other elements all labeled and sorted out for you. Still, for educational
|
other elements all labeled and sorted out for you (not identically, but
|
||||||
purposes it's often best to do it yourself at least once.
|
similarly). Still, for educational purposes it's often best to do it yourself at
|
||||||
|
least once.
|
|
@ -107,6 +107,3 @@ First let's [convert that to
|
||||||
binary](https://www.wolframalpha.com/input/?i=0x0403), and we get
|
binary](https://www.wolframalpha.com/input/?i=0x0403), and we get
|
||||||
`0b100_0000_0011`. So, that's setting Mode 3 with background 2 enabled and
|
`0b100_0000_0011`. So, that's setting Mode 3 with background 2 enabled and
|
||||||
nothing else special.
|
nothing else special.
|
||||||
|
|
||||||
However, I think we can do better than that. This is a prime target for more
|
|
||||||
newtyping as we attempt a `hello2` program.
|
|
|
@ -106,8 +106,8 @@ So at pixels `(120,80)`, `(136,80)`, and `(120,96)` we write three values. Once
|
||||||
again we probably need to [convert them](https://www.wolframalpha.com/) into
|
again we probably need to [convert them](https://www.wolframalpha.com/) into
|
||||||
binary to make sense of it.
|
binary to make sense of it.
|
||||||
|
|
||||||
* 0x001F: 0b11111
|
* 0x001F: 0b0_00000_00000_11111
|
||||||
* 0x03E0: 0b11111_00000
|
* 0x03E0: 0b0_00000_11111_00000
|
||||||
* 0x7C00: 0b11111_00000_00000
|
* 0x7C00: 0b0_11111_00000_00000
|
||||||
|
|
||||||
Ah, of course, a red pixel, a green pixel, and a blue pixel.
|
Ah, of course, a red pixel, a green pixel, and a blue pixel.
|
70
book/src/ch01/volatile.md
Normal file
70
book/src/ch01/volatile.md
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
# Volatile
|
||||||
|
|
||||||
|
Before we focus on what the numbers mean, first let's ask ourselves: Why are we
|
||||||
|
doing _volatile_ writes? You've probably never used that keywords before at all.
|
||||||
|
What _is_ volatile anyway?
|
||||||
|
|
||||||
|
Well, the optimizer is pretty aggressive, and so it'll skip reads and writes
|
||||||
|
when it thinks can. Like if you write to a pointer once, and then again a moment
|
||||||
|
later, and it didn't see any other reads in between, it'll think that it can
|
||||||
|
just skip doing that first write since it'll get overwritten anyway. Sometimes
|
||||||
|
that's correct, but sometimes it's not.
|
||||||
|
|
||||||
|
Marking a read or write as _volatile_ tells the compiler that it really must do
|
||||||
|
that action, and in the exact order that we wrote it out. It says that there
|
||||||
|
might even be special hardware side effects going on that the compiler isn't
|
||||||
|
aware of. In this case, the write to the display control register sets a video
|
||||||
|
mode, and the writes to the Video RAM set pixels that will show up on the
|
||||||
|
screen.
|
||||||
|
|
||||||
|
Similar to "atomic" operations you might have heard about, all volatile
|
||||||
|
operations are enforced to happen in the exact order that you specify them, but
|
||||||
|
only relative to other volatile operations. So something like
|
||||||
|
|
||||||
|
```rust
|
||||||
|
c.write_volatile(5);
|
||||||
|
a += b;
|
||||||
|
d.write_volatile(7);
|
||||||
|
```
|
||||||
|
|
||||||
|
might end up changing `a` either before or after the change to `c` (since the
|
||||||
|
value of `a` doesn't affect the write to `c`), but the write to `d` will
|
||||||
|
_always_ happen after the write to `c`, even though the compiler doesn't see any
|
||||||
|
direct data dependency there.
|
||||||
|
|
||||||
|
If you ever go on to use volatile stuff on other platforms it's important to
|
||||||
|
note that volatile doesn't make things thread-safe, you still need atomic for
|
||||||
|
that. However, the GBA doesn't have threads, so we don't have to worry about
|
||||||
|
those sorts of thread safety concerns (there's interrupts, but that's another
|
||||||
|
matter).
|
||||||
|
|
||||||
|
## Volatile by default
|
||||||
|
|
||||||
|
Of course, writing out `volatile_write` every time is more than we wanna do.
|
||||||
|
There's clarity and then there's excessive. This is a chance to write our first
|
||||||
|
[newtype](https://doc.rust-lang.org/1.0.0/style/features/types/newtype.html).
|
||||||
|
Basically a type that's got the exact same binary representation as some other
|
||||||
|
type, but new methods and trait implementations.
|
||||||
|
|
||||||
|
We want a `*mut T` that's volatile by default, and also when we offset it...
|
||||||
|
well the verdict is slightly unclear on how `offset` vs `wrapping_offset` work
|
||||||
|
when you're using pointers that you made up out of nowhere. I've asked the
|
||||||
|
experts and they genuinely weren't sure, so we'll make an `offset` method that
|
||||||
|
does a `wrapping_offset` just to be careful.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct VolatilePtr<T>(pub *mut T);
|
||||||
|
impl<T> VolatilePtr<T> {
|
||||||
|
pub unsafe fn read(&self) -> T {
|
||||||
|
core::ptr::read_volatile(self.0)
|
||||||
|
}
|
||||||
|
pub unsafe fn write(&self, data: T) {
|
||||||
|
core::ptr::write_volatile(self.0, data);
|
||||||
|
}
|
||||||
|
pub unsafe fn offset(self, count: isize) -> Self {
|
||||||
|
VolatilePtr(self.0.wrapping_offset(count))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
|
@ -16,21 +16,21 @@ We need some better drawing operations this time around.
|
||||||
pub unsafe fn mode3_clear_screen(color: u16) {
|
pub unsafe fn mode3_clear_screen(color: u16) {
|
||||||
let color = color as u32;
|
let color = color as u32;
|
||||||
let bulk_color = color << 16 | color;
|
let bulk_color = color << 16 | color;
|
||||||
let mut ptr = VRAM as *mut u32;
|
let mut ptr = VolatilePtr(VRAM as *mut u32);
|
||||||
for _ in 0..SCREEN_HEIGHT {
|
for _ in 0..SCREEN_HEIGHT {
|
||||||
for _ in 0..(SCREEN_WIDTH / 2) {
|
for _ in 0..(SCREEN_WIDTH / 2) {
|
||||||
ptr.write_volatile(bulk_color);
|
ptr.write(bulk_color);
|
||||||
ptr = ptr.offset(1);
|
ptr = ptr.offset(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn mode3_draw_pixel(col: isize, row: isize, color: u16) {
|
pub unsafe fn mode3_draw_pixel(col: isize, row: isize, color: u16) {
|
||||||
(VRAM as *mut u16).offset(col + row * SCREEN_WIDTH).write_volatile(color);
|
VolatilePtr(VRAM as *mut u16).offset(col + row * SCREEN_WIDTH).write(color);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn mode3_read_pixel(col: isize, row: isize) -> u16 {
|
pub unsafe fn mode3_read_pixel(col: isize, row: isize) -> u16 {
|
||||||
(VRAM as *mut u16).offset(col + row * SCREEN_WIDTH).read_volatile()
|
VolatilePtr(VRAM as *mut u16).offset(col + row * SCREEN_WIDTH).read()
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ Now we just have to fill in the main function:
|
||||||
#[start]
|
#[start]
|
||||||
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||||
unsafe {
|
unsafe {
|
||||||
DISPCNT.write_volatile(MODE3 | BG2);
|
DISPCNT.write(MODE3 | BG2);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut px = SCREEN_WIDTH / 2;
|
let mut px = SCREEN_WIDTH / 2;
|
||||||
|
@ -55,7 +55,7 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// read the input for this frame
|
// read the input for this frame
|
||||||
let this_frame_keys = read_key_input();
|
let this_frame_keys = key_input();
|
||||||
|
|
||||||
// adjust game state and wait for vblank
|
// adjust game state and wait for vblank
|
||||||
px += 2 * this_frame_keys.column_direction() as isize;
|
px += 2 * this_frame_keys.column_direction() as isize;
|
||||||
|
@ -113,7 +113,23 @@ it's not black that means we've been here before and the player has crashed into
|
||||||
their own line. In this case, we reset the game without moving them to a new
|
their own line. In this case, we reset the game without moving them to a new
|
||||||
location.
|
location.
|
||||||
|
|
||||||
Finally, if the player is in bounds and they haven't crashed, we write their color into memory at this position.
|
Finally, if the player is in bounds and they haven't crashed, we write their
|
||||||
|
color into memory at this position.
|
||||||
|
|
||||||
Regardless of how it worked out, we hold here until vdraw starts before going to
|
Regardless of how it worked out, we hold here until vdraw starts before going to
|
||||||
the next loop.
|
the next loop. That's all there is to it.
|
||||||
|
|
||||||
|
## The gba crate doesn't quite work like this
|
||||||
|
|
||||||
|
Once again, as with the `hello1` and `hello2` examples, the `gba` crate covers
|
||||||
|
much of this same ground as our example here, but in slightly different ways.
|
||||||
|
|
||||||
|
Better organization and abstractions are usually only realized once you've used
|
||||||
|
more of the whole thing you're trying to work with. If we want to have a crate
|
||||||
|
where the whole thing is well integrated with itself, then the examples would
|
||||||
|
also end up having to explain about things we haven't really touched on much
|
||||||
|
yet. It becomes a lot harder to teach.
|
||||||
|
|
||||||
|
So, going forward, we will continue to teach concepts and build examples that
|
||||||
|
don't directly depend on the `gba` crate. This allows the crate to freely grow
|
||||||
|
without all the past examples becoming a great inertia upon it.
|
|
@ -56,15 +56,15 @@ a `u16` and then wrap that in our newtype which will implement methods for
|
||||||
reading and writing the key bits.
|
reading and writing the key bits.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
pub const KEYINPUT: *mut u16 = 0x400_0130 as *mut u16;
|
pub const KEYINPUT: VolatilePtr<u16> = VolatilePtr(0x400_0130 as *mut u16);
|
||||||
|
|
||||||
/// A newtype over the key input state of the GBA.
|
/// A newtype over the key input state of the GBA.
|
||||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct KeyInputSetting(u16);
|
pub struct KeyInputSetting(u16);
|
||||||
|
|
||||||
pub fn read_key_input() -> KeyInputSetting {
|
pub fn key_input() -> KeyInputSetting {
|
||||||
unsafe { KeyInputSetting(KEYINPUT.read_volatile()) }
|
unsafe { KeyInputSetting(KEYINPUT.read()) }
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -93,11 +93,13 @@ folded and refolded by the compiler, but we can just check.
|
||||||
It turns out that the `!=0` test is 4 instructions and the `==0` test is 6
|
It turns out that the `!=0` test is 4 instructions and the `==0` test is 6
|
||||||
instructions. Since we want to get savings where we can, and we'll probably
|
instructions. Since we want to get savings where we can, and we'll probably
|
||||||
check the keys of an input often enough, we'll just always use a `!=0` test and
|
check the keys of an input often enough, we'll just always use a `!=0` test and
|
||||||
then adjust how we initially read the register to compensate.
|
then adjust how we initially read the register to compensate. By using xor with
|
||||||
|
a mask for only the 10 used bits we can flip the "low when pressed" values so
|
||||||
|
that the entire result has active bits in all positions where a key is pressed.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
pub fn read_key_input() -> KeyInputSetting {
|
pub fn key_input() -> KeyInputSetting {
|
||||||
unsafe { KeyInputSetting(KEYINPUT.read_volatile() ^ 0b1111_1111_1111_1111) }
|
unsafe { KeyInputSetting(KEYINPUT.read_volatile() ^ 0b0000_0011_1111_1111) }
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
|
@ -46,10 +46,10 @@ quickly during the blank period.
|
||||||
So first we want a way to check the vcount value at all:
|
So first we want a way to check the vcount value at all:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
pub const VCOUNT: *mut u16 = 0x0400_0006 as *mut u16;
|
pub const VCOUNT: VolatilePtr<u16> = VolatilePtr(0x0400_0006 as *mut u16);
|
||||||
|
|
||||||
pub fn read_vcount() -> u16 {
|
pub fn vcount() -> u16 {
|
||||||
unsafe { VCOUNT.read_volatile() }
|
unsafe { VCOUNT.read() }
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -59,11 +59,11 @@ Then we want two little helper functions to wait until VBlank and vdraw.
|
||||||
pub const SCREEN_HEIGHT: isize = 160;
|
pub const SCREEN_HEIGHT: isize = 160;
|
||||||
|
|
||||||
pub fn wait_until_vblank() {
|
pub fn wait_until_vblank() {
|
||||||
while read_vcount() < SCREEN_HEIGHT as u16 {}
|
while vcount() < SCREEN_HEIGHT as u16 {}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wait_until_vdraw() {
|
pub fn wait_until_vdraw() {
|
||||||
while read_vcount() >= SCREEN_HEIGHT as u16 {}
|
while vcount() >= SCREEN_HEIGHT as u16 {}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
237
book/src/ch03/gba_memory.md
Normal file
237
book/src/ch03/gba_memory.md
Normal file
|
@ -0,0 +1,237 @@
|
||||||
|
# GBA Memory
|
||||||
|
|
||||||
|
The [GBA Memory Map](http://problemkaputt.de/gbatek.htm#gbamemorymap) has
|
||||||
|
several memory portions to it, each with their own little differences. Most of
|
||||||
|
the memory has pre-determined use according to the hardware, but there is also
|
||||||
|
space for games to use as a scratch pad in whatever way the game sees fit.
|
||||||
|
|
||||||
|
The memory ranges listed here are _inclusive_, so they end with a lot of `F`s
|
||||||
|
and `E`s.
|
||||||
|
|
||||||
|
We've talked about volatile memory before, but just as a reminder I'll say that
|
||||||
|
all of the memory we'll talk about here should be accessed with volatile with
|
||||||
|
two exceptions:
|
||||||
|
|
||||||
|
1) Work RAM (both internal and external) can be used normally, and if the
|
||||||
|
compiler is able to totally elide any reads and writes that's okay.
|
||||||
|
2) However, if you set aside any space in Work RAM where an interrupt will
|
||||||
|
communicate with the main program then that specific location will have to
|
||||||
|
keep using volatile access, since the compiler never knows when an interrupt
|
||||||
|
will actually happen.
|
||||||
|
|
||||||
|
## BIOS / System ROM
|
||||||
|
|
||||||
|
* `0x0` to `0x3FFF` (16k)
|
||||||
|
|
||||||
|
This is special memory for the BIOS. It is "read-only", but even then it's only
|
||||||
|
accessible when the program counter is pointing into the BIOS region. At all
|
||||||
|
other times you get a [garbage
|
||||||
|
value](http://problemkaputt.de/gbatek.htm#gbaunpredictablethings) back when you
|
||||||
|
try to read out of the BIOS.
|
||||||
|
|
||||||
|
## External Work RAM / EWRAM
|
||||||
|
|
||||||
|
* `0x2000000` to `0x203FFFF` (256k)
|
||||||
|
|
||||||
|
This is a big pile of space, the use of which is up to each game. However, the
|
||||||
|
external work ram has only a 16-bit bus (if you read/write a 32-bit value it
|
||||||
|
silently breaks it up into two 16-bit operations) and also 2 wait cycles (extra
|
||||||
|
CPU cycles that you have to expend _per 16-bit bus use_).
|
||||||
|
|
||||||
|
In other words, we should think of EWRAM as if it was "heap space" in a normal
|
||||||
|
application. You can take the time to go store something within EWRAM, or to
|
||||||
|
load it out of EWRAM, but you should always avoid doing a critical computation
|
||||||
|
on values in EWRAM. It's a bit of a pain, but if you wanna be speedy and you
|
||||||
|
have more than just one manipulation that you want to do, you should pull the
|
||||||
|
value into a local variable, do all of your manipulations, and then push it back
|
||||||
|
out at the end.
|
||||||
|
|
||||||
|
## Internal Work RAM / IWRAM
|
||||||
|
|
||||||
|
* `0x3000000` to `0x3007FFF` (32k)
|
||||||
|
|
||||||
|
This is a smaller pile of space, but it has a 32-bit bus and no wait.
|
||||||
|
|
||||||
|
By default, `0x3007F00` to `0x3007FFF` is reserved for interrupt and BIOS use.
|
||||||
|
The rest of it is totally up to you. The user's stack space starts at
|
||||||
|
`0x3007F00` and proceeds _down_ from there. In other words, if you start your
|
||||||
|
own customized IWRAM use at `0x3000000` and go up, eventually you might hit your
|
||||||
|
stack. However, most reasonable uses won't actually cause a memory collision.
|
||||||
|
It's just something you should know about if you're using a ton of stack or
|
||||||
|
IWRAM and then get problems.
|
||||||
|
|
||||||
|
## IO Registers
|
||||||
|
|
||||||
|
* `0x4000000` to `0x40003FE`
|
||||||
|
|
||||||
|
We've touched upon a few of these so far, and we'll get to more later. At the
|
||||||
|
moment it is enough to say that, as you might have guessed, all of them live in
|
||||||
|
this region. Each individual register is a `u16` or `u32` and they control all
|
||||||
|
sorts of things. We'll actually be talking about some more of them in this very
|
||||||
|
chapter, because that's how we'll control some of the background and object
|
||||||
|
stuff.
|
||||||
|
|
||||||
|
## Palette RAM / PALRAM
|
||||||
|
|
||||||
|
* `0x5000000` to `0x50003FF` (1k)
|
||||||
|
|
||||||
|
Palette RAM has a 16-bit bus, which isn't really a problem because it
|
||||||
|
conceptually just holds `u16` values. There's no automatic wait state, but if
|
||||||
|
you try to access the same location that the display controller is accessing you
|
||||||
|
get bumped by 1 cycle. Since the display controller can use the palette ram any
|
||||||
|
number of times per scanline it's basically impossible to predict if you'll have
|
||||||
|
to do a wait or not during VDraw. During VBlank you won't have any wait of
|
||||||
|
course.
|
||||||
|
|
||||||
|
PALRAM is among the memory where there's weirdness if you try to write just one
|
||||||
|
byte: if you try to write just 1 byte, it writes that byte into _both_ parts of
|
||||||
|
the larger 16-bit location. This doesn't really affect us much with PALRAM,
|
||||||
|
because palette values are all supposed to be `u16` anyway.
|
||||||
|
|
||||||
|
The palette memory actually contains not one, but _two_ sets of palettes. First
|
||||||
|
there's 256 entries for the background palette data (starting at `0x5000000`),
|
||||||
|
and then there's 256 entries for object palette data (starting at `0x5000200`).
|
||||||
|
|
||||||
|
The GBA also has two modes for palette access: 8-bits-per-pixel (8bpp) and
|
||||||
|
4-bits-per-pixel (4bpp).
|
||||||
|
|
||||||
|
* In 8bpp mode an (8-bit) palette index value within a background or sprite
|
||||||
|
simply indexes directly into the 256 slots for that type of thing.
|
||||||
|
* In 4bpp mode a (4-bit) palette index value within a background or sprite
|
||||||
|
specifies an index within a particular "palbank" (16 palette entries each),
|
||||||
|
and then a _separate_ setting outside of the graphical data determines which
|
||||||
|
palbank is to be used for that background or object (the screen entry data for
|
||||||
|
backgrounds, and the object attributes for objects).
|
||||||
|
|
||||||
|
## Video RAM / VRAM
|
||||||
|
|
||||||
|
* `0x6000000` to `0x6017FFF` (96k)
|
||||||
|
|
||||||
|
We've used this before! VRAM has a 16-bit bus and no wait. However, the same as
|
||||||
|
with PALRAM, the "you might have to wait if the display controller is looking at
|
||||||
|
it" rule applies here.
|
||||||
|
|
||||||
|
Unfortunately there's not much more exact detail that can be given about VRAM.
|
||||||
|
The use of the memory depends on the video mode that you're using.
|
||||||
|
|
||||||
|
One general detail of note is that you can't write individual bytes to any part
|
||||||
|
of VRAM. Depending on mode and location, you'll either get your bytes doubled
|
||||||
|
into both the upper and lower parts of the 16-bit location targeted, or you
|
||||||
|
won't even affect the memory. This usually isn't a big deal, except in two
|
||||||
|
situations:
|
||||||
|
|
||||||
|
* In Mode 4, if you want to change just 1 pixel, you'll have to be very careful
|
||||||
|
to read the old `u16`, overwrite just the byte you wanted to change, and then
|
||||||
|
write that back.
|
||||||
|
* In any display mode, avoid using `memcopy` to place things into VRAM.
|
||||||
|
It's written to be byte oriented, and only does 32-bit transfers under select
|
||||||
|
conditions. The rest of the time it'll copy one byte at a time and you'll get
|
||||||
|
either garbage or nothing at all.
|
||||||
|
|
||||||
|
## Object Attribute Memory / OAM
|
||||||
|
|
||||||
|
* `0x7000000` to `0x70003FF` (1k)
|
||||||
|
|
||||||
|
The Object Attribute Memory has a 32-bit bus and no default wait, but suffers
|
||||||
|
from the "you might have to wait if the display controller is looking at it"
|
||||||
|
rule. You cannot write individual bytes to OAM at all, but that's not really a
|
||||||
|
problem because all the fields of the data types within OAM are either `i16` or
|
||||||
|
`u16` anyway.
|
||||||
|
|
||||||
|
Object attribute memory is the wildest yet: it conceptually contains two types
|
||||||
|
of things, but they're _interlaced_ with each other all the way through.
|
||||||
|
|
||||||
|
Now, [GBATEK](http://problemkaputt.de/gbatek.htm#lcdobjoamattributes) and
|
||||||
|
[CowByte](https://www.cs.rit.edu/~tjh8300/CowBite/CowBiteSpec.htm#OAM%20(sprites))
|
||||||
|
doesn't quite give names to the two data types, though
|
||||||
|
[TONC](https://www.coranac.com/tonc/text/regobj.htm#sec-oam) calls them
|
||||||
|
`OBJ_ATTR` and `OBJ_AFFINE`. We'll give them Rust names of course. In Rust terms
|
||||||
|
their layout would look like this:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct ObjectAttribute {
|
||||||
|
attr0: u16,
|
||||||
|
attr1: u16,
|
||||||
|
attr2: u16,
|
||||||
|
filler: i16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct AffineMatrix {
|
||||||
|
filler0: [u16; 3],
|
||||||
|
pa: i16,
|
||||||
|
filler1: [u16; 3],
|
||||||
|
pb: i16,
|
||||||
|
filler2: [u16; 3],
|
||||||
|
pc: i16,
|
||||||
|
filler3: [u16; 3],
|
||||||
|
pd: i16,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
(Note: the `#[repr(C)]` part just means that Rust must lay out the data exactly
|
||||||
|
in the order we specify, which otherwise it is not required to do).
|
||||||
|
|
||||||
|
So, we've got 1024 bytes in OAM and each `ObjectAttribute` value is 8 bytes, so
|
||||||
|
naturally we can support up to 128 objects.
|
||||||
|
|
||||||
|
_At the same time_, we've got 1024 bytes in OAM and each `AffineMatrix` is 32
|
||||||
|
bytes, so we can have 32 of them.
|
||||||
|
|
||||||
|
But, as I said, these things are all _interlaced_ with each other. See how
|
||||||
|
there's "filler" fields in each struct? If we imagine the OAM as being just an
|
||||||
|
array of one type or the other, indexes 0/1/2/3 of the `ObjectAttribute` array
|
||||||
|
would line up with index 0 of the `AffineMatrix` array. It's kinda weird, but
|
||||||
|
that's just how it works. When we setup functions to read and write these values
|
||||||
|
we'll have to be careful with how we do it. We probably _won't_ want to use
|
||||||
|
those representations above, at least not with the `AffineMatrix` type, because
|
||||||
|
they're quite wasteful if you want to store just object attributes or just
|
||||||
|
affine matrices.
|
||||||
|
|
||||||
|
## Game Pak ROM / Flash ROM
|
||||||
|
|
||||||
|
* `0x8000000` to `0x9FFFFFF` (wait 0)
|
||||||
|
* `0xA000000` to `0xBFFFFFF` (wait 1)
|
||||||
|
* `0xC000000` to `0xDFFFFFF` (wait 2)
|
||||||
|
* Max of 32Mb
|
||||||
|
|
||||||
|
These portions of the memory are less fixed, because they depend on the precise
|
||||||
|
details of the game pak you've inserted into the GBA. In general, they connect
|
||||||
|
to the game pak ROM and/or Flash memory, using a 16-bit bus. The ROM is
|
||||||
|
read-only, but the Flash memory (if any) allows writes.
|
||||||
|
|
||||||
|
The game pak ROM is listed as being in three sections, but it's actually the
|
||||||
|
same memory being effectively mirrored into three different locations. The
|
||||||
|
mirror that you choose to access the game pak through affects which wait state
|
||||||
|
setting it uses (configured via IO register of course). Unfortunately, the
|
||||||
|
details come down more to the game pak hardware that you load your game onto
|
||||||
|
than anything else, so there's not much I can say right here. We'll eventually
|
||||||
|
talk about it more later,
|
||||||
|
|
||||||
|
One thing of note is the way that the 16-bit bus affects us: the instructions to
|
||||||
|
execute are coming through the same bus as the rest of the game data, so we want
|
||||||
|
them to be as compact as possible. The ARM chip in the GBA supports two
|
||||||
|
different instruction sets, "thumb" and "non-thumb". The thumb mode instructions
|
||||||
|
are 16-bit, so they can each be loaded one at a time, and the non-thumb
|
||||||
|
instructions are 32-bit, so we're at a penalty if we execute them directly out
|
||||||
|
of the game pak. However, some things will demand that we use non-thumb code, so
|
||||||
|
we'll have to deal with that eventually. It's possible to switch between modes,
|
||||||
|
but it's a pain to keep track of what mode you're in because there's not
|
||||||
|
currently support for it in Rust itself (perhaps some day). So we'll stick with
|
||||||
|
thumb code as much as we possibly can, that's why our target profile for our
|
||||||
|
builds starts with `thumbv4`.
|
||||||
|
|
||||||
|
## Game Pak SRAM
|
||||||
|
|
||||||
|
* `0xE000000` to `0xE00FFFF` (64k)
|
||||||
|
|
||||||
|
The game pak SRAM has an 8-bit bus. Why did Pokémon always take so long to save?
|
||||||
|
This is why. It also has some amount of wait, but as with the ROM, the details
|
||||||
|
depend on your game pak hardware (and also as with ROM, you can adjust the
|
||||||
|
settings with an IO register, should you need to).
|
||||||
|
|
||||||
|
One thing to note about the SRAM is that the GBA has a Direct Memory Access
|
||||||
|
(DMA) feature that can be used for bulk memory movements in some cases, but the
|
||||||
|
DMA _cannot_ access the SRAM region. You really are stuck reading and writing
|
||||||
|
one byte at a time when you're using the SRAM.
|
2
book/src/ch03/gba_rng.md
Normal file
2
book/src/ch03/gba_rng.md
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# GBA RNG
|
||||||
|
TODO
|
34
book/src/ch03/index.md
Normal file
34
book/src/ch03/index.md
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
# Ch 3: Memory and Objects
|
||||||
|
|
||||||
|
Alright so we can do some basic "movement", but we left a big trail in the video
|
||||||
|
memory of everywhere we went. Most of the time that's not what we want at all.
|
||||||
|
If we want more hardware support we're going to have to use a new video mode. So
|
||||||
|
far we've only used Mode 3, but modes 4 and 5 are basically the same. Instead,
|
||||||
|
we'll switch focus to using a tiled graphical mode.
|
||||||
|
|
||||||
|
First we will go over the complete GBA memory mapping. Part of this is the
|
||||||
|
memory for tiled graphics, but also things like all those IO registers, where
|
||||||
|
our RAM is for scratch space, all that stuff. Even if we can't put all of them
|
||||||
|
to use at once, it's helpful to have an idea of what will be available in the
|
||||||
|
long run.
|
||||||
|
|
||||||
|
Tiled modes bring us two big new concepts that each have their own complexity:
|
||||||
|
backgrounds and objects. They share some concepts, but fundamentally the
|
||||||
|
background is for creating a very large static space that you can scroll around
|
||||||
|
the view within, and the objects are about having a few moving bits that appear
|
||||||
|
over the background. Careful use of backgrounds and objects is key to having the
|
||||||
|
best looking GBA game, so we won't even be able to cover it all in a single
|
||||||
|
chapter.
|
||||||
|
|
||||||
|
And, of course, since most games are pretty boring if they're totally static
|
||||||
|
we'll touch on the kinds of RNG implementations you might want to have on a GBA.
|
||||||
|
Most general purpose RNGs that you find are rather big compared to the amount of
|
||||||
|
memory we want to give them, and they often use a lot of `u64` operations, so
|
||||||
|
they end up much slower on a 32-bit machine like the GBA (you can lower 64-bit
|
||||||
|
ops to combinations of 32-bit ops, but that's quite a bit more work). We'll
|
||||||
|
cover a few RNG options that size down the RNG to a good size and a good speed
|
||||||
|
without trading away too much in terms of quality.
|
||||||
|
|
||||||
|
To top it all off, we'll make a simple "memory game" sort of thing. There's some
|
||||||
|
face down cards in a grid, you pick one to check, then you pick the other to
|
||||||
|
check, and then if they match the pair disappears.
|
2
book/src/ch03/memory_game.md
Normal file
2
book/src/ch03/memory_game.md
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# memory_game
|
||||||
|
TODO
|
2
book/src/ch03/object_basics.md
Normal file
2
book/src/ch03/object_basics.md
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# Object Basics
|
||||||
|
TODO
|
2
book/src/ch03/tiled_backgrounds.md
Normal file
2
book/src/ch03/tiled_backgrounds.md
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# Tiled Backgrounds
|
||||||
|
TODO
|
|
@ -1,8 +1,38 @@
|
||||||
# Introduction
|
# Introduction
|
||||||
|
|
||||||
Here's a book that'll help you program in Rust on the GBA.
|
Here's a book that'll help you program in Rust on the Game Boy Advance (GBA).
|
||||||
|
|
||||||
It's very "work in progress". At the moment there's only one demo program.
|
It's a work in progress of course, but so is most of everything in Rust.
|
||||||
|
|
||||||
|
## Style and Purpose
|
||||||
|
|
||||||
|
I'm out to teach you how to program in Rust on the GBA, obviously. However,
|
||||||
|
while there _is_ a [gba](https://github.com/rust-console/gba) crate, and while I
|
||||||
|
genuinely believe it to be a good and useful crate for GBA programming, we _will
|
||||||
|
not_ be using the `gba` crate within this book. In fact we won't be using any
|
||||||
|
crates at all. We can call it the [Handmade Hero](https://handmadehero.org/)
|
||||||
|
approach, if you like.
|
||||||
|
|
||||||
|
I don't want to just teach you how to use the `gba` crate, I want to teach you
|
||||||
|
what you'd need to know to write the crate from scratch if it wasn't there.
|
||||||
|
|
||||||
|
Each chapter of the book will focus on a few things you'll need to know about
|
||||||
|
GBA programming and then present a fully self-contained example that puts those
|
||||||
|
ideas into action. Just one file per example, no dependencies, no external
|
||||||
|
assets, no fuss. The examples will be in the text of the book within code
|
||||||
|
blocks, but also you can find them in the [examples
|
||||||
|
directory](https://github.com/rust-console/gba/tree/master/examples) of the repo
|
||||||
|
if you want to get them that way.
|
||||||
|
|
||||||
|
## Expected Knowledge
|
||||||
|
|
||||||
|
I will try not to ask too much of the reader ahead of time, but you are expected
|
||||||
|
to have already read [The Rust Book](https://doc.rust-lang.org/book/).
|
||||||
|
|
||||||
|
It's very difficult to know when you've said something that someone else won't
|
||||||
|
already know about, or if you're presenting ideas out of order. If things aren't
|
||||||
|
clear please [file an issue](https://github.com/rust-console/gba/issues) and
|
||||||
|
we'll try to address it.
|
||||||
|
|
||||||
## Getting Help
|
## Getting Help
|
||||||
|
|
||||||
|
@ -14,11 +44,12 @@ channel.
|
||||||
* `Lokathor` is the fool who decided to write a crate and book for it.
|
* `Lokathor` is the fool who decided to write a crate and book for it.
|
||||||
|
|
||||||
If it's _not_ a GBA specific question then you can probably ask any of the other
|
If it's _not_ a GBA specific question then you can probably ask any of the other
|
||||||
folks in the server as well.
|
folks in the server as well (there's a few hundred folks).
|
||||||
|
|
||||||
## Other Works
|
## Further Reading
|
||||||
|
|
||||||
If you want to read more about developing on the GBA there are some other good resources as well:
|
If you want to read more about developing on the GBA there are some other good
|
||||||
|
resources as well:
|
||||||
|
|
||||||
* [Tonc](https://www.coranac.com/tonc/text/toc.htm), a tutorial series written
|
* [Tonc](https://www.coranac.com/tonc/text/toc.htm), a tutorial series written
|
||||||
for C, but it's what I based the ordering of this book's sections on.
|
for C, but it's what I based the ordering of this book's sections on.
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
|
|
||||||
@rem Build the crt0 file before we begin
|
@rem Build the crt0 file before we begin
|
||||||
arm-none-eabi-as crt0.s -o crt0.o
|
@if not exist ".\target" mkdir target
|
||||||
|
arm-none-eabi-as crt0.s -o target/crt0.o
|
||||||
|
|
||||||
@rem Build all examples, both debug and release
|
@rem Build all examples, both debug and release
|
||||||
cargo xbuild --examples --target thumbv4-none-agb.json
|
cargo xbuild --examples --target thumbv4-none-agb.json
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<!-- Book generated using mdBook -->
|
<!-- Book generated using mdBook -->
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Ch 0: Development Setup - Rust GBA Tutorials</title>
|
<title>Ch 0: Development Setup - Rust GBA Guide</title>
|
||||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||||
<meta name="description" content="">
|
<meta name="description" content="">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
@ -72,7 +72,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||||
<ol class="chapter"><li><a href="../introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="../ch0/index.html" class="active"><strong aria-hidden="true">2.</strong> Ch 0: Development Setup</a></li><li><a href="../ch1/index.html"><strong aria-hidden="true">3.</strong> Ch 1: Hello GBA</a></li><li><ol class="section"><li><a href="../ch1/hello1.html"><strong aria-hidden="true">3.1.</strong> hello1</a></li><li><a href="../ch1/io_registers.html"><strong aria-hidden="true">3.2.</strong> IO Registers</a></li><li><a href="../ch1/the_display_control_register.html"><strong aria-hidden="true">3.3.</strong> The Display Control Register</a></li><li><a href="../ch1/video_memory_intro.html"><strong aria-hidden="true">3.4.</strong> Video Memory Intro</a></li><li><a href="../ch1/hello2.html"><strong aria-hidden="true">3.5.</strong> hello2</a></li></ol></li><li><a href="../ch2/index.html"><strong aria-hidden="true">4.</strong> Ch 2: User Input</a></li><li><ol class="section"><li><a href="../ch2/the_key_input_register.html"><strong aria-hidden="true">4.1.</strong> The Key Input Register</a></li><li><a href="../ch2/the_vcount_register.html"><strong aria-hidden="true">4.2.</strong> The VCount Register</a></li><li><a href="../ch2/light_cycle.html"><strong aria-hidden="true">4.3.</strong> light_cycle</a></li></ol></li></ol>
|
<ol class="chapter"><li><a href="../introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="../ch00/index.html" class="active"><strong aria-hidden="true">2.</strong> Ch 0: Development Setup</a></li><li><a href="../ch01/index.html"><strong aria-hidden="true">3.</strong> Ch 1: Hello GBA</a></li><li><ol class="section"><li><a href="../ch01/hello1.html"><strong aria-hidden="true">3.1.</strong> hello1</a></li><li><a href="../ch01/volatile.html"><strong aria-hidden="true">3.2.</strong> Volatile</a></li><li><a href="../ch01/io_registers.html"><strong aria-hidden="true">3.3.</strong> IO Registers</a></li><li><a href="../ch01/the_display_control_register.html"><strong aria-hidden="true">3.4.</strong> The Display Control Register</a></li><li><a href="../ch01/video_memory_intro.html"><strong aria-hidden="true">3.5.</strong> Video Memory Intro</a></li><li><a href="../ch01/hello2.html"><strong aria-hidden="true">3.6.</strong> hello2</a></li></ol></li><li><a href="../ch02/index.html"><strong aria-hidden="true">4.</strong> Ch 2: User Input</a></li><li><ol class="section"><li><a href="../ch02/the_key_input_register.html"><strong aria-hidden="true">4.1.</strong> The Key Input Register</a></li><li><a href="../ch02/the_vcount_register.html"><strong aria-hidden="true">4.2.</strong> The VCount Register</a></li><li><a href="../ch02/light_cycle.html"><strong aria-hidden="true">4.3.</strong> light_cycle</a></li></ol></li><li><a href="../ch03/index.html"><strong aria-hidden="true">5.</strong> Ch 3: Memory and Objects</a></li><li><ol class="section"><li><a href="../ch03/gba_memory.html"><strong aria-hidden="true">5.1.</strong> GBA Memory</a></li><li><a href="../ch03/tiled_backgrounds.html"><strong aria-hidden="true">5.2.</strong> Tiled Backgrounds</a></li><li><a href="../ch03/object_basics.html"><strong aria-hidden="true">5.3.</strong> Object Basics</a></li><li><a href="../ch03/gba_rng.html"><strong aria-hidden="true">5.4.</strong> GBA RNG</a></li><li><a href="../ch03/memory_game.html"><strong aria-hidden="true">5.5.</strong> memory_game</a></li></ol></li></ol>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div id="page-wrapper" class="page-wrapper">
|
<div id="page-wrapper" class="page-wrapper">
|
||||||
|
@ -102,7 +102,7 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 class="menu-title">Rust GBA Tutorials</h1>
|
<h1 class="menu-title">Rust GBA Guide</h1>
|
||||||
|
|
||||||
<div class="right-buttons">
|
<div class="right-buttons">
|
||||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||||
|
@ -140,49 +140,53 @@
|
||||||
<p>Before you can build a GBA game you'll have to follow some special steps to
|
<p>Before you can build a GBA game you'll have to follow some special steps to
|
||||||
setup the development environment. Perhaps unfortunately, there's enough detail
|
setup the development environment. Perhaps unfortunately, there's enough detail
|
||||||
here to warrant a mini-chapter all on its own.</p>
|
here to warrant a mini-chapter all on its own.</p>
|
||||||
<p>Before we begin I'd like to give a special thanks to <strong>Ketsuban</strong>, who is the
|
<p>Once again, extra special thanks to <strong>Ketsuban</strong>, who first dove into how to
|
||||||
wizard that arranged for all of this to be able to happen and laid out the
|
make this all work with rust and then shared it with the world.</p>
|
||||||
details of the plan to the rest of the world.</p>
|
|
||||||
<a class="header" href="#per-system-setup" id="per-system-setup"><h2>Per System Setup</h2></a>
|
<a class="header" href="#per-system-setup" id="per-system-setup"><h2>Per System Setup</h2></a>
|
||||||
<p>Obviously you need your computer to have a working rust installation. However,
|
<p>Obviously you need your computer to have a <a href="https://rustup.rs/">working rust
|
||||||
you'll also need to ensure that you're using a nightly toolchain. You can run
|
installation</a>. However, you'll also need to ensure that
|
||||||
<code>rustup default nightly</code> to set nightly as the system wide default toolchain, or
|
you're using a nightly toolchain (we will need it for inline assembly, among
|
||||||
you can use a <a href="https://github.com/rust-lang-nursery/rustup.rs#the-toolchain-file">toolchain
|
other potential useful features). You can run <code>rustup default nightly</code> to set
|
||||||
|
nightly as the system wide default toolchain, or you can use a <a href="https://github.com/rust-lang-nursery/rustup.rs#the-toolchain-file">toolchain
|
||||||
file</a> to use
|
file</a> to use
|
||||||
nightly just on a specific project, but either way we'll be assuming nightly
|
nightly just on a specific project, but either way we'll be assuming the use of
|
||||||
from now on.</p>
|
nightly from now on. You'll also need the <code>rust-src</code> component so that
|
||||||
<p>Next you need <a href="https://devkitpro.org/wiki/Getting_Started">devkitpro</a>. They've
|
<code>cargo-xbuild</code> will be able to compile the core crate for us in a bit, so run
|
||||||
got a graphical installer for Windows, and <code>pacman</code> support on Linux. We'll be
|
<code>rustup component add rust-src</code>.</p>
|
||||||
using a few of their binutils for the <code>arm-none-eabi</code> target, and we'll also be
|
<p>Next, you need <a href="https://devkitpro.org/wiki/Getting_Started">devkitpro</a>. They've
|
||||||
using some of their tools that are specific to GBA development, so <em>even if</em> you
|
got a graphical installer for Windows that runs nicely, and I guess <code>pacman</code>
|
||||||
already have the right binutils for whatever reason, you'll still want devkitpro
|
support on Linux (I'm on Windows so I haven't tried the Linux install myself).
|
||||||
for the <code>gbafix</code> utility.</p>
|
We'll be using a few of their general binutils for the <code>arm-none-eabi</code> target,
|
||||||
|
and we'll also be using some of their tools that are specific to GBA
|
||||||
|
development, so <em>even if</em> you already have the right binutils for whatever
|
||||||
|
reason, you'll still want devkitpro for the <code>gbafix</code> utility.</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>On Windows you'll want something like <code>C:\devkitpro\devkitARM\bin</code> and
|
<li>On Windows you'll want something like <code>C:\devkitpro\devkitARM\bin</code> and
|
||||||
<code>C:\devkitpro\tools\bin</code> to be <a href="https://stackoverflow.com/q/44272416/455232">added to your
|
<code>C:\devkitpro\tools\bin</code> to be <a href="https://stackoverflow.com/q/44272416/455232">added to your
|
||||||
PATH</a>, depending on where you
|
PATH</a>, depending on where you
|
||||||
installed it to and such.</li>
|
installed it to and such.</li>
|
||||||
<li>On Linux you'll also want it to be added to your path, but if you're using
|
<li>On Linux you'll also want it to be added to your path, but if you're using
|
||||||
Linux I'll just assume you know how to do all that.</li>
|
Linux I'll just assume you know how to do all that. I'm told that the default
|
||||||
|
installation path is <code>/opt/devkitpro/devkitARM/bin</code>, so look there first if
|
||||||
|
you didn't select some other place.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>Finally, you'll need <code>cargo-xbuild</code>. Just run <code>cargo install cargo-xbuild</code> and
|
<p>Finally, you'll need <code>cargo-xbuild</code>. Just run <code>cargo install cargo-xbuild</code> and
|
||||||
cargo will figure it all out for you.</p>
|
cargo will figure it all out for you.</p>
|
||||||
<a class="header" href="#per-project-setup" id="per-project-setup"><h2>Per Project Setup</h2></a>
|
<a class="header" href="#per-project-setup" id="per-project-setup"><h2>Per Project Setup</h2></a>
|
||||||
<p>Now you'll need some particular files each time you want to start a new project.
|
<p>Once the system wide tools are ready, you'll need some particular files each
|
||||||
You can find them in the root of the <a href="https://github.com/rust-console/gba">rust-console/gba
|
time you want to start a new project. You can find them in the root of the
|
||||||
repo</a>.</p>
|
<a href="https://github.com/rust-console/gba">rust-console/gba repo</a>.</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><code>thumbv4-none-agb.json</code> describes the overall GBA to cargo-xbuild so it knows
|
<li><code>thumbv4-none-agb.json</code> describes the overall GBA to cargo-xbuild (and LLVM)
|
||||||
what to do. This is actually a somewhat made up target name since there's no
|
so it knows what to do. Technically the GBA is <code>thumbv4-none-eabi</code>, but we
|
||||||
official target name. The GBA is essentially the same as a normal
|
change the <code>eabi</code> to <code>agb</code> so that we can distinguish it from other <code>eabi</code>
|
||||||
<code>thumbv4-none-eabi</code> device, but we give it the "agb" signifier so that later
|
devices when using <code>cfg</code> flags.</li>
|
||||||
on we'll be able to use rust's <code>cfg</code> ability to allow our code to know if it's
|
|
||||||
specifically targeting a GBA or some other similar device (like an NDS).</li>
|
|
||||||
<li><code>crt0.s</code> describes some ASM startup stuff. If you have more ASM to place here
|
<li><code>crt0.s</code> describes some ASM startup stuff. If you have more ASM to place here
|
||||||
later on this is where you can put it. You also need to build it into a
|
later on this is where you can put it. You also need to build it into a
|
||||||
<code>crt0.o</code> file before it can actually be used, but we'll cover that below.</li>
|
<code>crt0.o</code> file before it can actually be used, but we'll cover that below.</li>
|
||||||
<li><code>linker.ld</code> tells the linker more critical info about the layout expectations
|
<li><code>linker.ld</code> tells the linker all the critical info about the layout
|
||||||
that the GBA has about our program.</li>
|
expectations that the GBA has about our program, and that it should also
|
||||||
|
include the <code>crt0.o</code> file with our compiled rust code.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<a class="header" href="#compiling" id="compiling"><h2>Compiling</h2></a>
|
<a class="header" href="#compiling" id="compiling"><h2>Compiling</h2></a>
|
||||||
<p>The next steps only work once you've got some source code to build. If you need
|
<p>The next steps only work once you've got some source code to build. If you need
|
||||||
|
@ -206,13 +210,13 @@ practically instant operation.</li>
|
||||||
as <code>--release</code>, and options, such as <code>--bin foo</code> or <code>--examples</code>, that you'd
|
as <code>--release</code>, and options, such as <code>--bin foo</code> or <code>--examples</code>, that you'd
|
||||||
expect <code>cargo</code> to accept.</li>
|
expect <code>cargo</code> to accept.</li>
|
||||||
<li>You <strong>can not</strong> build and run tests this way, because they require <code>std</code>,
|
<li>You <strong>can not</strong> build and run tests this way, because they require <code>std</code>,
|
||||||
which the GBA doesn't have. You can still run some of your project's tests
|
which the GBA doesn't have. If you want you can still run some of your
|
||||||
with <code>cargo test</code>, but that builds for your local machine, so anything
|
project's tests with <code>cargo test --lib</code> or similar, but that builds for your
|
||||||
specific to the GBA (such as reading and writing registers) won't be
|
local machine, so anything specific to the GBA (such as reading and writing
|
||||||
testable that way. If you want to isolate and try out some piece code
|
registers) won't be testable that way. If you want to isolate and try out
|
||||||
running on the GBA you'll unfortunately have to make a demo for it in your
|
some piece code running on the GBA you'll unfortunately have to make a demo
|
||||||
<code>examples/</code> directory and then run the demo in an emulator and see if it
|
for it in your <code>examples/</code> directory and then run the demo in an emulator
|
||||||
does what you expect.</li>
|
and see if it does what you expect.</li>
|
||||||
<li>The file extension is important. <code>cargo xbuild</code> takes it as a flag to
|
<li>The file extension is important. <code>cargo xbuild</code> takes it as a flag to
|
||||||
compile dependencies with the same sysroot, so you can include crates
|
compile dependencies with the same sysroot, so you can include crates
|
||||||
normally. Well, creates that work in the GBA's limited environment, but you
|
normally. Well, creates that work in the GBA's limited environment, but you
|
||||||
|
@ -275,7 +279,10 @@ ROM is patched in place, so we don't even need to specify a new destination.</li
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>And you're finally done!</p>
|
<p>And you're finally done!</p>
|
||||||
<p>Of course, you probably want to make a script for all that, but it's up to you.</p>
|
<p>Of course, you probably want to make a script for all that, but it's up to you.
|
||||||
|
On our own project we have it mostly set up within a <code>Makefile.toml</code> which runs
|
||||||
|
using the <a href="https://github.com/sagiegurari/cargo-make">cargo-make</a> plugin. It's
|
||||||
|
not really the best plugin, but it's what's available.</p>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
@ -288,7 +295,7 @@ ROM is patched in place, so we don't even need to specify a new destination.</li
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a rel="next" href="../ch1/index.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
<a rel="next" href="../ch01/index.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
<i class="fa fa-angle-right"></i>
|
<i class="fa fa-angle-right"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
@ -306,7 +313,7 @@ ROM is patched in place, so we don't even need to specify a new destination.</li
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a href="../ch1/index.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
<a href="../ch01/index.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
<i class="fa fa-angle-right"></i>
|
<i class="fa fa-angle-right"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<!-- Book generated using mdBook -->
|
<!-- Book generated using mdBook -->
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>hello1 - Rust GBA Tutorials</title>
|
<title>hello1 - Rust GBA Guide</title>
|
||||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||||
<meta name="description" content="">
|
<meta name="description" content="">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
@ -72,7 +72,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||||
<ol class="chapter"><li><a href="../introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="../ch0/index.html"><strong aria-hidden="true">2.</strong> Ch 0: Development Setup</a></li><li><a href="../ch1/index.html"><strong aria-hidden="true">3.</strong> Ch 1: Hello GBA</a></li><li><ol class="section"><li><a href="../ch1/hello1.html" class="active"><strong aria-hidden="true">3.1.</strong> hello1</a></li><li><a href="../ch1/io_registers.html"><strong aria-hidden="true">3.2.</strong> IO Registers</a></li><li><a href="../ch1/the_display_control_register.html"><strong aria-hidden="true">3.3.</strong> The Display Control Register</a></li><li><a href="../ch1/video_memory_intro.html"><strong aria-hidden="true">3.4.</strong> Video Memory Intro</a></li><li><a href="../ch1/hello2.html"><strong aria-hidden="true">3.5.</strong> hello2</a></li></ol></li><li><a href="../ch2/index.html"><strong aria-hidden="true">4.</strong> Ch 2: User Input</a></li><li><ol class="section"><li><a href="../ch2/the_key_input_register.html"><strong aria-hidden="true">4.1.</strong> The Key Input Register</a></li><li><a href="../ch2/the_vcount_register.html"><strong aria-hidden="true">4.2.</strong> The VCount Register</a></li><li><a href="../ch2/light_cycle.html"><strong aria-hidden="true">4.3.</strong> light_cycle</a></li></ol></li></ol>
|
<ol class="chapter"><li><a href="../introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="../ch00/index.html"><strong aria-hidden="true">2.</strong> Ch 0: Development Setup</a></li><li><a href="../ch01/index.html"><strong aria-hidden="true">3.</strong> Ch 1: Hello GBA</a></li><li><ol class="section"><li><a href="../ch01/hello1.html" class="active"><strong aria-hidden="true">3.1.</strong> hello1</a></li><li><a href="../ch01/volatile.html"><strong aria-hidden="true">3.2.</strong> Volatile</a></li><li><a href="../ch01/io_registers.html"><strong aria-hidden="true">3.3.</strong> IO Registers</a></li><li><a href="../ch01/the_display_control_register.html"><strong aria-hidden="true">3.4.</strong> The Display Control Register</a></li><li><a href="../ch01/video_memory_intro.html"><strong aria-hidden="true">3.5.</strong> Video Memory Intro</a></li><li><a href="../ch01/hello2.html"><strong aria-hidden="true">3.6.</strong> hello2</a></li></ol></li><li><a href="../ch02/index.html"><strong aria-hidden="true">4.</strong> Ch 2: User Input</a></li><li><ol class="section"><li><a href="../ch02/the_key_input_register.html"><strong aria-hidden="true">4.1.</strong> The Key Input Register</a></li><li><a href="../ch02/the_vcount_register.html"><strong aria-hidden="true">4.2.</strong> The VCount Register</a></li><li><a href="../ch02/light_cycle.html"><strong aria-hidden="true">4.3.</strong> light_cycle</a></li></ol></li><li><a href="../ch03/index.html"><strong aria-hidden="true">5.</strong> Ch 3: Memory and Objects</a></li><li><ol class="section"><li><a href="../ch03/gba_memory.html"><strong aria-hidden="true">5.1.</strong> GBA Memory</a></li><li><a href="../ch03/tiled_backgrounds.html"><strong aria-hidden="true">5.2.</strong> Tiled Backgrounds</a></li><li><a href="../ch03/object_basics.html"><strong aria-hidden="true">5.3.</strong> Object Basics</a></li><li><a href="../ch03/gba_rng.html"><strong aria-hidden="true">5.4.</strong> GBA RNG</a></li><li><a href="../ch03/memory_game.html"><strong aria-hidden="true">5.5.</strong> memory_game</a></li></ol></li></ol>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div id="page-wrapper" class="page-wrapper">
|
<div id="page-wrapper" class="page-wrapper">
|
||||||
|
@ -102,7 +102,7 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 class="menu-title">Rust GBA Tutorials</h1>
|
<h1 class="menu-title">Rust GBA Guide</h1>
|
||||||
|
|
||||||
<div class="right-buttons">
|
<div class="right-buttons">
|
||||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||||
|
@ -137,12 +137,12 @@
|
||||||
<div id="content" class="content">
|
<div id="content" class="content">
|
||||||
<main>
|
<main>
|
||||||
<a class="header" href="#hello1" id="hello1"><h1>hello1</h1></a>
|
<a class="header" href="#hello1" id="hello1"><h1>hello1</h1></a>
|
||||||
<p>Ready? Here goes:</p>
|
<p>Our first example will be a totally minimal, full magic number crazy town.
|
||||||
|
Ready? Here goes:</p>
|
||||||
<p><code>hello1.rs</code></p>
|
<p><code>hello1.rs</code></p>
|
||||||
<pre><pre class="playpen"><code class="language-rust">#![feature(start)]
|
<pre><pre class="playpen"><code class="language-rust">#![feature(start)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
#[cfg(not(test))]
|
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||||
loop {}
|
loop {}
|
||||||
|
@ -159,15 +159,16 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<p>Throw that into your project, build the program (as described back in Chapter
|
<p>Throw that into your project skeleton, build the program (as described back in
|
||||||
0), and give it a run. You should see a red, green, and blue dot close-ish to
|
Chapter 0), and give it a run in your emulator. You should see a red, green, and
|
||||||
the middle of the screen. If you don't, something already went wrong. Double
|
blue dot close-ish to the middle of the screen. If you don't, something already
|
||||||
check things, phone a friend, write your senators, try asking Ketsuban on the
|
went wrong. Double check things, phone a friend, write your senators, try asking
|
||||||
<a href="https://discordapp.com/invite/aVESxV8">Rust Community Discord</a>, until you're
|
Ketsuban on the <a href="https://discordapp.com/invite/aVESxV8">Rust Community Discord</a>,
|
||||||
able to get your three dots going.</p>
|
until you're able to get your three dots going.</p>
|
||||||
<a class="header" href="#explaining-hello1" id="explaining-hello1"><h2>Explaining hello1</h2></a>
|
<a class="header" href="#a-basic-hello1-explanation" id="a-basic-hello1-explanation"><h2>A basic hello1 explanation</h2></a>
|
||||||
<p>So, what just happened? Even if you're used to Rust that might look pretty
|
<p>So, what just happened? Even if you're used to Rust that might look pretty
|
||||||
strange. We'll go over each part extra carefully.</p>
|
strange. We'll go over most of the little parts right here, and then bigger
|
||||||
|
parts will get their own sections.</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust">
|
<pre><pre class="playpen"><code class="language-rust">
|
||||||
# #![allow(unused_variables)]
|
# #![allow(unused_variables)]
|
||||||
#![feature(start)]
|
#![feature(start)]
|
||||||
|
@ -191,7 +192,6 @@ only life.</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust">
|
<pre><pre class="playpen"><code class="language-rust">
|
||||||
# #![allow(unused_variables)]
|
# #![allow(unused_variables)]
|
||||||
#fn main() {
|
#fn main() {
|
||||||
#[cfg(not(test))]
|
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||||
loop {}
|
loop {}
|
||||||
|
@ -203,9 +203,6 @@ Basically, if we somehow trigger a panic, this is where the program goes.
|
||||||
However, right now we don't know how to get any sort of message out to the user
|
However, right now we don't know how to get any sort of message out to the user
|
||||||
so... we do nothing at all. We <em>can't even return</em> from here, so we just sit in
|
so... we do nothing at all. We <em>can't even return</em> from here, so we just sit in
|
||||||
an infinite loop. The player will have to reset the universe from the outside.</p>
|
an infinite loop. The player will have to reset the universe from the outside.</p>
|
||||||
<p>The <code>#[cfg(not(test))]</code> part makes this item only exist in the program when
|
|
||||||
we're <em>not</em> in a test build. This is so that <code>cargo test</code> and such work right as
|
|
||||||
much as possible.</p>
|
|
||||||
<pre><pre class="playpen"><code class="language-rust">#[start]
|
<pre><pre class="playpen"><code class="language-rust">#[start]
|
||||||
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
|
@ -269,55 +266,21 @@ magic numbers mean or do.</p>
|
||||||
<li><code>0x06000000</code> is the start of Video RAM.</li>
|
<li><code>0x06000000</code> is the start of Video RAM.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>So we write some magic to the display control register once, then we write some
|
<p>So we write some magic to the display control register once, then we write some
|
||||||
other magic to three locations of magic to the Video RAM. We get three dots,
|
other magic to three magic locations in the Video RAM. Somehow that shows three
|
||||||
each in their own location... so that second part makes sense at least.</p>
|
dots. Gotta read on to find out why!</p>
|
||||||
<p>We'll get into the magic number details in the other sections of this chapter.</p>
|
|
||||||
<a class="header" href="#sidebar-volatile" id="sidebar-volatile"><h2>Sidebar: Volatile</h2></a>
|
|
||||||
<p>We'll get into what all that is in a moment, but first let's ask ourselves: Why
|
|
||||||
are we doing <em>volatile</em> writes? You've probably never used it before at all.
|
|
||||||
What is volatile anyway?</p>
|
|
||||||
<p>Well, the optimizer is pretty aggressive some of the time, and so it'll skip
|
|
||||||
reads and writes when it thinks can. Like if you write to a pointer once, and
|
|
||||||
then again a moment later, and it didn't see any other reads in between, it'll
|
|
||||||
think that it can just skip doing that first write since it'll get overwritten
|
|
||||||
anyway. Sometimes that's right, but sometimes it's wrong.</p>
|
|
||||||
<p>Marking a read or write as <em>volatile</em> tells the compiler that it really must do
|
|
||||||
that action, and in the exact order that we wrote it out. It says that there
|
|
||||||
might even be special hardware side effects going on that the compiler isn't
|
|
||||||
aware of. In this case, the write to the display control register sets a video
|
|
||||||
mode, and the writes to the Video RAM set pixels that will show up on the
|
|
||||||
screen.</p>
|
|
||||||
<p>Similar to "atomic" operations you might have heard about, all volatile
|
|
||||||
operations are enforced to happen in the exact order that you specify them, but
|
|
||||||
only relative to other volatile operations. So something like</p>
|
|
||||||
<pre><pre class="playpen"><code class="language-rust">
|
|
||||||
# #![allow(unused_variables)]
|
|
||||||
#fn main() {
|
|
||||||
c.volatile_write(5);
|
|
||||||
a += b;
|
|
||||||
d.volatile_write(7);
|
|
||||||
#}</code></pre></pre>
|
|
||||||
<p>might end up changing <code>a</code> either before or after the change to <code>c</code> (since the
|
|
||||||
value of <code>a</code> doesn't affect the write to <code>c</code>), but the write to <code>d</code> will
|
|
||||||
<em>always</em> happen after the write to <code>c</code> even though the compiler doesn't see any
|
|
||||||
direct data dependency there.</p>
|
|
||||||
<p>If you ever use volatile stuff on other platforms it's important to note that
|
|
||||||
volatile doesn't make things thread-safe, you still need atomic for that.
|
|
||||||
However, the GBA doesn't have threads, so we don't have to worry about thread
|
|
||||||
safety concerns.</p>
|
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||||
<!-- Mobile navigation buttons -->
|
<!-- Mobile navigation buttons -->
|
||||||
|
|
||||||
<a rel="prev" href="../ch1/index.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
<a rel="prev" href="../ch01/index.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
<i class="fa fa-angle-left"></i>
|
<i class="fa fa-angle-left"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a rel="next" href="../ch1/io_registers.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
<a rel="next" href="../ch01/volatile.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
<i class="fa fa-angle-right"></i>
|
<i class="fa fa-angle-right"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
@ -329,13 +292,13 @@ safety concerns.</p>
|
||||||
|
|
||||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||||
|
|
||||||
<a href="../ch1/index.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
<a href="../ch01/index.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
<i class="fa fa-angle-left"></i>
|
<i class="fa fa-angle-left"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a href="../ch1/io_registers.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
<a href="../ch01/volatile.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
<i class="fa fa-angle-right"></i>
|
<i class="fa fa-angle-right"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<!-- Book generated using mdBook -->
|
<!-- Book generated using mdBook -->
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>hello2 - Rust GBA Tutorials</title>
|
<title>hello2 - Rust GBA Guide</title>
|
||||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||||
<meta name="description" content="">
|
<meta name="description" content="">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
@ -72,7 +72,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||||
<ol class="chapter"><li><a href="../introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="../ch0/index.html"><strong aria-hidden="true">2.</strong> Ch 0: Development Setup</a></li><li><a href="../ch1/index.html"><strong aria-hidden="true">3.</strong> Ch 1: Hello GBA</a></li><li><ol class="section"><li><a href="../ch1/hello1.html"><strong aria-hidden="true">3.1.</strong> hello1</a></li><li><a href="../ch1/io_registers.html"><strong aria-hidden="true">3.2.</strong> IO Registers</a></li><li><a href="../ch1/the_display_control_register.html"><strong aria-hidden="true">3.3.</strong> The Display Control Register</a></li><li><a href="../ch1/video_memory_intro.html"><strong aria-hidden="true">3.4.</strong> Video Memory Intro</a></li><li><a href="../ch1/hello2.html" class="active"><strong aria-hidden="true">3.5.</strong> hello2</a></li></ol></li><li><a href="../ch2/index.html"><strong aria-hidden="true">4.</strong> Ch 2: User Input</a></li><li><ol class="section"><li><a href="../ch2/the_key_input_register.html"><strong aria-hidden="true">4.1.</strong> The Key Input Register</a></li><li><a href="../ch2/the_vcount_register.html"><strong aria-hidden="true">4.2.</strong> The VCount Register</a></li><li><a href="../ch2/light_cycle.html"><strong aria-hidden="true">4.3.</strong> light_cycle</a></li></ol></li></ol>
|
<ol class="chapter"><li><a href="../introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="../ch00/index.html"><strong aria-hidden="true">2.</strong> Ch 0: Development Setup</a></li><li><a href="../ch01/index.html"><strong aria-hidden="true">3.</strong> Ch 1: Hello GBA</a></li><li><ol class="section"><li><a href="../ch01/hello1.html"><strong aria-hidden="true">3.1.</strong> hello1</a></li><li><a href="../ch01/volatile.html"><strong aria-hidden="true">3.2.</strong> Volatile</a></li><li><a href="../ch01/io_registers.html"><strong aria-hidden="true">3.3.</strong> IO Registers</a></li><li><a href="../ch01/the_display_control_register.html"><strong aria-hidden="true">3.4.</strong> The Display Control Register</a></li><li><a href="../ch01/video_memory_intro.html"><strong aria-hidden="true">3.5.</strong> Video Memory Intro</a></li><li><a href="../ch01/hello2.html" class="active"><strong aria-hidden="true">3.6.</strong> hello2</a></li></ol></li><li><a href="../ch02/index.html"><strong aria-hidden="true">4.</strong> Ch 2: User Input</a></li><li><ol class="section"><li><a href="../ch02/the_key_input_register.html"><strong aria-hidden="true">4.1.</strong> The Key Input Register</a></li><li><a href="../ch02/the_vcount_register.html"><strong aria-hidden="true">4.2.</strong> The VCount Register</a></li><li><a href="../ch02/light_cycle.html"><strong aria-hidden="true">4.3.</strong> light_cycle</a></li></ol></li><li><a href="../ch03/index.html"><strong aria-hidden="true">5.</strong> Ch 3: Memory and Objects</a></li><li><ol class="section"><li><a href="../ch03/gba_memory.html"><strong aria-hidden="true">5.1.</strong> GBA Memory</a></li><li><a href="../ch03/tiled_backgrounds.html"><strong aria-hidden="true">5.2.</strong> Tiled Backgrounds</a></li><li><a href="../ch03/object_basics.html"><strong aria-hidden="true">5.3.</strong> Object Basics</a></li><li><a href="../ch03/gba_rng.html"><strong aria-hidden="true">5.4.</strong> GBA RNG</a></li><li><a href="../ch03/memory_game.html"><strong aria-hidden="true">5.5.</strong> memory_game</a></li></ol></li></ol>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div id="page-wrapper" class="page-wrapper">
|
<div id="page-wrapper" class="page-wrapper">
|
||||||
|
@ -102,7 +102,7 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 class="menu-title">Rust GBA Tutorials</h1>
|
<h1 class="menu-title">Rust GBA Guide</h1>
|
||||||
|
|
||||||
<div class="right-buttons">
|
<div class="right-buttons">
|
||||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||||
|
@ -142,7 +142,6 @@
|
||||||
<pre><pre class="playpen"><code class="language-rust">#![feature(start)]
|
<pre><pre class="playpen"><code class="language-rust">#![feature(start)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
#[cfg(not(test))]
|
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||||
loop {}
|
loop {}
|
||||||
|
@ -160,11 +159,12 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||||
}
|
}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<p>Now let's clean this up so that it's clearer what's going on.</p>
|
<p>Now let's clean this up so that it's clearer what's going on.</p>
|
||||||
<p>First we'll label that display control stuff:</p>
|
<p>First we'll label that display control stuff, including using the <code>VolatilePtr</code>
|
||||||
|
type from the volatile explanation:</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust">
|
<pre><pre class="playpen"><code class="language-rust">
|
||||||
# #![allow(unused_variables)]
|
# #![allow(unused_variables)]
|
||||||
#fn main() {
|
#fn main() {
|
||||||
pub const DISPCNT: *mut u16 = 0x04000000 as *mut u16;
|
pub const DISPCNT: VolatilePtr<u16> = VolatilePtr(0x04000000 as *mut u16);
|
||||||
pub const MODE3: u16 = 3;
|
pub const MODE3: u16 = 3;
|
||||||
pub const BG2: u16 = 0b100_0000_0000;
|
pub const BG2: u16 = 0b100_0000_0000;
|
||||||
#}</code></pre></pre>
|
#}</code></pre></pre>
|
||||||
|
@ -175,8 +175,11 @@ pub const BG2: u16 = 0b100_0000_0000;
|
||||||
pub const VRAM: usize = 0x06000000;
|
pub const VRAM: usize = 0x06000000;
|
||||||
pub const SCREEN_WIDTH: isize = 240;
|
pub const SCREEN_WIDTH: isize = 240;
|
||||||
#}</code></pre></pre>
|
#}</code></pre></pre>
|
||||||
<p>And then we want a small helper function for putting together a color value.</p>
|
<p>Note that VRAM has to be interpreted in different ways depending on mode, so we
|
||||||
<p>Happily, this one can even be declared as a const function. At the time of
|
just leave it as <code>usize</code> and we'll cast it into the right form closer to the
|
||||||
|
actual use.</p>
|
||||||
|
<p>Next we want a small helper function for putting together a color value.
|
||||||
|
Happily, this one can even be declared as a <code>const</code> function. At the time of
|
||||||
writing, we've got the "minimal const fn" support in nightly. It really is quite
|
writing, we've got the "minimal const fn" support in nightly. It really is quite
|
||||||
limited, but I'm happy to let rustc and LLVM pre-compute as much as they can
|
limited, but I'm happy to let rustc and LLVM pre-compute as much as they can
|
||||||
when it comes to the GBA's tiny CPU.</p>
|
when it comes to the GBA's tiny CPU.</p>
|
||||||
|
@ -194,7 +197,7 @@ usually helps you think about it a lot better.</p>
|
||||||
# #![allow(unused_variables)]
|
# #![allow(unused_variables)]
|
||||||
#fn main() {
|
#fn main() {
|
||||||
pub unsafe fn mode3_pixel(col: isize, row: isize, color: u16) {
|
pub unsafe fn mode3_pixel(col: isize, row: isize, color: u16) {
|
||||||
(VRAM as *mut u16).offset(col + row * SCREEN_WIDTH).write_volatile(color);
|
VolatilePtr(VRAM as *mut u16).offset(col + row * SCREEN_WIDTH).write(color);
|
||||||
}
|
}
|
||||||
#}</code></pre></pre>
|
#}</code></pre></pre>
|
||||||
<p>So now we've got this:</p>
|
<p>So now we've got this:</p>
|
||||||
|
@ -202,7 +205,6 @@ pub unsafe fn mode3_pixel(col: isize, row: isize, color: u16) {
|
||||||
<pre><pre class="playpen"><code class="language-rust">#![feature(start)]
|
<pre><pre class="playpen"><code class="language-rust">#![feature(start)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
#[cfg(not(test))]
|
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||||
loop {}
|
loop {}
|
||||||
|
@ -211,7 +213,7 @@ fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||||
#[start]
|
#[start]
|
||||||
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||||
unsafe {
|
unsafe {
|
||||||
DISPCNT.write_volatile(MODE3 | BG2);
|
DISPCNT.write(MODE3 | BG2);
|
||||||
mode3_pixel(120, 80, rgb16(31, 0, 0));
|
mode3_pixel(120, 80, rgb16(31, 0, 0));
|
||||||
mode3_pixel(136, 80, rgb16(0, 31, 0));
|
mode3_pixel(136, 80, rgb16(0, 31, 0));
|
||||||
mode3_pixel(120, 96, rgb16(0, 0, 31));
|
mode3_pixel(120, 96, rgb16(0, 0, 31));
|
||||||
|
@ -219,7 +221,22 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const DISPCNT: *mut u16 = 0x04000000 as *mut u16;
|
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct VolatilePtr<T>(pub *mut T);
|
||||||
|
impl<T> VolatilePtr<T> {
|
||||||
|
pub unsafe fn read(&self) -> T {
|
||||||
|
core::ptr::read_volatile(self.0)
|
||||||
|
}
|
||||||
|
pub unsafe fn write(&self, data: T) {
|
||||||
|
core::ptr::write_volatile(self.0, data);
|
||||||
|
}
|
||||||
|
pub unsafe fn offset(self, count: isize) -> Self {
|
||||||
|
VolatilePtr(self.0.wrapping_offset(count))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const DISPCNT: VolatilePtr<u16> = VolatilePtr(0x04000000 as *mut u16);
|
||||||
pub const MODE3: u16 = 3;
|
pub const MODE3: u16 = 3;
|
||||||
pub const BG2: u16 = 0b100_0000_0000;
|
pub const BG2: u16 = 0b100_0000_0000;
|
||||||
|
|
||||||
|
@ -231,26 +248,27 @@ pub const fn rgb16(red: u16, green: u16, blue: u16) -> u16 {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn mode3_pixel(col: isize, row: isize, color: u16) {
|
pub unsafe fn mode3_pixel(col: isize, row: isize, color: u16) {
|
||||||
(VRAM as *mut u16).offset(col + row * SCREEN_WIDTH).write_volatile(color);
|
VolatilePtr(VRAM as *mut u16).offset(col + row * SCREEN_WIDTH).write(color);
|
||||||
}
|
}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<p>Exact same program that we started with, but much easier to read.</p>
|
<p>Exact same program that we started with, but much easier to read.</p>
|
||||||
<p>Of course, in the full <code>gba</code> crate that this book is a part of we have these and
|
<p>Of course, in the full <code>gba</code> crate that this book is a part of we have these and
|
||||||
other elements all labeled and sorted out for you. Still, for educational
|
other elements all labeled and sorted out for you (not identically, but
|
||||||
purposes it's often best to do it yourself at least once.</p>
|
similarly). Still, for educational purposes it's often best to do it yourself at
|
||||||
|
least once.</p>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||||
<!-- Mobile navigation buttons -->
|
<!-- Mobile navigation buttons -->
|
||||||
|
|
||||||
<a rel="prev" href="../ch1/video_memory_intro.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
<a rel="prev" href="../ch01/video_memory_intro.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
<i class="fa fa-angle-left"></i>
|
<i class="fa fa-angle-left"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a rel="next" href="../ch2/index.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
<a rel="next" href="../ch02/index.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
<i class="fa fa-angle-right"></i>
|
<i class="fa fa-angle-right"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
@ -262,13 +280,13 @@ purposes it's often best to do it yourself at least once.</p>
|
||||||
|
|
||||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||||
|
|
||||||
<a href="../ch1/video_memory_intro.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
<a href="../ch01/video_memory_intro.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
<i class="fa fa-angle-left"></i>
|
<i class="fa fa-angle-left"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a href="../ch2/index.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
<a href="../ch02/index.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
<i class="fa fa-angle-right"></i>
|
<i class="fa fa-angle-right"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<!-- Book generated using mdBook -->
|
<!-- Book generated using mdBook -->
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Ch 1: Hello GBA - Rust GBA Tutorials</title>
|
<title>Ch 1: Hello GBA - Rust GBA Guide</title>
|
||||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||||
<meta name="description" content="">
|
<meta name="description" content="">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
@ -72,7 +72,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||||
<ol class="chapter"><li><a href="../introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="../ch0/index.html"><strong aria-hidden="true">2.</strong> Ch 0: Development Setup</a></li><li><a href="../ch1/index.html" class="active"><strong aria-hidden="true">3.</strong> Ch 1: Hello GBA</a></li><li><ol class="section"><li><a href="../ch1/hello1.html"><strong aria-hidden="true">3.1.</strong> hello1</a></li><li><a href="../ch1/io_registers.html"><strong aria-hidden="true">3.2.</strong> IO Registers</a></li><li><a href="../ch1/the_display_control_register.html"><strong aria-hidden="true">3.3.</strong> The Display Control Register</a></li><li><a href="../ch1/video_memory_intro.html"><strong aria-hidden="true">3.4.</strong> Video Memory Intro</a></li><li><a href="../ch1/hello2.html"><strong aria-hidden="true">3.5.</strong> hello2</a></li></ol></li><li><a href="../ch2/index.html"><strong aria-hidden="true">4.</strong> Ch 2: User Input</a></li><li><ol class="section"><li><a href="../ch2/the_key_input_register.html"><strong aria-hidden="true">4.1.</strong> The Key Input Register</a></li><li><a href="../ch2/the_vcount_register.html"><strong aria-hidden="true">4.2.</strong> The VCount Register</a></li><li><a href="../ch2/light_cycle.html"><strong aria-hidden="true">4.3.</strong> light_cycle</a></li></ol></li></ol>
|
<ol class="chapter"><li><a href="../introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="../ch00/index.html"><strong aria-hidden="true">2.</strong> Ch 0: Development Setup</a></li><li><a href="../ch01/index.html" class="active"><strong aria-hidden="true">3.</strong> Ch 1: Hello GBA</a></li><li><ol class="section"><li><a href="../ch01/hello1.html"><strong aria-hidden="true">3.1.</strong> hello1</a></li><li><a href="../ch01/volatile.html"><strong aria-hidden="true">3.2.</strong> Volatile</a></li><li><a href="../ch01/io_registers.html"><strong aria-hidden="true">3.3.</strong> IO Registers</a></li><li><a href="../ch01/the_display_control_register.html"><strong aria-hidden="true">3.4.</strong> The Display Control Register</a></li><li><a href="../ch01/video_memory_intro.html"><strong aria-hidden="true">3.5.</strong> Video Memory Intro</a></li><li><a href="../ch01/hello2.html"><strong aria-hidden="true">3.6.</strong> hello2</a></li></ol></li><li><a href="../ch02/index.html"><strong aria-hidden="true">4.</strong> Ch 2: User Input</a></li><li><ol class="section"><li><a href="../ch02/the_key_input_register.html"><strong aria-hidden="true">4.1.</strong> The Key Input Register</a></li><li><a href="../ch02/the_vcount_register.html"><strong aria-hidden="true">4.2.</strong> The VCount Register</a></li><li><a href="../ch02/light_cycle.html"><strong aria-hidden="true">4.3.</strong> light_cycle</a></li></ol></li><li><a href="../ch03/index.html"><strong aria-hidden="true">5.</strong> Ch 3: Memory and Objects</a></li><li><ol class="section"><li><a href="../ch03/gba_memory.html"><strong aria-hidden="true">5.1.</strong> GBA Memory</a></li><li><a href="../ch03/tiled_backgrounds.html"><strong aria-hidden="true">5.2.</strong> Tiled Backgrounds</a></li><li><a href="../ch03/object_basics.html"><strong aria-hidden="true">5.3.</strong> Object Basics</a></li><li><a href="../ch03/gba_rng.html"><strong aria-hidden="true">5.4.</strong> GBA RNG</a></li><li><a href="../ch03/memory_game.html"><strong aria-hidden="true">5.5.</strong> memory_game</a></li></ol></li></ol>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div id="page-wrapper" class="page-wrapper">
|
<div id="page-wrapper" class="page-wrapper">
|
||||||
|
@ -102,7 +102,7 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 class="menu-title">Rust GBA Tutorials</h1>
|
<h1 class="menu-title">Rust GBA Guide</h1>
|
||||||
|
|
||||||
<div class="right-buttons">
|
<div class="right-buttons">
|
||||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||||
|
@ -150,13 +150,13 @@ three dots to the screen.</p>
|
||||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||||
<!-- Mobile navigation buttons -->
|
<!-- Mobile navigation buttons -->
|
||||||
|
|
||||||
<a rel="prev" href="../ch0/index.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
<a rel="prev" href="../ch00/index.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
<i class="fa fa-angle-left"></i>
|
<i class="fa fa-angle-left"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a rel="next" href="../ch1/hello1.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
<a rel="next" href="../ch01/hello1.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
<i class="fa fa-angle-right"></i>
|
<i class="fa fa-angle-right"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
@ -168,13 +168,13 @@ three dots to the screen.</p>
|
||||||
|
|
||||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||||
|
|
||||||
<a href="../ch0/index.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
<a href="../ch00/index.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
<i class="fa fa-angle-left"></i>
|
<i class="fa fa-angle-left"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a href="../ch1/hello1.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
<a href="../ch01/hello1.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
<i class="fa fa-angle-right"></i>
|
<i class="fa fa-angle-right"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<!-- Book generated using mdBook -->
|
<!-- Book generated using mdBook -->
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>IO Registers - Rust GBA Tutorials</title>
|
<title>IO Registers - Rust GBA Guide</title>
|
||||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||||
<meta name="description" content="">
|
<meta name="description" content="">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
@ -72,7 +72,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||||
<ol class="chapter"><li><a href="../introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="../ch0/index.html"><strong aria-hidden="true">2.</strong> Ch 0: Development Setup</a></li><li><a href="../ch1/index.html"><strong aria-hidden="true">3.</strong> Ch 1: Hello GBA</a></li><li><ol class="section"><li><a href="../ch1/hello1.html"><strong aria-hidden="true">3.1.</strong> hello1</a></li><li><a href="../ch1/io_registers.html" class="active"><strong aria-hidden="true">3.2.</strong> IO Registers</a></li><li><a href="../ch1/the_display_control_register.html"><strong aria-hidden="true">3.3.</strong> The Display Control Register</a></li><li><a href="../ch1/video_memory_intro.html"><strong aria-hidden="true">3.4.</strong> Video Memory Intro</a></li><li><a href="../ch1/hello2.html"><strong aria-hidden="true">3.5.</strong> hello2</a></li></ol></li><li><a href="../ch2/index.html"><strong aria-hidden="true">4.</strong> Ch 2: User Input</a></li><li><ol class="section"><li><a href="../ch2/the_key_input_register.html"><strong aria-hidden="true">4.1.</strong> The Key Input Register</a></li><li><a href="../ch2/the_vcount_register.html"><strong aria-hidden="true">4.2.</strong> The VCount Register</a></li><li><a href="../ch2/light_cycle.html"><strong aria-hidden="true">4.3.</strong> light_cycle</a></li></ol></li></ol>
|
<ol class="chapter"><li><a href="../introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="../ch00/index.html"><strong aria-hidden="true">2.</strong> Ch 0: Development Setup</a></li><li><a href="../ch01/index.html"><strong aria-hidden="true">3.</strong> Ch 1: Hello GBA</a></li><li><ol class="section"><li><a href="../ch01/hello1.html"><strong aria-hidden="true">3.1.</strong> hello1</a></li><li><a href="../ch01/volatile.html"><strong aria-hidden="true">3.2.</strong> Volatile</a></li><li><a href="../ch01/io_registers.html" class="active"><strong aria-hidden="true">3.3.</strong> IO Registers</a></li><li><a href="../ch01/the_display_control_register.html"><strong aria-hidden="true">3.4.</strong> The Display Control Register</a></li><li><a href="../ch01/video_memory_intro.html"><strong aria-hidden="true">3.5.</strong> Video Memory Intro</a></li><li><a href="../ch01/hello2.html"><strong aria-hidden="true">3.6.</strong> hello2</a></li></ol></li><li><a href="../ch02/index.html"><strong aria-hidden="true">4.</strong> Ch 2: User Input</a></li><li><ol class="section"><li><a href="../ch02/the_key_input_register.html"><strong aria-hidden="true">4.1.</strong> The Key Input Register</a></li><li><a href="../ch02/the_vcount_register.html"><strong aria-hidden="true">4.2.</strong> The VCount Register</a></li><li><a href="../ch02/light_cycle.html"><strong aria-hidden="true">4.3.</strong> light_cycle</a></li></ol></li><li><a href="../ch03/index.html"><strong aria-hidden="true">5.</strong> Ch 3: Memory and Objects</a></li><li><ol class="section"><li><a href="../ch03/gba_memory.html"><strong aria-hidden="true">5.1.</strong> GBA Memory</a></li><li><a href="../ch03/tiled_backgrounds.html"><strong aria-hidden="true">5.2.</strong> Tiled Backgrounds</a></li><li><a href="../ch03/object_basics.html"><strong aria-hidden="true">5.3.</strong> Object Basics</a></li><li><a href="../ch03/gba_rng.html"><strong aria-hidden="true">5.4.</strong> GBA RNG</a></li><li><a href="../ch03/memory_game.html"><strong aria-hidden="true">5.5.</strong> memory_game</a></li></ol></li></ol>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div id="page-wrapper" class="page-wrapper">
|
<div id="page-wrapper" class="page-wrapper">
|
||||||
|
@ -102,7 +102,7 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 class="menu-title">Rust GBA Tutorials</h1>
|
<h1 class="menu-title">Rust GBA Guide</h1>
|
||||||
|
|
||||||
<div class="right-buttons">
|
<div class="right-buttons">
|
||||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||||
|
@ -176,13 +176,13 @@ array index is.</p>
|
||||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||||
<!-- Mobile navigation buttons -->
|
<!-- Mobile navigation buttons -->
|
||||||
|
|
||||||
<a rel="prev" href="../ch1/hello1.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
<a rel="prev" href="../ch01/volatile.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
<i class="fa fa-angle-left"></i>
|
<i class="fa fa-angle-left"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a rel="next" href="../ch1/the_display_control_register.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
<a rel="next" href="../ch01/the_display_control_register.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
<i class="fa fa-angle-right"></i>
|
<i class="fa fa-angle-right"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
@ -194,13 +194,13 @@ array index is.</p>
|
||||||
|
|
||||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||||
|
|
||||||
<a href="../ch1/hello1.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
<a href="../ch01/volatile.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
<i class="fa fa-angle-left"></i>
|
<i class="fa fa-angle-left"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a href="../ch1/the_display_control_register.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
<a href="../ch01/the_display_control_register.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
<i class="fa fa-angle-right"></i>
|
<i class="fa fa-angle-right"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<!-- Book generated using mdBook -->
|
<!-- Book generated using mdBook -->
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>The Display Control Register - Rust GBA Tutorials</title>
|
<title>The Display Control Register - Rust GBA Guide</title>
|
||||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||||
<meta name="description" content="">
|
<meta name="description" content="">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
@ -72,7 +72,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||||
<ol class="chapter"><li><a href="../introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="../ch0/index.html"><strong aria-hidden="true">2.</strong> Ch 0: Development Setup</a></li><li><a href="../ch1/index.html"><strong aria-hidden="true">3.</strong> Ch 1: Hello GBA</a></li><li><ol class="section"><li><a href="../ch1/hello1.html"><strong aria-hidden="true">3.1.</strong> hello1</a></li><li><a href="../ch1/io_registers.html"><strong aria-hidden="true">3.2.</strong> IO Registers</a></li><li><a href="../ch1/the_display_control_register.html" class="active"><strong aria-hidden="true">3.3.</strong> The Display Control Register</a></li><li><a href="../ch1/video_memory_intro.html"><strong aria-hidden="true">3.4.</strong> Video Memory Intro</a></li><li><a href="../ch1/hello2.html"><strong aria-hidden="true">3.5.</strong> hello2</a></li></ol></li><li><a href="../ch2/index.html"><strong aria-hidden="true">4.</strong> Ch 2: User Input</a></li><li><ol class="section"><li><a href="../ch2/the_key_input_register.html"><strong aria-hidden="true">4.1.</strong> The Key Input Register</a></li><li><a href="../ch2/the_vcount_register.html"><strong aria-hidden="true">4.2.</strong> The VCount Register</a></li><li><a href="../ch2/light_cycle.html"><strong aria-hidden="true">4.3.</strong> light_cycle</a></li></ol></li></ol>
|
<ol class="chapter"><li><a href="../introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="../ch00/index.html"><strong aria-hidden="true">2.</strong> Ch 0: Development Setup</a></li><li><a href="../ch01/index.html"><strong aria-hidden="true">3.</strong> Ch 1: Hello GBA</a></li><li><ol class="section"><li><a href="../ch01/hello1.html"><strong aria-hidden="true">3.1.</strong> hello1</a></li><li><a href="../ch01/volatile.html"><strong aria-hidden="true">3.2.</strong> Volatile</a></li><li><a href="../ch01/io_registers.html"><strong aria-hidden="true">3.3.</strong> IO Registers</a></li><li><a href="../ch01/the_display_control_register.html" class="active"><strong aria-hidden="true">3.4.</strong> The Display Control Register</a></li><li><a href="../ch01/video_memory_intro.html"><strong aria-hidden="true">3.5.</strong> Video Memory Intro</a></li><li><a href="../ch01/hello2.html"><strong aria-hidden="true">3.6.</strong> hello2</a></li></ol></li><li><a href="../ch02/index.html"><strong aria-hidden="true">4.</strong> Ch 2: User Input</a></li><li><ol class="section"><li><a href="../ch02/the_key_input_register.html"><strong aria-hidden="true">4.1.</strong> The Key Input Register</a></li><li><a href="../ch02/the_vcount_register.html"><strong aria-hidden="true">4.2.</strong> The VCount Register</a></li><li><a href="../ch02/light_cycle.html"><strong aria-hidden="true">4.3.</strong> light_cycle</a></li></ol></li><li><a href="../ch03/index.html"><strong aria-hidden="true">5.</strong> Ch 3: Memory and Objects</a></li><li><ol class="section"><li><a href="../ch03/gba_memory.html"><strong aria-hidden="true">5.1.</strong> GBA Memory</a></li><li><a href="../ch03/tiled_backgrounds.html"><strong aria-hidden="true">5.2.</strong> Tiled Backgrounds</a></li><li><a href="../ch03/object_basics.html"><strong aria-hidden="true">5.3.</strong> Object Basics</a></li><li><a href="../ch03/gba_rng.html"><strong aria-hidden="true">5.4.</strong> GBA RNG</a></li><li><a href="../ch03/memory_game.html"><strong aria-hidden="true">5.5.</strong> memory_game</a></li></ol></li></ol>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div id="page-wrapper" class="page-wrapper">
|
<div id="page-wrapper" class="page-wrapper">
|
||||||
|
@ -102,7 +102,7 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 class="menu-title">Rust GBA Tutorials</h1>
|
<h1 class="menu-title">Rust GBA Guide</h1>
|
||||||
|
|
||||||
<div class="right-buttons">
|
<div class="right-buttons">
|
||||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||||
|
@ -221,21 +221,19 @@ some nifty graphical effects.</p>
|
||||||
binary</a>, and we get
|
binary</a>, and we get
|
||||||
<code>0b100_0000_0011</code>. So, that's setting Mode 3 with background 2 enabled and
|
<code>0b100_0000_0011</code>. So, that's setting Mode 3 with background 2 enabled and
|
||||||
nothing else special.</p>
|
nothing else special.</p>
|
||||||
<p>However, I think we can do better than that. This is a prime target for more
|
|
||||||
newtyping as we attempt a <code>hello2</code> program.</p>
|
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||||
<!-- Mobile navigation buttons -->
|
<!-- Mobile navigation buttons -->
|
||||||
|
|
||||||
<a rel="prev" href="../ch1/io_registers.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
<a rel="prev" href="../ch01/io_registers.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
<i class="fa fa-angle-left"></i>
|
<i class="fa fa-angle-left"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a rel="next" href="../ch1/video_memory_intro.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
<a rel="next" href="../ch01/video_memory_intro.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
<i class="fa fa-angle-right"></i>
|
<i class="fa fa-angle-right"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
@ -247,13 +245,13 @@ newtyping as we attempt a <code>hello2</code> program.</p>
|
||||||
|
|
||||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||||
|
|
||||||
<a href="../ch1/io_registers.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
<a href="../ch01/io_registers.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
<i class="fa fa-angle-left"></i>
|
<i class="fa fa-angle-left"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a href="../ch1/video_memory_intro.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
<a href="../ch01/video_memory_intro.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
<i class="fa fa-angle-right"></i>
|
<i class="fa fa-angle-right"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<!-- Book generated using mdBook -->
|
<!-- Book generated using mdBook -->
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Video Memory Intro - Rust GBA Tutorials</title>
|
<title>Video Memory Intro - Rust GBA Guide</title>
|
||||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||||
<meta name="description" content="">
|
<meta name="description" content="">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
@ -72,7 +72,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||||
<ol class="chapter"><li><a href="../introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="../ch0/index.html"><strong aria-hidden="true">2.</strong> Ch 0: Development Setup</a></li><li><a href="../ch1/index.html"><strong aria-hidden="true">3.</strong> Ch 1: Hello GBA</a></li><li><ol class="section"><li><a href="../ch1/hello1.html"><strong aria-hidden="true">3.1.</strong> hello1</a></li><li><a href="../ch1/io_registers.html"><strong aria-hidden="true">3.2.</strong> IO Registers</a></li><li><a href="../ch1/the_display_control_register.html"><strong aria-hidden="true">3.3.</strong> The Display Control Register</a></li><li><a href="../ch1/video_memory_intro.html" class="active"><strong aria-hidden="true">3.4.</strong> Video Memory Intro</a></li><li><a href="../ch1/hello2.html"><strong aria-hidden="true">3.5.</strong> hello2</a></li></ol></li><li><a href="../ch2/index.html"><strong aria-hidden="true">4.</strong> Ch 2: User Input</a></li><li><ol class="section"><li><a href="../ch2/the_key_input_register.html"><strong aria-hidden="true">4.1.</strong> The Key Input Register</a></li><li><a href="../ch2/the_vcount_register.html"><strong aria-hidden="true">4.2.</strong> The VCount Register</a></li><li><a href="../ch2/light_cycle.html"><strong aria-hidden="true">4.3.</strong> light_cycle</a></li></ol></li></ol>
|
<ol class="chapter"><li><a href="../introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="../ch00/index.html"><strong aria-hidden="true">2.</strong> Ch 0: Development Setup</a></li><li><a href="../ch01/index.html"><strong aria-hidden="true">3.</strong> Ch 1: Hello GBA</a></li><li><ol class="section"><li><a href="../ch01/hello1.html"><strong aria-hidden="true">3.1.</strong> hello1</a></li><li><a href="../ch01/volatile.html"><strong aria-hidden="true">3.2.</strong> Volatile</a></li><li><a href="../ch01/io_registers.html"><strong aria-hidden="true">3.3.</strong> IO Registers</a></li><li><a href="../ch01/the_display_control_register.html"><strong aria-hidden="true">3.4.</strong> The Display Control Register</a></li><li><a href="../ch01/video_memory_intro.html" class="active"><strong aria-hidden="true">3.5.</strong> Video Memory Intro</a></li><li><a href="../ch01/hello2.html"><strong aria-hidden="true">3.6.</strong> hello2</a></li></ol></li><li><a href="../ch02/index.html"><strong aria-hidden="true">4.</strong> Ch 2: User Input</a></li><li><ol class="section"><li><a href="../ch02/the_key_input_register.html"><strong aria-hidden="true">4.1.</strong> The Key Input Register</a></li><li><a href="../ch02/the_vcount_register.html"><strong aria-hidden="true">4.2.</strong> The VCount Register</a></li><li><a href="../ch02/light_cycle.html"><strong aria-hidden="true">4.3.</strong> light_cycle</a></li></ol></li><li><a href="../ch03/index.html"><strong aria-hidden="true">5.</strong> Ch 3: Memory and Objects</a></li><li><ol class="section"><li><a href="../ch03/gba_memory.html"><strong aria-hidden="true">5.1.</strong> GBA Memory</a></li><li><a href="../ch03/tiled_backgrounds.html"><strong aria-hidden="true">5.2.</strong> Tiled Backgrounds</a></li><li><a href="../ch03/object_basics.html"><strong aria-hidden="true">5.3.</strong> Object Basics</a></li><li><a href="../ch03/gba_rng.html"><strong aria-hidden="true">5.4.</strong> GBA RNG</a></li><li><a href="../ch03/memory_game.html"><strong aria-hidden="true">5.5.</strong> memory_game</a></li></ol></li></ol>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div id="page-wrapper" class="page-wrapper">
|
<div id="page-wrapper" class="page-wrapper">
|
||||||
|
@ -102,7 +102,7 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 class="menu-title">Rust GBA Tutorials</h1>
|
<h1 class="menu-title">Rust GBA Guide</h1>
|
||||||
|
|
||||||
<div class="right-buttons">
|
<div class="right-buttons">
|
||||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||||
|
@ -228,9 +228,9 @@ data to fit in two pages, we compress the resolution.</p>
|
||||||
again we probably need to <a href="https://www.wolframalpha.com/">convert them</a> into
|
again we probably need to <a href="https://www.wolframalpha.com/">convert them</a> into
|
||||||
binary to make sense of it.</p>
|
binary to make sense of it.</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>0x001F: 0b11111</li>
|
<li>0x001F: 0b0_00000_00000_11111</li>
|
||||||
<li>0x03E0: 0b11111_00000</li>
|
<li>0x03E0: 0b0_00000_11111_00000</li>
|
||||||
<li>0x7C00: 0b11111_00000_00000</li>
|
<li>0x7C00: 0b0_11111_00000_00000</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>Ah, of course, a red pixel, a green pixel, and a blue pixel.</p>
|
<p>Ah, of course, a red pixel, a green pixel, and a blue pixel.</p>
|
||||||
|
|
||||||
|
@ -239,13 +239,13 @@ binary to make sense of it.</p>
|
||||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||||
<!-- Mobile navigation buttons -->
|
<!-- Mobile navigation buttons -->
|
||||||
|
|
||||||
<a rel="prev" href="../ch1/the_display_control_register.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
<a rel="prev" href="../ch01/the_display_control_register.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
<i class="fa fa-angle-left"></i>
|
<i class="fa fa-angle-left"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a rel="next" href="../ch1/hello2.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
<a rel="next" href="../ch01/hello2.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
<i class="fa fa-angle-right"></i>
|
<i class="fa fa-angle-right"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
@ -257,13 +257,13 @@ binary to make sense of it.</p>
|
||||||
|
|
||||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||||
|
|
||||||
<a href="../ch1/the_display_control_register.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
<a href="../ch01/the_display_control_register.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
<i class="fa fa-angle-left"></i>
|
<i class="fa fa-angle-left"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a href="../ch1/hello2.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
<a href="../ch01/hello2.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
<i class="fa fa-angle-right"></i>
|
<i class="fa fa-angle-right"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
262
docs/ch01/volatile.html
Normal file
262
docs/ch01/volatile.html
Normal file
|
@ -0,0 +1,262 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html lang="en" class="sidebar-visible no-js">
|
||||||
|
<head>
|
||||||
|
<!-- Book generated using mdBook -->
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Volatile - Rust GBA Guide</title>
|
||||||
|
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||||
|
<meta name="description" content="">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="theme-color" content="#ffffff" />
|
||||||
|
|
||||||
|
<link rel="shortcut icon" href="../favicon.png">
|
||||||
|
<link rel="stylesheet" href="../css/variables.css">
|
||||||
|
<link rel="stylesheet" href="../css/general.css">
|
||||||
|
<link rel="stylesheet" href="../css/chrome.css">
|
||||||
|
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||||
|
|
||||||
|
<!-- Fonts -->
|
||||||
|
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800" rel="stylesheet" type="text/css">
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Source+Code+Pro:500" rel="stylesheet" type="text/css">
|
||||||
|
|
||||||
|
<!-- Highlight.js Stylesheets -->
|
||||||
|
<link rel="stylesheet" href="../highlight.css">
|
||||||
|
<link rel="stylesheet" href="../tomorrow-night.css">
|
||||||
|
<link rel="stylesheet" href="../ayu-highlight.css">
|
||||||
|
|
||||||
|
<!-- Custom theme stylesheets -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body class="light">
|
||||||
|
<!-- Provide site root to javascript -->
|
||||||
|
<script type="text/javascript">var path_to_root = "../";</script>
|
||||||
|
|
||||||
|
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
try {
|
||||||
|
var theme = localStorage.getItem('mdbook-theme');
|
||||||
|
var sidebar = localStorage.getItem('mdbook-sidebar');
|
||||||
|
|
||||||
|
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||||
|
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||||
|
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||||
|
}
|
||||||
|
} catch (e) { }
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
var theme;
|
||||||
|
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||||
|
if (theme === null || theme === undefined) { theme = 'light'; }
|
||||||
|
document.body.className = theme;
|
||||||
|
document.querySelector('html').className = theme + ' js';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Hide / unhide sidebar before it is displayed -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
var html = document.querySelector('html');
|
||||||
|
var sidebar = 'hidden';
|
||||||
|
if (document.body.clientWidth >= 1080) {
|
||||||
|
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||||
|
sidebar = sidebar || 'visible';
|
||||||
|
}
|
||||||
|
html.classList.remove('sidebar-visible');
|
||||||
|
html.classList.add("sidebar-" + sidebar);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||||
|
<ol class="chapter"><li><a href="../introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="../ch00/index.html"><strong aria-hidden="true">2.</strong> Ch 0: Development Setup</a></li><li><a href="../ch01/index.html"><strong aria-hidden="true">3.</strong> Ch 1: Hello GBA</a></li><li><ol class="section"><li><a href="../ch01/hello1.html"><strong aria-hidden="true">3.1.</strong> hello1</a></li><li><a href="../ch01/volatile.html" class="active"><strong aria-hidden="true">3.2.</strong> Volatile</a></li><li><a href="../ch01/io_registers.html"><strong aria-hidden="true">3.3.</strong> IO Registers</a></li><li><a href="../ch01/the_display_control_register.html"><strong aria-hidden="true">3.4.</strong> The Display Control Register</a></li><li><a href="../ch01/video_memory_intro.html"><strong aria-hidden="true">3.5.</strong> Video Memory Intro</a></li><li><a href="../ch01/hello2.html"><strong aria-hidden="true">3.6.</strong> hello2</a></li></ol></li><li><a href="../ch02/index.html"><strong aria-hidden="true">4.</strong> Ch 2: User Input</a></li><li><ol class="section"><li><a href="../ch02/the_key_input_register.html"><strong aria-hidden="true">4.1.</strong> The Key Input Register</a></li><li><a href="../ch02/the_vcount_register.html"><strong aria-hidden="true">4.2.</strong> The VCount Register</a></li><li><a href="../ch02/light_cycle.html"><strong aria-hidden="true">4.3.</strong> light_cycle</a></li></ol></li><li><a href="../ch03/index.html"><strong aria-hidden="true">5.</strong> Ch 3: Memory and Objects</a></li><li><ol class="section"><li><a href="../ch03/gba_memory.html"><strong aria-hidden="true">5.1.</strong> GBA Memory</a></li><li><a href="../ch03/tiled_backgrounds.html"><strong aria-hidden="true">5.2.</strong> Tiled Backgrounds</a></li><li><a href="../ch03/object_basics.html"><strong aria-hidden="true">5.3.</strong> Object Basics</a></li><li><a href="../ch03/gba_rng.html"><strong aria-hidden="true">5.4.</strong> GBA RNG</a></li><li><a href="../ch03/memory_game.html"><strong aria-hidden="true">5.5.</strong> memory_game</a></li></ol></li></ol>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div id="page-wrapper" class="page-wrapper">
|
||||||
|
|
||||||
|
<div class="page">
|
||||||
|
|
||||||
|
<div id="menu-bar" class="menu-bar">
|
||||||
|
<div id="menu-bar-sticky-container">
|
||||||
|
<div class="left-buttons">
|
||||||
|
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||||
|
<i class="fa fa-bars"></i>
|
||||||
|
</button>
|
||||||
|
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||||
|
<i class="fa fa-paint-brush"></i>
|
||||||
|
</button>
|
||||||
|
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="light">Light <span class="default">(default)</span></button></li>
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
||||||
|
<i class="fa fa-search"></i>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1 class="menu-title">Rust GBA Guide</h1>
|
||||||
|
|
||||||
|
<div class="right-buttons">
|
||||||
|
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||||
|
<i id="print-button" class="fa fa-print"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="search-wrapper" class="hidden">
|
||||||
|
<form id="searchbar-outer" class="searchbar-outer">
|
||||||
|
<input type="search" name="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||||
|
</form>
|
||||||
|
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||||
|
<div id="searchresults-header" class="searchresults-header"></div>
|
||||||
|
<ul id="searchresults">
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||||
|
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||||
|
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||||
|
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div id="content" class="content">
|
||||||
|
<main>
|
||||||
|
<a class="header" href="#volatile" id="volatile"><h1>Volatile</h1></a>
|
||||||
|
<p>Before we focus on what the numbers mean, first let's ask ourselves: Why are we
|
||||||
|
doing <em>volatile</em> writes? You've probably never used that keywords before at all.
|
||||||
|
What <em>is</em> volatile anyway?</p>
|
||||||
|
<p>Well, the optimizer is pretty aggressive, and so it'll skip reads and writes
|
||||||
|
when it thinks can. Like if you write to a pointer once, and then again a moment
|
||||||
|
later, and it didn't see any other reads in between, it'll think that it can
|
||||||
|
just skip doing that first write since it'll get overwritten anyway. Sometimes
|
||||||
|
that's correct, but sometimes it's not.</p>
|
||||||
|
<p>Marking a read or write as <em>volatile</em> tells the compiler that it really must do
|
||||||
|
that action, and in the exact order that we wrote it out. It says that there
|
||||||
|
might even be special hardware side effects going on that the compiler isn't
|
||||||
|
aware of. In this case, the write to the display control register sets a video
|
||||||
|
mode, and the writes to the Video RAM set pixels that will show up on the
|
||||||
|
screen.</p>
|
||||||
|
<p>Similar to "atomic" operations you might have heard about, all volatile
|
||||||
|
operations are enforced to happen in the exact order that you specify them, but
|
||||||
|
only relative to other volatile operations. So something like</p>
|
||||||
|
<pre><pre class="playpen"><code class="language-rust">
|
||||||
|
# #![allow(unused_variables)]
|
||||||
|
#fn main() {
|
||||||
|
c.write_volatile(5);
|
||||||
|
a += b;
|
||||||
|
d.write_volatile(7);
|
||||||
|
#}</code></pre></pre>
|
||||||
|
<p>might end up changing <code>a</code> either before or after the change to <code>c</code> (since the
|
||||||
|
value of <code>a</code> doesn't affect the write to <code>c</code>), but the write to <code>d</code> will
|
||||||
|
<em>always</em> happen after the write to <code>c</code>, even though the compiler doesn't see any
|
||||||
|
direct data dependency there.</p>
|
||||||
|
<p>If you ever go on to use volatile stuff on other platforms it's important to
|
||||||
|
note that volatile doesn't make things thread-safe, you still need atomic for
|
||||||
|
that. However, the GBA doesn't have threads, so we don't have to worry about
|
||||||
|
those sorts of thread safety concerns (there's interrupts, but that's another
|
||||||
|
matter).</p>
|
||||||
|
<a class="header" href="#volatile-by-default" id="volatile-by-default"><h2>Volatile by default</h2></a>
|
||||||
|
<p>Of course, writing out <code>volatile_write</code> every time is more than we wanna do.
|
||||||
|
There's clarity and then there's excessive. This is a chance to write our first
|
||||||
|
<a href="https://doc.rust-lang.org/1.0.0/style/features/types/newtype.html">newtype</a>.
|
||||||
|
Basically a type that's got the exact same binary representation as some other
|
||||||
|
type, but new methods and trait implementations.</p>
|
||||||
|
<p>We want a <code>*mut T</code> that's volatile by default, and also when we offset it...
|
||||||
|
well the verdict is slightly unclear on how <code>offset</code> vs <code>wrapping_offset</code> work
|
||||||
|
when you're using pointers that you made up out of nowhere. I've asked the
|
||||||
|
experts and they genuinely weren't sure, so we'll make an <code>offset</code> method that
|
||||||
|
does a <code>wrapping_offset</code> just to be careful.</p>
|
||||||
|
<pre><pre class="playpen"><code class="language-rust">
|
||||||
|
# #![allow(unused_variables)]
|
||||||
|
#fn main() {
|
||||||
|
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct VolatilePtr<T>(pub *mut T);
|
||||||
|
impl<T> VolatilePtr<T> {
|
||||||
|
pub unsafe fn read(&self) -> T {
|
||||||
|
core::ptr::read_volatile(self.0)
|
||||||
|
}
|
||||||
|
pub unsafe fn write(&self, data: T) {
|
||||||
|
core::ptr::write_volatile(self.0, data);
|
||||||
|
}
|
||||||
|
pub unsafe fn offset(self, count: isize) -> Self {
|
||||||
|
VolatilePtr(self.0.wrapping_offset(count))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#}</code></pre></pre>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||||
|
<!-- Mobile navigation buttons -->
|
||||||
|
|
||||||
|
<a rel="prev" href="../ch01/hello1.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
|
<i class="fa fa-angle-left"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a rel="next" href="../ch01/io_registers.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
|
<i class="fa fa-angle-right"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
<div style="clear: both"></div>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||||
|
|
||||||
|
<a href="../ch01/hello1.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
|
<i class="fa fa-angle-left"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a href="../ch01/io_registers.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
|
<i class="fa fa-angle-right"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script src="../elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="../mark.min.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="../searcher.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
|
||||||
|
|
||||||
|
<script src="../clipboard.min.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="../highlight.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="../book.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
|
||||||
|
<!-- Custom JS scripts -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<!-- Book generated using mdBook -->
|
<!-- Book generated using mdBook -->
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Ch 2: User Input - Rust GBA Tutorials</title>
|
<title>Ch 2: User Input - Rust GBA Guide</title>
|
||||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||||
<meta name="description" content="">
|
<meta name="description" content="">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
@ -72,7 +72,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||||
<ol class="chapter"><li><a href="../introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="../ch0/index.html"><strong aria-hidden="true">2.</strong> Ch 0: Development Setup</a></li><li><a href="../ch1/index.html"><strong aria-hidden="true">3.</strong> Ch 1: Hello GBA</a></li><li><ol class="section"><li><a href="../ch1/hello1.html"><strong aria-hidden="true">3.1.</strong> hello1</a></li><li><a href="../ch1/io_registers.html"><strong aria-hidden="true">3.2.</strong> IO Registers</a></li><li><a href="../ch1/the_display_control_register.html"><strong aria-hidden="true">3.3.</strong> The Display Control Register</a></li><li><a href="../ch1/video_memory_intro.html"><strong aria-hidden="true">3.4.</strong> Video Memory Intro</a></li><li><a href="../ch1/hello2.html"><strong aria-hidden="true">3.5.</strong> hello2</a></li></ol></li><li><a href="../ch2/index.html" class="active"><strong aria-hidden="true">4.</strong> Ch 2: User Input</a></li><li><ol class="section"><li><a href="../ch2/the_key_input_register.html"><strong aria-hidden="true">4.1.</strong> The Key Input Register</a></li><li><a href="../ch2/the_vcount_register.html"><strong aria-hidden="true">4.2.</strong> The VCount Register</a></li><li><a href="../ch2/light_cycle.html"><strong aria-hidden="true">4.3.</strong> light_cycle</a></li></ol></li></ol>
|
<ol class="chapter"><li><a href="../introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="../ch00/index.html"><strong aria-hidden="true">2.</strong> Ch 0: Development Setup</a></li><li><a href="../ch01/index.html"><strong aria-hidden="true">3.</strong> Ch 1: Hello GBA</a></li><li><ol class="section"><li><a href="../ch01/hello1.html"><strong aria-hidden="true">3.1.</strong> hello1</a></li><li><a href="../ch01/volatile.html"><strong aria-hidden="true">3.2.</strong> Volatile</a></li><li><a href="../ch01/io_registers.html"><strong aria-hidden="true">3.3.</strong> IO Registers</a></li><li><a href="../ch01/the_display_control_register.html"><strong aria-hidden="true">3.4.</strong> The Display Control Register</a></li><li><a href="../ch01/video_memory_intro.html"><strong aria-hidden="true">3.5.</strong> Video Memory Intro</a></li><li><a href="../ch01/hello2.html"><strong aria-hidden="true">3.6.</strong> hello2</a></li></ol></li><li><a href="../ch02/index.html" class="active"><strong aria-hidden="true">4.</strong> Ch 2: User Input</a></li><li><ol class="section"><li><a href="../ch02/the_key_input_register.html"><strong aria-hidden="true">4.1.</strong> The Key Input Register</a></li><li><a href="../ch02/the_vcount_register.html"><strong aria-hidden="true">4.2.</strong> The VCount Register</a></li><li><a href="../ch02/light_cycle.html"><strong aria-hidden="true">4.3.</strong> light_cycle</a></li></ol></li><li><a href="../ch03/index.html"><strong aria-hidden="true">5.</strong> Ch 3: Memory and Objects</a></li><li><ol class="section"><li><a href="../ch03/gba_memory.html"><strong aria-hidden="true">5.1.</strong> GBA Memory</a></li><li><a href="../ch03/tiled_backgrounds.html"><strong aria-hidden="true">5.2.</strong> Tiled Backgrounds</a></li><li><a href="../ch03/object_basics.html"><strong aria-hidden="true">5.3.</strong> Object Basics</a></li><li><a href="../ch03/gba_rng.html"><strong aria-hidden="true">5.4.</strong> GBA RNG</a></li><li><a href="../ch03/memory_game.html"><strong aria-hidden="true">5.5.</strong> memory_game</a></li></ol></li></ol>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div id="page-wrapper" class="page-wrapper">
|
<div id="page-wrapper" class="page-wrapper">
|
||||||
|
@ -102,7 +102,7 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 class="menu-title">Rust GBA Tutorials</h1>
|
<h1 class="menu-title">Rust GBA Guide</h1>
|
||||||
|
|
||||||
<div class="right-buttons">
|
<div class="right-buttons">
|
||||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||||
|
@ -160,13 +160,13 @@ organized" for the long term.</p>
|
||||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||||
<!-- Mobile navigation buttons -->
|
<!-- Mobile navigation buttons -->
|
||||||
|
|
||||||
<a rel="prev" href="../ch1/hello2.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
<a rel="prev" href="../ch01/hello2.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
<i class="fa fa-angle-left"></i>
|
<i class="fa fa-angle-left"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a rel="next" href="../ch2/the_key_input_register.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
<a rel="next" href="../ch02/the_key_input_register.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
<i class="fa fa-angle-right"></i>
|
<i class="fa fa-angle-right"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
@ -178,13 +178,13 @@ organized" for the long term.</p>
|
||||||
|
|
||||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||||
|
|
||||||
<a href="../ch1/hello2.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
<a href="../ch01/hello2.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
<i class="fa fa-angle-left"></i>
|
<i class="fa fa-angle-left"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a href="../ch2/the_key_input_register.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
<a href="../ch02/the_key_input_register.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
<i class="fa fa-angle-right"></i>
|
<i class="fa fa-angle-right"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<!-- Book generated using mdBook -->
|
<!-- Book generated using mdBook -->
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>light_cycle - Rust GBA Tutorials</title>
|
<title>light_cycle - Rust GBA Guide</title>
|
||||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||||
<meta name="description" content="">
|
<meta name="description" content="">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
@ -72,7 +72,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||||
<ol class="chapter"><li><a href="../introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="../ch0/index.html"><strong aria-hidden="true">2.</strong> Ch 0: Development Setup</a></li><li><a href="../ch1/index.html"><strong aria-hidden="true">3.</strong> Ch 1: Hello GBA</a></li><li><ol class="section"><li><a href="../ch1/hello1.html"><strong aria-hidden="true">3.1.</strong> hello1</a></li><li><a href="../ch1/io_registers.html"><strong aria-hidden="true">3.2.</strong> IO Registers</a></li><li><a href="../ch1/the_display_control_register.html"><strong aria-hidden="true">3.3.</strong> The Display Control Register</a></li><li><a href="../ch1/video_memory_intro.html"><strong aria-hidden="true">3.4.</strong> Video Memory Intro</a></li><li><a href="../ch1/hello2.html"><strong aria-hidden="true">3.5.</strong> hello2</a></li></ol></li><li><a href="../ch2/index.html"><strong aria-hidden="true">4.</strong> Ch 2: User Input</a></li><li><ol class="section"><li><a href="../ch2/the_key_input_register.html"><strong aria-hidden="true">4.1.</strong> The Key Input Register</a></li><li><a href="../ch2/the_vcount_register.html"><strong aria-hidden="true">4.2.</strong> The VCount Register</a></li><li><a href="../ch2/light_cycle.html" class="active"><strong aria-hidden="true">4.3.</strong> light_cycle</a></li></ol></li></ol>
|
<ol class="chapter"><li><a href="../introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="../ch00/index.html"><strong aria-hidden="true">2.</strong> Ch 0: Development Setup</a></li><li><a href="../ch01/index.html"><strong aria-hidden="true">3.</strong> Ch 1: Hello GBA</a></li><li><ol class="section"><li><a href="../ch01/hello1.html"><strong aria-hidden="true">3.1.</strong> hello1</a></li><li><a href="../ch01/volatile.html"><strong aria-hidden="true">3.2.</strong> Volatile</a></li><li><a href="../ch01/io_registers.html"><strong aria-hidden="true">3.3.</strong> IO Registers</a></li><li><a href="../ch01/the_display_control_register.html"><strong aria-hidden="true">3.4.</strong> The Display Control Register</a></li><li><a href="../ch01/video_memory_intro.html"><strong aria-hidden="true">3.5.</strong> Video Memory Intro</a></li><li><a href="../ch01/hello2.html"><strong aria-hidden="true">3.6.</strong> hello2</a></li></ol></li><li><a href="../ch02/index.html"><strong aria-hidden="true">4.</strong> Ch 2: User Input</a></li><li><ol class="section"><li><a href="../ch02/the_key_input_register.html"><strong aria-hidden="true">4.1.</strong> The Key Input Register</a></li><li><a href="../ch02/the_vcount_register.html"><strong aria-hidden="true">4.2.</strong> The VCount Register</a></li><li><a href="../ch02/light_cycle.html" class="active"><strong aria-hidden="true">4.3.</strong> light_cycle</a></li></ol></li><li><a href="../ch03/index.html"><strong aria-hidden="true">5.</strong> Ch 3: Memory and Objects</a></li><li><ol class="section"><li><a href="../ch03/gba_memory.html"><strong aria-hidden="true">5.1.</strong> GBA Memory</a></li><li><a href="../ch03/tiled_backgrounds.html"><strong aria-hidden="true">5.2.</strong> Tiled Backgrounds</a></li><li><a href="../ch03/object_basics.html"><strong aria-hidden="true">5.3.</strong> Object Basics</a></li><li><a href="../ch03/gba_rng.html"><strong aria-hidden="true">5.4.</strong> GBA RNG</a></li><li><a href="../ch03/memory_game.html"><strong aria-hidden="true">5.5.</strong> memory_game</a></li></ol></li></ol>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div id="page-wrapper" class="page-wrapper">
|
<div id="page-wrapper" class="page-wrapper">
|
||||||
|
@ -102,7 +102,7 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 class="menu-title">Rust GBA Tutorials</h1>
|
<h1 class="menu-title">Rust GBA Guide</h1>
|
||||||
|
|
||||||
<div class="right-buttons">
|
<div class="right-buttons">
|
||||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||||
|
@ -150,21 +150,21 @@ go off the screen or if they touch their own trail.</p>
|
||||||
pub unsafe fn mode3_clear_screen(color: u16) {
|
pub unsafe fn mode3_clear_screen(color: u16) {
|
||||||
let color = color as u32;
|
let color = color as u32;
|
||||||
let bulk_color = color << 16 | color;
|
let bulk_color = color << 16 | color;
|
||||||
let mut ptr = VRAM as *mut u32;
|
let mut ptr = VolatilePtr(VRAM as *mut u32);
|
||||||
for _ in 0..SCREEN_HEIGHT {
|
for _ in 0..SCREEN_HEIGHT {
|
||||||
for _ in 0..(SCREEN_WIDTH / 2) {
|
for _ in 0..(SCREEN_WIDTH / 2) {
|
||||||
ptr.write_volatile(bulk_color);
|
ptr.write(bulk_color);
|
||||||
ptr = ptr.offset(1);
|
ptr = ptr.offset(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn mode3_draw_pixel(col: isize, row: isize, color: u16) {
|
pub unsafe fn mode3_draw_pixel(col: isize, row: isize, color: u16) {
|
||||||
(VRAM as *mut u16).offset(col + row * SCREEN_WIDTH).write_volatile(color);
|
VolatilePtr(VRAM as *mut u16).offset(col + row * SCREEN_WIDTH).write(color);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn mode3_read_pixel(col: isize, row: isize) -> u16 {
|
pub unsafe fn mode3_read_pixel(col: isize, row: isize) -> u16 {
|
||||||
(VRAM as *mut u16).offset(col + row * SCREEN_WIDTH).read_volatile()
|
VolatilePtr(VRAM as *mut u16).offset(col + row * SCREEN_WIDTH).read()
|
||||||
}
|
}
|
||||||
#}</code></pre></pre>
|
#}</code></pre></pre>
|
||||||
<p>The draw pixel and read pixel are both pretty obvious. What's new is the clear
|
<p>The draw pixel and read pixel are both pretty obvious. What's new is the clear
|
||||||
|
@ -176,7 +176,7 @@ screen clear is twice as fast.</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust">#[start]
|
<pre><pre class="playpen"><code class="language-rust">#[start]
|
||||||
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||||
unsafe {
|
unsafe {
|
||||||
DISPCNT.write_volatile(MODE3 | BG2);
|
DISPCNT.write(MODE3 | BG2);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut px = SCREEN_WIDTH / 2;
|
let mut px = SCREEN_WIDTH / 2;
|
||||||
|
@ -185,7 +185,7 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// read the input for this frame
|
// read the input for this frame
|
||||||
let this_frame_keys = read_key_input();
|
let this_frame_keys = key_input();
|
||||||
|
|
||||||
// adjust game state and wait for vblank
|
// adjust game state and wait for vblank
|
||||||
px += 2 * this_frame_keys.column_direction() as isize;
|
px += 2 * this_frame_keys.column_direction() as isize;
|
||||||
|
@ -236,21 +236,37 @@ different colors.</p>
|
||||||
it's not black that means we've been here before and the player has crashed into
|
it's not black that means we've been here before and the player has crashed into
|
||||||
their own line. In this case, we reset the game without moving them to a new
|
their own line. In this case, we reset the game without moving them to a new
|
||||||
location.</p>
|
location.</p>
|
||||||
<p>Finally, if the player is in bounds and they haven't crashed, we write their color into memory at this position.</p>
|
<p>Finally, if the player is in bounds and they haven't crashed, we write their
|
||||||
|
color into memory at this position.</p>
|
||||||
<p>Regardless of how it worked out, we hold here until vdraw starts before going to
|
<p>Regardless of how it worked out, we hold here until vdraw starts before going to
|
||||||
the next loop.</p>
|
the next loop. That's all there is to it.</p>
|
||||||
|
<a class="header" href="#the-gba-crate-doesnt-quite-work-like-this" id="the-gba-crate-doesnt-quite-work-like-this"><h2>The gba crate doesn't quite work like this</h2></a>
|
||||||
|
<p>Once again, as with the <code>hello1</code> and <code>hello2</code> examples, the <code>gba</code> crate covers
|
||||||
|
much of this same ground as our example here, but in slightly different ways.</p>
|
||||||
|
<p>Better organization and abstractions are usually only realized once you've used
|
||||||
|
more of the whole thing you're trying to work with. If we want to have a crate
|
||||||
|
where the whole thing is well integrated with itself, then the examples would
|
||||||
|
also end up having to explain about things we haven't really touched on much
|
||||||
|
yet. It becomes a lot harder to teach.</p>
|
||||||
|
<p>So, going forward, we will continue to teach concepts and build examples that
|
||||||
|
don't directly depend on the <code>gba</code> crate. This allows the crate to freely grow
|
||||||
|
without all the past examples becoming a great inertia upon it.</p>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||||
<!-- Mobile navigation buttons -->
|
<!-- Mobile navigation buttons -->
|
||||||
|
|
||||||
<a rel="prev" href="../ch2/the_vcount_register.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
<a rel="prev" href="../ch02/the_vcount_register.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
<i class="fa fa-angle-left"></i>
|
<i class="fa fa-angle-left"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a rel="next" href="../ch03/index.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
|
<i class="fa fa-angle-right"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
<div style="clear: both"></div>
|
<div style="clear: both"></div>
|
||||||
</nav>
|
</nav>
|
||||||
|
@ -259,12 +275,16 @@ the next loop.</p>
|
||||||
|
|
||||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||||
|
|
||||||
<a href="../ch2/the_vcount_register.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
<a href="../ch02/the_vcount_register.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
<i class="fa fa-angle-left"></i>
|
<i class="fa fa-angle-left"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a href="../ch03/index.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
|
<i class="fa fa-angle-right"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
</div>
|
</div>
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<!-- Book generated using mdBook -->
|
<!-- Book generated using mdBook -->
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>The Key Input Register - Rust GBA Tutorials</title>
|
<title>The Key Input Register - Rust GBA Guide</title>
|
||||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||||
<meta name="description" content="">
|
<meta name="description" content="">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
@ -72,7 +72,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||||
<ol class="chapter"><li><a href="../introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="../ch0/index.html"><strong aria-hidden="true">2.</strong> Ch 0: Development Setup</a></li><li><a href="../ch1/index.html"><strong aria-hidden="true">3.</strong> Ch 1: Hello GBA</a></li><li><ol class="section"><li><a href="../ch1/hello1.html"><strong aria-hidden="true">3.1.</strong> hello1</a></li><li><a href="../ch1/io_registers.html"><strong aria-hidden="true">3.2.</strong> IO Registers</a></li><li><a href="../ch1/the_display_control_register.html"><strong aria-hidden="true">3.3.</strong> The Display Control Register</a></li><li><a href="../ch1/video_memory_intro.html"><strong aria-hidden="true">3.4.</strong> Video Memory Intro</a></li><li><a href="../ch1/hello2.html"><strong aria-hidden="true">3.5.</strong> hello2</a></li></ol></li><li><a href="../ch2/index.html"><strong aria-hidden="true">4.</strong> Ch 2: User Input</a></li><li><ol class="section"><li><a href="../ch2/the_key_input_register.html" class="active"><strong aria-hidden="true">4.1.</strong> The Key Input Register</a></li><li><a href="../ch2/the_vcount_register.html"><strong aria-hidden="true">4.2.</strong> The VCount Register</a></li><li><a href="../ch2/light_cycle.html"><strong aria-hidden="true">4.3.</strong> light_cycle</a></li></ol></li></ol>
|
<ol class="chapter"><li><a href="../introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="../ch00/index.html"><strong aria-hidden="true">2.</strong> Ch 0: Development Setup</a></li><li><a href="../ch01/index.html"><strong aria-hidden="true">3.</strong> Ch 1: Hello GBA</a></li><li><ol class="section"><li><a href="../ch01/hello1.html"><strong aria-hidden="true">3.1.</strong> hello1</a></li><li><a href="../ch01/volatile.html"><strong aria-hidden="true">3.2.</strong> Volatile</a></li><li><a href="../ch01/io_registers.html"><strong aria-hidden="true">3.3.</strong> IO Registers</a></li><li><a href="../ch01/the_display_control_register.html"><strong aria-hidden="true">3.4.</strong> The Display Control Register</a></li><li><a href="../ch01/video_memory_intro.html"><strong aria-hidden="true">3.5.</strong> Video Memory Intro</a></li><li><a href="../ch01/hello2.html"><strong aria-hidden="true">3.6.</strong> hello2</a></li></ol></li><li><a href="../ch02/index.html"><strong aria-hidden="true">4.</strong> Ch 2: User Input</a></li><li><ol class="section"><li><a href="../ch02/the_key_input_register.html" class="active"><strong aria-hidden="true">4.1.</strong> The Key Input Register</a></li><li><a href="../ch02/the_vcount_register.html"><strong aria-hidden="true">4.2.</strong> The VCount Register</a></li><li><a href="../ch02/light_cycle.html"><strong aria-hidden="true">4.3.</strong> light_cycle</a></li></ol></li><li><a href="../ch03/index.html"><strong aria-hidden="true">5.</strong> Ch 3: Memory and Objects</a></li><li><ol class="section"><li><a href="../ch03/gba_memory.html"><strong aria-hidden="true">5.1.</strong> GBA Memory</a></li><li><a href="../ch03/tiled_backgrounds.html"><strong aria-hidden="true">5.2.</strong> Tiled Backgrounds</a></li><li><a href="../ch03/object_basics.html"><strong aria-hidden="true">5.3.</strong> Object Basics</a></li><li><a href="../ch03/gba_rng.html"><strong aria-hidden="true">5.4.</strong> GBA RNG</a></li><li><a href="../ch03/memory_game.html"><strong aria-hidden="true">5.5.</strong> memory_game</a></li></ol></li></ol>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div id="page-wrapper" class="page-wrapper">
|
<div id="page-wrapper" class="page-wrapper">
|
||||||
|
@ -102,7 +102,7 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 class="menu-title">Rust GBA Tutorials</h1>
|
<h1 class="menu-title">Rust GBA Guide</h1>
|
||||||
|
|
||||||
<div class="right-buttons">
|
<div class="right-buttons">
|
||||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||||
|
@ -185,15 +185,15 @@ reading and writing the key bits.</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust">
|
<pre><pre class="playpen"><code class="language-rust">
|
||||||
# #![allow(unused_variables)]
|
# #![allow(unused_variables)]
|
||||||
#fn main() {
|
#fn main() {
|
||||||
pub const KEYINPUT: *mut u16 = 0x400_0130 as *mut u16;
|
pub const KEYINPUT: VolatilePtr<u16> = VolatilePtr(0x400_0130 as *mut u16);
|
||||||
|
|
||||||
/// A newtype over the key input state of the GBA.
|
/// A newtype over the key input state of the GBA.
|
||||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct KeyInputSetting(u16);
|
pub struct KeyInputSetting(u16);
|
||||||
|
|
||||||
pub fn read_key_input() -> KeyInputSetting {
|
pub fn key_input() -> KeyInputSetting {
|
||||||
unsafe { KeyInputSetting(KEYINPUT.read_volatile()) }
|
unsafe { KeyInputSetting(KEYINPUT.read()) }
|
||||||
}
|
}
|
||||||
#}</code></pre></pre>
|
#}</code></pre></pre>
|
||||||
<p>Now we want a way to check if a key is <em>being pressed</em>, since that's normally
|
<p>Now we want a way to check if a key is <em>being pressed</em>, since that's normally
|
||||||
|
@ -218,12 +218,14 @@ folded and refolded by the compiler, but we can just check.</p>
|
||||||
<p>It turns out that the <code>!=0</code> test is 4 instructions and the <code>==0</code> test is 6
|
<p>It turns out that the <code>!=0</code> test is 4 instructions and the <code>==0</code> test is 6
|
||||||
instructions. Since we want to get savings where we can, and we'll probably
|
instructions. Since we want to get savings where we can, and we'll probably
|
||||||
check the keys of an input often enough, we'll just always use a <code>!=0</code> test and
|
check the keys of an input often enough, we'll just always use a <code>!=0</code> test and
|
||||||
then adjust how we initially read the register to compensate.</p>
|
then adjust how we initially read the register to compensate. By using xor with
|
||||||
|
a mask for only the 10 used bits we can flip the "low when pressed" values so
|
||||||
|
that the entire result has active bits in all positions where a key is pressed.</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust">
|
<pre><pre class="playpen"><code class="language-rust">
|
||||||
# #![allow(unused_variables)]
|
# #![allow(unused_variables)]
|
||||||
#fn main() {
|
#fn main() {
|
||||||
pub fn read_key_input() -> KeyInputSetting {
|
pub fn key_input() -> KeyInputSetting {
|
||||||
unsafe { KeyInputSetting(KEYINPUT.read_volatile() ^ 0b1111_1111_1111_1111) }
|
unsafe { KeyInputSetting(KEYINPUT.read_volatile() ^ 0b0000_0011_1111_1111) }
|
||||||
}
|
}
|
||||||
#}</code></pre></pre>
|
#}</code></pre></pre>
|
||||||
<p>Now we add a method for seeing if a key is pressed. In the full library there's
|
<p>Now we add a method for seeing if a key is pressed. In the full library there's
|
||||||
|
@ -339,13 +341,13 @@ stuff, but we'll cover that later on because it's not necessary right now.</p>
|
||||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||||
<!-- Mobile navigation buttons -->
|
<!-- Mobile navigation buttons -->
|
||||||
|
|
||||||
<a rel="prev" href="../ch2/index.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
<a rel="prev" href="../ch02/index.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
<i class="fa fa-angle-left"></i>
|
<i class="fa fa-angle-left"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a rel="next" href="../ch2/the_vcount_register.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
<a rel="next" href="../ch02/the_vcount_register.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
<i class="fa fa-angle-right"></i>
|
<i class="fa fa-angle-right"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
@ -357,13 +359,13 @@ stuff, but we'll cover that later on because it's not necessary right now.</p>
|
||||||
|
|
||||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||||
|
|
||||||
<a href="../ch2/index.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
<a href="../ch02/index.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
<i class="fa fa-angle-left"></i>
|
<i class="fa fa-angle-left"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a href="../ch2/the_vcount_register.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
<a href="../ch02/the_vcount_register.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
<i class="fa fa-angle-right"></i>
|
<i class="fa fa-angle-right"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<!-- Book generated using mdBook -->
|
<!-- Book generated using mdBook -->
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>The VCount Register - Rust GBA Tutorials</title>
|
<title>The VCount Register - Rust GBA Guide</title>
|
||||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||||
<meta name="description" content="">
|
<meta name="description" content="">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
@ -72,7 +72,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||||
<ol class="chapter"><li><a href="../introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="../ch0/index.html"><strong aria-hidden="true">2.</strong> Ch 0: Development Setup</a></li><li><a href="../ch1/index.html"><strong aria-hidden="true">3.</strong> Ch 1: Hello GBA</a></li><li><ol class="section"><li><a href="../ch1/hello1.html"><strong aria-hidden="true">3.1.</strong> hello1</a></li><li><a href="../ch1/io_registers.html"><strong aria-hidden="true">3.2.</strong> IO Registers</a></li><li><a href="../ch1/the_display_control_register.html"><strong aria-hidden="true">3.3.</strong> The Display Control Register</a></li><li><a href="../ch1/video_memory_intro.html"><strong aria-hidden="true">3.4.</strong> Video Memory Intro</a></li><li><a href="../ch1/hello2.html"><strong aria-hidden="true">3.5.</strong> hello2</a></li></ol></li><li><a href="../ch2/index.html"><strong aria-hidden="true">4.</strong> Ch 2: User Input</a></li><li><ol class="section"><li><a href="../ch2/the_key_input_register.html"><strong aria-hidden="true">4.1.</strong> The Key Input Register</a></li><li><a href="../ch2/the_vcount_register.html" class="active"><strong aria-hidden="true">4.2.</strong> The VCount Register</a></li><li><a href="../ch2/light_cycle.html"><strong aria-hidden="true">4.3.</strong> light_cycle</a></li></ol></li></ol>
|
<ol class="chapter"><li><a href="../introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="../ch00/index.html"><strong aria-hidden="true">2.</strong> Ch 0: Development Setup</a></li><li><a href="../ch01/index.html"><strong aria-hidden="true">3.</strong> Ch 1: Hello GBA</a></li><li><ol class="section"><li><a href="../ch01/hello1.html"><strong aria-hidden="true">3.1.</strong> hello1</a></li><li><a href="../ch01/volatile.html"><strong aria-hidden="true">3.2.</strong> Volatile</a></li><li><a href="../ch01/io_registers.html"><strong aria-hidden="true">3.3.</strong> IO Registers</a></li><li><a href="../ch01/the_display_control_register.html"><strong aria-hidden="true">3.4.</strong> The Display Control Register</a></li><li><a href="../ch01/video_memory_intro.html"><strong aria-hidden="true">3.5.</strong> Video Memory Intro</a></li><li><a href="../ch01/hello2.html"><strong aria-hidden="true">3.6.</strong> hello2</a></li></ol></li><li><a href="../ch02/index.html"><strong aria-hidden="true">4.</strong> Ch 2: User Input</a></li><li><ol class="section"><li><a href="../ch02/the_key_input_register.html"><strong aria-hidden="true">4.1.</strong> The Key Input Register</a></li><li><a href="../ch02/the_vcount_register.html" class="active"><strong aria-hidden="true">4.2.</strong> The VCount Register</a></li><li><a href="../ch02/light_cycle.html"><strong aria-hidden="true">4.3.</strong> light_cycle</a></li></ol></li><li><a href="../ch03/index.html"><strong aria-hidden="true">5.</strong> Ch 3: Memory and Objects</a></li><li><ol class="section"><li><a href="../ch03/gba_memory.html"><strong aria-hidden="true">5.1.</strong> GBA Memory</a></li><li><a href="../ch03/tiled_backgrounds.html"><strong aria-hidden="true">5.2.</strong> Tiled Backgrounds</a></li><li><a href="../ch03/object_basics.html"><strong aria-hidden="true">5.3.</strong> Object Basics</a></li><li><a href="../ch03/gba_rng.html"><strong aria-hidden="true">5.4.</strong> GBA RNG</a></li><li><a href="../ch03/memory_game.html"><strong aria-hidden="true">5.5.</strong> memory_game</a></li></ol></li></ol>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div id="page-wrapper" class="page-wrapper">
|
<div id="page-wrapper" class="page-wrapper">
|
||||||
|
@ -102,7 +102,7 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 class="menu-title">Rust GBA Tutorials</h1>
|
<h1 class="menu-title">Rust GBA Guide</h1>
|
||||||
|
|
||||||
<div class="right-buttons">
|
<div class="right-buttons">
|
||||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||||
|
@ -183,10 +183,10 @@ quickly during the blank period.</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust">
|
<pre><pre class="playpen"><code class="language-rust">
|
||||||
# #![allow(unused_variables)]
|
# #![allow(unused_variables)]
|
||||||
#fn main() {
|
#fn main() {
|
||||||
pub const VCOUNT: *mut u16 = 0x0400_0006 as *mut u16;
|
pub const VCOUNT: VolatilePtr<u16> = VolatilePtr(0x0400_0006 as *mut u16);
|
||||||
|
|
||||||
pub fn read_vcount() -> u16 {
|
pub fn vcount() -> u16 {
|
||||||
unsafe { VCOUNT.read_volatile() }
|
unsafe { VCOUNT.read() }
|
||||||
}
|
}
|
||||||
#}</code></pre></pre>
|
#}</code></pre></pre>
|
||||||
<p>Then we want two little helper functions to wait until VBlank and vdraw.</p>
|
<p>Then we want two little helper functions to wait until VBlank and vdraw.</p>
|
||||||
|
@ -196,11 +196,11 @@ pub fn read_vcount() -> u16 {
|
||||||
pub const SCREEN_HEIGHT: isize = 160;
|
pub const SCREEN_HEIGHT: isize = 160;
|
||||||
|
|
||||||
pub fn wait_until_vblank() {
|
pub fn wait_until_vblank() {
|
||||||
while read_vcount() < SCREEN_HEIGHT as u16 {}
|
while vcount() < SCREEN_HEIGHT as u16 {}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wait_until_vdraw() {
|
pub fn wait_until_vdraw() {
|
||||||
while read_vcount() >= SCREEN_HEIGHT as u16 {}
|
while vcount() >= SCREEN_HEIGHT as u16 {}
|
||||||
}
|
}
|
||||||
#}</code></pre></pre>
|
#}</code></pre></pre>
|
||||||
<p>And... that's it. No special types to be made this time around, it's just a
|
<p>And... that's it. No special types to be made this time around, it's just a
|
||||||
|
@ -211,13 +211,13 @@ number we read out of memory.</p>
|
||||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||||
<!-- Mobile navigation buttons -->
|
<!-- Mobile navigation buttons -->
|
||||||
|
|
||||||
<a rel="prev" href="../ch2/the_key_input_register.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
<a rel="prev" href="../ch02/the_key_input_register.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
<i class="fa fa-angle-left"></i>
|
<i class="fa fa-angle-left"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a rel="next" href="../ch2/light_cycle.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
<a rel="next" href="../ch02/light_cycle.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
<i class="fa fa-angle-right"></i>
|
<i class="fa fa-angle-right"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
@ -229,13 +229,13 @@ number we read out of memory.</p>
|
||||||
|
|
||||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||||
|
|
||||||
<a href="../ch2/the_key_input_register.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
<a href="../ch02/the_key_input_register.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
<i class="fa fa-angle-left"></i>
|
<i class="fa fa-angle-left"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a href="../ch2/light_cycle.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
<a href="../ch02/light_cycle.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
<i class="fa fa-angle-right"></i>
|
<i class="fa fa-angle-right"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
412
docs/ch03/gba_memory.html
Normal file
412
docs/ch03/gba_memory.html
Normal file
|
@ -0,0 +1,412 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html lang="en" class="sidebar-visible no-js">
|
||||||
|
<head>
|
||||||
|
<!-- Book generated using mdBook -->
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>GBA Memory - Rust GBA Guide</title>
|
||||||
|
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||||
|
<meta name="description" content="">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="theme-color" content="#ffffff" />
|
||||||
|
|
||||||
|
<link rel="shortcut icon" href="../favicon.png">
|
||||||
|
<link rel="stylesheet" href="../css/variables.css">
|
||||||
|
<link rel="stylesheet" href="../css/general.css">
|
||||||
|
<link rel="stylesheet" href="../css/chrome.css">
|
||||||
|
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||||
|
|
||||||
|
<!-- Fonts -->
|
||||||
|
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800" rel="stylesheet" type="text/css">
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Source+Code+Pro:500" rel="stylesheet" type="text/css">
|
||||||
|
|
||||||
|
<!-- Highlight.js Stylesheets -->
|
||||||
|
<link rel="stylesheet" href="../highlight.css">
|
||||||
|
<link rel="stylesheet" href="../tomorrow-night.css">
|
||||||
|
<link rel="stylesheet" href="../ayu-highlight.css">
|
||||||
|
|
||||||
|
<!-- Custom theme stylesheets -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body class="light">
|
||||||
|
<!-- Provide site root to javascript -->
|
||||||
|
<script type="text/javascript">var path_to_root = "../";</script>
|
||||||
|
|
||||||
|
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
try {
|
||||||
|
var theme = localStorage.getItem('mdbook-theme');
|
||||||
|
var sidebar = localStorage.getItem('mdbook-sidebar');
|
||||||
|
|
||||||
|
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||||
|
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||||
|
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||||
|
}
|
||||||
|
} catch (e) { }
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
var theme;
|
||||||
|
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||||
|
if (theme === null || theme === undefined) { theme = 'light'; }
|
||||||
|
document.body.className = theme;
|
||||||
|
document.querySelector('html').className = theme + ' js';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Hide / unhide sidebar before it is displayed -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
var html = document.querySelector('html');
|
||||||
|
var sidebar = 'hidden';
|
||||||
|
if (document.body.clientWidth >= 1080) {
|
||||||
|
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||||
|
sidebar = sidebar || 'visible';
|
||||||
|
}
|
||||||
|
html.classList.remove('sidebar-visible');
|
||||||
|
html.classList.add("sidebar-" + sidebar);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||||
|
<ol class="chapter"><li><a href="../introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="../ch00/index.html"><strong aria-hidden="true">2.</strong> Ch 0: Development Setup</a></li><li><a href="../ch01/index.html"><strong aria-hidden="true">3.</strong> Ch 1: Hello GBA</a></li><li><ol class="section"><li><a href="../ch01/hello1.html"><strong aria-hidden="true">3.1.</strong> hello1</a></li><li><a href="../ch01/volatile.html"><strong aria-hidden="true">3.2.</strong> Volatile</a></li><li><a href="../ch01/io_registers.html"><strong aria-hidden="true">3.3.</strong> IO Registers</a></li><li><a href="../ch01/the_display_control_register.html"><strong aria-hidden="true">3.4.</strong> The Display Control Register</a></li><li><a href="../ch01/video_memory_intro.html"><strong aria-hidden="true">3.5.</strong> Video Memory Intro</a></li><li><a href="../ch01/hello2.html"><strong aria-hidden="true">3.6.</strong> hello2</a></li></ol></li><li><a href="../ch02/index.html"><strong aria-hidden="true">4.</strong> Ch 2: User Input</a></li><li><ol class="section"><li><a href="../ch02/the_key_input_register.html"><strong aria-hidden="true">4.1.</strong> The Key Input Register</a></li><li><a href="../ch02/the_vcount_register.html"><strong aria-hidden="true">4.2.</strong> The VCount Register</a></li><li><a href="../ch02/light_cycle.html"><strong aria-hidden="true">4.3.</strong> light_cycle</a></li></ol></li><li><a href="../ch03/index.html"><strong aria-hidden="true">5.</strong> Ch 3: Memory and Objects</a></li><li><ol class="section"><li><a href="../ch03/gba_memory.html" class="active"><strong aria-hidden="true">5.1.</strong> GBA Memory</a></li><li><a href="../ch03/tiled_backgrounds.html"><strong aria-hidden="true">5.2.</strong> Tiled Backgrounds</a></li><li><a href="../ch03/object_basics.html"><strong aria-hidden="true">5.3.</strong> Object Basics</a></li><li><a href="../ch03/gba_rng.html"><strong aria-hidden="true">5.4.</strong> GBA RNG</a></li><li><a href="../ch03/memory_game.html"><strong aria-hidden="true">5.5.</strong> memory_game</a></li></ol></li></ol>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div id="page-wrapper" class="page-wrapper">
|
||||||
|
|
||||||
|
<div class="page">
|
||||||
|
|
||||||
|
<div id="menu-bar" class="menu-bar">
|
||||||
|
<div id="menu-bar-sticky-container">
|
||||||
|
<div class="left-buttons">
|
||||||
|
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||||
|
<i class="fa fa-bars"></i>
|
||||||
|
</button>
|
||||||
|
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||||
|
<i class="fa fa-paint-brush"></i>
|
||||||
|
</button>
|
||||||
|
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="light">Light <span class="default">(default)</span></button></li>
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
||||||
|
<i class="fa fa-search"></i>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1 class="menu-title">Rust GBA Guide</h1>
|
||||||
|
|
||||||
|
<div class="right-buttons">
|
||||||
|
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||||
|
<i id="print-button" class="fa fa-print"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="search-wrapper" class="hidden">
|
||||||
|
<form id="searchbar-outer" class="searchbar-outer">
|
||||||
|
<input type="search" name="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||||
|
</form>
|
||||||
|
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||||
|
<div id="searchresults-header" class="searchresults-header"></div>
|
||||||
|
<ul id="searchresults">
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||||
|
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||||
|
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||||
|
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div id="content" class="content">
|
||||||
|
<main>
|
||||||
|
<a class="header" href="#gba-memory" id="gba-memory"><h1>GBA Memory</h1></a>
|
||||||
|
<p>The <a href="http://problemkaputt.de/gbatek.htm#gbamemorymap">GBA Memory Map</a> has
|
||||||
|
several memory portions to it, each with their own little differences. Most of
|
||||||
|
the memory has pre-determined use according to the hardware, but there is also
|
||||||
|
space for games to use as a scratch pad in whatever way the game sees fit.</p>
|
||||||
|
<p>The memory ranges listed here are <em>inclusive</em>, so they end with a lot of <code>F</code>s
|
||||||
|
and <code>E</code>s.</p>
|
||||||
|
<p>We've talked about volatile memory before, but just as a reminder I'll say that
|
||||||
|
all of the memory we'll talk about here should be accessed with volatile with
|
||||||
|
two exceptions:</p>
|
||||||
|
<ol>
|
||||||
|
<li>Work RAM (both internal and external) can be used normally, and if the
|
||||||
|
compiler is able to totally elide any reads and writes that's okay.</li>
|
||||||
|
<li>However, if you set aside any space in Work RAM where an interrupt will
|
||||||
|
communicate with the main program then that specific location will have to
|
||||||
|
keep using volatile access, since the compiler never knows when an interrupt
|
||||||
|
will actually happen.</li>
|
||||||
|
</ol>
|
||||||
|
<a class="header" href="#bios--system-rom" id="bios--system-rom"><h2>BIOS / System ROM</h2></a>
|
||||||
|
<ul>
|
||||||
|
<li><code>0x0</code> to <code>0x3FFF</code> (16k)</li>
|
||||||
|
</ul>
|
||||||
|
<p>This is special memory for the BIOS. It is "read-only", but even then it's only
|
||||||
|
accessible when the program counter is pointing into the BIOS region. At all
|
||||||
|
other times you get a <a href="http://problemkaputt.de/gbatek.htm#gbaunpredictablethings">garbage
|
||||||
|
value</a> back when you
|
||||||
|
try to read out of the BIOS.</p>
|
||||||
|
<a class="header" href="#external-work-ram--ewram" id="external-work-ram--ewram"><h2>External Work RAM / EWRAM</h2></a>
|
||||||
|
<ul>
|
||||||
|
<li><code>0x2000000</code> to <code>0x203FFFF</code> (256k)</li>
|
||||||
|
</ul>
|
||||||
|
<p>This is a big pile of space, the use of which is up to each game. However, the
|
||||||
|
external work ram has only a 16-bit bus (if you read/write a 32-bit value it
|
||||||
|
silently breaks it up into two 16-bit operations) and also 2 wait cycles (extra
|
||||||
|
CPU cycles that you have to expend <em>per 16-bit bus use</em>).</p>
|
||||||
|
<p>In other words, we should think of EWRAM as if it was "heap space" in a normal
|
||||||
|
application. You can take the time to go store something within EWRAM, or to
|
||||||
|
load it out of EWRAM, but you should always avoid doing a critical computation
|
||||||
|
on values in EWRAM. It's a bit of a pain, but if you wanna be speedy and you
|
||||||
|
have more than just one manipulation that you want to do, you should pull the
|
||||||
|
value into a local variable, do all of your manipulations, and then push it back
|
||||||
|
out at the end.</p>
|
||||||
|
<a class="header" href="#internal-work-ram--iwram" id="internal-work-ram--iwram"><h2>Internal Work RAM / IWRAM</h2></a>
|
||||||
|
<ul>
|
||||||
|
<li><code>0x3000000</code> to <code>0x3007FFF</code> (32k)</li>
|
||||||
|
</ul>
|
||||||
|
<p>This is a smaller pile of space, but it has a 32-bit bus and no wait.</p>
|
||||||
|
<p>By default, <code>0x3007F00</code> to <code>0x3007FFF</code> is reserved for interrupt and BIOS use.
|
||||||
|
The rest of it is totally up to you. The user's stack space starts at
|
||||||
|
<code>0x3007F00</code> and proceeds <em>down</em> from there. In other words, if you start your
|
||||||
|
own customized IWRAM use at <code>0x3000000</code> and go up, eventually you might hit your
|
||||||
|
stack. However, most reasonable uses won't actually cause a memory collision.
|
||||||
|
It's just something you should know about if you're using a ton of stack or
|
||||||
|
IWRAM and then get problems.</p>
|
||||||
|
<a class="header" href="#io-registers" id="io-registers"><h2>IO Registers</h2></a>
|
||||||
|
<ul>
|
||||||
|
<li><code>0x4000000</code> to <code>0x40003FE</code></li>
|
||||||
|
</ul>
|
||||||
|
<p>We've touched upon a few of these so far, and we'll get to more later. At the
|
||||||
|
moment it is enough to say that, as you might have guessed, all of them live in
|
||||||
|
this region. Each individual register is a <code>u16</code> or <code>u32</code> and they control all
|
||||||
|
sorts of things. We'll actually be talking about some more of them in this very
|
||||||
|
chapter, because that's how we'll control some of the background and object
|
||||||
|
stuff.</p>
|
||||||
|
<a class="header" href="#palette-ram--palram" id="palette-ram--palram"><h2>Palette RAM / PALRAM</h2></a>
|
||||||
|
<ul>
|
||||||
|
<li><code>0x5000000</code> to <code>0x50003FF</code> (1k)</li>
|
||||||
|
</ul>
|
||||||
|
<p>Palette RAM has a 16-bit bus, which isn't really a problem because it
|
||||||
|
conceptually just holds <code>u16</code> values. There's no automatic wait state, but if
|
||||||
|
you try to access the same location that the display controller is accessing you
|
||||||
|
get bumped by 1 cycle. Since the display controller can use the palette ram any
|
||||||
|
number of times per scanline it's basically impossible to predict if you'll have
|
||||||
|
to do a wait or not during VDraw. During VBlank you won't have any wait of
|
||||||
|
course.</p>
|
||||||
|
<p>PALRAM is among the memory where there's weirdness if you try to write just one
|
||||||
|
byte: if you try to write just 1 byte, it writes that byte into <em>both</em> parts of
|
||||||
|
the larger 16-bit location. This doesn't really affect us much with PALRAM,
|
||||||
|
because palette values are all supposed to be <code>u16</code> anyway.</p>
|
||||||
|
<p>The palette memory actually contains not one, but <em>two</em> sets of palettes. First
|
||||||
|
there's 256 entries for the background palette data (starting at <code>0x5000000</code>),
|
||||||
|
and then there's 256 entries for object palette data (starting at <code>0x5000200</code>).</p>
|
||||||
|
<p>The GBA also has two modes for palette access: 8-bits-per-pixel (8bpp) and
|
||||||
|
4-bits-per-pixel (4bpp).</p>
|
||||||
|
<ul>
|
||||||
|
<li>In 8bpp mode an (8-bit) palette index value within a background or sprite
|
||||||
|
simply indexes directly into the 256 slots for that type of thing.</li>
|
||||||
|
<li>In 4bpp mode a (4-bit) palette index value within a background or sprite
|
||||||
|
specifies an index within a particular "palbank" (16 palette entries each),
|
||||||
|
and then a <em>separate</em> setting outside of the graphical data determines which
|
||||||
|
palbank is to be used for that background or object (the screen entry data for
|
||||||
|
backgrounds, and the object attributes for objects).</li>
|
||||||
|
</ul>
|
||||||
|
<a class="header" href="#video-ram--vram" id="video-ram--vram"><h2>Video RAM / VRAM</h2></a>
|
||||||
|
<ul>
|
||||||
|
<li><code>0x6000000</code> to <code>0x6017FFF</code> (96k)</li>
|
||||||
|
</ul>
|
||||||
|
<p>We've used this before! VRAM has a 16-bit bus and no wait. However, the same as
|
||||||
|
with PALRAM, the "you might have to wait if the display controller is looking at
|
||||||
|
it" rule applies here.</p>
|
||||||
|
<p>Unfortunately there's not much more exact detail that can be given about VRAM.
|
||||||
|
The use of the memory depends on the video mode that you're using.</p>
|
||||||
|
<p>One general detail of note is that you can't write individual bytes to any part
|
||||||
|
of VRAM. Depending on mode and location, you'll either get your bytes doubled
|
||||||
|
into both the upper and lower parts of the 16-bit location targeted, or you
|
||||||
|
won't even affect the memory. This usually isn't a big deal, except in two
|
||||||
|
situations:</p>
|
||||||
|
<ul>
|
||||||
|
<li>In Mode 4, if you want to change just 1 pixel, you'll have to be very careful
|
||||||
|
to read the old <code>u16</code>, overwrite just the byte you wanted to change, and then
|
||||||
|
write that back.</li>
|
||||||
|
<li>In any display mode, avoid using <code>memcopy</code> to place things into VRAM.
|
||||||
|
It's written to be byte oriented, and only does 32-bit transfers under select
|
||||||
|
conditions. The rest of the time it'll copy one byte at a time and you'll get
|
||||||
|
either garbage or nothing at all.</li>
|
||||||
|
</ul>
|
||||||
|
<a class="header" href="#object-attribute-memory--oam" id="object-attribute-memory--oam"><h2>Object Attribute Memory / OAM</h2></a>
|
||||||
|
<ul>
|
||||||
|
<li><code>0x7000000</code> to <code>0x70003FF</code> (1k)</li>
|
||||||
|
</ul>
|
||||||
|
<p>The Object Attribute Memory has a 32-bit bus and no default wait, but suffers
|
||||||
|
from the "you might have to wait if the display controller is looking at it"
|
||||||
|
rule. You cannot write individual bytes to OAM at all, but that's not really a
|
||||||
|
problem because all the fields of the data types within OAM are either <code>i16</code> or
|
||||||
|
<code>u16</code> anyway.</p>
|
||||||
|
<p>Object attribute memory is the wildest yet: it conceptually contains two types
|
||||||
|
of things, but they're <em>interlaced</em> with each other all the way through.</p>
|
||||||
|
<p>Now, <a href="http://problemkaputt.de/gbatek.htm#lcdobjoamattributes">GBATEK</a> and
|
||||||
|
<a href="https://www.cs.rit.edu/%7Etjh8300/CowBite/CowBiteSpec.htm#OAM%20(sprites)">CowByte</a>
|
||||||
|
doesn't quite give names to the two data types, though
|
||||||
|
<a href="https://www.coranac.com/tonc/text/regobj.htm#sec-oam">TONC</a> calls them
|
||||||
|
<code>OBJ_ATTR</code> and <code>OBJ_AFFINE</code>. We'll give them Rust names of course. In Rust terms
|
||||||
|
their layout would look like this:</p>
|
||||||
|
<pre><pre class="playpen"><code class="language-rust">
|
||||||
|
# #![allow(unused_variables)]
|
||||||
|
#fn main() {
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct ObjectAttribute {
|
||||||
|
attr0: u16,
|
||||||
|
attr1: u16,
|
||||||
|
attr2: u16,
|
||||||
|
filler: i16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct AffineMatrix {
|
||||||
|
filler0: [u16; 3],
|
||||||
|
pa: i16,
|
||||||
|
filler1: [u16; 3],
|
||||||
|
pb: i16,
|
||||||
|
filler2: [u16; 3],
|
||||||
|
pc: i16,
|
||||||
|
filler3: [u16; 3],
|
||||||
|
pd: i16,
|
||||||
|
}
|
||||||
|
#}</code></pre></pre>
|
||||||
|
<p>(Note: the <code>#[repr(C)]</code> part just means that Rust must lay out the data exactly
|
||||||
|
in the order we specify, which otherwise it is not required to do).</p>
|
||||||
|
<p>So, we've got 1024 bytes in OAM and each <code>ObjectAttribute</code> value is 8 bytes, so
|
||||||
|
naturally we can support up to 128 objects.</p>
|
||||||
|
<p><em>At the same time</em>, we've got 1024 bytes in OAM and each <code>AffineMatrix</code> is 32
|
||||||
|
bytes, so we can have 32 of them.</p>
|
||||||
|
<p>But, as I said, these things are all <em>interlaced</em> with each other. See how
|
||||||
|
there's "filler" fields in each struct? If we imagine the OAM as being just an
|
||||||
|
array of one type or the other, indexes 0/1/2/3 of the <code>ObjectAttribute</code> array
|
||||||
|
would line up with index 0 of the <code>AffineMatrix</code> array. It's kinda weird, but
|
||||||
|
that's just how it works. When we setup functions to read and write these values
|
||||||
|
we'll have to be careful with how we do it. We probably <em>won't</em> want to use
|
||||||
|
those representations above, at least not with the <code>AffineMatrix</code> type, because
|
||||||
|
they're quite wasteful if you want to store just object attributes or just
|
||||||
|
affine matrices.</p>
|
||||||
|
<a class="header" href="#game-pak-rom--flash-rom" id="game-pak-rom--flash-rom"><h2>Game Pak ROM / Flash ROM</h2></a>
|
||||||
|
<ul>
|
||||||
|
<li><code>0x8000000</code> to <code>0x9FFFFFF</code> (wait 0)</li>
|
||||||
|
<li><code>0xA000000</code> to <code>0xBFFFFFF</code> (wait 1)</li>
|
||||||
|
<li><code>0xC000000</code> to <code>0xDFFFFFF</code> (wait 2)</li>
|
||||||
|
<li>Max of 32Mb</li>
|
||||||
|
</ul>
|
||||||
|
<p>These portions of the memory are less fixed, because they depend on the precise
|
||||||
|
details of the game pak you've inserted into the GBA. In general, they connect
|
||||||
|
to the game pak ROM and/or Flash memory, using a 16-bit bus. The ROM is
|
||||||
|
read-only, but the Flash memory (if any) allows writes.</p>
|
||||||
|
<p>The game pak ROM is listed as being in three sections, but it's actually the
|
||||||
|
same memory being effectively mirrored into three different locations. The
|
||||||
|
mirror that you choose to access the game pak through affects which wait state
|
||||||
|
setting it uses (configured via IO register of course). Unfortunately, the
|
||||||
|
details come down more to the game pak hardware that you load your game onto
|
||||||
|
than anything else, so there's not much I can say right here. We'll eventually
|
||||||
|
talk about it more later,</p>
|
||||||
|
<p>One thing of note is the way that the 16-bit bus affects us: the instructions to
|
||||||
|
execute are coming through the same bus as the rest of the game data, so we want
|
||||||
|
them to be as compact as possible. The ARM chip in the GBA supports two
|
||||||
|
different instruction sets, "thumb" and "non-thumb". The thumb mode instructions
|
||||||
|
are 16-bit, so they can each be loaded one at a time, and the non-thumb
|
||||||
|
instructions are 32-bit, so we're at a penalty if we execute them directly out
|
||||||
|
of the game pak. However, some things will demand that we use non-thumb code, so
|
||||||
|
we'll have to deal with that eventually. It's possible to switch between modes,
|
||||||
|
but it's a pain to keep track of what mode you're in because there's not
|
||||||
|
currently support for it in Rust itself (perhaps some day). So we'll stick with
|
||||||
|
thumb code as much as we possibly can, that's why our target profile for our
|
||||||
|
builds starts with <code>thumbv4</code>.</p>
|
||||||
|
<a class="header" href="#game-pak-sram" id="game-pak-sram"><h2>Game Pak SRAM</h2></a>
|
||||||
|
<ul>
|
||||||
|
<li><code>0xE000000</code> to <code>0xE00FFFF</code> (64k)</li>
|
||||||
|
</ul>
|
||||||
|
<p>The game pak SRAM has an 8-bit bus. Why did pokemon always take so long to save?
|
||||||
|
This is why. It also has some amount of wait, but as with the ROM, the details
|
||||||
|
depend on your game pak hardware (and also as with ROM, you can adjust the
|
||||||
|
settings with an IO register, should you need to).</p>
|
||||||
|
<p>One thing to note about the SRAM is that the GBA has a Direct Memory Access
|
||||||
|
(DMA) feature that can be used for bulk memory movements in some cases, but the
|
||||||
|
DMA <em>cannot</em> access the SRAM region. You really are stuck reading and writing
|
||||||
|
one byte at a time when you're using the SRAM.</p>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||||
|
<!-- Mobile navigation buttons -->
|
||||||
|
|
||||||
|
<a rel="prev" href="../ch03/index.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
|
<i class="fa fa-angle-left"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a rel="next" href="../ch03/tiled_backgrounds.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
|
<i class="fa fa-angle-right"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
<div style="clear: both"></div>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||||
|
|
||||||
|
<a href="../ch03/index.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
|
<i class="fa fa-angle-left"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a href="../ch03/tiled_backgrounds.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
|
<i class="fa fa-angle-right"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script src="../elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="../mark.min.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="../searcher.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
|
||||||
|
|
||||||
|
<script src="../clipboard.min.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="../highlight.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="../book.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
|
||||||
|
<!-- Custom JS scripts -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
201
docs/ch03/gba_rng.html
Normal file
201
docs/ch03/gba_rng.html
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html lang="en" class="sidebar-visible no-js">
|
||||||
|
<head>
|
||||||
|
<!-- Book generated using mdBook -->
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>GBA RNG - Rust GBA Guide</title>
|
||||||
|
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||||
|
<meta name="description" content="">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="theme-color" content="#ffffff" />
|
||||||
|
|
||||||
|
<link rel="shortcut icon" href="../favicon.png">
|
||||||
|
<link rel="stylesheet" href="../css/variables.css">
|
||||||
|
<link rel="stylesheet" href="../css/general.css">
|
||||||
|
<link rel="stylesheet" href="../css/chrome.css">
|
||||||
|
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||||
|
|
||||||
|
<!-- Fonts -->
|
||||||
|
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800" rel="stylesheet" type="text/css">
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Source+Code+Pro:500" rel="stylesheet" type="text/css">
|
||||||
|
|
||||||
|
<!-- Highlight.js Stylesheets -->
|
||||||
|
<link rel="stylesheet" href="../highlight.css">
|
||||||
|
<link rel="stylesheet" href="../tomorrow-night.css">
|
||||||
|
<link rel="stylesheet" href="../ayu-highlight.css">
|
||||||
|
|
||||||
|
<!-- Custom theme stylesheets -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body class="light">
|
||||||
|
<!-- Provide site root to javascript -->
|
||||||
|
<script type="text/javascript">var path_to_root = "../";</script>
|
||||||
|
|
||||||
|
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
try {
|
||||||
|
var theme = localStorage.getItem('mdbook-theme');
|
||||||
|
var sidebar = localStorage.getItem('mdbook-sidebar');
|
||||||
|
|
||||||
|
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||||
|
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||||
|
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||||
|
}
|
||||||
|
} catch (e) { }
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
var theme;
|
||||||
|
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||||
|
if (theme === null || theme === undefined) { theme = 'light'; }
|
||||||
|
document.body.className = theme;
|
||||||
|
document.querySelector('html').className = theme + ' js';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Hide / unhide sidebar before it is displayed -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
var html = document.querySelector('html');
|
||||||
|
var sidebar = 'hidden';
|
||||||
|
if (document.body.clientWidth >= 1080) {
|
||||||
|
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||||
|
sidebar = sidebar || 'visible';
|
||||||
|
}
|
||||||
|
html.classList.remove('sidebar-visible');
|
||||||
|
html.classList.add("sidebar-" + sidebar);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||||
|
<ol class="chapter"><li><a href="../introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="../ch00/index.html"><strong aria-hidden="true">2.</strong> Ch 0: Development Setup</a></li><li><a href="../ch01/index.html"><strong aria-hidden="true">3.</strong> Ch 1: Hello GBA</a></li><li><ol class="section"><li><a href="../ch01/hello1.html"><strong aria-hidden="true">3.1.</strong> hello1</a></li><li><a href="../ch01/volatile.html"><strong aria-hidden="true">3.2.</strong> Volatile</a></li><li><a href="../ch01/io_registers.html"><strong aria-hidden="true">3.3.</strong> IO Registers</a></li><li><a href="../ch01/the_display_control_register.html"><strong aria-hidden="true">3.4.</strong> The Display Control Register</a></li><li><a href="../ch01/video_memory_intro.html"><strong aria-hidden="true">3.5.</strong> Video Memory Intro</a></li><li><a href="../ch01/hello2.html"><strong aria-hidden="true">3.6.</strong> hello2</a></li></ol></li><li><a href="../ch02/index.html"><strong aria-hidden="true">4.</strong> Ch 2: User Input</a></li><li><ol class="section"><li><a href="../ch02/the_key_input_register.html"><strong aria-hidden="true">4.1.</strong> The Key Input Register</a></li><li><a href="../ch02/the_vcount_register.html"><strong aria-hidden="true">4.2.</strong> The VCount Register</a></li><li><a href="../ch02/light_cycle.html"><strong aria-hidden="true">4.3.</strong> light_cycle</a></li></ol></li><li><a href="../ch03/index.html"><strong aria-hidden="true">5.</strong> Ch 3: Memory and Objects</a></li><li><ol class="section"><li><a href="../ch03/gba_memory.html"><strong aria-hidden="true">5.1.</strong> GBA Memory</a></li><li><a href="../ch03/tiled_backgrounds.html"><strong aria-hidden="true">5.2.</strong> Tiled Backgrounds</a></li><li><a href="../ch03/object_basics.html"><strong aria-hidden="true">5.3.</strong> Object Basics</a></li><li><a href="../ch03/gba_rng.html" class="active"><strong aria-hidden="true">5.4.</strong> GBA RNG</a></li><li><a href="../ch03/memory_game.html"><strong aria-hidden="true">5.5.</strong> memory_game</a></li></ol></li></ol>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div id="page-wrapper" class="page-wrapper">
|
||||||
|
|
||||||
|
<div class="page">
|
||||||
|
|
||||||
|
<div id="menu-bar" class="menu-bar">
|
||||||
|
<div id="menu-bar-sticky-container">
|
||||||
|
<div class="left-buttons">
|
||||||
|
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||||
|
<i class="fa fa-bars"></i>
|
||||||
|
</button>
|
||||||
|
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||||
|
<i class="fa fa-paint-brush"></i>
|
||||||
|
</button>
|
||||||
|
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="light">Light <span class="default">(default)</span></button></li>
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
||||||
|
<i class="fa fa-search"></i>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1 class="menu-title">Rust GBA Guide</h1>
|
||||||
|
|
||||||
|
<div class="right-buttons">
|
||||||
|
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||||
|
<i id="print-button" class="fa fa-print"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="search-wrapper" class="hidden">
|
||||||
|
<form id="searchbar-outer" class="searchbar-outer">
|
||||||
|
<input type="search" name="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||||
|
</form>
|
||||||
|
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||||
|
<div id="searchresults-header" class="searchresults-header"></div>
|
||||||
|
<ul id="searchresults">
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||||
|
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||||
|
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||||
|
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div id="content" class="content">
|
||||||
|
<main>
|
||||||
|
<a class="header" href="#gba-rng" id="gba-rng"><h1>GBA RNG</h1></a>
|
||||||
|
<p>TODO</p>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||||
|
<!-- Mobile navigation buttons -->
|
||||||
|
|
||||||
|
<a rel="prev" href="../ch03/object_basics.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
|
<i class="fa fa-angle-left"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a rel="next" href="../ch03/memory_game.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
|
<i class="fa fa-angle-right"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
<div style="clear: both"></div>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||||
|
|
||||||
|
<a href="../ch03/object_basics.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
|
<i class="fa fa-angle-left"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a href="../ch03/memory_game.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
|
<i class="fa fa-angle-right"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script src="../elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="../mark.min.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="../searcher.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
|
||||||
|
|
||||||
|
<script src="../clipboard.min.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="../highlight.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="../book.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
|
||||||
|
<!-- Custom JS scripts -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
228
docs/ch03/index.html
Normal file
228
docs/ch03/index.html
Normal file
|
@ -0,0 +1,228 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html lang="en" class="sidebar-visible no-js">
|
||||||
|
<head>
|
||||||
|
<!-- Book generated using mdBook -->
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Ch 3: Memory and Objects - Rust GBA Guide</title>
|
||||||
|
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||||
|
<meta name="description" content="">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="theme-color" content="#ffffff" />
|
||||||
|
|
||||||
|
<link rel="shortcut icon" href="../favicon.png">
|
||||||
|
<link rel="stylesheet" href="../css/variables.css">
|
||||||
|
<link rel="stylesheet" href="../css/general.css">
|
||||||
|
<link rel="stylesheet" href="../css/chrome.css">
|
||||||
|
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||||
|
|
||||||
|
<!-- Fonts -->
|
||||||
|
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800" rel="stylesheet" type="text/css">
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Source+Code+Pro:500" rel="stylesheet" type="text/css">
|
||||||
|
|
||||||
|
<!-- Highlight.js Stylesheets -->
|
||||||
|
<link rel="stylesheet" href="../highlight.css">
|
||||||
|
<link rel="stylesheet" href="../tomorrow-night.css">
|
||||||
|
<link rel="stylesheet" href="../ayu-highlight.css">
|
||||||
|
|
||||||
|
<!-- Custom theme stylesheets -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body class="light">
|
||||||
|
<!-- Provide site root to javascript -->
|
||||||
|
<script type="text/javascript">var path_to_root = "../";</script>
|
||||||
|
|
||||||
|
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
try {
|
||||||
|
var theme = localStorage.getItem('mdbook-theme');
|
||||||
|
var sidebar = localStorage.getItem('mdbook-sidebar');
|
||||||
|
|
||||||
|
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||||
|
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||||
|
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||||
|
}
|
||||||
|
} catch (e) { }
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
var theme;
|
||||||
|
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||||
|
if (theme === null || theme === undefined) { theme = 'light'; }
|
||||||
|
document.body.className = theme;
|
||||||
|
document.querySelector('html').className = theme + ' js';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Hide / unhide sidebar before it is displayed -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
var html = document.querySelector('html');
|
||||||
|
var sidebar = 'hidden';
|
||||||
|
if (document.body.clientWidth >= 1080) {
|
||||||
|
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||||
|
sidebar = sidebar || 'visible';
|
||||||
|
}
|
||||||
|
html.classList.remove('sidebar-visible');
|
||||||
|
html.classList.add("sidebar-" + sidebar);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||||
|
<ol class="chapter"><li><a href="../introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="../ch00/index.html"><strong aria-hidden="true">2.</strong> Ch 0: Development Setup</a></li><li><a href="../ch01/index.html"><strong aria-hidden="true">3.</strong> Ch 1: Hello GBA</a></li><li><ol class="section"><li><a href="../ch01/hello1.html"><strong aria-hidden="true">3.1.</strong> hello1</a></li><li><a href="../ch01/volatile.html"><strong aria-hidden="true">3.2.</strong> Volatile</a></li><li><a href="../ch01/io_registers.html"><strong aria-hidden="true">3.3.</strong> IO Registers</a></li><li><a href="../ch01/the_display_control_register.html"><strong aria-hidden="true">3.4.</strong> The Display Control Register</a></li><li><a href="../ch01/video_memory_intro.html"><strong aria-hidden="true">3.5.</strong> Video Memory Intro</a></li><li><a href="../ch01/hello2.html"><strong aria-hidden="true">3.6.</strong> hello2</a></li></ol></li><li><a href="../ch02/index.html"><strong aria-hidden="true">4.</strong> Ch 2: User Input</a></li><li><ol class="section"><li><a href="../ch02/the_key_input_register.html"><strong aria-hidden="true">4.1.</strong> The Key Input Register</a></li><li><a href="../ch02/the_vcount_register.html"><strong aria-hidden="true">4.2.</strong> The VCount Register</a></li><li><a href="../ch02/light_cycle.html"><strong aria-hidden="true">4.3.</strong> light_cycle</a></li></ol></li><li><a href="../ch03/index.html" class="active"><strong aria-hidden="true">5.</strong> Ch 3: Memory and Objects</a></li><li><ol class="section"><li><a href="../ch03/gba_memory.html"><strong aria-hidden="true">5.1.</strong> GBA Memory</a></li><li><a href="../ch03/tiled_backgrounds.html"><strong aria-hidden="true">5.2.</strong> Tiled Backgrounds</a></li><li><a href="../ch03/object_basics.html"><strong aria-hidden="true">5.3.</strong> Object Basics</a></li><li><a href="../ch03/gba_rng.html"><strong aria-hidden="true">5.4.</strong> GBA RNG</a></li><li><a href="../ch03/memory_game.html"><strong aria-hidden="true">5.5.</strong> memory_game</a></li></ol></li></ol>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div id="page-wrapper" class="page-wrapper">
|
||||||
|
|
||||||
|
<div class="page">
|
||||||
|
|
||||||
|
<div id="menu-bar" class="menu-bar">
|
||||||
|
<div id="menu-bar-sticky-container">
|
||||||
|
<div class="left-buttons">
|
||||||
|
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||||
|
<i class="fa fa-bars"></i>
|
||||||
|
</button>
|
||||||
|
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||||
|
<i class="fa fa-paint-brush"></i>
|
||||||
|
</button>
|
||||||
|
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="light">Light <span class="default">(default)</span></button></li>
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
||||||
|
<i class="fa fa-search"></i>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1 class="menu-title">Rust GBA Guide</h1>
|
||||||
|
|
||||||
|
<div class="right-buttons">
|
||||||
|
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||||
|
<i id="print-button" class="fa fa-print"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="search-wrapper" class="hidden">
|
||||||
|
<form id="searchbar-outer" class="searchbar-outer">
|
||||||
|
<input type="search" name="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||||
|
</form>
|
||||||
|
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||||
|
<div id="searchresults-header" class="searchresults-header"></div>
|
||||||
|
<ul id="searchresults">
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||||
|
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||||
|
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||||
|
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div id="content" class="content">
|
||||||
|
<main>
|
||||||
|
<a class="header" href="#ch-3-memory-and-objects" id="ch-3-memory-and-objects"><h1>Ch 3: Memory and Objects</h1></a>
|
||||||
|
<p>Alright so we can do some basic "movement", but we left a big trail in the video
|
||||||
|
memory of everywhere we went. Most of the time that's not what we want at all.
|
||||||
|
If we want more hardware support we're going to have to use a new video mode. So
|
||||||
|
far we've only used Mode 3, but modes 4 and 5 are basically the same. Instead,
|
||||||
|
we'll switch focus to using a tiled graphical mode.</p>
|
||||||
|
<p>First we will go over the complete GBA memory mapping. Part of this is the
|
||||||
|
memory for tiled graphics, but also things like all those IO registers, where
|
||||||
|
our RAM is for scratch space, all that stuff. Even if we can't put all of them
|
||||||
|
to use at once, it's helpful to have an idea of what will be available in the
|
||||||
|
long run.</p>
|
||||||
|
<p>Tiled modes bring us two big new concepts that each have their own complexity:
|
||||||
|
backgrounds and objects. They share some concepts, but fundamentally the
|
||||||
|
background is for creating a very large static space that you can scroll around
|
||||||
|
the view within, and the objects are about having a few moving bits that appear
|
||||||
|
over the background. Careful use of backgrounds and objects is key to having the
|
||||||
|
best looking GBA game, so we won't even be able to cover it all in a single
|
||||||
|
chapter.</p>
|
||||||
|
<p>And, of course, since most games are pretty boring if they're totally static
|
||||||
|
we'll touch on the kinds of RNG implementations you might want to have on a GBA.
|
||||||
|
Most general purpose RNGs that you find are rather big compared to the amount of
|
||||||
|
memory we want to give them, and they often use a lot of <code>u64</code> operations, so
|
||||||
|
they end up much slower on a 32-bit machine like the GBA (you can lower 64-bit
|
||||||
|
ops to combinations of 32-bit ops, but that's quite a bit more work). We'll
|
||||||
|
cover a few RNG options that size down the RNG to a good size and a good speed
|
||||||
|
without trading away too much in terms of quality.</p>
|
||||||
|
<p>To top it all off, we'll make a simple "memory game" sort of thing. There's some
|
||||||
|
face down cards in a grid, you pick one to check, then you pick the other to
|
||||||
|
check, and then if they match the pair disappears.</p>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||||
|
<!-- Mobile navigation buttons -->
|
||||||
|
|
||||||
|
<a rel="prev" href="../ch02/light_cycle.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
|
<i class="fa fa-angle-left"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a rel="next" href="../ch03/gba_memory.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
|
<i class="fa fa-angle-right"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
<div style="clear: both"></div>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||||
|
|
||||||
|
<a href="../ch02/light_cycle.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
|
<i class="fa fa-angle-left"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a href="../ch03/gba_memory.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
|
<i class="fa fa-angle-right"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script src="../elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="../mark.min.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="../searcher.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
|
||||||
|
|
||||||
|
<script src="../clipboard.min.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="../highlight.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="../book.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
|
||||||
|
<!-- Custom JS scripts -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
193
docs/ch03/memory_game.html
Normal file
193
docs/ch03/memory_game.html
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html lang="en" class="sidebar-visible no-js">
|
||||||
|
<head>
|
||||||
|
<!-- Book generated using mdBook -->
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>memory_game - Rust GBA Guide</title>
|
||||||
|
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||||
|
<meta name="description" content="">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="theme-color" content="#ffffff" />
|
||||||
|
|
||||||
|
<link rel="shortcut icon" href="../favicon.png">
|
||||||
|
<link rel="stylesheet" href="../css/variables.css">
|
||||||
|
<link rel="stylesheet" href="../css/general.css">
|
||||||
|
<link rel="stylesheet" href="../css/chrome.css">
|
||||||
|
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||||
|
|
||||||
|
<!-- Fonts -->
|
||||||
|
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800" rel="stylesheet" type="text/css">
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Source+Code+Pro:500" rel="stylesheet" type="text/css">
|
||||||
|
|
||||||
|
<!-- Highlight.js Stylesheets -->
|
||||||
|
<link rel="stylesheet" href="../highlight.css">
|
||||||
|
<link rel="stylesheet" href="../tomorrow-night.css">
|
||||||
|
<link rel="stylesheet" href="../ayu-highlight.css">
|
||||||
|
|
||||||
|
<!-- Custom theme stylesheets -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body class="light">
|
||||||
|
<!-- Provide site root to javascript -->
|
||||||
|
<script type="text/javascript">var path_to_root = "../";</script>
|
||||||
|
|
||||||
|
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
try {
|
||||||
|
var theme = localStorage.getItem('mdbook-theme');
|
||||||
|
var sidebar = localStorage.getItem('mdbook-sidebar');
|
||||||
|
|
||||||
|
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||||
|
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||||
|
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||||
|
}
|
||||||
|
} catch (e) { }
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
var theme;
|
||||||
|
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||||
|
if (theme === null || theme === undefined) { theme = 'light'; }
|
||||||
|
document.body.className = theme;
|
||||||
|
document.querySelector('html').className = theme + ' js';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Hide / unhide sidebar before it is displayed -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
var html = document.querySelector('html');
|
||||||
|
var sidebar = 'hidden';
|
||||||
|
if (document.body.clientWidth >= 1080) {
|
||||||
|
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||||
|
sidebar = sidebar || 'visible';
|
||||||
|
}
|
||||||
|
html.classList.remove('sidebar-visible');
|
||||||
|
html.classList.add("sidebar-" + sidebar);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||||
|
<ol class="chapter"><li><a href="../introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="../ch00/index.html"><strong aria-hidden="true">2.</strong> Ch 0: Development Setup</a></li><li><a href="../ch01/index.html"><strong aria-hidden="true">3.</strong> Ch 1: Hello GBA</a></li><li><ol class="section"><li><a href="../ch01/hello1.html"><strong aria-hidden="true">3.1.</strong> hello1</a></li><li><a href="../ch01/volatile.html"><strong aria-hidden="true">3.2.</strong> Volatile</a></li><li><a href="../ch01/io_registers.html"><strong aria-hidden="true">3.3.</strong> IO Registers</a></li><li><a href="../ch01/the_display_control_register.html"><strong aria-hidden="true">3.4.</strong> The Display Control Register</a></li><li><a href="../ch01/video_memory_intro.html"><strong aria-hidden="true">3.5.</strong> Video Memory Intro</a></li><li><a href="../ch01/hello2.html"><strong aria-hidden="true">3.6.</strong> hello2</a></li></ol></li><li><a href="../ch02/index.html"><strong aria-hidden="true">4.</strong> Ch 2: User Input</a></li><li><ol class="section"><li><a href="../ch02/the_key_input_register.html"><strong aria-hidden="true">4.1.</strong> The Key Input Register</a></li><li><a href="../ch02/the_vcount_register.html"><strong aria-hidden="true">4.2.</strong> The VCount Register</a></li><li><a href="../ch02/light_cycle.html"><strong aria-hidden="true">4.3.</strong> light_cycle</a></li></ol></li><li><a href="../ch03/index.html"><strong aria-hidden="true">5.</strong> Ch 3: Memory and Objects</a></li><li><ol class="section"><li><a href="../ch03/gba_memory.html"><strong aria-hidden="true">5.1.</strong> GBA Memory</a></li><li><a href="../ch03/tiled_backgrounds.html"><strong aria-hidden="true">5.2.</strong> Tiled Backgrounds</a></li><li><a href="../ch03/object_basics.html"><strong aria-hidden="true">5.3.</strong> Object Basics</a></li><li><a href="../ch03/gba_rng.html"><strong aria-hidden="true">5.4.</strong> GBA RNG</a></li><li><a href="../ch03/memory_game.html" class="active"><strong aria-hidden="true">5.5.</strong> memory_game</a></li></ol></li></ol>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div id="page-wrapper" class="page-wrapper">
|
||||||
|
|
||||||
|
<div class="page">
|
||||||
|
|
||||||
|
<div id="menu-bar" class="menu-bar">
|
||||||
|
<div id="menu-bar-sticky-container">
|
||||||
|
<div class="left-buttons">
|
||||||
|
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||||
|
<i class="fa fa-bars"></i>
|
||||||
|
</button>
|
||||||
|
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||||
|
<i class="fa fa-paint-brush"></i>
|
||||||
|
</button>
|
||||||
|
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="light">Light <span class="default">(default)</span></button></li>
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
||||||
|
<i class="fa fa-search"></i>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1 class="menu-title">Rust GBA Guide</h1>
|
||||||
|
|
||||||
|
<div class="right-buttons">
|
||||||
|
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||||
|
<i id="print-button" class="fa fa-print"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="search-wrapper" class="hidden">
|
||||||
|
<form id="searchbar-outer" class="searchbar-outer">
|
||||||
|
<input type="search" name="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||||
|
</form>
|
||||||
|
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||||
|
<div id="searchresults-header" class="searchresults-header"></div>
|
||||||
|
<ul id="searchresults">
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||||
|
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||||
|
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||||
|
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div id="content" class="content">
|
||||||
|
<main>
|
||||||
|
<a class="header" href="#memory_game" id="memory_game"><h1>memory_game</h1></a>
|
||||||
|
<p>TODO</p>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||||
|
<!-- Mobile navigation buttons -->
|
||||||
|
|
||||||
|
<a rel="prev" href="../ch03/gba_rng.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
|
<i class="fa fa-angle-left"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div style="clear: both"></div>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||||
|
|
||||||
|
<a href="../ch03/gba_rng.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
|
<i class="fa fa-angle-left"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script src="../elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="../mark.min.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="../searcher.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
|
||||||
|
|
||||||
|
<script src="../clipboard.min.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="../highlight.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="../book.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
|
||||||
|
<!-- Custom JS scripts -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
201
docs/ch03/object_basics.html
Normal file
201
docs/ch03/object_basics.html
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html lang="en" class="sidebar-visible no-js">
|
||||||
|
<head>
|
||||||
|
<!-- Book generated using mdBook -->
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Object Basics - Rust GBA Guide</title>
|
||||||
|
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||||
|
<meta name="description" content="">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="theme-color" content="#ffffff" />
|
||||||
|
|
||||||
|
<link rel="shortcut icon" href="../favicon.png">
|
||||||
|
<link rel="stylesheet" href="../css/variables.css">
|
||||||
|
<link rel="stylesheet" href="../css/general.css">
|
||||||
|
<link rel="stylesheet" href="../css/chrome.css">
|
||||||
|
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||||
|
|
||||||
|
<!-- Fonts -->
|
||||||
|
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800" rel="stylesheet" type="text/css">
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Source+Code+Pro:500" rel="stylesheet" type="text/css">
|
||||||
|
|
||||||
|
<!-- Highlight.js Stylesheets -->
|
||||||
|
<link rel="stylesheet" href="../highlight.css">
|
||||||
|
<link rel="stylesheet" href="../tomorrow-night.css">
|
||||||
|
<link rel="stylesheet" href="../ayu-highlight.css">
|
||||||
|
|
||||||
|
<!-- Custom theme stylesheets -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body class="light">
|
||||||
|
<!-- Provide site root to javascript -->
|
||||||
|
<script type="text/javascript">var path_to_root = "../";</script>
|
||||||
|
|
||||||
|
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
try {
|
||||||
|
var theme = localStorage.getItem('mdbook-theme');
|
||||||
|
var sidebar = localStorage.getItem('mdbook-sidebar');
|
||||||
|
|
||||||
|
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||||
|
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||||
|
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||||
|
}
|
||||||
|
} catch (e) { }
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
var theme;
|
||||||
|
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||||
|
if (theme === null || theme === undefined) { theme = 'light'; }
|
||||||
|
document.body.className = theme;
|
||||||
|
document.querySelector('html').className = theme + ' js';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Hide / unhide sidebar before it is displayed -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
var html = document.querySelector('html');
|
||||||
|
var sidebar = 'hidden';
|
||||||
|
if (document.body.clientWidth >= 1080) {
|
||||||
|
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||||
|
sidebar = sidebar || 'visible';
|
||||||
|
}
|
||||||
|
html.classList.remove('sidebar-visible');
|
||||||
|
html.classList.add("sidebar-" + sidebar);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||||
|
<ol class="chapter"><li><a href="../introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="../ch00/index.html"><strong aria-hidden="true">2.</strong> Ch 0: Development Setup</a></li><li><a href="../ch01/index.html"><strong aria-hidden="true">3.</strong> Ch 1: Hello GBA</a></li><li><ol class="section"><li><a href="../ch01/hello1.html"><strong aria-hidden="true">3.1.</strong> hello1</a></li><li><a href="../ch01/volatile.html"><strong aria-hidden="true">3.2.</strong> Volatile</a></li><li><a href="../ch01/io_registers.html"><strong aria-hidden="true">3.3.</strong> IO Registers</a></li><li><a href="../ch01/the_display_control_register.html"><strong aria-hidden="true">3.4.</strong> The Display Control Register</a></li><li><a href="../ch01/video_memory_intro.html"><strong aria-hidden="true">3.5.</strong> Video Memory Intro</a></li><li><a href="../ch01/hello2.html"><strong aria-hidden="true">3.6.</strong> hello2</a></li></ol></li><li><a href="../ch02/index.html"><strong aria-hidden="true">4.</strong> Ch 2: User Input</a></li><li><ol class="section"><li><a href="../ch02/the_key_input_register.html"><strong aria-hidden="true">4.1.</strong> The Key Input Register</a></li><li><a href="../ch02/the_vcount_register.html"><strong aria-hidden="true">4.2.</strong> The VCount Register</a></li><li><a href="../ch02/light_cycle.html"><strong aria-hidden="true">4.3.</strong> light_cycle</a></li></ol></li><li><a href="../ch03/index.html"><strong aria-hidden="true">5.</strong> Ch 3: Memory and Objects</a></li><li><ol class="section"><li><a href="../ch03/gba_memory.html"><strong aria-hidden="true">5.1.</strong> GBA Memory</a></li><li><a href="../ch03/tiled_backgrounds.html"><strong aria-hidden="true">5.2.</strong> Tiled Backgrounds</a></li><li><a href="../ch03/object_basics.html" class="active"><strong aria-hidden="true">5.3.</strong> Object Basics</a></li><li><a href="../ch03/gba_rng.html"><strong aria-hidden="true">5.4.</strong> GBA RNG</a></li><li><a href="../ch03/memory_game.html"><strong aria-hidden="true">5.5.</strong> memory_game</a></li></ol></li></ol>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div id="page-wrapper" class="page-wrapper">
|
||||||
|
|
||||||
|
<div class="page">
|
||||||
|
|
||||||
|
<div id="menu-bar" class="menu-bar">
|
||||||
|
<div id="menu-bar-sticky-container">
|
||||||
|
<div class="left-buttons">
|
||||||
|
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||||
|
<i class="fa fa-bars"></i>
|
||||||
|
</button>
|
||||||
|
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||||
|
<i class="fa fa-paint-brush"></i>
|
||||||
|
</button>
|
||||||
|
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="light">Light <span class="default">(default)</span></button></li>
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
||||||
|
<i class="fa fa-search"></i>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1 class="menu-title">Rust GBA Guide</h1>
|
||||||
|
|
||||||
|
<div class="right-buttons">
|
||||||
|
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||||
|
<i id="print-button" class="fa fa-print"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="search-wrapper" class="hidden">
|
||||||
|
<form id="searchbar-outer" class="searchbar-outer">
|
||||||
|
<input type="search" name="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||||
|
</form>
|
||||||
|
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||||
|
<div id="searchresults-header" class="searchresults-header"></div>
|
||||||
|
<ul id="searchresults">
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||||
|
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||||
|
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||||
|
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div id="content" class="content">
|
||||||
|
<main>
|
||||||
|
<a class="header" href="#object-basics" id="object-basics"><h1>Object Basics</h1></a>
|
||||||
|
<p>TODO</p>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||||
|
<!-- Mobile navigation buttons -->
|
||||||
|
|
||||||
|
<a rel="prev" href="../ch03/tiled_backgrounds.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
|
<i class="fa fa-angle-left"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a rel="next" href="../ch03/gba_rng.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
|
<i class="fa fa-angle-right"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
<div style="clear: both"></div>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||||
|
|
||||||
|
<a href="../ch03/tiled_backgrounds.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
|
<i class="fa fa-angle-left"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a href="../ch03/gba_rng.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
|
<i class="fa fa-angle-right"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script src="../elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="../mark.min.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="../searcher.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
|
||||||
|
|
||||||
|
<script src="../clipboard.min.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="../highlight.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="../book.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
|
||||||
|
<!-- Custom JS scripts -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
201
docs/ch03/tiled_backgrounds.html
Normal file
201
docs/ch03/tiled_backgrounds.html
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html lang="en" class="sidebar-visible no-js">
|
||||||
|
<head>
|
||||||
|
<!-- Book generated using mdBook -->
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Tiled Backgrounds - Rust GBA Guide</title>
|
||||||
|
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||||
|
<meta name="description" content="">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="theme-color" content="#ffffff" />
|
||||||
|
|
||||||
|
<link rel="shortcut icon" href="../favicon.png">
|
||||||
|
<link rel="stylesheet" href="../css/variables.css">
|
||||||
|
<link rel="stylesheet" href="../css/general.css">
|
||||||
|
<link rel="stylesheet" href="../css/chrome.css">
|
||||||
|
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||||
|
|
||||||
|
<!-- Fonts -->
|
||||||
|
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800" rel="stylesheet" type="text/css">
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Source+Code+Pro:500" rel="stylesheet" type="text/css">
|
||||||
|
|
||||||
|
<!-- Highlight.js Stylesheets -->
|
||||||
|
<link rel="stylesheet" href="../highlight.css">
|
||||||
|
<link rel="stylesheet" href="../tomorrow-night.css">
|
||||||
|
<link rel="stylesheet" href="../ayu-highlight.css">
|
||||||
|
|
||||||
|
<!-- Custom theme stylesheets -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body class="light">
|
||||||
|
<!-- Provide site root to javascript -->
|
||||||
|
<script type="text/javascript">var path_to_root = "../";</script>
|
||||||
|
|
||||||
|
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
try {
|
||||||
|
var theme = localStorage.getItem('mdbook-theme');
|
||||||
|
var sidebar = localStorage.getItem('mdbook-sidebar');
|
||||||
|
|
||||||
|
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||||
|
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||||
|
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||||
|
}
|
||||||
|
} catch (e) { }
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
var theme;
|
||||||
|
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||||
|
if (theme === null || theme === undefined) { theme = 'light'; }
|
||||||
|
document.body.className = theme;
|
||||||
|
document.querySelector('html').className = theme + ' js';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Hide / unhide sidebar before it is displayed -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
var html = document.querySelector('html');
|
||||||
|
var sidebar = 'hidden';
|
||||||
|
if (document.body.clientWidth >= 1080) {
|
||||||
|
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||||
|
sidebar = sidebar || 'visible';
|
||||||
|
}
|
||||||
|
html.classList.remove('sidebar-visible');
|
||||||
|
html.classList.add("sidebar-" + sidebar);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||||
|
<ol class="chapter"><li><a href="../introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="../ch00/index.html"><strong aria-hidden="true">2.</strong> Ch 0: Development Setup</a></li><li><a href="../ch01/index.html"><strong aria-hidden="true">3.</strong> Ch 1: Hello GBA</a></li><li><ol class="section"><li><a href="../ch01/hello1.html"><strong aria-hidden="true">3.1.</strong> hello1</a></li><li><a href="../ch01/volatile.html"><strong aria-hidden="true">3.2.</strong> Volatile</a></li><li><a href="../ch01/io_registers.html"><strong aria-hidden="true">3.3.</strong> IO Registers</a></li><li><a href="../ch01/the_display_control_register.html"><strong aria-hidden="true">3.4.</strong> The Display Control Register</a></li><li><a href="../ch01/video_memory_intro.html"><strong aria-hidden="true">3.5.</strong> Video Memory Intro</a></li><li><a href="../ch01/hello2.html"><strong aria-hidden="true">3.6.</strong> hello2</a></li></ol></li><li><a href="../ch02/index.html"><strong aria-hidden="true">4.</strong> Ch 2: User Input</a></li><li><ol class="section"><li><a href="../ch02/the_key_input_register.html"><strong aria-hidden="true">4.1.</strong> The Key Input Register</a></li><li><a href="../ch02/the_vcount_register.html"><strong aria-hidden="true">4.2.</strong> The VCount Register</a></li><li><a href="../ch02/light_cycle.html"><strong aria-hidden="true">4.3.</strong> light_cycle</a></li></ol></li><li><a href="../ch03/index.html"><strong aria-hidden="true">5.</strong> Ch 3: Memory and Objects</a></li><li><ol class="section"><li><a href="../ch03/gba_memory.html"><strong aria-hidden="true">5.1.</strong> GBA Memory</a></li><li><a href="../ch03/tiled_backgrounds.html" class="active"><strong aria-hidden="true">5.2.</strong> Tiled Backgrounds</a></li><li><a href="../ch03/object_basics.html"><strong aria-hidden="true">5.3.</strong> Object Basics</a></li><li><a href="../ch03/gba_rng.html"><strong aria-hidden="true">5.4.</strong> GBA RNG</a></li><li><a href="../ch03/memory_game.html"><strong aria-hidden="true">5.5.</strong> memory_game</a></li></ol></li></ol>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div id="page-wrapper" class="page-wrapper">
|
||||||
|
|
||||||
|
<div class="page">
|
||||||
|
|
||||||
|
<div id="menu-bar" class="menu-bar">
|
||||||
|
<div id="menu-bar-sticky-container">
|
||||||
|
<div class="left-buttons">
|
||||||
|
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||||
|
<i class="fa fa-bars"></i>
|
||||||
|
</button>
|
||||||
|
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||||
|
<i class="fa fa-paint-brush"></i>
|
||||||
|
</button>
|
||||||
|
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="light">Light <span class="default">(default)</span></button></li>
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
||||||
|
<i class="fa fa-search"></i>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1 class="menu-title">Rust GBA Guide</h1>
|
||||||
|
|
||||||
|
<div class="right-buttons">
|
||||||
|
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||||
|
<i id="print-button" class="fa fa-print"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="search-wrapper" class="hidden">
|
||||||
|
<form id="searchbar-outer" class="searchbar-outer">
|
||||||
|
<input type="search" name="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||||
|
</form>
|
||||||
|
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||||
|
<div id="searchresults-header" class="searchresults-header"></div>
|
||||||
|
<ul id="searchresults">
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||||
|
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||||
|
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||||
|
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div id="content" class="content">
|
||||||
|
<main>
|
||||||
|
<a class="header" href="#tiled-backgrounds" id="tiled-backgrounds"><h1>Tiled Backgrounds</h1></a>
|
||||||
|
<p>TODO</p>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||||
|
<!-- Mobile navigation buttons -->
|
||||||
|
|
||||||
|
<a rel="prev" href="../ch03/gba_memory.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
|
<i class="fa fa-angle-left"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a rel="next" href="../ch03/object_basics.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
|
<i class="fa fa-angle-right"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
<div style="clear: both"></div>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||||
|
|
||||||
|
<a href="../ch03/gba_memory.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
|
<i class="fa fa-angle-left"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a href="../ch03/object_basics.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
|
<i class="fa fa-angle-right"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script src="../elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="../mark.min.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="../searcher.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
|
||||||
|
|
||||||
|
<script src="../clipboard.min.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="../highlight.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="../book.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
|
||||||
|
<!-- Custom JS scripts -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<!-- Book generated using mdBook -->
|
<!-- Book generated using mdBook -->
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Introduction - Rust GBA Tutorials</title>
|
<title>Introduction - Rust GBA Guide</title>
|
||||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||||
<meta name="description" content="">
|
<meta name="description" content="">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
@ -72,7 +72,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||||
<ol class="chapter"><li><a href="introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="ch0/index.html"><strong aria-hidden="true">2.</strong> Ch 0: Development Setup</a></li><li><a href="ch1/index.html"><strong aria-hidden="true">3.</strong> Ch 1: Hello GBA</a></li><li><ol class="section"><li><a href="ch1/hello1.html"><strong aria-hidden="true">3.1.</strong> hello1</a></li><li><a href="ch1/io_registers.html"><strong aria-hidden="true">3.2.</strong> IO Registers</a></li><li><a href="ch1/the_display_control_register.html"><strong aria-hidden="true">3.3.</strong> The Display Control Register</a></li><li><a href="ch1/video_memory_intro.html"><strong aria-hidden="true">3.4.</strong> Video Memory Intro</a></li><li><a href="ch1/hello2.html"><strong aria-hidden="true">3.5.</strong> hello2</a></li></ol></li><li><a href="ch2/index.html"><strong aria-hidden="true">4.</strong> Ch 2: User Input</a></li><li><ol class="section"><li><a href="ch2/the_key_input_register.html"><strong aria-hidden="true">4.1.</strong> The Key Input Register</a></li><li><a href="ch2/the_vcount_register.html"><strong aria-hidden="true">4.2.</strong> The VCount Register</a></li><li><a href="ch2/light_cycle.html"><strong aria-hidden="true">4.3.</strong> light_cycle</a></li></ol></li></ol>
|
<ol class="chapter"><li><a href="introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="ch00/index.html"><strong aria-hidden="true">2.</strong> Ch 0: Development Setup</a></li><li><a href="ch01/index.html"><strong aria-hidden="true">3.</strong> Ch 1: Hello GBA</a></li><li><ol class="section"><li><a href="ch01/hello1.html"><strong aria-hidden="true">3.1.</strong> hello1</a></li><li><a href="ch01/volatile.html"><strong aria-hidden="true">3.2.</strong> Volatile</a></li><li><a href="ch01/io_registers.html"><strong aria-hidden="true">3.3.</strong> IO Registers</a></li><li><a href="ch01/the_display_control_register.html"><strong aria-hidden="true">3.4.</strong> The Display Control Register</a></li><li><a href="ch01/video_memory_intro.html"><strong aria-hidden="true">3.5.</strong> Video Memory Intro</a></li><li><a href="ch01/hello2.html"><strong aria-hidden="true">3.6.</strong> hello2</a></li></ol></li><li><a href="ch02/index.html"><strong aria-hidden="true">4.</strong> Ch 2: User Input</a></li><li><ol class="section"><li><a href="ch02/the_key_input_register.html"><strong aria-hidden="true">4.1.</strong> The Key Input Register</a></li><li><a href="ch02/the_vcount_register.html"><strong aria-hidden="true">4.2.</strong> The VCount Register</a></li><li><a href="ch02/light_cycle.html"><strong aria-hidden="true">4.3.</strong> light_cycle</a></li></ol></li><li><a href="ch03/index.html"><strong aria-hidden="true">5.</strong> Ch 3: Memory and Objects</a></li><li><ol class="section"><li><a href="ch03/gba_memory.html"><strong aria-hidden="true">5.1.</strong> GBA Memory</a></li><li><a href="ch03/tiled_backgrounds.html"><strong aria-hidden="true">5.2.</strong> Tiled Backgrounds</a></li><li><a href="ch03/object_basics.html"><strong aria-hidden="true">5.3.</strong> Object Basics</a></li><li><a href="ch03/gba_rng.html"><strong aria-hidden="true">5.4.</strong> GBA RNG</a></li><li><a href="ch03/memory_game.html"><strong aria-hidden="true">5.5.</strong> memory_game</a></li></ol></li></ol>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div id="page-wrapper" class="page-wrapper">
|
<div id="page-wrapper" class="page-wrapper">
|
||||||
|
@ -102,7 +102,7 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 class="menu-title">Rust GBA Tutorials</h1>
|
<h1 class="menu-title">Rust GBA Guide</h1>
|
||||||
|
|
||||||
<div class="right-buttons">
|
<div class="right-buttons">
|
||||||
<a href="print.html" title="Print this book" aria-label="Print this book">
|
<a href="print.html" title="Print this book" aria-label="Print this book">
|
||||||
|
@ -137,8 +137,31 @@
|
||||||
<div id="content" class="content">
|
<div id="content" class="content">
|
||||||
<main>
|
<main>
|
||||||
<a class="header" href="#introduction" id="introduction"><h1>Introduction</h1></a>
|
<a class="header" href="#introduction" id="introduction"><h1>Introduction</h1></a>
|
||||||
<p>Here's a book that'll help you program in Rust on the GBA.</p>
|
<p>Here's a book that'll help you program in Rust on the Game Boy Advance (GBA).</p>
|
||||||
<p>It's very "work in progress". At the moment there's only one demo program.</p>
|
<p>It's a work in progress of course, but so is most of everything in Rust.</p>
|
||||||
|
<a class="header" href="#style-and-purpose" id="style-and-purpose"><h2>Style and Purpose</h2></a>
|
||||||
|
<p>I'm out to teach you how to program in Rust on the GBA, obviously. However,
|
||||||
|
while there <em>is</em> a <a href="https://github.com/rust-console/gba">gba</a> crate, and while I
|
||||||
|
genuinely believe it to be a good and useful crate for GBA programming, we <em>will
|
||||||
|
not</em> be using the <code>gba</code> crate within this book. In fact we won't be using any
|
||||||
|
crates at all. We can call it the <a href="https://handmadehero.org/">Handmade Hero</a>
|
||||||
|
approach, if you like.</p>
|
||||||
|
<p>I don't want to just teach you how to use the <code>gba</code> crate, I want to teach you
|
||||||
|
what you'd need to know to write the crate from scratch if it wasn't there.</p>
|
||||||
|
<p>Each chapter of the book will focus on a few things you'll need to know about
|
||||||
|
GBA programming and then present a fully self-contained example that puts those
|
||||||
|
ideas into action. Just one file per example, no dependencies, no external
|
||||||
|
assets, no fuss. The examples will be in the text of the book within code
|
||||||
|
blocks, but also you can find them in the <a href="https://github.com/rust-console/gba/tree/master/examples">examples
|
||||||
|
directory</a> of the repo
|
||||||
|
if you want to get them that way.</p>
|
||||||
|
<a class="header" href="#expected-knowledge" id="expected-knowledge"><h2>Expected Knowledge</h2></a>
|
||||||
|
<p>I will try not to ask too much of the reader ahead of time, but you are expected
|
||||||
|
to have already read <a href="https://doc.rust-lang.org/book/">The Rust Book</a>.</p>
|
||||||
|
<p>It's very difficult to know when you've said something that someone else won't
|
||||||
|
already know about, or if you're presenting ideas out of order. If things aren't
|
||||||
|
clear please <a href="https://github.com/rust-console/gba/issues">file an issue</a> and
|
||||||
|
we'll try to address it.</p>
|
||||||
<a class="header" href="#getting-help" id="getting-help"><h2>Getting Help</h2></a>
|
<a class="header" href="#getting-help" id="getting-help"><h2>Getting Help</h2></a>
|
||||||
<p>If you want to contact us you should join the <a href="https://discordapp.com/invite/aVESxV8">Rust Community
|
<p>If you want to contact us you should join the <a href="https://discordapp.com/invite/aVESxV8">Rust Community
|
||||||
Discord</a> and ask in the <code>#gamedev</code>
|
Discord</a> and ask in the <code>#gamedev</code>
|
||||||
|
@ -148,9 +171,10 @@ channel.</p>
|
||||||
<li><code>Lokathor</code> is the fool who decided to write a crate and book for it.</li>
|
<li><code>Lokathor</code> is the fool who decided to write a crate and book for it.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>If it's <em>not</em> a GBA specific question then you can probably ask any of the other
|
<p>If it's <em>not</em> a GBA specific question then you can probably ask any of the other
|
||||||
folks in the server as well.</p>
|
folks in the server as well (there's a few hundred folks).</p>
|
||||||
<a class="header" href="#other-works" id="other-works"><h2>Other Works</h2></a>
|
<a class="header" href="#further-reading" id="further-reading"><h2>Further Reading</h2></a>
|
||||||
<p>If you want to read more about developing on the GBA there are some other good resources as well:</p>
|
<p>If you want to read more about developing on the GBA there are some other good
|
||||||
|
resources as well:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="https://www.coranac.com/tonc/text/toc.htm">Tonc</a>, a tutorial series written
|
<li><a href="https://www.coranac.com/tonc/text/toc.htm">Tonc</a>, a tutorial series written
|
||||||
for C, but it's what I based the ordering of this book's sections on.</li>
|
for C, but it's what I based the ordering of this book's sections on.</li>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<!-- Book generated using mdBook -->
|
<!-- Book generated using mdBook -->
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Introduction - Rust GBA Tutorials</title>
|
<title>Introduction - Rust GBA Guide</title>
|
||||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||||
<meta name="description" content="">
|
<meta name="description" content="">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
@ -72,7 +72,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||||
<ol class="chapter"><li><a href="introduction.html" class="active"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="ch0/index.html"><strong aria-hidden="true">2.</strong> Ch 0: Development Setup</a></li><li><a href="ch1/index.html"><strong aria-hidden="true">3.</strong> Ch 1: Hello GBA</a></li><li><ol class="section"><li><a href="ch1/hello1.html"><strong aria-hidden="true">3.1.</strong> hello1</a></li><li><a href="ch1/io_registers.html"><strong aria-hidden="true">3.2.</strong> IO Registers</a></li><li><a href="ch1/the_display_control_register.html"><strong aria-hidden="true">3.3.</strong> The Display Control Register</a></li><li><a href="ch1/video_memory_intro.html"><strong aria-hidden="true">3.4.</strong> Video Memory Intro</a></li><li><a href="ch1/hello2.html"><strong aria-hidden="true">3.5.</strong> hello2</a></li></ol></li><li><a href="ch2/index.html"><strong aria-hidden="true">4.</strong> Ch 2: User Input</a></li><li><ol class="section"><li><a href="ch2/the_key_input_register.html"><strong aria-hidden="true">4.1.</strong> The Key Input Register</a></li><li><a href="ch2/the_vcount_register.html"><strong aria-hidden="true">4.2.</strong> The VCount Register</a></li><li><a href="ch2/light_cycle.html"><strong aria-hidden="true">4.3.</strong> light_cycle</a></li></ol></li></ol>
|
<ol class="chapter"><li><a href="introduction.html" class="active"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="ch00/index.html"><strong aria-hidden="true">2.</strong> Ch 0: Development Setup</a></li><li><a href="ch01/index.html"><strong aria-hidden="true">3.</strong> Ch 1: Hello GBA</a></li><li><ol class="section"><li><a href="ch01/hello1.html"><strong aria-hidden="true">3.1.</strong> hello1</a></li><li><a href="ch01/volatile.html"><strong aria-hidden="true">3.2.</strong> Volatile</a></li><li><a href="ch01/io_registers.html"><strong aria-hidden="true">3.3.</strong> IO Registers</a></li><li><a href="ch01/the_display_control_register.html"><strong aria-hidden="true">3.4.</strong> The Display Control Register</a></li><li><a href="ch01/video_memory_intro.html"><strong aria-hidden="true">3.5.</strong> Video Memory Intro</a></li><li><a href="ch01/hello2.html"><strong aria-hidden="true">3.6.</strong> hello2</a></li></ol></li><li><a href="ch02/index.html"><strong aria-hidden="true">4.</strong> Ch 2: User Input</a></li><li><ol class="section"><li><a href="ch02/the_key_input_register.html"><strong aria-hidden="true">4.1.</strong> The Key Input Register</a></li><li><a href="ch02/the_vcount_register.html"><strong aria-hidden="true">4.2.</strong> The VCount Register</a></li><li><a href="ch02/light_cycle.html"><strong aria-hidden="true">4.3.</strong> light_cycle</a></li></ol></li><li><a href="ch03/index.html"><strong aria-hidden="true">5.</strong> Ch 3: Memory and Objects</a></li><li><ol class="section"><li><a href="ch03/gba_memory.html"><strong aria-hidden="true">5.1.</strong> GBA Memory</a></li><li><a href="ch03/tiled_backgrounds.html"><strong aria-hidden="true">5.2.</strong> Tiled Backgrounds</a></li><li><a href="ch03/object_basics.html"><strong aria-hidden="true">5.3.</strong> Object Basics</a></li><li><a href="ch03/gba_rng.html"><strong aria-hidden="true">5.4.</strong> GBA RNG</a></li><li><a href="ch03/memory_game.html"><strong aria-hidden="true">5.5.</strong> memory_game</a></li></ol></li></ol>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div id="page-wrapper" class="page-wrapper">
|
<div id="page-wrapper" class="page-wrapper">
|
||||||
|
@ -102,7 +102,7 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 class="menu-title">Rust GBA Tutorials</h1>
|
<h1 class="menu-title">Rust GBA Guide</h1>
|
||||||
|
|
||||||
<div class="right-buttons">
|
<div class="right-buttons">
|
||||||
<a href="print.html" title="Print this book" aria-label="Print this book">
|
<a href="print.html" title="Print this book" aria-label="Print this book">
|
||||||
|
@ -137,8 +137,31 @@
|
||||||
<div id="content" class="content">
|
<div id="content" class="content">
|
||||||
<main>
|
<main>
|
||||||
<a class="header" href="#introduction" id="introduction"><h1>Introduction</h1></a>
|
<a class="header" href="#introduction" id="introduction"><h1>Introduction</h1></a>
|
||||||
<p>Here's a book that'll help you program in Rust on the GBA.</p>
|
<p>Here's a book that'll help you program in Rust on the Game Boy Advance (GBA).</p>
|
||||||
<p>It's very "work in progress". At the moment there's only one demo program.</p>
|
<p>It's a work in progress of course, but so is most of everything in Rust.</p>
|
||||||
|
<a class="header" href="#style-and-purpose" id="style-and-purpose"><h2>Style and Purpose</h2></a>
|
||||||
|
<p>I'm out to teach you how to program in Rust on the GBA, obviously. However,
|
||||||
|
while there <em>is</em> a <a href="https://github.com/rust-console/gba">gba</a> crate, and while I
|
||||||
|
genuinely believe it to be a good and useful crate for GBA programming, we <em>will
|
||||||
|
not</em> be using the <code>gba</code> crate within this book. In fact we won't be using any
|
||||||
|
crates at all. We can call it the <a href="https://handmadehero.org/">Handmade Hero</a>
|
||||||
|
approach, if you like.</p>
|
||||||
|
<p>I don't want to just teach you how to use the <code>gba</code> crate, I want to teach you
|
||||||
|
what you'd need to know to write the crate from scratch if it wasn't there.</p>
|
||||||
|
<p>Each chapter of the book will focus on a few things you'll need to know about
|
||||||
|
GBA programming and then present a fully self-contained example that puts those
|
||||||
|
ideas into action. Just one file per example, no dependencies, no external
|
||||||
|
assets, no fuss. The examples will be in the text of the book within code
|
||||||
|
blocks, but also you can find them in the <a href="https://github.com/rust-console/gba/tree/master/examples">examples
|
||||||
|
directory</a> of the repo
|
||||||
|
if you want to get them that way.</p>
|
||||||
|
<a class="header" href="#expected-knowledge" id="expected-knowledge"><h2>Expected Knowledge</h2></a>
|
||||||
|
<p>I will try not to ask too much of the reader ahead of time, but you are expected
|
||||||
|
to have already read <a href="https://doc.rust-lang.org/book/">The Rust Book</a>.</p>
|
||||||
|
<p>It's very difficult to know when you've said something that someone else won't
|
||||||
|
already know about, or if you're presenting ideas out of order. If things aren't
|
||||||
|
clear please <a href="https://github.com/rust-console/gba/issues">file an issue</a> and
|
||||||
|
we'll try to address it.</p>
|
||||||
<a class="header" href="#getting-help" id="getting-help"><h2>Getting Help</h2></a>
|
<a class="header" href="#getting-help" id="getting-help"><h2>Getting Help</h2></a>
|
||||||
<p>If you want to contact us you should join the <a href="https://discordapp.com/invite/aVESxV8">Rust Community
|
<p>If you want to contact us you should join the <a href="https://discordapp.com/invite/aVESxV8">Rust Community
|
||||||
Discord</a> and ask in the <code>#gamedev</code>
|
Discord</a> and ask in the <code>#gamedev</code>
|
||||||
|
@ -148,9 +171,10 @@ channel.</p>
|
||||||
<li><code>Lokathor</code> is the fool who decided to write a crate and book for it.</li>
|
<li><code>Lokathor</code> is the fool who decided to write a crate and book for it.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>If it's <em>not</em> a GBA specific question then you can probably ask any of the other
|
<p>If it's <em>not</em> a GBA specific question then you can probably ask any of the other
|
||||||
folks in the server as well.</p>
|
folks in the server as well (there's a few hundred folks).</p>
|
||||||
<a class="header" href="#other-works" id="other-works"><h2>Other Works</h2></a>
|
<a class="header" href="#further-reading" id="further-reading"><h2>Further Reading</h2></a>
|
||||||
<p>If you want to read more about developing on the GBA there are some other good resources as well:</p>
|
<p>If you want to read more about developing on the GBA there are some other good
|
||||||
|
resources as well:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="https://www.coranac.com/tonc/text/toc.htm">Tonc</a>, a tutorial series written
|
<li><a href="https://www.coranac.com/tonc/text/toc.htm">Tonc</a>, a tutorial series written
|
||||||
for C, but it's what I based the ordering of this book's sections on.</li>
|
for C, but it's what I based the ordering of this book's sections on.</li>
|
||||||
|
@ -169,7 +193,7 @@ art diagrams and example C struct layouts than GBATEK does.</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a rel="next" href="ch0/index.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
<a rel="next" href="ch00/index.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
<i class="fa fa-angle-right"></i>
|
<i class="fa fa-angle-right"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
@ -183,7 +207,7 @@ art diagrams and example C struct layouts than GBATEK does.</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a href="ch0/index.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
<a href="ch00/index.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
<i class="fa fa-angle-right"></i>
|
<i class="fa fa-angle-right"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|
561
docs/print.html
561
docs/print.html
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<!-- Book generated using mdBook -->
|
<!-- Book generated using mdBook -->
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Rust GBA Tutorials</title>
|
<title>Rust GBA Guide</title>
|
||||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||||
<meta name="description" content="">
|
<meta name="description" content="">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
@ -72,7 +72,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||||
<ol class="chapter"><li><a href="introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="ch0/index.html"><strong aria-hidden="true">2.</strong> Ch 0: Development Setup</a></li><li><a href="ch1/index.html"><strong aria-hidden="true">3.</strong> Ch 1: Hello GBA</a></li><li><ol class="section"><li><a href="ch1/hello1.html"><strong aria-hidden="true">3.1.</strong> hello1</a></li><li><a href="ch1/io_registers.html"><strong aria-hidden="true">3.2.</strong> IO Registers</a></li><li><a href="ch1/the_display_control_register.html"><strong aria-hidden="true">3.3.</strong> The Display Control Register</a></li><li><a href="ch1/video_memory_intro.html"><strong aria-hidden="true">3.4.</strong> Video Memory Intro</a></li><li><a href="ch1/hello2.html"><strong aria-hidden="true">3.5.</strong> hello2</a></li></ol></li><li><a href="ch2/index.html"><strong aria-hidden="true">4.</strong> Ch 2: User Input</a></li><li><ol class="section"><li><a href="ch2/the_key_input_register.html"><strong aria-hidden="true">4.1.</strong> The Key Input Register</a></li><li><a href="ch2/the_vcount_register.html"><strong aria-hidden="true">4.2.</strong> The VCount Register</a></li><li><a href="ch2/light_cycle.html"><strong aria-hidden="true">4.3.</strong> light_cycle</a></li></ol></li></ol>
|
<ol class="chapter"><li><a href="introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="ch00/index.html"><strong aria-hidden="true">2.</strong> Ch 0: Development Setup</a></li><li><a href="ch01/index.html"><strong aria-hidden="true">3.</strong> Ch 1: Hello GBA</a></li><li><ol class="section"><li><a href="ch01/hello1.html"><strong aria-hidden="true">3.1.</strong> hello1</a></li><li><a href="ch01/volatile.html"><strong aria-hidden="true">3.2.</strong> Volatile</a></li><li><a href="ch01/io_registers.html"><strong aria-hidden="true">3.3.</strong> IO Registers</a></li><li><a href="ch01/the_display_control_register.html"><strong aria-hidden="true">3.4.</strong> The Display Control Register</a></li><li><a href="ch01/video_memory_intro.html"><strong aria-hidden="true">3.5.</strong> Video Memory Intro</a></li><li><a href="ch01/hello2.html"><strong aria-hidden="true">3.6.</strong> hello2</a></li></ol></li><li><a href="ch02/index.html"><strong aria-hidden="true">4.</strong> Ch 2: User Input</a></li><li><ol class="section"><li><a href="ch02/the_key_input_register.html"><strong aria-hidden="true">4.1.</strong> The Key Input Register</a></li><li><a href="ch02/the_vcount_register.html"><strong aria-hidden="true">4.2.</strong> The VCount Register</a></li><li><a href="ch02/light_cycle.html"><strong aria-hidden="true">4.3.</strong> light_cycle</a></li></ol></li><li><a href="ch03/index.html"><strong aria-hidden="true">5.</strong> Ch 3: Memory and Objects</a></li><li><ol class="section"><li><a href="ch03/gba_memory.html"><strong aria-hidden="true">5.1.</strong> GBA Memory</a></li><li><a href="ch03/tiled_backgrounds.html"><strong aria-hidden="true">5.2.</strong> Tiled Backgrounds</a></li><li><a href="ch03/object_basics.html"><strong aria-hidden="true">5.3.</strong> Object Basics</a></li><li><a href="ch03/gba_rng.html"><strong aria-hidden="true">5.4.</strong> GBA RNG</a></li><li><a href="ch03/memory_game.html"><strong aria-hidden="true">5.5.</strong> memory_game</a></li></ol></li></ol>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div id="page-wrapper" class="page-wrapper">
|
<div id="page-wrapper" class="page-wrapper">
|
||||||
|
@ -102,7 +102,7 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 class="menu-title">Rust GBA Tutorials</h1>
|
<h1 class="menu-title">Rust GBA Guide</h1>
|
||||||
|
|
||||||
<div class="right-buttons">
|
<div class="right-buttons">
|
||||||
<a href="print.html" title="Print this book" aria-label="Print this book">
|
<a href="print.html" title="Print this book" aria-label="Print this book">
|
||||||
|
@ -137,8 +137,31 @@
|
||||||
<div id="content" class="content">
|
<div id="content" class="content">
|
||||||
<main>
|
<main>
|
||||||
<a class="header" href="#introduction" id="introduction"><h1>Introduction</h1></a>
|
<a class="header" href="#introduction" id="introduction"><h1>Introduction</h1></a>
|
||||||
<p>Here's a book that'll help you program in Rust on the GBA.</p>
|
<p>Here's a book that'll help you program in Rust on the Game Boy Advance (GBA).</p>
|
||||||
<p>It's very "work in progress". At the moment there's only one demo program.</p>
|
<p>It's a work in progress of course, but so is most of everything in Rust.</p>
|
||||||
|
<a class="header" href="#style-and-purpose" id="style-and-purpose"><h2>Style and Purpose</h2></a>
|
||||||
|
<p>I'm out to teach you how to program in Rust on the GBA, obviously. However,
|
||||||
|
while there <em>is</em> a <a href="https://github.com/rust-console/gba">gba</a> crate, and while I
|
||||||
|
genuinely believe it to be a good and useful crate for GBA programming, we <em>will
|
||||||
|
not</em> be using the <code>gba</code> crate within this book. In fact we won't be using any
|
||||||
|
crates at all. We can call it the <a href="https://handmadehero.org/">Handmade Hero</a>
|
||||||
|
approach, if you like.</p>
|
||||||
|
<p>I don't want to just teach you how to use the <code>gba</code> crate, I want to teach you
|
||||||
|
what you'd need to know to write the crate from scratch if it wasn't there.</p>
|
||||||
|
<p>Each chapter of the book will focus on a few things you'll need to know about
|
||||||
|
GBA programming and then present a fully self-contained example that puts those
|
||||||
|
ideas into action. Just one file per example, no dependencies, no external
|
||||||
|
assets, no fuss. The examples will be in the text of the book within code
|
||||||
|
blocks, but also you can find them in the <a href="https://github.com/rust-console/gba/tree/master/examples">examples
|
||||||
|
directory</a> of the repo
|
||||||
|
if you want to get them that way.</p>
|
||||||
|
<a class="header" href="#expected-knowledge" id="expected-knowledge"><h2>Expected Knowledge</h2></a>
|
||||||
|
<p>I will try not to ask too much of the reader ahead of time, but you are expected
|
||||||
|
to have already read <a href="https://doc.rust-lang.org/book/">The Rust Book</a>.</p>
|
||||||
|
<p>It's very difficult to know when you've said something that someone else won't
|
||||||
|
already know about, or if you're presenting ideas out of order. If things aren't
|
||||||
|
clear please <a href="https://github.com/rust-console/gba/issues">file an issue</a> and
|
||||||
|
we'll try to address it.</p>
|
||||||
<a class="header" href="#getting-help" id="getting-help"><h2>Getting Help</h2></a>
|
<a class="header" href="#getting-help" id="getting-help"><h2>Getting Help</h2></a>
|
||||||
<p>If you want to contact us you should join the <a href="https://discordapp.com/invite/aVESxV8">Rust Community
|
<p>If you want to contact us you should join the <a href="https://discordapp.com/invite/aVESxV8">Rust Community
|
||||||
Discord</a> and ask in the <code>#gamedev</code>
|
Discord</a> and ask in the <code>#gamedev</code>
|
||||||
|
@ -148,9 +171,10 @@ channel.</p>
|
||||||
<li><code>Lokathor</code> is the fool who decided to write a crate and book for it.</li>
|
<li><code>Lokathor</code> is the fool who decided to write a crate and book for it.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>If it's <em>not</em> a GBA specific question then you can probably ask any of the other
|
<p>If it's <em>not</em> a GBA specific question then you can probably ask any of the other
|
||||||
folks in the server as well.</p>
|
folks in the server as well (there's a few hundred folks).</p>
|
||||||
<a class="header" href="#other-works" id="other-works"><h2>Other Works</h2></a>
|
<a class="header" href="#further-reading" id="further-reading"><h2>Further Reading</h2></a>
|
||||||
<p>If you want to read more about developing on the GBA there are some other good resources as well:</p>
|
<p>If you want to read more about developing on the GBA there are some other good
|
||||||
|
resources as well:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="https://www.coranac.com/tonc/text/toc.htm">Tonc</a>, a tutorial series written
|
<li><a href="https://www.coranac.com/tonc/text/toc.htm">Tonc</a>, a tutorial series written
|
||||||
for C, but it's what I based the ordering of this book's sections on.</li>
|
for C, but it's what I based the ordering of this book's sections on.</li>
|
||||||
|
@ -165,49 +189,53 @@ art diagrams and example C struct layouts than GBATEK does.</li>
|
||||||
<p>Before you can build a GBA game you'll have to follow some special steps to
|
<p>Before you can build a GBA game you'll have to follow some special steps to
|
||||||
setup the development environment. Perhaps unfortunately, there's enough detail
|
setup the development environment. Perhaps unfortunately, there's enough detail
|
||||||
here to warrant a mini-chapter all on its own.</p>
|
here to warrant a mini-chapter all on its own.</p>
|
||||||
<p>Before we begin I'd like to give a special thanks to <strong>Ketsuban</strong>, who is the
|
<p>Once again, extra special thanks to <strong>Ketsuban</strong>, who first dove into how to
|
||||||
wizard that arranged for all of this to be able to happen and laid out the
|
make this all work with rust and then shared it with the world.</p>
|
||||||
details of the plan to the rest of the world.</p>
|
|
||||||
<a class="header" href="#per-system-setup" id="per-system-setup"><h2>Per System Setup</h2></a>
|
<a class="header" href="#per-system-setup" id="per-system-setup"><h2>Per System Setup</h2></a>
|
||||||
<p>Obviously you need your computer to have a working rust installation. However,
|
<p>Obviously you need your computer to have a <a href="https://rustup.rs/">working rust
|
||||||
you'll also need to ensure that you're using a nightly toolchain. You can run
|
installation</a>. However, you'll also need to ensure that
|
||||||
<code>rustup default nightly</code> to set nightly as the system wide default toolchain, or
|
you're using a nightly toolchain (we will need it for inline assembly, among
|
||||||
you can use a <a href="https://github.com/rust-lang-nursery/rustup.rs#the-toolchain-file">toolchain
|
other potential useful features). You can run <code>rustup default nightly</code> to set
|
||||||
|
nightly as the system wide default toolchain, or you can use a <a href="https://github.com/rust-lang-nursery/rustup.rs#the-toolchain-file">toolchain
|
||||||
file</a> to use
|
file</a> to use
|
||||||
nightly just on a specific project, but either way we'll be assuming nightly
|
nightly just on a specific project, but either way we'll be assuming the use of
|
||||||
from now on.</p>
|
nightly from now on. You'll also need the <code>rust-src</code> component so that
|
||||||
<p>Next you need <a href="https://devkitpro.org/wiki/Getting_Started">devkitpro</a>. They've
|
<code>cargo-xbuild</code> will be able to compile the core crate for us in a bit, so run
|
||||||
got a graphical installer for Windows, and <code>pacman</code> support on Linux. We'll be
|
<code>rustup component add rust-src</code>.</p>
|
||||||
using a few of their binutils for the <code>arm-none-eabi</code> target, and we'll also be
|
<p>Next, you need <a href="https://devkitpro.org/wiki/Getting_Started">devkitpro</a>. They've
|
||||||
using some of their tools that are specific to GBA development, so <em>even if</em> you
|
got a graphical installer for Windows that runs nicely, and I guess <code>pacman</code>
|
||||||
already have the right binutils for whatever reason, you'll still want devkitpro
|
support on Linux (I'm on Windows so I haven't tried the Linux install myself).
|
||||||
for the <code>gbafix</code> utility.</p>
|
We'll be using a few of their general binutils for the <code>arm-none-eabi</code> target,
|
||||||
|
and we'll also be using some of their tools that are specific to GBA
|
||||||
|
development, so <em>even if</em> you already have the right binutils for whatever
|
||||||
|
reason, you'll still want devkitpro for the <code>gbafix</code> utility.</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>On Windows you'll want something like <code>C:\devkitpro\devkitARM\bin</code> and
|
<li>On Windows you'll want something like <code>C:\devkitpro\devkitARM\bin</code> and
|
||||||
<code>C:\devkitpro\tools\bin</code> to be <a href="https://stackoverflow.com/q/44272416/455232">added to your
|
<code>C:\devkitpro\tools\bin</code> to be <a href="https://stackoverflow.com/q/44272416/455232">added to your
|
||||||
PATH</a>, depending on where you
|
PATH</a>, depending on where you
|
||||||
installed it to and such.</li>
|
installed it to and such.</li>
|
||||||
<li>On Linux you'll also want it to be added to your path, but if you're using
|
<li>On Linux you'll also want it to be added to your path, but if you're using
|
||||||
Linux I'll just assume you know how to do all that.</li>
|
Linux I'll just assume you know how to do all that. I'm told that the default
|
||||||
|
installation path is <code>/opt/devkitpro/devkitARM/bin</code>, so look there first if
|
||||||
|
you didn't select some other place.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>Finally, you'll need <code>cargo-xbuild</code>. Just run <code>cargo install cargo-xbuild</code> and
|
<p>Finally, you'll need <code>cargo-xbuild</code>. Just run <code>cargo install cargo-xbuild</code> and
|
||||||
cargo will figure it all out for you.</p>
|
cargo will figure it all out for you.</p>
|
||||||
<a class="header" href="#per-project-setup" id="per-project-setup"><h2>Per Project Setup</h2></a>
|
<a class="header" href="#per-project-setup" id="per-project-setup"><h2>Per Project Setup</h2></a>
|
||||||
<p>Now you'll need some particular files each time you want to start a new project.
|
<p>Once the system wide tools are ready, you'll need some particular files each
|
||||||
You can find them in the root of the <a href="https://github.com/rust-console/gba">rust-console/gba
|
time you want to start a new project. You can find them in the root of the
|
||||||
repo</a>.</p>
|
<a href="https://github.com/rust-console/gba">rust-console/gba repo</a>.</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><code>thumbv4-none-agb.json</code> describes the overall GBA to cargo-xbuild so it knows
|
<li><code>thumbv4-none-agb.json</code> describes the overall GBA to cargo-xbuild (and LLVM)
|
||||||
what to do. This is actually a somewhat made up target name since there's no
|
so it knows what to do. Technically the GBA is <code>thumbv4-none-eabi</code>, but we
|
||||||
official target name. The GBA is essentially the same as a normal
|
change the <code>eabi</code> to <code>agb</code> so that we can distinguish it from other <code>eabi</code>
|
||||||
<code>thumbv4-none-eabi</code> device, but we give it the "agb" signifier so that later
|
devices when using <code>cfg</code> flags.</li>
|
||||||
on we'll be able to use rust's <code>cfg</code> ability to allow our code to know if it's
|
|
||||||
specifically targeting a GBA or some other similar device (like an NDS).</li>
|
|
||||||
<li><code>crt0.s</code> describes some ASM startup stuff. If you have more ASM to place here
|
<li><code>crt0.s</code> describes some ASM startup stuff. If you have more ASM to place here
|
||||||
later on this is where you can put it. You also need to build it into a
|
later on this is where you can put it. You also need to build it into a
|
||||||
<code>crt0.o</code> file before it can actually be used, but we'll cover that below.</li>
|
<code>crt0.o</code> file before it can actually be used, but we'll cover that below.</li>
|
||||||
<li><code>linker.ld</code> tells the linker more critical info about the layout expectations
|
<li><code>linker.ld</code> tells the linker all the critical info about the layout
|
||||||
that the GBA has about our program.</li>
|
expectations that the GBA has about our program, and that it should also
|
||||||
|
include the <code>crt0.o</code> file with our compiled rust code.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<a class="header" href="#compiling" id="compiling"><h2>Compiling</h2></a>
|
<a class="header" href="#compiling" id="compiling"><h2>Compiling</h2></a>
|
||||||
<p>The next steps only work once you've got some source code to build. If you need
|
<p>The next steps only work once you've got some source code to build. If you need
|
||||||
|
@ -231,13 +259,13 @@ practically instant operation.</li>
|
||||||
as <code>--release</code>, and options, such as <code>--bin foo</code> or <code>--examples</code>, that you'd
|
as <code>--release</code>, and options, such as <code>--bin foo</code> or <code>--examples</code>, that you'd
|
||||||
expect <code>cargo</code> to accept.</li>
|
expect <code>cargo</code> to accept.</li>
|
||||||
<li>You <strong>can not</strong> build and run tests this way, because they require <code>std</code>,
|
<li>You <strong>can not</strong> build and run tests this way, because they require <code>std</code>,
|
||||||
which the GBA doesn't have. You can still run some of your project's tests
|
which the GBA doesn't have. If you want you can still run some of your
|
||||||
with <code>cargo test</code>, but that builds for your local machine, so anything
|
project's tests with <code>cargo test --lib</code> or similar, but that builds for your
|
||||||
specific to the GBA (such as reading and writing registers) won't be
|
local machine, so anything specific to the GBA (such as reading and writing
|
||||||
testable that way. If you want to isolate and try out some piece code
|
registers) won't be testable that way. If you want to isolate and try out
|
||||||
running on the GBA you'll unfortunately have to make a demo for it in your
|
some piece code running on the GBA you'll unfortunately have to make a demo
|
||||||
<code>examples/</code> directory and then run the demo in an emulator and see if it
|
for it in your <code>examples/</code> directory and then run the demo in an emulator
|
||||||
does what you expect.</li>
|
and see if it does what you expect.</li>
|
||||||
<li>The file extension is important. <code>cargo xbuild</code> takes it as a flag to
|
<li>The file extension is important. <code>cargo xbuild</code> takes it as a flag to
|
||||||
compile dependencies with the same sysroot, so you can include crates
|
compile dependencies with the same sysroot, so you can include crates
|
||||||
normally. Well, creates that work in the GBA's limited environment, but you
|
normally. Well, creates that work in the GBA's limited environment, but you
|
||||||
|
@ -300,7 +328,10 @@ ROM is patched in place, so we don't even need to specify a new destination.</li
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>And you're finally done!</p>
|
<p>And you're finally done!</p>
|
||||||
<p>Of course, you probably want to make a script for all that, but it's up to you.</p>
|
<p>Of course, you probably want to make a script for all that, but it's up to you.
|
||||||
|
On our own project we have it mostly set up within a <code>Makefile.toml</code> which runs
|
||||||
|
using the <a href="https://github.com/sagiegurari/cargo-make">cargo-make</a> plugin. It's
|
||||||
|
not really the best plugin, but it's what's available.</p>
|
||||||
<a class="header" href="#ch-1-hello-gba" id="ch-1-hello-gba"><h1>Ch 1: Hello GBA</h1></a>
|
<a class="header" href="#ch-1-hello-gba" id="ch-1-hello-gba"><h1>Ch 1: Hello GBA</h1></a>
|
||||||
<p>Traditionally a person writes a "hello, world" program so that they can test
|
<p>Traditionally a person writes a "hello, world" program so that they can test
|
||||||
that their development environment is setup properly and to just get a feel for
|
that their development environment is setup properly and to just get a feel for
|
||||||
|
@ -310,12 +341,12 @@ will look like. All that stuff.</p>
|
||||||
GBA has no terminal, but it does have a screen, so instead we're going to draw
|
GBA has no terminal, but it does have a screen, so instead we're going to draw
|
||||||
three dots to the screen.</p>
|
three dots to the screen.</p>
|
||||||
<a class="header" href="#hello1" id="hello1"><h1>hello1</h1></a>
|
<a class="header" href="#hello1" id="hello1"><h1>hello1</h1></a>
|
||||||
<p>Ready? Here goes:</p>
|
<p>Our first example will be a totally minimal, full magic number crazy town.
|
||||||
|
Ready? Here goes:</p>
|
||||||
<p><code>hello1.rs</code></p>
|
<p><code>hello1.rs</code></p>
|
||||||
<pre><pre class="playpen"><code class="language-rust">#![feature(start)]
|
<pre><pre class="playpen"><code class="language-rust">#![feature(start)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
#[cfg(not(test))]
|
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||||
loop {}
|
loop {}
|
||||||
|
@ -332,15 +363,16 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<p>Throw that into your project, build the program (as described back in Chapter
|
<p>Throw that into your project skeleton, build the program (as described back in
|
||||||
0), and give it a run. You should see a red, green, and blue dot close-ish to
|
Chapter 0), and give it a run in your emulator. You should see a red, green, and
|
||||||
the middle of the screen. If you don't, something already went wrong. Double
|
blue dot close-ish to the middle of the screen. If you don't, something already
|
||||||
check things, phone a friend, write your senators, try asking Ketsuban on the
|
went wrong. Double check things, phone a friend, write your senators, try asking
|
||||||
<a href="https://discordapp.com/invite/aVESxV8">Rust Community Discord</a>, until you're
|
Ketsuban on the <a href="https://discordapp.com/invite/aVESxV8">Rust Community Discord</a>,
|
||||||
able to get your three dots going.</p>
|
until you're able to get your three dots going.</p>
|
||||||
<a class="header" href="#explaining-hello1" id="explaining-hello1"><h2>Explaining hello1</h2></a>
|
<a class="header" href="#a-basic-hello1-explanation" id="a-basic-hello1-explanation"><h2>A basic hello1 explanation</h2></a>
|
||||||
<p>So, what just happened? Even if you're used to Rust that might look pretty
|
<p>So, what just happened? Even if you're used to Rust that might look pretty
|
||||||
strange. We'll go over each part extra carefully.</p>
|
strange. We'll go over most of the little parts right here, and then bigger
|
||||||
|
parts will get their own sections.</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust">
|
<pre><pre class="playpen"><code class="language-rust">
|
||||||
# #![allow(unused_variables)]
|
# #![allow(unused_variables)]
|
||||||
#![feature(start)]
|
#![feature(start)]
|
||||||
|
@ -364,7 +396,6 @@ only life.</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust">
|
<pre><pre class="playpen"><code class="language-rust">
|
||||||
# #![allow(unused_variables)]
|
# #![allow(unused_variables)]
|
||||||
#fn main() {
|
#fn main() {
|
||||||
#[cfg(not(test))]
|
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||||
loop {}
|
loop {}
|
||||||
|
@ -376,9 +407,6 @@ Basically, if we somehow trigger a panic, this is where the program goes.
|
||||||
However, right now we don't know how to get any sort of message out to the user
|
However, right now we don't know how to get any sort of message out to the user
|
||||||
so... we do nothing at all. We <em>can't even return</em> from here, so we just sit in
|
so... we do nothing at all. We <em>can't even return</em> from here, so we just sit in
|
||||||
an infinite loop. The player will have to reset the universe from the outside.</p>
|
an infinite loop. The player will have to reset the universe from the outside.</p>
|
||||||
<p>The <code>#[cfg(not(test))]</code> part makes this item only exist in the program when
|
|
||||||
we're <em>not</em> in a test build. This is so that <code>cargo test</code> and such work right as
|
|
||||||
much as possible.</p>
|
|
||||||
<pre><pre class="playpen"><code class="language-rust">#[start]
|
<pre><pre class="playpen"><code class="language-rust">#[start]
|
||||||
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
|
@ -442,18 +470,17 @@ magic numbers mean or do.</p>
|
||||||
<li><code>0x06000000</code> is the start of Video RAM.</li>
|
<li><code>0x06000000</code> is the start of Video RAM.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>So we write some magic to the display control register once, then we write some
|
<p>So we write some magic to the display control register once, then we write some
|
||||||
other magic to three locations of magic to the Video RAM. We get three dots,
|
other magic to three magic locations in the Video RAM. Somehow that shows three
|
||||||
each in their own location... so that second part makes sense at least.</p>
|
dots. Gotta read on to find out why!</p>
|
||||||
<p>We'll get into the magic number details in the other sections of this chapter.</p>
|
<a class="header" href="#volatile" id="volatile"><h1>Volatile</h1></a>
|
||||||
<a class="header" href="#sidebar-volatile" id="sidebar-volatile"><h2>Sidebar: Volatile</h2></a>
|
<p>Before we focus on what the numbers mean, first let's ask ourselves: Why are we
|
||||||
<p>We'll get into what all that is in a moment, but first let's ask ourselves: Why
|
doing <em>volatile</em> writes? You've probably never used that keywords before at all.
|
||||||
are we doing <em>volatile</em> writes? You've probably never used it before at all.
|
What <em>is</em> volatile anyway?</p>
|
||||||
What is volatile anyway?</p>
|
<p>Well, the optimizer is pretty aggressive, and so it'll skip reads and writes
|
||||||
<p>Well, the optimizer is pretty aggressive some of the time, and so it'll skip
|
when it thinks can. Like if you write to a pointer once, and then again a moment
|
||||||
reads and writes when it thinks can. Like if you write to a pointer once, and
|
later, and it didn't see any other reads in between, it'll think that it can
|
||||||
then again a moment later, and it didn't see any other reads in between, it'll
|
just skip doing that first write since it'll get overwritten anyway. Sometimes
|
||||||
think that it can just skip doing that first write since it'll get overwritten
|
that's correct, but sometimes it's not.</p>
|
||||||
anyway. Sometimes that's right, but sometimes it's wrong.</p>
|
|
||||||
<p>Marking a read or write as <em>volatile</em> tells the compiler that it really must do
|
<p>Marking a read or write as <em>volatile</em> tells the compiler that it really must do
|
||||||
that action, and in the exact order that we wrote it out. It says that there
|
that action, and in the exact order that we wrote it out. It says that there
|
||||||
might even be special hardware side effects going on that the compiler isn't
|
might even be special hardware side effects going on that the compiler isn't
|
||||||
|
@ -466,18 +493,48 @@ only relative to other volatile operations. So something like</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust">
|
<pre><pre class="playpen"><code class="language-rust">
|
||||||
# #![allow(unused_variables)]
|
# #![allow(unused_variables)]
|
||||||
#fn main() {
|
#fn main() {
|
||||||
c.volatile_write(5);
|
c.write_volatile(5);
|
||||||
a += b;
|
a += b;
|
||||||
d.volatile_write(7);
|
d.write_volatile(7);
|
||||||
#}</code></pre></pre>
|
#}</code></pre></pre>
|
||||||
<p>might end up changing <code>a</code> either before or after the change to <code>c</code> (since the
|
<p>might end up changing <code>a</code> either before or after the change to <code>c</code> (since the
|
||||||
value of <code>a</code> doesn't affect the write to <code>c</code>), but the write to <code>d</code> will
|
value of <code>a</code> doesn't affect the write to <code>c</code>), but the write to <code>d</code> will
|
||||||
<em>always</em> happen after the write to <code>c</code> even though the compiler doesn't see any
|
<em>always</em> happen after the write to <code>c</code>, even though the compiler doesn't see any
|
||||||
direct data dependency there.</p>
|
direct data dependency there.</p>
|
||||||
<p>If you ever use volatile stuff on other platforms it's important to note that
|
<p>If you ever go on to use volatile stuff on other platforms it's important to
|
||||||
volatile doesn't make things thread-safe, you still need atomic for that.
|
note that volatile doesn't make things thread-safe, you still need atomic for
|
||||||
However, the GBA doesn't have threads, so we don't have to worry about thread
|
that. However, the GBA doesn't have threads, so we don't have to worry about
|
||||||
safety concerns.</p>
|
those sorts of thread safety concerns (there's interrupts, but that's another
|
||||||
|
matter).</p>
|
||||||
|
<a class="header" href="#volatile-by-default" id="volatile-by-default"><h2>Volatile by default</h2></a>
|
||||||
|
<p>Of course, writing out <code>volatile_write</code> every time is more than we wanna do.
|
||||||
|
There's clarity and then there's excessive. This is a chance to write our first
|
||||||
|
<a href="https://doc.rust-lang.org/1.0.0/style/features/types/newtype.html">newtype</a>.
|
||||||
|
Basically a type that's got the exact same binary representation as some other
|
||||||
|
type, but new methods and trait implementations.</p>
|
||||||
|
<p>We want a <code>*mut T</code> that's volatile by default, and also when we offset it...
|
||||||
|
well the verdict is slightly unclear on how <code>offset</code> vs <code>wrapping_offset</code> work
|
||||||
|
when you're using pointers that you made up out of nowhere. I've asked the
|
||||||
|
experts and they genuinely weren't sure, so we'll make an <code>offset</code> method that
|
||||||
|
does a <code>wrapping_offset</code> just to be careful.</p>
|
||||||
|
<pre><pre class="playpen"><code class="language-rust">
|
||||||
|
# #![allow(unused_variables)]
|
||||||
|
#fn main() {
|
||||||
|
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct VolatilePtr<T>(pub *mut T);
|
||||||
|
impl<T> VolatilePtr<T> {
|
||||||
|
pub unsafe fn read(&self) -> T {
|
||||||
|
core::ptr::read_volatile(self.0)
|
||||||
|
}
|
||||||
|
pub unsafe fn write(&self, data: T) {
|
||||||
|
core::ptr::write_volatile(self.0, data);
|
||||||
|
}
|
||||||
|
pub unsafe fn offset(self, count: isize) -> Self {
|
||||||
|
VolatilePtr(self.0.wrapping_offset(count))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#}</code></pre></pre>
|
||||||
<a class="header" href="#io-registers" id="io-registers"><h1>IO Registers</h1></a>
|
<a class="header" href="#io-registers" id="io-registers"><h1>IO Registers</h1></a>
|
||||||
<p>The GBA has a large number of <strong>IO Registers</strong> (not to be confused with CPU
|
<p>The GBA has a large number of <strong>IO Registers</strong> (not to be confused with CPU
|
||||||
registers). These are special memory locations from <code>0x04000000</code> to
|
registers). These are special memory locations from <code>0x04000000</code> to
|
||||||
|
@ -597,8 +654,6 @@ some nifty graphical effects.</p>
|
||||||
binary</a>, and we get
|
binary</a>, and we get
|
||||||
<code>0b100_0000_0011</code>. So, that's setting Mode 3 with background 2 enabled and
|
<code>0b100_0000_0011</code>. So, that's setting Mode 3 with background 2 enabled and
|
||||||
nothing else special.</p>
|
nothing else special.</p>
|
||||||
<p>However, I think we can do better than that. This is a prime target for more
|
|
||||||
newtyping as we attempt a <code>hello2</code> program.</p>
|
|
||||||
<a class="header" href="#video-memory-intro" id="video-memory-intro"><h1>Video Memory Intro</h1></a>
|
<a class="header" href="#video-memory-intro" id="video-memory-intro"><h1>Video Memory Intro</h1></a>
|
||||||
<p>The GBA's Video RAM is 96k stretching from <code>0x0600_0000</code> to <code>0x0601_7FFF</code>.</p>
|
<p>The GBA's Video RAM is 96k stretching from <code>0x0600_0000</code> to <code>0x0601_7FFF</code>.</p>
|
||||||
<p>The Video RAM can only be accessed totally freely during a Vertical Blank (aka
|
<p>The Video RAM can only be accessed totally freely during a Vertical Blank (aka
|
||||||
|
@ -691,9 +746,9 @@ data to fit in two pages, we compress the resolution.</p>
|
||||||
again we probably need to <a href="https://www.wolframalpha.com/">convert them</a> into
|
again we probably need to <a href="https://www.wolframalpha.com/">convert them</a> into
|
||||||
binary to make sense of it.</p>
|
binary to make sense of it.</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>0x001F: 0b11111</li>
|
<li>0x001F: 0b0_00000_00000_11111</li>
|
||||||
<li>0x03E0: 0b11111_00000</li>
|
<li>0x03E0: 0b0_00000_11111_00000</li>
|
||||||
<li>0x7C00: 0b11111_00000_00000</li>
|
<li>0x7C00: 0b0_11111_00000_00000</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>Ah, of course, a red pixel, a green pixel, and a blue pixel.</p>
|
<p>Ah, of course, a red pixel, a green pixel, and a blue pixel.</p>
|
||||||
<a class="header" href="#hello2" id="hello2"><h1>hello2</h1></a>
|
<a class="header" href="#hello2" id="hello2"><h1>hello2</h1></a>
|
||||||
|
@ -702,7 +757,6 @@ binary to make sense of it.</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust">#![feature(start)]
|
<pre><pre class="playpen"><code class="language-rust">#![feature(start)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
#[cfg(not(test))]
|
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||||
loop {}
|
loop {}
|
||||||
|
@ -720,11 +774,12 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||||
}
|
}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<p>Now let's clean this up so that it's clearer what's going on.</p>
|
<p>Now let's clean this up so that it's clearer what's going on.</p>
|
||||||
<p>First we'll label that display control stuff:</p>
|
<p>First we'll label that display control stuff, including using the <code>VolatilePtr</code>
|
||||||
|
type from the volatile explanation:</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust">
|
<pre><pre class="playpen"><code class="language-rust">
|
||||||
# #![allow(unused_variables)]
|
# #![allow(unused_variables)]
|
||||||
#fn main() {
|
#fn main() {
|
||||||
pub const DISPCNT: *mut u16 = 0x04000000 as *mut u16;
|
pub const DISPCNT: VolatilePtr<u16> = VolatilePtr(0x04000000 as *mut u16);
|
||||||
pub const MODE3: u16 = 3;
|
pub const MODE3: u16 = 3;
|
||||||
pub const BG2: u16 = 0b100_0000_0000;
|
pub const BG2: u16 = 0b100_0000_0000;
|
||||||
#}</code></pre></pre>
|
#}</code></pre></pre>
|
||||||
|
@ -735,8 +790,11 @@ pub const BG2: u16 = 0b100_0000_0000;
|
||||||
pub const VRAM: usize = 0x06000000;
|
pub const VRAM: usize = 0x06000000;
|
||||||
pub const SCREEN_WIDTH: isize = 240;
|
pub const SCREEN_WIDTH: isize = 240;
|
||||||
#}</code></pre></pre>
|
#}</code></pre></pre>
|
||||||
<p>And then we want a small helper function for putting together a color value.</p>
|
<p>Note that VRAM has to be interpreted in different ways depending on mode, so we
|
||||||
<p>Happily, this one can even be declared as a const function. At the time of
|
just leave it as <code>usize</code> and we'll cast it into the right form closer to the
|
||||||
|
actual use.</p>
|
||||||
|
<p>Next we want a small helper function for putting together a color value.
|
||||||
|
Happily, this one can even be declared as a <code>const</code> function. At the time of
|
||||||
writing, we've got the "minimal const fn" support in nightly. It really is quite
|
writing, we've got the "minimal const fn" support in nightly. It really is quite
|
||||||
limited, but I'm happy to let rustc and LLVM pre-compute as much as they can
|
limited, but I'm happy to let rustc and LLVM pre-compute as much as they can
|
||||||
when it comes to the GBA's tiny CPU.</p>
|
when it comes to the GBA's tiny CPU.</p>
|
||||||
|
@ -754,7 +812,7 @@ usually helps you think about it a lot better.</p>
|
||||||
# #![allow(unused_variables)]
|
# #![allow(unused_variables)]
|
||||||
#fn main() {
|
#fn main() {
|
||||||
pub unsafe fn mode3_pixel(col: isize, row: isize, color: u16) {
|
pub unsafe fn mode3_pixel(col: isize, row: isize, color: u16) {
|
||||||
(VRAM as *mut u16).offset(col + row * SCREEN_WIDTH).write_volatile(color);
|
VolatilePtr(VRAM as *mut u16).offset(col + row * SCREEN_WIDTH).write(color);
|
||||||
}
|
}
|
||||||
#}</code></pre></pre>
|
#}</code></pre></pre>
|
||||||
<p>So now we've got this:</p>
|
<p>So now we've got this:</p>
|
||||||
|
@ -762,7 +820,6 @@ pub unsafe fn mode3_pixel(col: isize, row: isize, color: u16) {
|
||||||
<pre><pre class="playpen"><code class="language-rust">#![feature(start)]
|
<pre><pre class="playpen"><code class="language-rust">#![feature(start)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
#[cfg(not(test))]
|
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||||
loop {}
|
loop {}
|
||||||
|
@ -771,7 +828,7 @@ fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||||
#[start]
|
#[start]
|
||||||
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||||
unsafe {
|
unsafe {
|
||||||
DISPCNT.write_volatile(MODE3 | BG2);
|
DISPCNT.write(MODE3 | BG2);
|
||||||
mode3_pixel(120, 80, rgb16(31, 0, 0));
|
mode3_pixel(120, 80, rgb16(31, 0, 0));
|
||||||
mode3_pixel(136, 80, rgb16(0, 31, 0));
|
mode3_pixel(136, 80, rgb16(0, 31, 0));
|
||||||
mode3_pixel(120, 96, rgb16(0, 0, 31));
|
mode3_pixel(120, 96, rgb16(0, 0, 31));
|
||||||
|
@ -779,7 +836,22 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const DISPCNT: *mut u16 = 0x04000000 as *mut u16;
|
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct VolatilePtr<T>(pub *mut T);
|
||||||
|
impl<T> VolatilePtr<T> {
|
||||||
|
pub unsafe fn read(&self) -> T {
|
||||||
|
core::ptr::read_volatile(self.0)
|
||||||
|
}
|
||||||
|
pub unsafe fn write(&self, data: T) {
|
||||||
|
core::ptr::write_volatile(self.0, data);
|
||||||
|
}
|
||||||
|
pub unsafe fn offset(self, count: isize) -> Self {
|
||||||
|
VolatilePtr(self.0.wrapping_offset(count))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const DISPCNT: VolatilePtr<u16> = VolatilePtr(0x04000000 as *mut u16);
|
||||||
pub const MODE3: u16 = 3;
|
pub const MODE3: u16 = 3;
|
||||||
pub const BG2: u16 = 0b100_0000_0000;
|
pub const BG2: u16 = 0b100_0000_0000;
|
||||||
|
|
||||||
|
@ -791,13 +863,14 @@ pub const fn rgb16(red: u16, green: u16, blue: u16) -> u16 {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn mode3_pixel(col: isize, row: isize, color: u16) {
|
pub unsafe fn mode3_pixel(col: isize, row: isize, color: u16) {
|
||||||
(VRAM as *mut u16).offset(col + row * SCREEN_WIDTH).write_volatile(color);
|
VolatilePtr(VRAM as *mut u16).offset(col + row * SCREEN_WIDTH).write(color);
|
||||||
}
|
}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<p>Exact same program that we started with, but much easier to read.</p>
|
<p>Exact same program that we started with, but much easier to read.</p>
|
||||||
<p>Of course, in the full <code>gba</code> crate that this book is a part of we have these and
|
<p>Of course, in the full <code>gba</code> crate that this book is a part of we have these and
|
||||||
other elements all labeled and sorted out for you. Still, for educational
|
other elements all labeled and sorted out for you (not identically, but
|
||||||
purposes it's often best to do it yourself at least once.</p>
|
similarly). Still, for educational purposes it's often best to do it yourself at
|
||||||
|
least once.</p>
|
||||||
<a class="header" href="#ch-2-user-input" id="ch-2-user-input"><h1>Ch 2: User Input</h1></a>
|
<a class="header" href="#ch-2-user-input" id="ch-2-user-input"><h1>Ch 2: User Input</h1></a>
|
||||||
<p>It's all well and good to draw three pixels, but they don't do anything yet. We
|
<p>It's all well and good to draw three pixels, but they don't do anything yet. We
|
||||||
want them to do something, and for that we need to get some input from the user.</p>
|
want them to do something, and for that we need to get some input from the user.</p>
|
||||||
|
@ -865,15 +938,15 @@ reading and writing the key bits.</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust">
|
<pre><pre class="playpen"><code class="language-rust">
|
||||||
# #![allow(unused_variables)]
|
# #![allow(unused_variables)]
|
||||||
#fn main() {
|
#fn main() {
|
||||||
pub const KEYINPUT: *mut u16 = 0x400_0130 as *mut u16;
|
pub const KEYINPUT: VolatilePtr<u16> = VolatilePtr(0x400_0130 as *mut u16);
|
||||||
|
|
||||||
/// A newtype over the key input state of the GBA.
|
/// A newtype over the key input state of the GBA.
|
||||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct KeyInputSetting(u16);
|
pub struct KeyInputSetting(u16);
|
||||||
|
|
||||||
pub fn read_key_input() -> KeyInputSetting {
|
pub fn key_input() -> KeyInputSetting {
|
||||||
unsafe { KeyInputSetting(KEYINPUT.read_volatile()) }
|
unsafe { KeyInputSetting(KEYINPUT.read()) }
|
||||||
}
|
}
|
||||||
#}</code></pre></pre>
|
#}</code></pre></pre>
|
||||||
<p>Now we want a way to check if a key is <em>being pressed</em>, since that's normally
|
<p>Now we want a way to check if a key is <em>being pressed</em>, since that's normally
|
||||||
|
@ -898,12 +971,14 @@ folded and refolded by the compiler, but we can just check.</p>
|
||||||
<p>It turns out that the <code>!=0</code> test is 4 instructions and the <code>==0</code> test is 6
|
<p>It turns out that the <code>!=0</code> test is 4 instructions and the <code>==0</code> test is 6
|
||||||
instructions. Since we want to get savings where we can, and we'll probably
|
instructions. Since we want to get savings where we can, and we'll probably
|
||||||
check the keys of an input often enough, we'll just always use a <code>!=0</code> test and
|
check the keys of an input often enough, we'll just always use a <code>!=0</code> test and
|
||||||
then adjust how we initially read the register to compensate.</p>
|
then adjust how we initially read the register to compensate. By using xor with
|
||||||
|
a mask for only the 10 used bits we can flip the "low when pressed" values so
|
||||||
|
that the entire result has active bits in all positions where a key is pressed.</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust">
|
<pre><pre class="playpen"><code class="language-rust">
|
||||||
# #![allow(unused_variables)]
|
# #![allow(unused_variables)]
|
||||||
#fn main() {
|
#fn main() {
|
||||||
pub fn read_key_input() -> KeyInputSetting {
|
pub fn key_input() -> KeyInputSetting {
|
||||||
unsafe { KeyInputSetting(KEYINPUT.read_volatile() ^ 0b1111_1111_1111_1111) }
|
unsafe { KeyInputSetting(KEYINPUT.read_volatile() ^ 0b0000_0011_1111_1111) }
|
||||||
}
|
}
|
||||||
#}</code></pre></pre>
|
#}</code></pre></pre>
|
||||||
<p>Now we add a method for seeing if a key is pressed. In the full library there's
|
<p>Now we add a method for seeing if a key is pressed. In the full library there's
|
||||||
|
@ -1060,10 +1135,10 @@ quickly during the blank period.</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust">
|
<pre><pre class="playpen"><code class="language-rust">
|
||||||
# #![allow(unused_variables)]
|
# #![allow(unused_variables)]
|
||||||
#fn main() {
|
#fn main() {
|
||||||
pub const VCOUNT: *mut u16 = 0x0400_0006 as *mut u16;
|
pub const VCOUNT: VolatilePtr<u16> = VolatilePtr(0x0400_0006 as *mut u16);
|
||||||
|
|
||||||
pub fn read_vcount() -> u16 {
|
pub fn vcount() -> u16 {
|
||||||
unsafe { VCOUNT.read_volatile() }
|
unsafe { VCOUNT.read() }
|
||||||
}
|
}
|
||||||
#}</code></pre></pre>
|
#}</code></pre></pre>
|
||||||
<p>Then we want two little helper functions to wait until VBlank and vdraw.</p>
|
<p>Then we want two little helper functions to wait until VBlank and vdraw.</p>
|
||||||
|
@ -1073,11 +1148,11 @@ pub fn read_vcount() -> u16 {
|
||||||
pub const SCREEN_HEIGHT: isize = 160;
|
pub const SCREEN_HEIGHT: isize = 160;
|
||||||
|
|
||||||
pub fn wait_until_vblank() {
|
pub fn wait_until_vblank() {
|
||||||
while read_vcount() < SCREEN_HEIGHT as u16 {}
|
while vcount() < SCREEN_HEIGHT as u16 {}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wait_until_vdraw() {
|
pub fn wait_until_vdraw() {
|
||||||
while read_vcount() >= SCREEN_HEIGHT as u16 {}
|
while vcount() >= SCREEN_HEIGHT as u16 {}
|
||||||
}
|
}
|
||||||
#}</code></pre></pre>
|
#}</code></pre></pre>
|
||||||
<p>And... that's it. No special types to be made this time around, it's just a
|
<p>And... that's it. No special types to be made this time around, it's just a
|
||||||
|
@ -1096,21 +1171,21 @@ go off the screen or if they touch their own trail.</p>
|
||||||
pub unsafe fn mode3_clear_screen(color: u16) {
|
pub unsafe fn mode3_clear_screen(color: u16) {
|
||||||
let color = color as u32;
|
let color = color as u32;
|
||||||
let bulk_color = color << 16 | color;
|
let bulk_color = color << 16 | color;
|
||||||
let mut ptr = VRAM as *mut u32;
|
let mut ptr = VolatilePtr(VRAM as *mut u32);
|
||||||
for _ in 0..SCREEN_HEIGHT {
|
for _ in 0..SCREEN_HEIGHT {
|
||||||
for _ in 0..(SCREEN_WIDTH / 2) {
|
for _ in 0..(SCREEN_WIDTH / 2) {
|
||||||
ptr.write_volatile(bulk_color);
|
ptr.write(bulk_color);
|
||||||
ptr = ptr.offset(1);
|
ptr = ptr.offset(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn mode3_draw_pixel(col: isize, row: isize, color: u16) {
|
pub unsafe fn mode3_draw_pixel(col: isize, row: isize, color: u16) {
|
||||||
(VRAM as *mut u16).offset(col + row * SCREEN_WIDTH).write_volatile(color);
|
VolatilePtr(VRAM as *mut u16).offset(col + row * SCREEN_WIDTH).write(color);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn mode3_read_pixel(col: isize, row: isize) -> u16 {
|
pub unsafe fn mode3_read_pixel(col: isize, row: isize) -> u16 {
|
||||||
(VRAM as *mut u16).offset(col + row * SCREEN_WIDTH).read_volatile()
|
VolatilePtr(VRAM as *mut u16).offset(col + row * SCREEN_WIDTH).read()
|
||||||
}
|
}
|
||||||
#}</code></pre></pre>
|
#}</code></pre></pre>
|
||||||
<p>The draw pixel and read pixel are both pretty obvious. What's new is the clear
|
<p>The draw pixel and read pixel are both pretty obvious. What's new is the clear
|
||||||
|
@ -1122,7 +1197,7 @@ screen clear is twice as fast.</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust">#[start]
|
<pre><pre class="playpen"><code class="language-rust">#[start]
|
||||||
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||||
unsafe {
|
unsafe {
|
||||||
DISPCNT.write_volatile(MODE3 | BG2);
|
DISPCNT.write(MODE3 | BG2);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut px = SCREEN_WIDTH / 2;
|
let mut px = SCREEN_WIDTH / 2;
|
||||||
|
@ -1131,7 +1206,7 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// read the input for this frame
|
// read the input for this frame
|
||||||
let this_frame_keys = read_key_input();
|
let this_frame_keys = key_input();
|
||||||
|
|
||||||
// adjust game state and wait for vblank
|
// adjust game state and wait for vblank
|
||||||
px += 2 * this_frame_keys.column_direction() as isize;
|
px += 2 * this_frame_keys.column_direction() as isize;
|
||||||
|
@ -1182,9 +1257,271 @@ different colors.</p>
|
||||||
it's not black that means we've been here before and the player has crashed into
|
it's not black that means we've been here before and the player has crashed into
|
||||||
their own line. In this case, we reset the game without moving them to a new
|
their own line. In this case, we reset the game without moving them to a new
|
||||||
location.</p>
|
location.</p>
|
||||||
<p>Finally, if the player is in bounds and they haven't crashed, we write their color into memory at this position.</p>
|
<p>Finally, if the player is in bounds and they haven't crashed, we write their
|
||||||
|
color into memory at this position.</p>
|
||||||
<p>Regardless of how it worked out, we hold here until vdraw starts before going to
|
<p>Regardless of how it worked out, we hold here until vdraw starts before going to
|
||||||
the next loop.</p>
|
the next loop. That's all there is to it.</p>
|
||||||
|
<a class="header" href="#the-gba-crate-doesnt-quite-work-like-this" id="the-gba-crate-doesnt-quite-work-like-this"><h2>The gba crate doesn't quite work like this</h2></a>
|
||||||
|
<p>Once again, as with the <code>hello1</code> and <code>hello2</code> examples, the <code>gba</code> crate covers
|
||||||
|
much of this same ground as our example here, but in slightly different ways.</p>
|
||||||
|
<p>Better organization and abstractions are usually only realized once you've used
|
||||||
|
more of the whole thing you're trying to work with. If we want to have a crate
|
||||||
|
where the whole thing is well integrated with itself, then the examples would
|
||||||
|
also end up having to explain about things we haven't really touched on much
|
||||||
|
yet. It becomes a lot harder to teach.</p>
|
||||||
|
<p>So, going forward, we will continue to teach concepts and build examples that
|
||||||
|
don't directly depend on the <code>gba</code> crate. This allows the crate to freely grow
|
||||||
|
without all the past examples becoming a great inertia upon it.</p>
|
||||||
|
<a class="header" href="#ch-3-memory-and-objects" id="ch-3-memory-and-objects"><h1>Ch 3: Memory and Objects</h1></a>
|
||||||
|
<p>Alright so we can do some basic "movement", but we left a big trail in the video
|
||||||
|
memory of everywhere we went. Most of the time that's not what we want at all.
|
||||||
|
If we want more hardware support we're going to have to use a new video mode. So
|
||||||
|
far we've only used Mode 3, but modes 4 and 5 are basically the same. Instead,
|
||||||
|
we'll switch focus to using a tiled graphical mode.</p>
|
||||||
|
<p>First we will go over the complete GBA memory mapping. Part of this is the
|
||||||
|
memory for tiled graphics, but also things like all those IO registers, where
|
||||||
|
our RAM is for scratch space, all that stuff. Even if we can't put all of them
|
||||||
|
to use at once, it's helpful to have an idea of what will be available in the
|
||||||
|
long run.</p>
|
||||||
|
<p>Tiled modes bring us two big new concepts that each have their own complexity:
|
||||||
|
backgrounds and objects. They share some concepts, but fundamentally the
|
||||||
|
background is for creating a very large static space that you can scroll around
|
||||||
|
the view within, and the objects are about having a few moving bits that appear
|
||||||
|
over the background. Careful use of backgrounds and objects is key to having the
|
||||||
|
best looking GBA game, so we won't even be able to cover it all in a single
|
||||||
|
chapter.</p>
|
||||||
|
<p>And, of course, since most games are pretty boring if they're totally static
|
||||||
|
we'll touch on the kinds of RNG implementations you might want to have on a GBA.
|
||||||
|
Most general purpose RNGs that you find are rather big compared to the amount of
|
||||||
|
memory we want to give them, and they often use a lot of <code>u64</code> operations, so
|
||||||
|
they end up much slower on a 32-bit machine like the GBA (you can lower 64-bit
|
||||||
|
ops to combinations of 32-bit ops, but that's quite a bit more work). We'll
|
||||||
|
cover a few RNG options that size down the RNG to a good size and a good speed
|
||||||
|
without trading away too much in terms of quality.</p>
|
||||||
|
<p>To top it all off, we'll make a simple "memory game" sort of thing. There's some
|
||||||
|
face down cards in a grid, you pick one to check, then you pick the other to
|
||||||
|
check, and then if they match the pair disappears.</p>
|
||||||
|
<a class="header" href="#gba-memory" id="gba-memory"><h1>GBA Memory</h1></a>
|
||||||
|
<p>The <a href="http://problemkaputt.de/gbatek.htm#gbamemorymap">GBA Memory Map</a> has
|
||||||
|
several memory portions to it, each with their own little differences. Most of
|
||||||
|
the memory has pre-determined use according to the hardware, but there is also
|
||||||
|
space for games to use as a scratch pad in whatever way the game sees fit.</p>
|
||||||
|
<p>The memory ranges listed here are <em>inclusive</em>, so they end with a lot of <code>F</code>s
|
||||||
|
and <code>E</code>s.</p>
|
||||||
|
<p>We've talked about volatile memory before, but just as a reminder I'll say that
|
||||||
|
all of the memory we'll talk about here should be accessed with volatile with
|
||||||
|
two exceptions:</p>
|
||||||
|
<ol>
|
||||||
|
<li>Work RAM (both internal and external) can be used normally, and if the
|
||||||
|
compiler is able to totally elide any reads and writes that's okay.</li>
|
||||||
|
<li>However, if you set aside any space in Work RAM where an interrupt will
|
||||||
|
communicate with the main program then that specific location will have to
|
||||||
|
keep using volatile access, since the compiler never knows when an interrupt
|
||||||
|
will actually happen.</li>
|
||||||
|
</ol>
|
||||||
|
<a class="header" href="#bios--system-rom" id="bios--system-rom"><h2>BIOS / System ROM</h2></a>
|
||||||
|
<ul>
|
||||||
|
<li><code>0x0</code> to <code>0x3FFF</code> (16k)</li>
|
||||||
|
</ul>
|
||||||
|
<p>This is special memory for the BIOS. It is "read-only", but even then it's only
|
||||||
|
accessible when the program counter is pointing into the BIOS region. At all
|
||||||
|
other times you get a <a href="http://problemkaputt.de/gbatek.htm#gbaunpredictablethings">garbage
|
||||||
|
value</a> back when you
|
||||||
|
try to read out of the BIOS.</p>
|
||||||
|
<a class="header" href="#external-work-ram--ewram" id="external-work-ram--ewram"><h2>External Work RAM / EWRAM</h2></a>
|
||||||
|
<ul>
|
||||||
|
<li><code>0x2000000</code> to <code>0x203FFFF</code> (256k)</li>
|
||||||
|
</ul>
|
||||||
|
<p>This is a big pile of space, the use of which is up to each game. However, the
|
||||||
|
external work ram has only a 16-bit bus (if you read/write a 32-bit value it
|
||||||
|
silently breaks it up into two 16-bit operations) and also 2 wait cycles (extra
|
||||||
|
CPU cycles that you have to expend <em>per 16-bit bus use</em>).</p>
|
||||||
|
<p>In other words, we should think of EWRAM as if it was "heap space" in a normal
|
||||||
|
application. You can take the time to go store something within EWRAM, or to
|
||||||
|
load it out of EWRAM, but you should always avoid doing a critical computation
|
||||||
|
on values in EWRAM. It's a bit of a pain, but if you wanna be speedy and you
|
||||||
|
have more than just one manipulation that you want to do, you should pull the
|
||||||
|
value into a local variable, do all of your manipulations, and then push it back
|
||||||
|
out at the end.</p>
|
||||||
|
<a class="header" href="#internal-work-ram--iwram" id="internal-work-ram--iwram"><h2>Internal Work RAM / IWRAM</h2></a>
|
||||||
|
<ul>
|
||||||
|
<li><code>0x3000000</code> to <code>0x3007FFF</code> (32k)</li>
|
||||||
|
</ul>
|
||||||
|
<p>This is a smaller pile of space, but it has a 32-bit bus and no wait.</p>
|
||||||
|
<p>By default, <code>0x3007F00</code> to <code>0x3007FFF</code> is reserved for interrupt and BIOS use.
|
||||||
|
The rest of it is totally up to you. The user's stack space starts at
|
||||||
|
<code>0x3007F00</code> and proceeds <em>down</em> from there. In other words, if you start your
|
||||||
|
own customized IWRAM use at <code>0x3000000</code> and go up, eventually you might hit your
|
||||||
|
stack. However, most reasonable uses won't actually cause a memory collision.
|
||||||
|
It's just something you should know about if you're using a ton of stack or
|
||||||
|
IWRAM and then get problems.</p>
|
||||||
|
<a class="header" href="#io-registers-1" id="io-registers-1"><h2>IO Registers</h2></a>
|
||||||
|
<ul>
|
||||||
|
<li><code>0x4000000</code> to <code>0x40003FE</code></li>
|
||||||
|
</ul>
|
||||||
|
<p>We've touched upon a few of these so far, and we'll get to more later. At the
|
||||||
|
moment it is enough to say that, as you might have guessed, all of them live in
|
||||||
|
this region. Each individual register is a <code>u16</code> or <code>u32</code> and they control all
|
||||||
|
sorts of things. We'll actually be talking about some more of them in this very
|
||||||
|
chapter, because that's how we'll control some of the background and object
|
||||||
|
stuff.</p>
|
||||||
|
<a class="header" href="#palette-ram--palram" id="palette-ram--palram"><h2>Palette RAM / PALRAM</h2></a>
|
||||||
|
<ul>
|
||||||
|
<li><code>0x5000000</code> to <code>0x50003FF</code> (1k)</li>
|
||||||
|
</ul>
|
||||||
|
<p>Palette RAM has a 16-bit bus, which isn't really a problem because it
|
||||||
|
conceptually just holds <code>u16</code> values. There's no automatic wait state, but if
|
||||||
|
you try to access the same location that the display controller is accessing you
|
||||||
|
get bumped by 1 cycle. Since the display controller can use the palette ram any
|
||||||
|
number of times per scanline it's basically impossible to predict if you'll have
|
||||||
|
to do a wait or not during VDraw. During VBlank you won't have any wait of
|
||||||
|
course.</p>
|
||||||
|
<p>PALRAM is among the memory where there's weirdness if you try to write just one
|
||||||
|
byte: if you try to write just 1 byte, it writes that byte into <em>both</em> parts of
|
||||||
|
the larger 16-bit location. This doesn't really affect us much with PALRAM,
|
||||||
|
because palette values are all supposed to be <code>u16</code> anyway.</p>
|
||||||
|
<p>The palette memory actually contains not one, but <em>two</em> sets of palettes. First
|
||||||
|
there's 256 entries for the background palette data (starting at <code>0x5000000</code>),
|
||||||
|
and then there's 256 entries for object palette data (starting at <code>0x5000200</code>).</p>
|
||||||
|
<p>The GBA also has two modes for palette access: 8-bits-per-pixel (8bpp) and
|
||||||
|
4-bits-per-pixel (4bpp).</p>
|
||||||
|
<ul>
|
||||||
|
<li>In 8bpp mode an (8-bit) palette index value within a background or sprite
|
||||||
|
simply indexes directly into the 256 slots for that type of thing.</li>
|
||||||
|
<li>In 4bpp mode a (4-bit) palette index value within a background or sprite
|
||||||
|
specifies an index within a particular "palbank" (16 palette entries each),
|
||||||
|
and then a <em>separate</em> setting outside of the graphical data determines which
|
||||||
|
palbank is to be used for that background or object (the screen entry data for
|
||||||
|
backgrounds, and the object attributes for objects).</li>
|
||||||
|
</ul>
|
||||||
|
<a class="header" href="#video-ram--vram" id="video-ram--vram"><h2>Video RAM / VRAM</h2></a>
|
||||||
|
<ul>
|
||||||
|
<li><code>0x6000000</code> to <code>0x6017FFF</code> (96k)</li>
|
||||||
|
</ul>
|
||||||
|
<p>We've used this before! VRAM has a 16-bit bus and no wait. However, the same as
|
||||||
|
with PALRAM, the "you might have to wait if the display controller is looking at
|
||||||
|
it" rule applies here.</p>
|
||||||
|
<p>Unfortunately there's not much more exact detail that can be given about VRAM.
|
||||||
|
The use of the memory depends on the video mode that you're using.</p>
|
||||||
|
<p>One general detail of note is that you can't write individual bytes to any part
|
||||||
|
of VRAM. Depending on mode and location, you'll either get your bytes doubled
|
||||||
|
into both the upper and lower parts of the 16-bit location targeted, or you
|
||||||
|
won't even affect the memory. This usually isn't a big deal, except in two
|
||||||
|
situations:</p>
|
||||||
|
<ul>
|
||||||
|
<li>In Mode 4, if you want to change just 1 pixel, you'll have to be very careful
|
||||||
|
to read the old <code>u16</code>, overwrite just the byte you wanted to change, and then
|
||||||
|
write that back.</li>
|
||||||
|
<li>In any display mode, avoid using <code>memcopy</code> to place things into VRAM.
|
||||||
|
It's written to be byte oriented, and only does 32-bit transfers under select
|
||||||
|
conditions. The rest of the time it'll copy one byte at a time and you'll get
|
||||||
|
either garbage or nothing at all.</li>
|
||||||
|
</ul>
|
||||||
|
<a class="header" href="#object-attribute-memory--oam" id="object-attribute-memory--oam"><h2>Object Attribute Memory / OAM</h2></a>
|
||||||
|
<ul>
|
||||||
|
<li><code>0x7000000</code> to <code>0x70003FF</code> (1k)</li>
|
||||||
|
</ul>
|
||||||
|
<p>The Object Attribute Memory has a 32-bit bus and no default wait, but suffers
|
||||||
|
from the "you might have to wait if the display controller is looking at it"
|
||||||
|
rule. You cannot write individual bytes to OAM at all, but that's not really a
|
||||||
|
problem because all the fields of the data types within OAM are either <code>i16</code> or
|
||||||
|
<code>u16</code> anyway.</p>
|
||||||
|
<p>Object attribute memory is the wildest yet: it conceptually contains two types
|
||||||
|
of things, but they're <em>interlaced</em> with each other all the way through.</p>
|
||||||
|
<p>Now, <a href="http://problemkaputt.de/gbatek.htm#lcdobjoamattributes">GBATEK</a> and
|
||||||
|
<a href="https://www.cs.rit.edu/%7Etjh8300/CowBite/CowBiteSpec.htm#OAM%20(sprites)">CowByte</a>
|
||||||
|
doesn't quite give names to the two data types, though
|
||||||
|
<a href="https://www.coranac.com/tonc/text/regobj.htm#sec-oam">TONC</a> calls them
|
||||||
|
<code>OBJ_ATTR</code> and <code>OBJ_AFFINE</code>. We'll give them Rust names of course. In Rust terms
|
||||||
|
their layout would look like this:</p>
|
||||||
|
<pre><pre class="playpen"><code class="language-rust">
|
||||||
|
# #![allow(unused_variables)]
|
||||||
|
#fn main() {
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct ObjectAttribute {
|
||||||
|
attr0: u16,
|
||||||
|
attr1: u16,
|
||||||
|
attr2: u16,
|
||||||
|
filler: i16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct AffineMatrix {
|
||||||
|
filler0: [u16; 3],
|
||||||
|
pa: i16,
|
||||||
|
filler1: [u16; 3],
|
||||||
|
pb: i16,
|
||||||
|
filler2: [u16; 3],
|
||||||
|
pc: i16,
|
||||||
|
filler3: [u16; 3],
|
||||||
|
pd: i16,
|
||||||
|
}
|
||||||
|
#}</code></pre></pre>
|
||||||
|
<p>(Note: the <code>#[repr(C)]</code> part just means that Rust must lay out the data exactly
|
||||||
|
in the order we specify, which otherwise it is not required to do).</p>
|
||||||
|
<p>So, we've got 1024 bytes in OAM and each <code>ObjectAttribute</code> value is 8 bytes, so
|
||||||
|
naturally we can support up to 128 objects.</p>
|
||||||
|
<p><em>At the same time</em>, we've got 1024 bytes in OAM and each <code>AffineMatrix</code> is 32
|
||||||
|
bytes, so we can have 32 of them.</p>
|
||||||
|
<p>But, as I said, these things are all <em>interlaced</em> with each other. See how
|
||||||
|
there's "filler" fields in each struct? If we imagine the OAM as being just an
|
||||||
|
array of one type or the other, indexes 0/1/2/3 of the <code>ObjectAttribute</code> array
|
||||||
|
would line up with index 0 of the <code>AffineMatrix</code> array. It's kinda weird, but
|
||||||
|
that's just how it works. When we setup functions to read and write these values
|
||||||
|
we'll have to be careful with how we do it. We probably <em>won't</em> want to use
|
||||||
|
those representations above, at least not with the <code>AffineMatrix</code> type, because
|
||||||
|
they're quite wasteful if you want to store just object attributes or just
|
||||||
|
affine matrices.</p>
|
||||||
|
<a class="header" href="#game-pak-rom--flash-rom" id="game-pak-rom--flash-rom"><h2>Game Pak ROM / Flash ROM</h2></a>
|
||||||
|
<ul>
|
||||||
|
<li><code>0x8000000</code> to <code>0x9FFFFFF</code> (wait 0)</li>
|
||||||
|
<li><code>0xA000000</code> to <code>0xBFFFFFF</code> (wait 1)</li>
|
||||||
|
<li><code>0xC000000</code> to <code>0xDFFFFFF</code> (wait 2)</li>
|
||||||
|
<li>Max of 32Mb</li>
|
||||||
|
</ul>
|
||||||
|
<p>These portions of the memory are less fixed, because they depend on the precise
|
||||||
|
details of the game pak you've inserted into the GBA. In general, they connect
|
||||||
|
to the game pak ROM and/or Flash memory, using a 16-bit bus. The ROM is
|
||||||
|
read-only, but the Flash memory (if any) allows writes.</p>
|
||||||
|
<p>The game pak ROM is listed as being in three sections, but it's actually the
|
||||||
|
same memory being effectively mirrored into three different locations. The
|
||||||
|
mirror that you choose to access the game pak through affects which wait state
|
||||||
|
setting it uses (configured via IO register of course). Unfortunately, the
|
||||||
|
details come down more to the game pak hardware that you load your game onto
|
||||||
|
than anything else, so there's not much I can say right here. We'll eventually
|
||||||
|
talk about it more later,</p>
|
||||||
|
<p>One thing of note is the way that the 16-bit bus affects us: the instructions to
|
||||||
|
execute are coming through the same bus as the rest of the game data, so we want
|
||||||
|
them to be as compact as possible. The ARM chip in the GBA supports two
|
||||||
|
different instruction sets, "thumb" and "non-thumb". The thumb mode instructions
|
||||||
|
are 16-bit, so they can each be loaded one at a time, and the non-thumb
|
||||||
|
instructions are 32-bit, so we're at a penalty if we execute them directly out
|
||||||
|
of the game pak. However, some things will demand that we use non-thumb code, so
|
||||||
|
we'll have to deal with that eventually. It's possible to switch between modes,
|
||||||
|
but it's a pain to keep track of what mode you're in because there's not
|
||||||
|
currently support for it in Rust itself (perhaps some day). So we'll stick with
|
||||||
|
thumb code as much as we possibly can, that's why our target profile for our
|
||||||
|
builds starts with <code>thumbv4</code>.</p>
|
||||||
|
<a class="header" href="#game-pak-sram" id="game-pak-sram"><h2>Game Pak SRAM</h2></a>
|
||||||
|
<ul>
|
||||||
|
<li><code>0xE000000</code> to <code>0xE00FFFF</code> (64k)</li>
|
||||||
|
</ul>
|
||||||
|
<p>The game pak SRAM has an 8-bit bus. Why did pokemon always take so long to save?
|
||||||
|
This is why. It also has some amount of wait, but as with the ROM, the details
|
||||||
|
depend on your game pak hardware (and also as with ROM, you can adjust the
|
||||||
|
settings with an IO register, should you need to).</p>
|
||||||
|
<p>One thing to note about the SRAM is that the GBA has a Direct Memory Access
|
||||||
|
(DMA) feature that can be used for bulk memory movements in some cases, but the
|
||||||
|
DMA <em>cannot</em> access the SRAM region. You really are stuck reading and writing
|
||||||
|
one byte at a time when you're using the SRAM.</p>
|
||||||
|
<a class="header" href="#tiled-backgrounds" id="tiled-backgrounds"><h1>Tiled Backgrounds</h1></a>
|
||||||
|
<p>TODO</p>
|
||||||
|
<a class="header" href="#object-basics" id="object-basics"><h1>Object Basics</h1></a>
|
||||||
|
<p>TODO</p>
|
||||||
|
<a class="header" href="#gba-rng" id="gba-rng"><h1>GBA RNG</h1></a>
|
||||||
|
<p>TODO</p>
|
||||||
|
<a class="header" href="#memory_game" id="memory_game"><h1>memory_game</h1></a>
|
||||||
|
<p>TODO</p>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,7 +1,6 @@
|
||||||
#![feature(start)]
|
#![feature(start)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
#[cfg(not(test))]
|
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||||
loop {}
|
loop {}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
#![feature(start)]
|
#![feature(start)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
#[cfg(not(test))]
|
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||||
loop {}
|
loop {}
|
||||||
|
@ -10,7 +9,7 @@ fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||||
#[start]
|
#[start]
|
||||||
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||||
unsafe {
|
unsafe {
|
||||||
DISPCNT.write_volatile(MODE3 | BG2);
|
DISPCNT.write(MODE3 | BG2);
|
||||||
mode3_pixel(120, 80, rgb16(31, 0, 0));
|
mode3_pixel(120, 80, rgb16(31, 0, 0));
|
||||||
mode3_pixel(136, 80, rgb16(0, 31, 0));
|
mode3_pixel(136, 80, rgb16(0, 31, 0));
|
||||||
mode3_pixel(120, 96, rgb16(0, 0, 31));
|
mode3_pixel(120, 96, rgb16(0, 0, 31));
|
||||||
|
@ -18,7 +17,22 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const DISPCNT: *mut u16 = 0x04000000 as *mut u16;
|
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct VolatilePtr<T>(pub *mut T);
|
||||||
|
impl<T> VolatilePtr<T> {
|
||||||
|
pub unsafe fn read(&self) -> T {
|
||||||
|
core::ptr::read_volatile(self.0)
|
||||||
|
}
|
||||||
|
pub unsafe fn write(&self, data: T) {
|
||||||
|
core::ptr::write_volatile(self.0, data);
|
||||||
|
}
|
||||||
|
pub unsafe fn offset(self, count: isize) -> Self {
|
||||||
|
VolatilePtr(self.0.wrapping_offset(count))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const DISPCNT: VolatilePtr<u16> = VolatilePtr(0x04000000 as *mut u16);
|
||||||
pub const MODE3: u16 = 3;
|
pub const MODE3: u16 = 3;
|
||||||
pub const BG2: u16 = 0b100_0000_0000;
|
pub const BG2: u16 = 0b100_0000_0000;
|
||||||
|
|
||||||
|
@ -30,5 +44,5 @@ pub const fn rgb16(red: u16, green: u16, blue: u16) -> u16 {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn mode3_pixel(col: isize, row: isize, color: u16) {
|
pub unsafe fn mode3_pixel(col: isize, row: isize, color: u16) {
|
||||||
(VRAM as *mut u16).offset(col + row * SCREEN_WIDTH).write_volatile(color);
|
VolatilePtr(VRAM as *mut u16).offset(col + row * SCREEN_WIDTH).write(color);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
#![feature(start)]
|
#![feature(start)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
#[cfg(not(test))]
|
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||||
loop {}
|
loop {}
|
||||||
|
@ -10,7 +9,7 @@ fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||||
#[start]
|
#[start]
|
||||||
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||||
unsafe {
|
unsafe {
|
||||||
DISPCNT.write_volatile(MODE3 | BG2);
|
DISPCNT.write(MODE3 | BG2);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut px = SCREEN_WIDTH / 2;
|
let mut px = SCREEN_WIDTH / 2;
|
||||||
|
@ -19,7 +18,7 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// read the input for this frame
|
// read the input for this frame
|
||||||
let this_frame_keys = read_key_input();
|
let this_frame_keys = key_input();
|
||||||
|
|
||||||
// adjust game state and wait for vblank
|
// adjust game state and wait for vblank
|
||||||
px += 2 * this_frame_keys.column_direction() as isize;
|
px += 2 * this_frame_keys.column_direction() as isize;
|
||||||
|
@ -53,7 +52,22 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const DISPCNT: *mut u16 = 0x04000000 as *mut u16;
|
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct VolatilePtr<T>(pub *mut T);
|
||||||
|
impl<T> VolatilePtr<T> {
|
||||||
|
pub unsafe fn read(&self) -> T {
|
||||||
|
core::ptr::read_volatile(self.0)
|
||||||
|
}
|
||||||
|
pub unsafe fn write(&self, data: T) {
|
||||||
|
core::ptr::write_volatile(self.0, data);
|
||||||
|
}
|
||||||
|
pub unsafe fn offset(self, count: isize) -> Self {
|
||||||
|
VolatilePtr(self.0.wrapping_offset(count))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const DISPCNT: VolatilePtr<u16> = VolatilePtr(0x04000000 as *mut u16);
|
||||||
pub const MODE3: u16 = 3;
|
pub const MODE3: u16 = 3;
|
||||||
pub const BG2: u16 = 0b100_0000_0000;
|
pub const BG2: u16 = 0b100_0000_0000;
|
||||||
|
|
||||||
|
@ -68,24 +82,24 @@ pub const fn rgb16(red: u16, green: u16, blue: u16) -> u16 {
|
||||||
pub unsafe fn mode3_clear_screen(color: u16) {
|
pub unsafe fn mode3_clear_screen(color: u16) {
|
||||||
let color = color as u32;
|
let color = color as u32;
|
||||||
let bulk_color = color << 16 | color;
|
let bulk_color = color << 16 | color;
|
||||||
let mut ptr = VRAM as *mut u32;
|
let mut ptr = VolatilePtr(VRAM as *mut u32);
|
||||||
for _ in 0..SCREEN_HEIGHT {
|
for _ in 0..SCREEN_HEIGHT {
|
||||||
for _ in 0..(SCREEN_WIDTH / 2) {
|
for _ in 0..(SCREEN_WIDTH / 2) {
|
||||||
ptr.write_volatile(bulk_color);
|
ptr.write(bulk_color);
|
||||||
ptr = ptr.offset(1);
|
ptr = ptr.offset(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn mode3_draw_pixel(col: isize, row: isize, color: u16) {
|
pub unsafe fn mode3_draw_pixel(col: isize, row: isize, color: u16) {
|
||||||
(VRAM as *mut u16).offset(col + row * SCREEN_WIDTH).write_volatile(color);
|
VolatilePtr(VRAM as *mut u16).offset(col + row * SCREEN_WIDTH).write(color);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn mode3_read_pixel(col: isize, row: isize) -> u16 {
|
pub unsafe fn mode3_read_pixel(col: isize, row: isize) -> u16 {
|
||||||
(VRAM as *mut u16).offset(col + row * SCREEN_WIDTH).read_volatile()
|
VolatilePtr(VRAM as *mut u16).offset(col + row * SCREEN_WIDTH).read()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const KEYINPUT: *mut u16 = 0x400_0130 as *mut u16;
|
pub const KEYINPUT: VolatilePtr<u16> = VolatilePtr(0x400_0130 as *mut u16);
|
||||||
|
|
||||||
/// A newtype over the key input state of the GBA.
|
/// A newtype over the key input state of the GBA.
|
||||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
||||||
|
@ -101,8 +115,8 @@ pub enum TriBool {
|
||||||
Plus = 1,
|
Plus = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_key_input() -> KeyInputSetting {
|
pub fn key_input() -> KeyInputSetting {
|
||||||
unsafe { KeyInputSetting(KEYINPUT.read_volatile() ^ 0b1111_1111_1111_1111) }
|
unsafe { KeyInputSetting(KEYINPUT.read() ^ 0b0000_0011_1111_1111) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const KEY_A: u16 = 1 << 0;
|
pub const KEY_A: u16 = 1 << 0;
|
||||||
|
@ -146,16 +160,16 @@ impl KeyInputSetting {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const VCOUNT: *mut u16 = 0x0400_0006 as *mut u16;
|
pub const VCOUNT: VolatilePtr<u16> = VolatilePtr(0x0400_0006 as *mut u16);
|
||||||
|
|
||||||
pub fn read_vcount() -> u16 {
|
pub fn vcount() -> u16 {
|
||||||
unsafe { VCOUNT.read_volatile() }
|
unsafe { VCOUNT.read() }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wait_until_vblank() {
|
pub fn wait_until_vblank() {
|
||||||
while read_vcount() < SCREEN_HEIGHT as u16 {}
|
while vcount() < SCREEN_HEIGHT as u16 {}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wait_until_vdraw() {
|
pub fn wait_until_vdraw() {
|
||||||
while read_vcount() >= SCREEN_HEIGHT as u16 {}
|
while vcount() >= SCREEN_HEIGHT as u16 {}
|
||||||
}
|
}
|
||||||
|
|
197
examples/memory_game.rs
Normal file
197
examples/memory_game.rs
Normal file
|
@ -0,0 +1,197 @@
|
||||||
|
#![feature(start)]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[start]
|
||||||
|
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||||
|
unsafe {
|
||||||
|
DISPCNT.write(MODE3 | BG2);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut px = SCREEN_WIDTH / 2;
|
||||||
|
let mut py = SCREEN_HEIGHT / 2;
|
||||||
|
let mut color = rgb16(31, 0, 0);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
// read the input for this frame
|
||||||
|
let this_frame_keys = key_input();
|
||||||
|
|
||||||
|
// adjust game state and wait for vblank
|
||||||
|
px += 2 * this_frame_keys.column_direction() as isize;
|
||||||
|
py += 2 * this_frame_keys.row_direction() as isize;
|
||||||
|
wait_until_vblank();
|
||||||
|
|
||||||
|
// draw the new game and wait until the next frame starts.
|
||||||
|
unsafe {
|
||||||
|
if px < 0 || py < 0 || px == SCREEN_WIDTH || py == SCREEN_HEIGHT {
|
||||||
|
// out of bounds, reset the screen and position.
|
||||||
|
mode3_clear_screen(0);
|
||||||
|
color = color.rotate_left(5);
|
||||||
|
px = SCREEN_WIDTH / 2;
|
||||||
|
py = SCREEN_HEIGHT / 2;
|
||||||
|
} else {
|
||||||
|
let color_here = mode3_read_pixel(px, py);
|
||||||
|
if color_here != 0 {
|
||||||
|
// crashed into our own line, reset the screen
|
||||||
|
mode3_clear_screen(0);
|
||||||
|
color = color.rotate_left(5);
|
||||||
|
} else {
|
||||||
|
// draw the new part of the line
|
||||||
|
mode3_draw_pixel(px, py, color);
|
||||||
|
mode3_draw_pixel(px, py + 1, color);
|
||||||
|
mode3_draw_pixel(px + 1, py, color);
|
||||||
|
mode3_draw_pixel(px + 1, py + 1, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wait_until_vdraw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct VolatilePtr<T>(pub *mut T);
|
||||||
|
impl<T> VolatilePtr<T> {
|
||||||
|
pub unsafe fn read(&self) -> T {
|
||||||
|
core::ptr::read_volatile(self.0)
|
||||||
|
}
|
||||||
|
pub unsafe fn write(&self, data: T) {
|
||||||
|
core::ptr::write_volatile(self.0, data);
|
||||||
|
}
|
||||||
|
pub unsafe fn offset(self, count: isize) -> Self {
|
||||||
|
VolatilePtr(self.0.wrapping_offset(count))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const DISPCNT: VolatilePtr<u16> = VolatilePtr(0x04000000 as *mut u16);
|
||||||
|
pub const MODE3: u16 = 3;
|
||||||
|
pub const BG2: u16 = 0b100_0000_0000;
|
||||||
|
|
||||||
|
pub const VRAM: usize = 0x06000000;
|
||||||
|
pub const SCREEN_WIDTH: isize = 240;
|
||||||
|
pub const SCREEN_HEIGHT: isize = 160;
|
||||||
|
|
||||||
|
pub const fn rgb16(red: u16, green: u16, blue: u16) -> u16 {
|
||||||
|
blue << 10 | green << 5 | red
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mode3_clear_screen(color: u16) {
|
||||||
|
let color = color as u32;
|
||||||
|
let bulk_color = color << 16 | color;
|
||||||
|
let mut ptr = VolatilePtr(VRAM as *mut u32);
|
||||||
|
for _ in 0..SCREEN_HEIGHT {
|
||||||
|
for _ in 0..(SCREEN_WIDTH / 2) {
|
||||||
|
ptr.write(bulk_color);
|
||||||
|
ptr = ptr.offset(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mode3_draw_pixel(col: isize, row: isize, color: u16) {
|
||||||
|
VolatilePtr(VRAM as *mut u16).offset(col + row * SCREEN_WIDTH).write(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mode3_read_pixel(col: isize, row: isize) -> u16 {
|
||||||
|
VolatilePtr(VRAM as *mut u16).offset(col + row * SCREEN_WIDTH).read()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const KEYINPUT: VolatilePtr<u16> = VolatilePtr(0x400_0130 as *mut u16);
|
||||||
|
|
||||||
|
/// A newtype over the key input state of the GBA.
|
||||||
|
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct KeyInputSetting(u16);
|
||||||
|
|
||||||
|
/// A "tribool" value helps us interpret the arrow pad.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
#[repr(i32)]
|
||||||
|
pub enum TriBool {
|
||||||
|
Minus = -1,
|
||||||
|
Neutral = 0,
|
||||||
|
Plus = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn key_input() -> KeyInputSetting {
|
||||||
|
unsafe { KeyInputSetting(KEYINPUT.read() ^ 0b0000_0011_1111_1111) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const KEY_A: u16 = 1 << 0;
|
||||||
|
pub const KEY_B: u16 = 1 << 1;
|
||||||
|
pub const KEY_SELECT: u16 = 1 << 2;
|
||||||
|
pub const KEY_START: u16 = 1 << 3;
|
||||||
|
pub const KEY_RIGHT: u16 = 1 << 4;
|
||||||
|
pub const KEY_LEFT: u16 = 1 << 5;
|
||||||
|
pub const KEY_UP: u16 = 1 << 6;
|
||||||
|
pub const KEY_DOWN: u16 = 1 << 7;
|
||||||
|
pub const KEY_R: u16 = 1 << 8;
|
||||||
|
pub const KEY_L: u16 = 1 << 9;
|
||||||
|
|
||||||
|
impl KeyInputSetting {
|
||||||
|
pub fn contains(&self, key: u16) -> bool {
|
||||||
|
(self.0 & key) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn difference(&self, other: KeyInputSetting) -> KeyInputSetting {
|
||||||
|
KeyInputSetting(self.0 ^ other.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn column_direction(&self) -> TriBool {
|
||||||
|
if self.contains(KEY_RIGHT) {
|
||||||
|
TriBool::Plus
|
||||||
|
} else if self.contains(KEY_LEFT) {
|
||||||
|
TriBool::Minus
|
||||||
|
} else {
|
||||||
|
TriBool::Neutral
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn row_direction(&self) -> TriBool {
|
||||||
|
if self.contains(KEY_DOWN) {
|
||||||
|
TriBool::Plus
|
||||||
|
} else if self.contains(KEY_UP) {
|
||||||
|
TriBool::Minus
|
||||||
|
} else {
|
||||||
|
TriBool::Neutral
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const VCOUNT: VolatilePtr<u16> = VolatilePtr(0x0400_0006 as *mut u16);
|
||||||
|
|
||||||
|
pub fn vcount() -> u16 {
|
||||||
|
unsafe { VCOUNT.read() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wait_until_vblank() {
|
||||||
|
while vcount() < SCREEN_HEIGHT as u16 {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wait_until_vdraw() {
|
||||||
|
while vcount() >= SCREEN_HEIGHT as u16 {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[align(4)]
|
||||||
|
pub struct ObjectAttributes {
|
||||||
|
attr0: u16,
|
||||||
|
attr1: u16,
|
||||||
|
attr2: u16,
|
||||||
|
filler: i16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[align(4)]
|
||||||
|
pub struct ObjectAffine {
|
||||||
|
filler0: [u16; 3],
|
||||||
|
pa: i16,
|
||||||
|
filler1: [u16; 3],
|
||||||
|
pb: i16,
|
||||||
|
filler2: [u16; 3],
|
||||||
|
pc: i16,
|
||||||
|
filler3: [u16; 3],
|
||||||
|
pd: i16,
|
||||||
|
}
|
|
@ -8,7 +8,7 @@ MEMORY {
|
||||||
|
|
||||||
SECTIONS {
|
SECTIONS {
|
||||||
.text : {
|
.text : {
|
||||||
KEEP(crt0.o(.text));
|
KEEP(target/crt0.o(.text));
|
||||||
*(.text .text.*);
|
*(.text .text.*);
|
||||||
. = ALIGN(4);
|
. = ALIGN(4);
|
||||||
} >rom = 0xff
|
} >rom = 0xff
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
//! Things that I wish were in core, but aren't.
|
//! Things that I wish were in core, but aren't.
|
||||||
|
|
||||||
/// A simple wrapper to any `*mut T` so that the basic "read" and "write"
|
/// A simple wrapper for any `*mut T` to adjust the basic operations.
|
||||||
/// operations are volatile.
|
|
||||||
///
|
///
|
||||||
/// Accessing the GBA's IO registers and video ram and specific other places on
|
/// Read and Write are made to be volatile. Offset is made to be
|
||||||
/// **must** be done with volatile operations. Having this wrapper makes that
|
/// wrapping_offset. This makes it much easier to correctly work with IO
|
||||||
/// more clear for all the global const values into IO registers.
|
/// Registers and all display related memory on the GBA.
|
||||||
|
///
|
||||||
|
/// As a bonus, use of this type is mostly `cargo test` safe. Reads will return
|
||||||
|
/// a `zeroed()` value instead, and writes will do nothing.
|
||||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct VolatilePtr<T>(pub *mut T);
|
pub struct VolatilePtr<T>(pub *mut T);
|
||||||
|
@ -25,7 +27,14 @@ impl<T> VolatilePtr<T> {
|
||||||
/// This method adds absolutely no additional safety, so all safety concerns
|
/// This method adds absolutely no additional safety, so all safety concerns
|
||||||
/// for a normal raw pointer volatile read apply.
|
/// for a normal raw pointer volatile read apply.
|
||||||
pub unsafe fn read(&self) -> T {
|
pub unsafe fn read(&self) -> T {
|
||||||
core::ptr::read_volatile(self.0)
|
#[cfg(not(test))]
|
||||||
|
{
|
||||||
|
core::ptr::read_volatile(self.0)
|
||||||
|
}
|
||||||
|
#[cfg(test)]
|
||||||
|
{
|
||||||
|
core::mem::zeroed::<T>()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Performs a volatile write.
|
/// Performs a volatile write.
|
||||||
|
@ -35,16 +44,23 @@ impl<T> VolatilePtr<T> {
|
||||||
/// This method adds absolutely no additional safety, so all safety concerns
|
/// This method adds absolutely no additional safety, so all safety concerns
|
||||||
/// for a normal raw pointer volatile write apply.
|
/// for a normal raw pointer volatile write apply.
|
||||||
pub unsafe fn write(&self, data: T) {
|
pub unsafe fn write(&self, data: T) {
|
||||||
core::ptr::write_volatile(self.0, data);
|
#[cfg(not(test))]
|
||||||
|
{
|
||||||
|
core::ptr::write_volatile(self.0, data);
|
||||||
|
}
|
||||||
|
#[cfg(test)]
|
||||||
|
{
|
||||||
|
drop(data)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Offsets this address by the amount given.
|
/// Performs a wrapping_offset by the number of slots given to a new position.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// This is a standard offset, so all safety concerns of a normal raw pointer
|
/// This is a wrapping_offset, so all safety concerns of a normal raw pointer
|
||||||
/// offset apply.
|
/// wrapping_offset apply.
|
||||||
pub unsafe fn offset(self, count: isize) -> Self {
|
pub unsafe fn offset(self, count: isize) -> Self {
|
||||||
VolatilePtr(self.0.offset(count))
|
VolatilePtr(self.0.wrapping_offset(count))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
// TODO(lokathor): IO Register newtypes.
|
// TODO(lokathor): IO Register newtypes.
|
||||||
|
|
||||||
use gba_proc_macro::register_bit;
|
use gba_proc_macro::{newtype, register_bit};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
@ -24,10 +24,11 @@ use super::*;
|
||||||
/// * [gbatek entry](http://problemkaputt.de/gbatek.htm#lcdiodisplaycontrol)
|
/// * [gbatek entry](http://problemkaputt.de/gbatek.htm#lcdiodisplaycontrol)
|
||||||
pub const DISPCNT: VolatilePtr<u16> = VolatilePtr(0x4000000 as *mut u16);
|
pub const DISPCNT: VolatilePtr<u16> = VolatilePtr(0x4000000 as *mut u16);
|
||||||
|
|
||||||
/// A newtype over the various display control options that you have on a GBA.
|
newtype!(
|
||||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
DisplayControlSetting,
|
||||||
#[repr(transparent)]
|
u16,
|
||||||
pub struct DisplayControlSetting(u16);
|
"A newtype over the various display control options that you have on a GBA."
|
||||||
|
);
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
impl DisplayControlSetting {
|
impl DisplayControlSetting {
|
||||||
|
@ -56,19 +57,19 @@ impl DisplayControlSetting {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
register_bit!(CGB_MODE_BIT, u16, 0b1000, cgb_mode, read);
|
register_bit!(CGB_MODE_BIT, u16, 0b1000, cgb_mode);
|
||||||
register_bit!(PAGE_SELECT_BIT, u16, 0b1_0000, page1_enabled, read_write);
|
register_bit!(PAGE_SELECT_BIT, u16, 0b1_0000, page1_enabled);
|
||||||
register_bit!(HBLANK_INTERVAL_FREE_BIT, u16, 0b10_0000, hblank_interval_free, read_write);
|
register_bit!(HBLANK_INTERVAL_FREE_BIT, u16, 0b10_0000, hblank_interval_free);
|
||||||
register_bit!(OBJECT_MEMORY_1D, u16, 0b100_0000, object_memory_1d, read_write);
|
register_bit!(OBJECT_MEMORY_1D, u16, 0b100_0000, object_memory_1d);
|
||||||
register_bit!(FORCE_BLANK_BIT, u16, 0b1000_0000, force_blank, read_write);
|
register_bit!(FORCE_BLANK_BIT, u16, 0b1000_0000, force_blank);
|
||||||
register_bit!(DISPLAY_BG0_BIT, u16, 0b1_0000_0000, display_bg0, read_write);
|
register_bit!(DISPLAY_BG0_BIT, u16, 0b1_0000_0000, display_bg0);
|
||||||
register_bit!(DISPLAY_BG1_BIT, u16, 0b10_0000_0000, display_bg1, read_write);
|
register_bit!(DISPLAY_BG1_BIT, u16, 0b10_0000_0000, display_bg1);
|
||||||
register_bit!(DISPLAY_BG2_BIT, u16, 0b100_0000_0000, display_bg2, read_write);
|
register_bit!(DISPLAY_BG2_BIT, u16, 0b100_0000_0000, display_bg2);
|
||||||
register_bit!(DISPLAY_BG3_BIT, u16, 0b1000_0000_0000, display_bg3, read_write);
|
register_bit!(DISPLAY_BG3_BIT, u16, 0b1000_0000_0000, display_bg3);
|
||||||
register_bit!(DISPLAY_OBJECT_BIT, u16, 0b1_0000_0000_0000, display_object, read_write);
|
register_bit!(DISPLAY_OBJECT_BIT, u16, 0b1_0000_0000_0000, display_object);
|
||||||
register_bit!(DISPLAY_WINDOW0_BIT, u16, 0b10_0000_0000_0000, display_window0, read_write);
|
register_bit!(DISPLAY_WINDOW0_BIT, u16, 0b10_0000_0000_0000, display_window0);
|
||||||
register_bit!(DISPLAY_WINDOW1_BIT, u16, 0b100_0000_0000_0000, display_window1, read_write);
|
register_bit!(DISPLAY_WINDOW1_BIT, u16, 0b100_0000_0000_0000, display_window1);
|
||||||
register_bit!(OBJECT_WINDOW_BIT, u16, 0b1000_0000_0000_0000, display_object_window, read_write);
|
register_bit!(OBJECT_WINDOW_BIT, u16, 0b1000_0000_0000_0000, display_object_window);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The six display modes available on the GBA.
|
/// The six display modes available on the GBA.
|
||||||
|
@ -110,6 +111,23 @@ pub const DISPSTAT: VolatilePtr<u16> = VolatilePtr(0x4000004 as *mut u16);
|
||||||
/// Vertical Counter (LY)
|
/// Vertical Counter (LY)
|
||||||
pub const VCOUNT: VolatilePtr<u16> = VolatilePtr(0x4000006 as *mut u16);
|
pub const VCOUNT: VolatilePtr<u16> = VolatilePtr(0x4000006 as *mut u16);
|
||||||
|
|
||||||
|
/// Obtains the current VCount value.
|
||||||
|
pub fn vcount() -> u16 {
|
||||||
|
unsafe { VCOUNT.read() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Performs a busy loop until VBlank starts.
|
||||||
|
pub fn wait_until_vblank() {
|
||||||
|
// TODO: make this the better version with BIOS and interrupts and such.
|
||||||
|
while vcount() < SCREEN_HEIGHT as u16 {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Performs a busy loop until VDraw starts.
|
||||||
|
pub fn wait_until_vdraw() {
|
||||||
|
// TODO: make this the better version with BIOS and interrupts and such.
|
||||||
|
while vcount() >= SCREEN_HEIGHT as u16 {}
|
||||||
|
}
|
||||||
|
|
||||||
/// BG0 Control
|
/// BG0 Control
|
||||||
pub const BG0CNT: VolatilePtr<u16> = VolatilePtr(0x4000008 as *mut u16);
|
pub const BG0CNT: VolatilePtr<u16> = VolatilePtr(0x4000008 as *mut u16);
|
||||||
|
|
||||||
|
@ -383,11 +401,6 @@ pub const SIODATA8: VolatilePtr<u16> = VolatilePtr(0x400012A as *mut u16);
|
||||||
/// Key Status
|
/// Key Status
|
||||||
pub const KEYINPUT: VolatilePtr<u16> = VolatilePtr(0x4000130 as *mut u16);
|
pub const KEYINPUT: VolatilePtr<u16> = VolatilePtr(0x4000130 as *mut u16);
|
||||||
|
|
||||||
/// A newtype over the key input state of the GBA.
|
|
||||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
|
||||||
#[repr(transparent)]
|
|
||||||
pub struct KeyInputSetting(u16);
|
|
||||||
|
|
||||||
/// A "tribool" value helps us interpret the arrow pad.
|
/// A "tribool" value helps us interpret the arrow pad.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
|
@ -398,18 +411,20 @@ pub enum TriBool {
|
||||||
Plus = 1,
|
Plus = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newtype!(KeyInputSetting, u16, "A newtype over the key input state of the GBA");
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
impl KeyInputSetting {
|
impl KeyInputSetting {
|
||||||
register_bit!(A_BIT, u16, 1 << 0, a_pressed, read_write);
|
register_bit!(A_BIT, u16, 1 << 0, a_pressed);
|
||||||
register_bit!(B_BIT, u16, 1 << 1, b_pressed, read_write);
|
register_bit!(B_BIT, u16, 1 << 1, b_pressed);
|
||||||
register_bit!(SELECT_BIT, u16, 1 << 2, select_pressed, read_write);
|
register_bit!(SELECT_BIT, u16, 1 << 2, select_pressed);
|
||||||
register_bit!(START_BIT, u16, 1 << 3, start_pressed, read_write);
|
register_bit!(START_BIT, u16, 1 << 3, start_pressed);
|
||||||
register_bit!(RIGHT_BIT, u16, 1 << 4, right_pressed, read_write);
|
register_bit!(RIGHT_BIT, u16, 1 << 4, right_pressed);
|
||||||
register_bit!(LEFT_BIT, u16, 1 << 5, left_pressed, read_write);
|
register_bit!(LEFT_BIT, u16, 1 << 5, left_pressed);
|
||||||
register_bit!(UP_BIT, u16, 1 << 6, up_pressed, read_write);
|
register_bit!(UP_BIT, u16, 1 << 6, up_pressed);
|
||||||
register_bit!(DOWN_BIT, u16, 1 << 7, down_pressed, read_write);
|
register_bit!(DOWN_BIT, u16, 1 << 7, down_pressed);
|
||||||
register_bit!(R_BIT, u16, 1 << 8, r_pressed, read_write);
|
register_bit!(R_BIT, u16, 1 << 8, r_pressed);
|
||||||
register_bit!(L_BIT, u16, 1 << 9, l_pressed, read_write);
|
register_bit!(L_BIT, u16, 1 << 9, l_pressed);
|
||||||
|
|
||||||
/// Takes the difference between these keys and another set of keys.
|
/// Takes the difference between these keys and another set of keys.
|
||||||
pub fn difference(&self, other: KeyInputSetting) -> KeyInputSetting {
|
pub fn difference(&self, other: KeyInputSetting) -> KeyInputSetting {
|
||||||
|
@ -442,8 +457,11 @@ impl KeyInputSetting {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the current state of the keys
|
/// Gets the current state of the keys
|
||||||
pub fn read_key_input() -> KeyInputSetting {
|
pub fn key_input() -> KeyInputSetting {
|
||||||
unsafe { KeyInputSetting(KEYINPUT.read() ^ 0b1111_1111_1111_1111) }
|
// Note(Lokathor): The 10 used bits are "low when pressed" style, but the 6
|
||||||
|
// unused bits are always low, so we XOR with this mask to get a result where
|
||||||
|
// the only active bits are currently pressed keys.
|
||||||
|
unsafe { KeyInputSetting(KEYINPUT.read() ^ 0b0000_0011_1111_1111) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Key Interrupt Control
|
/// Key Interrupt Control
|
||||||
|
|
66
src/lib.rs
66
src/lib.rs
|
@ -1,4 +1,5 @@
|
||||||
#![no_std]
|
#![cfg_attr(not(test), no_std)]
|
||||||
|
#![cfg_attr(not(test), feature(asm))]
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
//! This crate helps you write GBA ROMs.
|
//! This crate helps you write GBA ROMs.
|
||||||
|
@ -13,6 +14,14 @@
|
||||||
//!
|
//!
|
||||||
//! **Do not** use this crate in programs that aren't running on the GBA. If you
|
//! **Do not** use this crate in programs that aren't running on the GBA. If you
|
||||||
//! do, it's a giant bag of Undefined Behavior.
|
//! do, it's a giant bag of Undefined Behavior.
|
||||||
|
//!
|
||||||
|
//! # TESTING POLICY
|
||||||
|
//!
|
||||||
|
//! It is the intent of the crate authors that as much of the crate as possible
|
||||||
|
//! be written so that you can use `cargo test` for at least some parts of your
|
||||||
|
//! code without everything exploding instantly. To that end, where possible we
|
||||||
|
//! attempt to use `cfg` flags to make things safe for `cargo test`. Hopefully
|
||||||
|
//! we got it all.
|
||||||
|
|
||||||
pub mod core_extras;
|
pub mod core_extras;
|
||||||
pub(crate) use crate::core_extras::*;
|
pub(crate) use crate::core_extras::*;
|
||||||
|
@ -20,8 +29,63 @@ pub(crate) use crate::core_extras::*;
|
||||||
pub mod io_registers;
|
pub mod io_registers;
|
||||||
|
|
||||||
pub mod video_ram;
|
pub mod video_ram;
|
||||||
|
pub(crate) use crate::video_ram::*;
|
||||||
|
|
||||||
/// Combines the Red, Blue, and Green provided into a single color value.
|
/// Combines the Red, Blue, and Green provided into a single color value.
|
||||||
pub const fn rgb16(red: u16, green: u16, blue: u16) -> u16 {
|
pub const fn rgb16(red: u16, green: u16, blue: u16) -> u16 {
|
||||||
blue << 10 | green << 5 | red
|
blue << 10 | green << 5 | red
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// BIOS Call: Div (GBA SWI 0x06).
|
||||||
|
///
|
||||||
|
/// Gives just the DIV output of `numerator / denominator`.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// If `denominator` is 0.
|
||||||
|
#[inline]
|
||||||
|
pub fn div(numerator: i32, denominator: i32) -> i32 {
|
||||||
|
div_modulus(numerator, denominator).0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// BIOS Call: Div (GBA SWI 0x06).
|
||||||
|
///
|
||||||
|
/// Gives just the MOD output of `numerator / denominator`.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// If `denominator` is 0.
|
||||||
|
#[inline]
|
||||||
|
pub fn modulus(numerator: i32, denominator: i32) -> i32 {
|
||||||
|
div_modulus(numerator, denominator).1
|
||||||
|
}
|
||||||
|
|
||||||
|
/// BIOS Call: Div (GBA SWI 0x06).
|
||||||
|
///
|
||||||
|
/// Gives both the DIV and MOD output of `numerator / denominator`.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// If `denominator` is 0.
|
||||||
|
#[inline]
|
||||||
|
pub fn div_modulus(numerator: i32, denominator: i32) -> (i32, i32) {
|
||||||
|
assert!(denominator != 0);
|
||||||
|
#[cfg(not(test))]
|
||||||
|
{
|
||||||
|
let div_out: i32;
|
||||||
|
let mod_out: i32;
|
||||||
|
unsafe {
|
||||||
|
asm!(/* assembly template */ "swi 0x06"
|
||||||
|
:/* output operands */ "={r0}"(div_out), "={r1}"(mod_out)
|
||||||
|
:/* input operands */ "{r0}"(numerator), "{r1}"(denominator)
|
||||||
|
:/* clobbers */ "r3"
|
||||||
|
:/* options */
|
||||||
|
);
|
||||||
|
}
|
||||||
|
(div_out, mod_out)
|
||||||
|
}
|
||||||
|
#[cfg(test)]
|
||||||
|
{
|
||||||
|
(numerator / denominator, numerator % denominator)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
//! they won't bother to check that you've set the video mode they're designed
|
//! they won't bother to check that you've set the video mode they're designed
|
||||||
//! for.
|
//! for.
|
||||||
|
|
||||||
|
pub use super::*;
|
||||||
|
|
||||||
/// The physical width in pixels of the GBA screen.
|
/// The physical width in pixels of the GBA screen.
|
||||||
pub const SCREEN_WIDTH: isize = 240;
|
pub const SCREEN_WIDTH: isize = 240;
|
||||||
|
|
||||||
|
@ -27,10 +29,14 @@ pub const SCREEN_HEIGHT: isize = 160;
|
||||||
pub const VRAM_BASE_ADDRESS: usize = 0x0600_0000;
|
pub const VRAM_BASE_ADDRESS: usize = 0x0600_0000;
|
||||||
|
|
||||||
/// Draws a pixel to the screen while in Display Mode 3, with bounds checks.
|
/// Draws a pixel to the screen while in Display Mode 3, with bounds checks.
|
||||||
pub fn mode3_pixel(col: isize, row: isize, color: u16) {
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// If `col` or `row` are out of bounds this will panic.
|
||||||
|
pub fn mode3_draw_pixel(col: isize, row: isize, color: u16) {
|
||||||
assert!(col >= 0 && col < SCREEN_WIDTH);
|
assert!(col >= 0 && col < SCREEN_WIDTH);
|
||||||
assert!(row >= 0 && row < SCREEN_HEIGHT);
|
assert!(row >= 0 && row < SCREEN_HEIGHT);
|
||||||
unsafe { mode3_pixel_unchecked(col, row, color) }
|
unsafe { mode3_draw_pixel_unchecked(col, row, color) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draws a pixel to the screen while in Display Mode 3.
|
/// Draws a pixel to the screen while in Display Mode 3.
|
||||||
|
@ -44,6 +50,32 @@ pub fn mode3_pixel(col: isize, row: isize, color: u16) {
|
||||||
///
|
///
|
||||||
/// * `col` must be in `0..SCREEN_WIDTH`
|
/// * `col` must be in `0..SCREEN_WIDTH`
|
||||||
/// * `row` must be in `0..SCREEN_HEIGHT`
|
/// * `row` must be in `0..SCREEN_HEIGHT`
|
||||||
pub unsafe fn mode3_pixel_unchecked(col: isize, row: isize, color: u16) {
|
pub unsafe fn mode3_draw_pixel_unchecked(col: isize, row: isize, color: u16) {
|
||||||
core::ptr::write_volatile((VRAM_BASE_ADDRESS as *mut u16).offset(col + row * SCREEN_WIDTH), color);
|
VolatilePtr(VRAM_BASE_ADDRESS as *mut u16).offset(col + row * SCREEN_WIDTH).write(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads the given pixel of video memory according to Mode 3 placement.
|
||||||
|
///
|
||||||
|
/// # Failure
|
||||||
|
///
|
||||||
|
/// If the location is out of bounds you get `None`.
|
||||||
|
pub fn mode3_read_pixel(col: isize, row: isize) -> Option<u16> {
|
||||||
|
if col >= 0 && col < SCREEN_WIDTH && row >= 0 && row < SCREEN_HEIGHT {
|
||||||
|
unsafe { Some(VolatilePtr(VRAM_BASE_ADDRESS as *mut u16).offset(col + row * SCREEN_WIDTH).read()) }
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clears the entire screen to the color specified.
|
||||||
|
pub unsafe fn mode3_clear_screen(color: u16) {
|
||||||
|
let color = color as u32;
|
||||||
|
let bulk_color = color << 16 | color;
|
||||||
|
let mut ptr = VolatilePtr(VRAM_BASE_ADDRESS as *mut u32);
|
||||||
|
for _ in 0..SCREEN_HEIGHT {
|
||||||
|
for _ in 0..(SCREEN_WIDTH / 2) {
|
||||||
|
ptr.write(bulk_color);
|
||||||
|
ptr = ptr.offset(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue