almost chapter 1

This commit is contained in:
Lokathor 2018-11-10 02:03:37 -07:00
parent 5a49cbc713
commit 097dd72124
26 changed files with 2734 additions and 189 deletions

View file

@ -6,7 +6,7 @@ edition = "2018"
license = "Apache-2.0"
[dependencies]
#gba-proc-macro = "0.1"
gba-proc-macro = "0.1.1"
[profile.release]
lto = true

View file

@ -1,4 +1,10 @@
# Rust GBA Tutorials
* [ch01](ch01.md)
* [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](ch1/the_display_control.md)
* [Video Memory Intro](ch1/video_memory_intro.md)

122
book/src/ch0/index.md Normal file
View file

@ -0,0 +1,122 @@
# Chapter 0: Development Setup
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.
## 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
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.
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.
* 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.
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).
* `thumbv4-none-eabi.json` describes the overall GBA to cargo-xbuild so it knows
what to do.
* `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.
## Compiling
Once you've got something to build, you perform the following steps:
* `arm-none-eabi-as crt0.s -o crt0.o`
* This builds your text format `crt0.s` file into object format `crt0.o`. You
don't need to perform it every time, only when `crt0.s` changes, but you
might as well do it every time so that you never forget to because it's a
practically instant operation.
* `cargo xbuild --target thumbv4-none-eabi.json`
* This builds your Rust source. It accepts _most of_ the normal options, such
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.
* 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
get the idea.
At this point you have an ELF binary that some emulators can execute directly.
This is helpful because it'll have debug symbols and all that, assuming a debug
build. Specifically, [mgba 0.1 beta
1](https://mgba.io/2018/09/24/mgba-0.7-beta1/) can do it, and perhaps other
emulators can also do it.
However, if you want a "real" ROM that works in all emulators and that you could
transfer to a flash cart there's a little more to do.
* `arm-none-eabi-objcopy -O binary target/thumbv4-none-eabi/MODE/BIN_NAME target/ROM_NAME.gba`
* This will perform an [objcopy](https://linux.die.net/man/1/objcopy) on our
program. Here I've named the program `arm-none-eabi-objcopy`, which is what
devkitpro calls their version of `objcopy` that's specific to the GBA in the
Windows install. If the program isn't found under that name, have a look in
your installation directory to see if it's under a slightly different name
or something.
* As you can see from reading the man page, the `-O binary` option takes our
lovely ELF file with symbols and all that and strips it down to basically a
bare memory dump of the program.
* The next argument is the input file. You might not be familiar with how
`cargo` arranges stuff in the `target/` directory, and between RLS and
`cargo doc` and stuff it gets kinda crowded, so it goes like this:
* Since our program was built for a non-local target, first we've got a
directory named for that target, `thumbv4-none-eabi/`
* Next, the "MODE" is either `debug/` or `release/`, depending on if we had
the `--release` flag included. You'll probably only be packing release
mode programs all the way into GBA roms, but it works with either mode.
* Finally, the name of the program. If your program is something out of the
project's `src/bin/` then it'll be that file's name, or whatever name you
configured for the bin in the `Cargo.toml` file. If your program is
something out of the project's `examples/` directory there will be a
similar `examples/` sub-directory first, and then the example's name.
* The final argument is the output of the `objcopy`, which I suggest putting
at just the top level of the `target/` directory. Really it could go
anywhere, but if you're using git then it's likely that your `.gitignore`
file is already setup to exclude everything in `target/`, so this makes sure
that your intermediate game builds don't get checked into your git.
* `gbafix target/ROM_NAME.gba`
* The `gbafix` tool also comes from devkitpro. The GBA is very picky about a
ROM's format, and `gbafix` patches the ROM's header and such so that it'll
work right. Unlike `objcopy`, this tool is custom built for GBA development,
so it works just perfectly without any arguments beyond the file name. The
ROM is patched in place, so we don't even need to specify a new destination.
And you're finally done!
Of course, you probably want to make a script for all that, but it's up to you.

View file

@ -1 +0,0 @@
# ch01

179
book/src/ch1/hello1.md Normal file
View file

@ -0,0 +1,179 @@
# hello1
Ready? Here goes:
`hello1.rs`
```rust
#![feature(start)]
#![no_std]
#[cfg(not(test))]
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
loop {}
}
#[start]
fn main(_argc: isize, _argv: *const *const u8) -> isize {
unsafe {
(0x04000000 as *mut u16).write_volatile(0x0403);
(0x06000000 as *mut u16).offset(120 + 80 * 240).write_volatile(0x001F);
(0x06000000 as *mut u16).offset(136 + 80 * 240).write_volatile(0x03E0);
(0x06000000 as *mut u16).offset(120 + 96 * 240).write_volatile(0x7C00);
loop {}
}
}
```
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.
## Explaining hello1
So, what just happened? Even if you're used to Rust that might look pretty
strange. We'll go over each part extra carefully.
```rust
#![feature(start)]
```
This enables the [start
feature](https://doc.rust-lang.org/beta/unstable-book/language-features/start.html),
which you would normally be able to read about in the unstable book, except that
the book tells you nothing at all except to look at the [tracking
issue](https://github.com/rust-lang/rust/issues/29633).
Basically, a GBA game is even more low-level than the _normal_ amount of
low-level that you get from Rust, so we have to tell the compiler to account for
that by specifying a `#[start]`, and we need this feature on to do that.
```rust
#![no_std]
```
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 {}
}
```
This sets our [panic
handler](https://doc.rust-lang.org/nightly/nomicon/panic-handler.html).
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 _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 {
```
This is our `#[start]`. We call it `main`, but the signature looks a lot more
like the main from C than it does the main from Rust. Actually, those inputs are
useless, because nothing will be calling our code from the outside. Similarly,
it's totally undefined to return anything, so the fact that we output an `isize`
is vacuously true at best. We just have to use this function signature because
that's how `#[start]` works, not because the inputs and outputs are meaningful.
```rust
unsafe {
```
I hope you're all set for some `unsafe`, because there's a lot of it to be had.
```rust
(0x04000000 as *mut u16).write_volatile(0x0403);
```
Sure!
```rust
(0x06000000 as *mut u16).offset(120 + 80 * 240).write_volatile(0x001F);
(0x06000000 as *mut u16).offset(136 + 80 * 240).write_volatile(0x03E0);
(0x06000000 as *mut u16).offset(120 + 96 * 240).write_volatile(0x7C00);
```
Ah, of course.
```rust
loop {}
}
}
```
And, as mentioned above, there's no place for a GBA program to "return to", so
we can't ever let `main` try to return there. Instead, we go into an infinite
`loop` that does nothing. The fact that this doesn't ever return an `isize`
value doesn't seem to bother Rust, because I guess we're at least not returning
any other type of thing instead.
Fun fact: unlike in C++, an infinite loop with no side effects isn't Undefined
Behavior for us rustaceans... _semantically_. In truth LLVM has a [known
bug](https://github.com/rust-lang/rust/issues/28728) in this area, so we won't
actually be relying on empty loops in any future programs.
## All Those Magic Numbers
Alright, I cheated quite a bit in the middle there. The program works, but I
didn't really tell you why because I didn't really tell you what any of those
magic numbers mean or do.
* `0x04000000` is the address of an IO Register called the Display Control.
* `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 Display Control write sets a video mode, and the
Video RAM writes 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`, but the
write to `d` will _always_ happen after the write to `c`.
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.

10
book/src/ch1/index.md Normal file
View file

@ -0,0 +1,10 @@
# Ch 1: Hello GBA
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
using the tools involved. To get an idea of what a small part of a source file
will look like. All that stuff.
Normally, you write a program that prints "hello, world" to the terminal. The
GBA has no terminal, but it does have a screen, so instead we're going to draw
three dots to the screen.

View file

@ -0,0 +1,33 @@
# IO Registers
The GBA has a large number of **IO Registers** (not to be confused with CPU
registers). These are special memory locations from `0x04000000` to
`0x040003FE`. GBATEK has a [full
list](http://problemkaputt.de/gbatek.htm#gbaiomap), but we only need to learn
about a few of them at a time as we go, so don't be worried.
The important facts to know about IO Registers are these:
* Each has their own specific size. Most are `u16`, but some are `u32`.
* All of them must be accessed in a `volatile` style.
* Each register is specifically readable or writable or both. Actually, with
some registers there are even individual bits that are read-only or
write-only.
* If you write to a read-only position, those writes are simply ignored. This
mostly matters if a writable register contains a read-only bit (such as the
Display Control, next section).
* If you read from a write-only position, you get back values that are
[basically
nonsense](http://problemkaputt.de/gbatek.htm#gbaunpredictablethings). There
aren't really any registers that mix writable bits with read only bits, so
you're basically safe here. The only (mild) concern is that when you write a
value into a write-only register you need to keep track of what you wrote
somewhere else if you want to know what you wrote (such to adjust an offset
value by +1, or whatever).
* You can always check GBATEK to be sure, but if I don't mention it then a bit
is probably both read and write.
* Some registers have invalid bit patterns. For example, the lowest three bits
of the Display Control register can't legally be set to the values 6 or 7.
When talking about bit positions, the numbers are _zero indexed_ just like an
array index is.

View file

@ -0,0 +1,99 @@
# The Display Control
The Display Control is our first actual IO Register. GBATEK gives it the
shorthand [DISPCNT](http://problemkaputt.de/gbatek.htm#lcdiodisplaycontrol), so
you might see it under that name if you read other guides.
Among IO Registers, it's one of the simpler ones, but it's got enough complexity
that we can get a hint of what's to come.
Also it's the one that you basically always need to set at least once in every
GBA game, so it's a good starting one to go over for that reason too.
The Display Control is a `u16` value located at `0x0400_0000`.
## Video Modes
The lowest three bits (0-2) let you select from among the GBA's six video modes.
You'll notice that 3 bits allows for eight modes, but the values 6 and 7 are
prohibited.
Modes 0, 1, and 2 are "Tiled" modes. These are actually the modes that you
should eventually learn to use as much as possible. It lets the GBA's limited
video hardware do as much of the work as possible, leaving more of your CPU time
for gameplay computations. However, they're also complex enough to deserve their
own demos and chapters later on, so that's all we'll say about them for now.
Modes 3, 4, and 5 are "Bitmap" modes. These let you write individual pixels to
locations on the screen.
* **Mode 3** is full resolution (240w x 160h) RBG15 color. You might not be used to
RGB15, since modern computers have 24 or 32 bit colors. In RGB15, there's 5
bits for each color channel, and the highest bit is totally ignored.
* **Mode 4** is full resolution paletted color. Instead of being a `u16` color, each
pixel value is a `u8` palette index entry, and then the display uses the
palette memory (which we'll talk about later) to store the actual color data.
Since each pixel is half sized, we can fit twice as many. This lets us have
two "pages". At any given moment only one page is active, and you can draw to
the other page without the user noticing. You set which page to show with
another bit we'll get to in a moment.
* **Mode 5** is full color, but also with pages. This means that we must have a
reduced resolution to compensate (video memory is only so big!). The screen is
effectively only 160w x 128h in this mode.
## CGB Mode
Bit 3 is read only. It's on if you're running in CGB mode. Since we're making
GBA games you'd think that it'll never be on at all, but I guess you can change
it with BIOS stuff. Still, basically not an important bit.
## Page Flipping
Bit 4 lets you pick which page to use. This is only relevent in video modes 4 or
5, and is just ignored otherwise. It's very easy to remember: when the bit is 0
the 0th page is used, and when the bit is 1 the 1st page is used.
The second page always starts at `0x0600_A000`.
## OAM, VRAM, and Blanking
Bit 5 lets you access OAM during HBlank if enabled. This is cool, but it reduces
the maximum sprites per scanline, so it's not default.
Bit 6 lets you adjust if the GBA should treat Object Character VRAM as being 2d
(off) or 1d (on).
Bit 7 forces the screen to stay in vblank as long as it's set. This allows the
fastest use of the VRAM, Palette, and Object Attribute Memory. Obviously if you
leave this on for too long the player will notice a blank screen, but it might
be okay to use for a moment or two every once in a while.
## Screen Layers
Bits 8 through 11 control if Background layers 0 through 3 should be active.
Bit 12 affects the Object layer.
Note that not all background layers are available in all video modes:
* Mode 0: all
* Mode 1: 0/1/2
* Mode 2: 2/3
* Mode 3/4/5: 2
Bit 13 and 14 enable the display of Windows 0 and 1, and Bit 15 enables the
object display window. We'll get into how windows work later on, they let you do
some nifty graphical effects.
## In Conclusion...
So what did we do to the display control in `hello1`?
```rust
(0x04000000 as *mut u16).write_volatile(0x0403);
```
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.

View file

@ -0,0 +1,37 @@
# Video Memory Intro
The GBA's Video RAM is 96k stretching from `0x0600_0000` to `0x0601_7FFF`.
The Video RAM can only be accessed totally freely during a Vertical Blank
(aka "vblank"). At other times, if the CPU tries to touch the same part of video
memory as the display controller is accessing then the CPU gets bumped by a
cycle to avoid a clash.
Annoyingly, VRAM can only be properly written to in 16 and 32 bit segments (same
with PALRAM and OAM). If you try to write just an 8 bit segment, then both parts
of the 16 bit segment get the same value written to them. In other words, if you
write the byte `5` to `0x0600_0000`, then both `0x0600_0000` and ALSO
`0x0600_0001` will have the byte `5` in them. We have to be extra careful when
trying to set an individual byte, and we also have to be careful if we use
`memcopy` or `memset` as well, because they're byte oriented by default and
don't know to follow the special rules.
## RGB15
TODO
## Mode 3
TODO
## Mode 4
TODO
## Mode 5
TODO
## In Conclusion...
TODO

15
book/src/introduction.md Normal file
View file

@ -0,0 +1,15 @@
# Introduction
Here's a book that'll help you program in Rust on the GBA.
It's very "work in progress". At the moment there's only one demo program.
## Other Works
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.
* [GBATEK](http://problemkaputt.de/gbatek.htm), a homebrew tech manual for
GBA/NDS/DSi. We will regularly link to parts of it when talking about various
bits of the GBA.

View file

@ -1,32 +1,14 @@
@echo off
REM It could work to only rebuild the `crt0.o` file when `crt0.s` actually
REM changes, but it's actually a super cheap operation so we'll just do it
REM every single time to avoid any mix ups.
@echo on
@rem Build the crt0 file before we begin
arm-none-eabi-as crt0.s -o crt0.o
@echo off
REM This builds our program for the GBA. Note that the extension here is
REM important, because it causes all crates that we might import to also
REM use the correct target.
@echo on
@rem Build all examples, both debug and release
cargo xbuild --examples --target thumbv4-none-eabi.json
cargo xbuild --examples --target thumbv4-none-eabi.json --release
cargo xbuild --target thumbv4-none-eabi.json
@echo off
REM Some emulators can use cargo's output directly (which is cool, because then
REM you can keep debug symbols and stuff), but to make a "real" ROM we have to
REM also use the devkitpro tools to patch up the file a bit.
@echo on
arm-none-eabi-objcopy -O binary target/thumbv4-none-eabi/debug/main target/output.gba
gbafix target/output.gba
@echo off
REM Now all the same for release mode too!
@echo on
cargo xbuild --target thumbv4-none-eabi.json --release
arm-none-eabi-objcopy -O binary target/thumbv4-none-eabi/release/main target/output-release.gba
gbafix target/output-release.gba
@echo Packing examples into ROM files...
@for %%I in (.\examples\*.*) do @(
echo %%~nI
arm-none-eabi-objcopy -O binary target/thumbv4-none-eabi/release/examples/%%~nI target/example-%%~nI.gba >nul
gbafix target/example-%%~nI.gba >nul
)

329
docs/ch0/index.html Normal file
View file

@ -0,0 +1,329 @@
<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Ch 0: Development Setup - Rust GBA Tutorials</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="../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.html"><strong aria-hidden="true">3.3.</strong> The Display Control</a></li><li><a href="../ch1/video_memory_intro.html"><strong aria-hidden="true">3.4.</strong> Video Memory Intro</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 Tutorials</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="#chapter-0-development-setup" id="chapter-0-development-setup"><h1>Chapter 0: Development Setup</h1></a>
<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>
<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
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>
<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>
</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>
<ul>
<li><code>thumbv4-none-eabi.json</code> describes the overall GBA to cargo-xbuild so it knows
what to do.</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>
</ul>
<a class="header" href="#compiling" id="compiling"><h2>Compiling</h2></a>
<p>Once you've got something to build, you perform the following steps:</p>
<ul>
<li>
<p><code>arm-none-eabi-as crt0.s -o crt0.o</code></p>
<ul>
<li>This builds your text format <code>crt0.s</code> file into object format <code>crt0.o</code>. You
don't need to perform it every time, only when <code>crt0.s</code> changes, but you
might as well do it every time so that you never forget to because it's a
practically instant operation.</li>
</ul>
</li>
<li>
<p><code>cargo xbuild --target thumbv4-none-eabi.json</code></p>
<ul>
<li>This builds your Rust source. It accepts <em>most of</em> the normal options, such
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>
<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
get the idea.</li>
</ul>
</li>
</ul>
<p>At this point you have an ELF binary that some emulators can execute directly.
This is helpful because it'll have debug symbols and all that, assuming a debug
build. Specifically, <a href="https://mgba.io/2018/09/24/mgba-0.7-beta1/">mgba 0.1 beta
1</a> can do it, and perhaps other
emulators can also do it.</p>
<p>However, if you want a &quot;real&quot; ROM that works in all emulators and that you could
transfer to a flash cart there's a little more to do.</p>
<ul>
<li>
<p><code>arm-none-eabi-objcopy -O binary target/thumbv4-none-eabi/MODE/BIN_NAME target/ROM_NAME.gba</code></p>
<ul>
<li>This will perform an <a href="https://linux.die.net/man/1/objcopy">objcopy</a> on our
program. Here I've named the program <code>arm-none-eabi-objcopy</code>, which is what
devkitpro calls their version of <code>objcopy</code> that's specific to the GBA in the
Windows install. If the program isn't found under that name, have a look in
your installation directory to see if it's under a slightly different name
or something.</li>
<li>As you can see from reading the man page, the <code>-O binary</code> option takes our
lovely ELF file with symbols and all that and strips it down to basically a
bare memory dump of the program.</li>
<li>The next argument is the input file. You might not be familiar with how
<code>cargo</code> arranges stuff in the <code>target/</code> directory, and between RLS and
<code>cargo doc</code> and stuff it gets kinda crowded, so it goes like this:
<ul>
<li>Since our program was built for a non-local target, first we've got a
directory named for that target, <code>thumbv4-none-eabi/</code></li>
<li>Next, the &quot;MODE&quot; is either <code>debug/</code> or <code>release/</code>, depending on if we had
the <code>--release</code> flag included. You'll probably only be packing release
mode programs all the way into GBA roms, but it works with either mode.</li>
<li>Finally, the name of the program. If your program is something out of the
project's <code>src/bin/</code> then it'll be that file's name, or whatever name you
configured for the bin in the <code>Cargo.toml</code> file. If your program is
something out of the project's <code>examples/</code> directory there will be a
similar <code>examples/</code> sub-directory first, and then the example's name.</li>
</ul>
</li>
<li>The final argument is the output of the <code>objcopy</code>, which I suggest putting
at just the top level of the <code>target/</code> directory. Really it could go
anywhere, but if you're using git then it's likely that your <code>.gitignore</code>
file is already setup to exclude everything in <code>target/</code>, so this makes sure
that your intermediate game builds don't get checked into your git.</li>
</ul>
</li>
<li>
<p><code>gbafix target/ROM_NAME.gba</code></p>
<ul>
<li>The <code>gbafix</code> tool also comes from devkitpro. The GBA is very picky about a
ROM's format, and <code>gbafix</code> patches the ROM's header and such so that it'll
work right. Unlike <code>objcopy</code>, this tool is custom built for GBA development,
so it works just perfectly without any arguments beyond the file name. The
ROM is patched in place, so we don't even need to specify a new destination.</li>
</ul>
</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>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../introduction.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/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>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a href="../introduction.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/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>
<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>

356
docs/ch1/hello1.html Normal file
View file

@ -0,0 +1,356 @@
<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>hello1 - Rust GBA Tutorials</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="../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.html"><strong aria-hidden="true">3.3.</strong> The Display Control</a></li><li><a href="../ch1/video_memory_intro.html"><strong aria-hidden="true">3.4.</strong> Video Memory Intro</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 Tutorials</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="#hello1" id="hello1"><h1>hello1</h1></a>
<p>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: &amp;core::panic::PanicInfo) -&gt; ! {
loop {}
}
#[start]
fn main(_argc: isize, _argv: *const *const u8) -&gt; isize {
unsafe {
(0x04000000 as *mut u16).write_volatile(0x0403);
(0x06000000 as *mut u16).offset(120 + 80 * 240).write_volatile(0x001F);
(0x06000000 as *mut u16).offset(136 + 80 * 240).write_volatile(0x03E0);
(0x06000000 as *mut u16).offset(120 + 96 * 240).write_volatile(0x7C00);
loop {}
}
}
</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>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>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#![feature(start)]
#fn main() {
#}</code></pre></pre>
<p>This enables the <a href="https://doc.rust-lang.org/beta/unstable-book/language-features/start.html">start
feature</a>,
which you would normally be able to read about in the unstable book, except that
the book tells you nothing at all except to look at the <a href="https://github.com/rust-lang/rust/issues/29633">tracking
issue</a>.</p>
<p>Basically, a GBA game is even more low-level than the <em>normal</em> amount of
low-level that you get from Rust, so we have to tell the compiler to account for
that by specifying a <code>#[start]</code>, and we need this feature on to do that.</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#![no_std]
#fn main() {
#}</code></pre></pre>
<p>There's no standard library available on the GBA, so we'll have to live a core
only life.</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
#[cfg(not(test))]
#[panic_handler]
fn panic(_info: &amp;core::panic::PanicInfo) -&gt; ! {
loop {}
}
#}</code></pre></pre>
<p>This sets our <a href="https://doc.rust-lang.org/nightly/nomicon/panic-handler.html">panic
handler</a>.
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) -&gt; isize {
</code></pre></pre>
<p>This is our <code>#[start]</code>. We call it <code>main</code>, but the signature looks a lot more
like the main from C than it does the main from Rust. Actually, those inputs are
useless, because nothing will be calling our code from the outside. Similarly,
it's totally undefined to return anything, so the fact that we output an <code>isize</code>
is vacuously true at best. We just have to use this function signature because
that's how <code>#[start]</code> works, not because the inputs and outputs are meaningful.</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
unsafe {
#}</code></pre></pre>
<p>I hope you're all set for some <code>unsafe</code>, because there's a lot of it to be had.</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
(0x04000000 as *mut u16).write_volatile(0x0403);
#}</code></pre></pre>
<p>Sure!</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
(0x06000000 as *mut u16).offset(120 + 80 * 240).write_volatile(0x001F);
(0x06000000 as *mut u16).offset(136 + 80 * 240).write_volatile(0x03E0);
(0x06000000 as *mut u16).offset(120 + 96 * 240).write_volatile(0x7C00);
#}</code></pre></pre>
<p>Ah, of course.</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
loop {}
}
}
#}</code></pre></pre>
<p>And, as mentioned above, there's no place for a GBA program to &quot;return to&quot;, so
we can't ever let <code>main</code> try to return there. Instead, we go into an infinite
<code>loop</code> that does nothing. The fact that this doesn't ever return an <code>isize</code>
value doesn't seem to bother Rust, because I guess we're at least not returning
any other type of thing instead.</p>
<p>Fun fact: unlike in C++, an infinite loop with no side effects isn't Undefined
Behavior for us rustaceans... <em>semantically</em>. In truth LLVM has a <a href="https://github.com/rust-lang/rust/issues/28728">known
bug</a> in this area, so we won't
actually be relying on empty loops in any future programs.</p>
<a class="header" href="#all-those-magic-numbers" id="all-those-magic-numbers"><h2>All Those Magic Numbers</h2></a>
<p>Alright, I cheated quite a bit in the middle there. The program works, but I
didn't really tell you why because I didn't really tell you what any of those
magic numbers mean or do.</p>
<ul>
<li><code>0x04000000</code> is the address of an IO Register called the Display Control.</li>
<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 Display Control write sets a video mode, and the
Video RAM writes set pixels that will show up on the screen.</p>
<p>Similar to &quot;atomic&quot; 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>, but the
write to <code>d</code> will <em>always</em> happen after the write to <code>c</code>.</p>
<p>If you ever use volatile stuff on other platforms it's important to note that
volatile doesn't make things thread-safe, you still need atomic for that.
However, the GBA doesn't have threads, so we don't have to worry about thread
safety concerns.</p>
</main>
<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">
<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">
<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="../ch1/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">
<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>

207
docs/ch1/index.html Normal file
View file

@ -0,0 +1,207 @@
<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Ch 1: Hello GBA - Rust GBA Tutorials</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="../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.html"><strong aria-hidden="true">3.3.</strong> The Display Control</a></li><li><a href="../ch1/video_memory_intro.html"><strong aria-hidden="true">3.4.</strong> Video Memory Intro</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 Tutorials</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-1-hello-gba" id="ch-1-hello-gba"><h1>Ch 1: Hello GBA</h1></a>
<p>Traditionally a person writes a &quot;hello, world&quot; program so that they can test
that their development environment is setup properly and to just get a feel for
using the tools involved. To get an idea of what a small part of a source file
will look like. All that stuff.</p>
<p>Normally, you write a program that prints &quot;hello, world&quot; to the terminal. The
GBA has no terminal, but it does have a screen, so instead we're going to draw
three dots to the screen.</p>
</main>
<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">
<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">
<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="../ch0/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">
<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>

233
docs/ch1/io_registers.html Normal file
View file

@ -0,0 +1,233 @@
<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>IO Registers - Rust GBA Tutorials</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="../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.html"><strong aria-hidden="true">3.3.</strong> The Display Control</a></li><li><a href="../ch1/video_memory_intro.html"><strong aria-hidden="true">3.4.</strong> Video Memory Intro</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 Tutorials</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="#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
<code>0x040003FE</code>. GBATEK has a <a href="http://problemkaputt.de/gbatek.htm#gbaiomap">full
list</a>, but we only need to learn
about a few of them at a time as we go, so don't be worried.</p>
<p>The important facts to know about IO Registers are these:</p>
<ul>
<li>Each has their own specific size. Most are <code>u16</code>, but some are <code>u32</code>.</li>
<li>All of them must be accessed in a <code>volatile</code> style.</li>
<li>Each register is specifically readable or writable or both. Actually, with
some registers there are even individual bits that are read-only or
write-only.
<ul>
<li>If you write to a read-only position, those writes are simply ignored. This
mostly matters if a writable register contains a read-only bit (such as the
Display Control, next section).</li>
<li>If you read from a write-only position, you get back values that are
<a href="http://problemkaputt.de/gbatek.htm#gbaunpredictablethings">basically
nonsense</a>. There
aren't really any registers that mix writable bits with read only bits, so
you're basically safe here. The only (mild) concern is that when you write a
value into a write-only register you need to keep track of what you wrote
somewhere else if you want to know what you wrote (such to adjust an offset
value by +1, or whatever).</li>
<li>You can always check GBATEK to be sure, but if I don't mention it then a bit
is probably both read and write.</li>
</ul>
</li>
<li>Some registers have invalid bit patterns. For example, the lowest three bits
of the Display Control register can't legally be set to the values 6 or 7.</li>
</ul>
<p>When talking about bit positions, the numbers are <em>zero indexed</em> just like an
array index is.</p>
</main>
<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">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next" href="../ch1/the_display_control.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="../ch1/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="../ch1/the_display_control.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>

View file

@ -0,0 +1,276 @@
<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>The Display Control - Rust GBA Tutorials</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="../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.html" class="active"><strong aria-hidden="true">3.3.</strong> The Display Control</a></li><li><a href="../ch1/video_memory_intro.html"><strong aria-hidden="true">3.4.</strong> Video Memory Intro</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 Tutorials</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="#the-display-control" id="the-display-control"><h1>The Display Control</h1></a>
<p>The Display Control is our first actual IO Register. GBATEK gives it the
shorthand <a href="http://problemkaputt.de/gbatek.htm#lcdiodisplaycontrol">DISPCNT</a>, so
you might see it under that name if you read other guides.</p>
<p>Among IO Registers, it's one of the simpler ones, but it's got enough complexity
that we can get a hint of what's to come.</p>
<p>Also it's the one that you basically always need to set at least once in every
GBA game, so it's a good starting one to go over for that reason too.</p>
<p>The Display Control is a <code>u16</code> value located at <code>0x0400_0000</code>.</p>
<a class="header" href="#video-modes" id="video-modes"><h2>Video Modes</h2></a>
<p>The lowest three bits (0-2) let you select from among the GBA's six video modes.
You'll notice that 3 bits allows for eight modes, but the values 6 and 7 are
prohibited.</p>
<p>Modes 0, 1, and 2 are &quot;Tiled&quot; modes. These are actually the modes that you
should eventually learn to use as much as possible. It lets the GBA's limited
video hardware do as much of the work as possible, leaving more of your CPU time
for gameplay computations. However, they're also complex enough to deserve their
own demos and chapters later on, so that's all we'll say about them for now.</p>
<p>Modes 3, 4, and 5 are &quot;Bitmap&quot; modes. These let you write individual pixels to
locations on the screen.</p>
<ul>
<li><strong>Mode 3</strong> is full resolution (240w x 160h) RBG15 color. You might not be used to
RGB15, since modern computers have 24 or 32 bit colors. In RGB15, there's 5
bits for each color channel, and the highest bit is totally ignored.</li>
<li><strong>Mode 4</strong> is full resolution paletted color. Instead of being a <code>u16</code> color, each
pixel value is a <code>u8</code> palette index entry, and then the display uses the
palette memory (which we'll talk about later) to store the actual color data.
Since each pixel is half sized, we can fit twice as many. This lets us have
two &quot;pages&quot;. At any given moment only one page is active, and you can draw to
the other page without the user noticing. You set which page to show with
another bit we'll get to in a moment.</li>
<li><strong>Mode 5</strong> is full color, but also with pages. This means that we must have a
reduced resolution to compensate (video memory is only so big!). The screen is
effectively only 160w x 128h in this mode.</li>
</ul>
<a class="header" href="#cgb-mode" id="cgb-mode"><h2>CGB Mode</h2></a>
<p>Bit 3 is read only. It's on if you're running in CGB mode. Since we're making
GBA games you'd think that it'll never be on at all, but I guess you can change
it with BIOS stuff. Still, basically not an important bit.</p>
<a class="header" href="#page-flipping" id="page-flipping"><h2>Page Flipping</h2></a>
<p>Bit 4 lets you pick which page to use. This is only relevent in video modes 4 or
5, and is just ignored otherwise. It's very easy to remember: when the bit is 0
the 0th page is used, and when the bit is 1 the 1st page is used.</p>
<p>The second page always starts at <code>0x0600_A000</code>.</p>
<a class="header" href="#oam-vram-and-blanking" id="oam-vram-and-blanking"><h2>OAM, VRAM, and Blanking</h2></a>
<p>Bit 5 lets you access OAM during HBlank if enabled. This is cool, but it reduces
the maximum sprites per scanline, so it's not default.</p>
<p>Bit 6 lets you adjust if the GBA should treat Object Character VRAM as being 2d
(off) or 1d (on).</p>
<p>Bit 7 forces the screen to stay in vblank as long as it's set. This allows the
fastest use of the VRAM, Palette, and Object Attribute Memory. Obviously if you
leave this on for too long the player will notice a blank screen, but it might
be okay to use for a moment or two every once in a while.</p>
<a class="header" href="#screen-layers" id="screen-layers"><h2>Screen Layers</h2></a>
<p>Bits 8 through 11 control if Background layers 0 through 3 should be active.</p>
<p>Bit 12 affects the Object layer.</p>
<p>Note that not all background layers are available in all video modes:</p>
<ul>
<li>Mode 0: all</li>
<li>Mode 1: 0/1/2</li>
<li>Mode 2: 2/3</li>
<li>Mode 3/4/5: 2</li>
</ul>
<p>Bit 13 and 14 enable the display of Windows 0 and 1, and Bit 15 enables the
object display window. We'll get into how windows work later on, they let you do
some nifty graphical effects.</p>
<a class="header" href="#in-conclusion" id="in-conclusion"><h2>In Conclusion...</h2></a>
<p>So what did we do to the display control in <code>hello1</code>?</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
(0x04000000 as *mut u16).write_volatile(0x0403);
#}</code></pre></pre>
<p>First let's <a href="https://www.wolframalpha.com/input/?i=0x0403">convert that to
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>
</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">
<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">
<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="../ch1/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">
<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>

View file

@ -0,0 +1,215 @@
<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Video Memory Intro - Rust GBA Tutorials</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="../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.html"><strong aria-hidden="true">3.3.</strong> The Display Control</a></li><li><a href="../ch1/video_memory_intro.html" class="active"><strong aria-hidden="true">3.4.</strong> Video Memory Intro</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 Tutorials</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="#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 &quot;vblank&quot;). At other times, if the CPU tries to touch the same part of video
memory as the display controller is accessing then the CPU gets bumped by a
cycle to avoid a clash.</p>
<p>Annoyingly, VRAM can only be properly written to in 16 and 32 bit segments (same
with PALRAM and OAM). If you try to write just an 8 bit segment, then both parts
of the 16 bit segment get the same value written to them. In other words, if you
write the byte <code>5</code> to <code>0x0600_0000</code>, then both <code>0x0600_0000</code> and ALSO
<code>0x0600_0001</code> will have the byte <code>5</code> in them. We have to be extra careful when
trying to set an individual byte, and we also have to be careful if we use
<code>memcopy</code> or <code>memset</code> as well, because they're byte oriented by default and
don't know to follow the special rules.</p>
<a class="header" href="#rgb15" id="rgb15"><h2>RGB15</h2></a>
<p>TODO</p>
<a class="header" href="#mode-3" id="mode-3"><h2>Mode 3</h2></a>
<p>TODO</p>
<a class="header" href="#mode-4" id="mode-4"><h2>Mode 4</h2></a>
<p>TODO</p>
<a class="header" href="#mode-5" id="mode-5"><h2>Mode 5</h2></a>
<p>TODO</p>
<a class="header" href="#in-conclusion" id="in-conclusion"><h2>In Conclusion...</h2></a>
<p>TODO</p>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../ch1/the_display_control.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="../ch1/the_display_control.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>

View file

@ -3,7 +3,7 @@
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>ch01 - Rust GBA Tutorials</title>
<title>Introduction - Rust GBA Tutorials</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="ch01.html"><strong aria-hidden="true">1.</strong> ch01</a></li></ol>
<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.html"><strong aria-hidden="true">3.3.</strong> The Display Control</a></li><li><a href="ch1/video_memory_intro.html"><strong aria-hidden="true">3.4.</strong> Video Memory Intro</a></li></ol></li></ol>
</nav>
<div id="page-wrapper" class="page-wrapper">
@ -136,7 +136,18 @@
<div id="content" class="content">
<main>
<a class="header" href="#ch01" id="ch01"><h1>ch01</h1></a>
<a class="header" href="#introduction" id="introduction"><h1>Introduction</h1></a>
<p>Here's a book that'll help you program in Rust on the GBA.</p>
<p>It's very &quot;work in progress&quot;. At the moment there's only one demo program.</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>
<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>
<li><a href="http://problemkaputt.de/gbatek.htm">GBATEK</a>, a homebrew tech manual for
GBA/NDS/DSi. We will regularly link to parts of it when talking about various
bits of the GBA.</li>
</ul>
</main>

View file

@ -3,7 +3,7 @@
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>ch01 - Rust GBA Tutorials</title>
<title>Introduction - Rust GBA Tutorials</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="ch01.html" class="active"><strong aria-hidden="true">1.</strong> ch01</a></li></ol>
<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.html"><strong aria-hidden="true">3.3.</strong> The Display Control</a></li><li><a href="ch1/video_memory_intro.html"><strong aria-hidden="true">3.4.</strong> Video Memory Intro</a></li></ol></li></ol>
</nav>
<div id="page-wrapper" class="page-wrapper">
@ -136,7 +136,18 @@
<div id="content" class="content">
<main>
<a class="header" href="#ch01" id="ch01"><h1>ch01</h1></a>
<a class="header" href="#introduction" id="introduction"><h1>Introduction</h1></a>
<p>Here's a book that'll help you program in Rust on the GBA.</p>
<p>It's very &quot;work in progress&quot;. At the moment there's only one demo program.</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>
<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>
<li><a href="http://problemkaputt.de/gbatek.htm">GBATEK</a>, a homebrew tech manual for
GBA/NDS/DSi. We will regularly link to parts of it when talking about various
bits of the GBA.</li>
</ul>
</main>
@ -145,6 +156,10 @@
<a rel="next" href="ch0/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>
@ -155,6 +170,10 @@
<a href="ch0/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>

View file

@ -72,7 +72,7 @@
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<ol class="chapter"><li><a href="ch01.html"><strong aria-hidden="true">1.</strong> ch01</a></li></ol>
<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.html"><strong aria-hidden="true">3.3.</strong> The Display Control</a></li><li><a href="ch1/video_memory_intro.html"><strong aria-hidden="true">3.4.</strong> Video Memory Intro</a></li></ol></li></ol>
</nav>
<div id="page-wrapper" class="page-wrapper">
@ -136,7 +136,448 @@
<div id="content" class="content">
<main>
<a class="header" href="#ch01" id="ch01"><h1>ch01</h1></a>
<a class="header" href="#introduction" id="introduction"><h1>Introduction</h1></a>
<p>Here's a book that'll help you program in Rust on the GBA.</p>
<p>It's very &quot;work in progress&quot;. At the moment there's only one demo program.</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>
<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>
<li><a href="http://problemkaputt.de/gbatek.htm">GBATEK</a>, a homebrew tech manual for
GBA/NDS/DSi. We will regularly link to parts of it when talking about various
bits of the GBA.</li>
</ul>
<a class="header" href="#chapter-0-development-setup" id="chapter-0-development-setup"><h1>Chapter 0: Development Setup</h1></a>
<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>
<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
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>
<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>
</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>
<ul>
<li><code>thumbv4-none-eabi.json</code> describes the overall GBA to cargo-xbuild so it knows
what to do.</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>
</ul>
<a class="header" href="#compiling" id="compiling"><h2>Compiling</h2></a>
<p>Once you've got something to build, you perform the following steps:</p>
<ul>
<li>
<p><code>arm-none-eabi-as crt0.s -o crt0.o</code></p>
<ul>
<li>This builds your text format <code>crt0.s</code> file into object format <code>crt0.o</code>. You
don't need to perform it every time, only when <code>crt0.s</code> changes, but you
might as well do it every time so that you never forget to because it's a
practically instant operation.</li>
</ul>
</li>
<li>
<p><code>cargo xbuild --target thumbv4-none-eabi.json</code></p>
<ul>
<li>This builds your Rust source. It accepts <em>most of</em> the normal options, such
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>
<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
get the idea.</li>
</ul>
</li>
</ul>
<p>At this point you have an ELF binary that some emulators can execute directly.
This is helpful because it'll have debug symbols and all that, assuming a debug
build. Specifically, <a href="https://mgba.io/2018/09/24/mgba-0.7-beta1/">mgba 0.1 beta
1</a> can do it, and perhaps other
emulators can also do it.</p>
<p>However, if you want a &quot;real&quot; ROM that works in all emulators and that you could
transfer to a flash cart there's a little more to do.</p>
<ul>
<li>
<p><code>arm-none-eabi-objcopy -O binary target/thumbv4-none-eabi/MODE/BIN_NAME target/ROM_NAME.gba</code></p>
<ul>
<li>This will perform an <a href="https://linux.die.net/man/1/objcopy">objcopy</a> on our
program. Here I've named the program <code>arm-none-eabi-objcopy</code>, which is what
devkitpro calls their version of <code>objcopy</code> that's specific to the GBA in the
Windows install. If the program isn't found under that name, have a look in
your installation directory to see if it's under a slightly different name
or something.</li>
<li>As you can see from reading the man page, the <code>-O binary</code> option takes our
lovely ELF file with symbols and all that and strips it down to basically a
bare memory dump of the program.</li>
<li>The next argument is the input file. You might not be familiar with how
<code>cargo</code> arranges stuff in the <code>target/</code> directory, and between RLS and
<code>cargo doc</code> and stuff it gets kinda crowded, so it goes like this:
<ul>
<li>Since our program was built for a non-local target, first we've got a
directory named for that target, <code>thumbv4-none-eabi/</code></li>
<li>Next, the &quot;MODE&quot; is either <code>debug/</code> or <code>release/</code>, depending on if we had
the <code>--release</code> flag included. You'll probably only be packing release
mode programs all the way into GBA roms, but it works with either mode.</li>
<li>Finally, the name of the program. If your program is something out of the
project's <code>src/bin/</code> then it'll be that file's name, or whatever name you
configured for the bin in the <code>Cargo.toml</code> file. If your program is
something out of the project's <code>examples/</code> directory there will be a
similar <code>examples/</code> sub-directory first, and then the example's name.</li>
</ul>
</li>
<li>The final argument is the output of the <code>objcopy</code>, which I suggest putting
at just the top level of the <code>target/</code> directory. Really it could go
anywhere, but if you're using git then it's likely that your <code>.gitignore</code>
file is already setup to exclude everything in <code>target/</code>, so this makes sure
that your intermediate game builds don't get checked into your git.</li>
</ul>
</li>
<li>
<p><code>gbafix target/ROM_NAME.gba</code></p>
<ul>
<li>The <code>gbafix</code> tool also comes from devkitpro. The GBA is very picky about a
ROM's format, and <code>gbafix</code> patches the ROM's header and such so that it'll
work right. Unlike <code>objcopy</code>, this tool is custom built for GBA development,
so it works just perfectly without any arguments beyond the file name. The
ROM is patched in place, so we don't even need to specify a new destination.</li>
</ul>
</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>
<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 &quot;hello, world&quot; program so that they can test
that their development environment is setup properly and to just get a feel for
using the tools involved. To get an idea of what a small part of a source file
will look like. All that stuff.</p>
<p>Normally, you write a program that prints &quot;hello, world&quot; to the terminal. The
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><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: &amp;core::panic::PanicInfo) -&gt; ! {
loop {}
}
#[start]
fn main(_argc: isize, _argv: *const *const u8) -&gt; isize {
unsafe {
(0x04000000 as *mut u16).write_volatile(0x0403);
(0x06000000 as *mut u16).offset(120 + 80 * 240).write_volatile(0x001F);
(0x06000000 as *mut u16).offset(136 + 80 * 240).write_volatile(0x03E0);
(0x06000000 as *mut u16).offset(120 + 96 * 240).write_volatile(0x7C00);
loop {}
}
}
</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>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>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#![feature(start)]
#fn main() {
#}</code></pre></pre>
<p>This enables the <a href="https://doc.rust-lang.org/beta/unstable-book/language-features/start.html">start
feature</a>,
which you would normally be able to read about in the unstable book, except that
the book tells you nothing at all except to look at the <a href="https://github.com/rust-lang/rust/issues/29633">tracking
issue</a>.</p>
<p>Basically, a GBA game is even more low-level than the <em>normal</em> amount of
low-level that you get from Rust, so we have to tell the compiler to account for
that by specifying a <code>#[start]</code>, and we need this feature on to do that.</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#![no_std]
#fn main() {
#}</code></pre></pre>
<p>There's no standard library available on the GBA, so we'll have to live a core
only life.</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
#[cfg(not(test))]
#[panic_handler]
fn panic(_info: &amp;core::panic::PanicInfo) -&gt; ! {
loop {}
}
#}</code></pre></pre>
<p>This sets our <a href="https://doc.rust-lang.org/nightly/nomicon/panic-handler.html">panic
handler</a>.
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) -&gt; isize {
</code></pre></pre>
<p>This is our <code>#[start]</code>. We call it <code>main</code>, but the signature looks a lot more
like the main from C than it does the main from Rust. Actually, those inputs are
useless, because nothing will be calling our code from the outside. Similarly,
it's totally undefined to return anything, so the fact that we output an <code>isize</code>
is vacuously true at best. We just have to use this function signature because
that's how <code>#[start]</code> works, not because the inputs and outputs are meaningful.</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
unsafe {
#}</code></pre></pre>
<p>I hope you're all set for some <code>unsafe</code>, because there's a lot of it to be had.</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
(0x04000000 as *mut u16).write_volatile(0x0403);
#}</code></pre></pre>
<p>Sure!</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
(0x06000000 as *mut u16).offset(120 + 80 * 240).write_volatile(0x001F);
(0x06000000 as *mut u16).offset(136 + 80 * 240).write_volatile(0x03E0);
(0x06000000 as *mut u16).offset(120 + 96 * 240).write_volatile(0x7C00);
#}</code></pre></pre>
<p>Ah, of course.</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
loop {}
}
}
#}</code></pre></pre>
<p>And, as mentioned above, there's no place for a GBA program to &quot;return to&quot;, so
we can't ever let <code>main</code> try to return there. Instead, we go into an infinite
<code>loop</code> that does nothing. The fact that this doesn't ever return an <code>isize</code>
value doesn't seem to bother Rust, because I guess we're at least not returning
any other type of thing instead.</p>
<p>Fun fact: unlike in C++, an infinite loop with no side effects isn't Undefined
Behavior for us rustaceans... <em>semantically</em>. In truth LLVM has a <a href="https://github.com/rust-lang/rust/issues/28728">known
bug</a> in this area, so we won't
actually be relying on empty loops in any future programs.</p>
<a class="header" href="#all-those-magic-numbers" id="all-those-magic-numbers"><h2>All Those Magic Numbers</h2></a>
<p>Alright, I cheated quite a bit in the middle there. The program works, but I
didn't really tell you why because I didn't really tell you what any of those
magic numbers mean or do.</p>
<ul>
<li><code>0x04000000</code> is the address of an IO Register called the Display Control.</li>
<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 Display Control write sets a video mode, and the
Video RAM writes set pixels that will show up on the screen.</p>
<p>Similar to &quot;atomic&quot; 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>, but the
write to <code>d</code> will <em>always</em> happen after the write to <code>c</code>.</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>
<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
<code>0x040003FE</code>. GBATEK has a <a href="http://problemkaputt.de/gbatek.htm#gbaiomap">full
list</a>, but we only need to learn
about a few of them at a time as we go, so don't be worried.</p>
<p>The important facts to know about IO Registers are these:</p>
<ul>
<li>Each has their own specific size. Most are <code>u16</code>, but some are <code>u32</code>.</li>
<li>All of them must be accessed in a <code>volatile</code> style.</li>
<li>Each register is specifically readable or writable or both. Actually, with
some registers there are even individual bits that are read-only or
write-only.
<ul>
<li>If you write to a read-only position, those writes are simply ignored. This
mostly matters if a writable register contains a read-only bit (such as the
Display Control, next section).</li>
<li>If you read from a write-only position, you get back values that are
<a href="http://problemkaputt.de/gbatek.htm#gbaunpredictablethings">basically
nonsense</a>. There
aren't really any registers that mix writable bits with read only bits, so
you're basically safe here. The only (mild) concern is that when you write a
value into a write-only register you need to keep track of what you wrote
somewhere else if you want to know what you wrote (such to adjust an offset
value by +1, or whatever).</li>
<li>You can always check GBATEK to be sure, but if I don't mention it then a bit
is probably both read and write.</li>
</ul>
</li>
<li>Some registers have invalid bit patterns. For example, the lowest three bits
of the Display Control register can't legally be set to the values 6 or 7.</li>
</ul>
<p>When talking about bit positions, the numbers are <em>zero indexed</em> just like an
array index is.</p>
<a class="header" href="#the-display-control" id="the-display-control"><h1>The Display Control</h1></a>
<p>The Display Control is our first actual IO Register. GBATEK gives it the
shorthand <a href="http://problemkaputt.de/gbatek.htm#lcdiodisplaycontrol">DISPCNT</a>, so
you might see it under that name if you read other guides.</p>
<p>Among IO Registers, it's one of the simpler ones, but it's got enough complexity
that we can get a hint of what's to come.</p>
<p>Also it's the one that you basically always need to set at least once in every
GBA game, so it's a good starting one to go over for that reason too.</p>
<p>The Display Control is a <code>u16</code> value located at <code>0x0400_0000</code>.</p>
<a class="header" href="#video-modes" id="video-modes"><h2>Video Modes</h2></a>
<p>The lowest three bits (0-2) let you select from among the GBA's six video modes.
You'll notice that 3 bits allows for eight modes, but the values 6 and 7 are
prohibited.</p>
<p>Modes 0, 1, and 2 are &quot;Tiled&quot; modes. These are actually the modes that you
should eventually learn to use as much as possible. It lets the GBA's limited
video hardware do as much of the work as possible, leaving more of your CPU time
for gameplay computations. However, they're also complex enough to deserve their
own demos and chapters later on, so that's all we'll say about them for now.</p>
<p>Modes 3, 4, and 5 are &quot;Bitmap&quot; modes. These let you write individual pixels to
locations on the screen.</p>
<ul>
<li><strong>Mode 3</strong> is full resolution (240w x 160h) RBG15 color. You might not be used to
RGB15, since modern computers have 24 or 32 bit colors. In RGB15, there's 5
bits for each color channel, and the highest bit is totally ignored.</li>
<li><strong>Mode 4</strong> is full resolution paletted color. Instead of being a <code>u16</code> color, each
pixel value is a <code>u8</code> palette index entry, and then the display uses the
palette memory (which we'll talk about later) to store the actual color data.
Since each pixel is half sized, we can fit twice as many. This lets us have
two &quot;pages&quot;. At any given moment only one page is active, and you can draw to
the other page without the user noticing. You set which page to show with
another bit we'll get to in a moment.</li>
<li><strong>Mode 5</strong> is full color, but also with pages. This means that we must have a
reduced resolution to compensate (video memory is only so big!). The screen is
effectively only 160w x 128h in this mode.</li>
</ul>
<a class="header" href="#cgb-mode" id="cgb-mode"><h2>CGB Mode</h2></a>
<p>Bit 3 is read only. It's on if you're running in CGB mode. Since we're making
GBA games you'd think that it'll never be on at all, but I guess you can change
it with BIOS stuff. Still, basically not an important bit.</p>
<a class="header" href="#page-flipping" id="page-flipping"><h2>Page Flipping</h2></a>
<p>Bit 4 lets you pick which page to use. This is only relevent in video modes 4 or
5, and is just ignored otherwise. It's very easy to remember: when the bit is 0
the 0th page is used, and when the bit is 1 the 1st page is used.</p>
<p>The second page always starts at <code>0x0600_A000</code>.</p>
<a class="header" href="#oam-vram-and-blanking" id="oam-vram-and-blanking"><h2>OAM, VRAM, and Blanking</h2></a>
<p>Bit 5 lets you access OAM during HBlank if enabled. This is cool, but it reduces
the maximum sprites per scanline, so it's not default.</p>
<p>Bit 6 lets you adjust if the GBA should treat Object Character VRAM as being 2d
(off) or 1d (on).</p>
<p>Bit 7 forces the screen to stay in vblank as long as it's set. This allows the
fastest use of the VRAM, Palette, and Object Attribute Memory. Obviously if you
leave this on for too long the player will notice a blank screen, but it might
be okay to use for a moment or two every once in a while.</p>
<a class="header" href="#screen-layers" id="screen-layers"><h2>Screen Layers</h2></a>
<p>Bits 8 through 11 control if Background layers 0 through 3 should be active.</p>
<p>Bit 12 affects the Object layer.</p>
<p>Note that not all background layers are available in all video modes:</p>
<ul>
<li>Mode 0: all</li>
<li>Mode 1: 0/1/2</li>
<li>Mode 2: 2/3</li>
<li>Mode 3/4/5: 2</li>
</ul>
<p>Bit 13 and 14 enable the display of Windows 0 and 1, and Bit 15 enables the
object display window. We'll get into how windows work later on, they let you do
some nifty graphical effects.</p>
<a class="header" href="#in-conclusion" id="in-conclusion"><h2>In Conclusion...</h2></a>
<p>So what did we do to the display control in <code>hello1</code>?</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
(0x04000000 as *mut u16).write_volatile(0x0403);
#}</code></pre></pre>
<p>First let's <a href="https://www.wolframalpha.com/input/?i=0x0403">convert that to
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>
<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 &quot;vblank&quot;). At other times, if the CPU tries to touch the same part of video
memory as the display controller is accessing then the CPU gets bumped by a
cycle to avoid a clash.</p>
<p>Annoyingly, VRAM can only be properly written to in 16 and 32 bit segments (same
with PALRAM and OAM). If you try to write just an 8 bit segment, then both parts
of the 16 bit segment get the same value written to them. In other words, if you
write the byte <code>5</code> to <code>0x0600_0000</code>, then both <code>0x0600_0000</code> and ALSO
<code>0x0600_0001</code> will have the byte <code>5</code> in them. We have to be extra careful when
trying to set an individual byte, and we also have to be careful if we use
<code>memcopy</code> or <code>memset</code> as well, because they're byte oriented by default and
don't know to follow the special rules.</p>
<a class="header" href="#rgb15" id="rgb15"><h2>RGB15</h2></a>
<p>TODO</p>
<a class="header" href="#mode-3" id="mode-3"><h2>Mode 3</h2></a>
<p>TODO</p>
<a class="header" href="#mode-4" id="mode-4"><h2>Mode 4</h2></a>
<p>TODO</p>
<a class="header" href="#mode-5" id="mode-5"><h2>Mode 5</h2></a>
<p>TODO</p>
<a class="header" href="#in-conclusion-1" id="in-conclusion-1"><h2>In Conclusion...</h2></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

19
examples/hello1.rs Normal file
View file

@ -0,0 +1,19 @@
#![feature(start)]
#![no_std]
#[cfg(not(test))]
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
loop {}
}
#[start]
fn main(_argc: isize, _argv: *const *const u8) -> isize {
unsafe {
(0x04000000 as *mut u16).write_volatile(0x0403);
(0x06000000 as *mut u16).offset(120 + 80 * 240).write_volatile(0x001F);
(0x06000000 as *mut u16).offset(136 + 80 * 240).write_volatile(0x03E0);
(0x06000000 as *mut u16).offset(120 + 96 * 240).write_volatile(0x7C00);
loop {}
}
}

63
linker.ld Normal file
View file

@ -0,0 +1,63 @@
ENTRY(__start)
MEMORY {
ewram (w!x) : ORIGIN = 0x2000000, LENGTH = 256K
iwram (w!x) : ORIGIN = 0x3000000, LENGTH = 32K
rom (rx) : ORIGIN = 0x8000000, LENGTH = 32M
}
SECTIONS {
.text : {
KEEP(crt0.o(.text));
*(.text .text.*);
. = ALIGN(4);
} >rom = 0xff
.rodata : {
*(.rodata .rodata.*);
. = ALIGN(4);
} >rom = 0xff
__data_lma = .;
.data : {
__data_start = ABSOLUTE(.);
*(.data .data.*);
. = ALIGN(4);
__data_end = ABSOLUTE(.);
} >iwram AT>rom = 0xff
/* debugging sections */
/* Stabs */
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.stab.excl 0 : { *(.stab.excl) }
.stab.exclstr 0 : { *(.stab.exclstr) }
.stab.index 0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
.comment 0 : { *(.comment) }
/* DWARF 1 */
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
/* GNU DWARF 1 extensions */
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
/* DWARF 1.1 and DWARF 2 */
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
/* DWARF 2 */
.debug_info 0 : { *(.debug_info) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line) }
.debug_frame 0 : { *(.debug_frame) }
.debug_str 0 : { *(.debug_str) }
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
/* SGI/MIPS DWARF 2 extensions */
.debug_weaknames 0 : { *(.debug_weaknames) }
.debug_funcnames 0 : { *(.debug_funcnames) }
.debug_typenames 0 : { *(.debug_typenames) }
.debug_varnames 0 : { *(.debug_varnames) }
/* discard anything not already mentioned */
/DISCARD/ : { *(*) }
}

View file

@ -15,6 +15,8 @@
// TODO(lokathor): IO Register newtypes.
use gba_proc_macro::register_bit;
use super::*;
/// LCD Control. Read/Write.
@ -30,18 +32,6 @@ pub struct DisplayControlSetting(u16);
#[allow(missing_docs)]
impl DisplayControlSetting {
pub const BG_MODE_MASK: u16 = 0b111;
pub const PAGE_SELECT_BIT: u16 = 0b1_0000;
pub const HBLANK_INTERVAL_FREE_BIT: u16 = 0b10_0000;
pub const OBJ_1D_BIT: u16 = 0b100_0000;
pub const FORCE_BLANK_BIT: u16 = 0b1000_0000;
pub const DISPLAY_BG0_BIT: u16 = 0b1_0000_0000;
pub const DISPLAY_BG1_BIT: u16 = 0b10_0000_0000;
pub const DISPLAY_BG2_BIT: u16 = 0b100_0000_0000;
pub const DISPLAY_BG3_BIT: u16 = 0b1000_0000_0000;
pub const DISPLAY_OBJ_BIT: u16 = 0b1_0000_0000_0000;
pub const DISPLAY_WINDOW0_BIT: u16 = 0b10_0000_0000_0000;
pub const DISPLAY_WINDOW1_BIT: u16 = 0b100_0000_0000_0000;
pub const OBJ_WINDOW_BIT: u16 = 0b1000_0000_0000_0000;
pub fn mode(&self) -> DisplayControlMode {
match self.0 & Self::BG_MODE_MASK {
@ -66,137 +56,19 @@ impl DisplayControlSetting {
};
}
pub fn uses_page_1(&self) -> bool {
(self.0 & Self::PAGE_SELECT_BIT) != 0
}
pub fn set_uses_page_1(&mut self, bit: bool) {
if bit {
self.0 |= Self::PAGE_SELECT_BIT;
} else {
self.0 &= !Self::PAGE_SELECT_BIT;
}
}
pub fn hblank_interval_free(&self) -> bool {
(self.0 & Self::HBLANK_INTERVAL_FREE_BIT) != 0
}
pub fn set_hblank_interval_free(&mut self, bit: bool) {
if bit {
self.0 |= Self::HBLANK_INTERVAL_FREE_BIT;
} else {
self.0 &= !Self::HBLANK_INTERVAL_FREE_BIT;
}
}
pub fn object_memory_1d(&self) -> bool {
(self.0 & Self::OBJ_1D_BIT) != 0
}
pub fn set_object_memory_1d(&mut self, bit: bool) {
if bit {
self.0 |= Self::OBJ_1D_BIT;
} else {
self.0 &= !Self::OBJ_1D_BIT;
}
}
pub fn force_blank(&self) -> bool {
(self.0 & Self::FORCE_BLANK_BIT) != 0
}
pub fn set_force_blank(&mut self, bit: bool) {
if bit {
self.0 |= Self::FORCE_BLANK_BIT;
} else {
self.0 &= !Self::FORCE_BLANK_BIT;
}
}
pub fn display_bg0(&self) -> bool {
(self.0 & Self::DISPLAY_BG0_BIT) != 0
}
pub fn set_display_bg0(&mut self, bit: bool) {
if bit {
self.0 |= Self::DISPLAY_BG0_BIT;
} else {
self.0 &= !Self::DISPLAY_BG0_BIT;
}
}
pub fn display_bg1(&self) -> bool {
(self.0 & Self::DISPLAY_BG1_BIT) != 0
}
pub fn set_display_bg1(&mut self, bit: bool) {
if bit {
self.0 |= Self::DISPLAY_BG1_BIT;
} else {
self.0 &= !Self::DISPLAY_BG1_BIT;
}
}
pub fn display_bg2(&self) -> bool {
(self.0 & Self::DISPLAY_BG2_BIT) != 0
}
pub fn set_display_bg2(&mut self, bit: bool) {
if bit {
self.0 |= Self::DISPLAY_BG2_BIT;
} else {
self.0 &= !Self::DISPLAY_BG2_BIT;
}
}
pub fn display_bg3(&self) -> bool {
(self.0 & Self::DISPLAY_BG3_BIT) != 0
}
pub fn set_display_bg3(&mut self, bit: bool) {
if bit {
self.0 |= Self::DISPLAY_BG3_BIT;
} else {
self.0 &= !Self::DISPLAY_BG3_BIT;
}
}
pub fn display_object(&self) -> bool {
(self.0 & Self::DISPLAY_OBJ_BIT) != 0
}
pub fn set_display_object(&mut self, bit: bool) {
if bit {
self.0 |= Self::DISPLAY_OBJ_BIT;
} else {
self.0 &= !Self::DISPLAY_OBJ_BIT;
}
}
pub fn display_window0(&self) -> bool {
(self.0 & Self::DISPLAY_WINDOW0_BIT) != 0
}
pub fn set_display_window0(&mut self, bit: bool) {
if bit {
self.0 |= Self::DISPLAY_WINDOW0_BIT;
} else {
self.0 &= !Self::DISPLAY_WINDOW0_BIT;
}
}
pub fn display_window1(&self) -> bool {
(self.0 & Self::DISPLAY_WINDOW1_BIT) != 0
}
pub fn set_display_window1(&mut self, bit: bool) {
if bit {
self.0 |= Self::DISPLAY_WINDOW1_BIT;
} else {
self.0 &= !Self::DISPLAY_WINDOW1_BIT;
}
}
pub fn display_object_window(&self) -> bool {
(self.0 & Self::OBJ_WINDOW_BIT) != 0
}
pub fn set_display_object_window(&mut self, bit: bool) {
if bit {
self.0 |= Self::OBJ_WINDOW_BIT;
} else {
self.0 &= !Self::OBJ_WINDOW_BIT;
}
}
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);
}
/// The six display modes available on the GBA.

View file

@ -1,16 +1,31 @@
use super::*;
//! Module for all things relating to the Video RAM.
//!
//! Note that that GBA has six different display modes available, and the
//! _meaning_ of Video RAM depends on which display mode is active. In all
//! cases, Video RAM is **96kb** from `0x0600_0000` to `0x0601_7FFF`.
//!
//! # Safety
//!
//! Note that all possible bit patterns are technically allowed within Video
//! Memory. If you write the "wrong" thing into video memory you don't crash the
//! GBA, instead you just get graphical glitches (or perhaps nothing at all).
//! Accordingly, the "safe" functions here will check that you're in bounds, but
//! they won't bother to check that you've set the video mode they're designed
//! for.
/// The width of the GBA screen.
//use super::*;
/// The physical width in pixels of the GBA screen.
pub const SCREEN_WIDTH: isize = 240;
/// The height of the GBA screen.
/// The physical height in pixels of the GBA screen.
pub const SCREEN_HEIGHT: isize = 160;
/// The start of VRAM.
///
/// Depending on what display mode is currently set there's different ways that
/// your program should interpret the VRAM space. Accordingly, we give the raw
/// value as just being a usize.
/// value as just being a `usize`.
pub const VRAM_BASE_ADDRESS: usize = 0x0600_0000;
/// Draws a pixel to the screen while in Display Mode 3.
@ -24,6 +39,13 @@ pub const VRAM_BASE_ADDRESS: usize = 0x0600_0000;
///
/// * `col` must be in `0..SCREEN_WIDTH`
/// * `row` must be in `0..SCREEN_HEIGHT`
pub unsafe fn mode3_plot(col: isize, row: isize, color: u16) {
pub unsafe fn mode3_plot_unchecked(col: isize, row: isize, color: u16) {
core::ptr::write_volatile((VRAM_BASE_ADDRESS as *mut u16).offset(col + row * SCREEN_WIDTH), color);
}
/// Draws a pixel to the screen while in Display Mode 3, with bounds checks.
pub fn mode3_plot(col: isize, row: isize, color: u16) {
assert!(col >= 0 && col < SCREEN_WIDTH);
assert!(row >= 0 && row < SCREEN_HEIGHT);
unsafe { mode3_plot_unchecked(col, row, color) }
}