gba/docs/print.html
2018-11-18 22:19:13 -07:00

1578 lines
87 KiB
HTML

<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Rust GBA Guide</title>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff" />
<link rel="shortcut icon" href="favicon.png">
<link rel="stylesheet" href="css/variables.css">
<link rel="stylesheet" href="css/general.css">
<link rel="stylesheet" href="css/chrome.css">
<link rel="stylesheet" href="css/print.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800" rel="stylesheet" type="text/css">
<link href="https://fonts.googleapis.com/css?family=Source+Code+Pro:500" rel="stylesheet" type="text/css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" href="highlight.css">
<link rel="stylesheet" href="tomorrow-night.css">
<link rel="stylesheet" href="ayu-highlight.css">
<!-- Custom theme stylesheets -->
</head>
<body class="light">
<!-- Provide site root to javascript -->
<script type="text/javascript">var path_to_root = "";</script>
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script type="text/javascript">
try {
var theme = localStorage.getItem('mdbook-theme');
var sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script type="text/javascript">
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = 'light'; }
document.body.className = theme;
document.querySelector('html').className = theme + ' js';
</script>
<!-- Hide / unhide sidebar before it is displayed -->
<script type="text/javascript">
var html = document.querySelector('html');
var sidebar = 'hidden';
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
}
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<ol class="chapter"><li><a href="introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="ch00/index.html"><strong aria-hidden="true">2.</strong> Ch 0: Development Setup</a></li><li><a href="ch01/index.html"><strong aria-hidden="true">3.</strong> Ch 1: Hello GBA</a></li><li><ol class="section"><li><a href="ch01/hello1.html"><strong aria-hidden="true">3.1.</strong> hello1</a></li><li><a href="ch01/volatile.html"><strong aria-hidden="true">3.2.</strong> Volatile</a></li><li><a href="ch01/io_registers.html"><strong aria-hidden="true">3.3.</strong> IO Registers</a></li><li><a href="ch01/the_display_control_register.html"><strong aria-hidden="true">3.4.</strong> The Display Control Register</a></li><li><a href="ch01/video_memory_intro.html"><strong aria-hidden="true">3.5.</strong> Video Memory Intro</a></li><li><a href="ch01/hello2.html"><strong aria-hidden="true">3.6.</strong> hello2</a></li></ol></li><li><a href="ch02/index.html"><strong aria-hidden="true">4.</strong> Ch 2: User Input</a></li><li><ol class="section"><li><a href="ch02/the_key_input_register.html"><strong aria-hidden="true">4.1.</strong> The Key Input Register</a></li><li><a href="ch02/the_vcount_register.html"><strong aria-hidden="true">4.2.</strong> The VCount Register</a></li><li><a href="ch02/light_cycle.html"><strong aria-hidden="true">4.3.</strong> light_cycle</a></li></ol></li><li><a href="ch03/index.html"><strong aria-hidden="true">5.</strong> Ch 3: Memory and Objects</a></li><li><ol class="section"><li><a href="ch03/gba_memory.html"><strong aria-hidden="true">5.1.</strong> GBA Memory</a></li><li><a href="ch03/tiled_backgrounds.html"><strong aria-hidden="true">5.2.</strong> Tiled Backgrounds</a></li><li><a href="ch03/object_basics.html"><strong aria-hidden="true">5.3.</strong> Object Basics</a></li><li><a href="ch03/gba_rng.html"><strong aria-hidden="true">5.4.</strong> GBA RNG</a></li><li><a href="ch03/memory_game.html"><strong aria-hidden="true">5.5.</strong> memory_game</a></li></ol></li></ol>
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar" class="menu-bar">
<div id="menu-bar-sticky-container">
<div class="left-buttons">
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</button>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="light">Light <span class="default">(default)</span></button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">Rust GBA Guide</h1>
<div class="right-buttons">
<a href="print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
</div>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" name="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script type="text/javascript">
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<a class="header" href="#introduction" id="introduction"><h1>Introduction</h1></a>
<p>Here's a book that'll help you program in Rust on the Game Boy Advance (GBA).</p>
<p>It's a work in progress of course, but so is most of everything in Rust.</p>
<a class="header" href="#style-and-purpose" id="style-and-purpose"><h2>Style and Purpose</h2></a>
<p>I'm out to teach you how to program in Rust on the GBA, obviously. However,
while there <em>is</em> a <a href="https://github.com/rust-console/gba">gba</a> crate, and while I
genuinely believe it to be a good and useful crate for GBA programming, we <em>will
not</em> be using the <code>gba</code> crate within this book. In fact we won't be using any
crates at all. We can call it the <a href="https://handmadehero.org/">Handmade Hero</a>
approach, if you like.</p>
<p>I don't want to just teach you how to use the <code>gba</code> crate, I want to teach you
what you'd need to know to write the crate from scratch if it wasn't there.</p>
<p>Each chapter of the book will focus on a few things you'll need to know about
GBA programming and then present a fully self-contained example that puts those
ideas into action. Just one file per example, no dependencies, no external
assets, no fuss. The examples will be in the text of the book within code
blocks, but also you can find them in the <a href="https://github.com/rust-console/gba/tree/master/examples">examples
directory</a> of the repo
if you want to get them that way.</p>
<a class="header" href="#expected-knowledge" id="expected-knowledge"><h2>Expected Knowledge</h2></a>
<p>I will try not to ask too much of the reader ahead of time, but you are expected
to have already read <a href="https://doc.rust-lang.org/book/">The Rust Book</a>.</p>
<p>It's very difficult to know when you've said something that someone else won't
already know about, or if you're presenting ideas out of order. If things aren't
clear please <a href="https://github.com/rust-console/gba/issues">file an issue</a> and
we'll try to address it.</p>
<a class="header" href="#getting-help" id="getting-help"><h2>Getting Help</h2></a>
<p>If you want to contact us you should join the <a href="https://discordapp.com/invite/aVESxV8">Rust Community
Discord</a> and ask in the <code>#gamedev</code>
channel.</p>
<ul>
<li><code>Ketsuban</code> is the wizard who knows much more about how it all works</li>
<li><code>Lokathor</code> is the fool who decided to write a crate and book for it.</li>
</ul>
<p>If it's <em>not</em> a GBA specific question then you can probably ask any of the other
folks in the server as well (there's a few hundred folks).</p>
<a class="header" href="#further-reading" id="further-reading"><h2>Further Reading</h2></a>
<p>If you want to read more about developing on the GBA there are some other good
resources as well:</p>
<ul>
<li><a href="https://www.coranac.com/tonc/text/toc.htm">Tonc</a>, a tutorial series written
for C, but it's what I based the ordering of this book's sections on.</li>
<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>
<li><a href="https://www.cs.rit.edu/%7Etjh8300/CowBite/CowBiteSpec.htm">CowBite</a> is another
tech specification that's more GBA specific. It's sometimes got more ASCII
art diagrams and example C struct layouts than GBATEK does.</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>
<p>Once again, extra special thanks to <strong>Ketsuban</strong>, who first dove into how to
make this all work with rust and then shared it with the world.</p>
<a class="header" href="#per-system-setup" id="per-system-setup"><h2>Per System Setup</h2></a>
<p>Obviously you need your computer to have a <a href="https://rustup.rs/">working rust
installation</a>. However, you'll also need to ensure that
you're using a nightly toolchain (we will need it for inline assembly, among
other potential useful features). You can run <code>rustup default nightly</code> to set
nightly as the system wide default toolchain, or you can use a <a href="https://github.com/rust-lang-nursery/rustup.rs#the-toolchain-file">toolchain
file</a> to use
nightly just on a specific project, but either way we'll be assuming the use of
nightly from now on. You'll also need the <code>rust-src</code> component so that
<code>cargo-xbuild</code> will be able to compile the core crate for us in a bit, so run
<code>rustup component add rust-src</code>.</p>
<p>Next, you need <a href="https://devkitpro.org/wiki/Getting_Started">devkitpro</a>. They've
got a graphical installer for Windows that runs nicely, and I guess <code>pacman</code>
support on Linux (I'm on Windows so I haven't tried the Linux install myself).
We'll be using a few of their general binutils for the <code>arm-none-eabi</code> target,
and we'll also be using some of their tools that are specific to GBA
development, so <em>even if</em> you already have the right binutils for whatever
reason, you'll still want devkitpro for the <code>gbafix</code> utility.</p>
<ul>
<li>On Windows you'll want something like <code>C:\devkitpro\devkitARM\bin</code> and
<code>C:\devkitpro\tools\bin</code> to be <a href="https://stackoverflow.com/q/44272416/455232">added to your
PATH</a>, depending on where you
installed it to and such.</li>
<li>On Linux you'll also want it to be added to your path, but if you're using
Linux I'll just assume you know how to do all that. I'm told that the default
installation path is <code>/opt/devkitpro/devkitARM/bin</code>, so look there first if
you didn't select some other place.</li>
</ul>
<p>Finally, you'll need <code>cargo-xbuild</code>. Just run <code>cargo install cargo-xbuild</code> and
cargo will figure it all out for you.</p>
<a class="header" href="#per-project-setup" id="per-project-setup"><h2>Per Project Setup</h2></a>
<p>Once the system wide tools are ready, you'll need some particular files each
time you want to start a new project. You can find them in the root of the
<a href="https://github.com/rust-console/gba">rust-console/gba repo</a>.</p>
<ul>
<li><code>thumbv4-none-agb.json</code> describes the overall GBA to cargo-xbuild (and LLVM)
so it knows what to do. Technically the GBA is <code>thumbv4-none-eabi</code>, but we
change the <code>eabi</code> to <code>agb</code> so that we can distinguish it from other <code>eabi</code>
devices when using <code>cfg</code> flags.</li>
<li><code>crt0.s</code> describes some ASM startup stuff. If you have more ASM to place here
later on this is where you can put it. You also need to build it into a
<code>crt0.o</code> file before it can actually be used, but we'll cover that below.</li>
<li><code>linker.ld</code> tells the linker all the critical info about the layout
expectations that the GBA has about our program, and that it should also
include the <code>crt0.o</code> file with our compiled rust code.</li>
</ul>
<a class="header" href="#compiling" id="compiling"><h2>Compiling</h2></a>
<p>The next steps only work once you've got some source code to build. If you need
a quick test, copy the <code>hello1.rs</code> file from our examples directory in the
repository.</p>
<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-agb.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. If you want you can still run some of your
project's tests with <code>cargo test --lib</code> or similar, but that builds for your
local machine, so anything specific to the GBA (such as reading and writing
registers) won't be testable that way. If you want to isolate and try out
some piece code running on the GBA you'll unfortunately have to make a demo
for it in your <code>examples/</code> directory and then run the demo in an emulator
and see if it does what you expect.</li>
<li>The file extension is important. <code>cargo xbuild</code> takes it as a flag to
compile dependencies with the same sysroot, so you can include crates
normally. Well, creates that work in the GBA's limited environment, but you
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.7 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-agb/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-agb/</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.
On our own project we have it mostly set up within a <code>Makefile.toml</code> which runs
using the <a href="https://github.com/sagiegurari/cargo-make">cargo-make</a> plugin. It's
not really the best plugin, but it's what's available.</p>
<a class="header" href="#ch-1-hello-gba" id="ch-1-hello-gba"><h1>Ch 1: Hello GBA</h1></a>
<p>Traditionally a person writes a &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>Our first example will be a totally minimal, full magic number crazy town.
Ready? Here goes:</p>
<p><code>hello1.rs</code></p>
<pre><pre class="playpen"><code class="language-rust">#![feature(start)]
#![no_std]
#[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 skeleton, build the program (as described back in
Chapter 0), and give it a run in your emulator. You should see a red, green, and
blue dot close-ish to the middle of the screen. If you don't, something already
went wrong. Double check things, phone a friend, write your senators, try asking
Ketsuban on the <a href="https://discordapp.com/invite/aVESxV8">Rust Community Discord</a>,
until you're able to get your three dots going.</p>
<a class="header" href="#a-basic-hello1-explanation" id="a-basic-hello1-explanation"><h2>A basic hello1 explanation</h2></a>
<p>So, what just happened? Even if you're used to Rust that might look pretty
strange. We'll go over most of the little parts right here, and then bigger
parts will get their own sections.</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#![feature(start)]
#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() {
#[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>
<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 it's not like a <code>main</code> that you'd
see in a Rust program. It's <em>more like</em> the sort of <code>main</code> that you'd see in a C
program, but it's still <strong>not</strong> that either. If you compile a <code>#[start]</code> program
for a target with an OS such as <code>arm-none-eabi-nm</code> you can open up the debug
info and see that your result will have the symbol for the C <code>main</code> along side
the symbol for the start <code>main</code> that we write here. Our start <code>main</code> is just its
own unique thing, and the inputs and outputs have to be like that because that's
how <code>#[start]</code> is specified to work in Rust.</p>
<p>If you think about it for a moment you'll probably realize that, those inputs
and outputs are totally useless to us on a GBA. There's no OS on the GBA to call
our program, and there's no place for our program to &quot;return to&quot; when it's done.</p>
<p>Side note: if you want to learn more about stuff &quot;before main gets called&quot; you
can watch a great <a href="https://www.youtube.com/watch?v=dOfucXtyEsU">CppCon talk</a> by
Matt Godbolt (yes, that Godbolt) where he delves into quite a bit of it. The
talk doesn't really apply to the GBA, but it's pretty good.</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 magic locations in the Video RAM. Somehow that shows three
dots. Gotta read on to find out why!</p>
<a class="header" href="#volatile" id="volatile"><h1>Volatile</h1></a>
<p>Before we focus on what the numbers mean, first let's ask ourselves: Why are we
doing <em>volatile</em> writes? You've probably never used that keywords before at all.
What <em>is</em> volatile anyway?</p>
<p>Well, the optimizer is pretty aggressive, and so it'll skip reads and writes
when it thinks can. Like if you write to a pointer once, and then again a moment
later, and it didn't see any other reads in between, it'll think that it can
just skip doing that first write since it'll get overwritten anyway. Sometimes
that's correct, but sometimes it's not.</p>
<p>Marking a read or write as <em>volatile</em> tells the compiler that it really must do
that action, and in the exact order that we wrote it out. It says that there
might even be special hardware side effects going on that the compiler isn't
aware of. In this case, the write to the display control register sets a video
mode, and the writes to the Video RAM set pixels that will show up on the
screen.</p>
<p>Similar to &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.write_volatile(5);
a += b;
d.write_volatile(7);
#}</code></pre></pre>
<p>might end up changing <code>a</code> either before or after the change to <code>c</code> (since the
value of <code>a</code> doesn't affect the write to <code>c</code>), but the write to <code>d</code> will
<em>always</em> happen after the write to <code>c</code>, even though the compiler doesn't see any
direct data dependency there.</p>
<p>If you ever go on to use volatile stuff on other platforms it's important to
note that volatile doesn't make things thread-safe, you still need atomic for
that. However, the GBA doesn't have threads, so we don't have to worry about
those sorts of thread safety concerns (there's interrupts, but that's another
matter).</p>
<a class="header" href="#volatile-by-default" id="volatile-by-default"><h2>Volatile by default</h2></a>
<p>Of course, writing out <code>volatile_write</code> every time is more than we wanna do.
There's clarity and then there's excessive. This is a chance to write our first
<a href="https://doc.rust-lang.org/1.0.0/style/features/types/newtype.html">newtype</a>.
Basically a type that's got the exact same binary representation as some other
type, but new methods and trait implementations.</p>
<p>We want a <code>*mut T</code> that's volatile by default, and also when we offset it...
well the verdict is slightly unclear on how <code>offset</code> vs <code>wrapping_offset</code> work
when you're using pointers that you made up out of nowhere. I've asked the
experts and they genuinely weren't sure, so we'll make an <code>offset</code> method that
does a <code>wrapping_offset</code> just to be careful.</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct VolatilePtr&lt;T&gt;(pub *mut T);
impl&lt;T&gt; VolatilePtr&lt;T&gt; {
pub unsafe fn read(&amp;self) -&gt; T {
core::ptr::read_volatile(self.0)
}
pub unsafe fn write(&amp;self, data: T) {
core::ptr::write_volatile(self.0, data);
}
pub unsafe fn offset(self, count: isize) -&gt; Self {
VolatilePtr(self.0.wrapping_offset(count))
}
}
#}</code></pre></pre>
<a class="header" href="#io-registers" id="io-registers"><h1>IO Registers</h1></a>
<p>The GBA has a large number of <strong>IO Registers</strong> (not to be confused with CPU
registers). These are special memory locations from <code>0x04000000</code> to
<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-register" id="the-display-control-register"><h1>The Display Control Register</h1></a>
<p>The display control register 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 register holds a <code>u16</code> value, and is located at <code>0x0400_0000</code>.</p>
<p>Many of the bits here won't mean much to you right now. <strong>That is fine.</strong> You do
NOT need to memorize them all or what they all do right away. We'll just skim
over all the parts of this register to start, and then we'll go into more detail
in later chapters when we need to come back and use more of the bits.</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) RGB15 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 stored within a <code>u16</code> value, and the highest bit is
simply 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 effectively read only. Technically it can be flipped using a BIOS call,
but when you write to the display control register normally it won't write to
this bit, so we'll call it effectively read only.</p>
<p>This bit is on if the CPU is in CGB mode.</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). This particular control can be kinda tricky to wrap your head
around, so we'll be sure to have some extra diagrams in the chapter that deals
with it.</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 register 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;, though sometimes I forget and don't capitalize it properly). 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>As I said before, RGB15 stores a color within a <code>u16</code> value using 5 bits for
each color channel.</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
pub const RED: u16 = 0b0_00000_00000_11111;
pub const GREEN: u16 = 0b0_00000_11111_00000;
pub const BLUE: u16 = 0b0_11111_00000_00000;
#}</code></pre></pre>
<p>In Mode 3 and Mode 5 we write direct color values into VRAM, and in Mode 4 we
write palette index values, and then the color values go into the PALRAM.</p>
<a class="header" href="#mode-3" id="mode-3"><h2>Mode 3</h2></a>
<p>Mode 3 is pretty easy. We have a full resolution grid of rgb15 pixels. There's
160 rows of 240 pixels each, with the base address being the top left corner. A
particular pixel uses normal &quot;2d indexing&quot; math:</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
let row_five_col_seven = 5 + (7 * SCREEN_WIDTH);
#}</code></pre></pre>
<p>To draw a pixel, we just write a value at the address for the row and col that
we want to draw to.</p>
<a class="header" href="#mode-4" id="mode-4"><h2>Mode 4</h2></a>
<p>Mode 4 introduces page flipping. Instead of one giant page at <code>0x0600_0000</code>,
there's Page 0 at <code>0x0600_0000</code> and then Page 1 at <code>0x0600_A000</code>. The resolution
for each page is the same as above, but instead of writing <code>u16</code> values, the
memory is treated as <code>u8</code> indexes into PALRAM. The PALRAM starts at
<code>0x0500_0000</code>, and there's enough space for 256 palette entries (each a <code>u16</code>).</p>
<p>To set the color of a palette entry we just do a normal <code>u16</code> write_volatile.</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
(0x0500_0000 as *mut u16).offset(target_index).write_volatile(new_color)
#}</code></pre></pre>
<p>To draw a pixel we set the palette entry that we want the pixel to use. However,
we must remember the &quot;minimum size&quot; write limitation that applies to VRAM. So,
if we want to change just a single pixel at a time we must</p>
<ol>
<li>Read the full <code>u16</code> it's a part of.</li>
<li>Clear the half of the <code>u16</code> we're going to replace</li>
<li>Write the half of the <code>u16</code> we're going to replace with the new value</li>
<li>Write that result back to the address.</li>
</ol>
<p>So, the math for finding a byte offset is the same as Mode 3 (since they're both
a 2d grid). If the byte offset is EVEN it'll be the high bits of the <code>u16</code> at
half the byte offset rounded down. If the offset is ODD it'll be the low bits of
the <code>u16</code> at half the byte.</p>
<p>Does that make sense?</p>
<ul>
<li>If we want to write pixel (0,0) the byte offset is 0, so we change the high
bits of <code>u16</code> offset 0. Then we want to write to (1,0), so the byte offset is
1, so we change the low bits of <code>u16</code> offset 0. The pixels are next to each
other, and the target bytes are next to each other, good so far.</li>
<li>If we want to write to (5,6) that'd be byte <code>5 + 6 * 240 = 1445</code>, so we'd
target the low bits of <code>u16</code> offset <code>floor(1445/2) = 722</code>.</li>
</ul>
<p>As you can see, trying to write individual pixels in Mode 4 is mostly a bad
time. Fret not! We don't <em>have</em> to write individual bytes. If our data is
arranged correctly ahead of time we can just write <code>u16</code> or <code>u32</code> values
directly. The video hardware doesn't care, it'll get along just fine.</p>
<a class="header" href="#mode-5" id="mode-5"><h2>Mode 5</h2></a>
<p>Mode 5 is also a two page mode, but instead of compressing the size of a pixel's
data to fit in two pages, we compress the resolution.</p>
<p>Mode 5 is full <code>u16</code> color, but only 160w x 128h per page.</p>
<a class="header" href="#in-conclusion-1" id="in-conclusion-1"><h2>In Conclusion...</h2></a>
<p>So what got written into VRAM in <code>hello1</code>?</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>So at pixels <code>(120,80)</code>, <code>(136,80)</code>, and <code>(120,96)</code> we write three values. Once
again we probably need to <a href="https://www.wolframalpha.com/">convert them</a> into
binary to make sense of it.</p>
<ul>
<li>0x001F: 0b0_00000_00000_11111</li>
<li>0x03E0: 0b0_00000_11111_00000</li>
<li>0x7C00: 0b0_11111_00000_00000</li>
</ul>
<p>Ah, of course, a red pixel, a green pixel, and a blue pixel.</p>
<a class="header" href="#hello2" id="hello2"><h1>hello2</h1></a>
<p>Okay so let's have a look again:</p>
<p><code>hello1</code></p>
<pre><pre class="playpen"><code class="language-rust">#![feature(start)]
#![no_std]
#[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>Now let's clean this up so that it's clearer what's going on.</p>
<p>First we'll label that display control stuff, including using the <code>VolatilePtr</code>
type from the volatile explanation:</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
pub const DISPCNT: VolatilePtr&lt;u16&gt; = VolatilePtr(0x04000000 as *mut u16);
pub const MODE3: u16 = 3;
pub const BG2: u16 = 0b100_0000_0000;
#}</code></pre></pre>
<p>Next we make some const values for the actual pixel drawing</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
pub const VRAM: usize = 0x06000000;
pub const SCREEN_WIDTH: isize = 240;
#}</code></pre></pre>
<p>Note that VRAM has to be interpreted in different ways depending on mode, so we
just leave it as <code>usize</code> and we'll cast it into the right form closer to the
actual use.</p>
<p>Next we want a small helper function for putting together a color value.
Happily, this one can even be declared as a <code>const</code> function. At the time of
writing, we've got the &quot;minimal const fn&quot; support in nightly. It really is quite
limited, but I'm happy to let rustc and LLVM pre-compute as much as they can
when it comes to the GBA's tiny CPU.</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
pub const fn rgb16(red: u16, green: u16, blue: u16) -&gt; u16 {
blue &lt;&lt; 10 | green &lt;&lt; 5 | red
}
#}</code></pre></pre>
<p>Finally, we'll make a function for drawing a pixel in Mode 3. Even though it's
just a one-liner, having the &quot;important parts&quot; be labeled as function arguments
usually helps you think about it a lot better.</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
pub unsafe fn mode3_pixel(col: isize, row: isize, color: u16) {
VolatilePtr(VRAM as *mut u16).offset(col + row * SCREEN_WIDTH).write(color);
}
#}</code></pre></pre>
<p>So now we've got this:</p>
<p><code>hello2</code></p>
<pre><pre class="playpen"><code class="language-rust">#![feature(start)]
#![no_std]
#[panic_handler]
fn panic(_info: &amp;core::panic::PanicInfo) -&gt; ! {
loop {}
}
#[start]
fn main(_argc: isize, _argv: *const *const u8) -&gt; isize {
unsafe {
DISPCNT.write(MODE3 | BG2);
mode3_pixel(120, 80, rgb16(31, 0, 0));
mode3_pixel(136, 80, rgb16(0, 31, 0));
mode3_pixel(120, 96, rgb16(0, 0, 31));
loop {}
}
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct VolatilePtr&lt;T&gt;(pub *mut T);
impl&lt;T&gt; VolatilePtr&lt;T&gt; {
pub unsafe fn read(&amp;self) -&gt; T {
core::ptr::read_volatile(self.0)
}
pub unsafe fn write(&amp;self, data: T) {
core::ptr::write_volatile(self.0, data);
}
pub unsafe fn offset(self, count: isize) -&gt; Self {
VolatilePtr(self.0.wrapping_offset(count))
}
}
pub const DISPCNT: VolatilePtr&lt;u16&gt; = VolatilePtr(0x04000000 as *mut u16);
pub const MODE3: u16 = 3;
pub const BG2: u16 = 0b100_0000_0000;
pub const VRAM: usize = 0x06000000;
pub const SCREEN_WIDTH: isize = 240;
pub const fn rgb16(red: u16, green: u16, blue: u16) -&gt; u16 {
blue &lt;&lt; 10 | green &lt;&lt; 5 | red
}
pub unsafe fn mode3_pixel(col: isize, row: isize, color: u16) {
VolatilePtr(VRAM as *mut u16).offset(col + row * SCREEN_WIDTH).write(color);
}
</code></pre></pre>
<p>Exact same program that we started with, but much easier to read.</p>
<p>Of course, in the full <code>gba</code> crate that this book is a part of we have these and
other elements all labeled and sorted out for you (not identically, but
similarly). Still, for educational purposes it's often best to do it yourself at
least once.</p>
<a class="header" href="#ch-2-user-input" id="ch-2-user-input"><h1>Ch 2: User Input</h1></a>
<p>It's all well and good to draw three pixels, but they don't do anything yet. We
want them to do something, and for that we need to get some input from the user.</p>
<p>The GBA, as I'm sure you know, has an arrow pad, A and B, L and R, Start and
Select. That's a little more than the NES/GB/CGB had, and a little less than the
SNES had. As you can guess, we get key state info from an IO register.</p>
<p>Also, we will need a way to keep the program from running &quot;too fast&quot;. On a
modern computer or console you do this with vsync info from the GPU and Monitor,
and on the GBA we'll be using vsync info from an IO register that tracks what
the display hardware is doing.</p>
<p>As a way to apply our knowledge We'll make a simple &quot;light cycle&quot; game where
your dot leaves a trail behind them and you die if you go off the screen or if
you touch your own trail. We just make a copy of <code>hello2.rs</code> named
<code>light_cycle.rs</code> and then fill it in as we go through the chapter. Normally you
might not place the entire program into a single source file, particularly as it
grows over time, but since these are small examples it's much better to have
them be completely self contained than it is to have them be &quot;properly
organized&quot; for the long term.</p>
<a class="header" href="#the-key-input-register" id="the-key-input-register"><h1>The Key Input Register</h1></a>
<p>The Key Input Register is our next IO register. Its shorthand name is
<a href="http://problemkaputt.de/gbatek.htm#gbakeypadinput">KEYINPUT</a> and it's a <code>u16</code>
at <code>0x4000130</code>. The entire register is obviously read only, you can't tell the
GBA what buttons are pressed.</p>
<p>Each button is exactly one bit:</p>
<table><thead><tr><th align="center"> Bit </th><th align="center"> Button </th></tr></thead><tbody>
<tr><td align="center"> 0 </td><td align="center"> A </td></tr>
<tr><td align="center"> 1 </td><td align="center"> B </td></tr>
<tr><td align="center"> 2 </td><td align="center"> Select </td></tr>
<tr><td align="center"> 3 </td><td align="center"> Start </td></tr>
<tr><td align="center"> 4 </td><td align="center"> Right </td></tr>
<tr><td align="center"> 5 </td><td align="center"> Left </td></tr>
<tr><td align="center"> 6 </td><td align="center"> Up </td></tr>
<tr><td align="center"> 7 </td><td align="center"> Down </td></tr>
<tr><td align="center"> 8 </td><td align="center"> R </td></tr>
<tr><td align="center"> 9 </td><td align="center"> L </td></tr>
</tbody></table>
<p>The higher bits above are not used at all.</p>
<p>Similar to other old hardware devices, the convention here is that a button's
bit is <strong>clear when pressed, active when released</strong>. In other words, when the
user is not touching the device at all the KEYINPUT value will read
<code>0b0000_0011_1111_1111</code>. There's similar values for when the user is pressing as
many buttons as possible, but since the left/right and up/down keys are on an
arrow pad the value can never be 0 since you can't ever press every single key
at once.</p>
<p>When dealing with key input, the register always shows the exact key values at
any moment you read it. Obviously that's what it should do, but what it means to
you as a programmer is that you should usually gather input once at the top of a
game frame and then use that single input poll as the input values across the
whole game frame.</p>
<p>Of course, you might want to know if a user's key state changed from frame to
frame. That's fairly easy too: We just store the last frame keys as well as the
current frame keys (it's only a <code>u16</code>) and then we can xor the two values.
Anything that shows up in the xor result is a key that changed. If it's changed
and it's now down, that means it was pushed this frame. If it's changed and it's
now up, that means it was released this frame.</p>
<p>The other major thing you might frequently want is to know &quot;which way&quot; the arrow
pad is pointing: Up/Down/None and Left/Right/None. Sounds like an enum to me.
Except that often time we'll have situations where the direction just needs to
be multiplied by a speed and applied as a delta to a position. We want to
support that as well as we can too.</p>
<a class="header" href="#key-input-code" id="key-input-code"><h2>Key Input Code</h2></a>
<p>Let's get down to some code. First we want to make a way to read the address as
a <code>u16</code> and then wrap that in our newtype which will implement methods for
reading and writing the key bits.</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
pub const KEYINPUT: VolatilePtr&lt;u16&gt; = VolatilePtr(0x400_0130 as *mut u16);
/// A newtype over the key input state of the GBA.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
#[repr(transparent)]
pub struct KeyInputSetting(u16);
pub fn key_input() -&gt; KeyInputSetting {
unsafe { KeyInputSetting(KEYINPUT.read()) }
}
#}</code></pre></pre>
<p>Now we want a way to check if a key is <em>being pressed</em>, since that's normally
how we think of things as a game designer and even as a player. That is, usually
you'd say &quot;if you press A, then X happens&quot; instead of &quot;if you don't press A,
then X does not happen&quot;.</p>
<p>Normally we'd pick a constant for the bit we want, <code>&amp;</code> it with our value, and
then check for <code>val != 0</code>. Since the bit we're looking for is <code>0</code> in the &quot;true&quot;
state we still pick the same constant and we still do the <code>&amp;</code>, but we test with
<code>== 0</code>. Practically the same, right? Well, since I'm asking a rhetorical
question like that you can probably already guess that it's not the same. I was
shocked to learn this too.</p>
<p>All we have to do is ask our good friend
<a href="https://rust.godbolt.org/z/d-8oCe">Godbolt</a> what's gonna happen when the code
compiles. The link there has the page set for the <code>stable</code> 1.30 compiler just so
that the link results stay consistent if you read this book in a year or
something. Also, we've set the target to <code>thumbv6m-none-eabi</code>, which is a
slightly later version of ARM than the actual GBA, but it's close enough for
just checking. Of course, in a full program small functions like these will
probably get inlined into the calling code and disappear entirely as they're
folded and refolded by the compiler, but we can just check.</p>
<p>It turns out that the <code>!=0</code> test is 4 instructions and the <code>==0</code> test is 6
instructions. Since we want to get savings where we can, and we'll probably
check the keys of an input often enough, we'll just always use a <code>!=0</code> test and
then adjust how we initially read the register to compensate. By using xor with
a mask for only the 10 used bits we can flip the &quot;low when pressed&quot; values so
that the entire result has active bits in all positions where a key is pressed.</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
pub fn key_input() -&gt; KeyInputSetting {
unsafe { KeyInputSetting(KEYINPUT.read_volatile() ^ 0b0000_0011_1111_1111) }
}
#}</code></pre></pre>
<p>Now we add a method for seeing if a key is pressed. In the full library there's
a more advanced version of this that's built up via macro, but for this example
we'll just name a bunch of <code>const</code> values and then have a method that takes a
value and says if that bit is on.</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
pub const KEY_A: u16 = 1 &lt;&lt; 0;
pub const KEY_B: u16 = 1 &lt;&lt; 1;
pub const KEY_SELECT: u16 = 1 &lt;&lt; 2;
pub const KEY_START: u16 = 1 &lt;&lt; 3;
pub const KEY_RIGHT: u16 = 1 &lt;&lt; 4;
pub const KEY_LEFT: u16 = 1 &lt;&lt; 5;
pub const KEY_UP: u16 = 1 &lt;&lt; 6;
pub const KEY_DOWN: u16 = 1 &lt;&lt; 7;
pub const KEY_R: u16 = 1 &lt;&lt; 8;
pub const KEY_L: u16 = 1 &lt;&lt; 9;
impl KeyInputSetting {
pub fn contains(&amp;self, key: u16) -&gt; bool {
(self.0 &amp; key) != 0
}
}
#}</code></pre></pre>
<p>Because each key is a unique bit you can even check for more than one key at
once by just adding two key values together.</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
let input_contains_a_and_l = input.contains(KEY_A + KEY_L);
#}</code></pre></pre>
<p>And we wanted to save the state of an old frame and compare it to the current
frame to see what was different:</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
pub fn difference(&amp;self, other: KeyInputSetting) -&gt; KeyInputSetting {
KeyInputSetting(self.0 ^ other.0)
}
#}</code></pre></pre>
<p>Anything that's &quot;in&quot; the difference output is a key that <em>changed</em>, and then if
the key reads as pressed this frame that means it was just pressed. The exact
mechanics of all the ways you might care to do something based on new key
presses is obviously quite varied, but it might be something like this:</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
let this_frame_diff = this_frame_input.difference(last_frame_input);
if this_frame_diff.contains(KEY_B) &amp;&amp; this_frame_input.contains(KEY_B) {
// the user just pressed B, react in some way
}
#}</code></pre></pre>
<p>And for the arrow pad, we'll make an enum that easily casts into <code>i32</code>. Whenever
we're working with stuff we can try to use <code>i32</code> / <code>isize</code> as often as possible
just because it's easier on the GBA's CPU if we stick to its native number size.
Having it be an enum lets us use <code>match</code> and be sure that we've covered all our
cases.</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
/// A &quot;tribool&quot; value helps us interpret the arrow pad.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
#[repr(i32)]
pub enum TriBool {
Minus = -1,
Neutral = 0,
Plus = +1,
}
#}</code></pre></pre>
<p>Now, how do we determine <em>which way</em> is plus or minus? Well... I don't know.
Really. I'm not sure what the best one is because the GBA really wants the
origin at 0,0 with higher rows going down and higher cols going right. On the
other hand, all the normal math you and I learned in school is oriented with
increasing Y being upward on the page. So, at least for this demo, we're going
to go with what the GBA wants us to do and give it a try. If we don't end up
confusing ourselves then we can stick with that. Maybe we can cover it over
somehow later on.</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
pub fn column_direction(&amp;self) -&gt; TriBool {
if self.contains(KEY_RIGHT) {
TriBool::Plus
} else if self.contains(KEY_LEFT) {
TriBool::Minus
} else {
TriBool::Neutral
}
}
pub fn row_direction(&amp;self) -&gt; TriBool {
if self.contains(KEY_DOWN) {
TriBool::Plus
} else if self.contains(KEY_UP) {
TriBool::Minus
} else {
TriBool::Neutral
}
}
#}</code></pre></pre>
<p>So then in our game, every frame we can check for <code>column_direction</code> and
<code>row_direction</code> and then apply those to the player's current position to make
them move around the screen.</p>
<p>With that settled I think we're all done with user input for now. There's some
other things to eventually know about like key interrupts that you can set and
stuff, but we'll cover that later on because it's not necessary right now.</p>
<a class="header" href="#the-vcount-register" id="the-vcount-register"><h1>The VCount Register</h1></a>
<p>There's an IO register called
<a href="http://problemkaputt.de/gbatek.htm#lcdiointerruptsandstatus">VCOUNT</a> that shows
you, what else, the Vertical (row) COUNT(er). It's a <code>u16</code> at address
<code>0x0400_0006</code>, and it's how we'll be doing our very poor quality vertical sync
code to start.</p>
<ul>
<li><strong>What makes it poor?</strong> Well, we're just going to read from the vcount value as
often as possible every time we need to wait for a specific value to come up,
and then proceed once it hits the point we're looking for.</li>
<li><strong>Why is this bad?</strong> Because we're making the CPU do a lot of useless work,
which uses a lot more power that necessary. Even if you're not on an actual
GBA you might be running inside an emulator on a phone or other handheld. You
wanna try to save battery if all you're doing with that power use is waiting
instead of making a game actually do something.</li>
<li><strong>Can we do better?</strong> We can, but not yet. The better way to do things is to
use a BIOS call to put the CPU into low power mode until a VBlank interrupt
happens. However, we don't know about interrupts yet, and we don't know about
BIOS calls yet, so we'll do the basic thing for now and then upgrade later.</li>
</ul>
<p>So the way that display hardware actually displays each frame is that it moves a
tiny pointer left to right across each pixel row one pixel at a time. When it's
within the actual screen width (240px) it's drawing out those pixels. Then it
goes <em>past</em> the edge of the screen for 68px during a period known as the
&quot;horizontal blank&quot; (HBlank). Then it starts on the next row and does that loop
over again. This happens for the whole screen height (160px) and then once again
it goes past the last row for another 68px into a &quot;vertical blank&quot; (VBlank)
period.</p>
<ul>
<li>One pixel is 4 CPU cycles</li>
<li>HDraw is 240 pixels, HBlank is 68 pixels (1,232 cycles per full scanline)</li>
<li>VDraw is 150 scanlines, VBlank is 68 scanlines (280,896 cycles per full refresh)</li>
</ul>
<p>Now you may remember some stuff from the display control register section where
it was mentioned that some parts of memory are best accessed during VBlank, and
also during hblank with a setting applied. These blanking periods are what was
being talked about. At other times if you attempt to access video or object
memory you (the CPU) might try touching the same memory that the display device
is trying to use, in which case you get bumped back a cycle so that the display
can finish what it's doing. Also, if you really insist on doing video memory
changes while the screen is being drawn then you might get some visual glitches.
If you can, just prepare all your changes ahead of time and then assign then all
quickly during the blank period.</p>
<p>So first we want a way to check the vcount value at all:</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
pub const VCOUNT: VolatilePtr&lt;u16&gt; = VolatilePtr(0x0400_0006 as *mut u16);
pub fn vcount() -&gt; u16 {
unsafe { VCOUNT.read() }
}
#}</code></pre></pre>
<p>Then we want two little helper functions to wait until VBlank and vdraw.</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
pub const SCREEN_HEIGHT: isize = 160;
pub fn wait_until_vblank() {
while vcount() &lt; SCREEN_HEIGHT as u16 {}
}
pub fn wait_until_vdraw() {
while vcount() &gt;= SCREEN_HEIGHT as u16 {}
}
#}</code></pre></pre>
<p>And... that's it. No special types to be made this time around, it's just a
number we read out of memory.</p>
<a class="header" href="#light_cycle" id="light_cycle"><h1>light_cycle</h1></a>
<p>Now let's make a game of &quot;light_cycle&quot; with our new knowledge.</p>
<a class="header" href="#gameplay" id="gameplay"><h2>Gameplay</h2></a>
<p><code>light_cycle</code> is pretty simple, and very obvious if you've ever seen Tron. The
player moves around the screen with a trail left behind them. They die if they
go off the screen or if they touch their own trail.</p>
<a class="header" href="#operations" id="operations"><h2>Operations</h2></a>
<p>We need some better drawing operations this time around.</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
pub unsafe fn mode3_clear_screen(color: u16) {
let color = color as u32;
let bulk_color = color &lt;&lt; 16 | color;
let mut ptr = VolatilePtr(VRAM as *mut u32);
for _ in 0..SCREEN_HEIGHT {
for _ in 0..(SCREEN_WIDTH / 2) {
ptr.write(bulk_color);
ptr = ptr.offset(1);
}
}
}
pub unsafe fn mode3_draw_pixel(col: isize, row: isize, color: u16) {
VolatilePtr(VRAM as *mut u16).offset(col + row * SCREEN_WIDTH).write(color);
}
pub unsafe fn mode3_read_pixel(col: isize, row: isize) -&gt; u16 {
VolatilePtr(VRAM as *mut u16).offset(col + row * SCREEN_WIDTH).read()
}
#}</code></pre></pre>
<p>The draw pixel and read pixel are both pretty obvious. What's new is the clear
screen operation. It changes the <code>u16</code> color into a <code>u32</code> and then packs the
value in twice. Then we write out <code>u32</code> values the whole way through screen
memory. This means we have to do less write operations overall, and so the
screen clear is twice as fast.</p>
<p>Now we just have to fill in the main function:</p>
<pre><pre class="playpen"><code class="language-rust">#[start]
fn main(_argc: isize, _argv: *const *const u8) -&gt; isize {
unsafe {
DISPCNT.write(MODE3 | BG2);
}
let mut px = SCREEN_WIDTH / 2;
let mut py = SCREEN_HEIGHT / 2;
let mut color = rgb16(31, 0, 0);
loop {
// read the input for this frame
let this_frame_keys = key_input();
// adjust game state and wait for vblank
px += 2 * this_frame_keys.column_direction() as isize;
py += 2 * this_frame_keys.row_direction() as isize;
wait_until_vblank();
// draw the new game and wait until the next frame starts.
unsafe {
if px &lt; 0 || py &lt; 0 || px == SCREEN_WIDTH || py == SCREEN_HEIGHT {
// out of bounds, reset the screen and position.
mode3_clear_screen(0);
color = color.rotate_left(5);
px = SCREEN_WIDTH / 2;
py = SCREEN_HEIGHT / 2;
} else {
let color_here = mode3_read_pixel(px, py);
if color_here != 0 {
// crashed into our own line, reset the screen
mode3_clear_screen(0);
color = color.rotate_left(5);
} else {
// draw the new part of the line
mode3_draw_pixel(px, py, color);
mode3_draw_pixel(px, py + 1, color);
mode3_draw_pixel(px + 1, py, color);
mode3_draw_pixel(px + 1, py + 1, color);
}
}
}
wait_until_vdraw();
}
}
</code></pre></pre>
<p>Oh that's a lot more than before!</p>
<p>First we set Mode 3 and Background 2, we know about that.</p>
<p>Then we're going to store the player's x and y, along with a color value for
their light cycle. Then we enter the core loop.</p>
<p>We read the keys for input, and then do as much as we can without touching video
memory. Since we're using video memory as the place to store the player's light
trail, we can't do much, we just update their position and wait for VBlank to
start. The player will be a 2x2 square, so the arrows will move you 2 pixels per
frame.</p>
<p>Once we're in VBlank we check to see what kind of drawing we're doing. If the
player has gone out of bounds, we clear the screen, rotate their color, and then
reset their position. Why rotate the color? Just because it's fun to have
different colors.</p>
<p>Next, if the player is in bounds we read the video memory for their position. If
it's not black that means we've been here before and the player has crashed into
their own line. In this case, we reset the game without moving them to a new
location.</p>
<p>Finally, if the player is in bounds and they haven't crashed, we write their
color into memory at this position.</p>
<p>Regardless of how it worked out, we hold here until vdraw starts before going to
the next loop. That's all there is to it.</p>
<a class="header" href="#the-gba-crate-doesnt-quite-work-like-this" id="the-gba-crate-doesnt-quite-work-like-this"><h2>The gba crate doesn't quite work like this</h2></a>
<p>Once again, as with the <code>hello1</code> and <code>hello2</code> examples, the <code>gba</code> crate covers
much of this same ground as our example here, but in slightly different ways.</p>
<p>Better organization and abstractions are usually only realized once you've used
more of the whole thing you're trying to work with. If we want to have a crate
where the whole thing is well integrated with itself, then the examples would
also end up having to explain about things we haven't really touched on much
yet. It becomes a lot harder to teach.</p>
<p>So, going forward, we will continue to teach concepts and build examples that
don't directly depend on the <code>gba</code> crate. This allows the crate to freely grow
without all the past examples becoming a great inertia upon it.</p>
<a class="header" href="#ch-3-memory-and-objects" id="ch-3-memory-and-objects"><h1>Ch 3: Memory and Objects</h1></a>
<p>Alright so we can do some basic &quot;movement&quot;, but we left a big trail in the video
memory of everywhere we went. Most of the time that's not what we want at all.
If we want more hardware support we're going to have to use a new video mode. So
far we've only used Mode 3, but modes 4 and 5 are basically the same. Instead,
we'll switch focus to using a tiled graphical mode.</p>
<p>First we will go over the complete GBA memory mapping. Part of this is the
memory for tiled graphics, but also things like all those IO registers, where
our RAM is for scratch space, all that stuff. Even if we can't put all of them
to use at once, it's helpful to have an idea of what will be available in the
long run.</p>
<p>Tiled modes bring us two big new concepts that each have their own complexity:
backgrounds and objects. They share some concepts, but fundamentally the
background is for creating a very large static space that you can scroll around
the view within, and the objects are about having a few moving bits that appear
over the background. Careful use of backgrounds and objects is key to having the
best looking GBA game, so we won't even be able to cover it all in a single
chapter.</p>
<p>And, of course, since most games are pretty boring if they're totally static
we'll touch on the kinds of RNG implementations you might want to have on a GBA.
Most general purpose RNGs that you find are rather big compared to the amount of
memory we want to give them, and they often use a lot of <code>u64</code> operations, so
they end up much slower on a 32-bit machine like the GBA (you can lower 64-bit
ops to combinations of 32-bit ops, but that's quite a bit more work). We'll
cover a few RNG options that size down the RNG to a good size and a good speed
without trading away too much in terms of quality.</p>
<p>To top it all off, we'll make a simple &quot;memory game&quot; sort of thing. There's some
face down cards in a grid, you pick one to check, then you pick the other to
check, and then if they match the pair disappears.</p>
<a class="header" href="#gba-memory" id="gba-memory"><h1>GBA Memory</h1></a>
<p>The <a href="http://problemkaputt.de/gbatek.htm#gbamemorymap">GBA Memory Map</a> has
several memory portions to it, each with their own little differences. Most of
the memory has pre-determined use according to the hardware, but there is also
space for games to use as a scratch pad in whatever way the game sees fit.</p>
<p>The memory ranges listed here are <em>inclusive</em>, so they end with a lot of <code>F</code>s
and <code>E</code>s.</p>
<p>We've talked about volatile memory before, but just as a reminder I'll say that
all of the memory we'll talk about here should be accessed with volatile with
two exceptions:</p>
<ol>
<li>Work RAM (both internal and external) can be used normally, and if the
compiler is able to totally elide any reads and writes that's okay.</li>
<li>However, if you set aside any space in Work RAM where an interrupt will
communicate with the main program then that specific location will have to
keep using volatile access, since the compiler never knows when an interrupt
will actually happen.</li>
</ol>
<a class="header" href="#bios--system-rom" id="bios--system-rom"><h2>BIOS / System ROM</h2></a>
<ul>
<li><code>0x0</code> to <code>0x3FFF</code> (16k)</li>
</ul>
<p>This is special memory for the BIOS. It is &quot;read-only&quot;, but even then it's only
accessible when the program counter is pointing into the BIOS region. At all
other times you get a <a href="http://problemkaputt.de/gbatek.htm#gbaunpredictablethings">garbage
value</a> back when you
try to read out of the BIOS.</p>
<a class="header" href="#external-work-ram--ewram" id="external-work-ram--ewram"><h2>External Work RAM / EWRAM</h2></a>
<ul>
<li><code>0x2000000</code> to <code>0x203FFFF</code> (256k)</li>
</ul>
<p>This is a big pile of space, the use of which is up to each game. However, the
external work ram has only a 16-bit bus (if you read/write a 32-bit value it
silently breaks it up into two 16-bit operations) and also 2 wait cycles (extra
CPU cycles that you have to expend <em>per 16-bit bus use</em>).</p>
<p>In other words, we should think of EWRAM as if it was &quot;heap space&quot; in a normal
application. You can take the time to go store something within EWRAM, or to
load it out of EWRAM, but you should always avoid doing a critical computation
on values in EWRAM. It's a bit of a pain, but if you wanna be speedy and you
have more than just one manipulation that you want to do, you should pull the
value into a local variable, do all of your manipulations, and then push it back
out at the end.</p>
<a class="header" href="#internal-work-ram--iwram" id="internal-work-ram--iwram"><h2>Internal Work RAM / IWRAM</h2></a>
<ul>
<li><code>0x3000000</code> to <code>0x3007FFF</code> (32k)</li>
</ul>
<p>This is a smaller pile of space, but it has a 32-bit bus and no wait.</p>
<p>By default, <code>0x3007F00</code> to <code>0x3007FFF</code> is reserved for interrupt and BIOS use.
The rest of it is totally up to you. The user's stack space starts at
<code>0x3007F00</code> and proceeds <em>down</em> from there. In other words, if you start your
own customized IWRAM use at <code>0x3000000</code> and go up, eventually you might hit your
stack. However, most reasonable uses won't actually cause a memory collision.
It's just something you should know about if you're using a ton of stack or
IWRAM and then get problems.</p>
<a class="header" href="#io-registers-1" id="io-registers-1"><h2>IO Registers</h2></a>
<ul>
<li><code>0x4000000</code> to <code>0x40003FE</code></li>
</ul>
<p>We've touched upon a few of these so far, and we'll get to more later. At the
moment it is enough to say that, as you might have guessed, all of them live in
this region. Each individual register is a <code>u16</code> or <code>u32</code> and they control all
sorts of things. We'll actually be talking about some more of them in this very
chapter, because that's how we'll control some of the background and object
stuff.</p>
<a class="header" href="#palette-ram--palram" id="palette-ram--palram"><h2>Palette RAM / PALRAM</h2></a>
<ul>
<li><code>0x5000000</code> to <code>0x50003FF</code> (1k)</li>
</ul>
<p>Palette RAM has a 16-bit bus, which isn't really a problem because it
conceptually just holds <code>u16</code> values. There's no automatic wait state, but if
you try to access the same location that the display controller is accessing you
get bumped by 1 cycle. Since the display controller can use the palette ram any
number of times per scanline it's basically impossible to predict if you'll have
to do a wait or not during VDraw. During VBlank you won't have any wait of
course.</p>
<p>PALRAM is among the memory where there's weirdness if you try to write just one
byte: if you try to write just 1 byte, it writes that byte into <em>both</em> parts of
the larger 16-bit location. This doesn't really affect us much with PALRAM,
because palette values are all supposed to be <code>u16</code> anyway.</p>
<p>The palette memory actually contains not one, but <em>two</em> sets of palettes. First
there's 256 entries for the background palette data (starting at <code>0x5000000</code>),
and then there's 256 entries for object palette data (starting at <code>0x5000200</code>).</p>
<p>The GBA also has two modes for palette access: 8-bits-per-pixel (8bpp) and
4-bits-per-pixel (4bpp).</p>
<ul>
<li>In 8bpp mode an (8-bit) palette index value within a background or sprite
simply indexes directly into the 256 slots for that type of thing.</li>
<li>In 4bpp mode a (4-bit) palette index value within a background or sprite
specifies an index within a particular &quot;palbank&quot; (16 palette entries each),
and then a <em>separate</em> setting outside of the graphical data determines which
palbank is to be used for that background or object (the screen entry data for
backgrounds, and the object attributes for objects).</li>
</ul>
<a class="header" href="#video-ram--vram" id="video-ram--vram"><h2>Video RAM / VRAM</h2></a>
<ul>
<li><code>0x6000000</code> to <code>0x6017FFF</code> (96k)</li>
</ul>
<p>We've used this before! VRAM has a 16-bit bus and no wait. However, the same as
with PALRAM, the &quot;you might have to wait if the display controller is looking at
it&quot; rule applies here.</p>
<p>Unfortunately there's not much more exact detail that can be given about VRAM.
The use of the memory depends on the video mode that you're using.</p>
<p>One general detail of note is that you can't write individual bytes to any part
of VRAM. Depending on mode and location, you'll either get your bytes doubled
into both the upper and lower parts of the 16-bit location targeted, or you
won't even affect the memory. This usually isn't a big deal, except in two
situations:</p>
<ul>
<li>In Mode 4, if you want to change just 1 pixel, you'll have to be very careful
to read the old <code>u16</code>, overwrite just the byte you wanted to change, and then
write that back.</li>
<li>In any display mode, avoid using <code>memcopy</code> to place things into VRAM.
It's written to be byte oriented, and only does 32-bit transfers under select
conditions. The rest of the time it'll copy one byte at a time and you'll get
either garbage or nothing at all.</li>
</ul>
<a class="header" href="#object-attribute-memory--oam" id="object-attribute-memory--oam"><h2>Object Attribute Memory / OAM</h2></a>
<ul>
<li><code>0x7000000</code> to <code>0x70003FF</code> (1k)</li>
</ul>
<p>The Object Attribute Memory has a 32-bit bus and no default wait, but suffers
from the &quot;you might have to wait if the display controller is looking at it&quot;
rule. You cannot write individual bytes to OAM at all, but that's not really a
problem because all the fields of the data types within OAM are either <code>i16</code> or
<code>u16</code> anyway.</p>
<p>Object attribute memory is the wildest yet: it conceptually contains two types
of things, but they're <em>interlaced</em> with each other all the way through.</p>
<p>Now, <a href="http://problemkaputt.de/gbatek.htm#lcdobjoamattributes">GBATEK</a> and
<a href="https://www.cs.rit.edu/%7Etjh8300/CowBite/CowBiteSpec.htm#OAM%20(sprites)">CowByte</a>
doesn't quite give names to the two data types, though
<a href="https://www.coranac.com/tonc/text/regobj.htm#sec-oam">TONC</a> calls them
<code>OBJ_ATTR</code> and <code>OBJ_AFFINE</code>. We'll give them Rust names of course. In Rust terms
their layout would look like this:</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
#[repr(C)]
pub struct ObjectAttribute {
attr0: u16,
attr1: u16,
attr2: u16,
filler: i16,
}
#[repr(C)]
pub struct AffineMatrix {
filler0: [u16; 3],
pa: i16,
filler1: [u16; 3],
pb: i16,
filler2: [u16; 3],
pc: i16,
filler3: [u16; 3],
pd: i16,
}
#}</code></pre></pre>
<p>(Note: the <code>#[repr(C)]</code> part just means that Rust must lay out the data exactly
in the order we specify, which otherwise it is not required to do).</p>
<p>So, we've got 1024 bytes in OAM and each <code>ObjectAttribute</code> value is 8 bytes, so
naturally we can support up to 128 objects.</p>
<p><em>At the same time</em>, we've got 1024 bytes in OAM and each <code>AffineMatrix</code> is 32
bytes, so we can have 32 of them.</p>
<p>But, as I said, these things are all <em>interlaced</em> with each other. See how
there's &quot;filler&quot; fields in each struct? If we imagine the OAM as being just an
array of one type or the other, indexes 0/1/2/3 of the <code>ObjectAttribute</code> array
would line up with index 0 of the <code>AffineMatrix</code> array. It's kinda weird, but
that's just how it works. When we setup functions to read and write these values
we'll have to be careful with how we do it. We probably <em>won't</em> want to use
those representations above, at least not with the <code>AffineMatrix</code> type, because
they're quite wasteful if you want to store just object attributes or just
affine matrices.</p>
<a class="header" href="#game-pak-rom--flash-rom" id="game-pak-rom--flash-rom"><h2>Game Pak ROM / Flash ROM</h2></a>
<ul>
<li><code>0x8000000</code> to <code>0x9FFFFFF</code> (wait 0)</li>
<li><code>0xA000000</code> to <code>0xBFFFFFF</code> (wait 1)</li>
<li><code>0xC000000</code> to <code>0xDFFFFFF</code> (wait 2)</li>
<li>Max of 32Mb</li>
</ul>
<p>These portions of the memory are less fixed, because they depend on the precise
details of the game pak you've inserted into the GBA. In general, they connect
to the game pak ROM and/or Flash memory, using a 16-bit bus. The ROM is
read-only, but the Flash memory (if any) allows writes.</p>
<p>The game pak ROM is listed as being in three sections, but it's actually the
same memory being effectively mirrored into three different locations. The
mirror that you choose to access the game pak through affects which wait state
setting it uses (configured via IO register of course). Unfortunately, the
details come down more to the game pak hardware that you load your game onto
than anything else, so there's not much I can say right here. We'll eventually
talk about it more later,</p>
<p>One thing of note is the way that the 16-bit bus affects us: the instructions to
execute are coming through the same bus as the rest of the game data, so we want
them to be as compact as possible. The ARM chip in the GBA supports two
different instruction sets, &quot;thumb&quot; and &quot;non-thumb&quot;. The thumb mode instructions
are 16-bit, so they can each be loaded one at a time, and the non-thumb
instructions are 32-bit, so we're at a penalty if we execute them directly out
of the game pak. However, some things will demand that we use non-thumb code, so
we'll have to deal with that eventually. It's possible to switch between modes,
but it's a pain to keep track of what mode you're in because there's not
currently support for it in Rust itself (perhaps some day). So we'll stick with
thumb code as much as we possibly can, that's why our target profile for our
builds starts with <code>thumbv4</code>.</p>
<a class="header" href="#game-pak-sram" id="game-pak-sram"><h2>Game Pak SRAM</h2></a>
<ul>
<li><code>0xE000000</code> to <code>0xE00FFFF</code> (64k)</li>
</ul>
<p>The game pak SRAM has an 8-bit bus. Why did pokemon always take so long to save?
This is why. It also has some amount of wait, but as with the ROM, the details
depend on your game pak hardware (and also as with ROM, you can adjust the
settings with an IO register, should you need to).</p>
<p>One thing to note about the SRAM is that the GBA has a Direct Memory Access
(DMA) feature that can be used for bulk memory movements in some cases, but the
DMA <em>cannot</em> access the SRAM region. You really are stuck reading and writing
one byte at a time when you're using the SRAM.</p>
<a class="header" href="#tiled-backgrounds" id="tiled-backgrounds"><h1>Tiled Backgrounds</h1></a>
<p>TODO</p>
<a class="header" href="#object-basics" id="object-basics"><h1>Object Basics</h1></a>
<p>TODO</p>
<a class="header" href="#gba-rng" id="gba-rng"><h1>GBA RNG</h1></a>
<p>TODO</p>
<a class="header" href="#memory_game" id="memory_game"><h1>memory_game</h1></a>
<p>TODO</p>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
</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 -->
<script type="text/javascript">
window.addEventListener('load', function() {
window.setTimeout(window.print, 100);
});
</script>
</body>
</html>