mirror of
https://github.com/italicsjenga/gba.git
synced 2024-12-23 19:01:30 +11:00
commit
5b327dce6f
|
@ -14,7 +14,8 @@ before_script:
|
|||
|
||||
script:
|
||||
- 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
|
||||
|
||||
deploy:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "gba"
|
||||
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"]
|
||||
repository = "https://github.com/rust-console/gba"
|
||||
readme = "README.md"
|
||||
|
@ -9,8 +9,10 @@ keywords = ["gba"]
|
|||
edition = "2018"
|
||||
license = "Apache-2.0"
|
||||
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
gba-proc-macro = "0.1.1"
|
||||
gba-proc-macro = "0.2.1"
|
||||
|
||||
[profile.release]
|
||||
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)
|
||||
[![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
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
[book]
|
||||
title = "Rust GBA Tutorials"
|
||||
title = "Rust GBA Guide"
|
||||
authors = ["Lokathor"]
|
||||
#description = "Rust GBA Tutorials."
|
||||
|
||||
[build]
|
||||
build-dir = "../docs"
|
||||
|
|
|
@ -2,14 +2,21 @@
|
|||
# Rust GBA Tutorials
|
||||
|
||||
* [Introduction](introduction.md)
|
||||
* [Ch 0: Development Setup](ch0/index.md)
|
||||
* [Ch 1: Hello GBA](ch1/index.md)
|
||||
* [hello1](ch1/hello1.md)
|
||||
* [IO Registers](ch1/io_registers.md)
|
||||
* [The Display Control Register](ch1/the_display_control_register.md)
|
||||
* [Video Memory Intro](ch1/video_memory_intro.md)
|
||||
* [hello2](ch1/hello2.md)
|
||||
* [Ch 2: User Input](ch2/index.md)
|
||||
* [The Key Input Register](ch2/the_key_input_register.md)
|
||||
* [The VCount Register](ch2/the_vcount_register.md)
|
||||
* [light_cycle](ch2/light_cycle.md)
|
||||
* [Ch 0: Development Setup](ch00/index.md)
|
||||
* [Ch 1: Hello GBA](ch01/index.md)
|
||||
* [hello1](ch01/hello1.md)
|
||||
* [Volatile](ch01/volatile.md)
|
||||
* [IO Registers](ch01/io_registers.md)
|
||||
* [The Display Control Register](ch01/the_display_control_register.md)
|
||||
* [Video Memory Intro](ch01/video_memory_intro.md)
|
||||
* [hello2](ch01/hello2.md)
|
||||
* [Ch 2: User Input](ch02/index.md)
|
||||
* [The Key Input Register](ch02/the_key_input_register.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
|
||||
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
|
||||
wizard that arranged for all of this to be able to happen and laid out the
|
||||
details of the plan to the rest of the world.
|
||||
Once again, extra special thanks to **Ketsuban**, who first dove into how to
|
||||
make this all work with rust and then shared it with the world.
|
||||
|
||||
## Per System Setup
|
||||
|
||||
Obviously you need your computer to have a working rust installation. However,
|
||||
you'll also need to ensure that you're using a nightly toolchain. You can run
|
||||
`rustup default nightly` to set nightly as the system wide default toolchain, or
|
||||
you can use a [toolchain
|
||||
Obviously you need your computer to have a [working rust
|
||||
installation](https://rustup.rs/). However, you'll also need to ensure that
|
||||
you're using a nightly toolchain (we will need it for inline assembly, among
|
||||
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
|
||||
nightly just on a specific project, but either way we'll be assuming nightly
|
||||
from now on.
|
||||
nightly just on a specific project, but either way we'll be assuming the use of
|
||||
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
|
||||
got a graphical installer for Windows, and `pacman` support on Linux. We'll be
|
||||
using a few of their binutils for the `arm-none-eabi` target, and we'll also be
|
||||
using some of their tools that are specific to GBA development, so _even if_ you
|
||||
already have the right binutils for whatever reason, you'll still want devkitpro
|
||||
for the `gbafix` utility.
|
||||
Next, you need [devkitpro](https://devkitpro.org/wiki/Getting_Started). They've
|
||||
got a graphical installer for Windows that runs nicely, and I guess `pacman`
|
||||
support on Linux (I'm on Windows so I haven't tried the Linux install myself).
|
||||
We'll be using a few of their general binutils for the `arm-none-eabi` target,
|
||||
and we'll also be using some of their tools that are specific to GBA
|
||||
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
|
||||
`C:\devkitpro\tools\bin` to be [added to your
|
||||
PATH](https://stackoverflow.com/q/44272416/455232), depending on where you
|
||||
installed it to and such.
|
||||
* 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
|
||||
cargo will figure it all out for you.
|
||||
|
||||
## Per Project Setup
|
||||
|
||||
Now you'll need some particular files each time you want to start a new project.
|
||||
You can find them in the root of the [rust-console/gba
|
||||
repo](https://github.com/rust-console/gba).
|
||||
Once the system wide tools are ready, you'll need some particular files each
|
||||
time you want to start a new project. You can find them in the root of the
|
||||
[rust-console/gba repo](https://github.com/rust-console/gba).
|
||||
|
||||
* `thumbv4-none-agb.json` describes the overall GBA to cargo-xbuild so it knows
|
||||
what to do. This is actually a somewhat made up target name since there's no
|
||||
official target name. The GBA is essentially the same as a normal
|
||||
`thumbv4-none-eabi` device, but we give it the "agb" signifier so that later
|
||||
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).
|
||||
* `thumbv4-none-agb.json` describes the overall GBA to cargo-xbuild (and LLVM)
|
||||
so it knows what to do. Technically the GBA is `thumbv4-none-eabi`, but we
|
||||
change the `eabi` to `agb` so that we can distinguish it from other `eabi`
|
||||
devices when using `cfg` flags.
|
||||
* `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
|
||||
`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
|
||||
that the GBA has about our program.
|
||||
* `linker.ld` tells the linker all the critical info about the layout
|
||||
expectations that the GBA has about our program, and that it should also
|
||||
include the `crt0.o` file with our compiled rust code.
|
||||
|
||||
## 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
|
||||
expect `cargo` to accept.
|
||||
* 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
|
||||
with `cargo test`, but that builds for your local machine, so anything
|
||||
specific to the GBA (such as reading and writing registers) won't be
|
||||
testable that way. If you want to isolate and try out some piece code
|
||||
running on the GBA you'll unfortunately have to make a demo for it in your
|
||||
`examples/` directory and then run the demo in an emulator and see if it
|
||||
does what you expect.
|
||||
which the GBA doesn't have. If you want you can still run some of your
|
||||
project's tests with `cargo test --lib` or similar, but that builds for your
|
||||
local machine, so anything specific to the GBA (such as reading and writing
|
||||
registers) won't be testable that way. If you want to isolate and try out
|
||||
some piece code running on the GBA you'll unfortunately have to make a demo
|
||||
for it in your `examples/` directory and then run the demo in an emulator
|
||||
and see if it does what you expect.
|
||||
* The file extension is important. `cargo xbuild` takes it as a flag to
|
||||
compile dependencies with the same sysroot, so you can include crates
|
||||
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!
|
||||
|
||||
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
|
||||
|
||||
Our first example will be a totally minimal, full magic number crazy town.
|
||||
Ready? Here goes:
|
||||
|
||||
`hello1.rs`
|
||||
|
@ -8,7 +9,6 @@ Ready? Here goes:
|
|||
#![feature(start)]
|
||||
#![no_std]
|
||||
|
||||
#[cfg(not(test))]
|
||||
#[panic_handler]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
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
|
||||
0), and give it a run. You should see a red, green, and blue dot close-ish to
|
||||
the middle of the screen. If you don't, something already went wrong. Double
|
||||
check things, phone a friend, write your senators, try asking Ketsuban on the
|
||||
[Rust Community Discord](https://discordapp.com/invite/aVESxV8), until you're
|
||||
able to get your three dots going.
|
||||
Throw that into your project skeleton, build the program (as described back in
|
||||
Chapter 0), and give it a run in your emulator. You should see a red, green, and
|
||||
blue dot close-ish to the middle of the screen. If you don't, something already
|
||||
went wrong. Double check things, phone a friend, write your senators, try asking
|
||||
Ketsuban on the [Rust Community Discord](https://discordapp.com/invite/aVESxV8),
|
||||
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
|
||||
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
|
||||
#![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.
|
||||
|
||||
```rust
|
||||
#[cfg(not(test))]
|
||||
#[panic_handler]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
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
|
||||
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
|
||||
#[start]
|
||||
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.
|
||||
|
||||
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,
|
||||
each in their own location... so that second part makes sense at least.
|
||||
|
||||
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.
|
||||
other magic to three magic locations in the Video RAM. Somehow that shows three
|
||||
dots. Gotta read on to find out why!
|
|
@ -8,7 +8,6 @@ Okay so let's have a look again:
|
|||
#![feature(start)]
|
||||
#![no_std]
|
||||
|
||||
#[cfg(not(test))]
|
||||
#[panic_handler]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
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.
|
||||
|
||||
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
|
||||
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 BG2: u16 = 0b100_0000_0000;
|
||||
```
|
||||
|
@ -43,9 +43,12 @@ pub const VRAM: usize = 0x06000000;
|
|||
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
|
||||
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.
|
||||
|
@ -62,7 +65,7 @@ usually helps you think about it a lot better.
|
|||
|
||||
```rust
|
||||
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)]
|
||||
#![no_std]
|
||||
|
||||
#[cfg(not(test))]
|
||||
#[panic_handler]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
loop {}
|
||||
|
@ -83,7 +85,7 @@ fn panic(_info: &core::panic::PanicInfo) -> ! {
|
|||
#[start]
|
||||
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||
unsafe {
|
||||
DISPCNT.write_volatile(MODE3 | BG2);
|
||||
DISPCNT.write(MODE3 | BG2);
|
||||
mode3_pixel(120, 80, rgb16(31, 0, 0));
|
||||
mode3_pixel(136, 80, rgb16(0, 31, 0));
|
||||
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 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) {
|
||||
(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.
|
||||
|
||||
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
|
||||
purposes it's often best to do it yourself at least once.
|
||||
other elements all labeled and sorted out for you (not identically, but
|
||||
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
|
||||
`0b100_0000_0011`. So, that's setting Mode 3 with background 2 enabled and
|
||||
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
|
||||
binary to make sense of it.
|
||||
|
||||
* 0x001F: 0b11111
|
||||
* 0x03E0: 0b11111_00000
|
||||
* 0x7C00: 0b11111_00000_00000
|
||||
* 0x001F: 0b0_00000_00000_11111
|
||||
* 0x03E0: 0b0_00000_11111_00000
|
||||
* 0x7C00: 0b0_11111_00000_00000
|
||||
|
||||
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) {
|
||||
let color = color as u32;
|
||||
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_WIDTH / 2) {
|
||||
ptr.write_volatile(bulk_color);
|
||||
ptr.write(bulk_color);
|
||||
ptr = ptr.offset(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
(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]
|
||||
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||
unsafe {
|
||||
DISPCNT.write_volatile(MODE3 | BG2);
|
||||
DISPCNT.write(MODE3 | BG2);
|
||||
}
|
||||
|
||||
let mut px = SCREEN_WIDTH / 2;
|
||||
|
@ -55,7 +55,7 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
|||
|
||||
loop {
|
||||
// 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
|
||||
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
|
||||
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
|
||||
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.
|
||||
|
||||
```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.
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
||||
#[repr(transparent)]
|
||||
pub struct KeyInputSetting(u16);
|
||||
|
||||
pub fn read_key_input() -> KeyInputSetting {
|
||||
unsafe { KeyInputSetting(KEYINPUT.read_volatile()) }
|
||||
pub fn key_input() -> KeyInputSetting {
|
||||
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
|
||||
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
|
||||
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
|
||||
pub fn read_key_input() -> KeyInputSetting {
|
||||
unsafe { KeyInputSetting(KEYINPUT.read_volatile() ^ 0b1111_1111_1111_1111) }
|
||||
pub fn key_input() -> KeyInputSetting {
|
||||
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:
|
||||
|
||||
```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 {
|
||||
unsafe { VCOUNT.read_volatile() }
|
||||
pub fn vcount() -> u16 {
|
||||
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 fn wait_until_vblank() {
|
||||
while read_vcount() < SCREEN_HEIGHT as u16 {}
|
||||
while vcount() < SCREEN_HEIGHT as u16 {}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
|
@ -14,11 +44,12 @@ channel.
|
|||
* `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
|
||||
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
|
||||
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
|
||||
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
|
||||
cargo xbuild --examples --target thumbv4-none-agb.json
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<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 name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
@ -72,7 +72,7 @@
|
|||
</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="../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>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
@ -102,7 +102,7 @@
|
|||
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">Rust GBA Tutorials</h1>
|
||||
<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">
|
||||
|
@ -140,49 +140,53 @@
|
|||
<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
|
||||
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
|
||||
wizard that arranged for all of this to be able to happen and laid out the
|
||||
details of the plan to the rest of the world.</p>
|
||||
<p>Once again, extra special thanks to <strong>Ketsuban</strong>, who first dove into how to
|
||||
make this all work with rust and then shared it with the world.</p>
|
||||
<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,
|
||||
you'll also need to ensure that you're using a nightly toolchain. 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
|
||||
<p>Obviously you need your computer to have a <a href="https://rustup.rs/">working rust
|
||||
installation</a>. However, you'll also need to ensure that
|
||||
you're using a nightly toolchain (we will need it for inline assembly, among
|
||||
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
|
||||
nightly just on a specific project, but either way we'll be assuming nightly
|
||||
from now on.</p>
|
||||
<p>Next you need <a href="https://devkitpro.org/wiki/Getting_Started">devkitpro</a>. They've
|
||||
got a graphical installer for Windows, and <code>pacman</code> support on Linux. We'll be
|
||||
using a few of their 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>
|
||||
nightly just on a specific project, but either way we'll be assuming the use of
|
||||
nightly from now on. You'll also need the <code>rust-src</code> component so that
|
||||
<code>cargo-xbuild</code> will be able to compile the core crate for us in a bit, so run
|
||||
<code>rustup component add rust-src</code>.</p>
|
||||
<p>Next, you need <a href="https://devkitpro.org/wiki/Getting_Started">devkitpro</a>. They've
|
||||
got a graphical installer for Windows that runs nicely, and I guess <code>pacman</code>
|
||||
support on Linux (I'm on Windows so I haven't tried the Linux install myself).
|
||||
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>
|
||||
<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
|
||||
PATH</a>, depending on where you
|
||||
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
|
||||
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>
|
||||
<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>
|
||||
<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.
|
||||
You can find them in the root of the <a href="https://github.com/rust-console/gba">rust-console/gba
|
||||
repo</a>.</p>
|
||||
<p>Once the system wide tools are ready, you'll need some particular files each
|
||||
time you want to start a new project. You can find them in the root of the
|
||||
<a href="https://github.com/rust-console/gba">rust-console/gba repo</a>.</p>
|
||||
<ul>
|
||||
<li><code>thumbv4-none-agb.json</code> describes the overall GBA to cargo-xbuild so it knows
|
||||
what to do. This is actually a somewhat made up target name since there's no
|
||||
official target name. The GBA is essentially the same as a normal
|
||||
<code>thumbv4-none-eabi</code> device, but we give it the "agb" signifier so that later
|
||||
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>thumbv4-none-agb.json</code> describes the overall GBA to cargo-xbuild (and LLVM)
|
||||
so it knows what to do. Technically the GBA is <code>thumbv4-none-eabi</code>, but we
|
||||
change the <code>eabi</code> to <code>agb</code> so that we can distinguish it from other <code>eabi</code>
|
||||
devices when using <code>cfg</code> flags.</li>
|
||||
<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
|
||||
<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
|
||||
that the GBA has about our program.</li>
|
||||
<li><code>linker.ld</code> tells the linker all the critical info about the layout
|
||||
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>
|
||||
<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
|
||||
|
@ -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
|
||||
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>,
|
||||
which the GBA doesn't have. You can still run some of your project's tests
|
||||
with <code>cargo test</code>, but that builds for your local machine, so anything
|
||||
specific to the GBA (such as reading and writing registers) won't be
|
||||
testable that way. If you want to isolate and try out some piece code
|
||||
running on the GBA you'll unfortunately have to make a demo for it in your
|
||||
<code>examples/</code> directory and then run the demo in an emulator and see if it
|
||||
does what you expect.</li>
|
||||
which the GBA doesn't have. If you want you can still run some of your
|
||||
project's tests with <code>cargo test --lib</code> or similar, but that builds for your
|
||||
local machine, so anything specific to the GBA (such as reading and writing
|
||||
registers) won't be testable that way. If you want to isolate and try out
|
||||
some piece code running on the GBA you'll unfortunately have to make a demo
|
||||
for it in your <code>examples/</code> directory and then run the demo in an emulator
|
||||
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
|
||||
compile dependencies with the same sysroot, so you can include crates
|
||||
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>
|
||||
</ul>
|
||||
<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>
|
||||
|
||||
|
@ -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>
|
||||
</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>
|
||||
</a>
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<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 name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
@ -72,7 +72,7 @@
|
|||
</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="../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>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
@ -102,7 +102,7 @@
|
|||
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">Rust GBA Tutorials</h1>
|
||||
<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">
|
||||
|
@ -137,12 +137,12 @@
|
|||
<div id="content" class="content">
|
||||
<main>
|
||||
<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>
|
||||
<pre><pre class="playpen"><code class="language-rust">#![feature(start)]
|
||||
#![no_std]
|
||||
|
||||
#[cfg(not(test))]
|
||||
#[panic_handler]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
loop {}
|
||||
|
@ -159,15 +159,16 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
|||
}
|
||||
}
|
||||
</code></pre></pre>
|
||||
<p>Throw that into your project, build the program (as described back in Chapter
|
||||
0), and give it a run. You should see a red, green, and blue dot close-ish to
|
||||
the middle of the screen. If you don't, something already went wrong. Double
|
||||
check things, phone a friend, write your senators, try asking Ketsuban on the
|
||||
<a href="https://discordapp.com/invite/aVESxV8">Rust Community Discord</a>, 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>
|
||||
<p>Throw that into your project skeleton, build the program (as described back in
|
||||
Chapter 0), and give it a run in your emulator. You should see a red, green, and
|
||||
blue dot close-ish to the middle of the screen. If you don't, something already
|
||||
went wrong. Double check things, phone a friend, write your senators, try asking
|
||||
Ketsuban on the <a href="https://discordapp.com/invite/aVESxV8">Rust Community Discord</a>,
|
||||
until you're able to get your three dots going.</p>
|
||||
<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
|
||||
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">
|
||||
# #![allow(unused_variables)]
|
||||
#![feature(start)]
|
||||
|
@ -191,7 +192,6 @@ only life.</p>
|
|||
<pre><pre class="playpen"><code class="language-rust">
|
||||
# #![allow(unused_variables)]
|
||||
#fn main() {
|
||||
#[cfg(not(test))]
|
||||
#[panic_handler]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
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
|
||||
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>
|
||||
<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]
|
||||
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||
</code></pre></pre>
|
||||
|
@ -269,55 +266,21 @@ magic numbers mean or do.</p>
|
|||
<li><code>0x06000000</code> is the start of Video RAM.</li>
|
||||
</ul>
|
||||
<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,
|
||||
each in their own location... so that second part makes sense at least.</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>
|
||||
other magic to three magic locations in the Video RAM. Somehow that shows three
|
||||
dots. Gotta read on to find out why!</p>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- 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>
|
||||
</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>
|
||||
</a>
|
||||
|
||||
|
@ -329,13 +292,13 @@ safety concerns.</p>
|
|||
|
||||
<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>
|
||||
</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>
|
||||
</a>
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<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 name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
@ -72,7 +72,7 @@
|
|||
</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="../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>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
@ -102,7 +102,7 @@
|
|||
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">Rust GBA Tutorials</h1>
|
||||
<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">
|
||||
|
@ -142,7 +142,6 @@
|
|||
<pre><pre class="playpen"><code class="language-rust">#![feature(start)]
|
||||
#![no_std]
|
||||
|
||||
#[cfg(not(test))]
|
||||
#[panic_handler]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
loop {}
|
||||
|
@ -160,11 +159,12 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
|||
}
|
||||
</code></pre></pre>
|
||||
<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">
|
||||
# #![allow(unused_variables)]
|
||||
#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 BG2: u16 = 0b100_0000_0000;
|
||||
#}</code></pre></pre>
|
||||
|
@ -175,8 +175,11 @@ pub const BG2: u16 = 0b100_0000_0000;
|
|||
pub const VRAM: usize = 0x06000000;
|
||||
pub const SCREEN_WIDTH: isize = 240;
|
||||
#}</code></pre></pre>
|
||||
<p>And then we want a small helper function for putting together a color value.</p>
|
||||
<p>Happily, this one can even be declared as a const function. At the time of
|
||||
<p>Note that VRAM has to be interpreted in different ways depending on mode, so we
|
||||
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
|
||||
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>
|
||||
|
@ -194,7 +197,7 @@ usually helps you think about it a lot better.</p>
|
|||
# #![allow(unused_variables)]
|
||||
#fn main() {
|
||||
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>
|
||||
<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)]
|
||||
#![no_std]
|
||||
|
||||
#[cfg(not(test))]
|
||||
#[panic_handler]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
loop {}
|
||||
|
@ -211,7 +213,7 @@ fn panic(_info: &core::panic::PanicInfo) -> ! {
|
|||
#[start]
|
||||
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||
unsafe {
|
||||
DISPCNT.write_volatile(MODE3 | BG2);
|
||||
DISPCNT.write(MODE3 | BG2);
|
||||
mode3_pixel(120, 80, rgb16(31, 0, 0));
|
||||
mode3_pixel(136, 80, rgb16(0, 31, 0));
|
||||
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 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) {
|
||||
(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>
|
||||
<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
|
||||
other elements all labeled and sorted out for you. Still, for educational
|
||||
purposes it's often best to do it yourself at least once.</p>
|
||||
other elements all labeled and sorted out for you (not identically, but
|
||||
similarly). Still, for educational purposes it's often best to do it yourself at
|
||||
least once.</p>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- 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>
|
||||
</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>
|
||||
</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">
|
||||
|
||||
<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>
|
||||
</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>
|
||||
</a>
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<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 name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
@ -72,7 +72,7 @@
|
|||
</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="../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>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
@ -102,7 +102,7 @@
|
|||
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">Rust GBA Tutorials</h1>
|
||||
<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">
|
||||
|
@ -150,13 +150,13 @@ three dots to the screen.</p>
|
|||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- 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>
|
||||
</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>
|
||||
</a>
|
||||
|
||||
|
@ -168,13 +168,13 @@ three dots to the screen.</p>
|
|||
|
||||
<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>
|
||||
</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>
|
||||
</a>
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<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 name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
@ -72,7 +72,7 @@
|
|||
</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="../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>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
@ -102,7 +102,7 @@
|
|||
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">Rust GBA Tutorials</h1>
|
||||
<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">
|
||||
|
@ -176,13 +176,13 @@ array index is.</p>
|
|||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- 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>
|
||||
</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>
|
||||
</a>
|
||||
|
||||
|
@ -194,13 +194,13 @@ array index is.</p>
|
|||
|
||||
<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>
|
||||
</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>
|
||||
</a>
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<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 name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
@ -72,7 +72,7 @@
|
|||
</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="../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>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
@ -102,7 +102,7 @@
|
|||
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">Rust GBA Tutorials</h1>
|
||||
<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">
|
||||
|
@ -221,21 +221,19 @@ some nifty graphical effects.</p>
|
|||
binary</a>, and we get
|
||||
<code>0b100_0000_0011</code>. So, that's setting Mode 3 with background 2 enabled and
|
||||
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>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- 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>
|
||||
</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>
|
||||
</a>
|
||||
|
||||
|
@ -247,13 +245,13 @@ newtyping as we attempt a <code>hello2</code> program.</p>
|
|||
|
||||
<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>
|
||||
</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>
|
||||
</a>
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<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 name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
@ -72,7 +72,7 @@
|
|||
</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="../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>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
@ -102,7 +102,7 @@
|
|||
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">Rust GBA Tutorials</h1>
|
||||
<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">
|
||||
|
@ -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
|
||||
binary to make sense of it.</p>
|
||||
<ul>
|
||||
<li>0x001F: 0b11111</li>
|
||||
<li>0x03E0: 0b11111_00000</li>
|
||||
<li>0x7C00: 0b11111_00000_00000</li>
|
||||
<li>0x001F: 0b0_00000_00000_11111</li>
|
||||
<li>0x03E0: 0b0_00000_11111_00000</li>
|
||||
<li>0x7C00: 0b0_11111_00000_00000</li>
|
||||
</ul>
|
||||
<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">
|
||||
<!-- 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>
|
||||
</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>
|
||||
</a>
|
||||
|
||||
|
@ -257,13 +257,13 @@ binary to make sense of it.</p>
|
|||
|
||||
<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>
|
||||
</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>
|
||||
</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>
|
||||
<!-- Book generated using mdBook -->
|
||||
<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 name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
@ -72,7 +72,7 @@
|
|||
</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="../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>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
@ -102,7 +102,7 @@
|
|||
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">Rust GBA Tutorials</h1>
|
||||
<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">
|
||||
|
@ -160,13 +160,13 @@ organized" for the long term.</p>
|
|||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- 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>
|
||||
</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>
|
||||
</a>
|
||||
|
||||
|
@ -178,13 +178,13 @@ organized" for the long term.</p>
|
|||
|
||||
<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>
|
||||
</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>
|
||||
</a>
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<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 name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
@ -72,7 +72,7 @@
|
|||
</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="../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>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
@ -102,7 +102,7 @@
|
|||
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">Rust GBA Tutorials</h1>
|
||||
<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">
|
||||
|
@ -150,21 +150,21 @@ go off the screen or if they touch their own trail.</p>
|
|||
pub unsafe fn mode3_clear_screen(color: u16) {
|
||||
let color = color as u32;
|
||||
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_WIDTH / 2) {
|
||||
ptr.write_volatile(bulk_color);
|
||||
ptr.write(bulk_color);
|
||||
ptr = ptr.offset(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
(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>
|
||||
<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]
|
||||
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||
unsafe {
|
||||
DISPCNT.write_volatile(MODE3 | BG2);
|
||||
DISPCNT.write(MODE3 | BG2);
|
||||
}
|
||||
|
||||
let mut px = SCREEN_WIDTH / 2;
|
||||
|
@ -185,7 +185,7 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
|||
|
||||
loop {
|
||||
// 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
|
||||
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
|
||||
their own line. In this case, we reset the game without moving them to a new
|
||||
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
|
||||
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>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- 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>
|
||||
</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>
|
||||
</nav>
|
||||
|
@ -259,12 +275,16 @@ the next loop.</p>
|
|||
|
||||
<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>
|
||||
</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>
|
||||
|
||||
</div>
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<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 name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
@ -72,7 +72,7 @@
|
|||
</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="../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>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
@ -102,7 +102,7 @@
|
|||
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">Rust GBA Tutorials</h1>
|
||||
<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">
|
||||
|
@ -185,15 +185,15 @@ reading and writing the key bits.</p>
|
|||
<pre><pre class="playpen"><code class="language-rust">
|
||||
# #![allow(unused_variables)]
|
||||
#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.
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
||||
#[repr(transparent)]
|
||||
pub struct KeyInputSetting(u16);
|
||||
|
||||
pub fn read_key_input() -> KeyInputSetting {
|
||||
unsafe { KeyInputSetting(KEYINPUT.read_volatile()) }
|
||||
pub fn key_input() -> KeyInputSetting {
|
||||
unsafe { KeyInputSetting(KEYINPUT.read()) }
|
||||
}
|
||||
#}</code></pre></pre>
|
||||
<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
|
||||
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
|
||||
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">
|
||||
# #![allow(unused_variables)]
|
||||
#fn main() {
|
||||
pub fn read_key_input() -> KeyInputSetting {
|
||||
unsafe { KeyInputSetting(KEYINPUT.read_volatile() ^ 0b1111_1111_1111_1111) }
|
||||
pub fn key_input() -> KeyInputSetting {
|
||||
unsafe { KeyInputSetting(KEYINPUT.read_volatile() ^ 0b0000_0011_1111_1111) }
|
||||
}
|
||||
#}</code></pre></pre>
|
||||
<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">
|
||||
<!-- 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>
|
||||
</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>
|
||||
</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">
|
||||
|
||||
<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>
|
||||
</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>
|
||||
</a>
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<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 name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
@ -72,7 +72,7 @@
|
|||
</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="../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>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
@ -102,7 +102,7 @@
|
|||
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">Rust GBA Tutorials</h1>
|
||||
<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">
|
||||
|
@ -183,10 +183,10 @@ quickly during the blank period.</p>
|
|||
<pre><pre class="playpen"><code class="language-rust">
|
||||
# #![allow(unused_variables)]
|
||||
#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 {
|
||||
unsafe { VCOUNT.read_volatile() }
|
||||
pub fn vcount() -> u16 {
|
||||
unsafe { VCOUNT.read() }
|
||||
}
|
||||
#}</code></pre></pre>
|
||||
<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 fn wait_until_vblank() {
|
||||
while read_vcount() < SCREEN_HEIGHT as u16 {}
|
||||
while vcount() < SCREEN_HEIGHT as u16 {}
|
||||
}
|
||||
|
||||
pub fn wait_until_vdraw() {
|
||||
while read_vcount() >= SCREEN_HEIGHT as u16 {}
|
||||
while vcount() >= SCREEN_HEIGHT as u16 {}
|
||||
}
|
||||
#}</code></pre></pre>
|
||||
<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">
|
||||
<!-- 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>
|
||||
</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>
|
||||
</a>
|
||||
|
||||
|
@ -229,13 +229,13 @@ number we read out of memory.</p>
|
|||
|
||||
<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>
|
||||
</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>
|
||||
</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>
|
||||
<!-- Book generated using mdBook -->
|
||||
<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 name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
@ -72,7 +72,7 @@
|
|||
</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="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>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
@ -102,7 +102,7 @@
|
|||
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">Rust GBA Tutorials</h1>
|
||||
<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">
|
||||
|
@ -137,8 +137,31 @@
|
|||
<div id="content" class="content">
|
||||
<main>
|
||||
<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>It's very "work in progress". At the moment there's only one demo program.</p>
|
||||
<p>Here's a book that'll help you program in Rust on the Game Boy Advance (GBA).</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>
|
||||
<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>
|
||||
|
@ -148,9 +171,10 @@ channel.</p>
|
|||
<li><code>Lokathor</code> is the fool who decided to write a crate and book for it.</li>
|
||||
</ul>
|
||||
<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>
|
||||
<a class="header" href="#other-works" id="other-works"><h2>Other Works</h2></a>
|
||||
<p>If you want to read more about developing on the GBA there are some other good resources as well:</p>
|
||||
folks in the server as well (there's a few hundred folks).</p>
|
||||
<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>
|
||||
<ul>
|
||||
<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>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<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 name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
@ -72,7 +72,7 @@
|
|||
</script>
|
||||
|
||||
<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>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
@ -102,7 +102,7 @@
|
|||
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">Rust GBA Tutorials</h1>
|
||||
<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">
|
||||
|
@ -137,8 +137,31 @@
|
|||
<div id="content" class="content">
|
||||
<main>
|
||||
<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>It's very "work in progress". At the moment there's only one demo program.</p>
|
||||
<p>Here's a book that'll help you program in Rust on the Game Boy Advance (GBA).</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>
|
||||
<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>
|
||||
|
@ -148,9 +171,10 @@ channel.</p>
|
|||
<li><code>Lokathor</code> is the fool who decided to write a crate and book for it.</li>
|
||||
</ul>
|
||||
<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>
|
||||
<a class="header" href="#other-works" id="other-works"><h2>Other Works</h2></a>
|
||||
<p>If you want to read more about developing on the GBA there are some other good resources as well:</p>
|
||||
folks in the server as well (there's a few hundred folks).</p>
|
||||
<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>
|
||||
<ul>
|
||||
<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>
|
||||
|
@ -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>
|
||||
</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>
|
||||
</a>
|
||||
|
||||
|
|
561
docs/print.html
561
docs/print.html
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<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 name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
@ -72,7 +72,7 @@
|
|||
</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="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>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
@ -102,7 +102,7 @@
|
|||
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">Rust GBA Tutorials</h1>
|
||||
<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">
|
||||
|
@ -137,8 +137,31 @@
|
|||
<div id="content" class="content">
|
||||
<main>
|
||||
<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>It's very "work in progress". At the moment there's only one demo program.</p>
|
||||
<p>Here's a book that'll help you program in Rust on the Game Boy Advance (GBA).</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>
|
||||
<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>
|
||||
|
@ -148,9 +171,10 @@ channel.</p>
|
|||
<li><code>Lokathor</code> is the fool who decided to write a crate and book for it.</li>
|
||||
</ul>
|
||||
<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>
|
||||
<a class="header" href="#other-works" id="other-works"><h2>Other Works</h2></a>
|
||||
<p>If you want to read more about developing on the GBA there are some other good resources as well:</p>
|
||||
folks in the server as well (there's a few hundred folks).</p>
|
||||
<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>
|
||||
<ul>
|
||||
<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>
|
||||
|
@ -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
|
||||
setup the development environment. Perhaps unfortunately, there's enough detail
|
||||
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
|
||||
wizard that arranged for all of this to be able to happen and laid out the
|
||||
details of the plan to the rest of the world.</p>
|
||||
<p>Once again, extra special thanks to <strong>Ketsuban</strong>, who first dove into how to
|
||||
make this all work with rust and then shared it with the world.</p>
|
||||
<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,
|
||||
you'll also need to ensure that you're using a nightly toolchain. 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
|
||||
<p>Obviously you need your computer to have a <a href="https://rustup.rs/">working rust
|
||||
installation</a>. However, you'll also need to ensure that
|
||||
you're using a nightly toolchain (we will need it for inline assembly, among
|
||||
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
|
||||
nightly just on a specific project, but either way we'll be assuming nightly
|
||||
from now on.</p>
|
||||
<p>Next you need <a href="https://devkitpro.org/wiki/Getting_Started">devkitpro</a>. They've
|
||||
got a graphical installer for Windows, and <code>pacman</code> support on Linux. We'll be
|
||||
using a few of their 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>
|
||||
nightly just on a specific project, but either way we'll be assuming the use of
|
||||
nightly from now on. You'll also need the <code>rust-src</code> component so that
|
||||
<code>cargo-xbuild</code> will be able to compile the core crate for us in a bit, so run
|
||||
<code>rustup component add rust-src</code>.</p>
|
||||
<p>Next, you need <a href="https://devkitpro.org/wiki/Getting_Started">devkitpro</a>. They've
|
||||
got a graphical installer for Windows that runs nicely, and I guess <code>pacman</code>
|
||||
support on Linux (I'm on Windows so I haven't tried the Linux install myself).
|
||||
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>
|
||||
<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
|
||||
PATH</a>, depending on where you
|
||||
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
|
||||
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>
|
||||
<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>
|
||||
<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.
|
||||
You can find them in the root of the <a href="https://github.com/rust-console/gba">rust-console/gba
|
||||
repo</a>.</p>
|
||||
<p>Once the system wide tools are ready, you'll need some particular files each
|
||||
time you want to start a new project. You can find them in the root of the
|
||||
<a href="https://github.com/rust-console/gba">rust-console/gba repo</a>.</p>
|
||||
<ul>
|
||||
<li><code>thumbv4-none-agb.json</code> describes the overall GBA to cargo-xbuild so it knows
|
||||
what to do. This is actually a somewhat made up target name since there's no
|
||||
official target name. The GBA is essentially the same as a normal
|
||||
<code>thumbv4-none-eabi</code> device, but we give it the "agb" signifier so that later
|
||||
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>thumbv4-none-agb.json</code> describes the overall GBA to cargo-xbuild (and LLVM)
|
||||
so it knows what to do. Technically the GBA is <code>thumbv4-none-eabi</code>, but we
|
||||
change the <code>eabi</code> to <code>agb</code> so that we can distinguish it from other <code>eabi</code>
|
||||
devices when using <code>cfg</code> flags.</li>
|
||||
<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
|
||||
<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
|
||||
that the GBA has about our program.</li>
|
||||
<li><code>linker.ld</code> tells the linker all the critical info about the layout
|
||||
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>
|
||||
<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
|
||||
|
@ -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
|
||||
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>,
|
||||
which the GBA doesn't have. You can still run some of your project's tests
|
||||
with <code>cargo test</code>, but that builds for your local machine, so anything
|
||||
specific to the GBA (such as reading and writing registers) won't be
|
||||
testable that way. If you want to isolate and try out some piece code
|
||||
running on the GBA you'll unfortunately have to make a demo for it in your
|
||||
<code>examples/</code> directory and then run the demo in an emulator and see if it
|
||||
does what you expect.</li>
|
||||
which the GBA doesn't have. If you want you can still run some of your
|
||||
project's tests with <code>cargo test --lib</code> or similar, but that builds for your
|
||||
local machine, so anything specific to the GBA (such as reading and writing
|
||||
registers) won't be testable that way. If you want to isolate and try out
|
||||
some piece code running on the GBA you'll unfortunately have to make a demo
|
||||
for it in your <code>examples/</code> directory and then run the demo in an emulator
|
||||
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
|
||||
compile dependencies with the same sysroot, so you can include crates
|
||||
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>
|
||||
</ul>
|
||||
<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>
|
||||
<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
|
||||
|
@ -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
|
||||
three dots to the screen.</p>
|
||||
<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>
|
||||
<pre><pre class="playpen"><code class="language-rust">#![feature(start)]
|
||||
#![no_std]
|
||||
|
||||
#[cfg(not(test))]
|
||||
#[panic_handler]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
loop {}
|
||||
|
@ -332,15 +363,16 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
|||
}
|
||||
}
|
||||
</code></pre></pre>
|
||||
<p>Throw that into your project, build the program (as described back in Chapter
|
||||
0), and give it a run. You should see a red, green, and blue dot close-ish to
|
||||
the middle of the screen. If you don't, something already went wrong. Double
|
||||
check things, phone a friend, write your senators, try asking Ketsuban on the
|
||||
<a href="https://discordapp.com/invite/aVESxV8">Rust Community Discord</a>, 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>
|
||||
<p>Throw that into your project skeleton, build the program (as described back in
|
||||
Chapter 0), and give it a run in your emulator. You should see a red, green, and
|
||||
blue dot close-ish to the middle of the screen. If you don't, something already
|
||||
went wrong. Double check things, phone a friend, write your senators, try asking
|
||||
Ketsuban on the <a href="https://discordapp.com/invite/aVESxV8">Rust Community Discord</a>,
|
||||
until you're able to get your three dots going.</p>
|
||||
<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
|
||||
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">
|
||||
# #![allow(unused_variables)]
|
||||
#![feature(start)]
|
||||
|
@ -364,7 +396,6 @@ only life.</p>
|
|||
<pre><pre class="playpen"><code class="language-rust">
|
||||
# #![allow(unused_variables)]
|
||||
#fn main() {
|
||||
#[cfg(not(test))]
|
||||
#[panic_handler]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
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
|
||||
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>
|
||||
<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]
|
||||
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||
</code></pre></pre>
|
||||
|
@ -442,18 +470,17 @@ magic numbers mean or do.</p>
|
|||
<li><code>0x06000000</code> is the start of Video RAM.</li>
|
||||
</ul>
|
||||
<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,
|
||||
each in their own location... so that second part makes sense at least.</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>
|
||||
other magic to three magic locations in the Video RAM. Somehow that shows three
|
||||
dots. Gotta read on to find out why!</p>
|
||||
<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
|
||||
|
@ -466,18 +493,48 @@ 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);
|
||||
c.write_volatile(5);
|
||||
a += b;
|
||||
d.volatile_write(7);
|
||||
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
|
||||
<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>
|
||||
<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>
|
||||
<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
|
||||
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
|
||||
<code>0b100_0000_0011</code>. So, that's setting Mode 3 with background 2 enabled and
|
||||
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>
|
||||
<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
|
||||
|
@ -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
|
||||
binary to make sense of it.</p>
|
||||
<ul>
|
||||
<li>0x001F: 0b11111</li>
|
||||
<li>0x03E0: 0b11111_00000</li>
|
||||
<li>0x7C00: 0b11111_00000_00000</li>
|
||||
<li>0x001F: 0b0_00000_00000_11111</li>
|
||||
<li>0x03E0: 0b0_00000_11111_00000</li>
|
||||
<li>0x7C00: 0b0_11111_00000_00000</li>
|
||||
</ul>
|
||||
<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>
|
||||
|
@ -702,7 +757,6 @@ binary to make sense of it.</p>
|
|||
<pre><pre class="playpen"><code class="language-rust">#![feature(start)]
|
||||
#![no_std]
|
||||
|
||||
#[cfg(not(test))]
|
||||
#[panic_handler]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
loop {}
|
||||
|
@ -720,11 +774,12 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
|||
}
|
||||
</code></pre></pre>
|
||||
<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">
|
||||
# #![allow(unused_variables)]
|
||||
#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 BG2: u16 = 0b100_0000_0000;
|
||||
#}</code></pre></pre>
|
||||
|
@ -735,8 +790,11 @@ pub const BG2: u16 = 0b100_0000_0000;
|
|||
pub const VRAM: usize = 0x06000000;
|
||||
pub const SCREEN_WIDTH: isize = 240;
|
||||
#}</code></pre></pre>
|
||||
<p>And then we want a small helper function for putting together a color value.</p>
|
||||
<p>Happily, this one can even be declared as a const function. At the time of
|
||||
<p>Note that VRAM has to be interpreted in different ways depending on mode, so we
|
||||
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
|
||||
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>
|
||||
|
@ -754,7 +812,7 @@ usually helps you think about it a lot better.</p>
|
|||
# #![allow(unused_variables)]
|
||||
#fn main() {
|
||||
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>
|
||||
<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)]
|
||||
#![no_std]
|
||||
|
||||
#[cfg(not(test))]
|
||||
#[panic_handler]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
loop {}
|
||||
|
@ -771,7 +828,7 @@ fn panic(_info: &core::panic::PanicInfo) -> ! {
|
|||
#[start]
|
||||
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||
unsafe {
|
||||
DISPCNT.write_volatile(MODE3 | BG2);
|
||||
DISPCNT.write(MODE3 | BG2);
|
||||
mode3_pixel(120, 80, rgb16(31, 0, 0));
|
||||
mode3_pixel(136, 80, rgb16(0, 31, 0));
|
||||
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 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) {
|
||||
(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>
|
||||
<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
|
||||
other elements all labeled and sorted out for you. Still, for educational
|
||||
purposes it's often best to do it yourself at least once.</p>
|
||||
other elements all labeled and sorted out for you (not identically, but
|
||||
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>
|
||||
<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>
|
||||
|
@ -865,15 +938,15 @@ reading and writing the key bits.</p>
|
|||
<pre><pre class="playpen"><code class="language-rust">
|
||||
# #![allow(unused_variables)]
|
||||
#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.
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
||||
#[repr(transparent)]
|
||||
pub struct KeyInputSetting(u16);
|
||||
|
||||
pub fn read_key_input() -> KeyInputSetting {
|
||||
unsafe { KeyInputSetting(KEYINPUT.read_volatile()) }
|
||||
pub fn key_input() -> KeyInputSetting {
|
||||
unsafe { KeyInputSetting(KEYINPUT.read()) }
|
||||
}
|
||||
#}</code></pre></pre>
|
||||
<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
|
||||
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
|
||||
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">
|
||||
# #![allow(unused_variables)]
|
||||
#fn main() {
|
||||
pub fn read_key_input() -> KeyInputSetting {
|
||||
unsafe { KeyInputSetting(KEYINPUT.read_volatile() ^ 0b1111_1111_1111_1111) }
|
||||
pub fn key_input() -> KeyInputSetting {
|
||||
unsafe { KeyInputSetting(KEYINPUT.read_volatile() ^ 0b0000_0011_1111_1111) }
|
||||
}
|
||||
#}</code></pre></pre>
|
||||
<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">
|
||||
# #![allow(unused_variables)]
|
||||
#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 {
|
||||
unsafe { VCOUNT.read_volatile() }
|
||||
pub fn vcount() -> u16 {
|
||||
unsafe { VCOUNT.read() }
|
||||
}
|
||||
#}</code></pre></pre>
|
||||
<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 fn wait_until_vblank() {
|
||||
while read_vcount() < SCREEN_HEIGHT as u16 {}
|
||||
while vcount() < SCREEN_HEIGHT as u16 {}
|
||||
}
|
||||
|
||||
pub fn wait_until_vdraw() {
|
||||
while read_vcount() >= SCREEN_HEIGHT as u16 {}
|
||||
while vcount() >= SCREEN_HEIGHT as u16 {}
|
||||
}
|
||||
#}</code></pre></pre>
|
||||
<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) {
|
||||
let color = color as u32;
|
||||
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_WIDTH / 2) {
|
||||
ptr.write_volatile(bulk_color);
|
||||
ptr.write(bulk_color);
|
||||
ptr = ptr.offset(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
(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>
|
||||
<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]
|
||||
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||
unsafe {
|
||||
DISPCNT.write_volatile(MODE3 | BG2);
|
||||
DISPCNT.write(MODE3 | BG2);
|
||||
}
|
||||
|
||||
let mut px = SCREEN_WIDTH / 2;
|
||||
|
@ -1131,7 +1206,7 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
|||
|
||||
loop {
|
||||
// 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
|
||||
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
|
||||
their own line. In this case, we reset the game without moving them to a new
|
||||
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
|
||||
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>
|
||||
|
||||
|
|
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)]
|
||||
#![no_std]
|
||||
|
||||
#[cfg(not(test))]
|
||||
#[panic_handler]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
loop {}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#![feature(start)]
|
||||
#![no_std]
|
||||
|
||||
#[cfg(not(test))]
|
||||
#[panic_handler]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
loop {}
|
||||
|
@ -10,7 +9,7 @@ fn panic(_info: &core::panic::PanicInfo) -> ! {
|
|||
#[start]
|
||||
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||
unsafe {
|
||||
DISPCNT.write_volatile(MODE3 | BG2);
|
||||
DISPCNT.write(MODE3 | BG2);
|
||||
mode3_pixel(120, 80, rgb16(31, 0, 0));
|
||||
mode3_pixel(136, 80, rgb16(0, 31, 0));
|
||||
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 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) {
|
||||
(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)]
|
||||
#![no_std]
|
||||
|
||||
#[cfg(not(test))]
|
||||
#[panic_handler]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
loop {}
|
||||
|
@ -10,7 +9,7 @@ fn panic(_info: &core::panic::PanicInfo) -> ! {
|
|||
#[start]
|
||||
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||
unsafe {
|
||||
DISPCNT.write_volatile(MODE3 | BG2);
|
||||
DISPCNT.write(MODE3 | BG2);
|
||||
}
|
||||
|
||||
let mut px = SCREEN_WIDTH / 2;
|
||||
|
@ -19,7 +18,7 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
|||
|
||||
loop {
|
||||
// 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
|
||||
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 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) {
|
||||
let color = color as u32;
|
||||
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_WIDTH / 2) {
|
||||
ptr.write_volatile(bulk_color);
|
||||
ptr.write(bulk_color);
|
||||
ptr = ptr.offset(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
(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.
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
||||
|
@ -101,8 +115,8 @@ pub enum TriBool {
|
|||
Plus = 1,
|
||||
}
|
||||
|
||||
pub fn read_key_input() -> KeyInputSetting {
|
||||
unsafe { KeyInputSetting(KEYINPUT.read_volatile() ^ 0b1111_1111_1111_1111) }
|
||||
pub fn key_input() -> KeyInputSetting {
|
||||
unsafe { KeyInputSetting(KEYINPUT.read() ^ 0b0000_0011_1111_1111) }
|
||||
}
|
||||
|
||||
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 {
|
||||
unsafe { VCOUNT.read_volatile() }
|
||||
pub fn vcount() -> u16 {
|
||||
unsafe { VCOUNT.read() }
|
||||
}
|
||||
|
||||
pub fn wait_until_vblank() {
|
||||
while read_vcount() < SCREEN_HEIGHT as u16 {}
|
||||
while vcount() < SCREEN_HEIGHT as u16 {}
|
||||
}
|
||||
|
||||
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 {
|
||||
.text : {
|
||||
KEEP(crt0.o(.text));
|
||||
KEEP(target/crt0.o(.text));
|
||||
*(.text .text.*);
|
||||
. = ALIGN(4);
|
||||
} >rom = 0xff
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
//! Things that I wish were in core, but aren't.
|
||||
|
||||
/// A simple wrapper to any `*mut T` so that the basic "read" and "write"
|
||||
/// operations are volatile.
|
||||
/// A simple wrapper for any `*mut T` to adjust the basic operations.
|
||||
///
|
||||
/// Accessing the GBA's IO registers and video ram and specific other places on
|
||||
/// **must** be done with volatile operations. Having this wrapper makes that
|
||||
/// more clear for all the global const values into IO registers.
|
||||
/// Read and Write are made to be volatile. Offset is made to be
|
||||
/// wrapping_offset. This makes it much easier to correctly work with IO
|
||||
/// 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)]
|
||||
#[repr(transparent)]
|
||||
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
|
||||
/// for a normal raw pointer volatile read apply.
|
||||
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.
|
||||
|
@ -35,16 +44,23 @@ impl<T> VolatilePtr<T> {
|
|||
/// This method adds absolutely no additional safety, so all safety concerns
|
||||
/// for a normal raw pointer volatile write apply.
|
||||
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
|
||||
///
|
||||
/// This is a standard offset, so all safety concerns of a normal raw pointer
|
||||
/// offset apply.
|
||||
/// This is a wrapping_offset, so all safety concerns of a normal raw pointer
|
||||
/// wrapping_offset apply.
|
||||
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.
|
||||
|
||||
use gba_proc_macro::register_bit;
|
||||
use gba_proc_macro::{newtype, register_bit};
|
||||
|
||||
use super::*;
|
||||
|
||||
|
@ -24,10 +24,11 @@ use super::*;
|
|||
/// * [gbatek entry](http://problemkaputt.de/gbatek.htm#lcdiodisplaycontrol)
|
||||
pub const DISPCNT: VolatilePtr<u16> = VolatilePtr(0x4000000 as *mut u16);
|
||||
|
||||
/// A newtype over the various display control options that you have on a GBA.
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
||||
#[repr(transparent)]
|
||||
pub struct DisplayControlSetting(u16);
|
||||
newtype!(
|
||||
DisplayControlSetting,
|
||||
u16,
|
||||
"A newtype over the various display control options that you have on a GBA."
|
||||
);
|
||||
|
||||
#[allow(missing_docs)]
|
||||
impl DisplayControlSetting {
|
||||
|
@ -56,19 +57,19 @@ impl DisplayControlSetting {
|
|||
};
|
||||
}
|
||||
|
||||
register_bit!(CGB_MODE_BIT, u16, 0b1000, cgb_mode, read);
|
||||
register_bit!(PAGE_SELECT_BIT, u16, 0b1_0000, page1_enabled, read_write);
|
||||
register_bit!(HBLANK_INTERVAL_FREE_BIT, u16, 0b10_0000, hblank_interval_free, read_write);
|
||||
register_bit!(OBJECT_MEMORY_1D, u16, 0b100_0000, object_memory_1d, read_write);
|
||||
register_bit!(FORCE_BLANK_BIT, u16, 0b1000_0000, force_blank, read_write);
|
||||
register_bit!(DISPLAY_BG0_BIT, u16, 0b1_0000_0000, display_bg0, read_write);
|
||||
register_bit!(DISPLAY_BG1_BIT, u16, 0b10_0000_0000, display_bg1, read_write);
|
||||
register_bit!(DISPLAY_BG2_BIT, u16, 0b100_0000_0000, display_bg2, read_write);
|
||||
register_bit!(DISPLAY_BG3_BIT, u16, 0b1000_0000_0000, display_bg3, read_write);
|
||||
register_bit!(DISPLAY_OBJECT_BIT, u16, 0b1_0000_0000_0000, display_object, read_write);
|
||||
register_bit!(DISPLAY_WINDOW0_BIT, u16, 0b10_0000_0000_0000, display_window0, read_write);
|
||||
register_bit!(DISPLAY_WINDOW1_BIT, u16, 0b100_0000_0000_0000, display_window1, read_write);
|
||||
register_bit!(OBJECT_WINDOW_BIT, u16, 0b1000_0000_0000_0000, display_object_window, read_write);
|
||||
register_bit!(CGB_MODE_BIT, u16, 0b1000, cgb_mode);
|
||||
register_bit!(PAGE_SELECT_BIT, u16, 0b1_0000, page1_enabled);
|
||||
register_bit!(HBLANK_INTERVAL_FREE_BIT, u16, 0b10_0000, hblank_interval_free);
|
||||
register_bit!(OBJECT_MEMORY_1D, u16, 0b100_0000, object_memory_1d);
|
||||
register_bit!(FORCE_BLANK_BIT, u16, 0b1000_0000, force_blank);
|
||||
register_bit!(DISPLAY_BG0_BIT, u16, 0b1_0000_0000, display_bg0);
|
||||
register_bit!(DISPLAY_BG1_BIT, u16, 0b10_0000_0000, display_bg1);
|
||||
register_bit!(DISPLAY_BG2_BIT, u16, 0b100_0000_0000, display_bg2);
|
||||
register_bit!(DISPLAY_BG3_BIT, u16, 0b1000_0000_0000, display_bg3);
|
||||
register_bit!(DISPLAY_OBJECT_BIT, u16, 0b1_0000_0000_0000, display_object);
|
||||
register_bit!(DISPLAY_WINDOW0_BIT, u16, 0b10_0000_0000_0000, display_window0);
|
||||
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);
|
||||
}
|
||||
|
||||
/// 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)
|
||||
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
|
||||
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
|
||||
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.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[repr(i32)]
|
||||
|
@ -398,18 +411,20 @@ pub enum TriBool {
|
|||
Plus = 1,
|
||||
}
|
||||
|
||||
newtype!(KeyInputSetting, u16, "A newtype over the key input state of the GBA");
|
||||
|
||||
#[allow(missing_docs)]
|
||||
impl KeyInputSetting {
|
||||
register_bit!(A_BIT, u16, 1 << 0, a_pressed, read_write);
|
||||
register_bit!(B_BIT, u16, 1 << 1, b_pressed, read_write);
|
||||
register_bit!(SELECT_BIT, u16, 1 << 2, select_pressed, read_write);
|
||||
register_bit!(START_BIT, u16, 1 << 3, start_pressed, read_write);
|
||||
register_bit!(RIGHT_BIT, u16, 1 << 4, right_pressed, read_write);
|
||||
register_bit!(LEFT_BIT, u16, 1 << 5, left_pressed, read_write);
|
||||
register_bit!(UP_BIT, u16, 1 << 6, up_pressed, read_write);
|
||||
register_bit!(DOWN_BIT, u16, 1 << 7, down_pressed, read_write);
|
||||
register_bit!(R_BIT, u16, 1 << 8, r_pressed, read_write);
|
||||
register_bit!(L_BIT, u16, 1 << 9, l_pressed, read_write);
|
||||
register_bit!(A_BIT, u16, 1 << 0, a_pressed);
|
||||
register_bit!(B_BIT, u16, 1 << 1, b_pressed);
|
||||
register_bit!(SELECT_BIT, u16, 1 << 2, select_pressed);
|
||||
register_bit!(START_BIT, u16, 1 << 3, start_pressed);
|
||||
register_bit!(RIGHT_BIT, u16, 1 << 4, right_pressed);
|
||||
register_bit!(LEFT_BIT, u16, 1 << 5, left_pressed);
|
||||
register_bit!(UP_BIT, u16, 1 << 6, up_pressed);
|
||||
register_bit!(DOWN_BIT, u16, 1 << 7, down_pressed);
|
||||
register_bit!(R_BIT, u16, 1 << 8, r_pressed);
|
||||
register_bit!(L_BIT, u16, 1 << 9, l_pressed);
|
||||
|
||||
/// Takes the difference between these keys and another set of keys.
|
||||
pub fn difference(&self, other: KeyInputSetting) -> KeyInputSetting {
|
||||
|
@ -442,8 +457,11 @@ impl KeyInputSetting {
|
|||
}
|
||||
|
||||
/// Gets the current state of the keys
|
||||
pub fn read_key_input() -> KeyInputSetting {
|
||||
unsafe { KeyInputSetting(KEYINPUT.read() ^ 0b1111_1111_1111_1111) }
|
||||
pub fn key_input() -> KeyInputSetting {
|
||||
// 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
|
||||
|
|
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)]
|
||||
|
||||
//! 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, 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(crate) use crate::core_extras::*;
|
||||
|
@ -20,8 +29,63 @@ pub(crate) use crate::core_extras::*;
|
|||
pub mod io_registers;
|
||||
|
||||
pub mod video_ram;
|
||||
pub(crate) use crate::video_ram::*;
|
||||
|
||||
/// Combines the Red, Blue, and Green provided into a single color value.
|
||||
pub const fn rgb16(red: u16, green: u16, blue: u16) -> u16 {
|
||||
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
|
||||
//! for.
|
||||
|
||||
pub use super::*;
|
||||
|
||||
/// The physical width in pixels of the GBA screen.
|
||||
pub const SCREEN_WIDTH: isize = 240;
|
||||
|
||||
|
@ -27,10 +29,14 @@ pub const SCREEN_HEIGHT: isize = 160;
|
|||
pub const VRAM_BASE_ADDRESS: usize = 0x0600_0000;
|
||||
|
||||
/// 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!(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.
|
||||
|
@ -44,6 +50,32 @@ pub fn mode3_pixel(col: isize, row: isize, color: u16) {
|
|||
///
|
||||
/// * `col` must be in `0..SCREEN_WIDTH`
|
||||
/// * `row` must be in `0..SCREEN_HEIGHT`
|
||||
pub unsafe fn mode3_pixel_unchecked(col: isize, row: isize, color: u16) {
|
||||
core::ptr::write_volatile((VRAM_BASE_ADDRESS as *mut u16).offset(col + row * SCREEN_WIDTH), color);
|
||||
pub unsafe fn mode3_draw_pixel_unchecked(col: isize, row: isize, color: u16) {
|
||||
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