mirror of
https://github.com/italicsjenga/gba.git
synced 2024-12-24 03:11:29 +11:00
2257 lines
122 KiB
HTML
2257 lines
122 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="00-introduction/00-index.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><ol class="section"><li><a href="00-introduction/01-requirements.html"><strong aria-hidden="true">1.1.</strong> Reader Requirements</a></li><li><a href="00-introduction/02-goals_and_style.html"><strong aria-hidden="true">1.2.</strong> Book Goals and Style</a></li><li><a href="00-introduction/03-development-setup.html"><strong aria-hidden="true">1.3.</strong> Development Setup</a></li><li><a href="00-introduction/04-hello-magic.html"><strong aria-hidden="true">1.4.</strong> Hello, Magic</a></li><li><a href="00-introduction/05-help_and_resources.html"><strong aria-hidden="true">1.5.</strong> Help and Resources</a></li></ol></li><li><a href="01-quirks/00-index.html"><strong aria-hidden="true">2.</strong> Quirks</a></li><li><ol class="section"><li><a href="01-quirks/01-no_std.html"><strong aria-hidden="true">2.1.</strong> No Std</a></li><li><a href="01-quirks/02-fixed_only.html"><strong aria-hidden="true">2.2.</strong> Fixed Only</a></li><li><a href="01-quirks/03-volatile_destination.html"><strong aria-hidden="true">2.3.</strong> Volatile Destination</a></li><li><a href="01-quirks/04-newtype.html"><strong aria-hidden="true">2.4.</strong> Newtype</a></li><li><a href="01-quirks/05-const_asserts.html"><strong aria-hidden="true">2.5.</strong> Const Asserts</a></li></ol></li><li><a href="02-concepts/00-index.html"><strong aria-hidden="true">3.</strong> Concepts</a></li><li><ol class="section"><li><a href="02-concepts/01-cpu.html"><strong aria-hidden="true">3.1.</strong> CPU</a></li><li><a href="02-concepts/02-bios.html"><strong aria-hidden="true">3.2.</strong> BIOS</a></li><li><a href="02-concepts/03-wram.html"><strong aria-hidden="true">3.3.</strong> Work RAM</a></li><li><a href="02-concepts/04-io-registers.html"><strong aria-hidden="true">3.4.</strong> IO Registers</a></li><li><a href="02-concepts/05-palram.html"><strong aria-hidden="true">3.5.</strong> Palette RAM</a></li><li><a href="02-concepts/06-vram.html"><strong aria-hidden="true">3.6.</strong> Video RAM</a></li><li><a href="02-concepts/07-oam.html"><strong aria-hidden="true">3.7.</strong> Object Attribute Memory</a></li><li><a href="02-concepts/08-rom.html"><strong aria-hidden="true">3.8.</strong> Game Pak ROM / Flash ROM</a></li><li><a href="02-concepts/09-sram.html"><strong aria-hidden="true">3.9.</strong> Save RAM</a></li></ol></li><li><a href="03-video/00-index.html"><strong aria-hidden="true">4.</strong> Video</a></li><li><ol class="section"><li><a href="03-video/01-rgb15.html"><strong aria-hidden="true">4.1.</strong> RBG15 Color</a></li><li><a href="03-video/TODO.html"><strong aria-hidden="true">4.2.</strong> TODO</a></li></ol></li><li><a href="04-non-video/00-index.html"><strong aria-hidden="true">5.</strong> Non-Video</a></li><li><ol class="section"><li><a href="04-non-video/01-buttons.html"><strong aria-hidden="true">5.1.</strong> Buttons</a></li><li><a href="04-non-video/02-timers.html"><strong aria-hidden="true">5.2.</strong> Timers</a></li><li><a href="04-non-video/03-dma.html"><strong aria-hidden="true">5.3.</strong> Direct Memory Access</a></li><li><a href="04-non-video/04-sound.html"><strong aria-hidden="true">5.4.</strong> Sound</a></li><li><a href="04-non-video/05-interrupts.html"><strong aria-hidden="true">5.5.</strong> Interrupts</a></li><li><a href="04-non-video/06-link_cable.html"><strong aria-hidden="true">5.6.</strong> Link Cable</a></li><li><a href="04-non-video/07-game_pak.html"><strong aria-hidden="true">5.7.</strong> Game Pak</a></li></ol></li><li><a href="05-examples/00-index.html"><strong aria-hidden="true">6.</strong> Examples</a></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>This is the book for learning how to write GameBoy Advance (GBA) games in Rust.</p>
|
|
<p>I'm <strong>Lokathor</strong>, the main author of the book. There's also <strong>Ketsuban</strong> who
|
|
provides the technical advisement, reviews the PRs, and keeps my crazy in check.</p>
|
|
<p>The book is a work in progress, as you can see if you actually try to open many
|
|
of the pages listed in the Table Of Contents.</p>
|
|
<a class="header" href="#feedback" id="feedback"><h2>Feedback</h2></a>
|
|
<p>It's very often hard to tell when you've explained something properly. In the
|
|
same way that your brain will read over small misspellings and correct things
|
|
into the right word, if an explanation for something you already understand
|
|
accidentally skips over some small detail then your brain can fill in the gaps
|
|
without you realizing it.</p>
|
|
<p><strong>Please</strong>, if things don't make sense then <a href="https://github.com/rust-console/gba/issues">file an
|
|
issue</a> about it so I know where
|
|
things need to improve.</p>
|
|
<a class="header" href="#reader-requirements" id="reader-requirements"><h1>Reader Requirements</h1></a>
|
|
<p>This book naturally assumes that you've already read Rust's core book:</p>
|
|
<ul>
|
|
<li><a href="https://doc.rust-lang.org/book/">The Rust Programming Language</a></li>
|
|
</ul>
|
|
<p>Now, I <em>know</em> it sounds silly to say "if you wanna program Rust on this old
|
|
video game system you should already know how to program Rust", but the more
|
|
people I meet and chat with the more they tell me that they jumped into Rust
|
|
without reading any or all of the book. You know who you are.</p>
|
|
<p>Please, read the whole book!</p>
|
|
<p>In addition to the core book, there's also an expansion book that I will declare
|
|
to be required reading for this:</p>
|
|
<ul>
|
|
<li><a href="https://doc.rust-lang.org/nomicon/">The Rustonomicon</a></li>
|
|
</ul>
|
|
<p>The Rustonomicon is all about trying to demystify <code>unsafe</code>. We'll end up using a
|
|
fair bit of unsafe code as a natural consequence of doing direct hardware
|
|
manipulations. Using unsafe is like <a href="https://www.zeldadungeon.net/wp-content/uploads/2013/04/tumblr_mlkpzij6T81qizbpto1_1280.gif">swinging a
|
|
sword</a>,
|
|
you should start slowly, practice carefully, and always pay attention no matter
|
|
how experienced you think you've become.</p>
|
|
<p>That said, it's sometimes a <a href="https://www.youtube.com/watch?v=rTo2u13lVcQ">necessary
|
|
tool</a> to get the job done, so you
|
|
have to break out of the borderline pathological fear of using it that most rust
|
|
programmers tend to have.</p>
|
|
<a class="header" href="#book-goals-and-style" id="book-goals-and-style"><h1>Book Goals and Style</h1></a>
|
|
<p>So, what's this book actually gonna teach you?</p>
|
|
<p>My goal is certainly not just showing off the crate. Programming for the GBA is
|
|
weird enough that I'm trying to teach you all the rest of the stuff you need to
|
|
know along the way. If I do my job right then you'd be able to write your own
|
|
crate for GBA stuff just how you think it should all go by the end.</p>
|
|
<p>Overall the book is sorted more for easy review once you're trying to program
|
|
something. The GBA has a few things that can stand on their own and many other
|
|
things are a mass of interconnected concepts, so some parts of the book end up
|
|
having to refer you to portions that you haven't read yet. The chapters and
|
|
sections are sorted so that <em>minimal</em> future references are required, but it's
|
|
unavoidable that it'll happen sometimes.</p>
|
|
<p>The actual "tutorial order" of the book is the
|
|
<a href="../05-examples/00-index.html">Examples</a> chapter. Each section of that chapter
|
|
breaks down one of the provided examples in the <a href="https://github.com/rust-console/gba/tree/master/examples">examples
|
|
directory</a> of the
|
|
repository. We go over what sections of the book you'll need to have read for
|
|
the example code to make sense, and also how we apply the general concepts
|
|
described in the book to the specific example cases.</p>
|
|
<a class="header" href="#development-setup" id="development-setup"><h1>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.</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 can use pacman to get it, and the default install puts the stuff
|
|
in <code>/opt/devkitpro/devkitARM/bin</code> and <code>/opt/devkitpro/tools/bin</code>. If you need
|
|
help you can look in our repository's
|
|
<a href="https://github.com/rust-console/gba/blob/master/.travis.yml">.travis.yml</a>
|
|
file to see exactly what our CI does.</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>Once all the tools are in place, there's particular steps that you need to
|
|
compile the project. For these to work you'll need some source code to compile.
|
|
Unlike with other things, an empty main file and/or an empty lib file will cause
|
|
a total build failure, because we'll need a
|
|
<a href="https://rust-embedded.github.io/book/intro/no-std.html">no_std</a> build, and rust
|
|
defaults to builds that use the standard library. The next section has a minimal
|
|
example file you can use (along with explanation), but we'll describe the build
|
|
steps here.</p>
|
|
<ul>
|
|
<li>
|
|
<p><code>arm-none-eabi-as crt0.s -o target/crt0.o</code></p>
|
|
<ul>
|
|
<li>This builds your text format <code>crt0.s</code> file into object format <code>crt0.o</code>
|
|
that's placed in the <code>target/</code> directory. Note that if the <code>target/</code>
|
|
directory doesn't exist yet it will fail, so you have to make the directory
|
|
if it's not there. You don't need to rebuild <code>crt0.s</code> every single time,
|
|
only when it changes, but you might as well throw a line to do it every time
|
|
into your build script so that you never forget because it's a practically
|
|
instant operation anyway.</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! It will work if you forget it, but <code>cargo xbuild</code> takes the inclusion of the extension as a flag to also compile
|
|
dependencies with the same sysroot, so you can include other crates in your
|
|
build. Well, crates 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
|
|
(more on that later). However, if you want a "real" ROM that works in all
|
|
emulators and that you could transfer to a flash cart to play on real hardware
|
|
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 "MODE" 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 <em>finally</em> 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.</p>
|
|
<a class="header" href="#hello-magic" id="hello-magic"><h1>Hello, Magic</h1></a>
|
|
<p>So we know all the steps to build our source, we just need some source.</p>
|
|
<p>We're beginners, so we'll start small. With normal programming there's usually a
|
|
console available, so the minimal program prints "Hello, world" to the terminal.
|
|
On a GBA we don't have a terminal and standard out and all that, so the minimal
|
|
program draws a red, blue, and green dot to the screen.</p>
|
|
<p>At the lowest level of device programming, it's all <a href="https://en.wikipedia.org/wiki/Magic_number_(programming)">Magic
|
|
Numbers</a>. You write
|
|
special values to special places and then the hardware does something. A clear
|
|
API makes every magic number and magic location easy to understand. A clear <em>and
|
|
good</em> API also prevents you from using the wrong magic number in the wrong place
|
|
and causing problems for yourself.</p>
|
|
<p>This is the minimal example to just test that our build system is all set, so
|
|
just this once we'll go <em>full</em> magic number crazy town, for fun. Ready? Here
|
|
goes:</p>
|
|
<p><code>hello_magic.rs</code>:</p>
|
|
<pre><pre class="playpen"><code class="language-rust">#![no_std]
|
|
#![feature(start)]
|
|
|
|
#[panic_handler]
|
|
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
|
loop {}
|
|
}
|
|
|
|
#[start]
|
|
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
|
unsafe {
|
|
(0x400_0000 as *mut u16).write_volatile(0x0403);
|
|
(0x600_0000 as *mut u16).offset(120 + 80 * 240).write_volatile(0x001F);
|
|
(0x600_0000 as *mut u16).offset(136 + 80 * 240).write_volatile(0x03E0);
|
|
(0x600_0000 as *mut u16).offset(120 + 96 * 240).write_volatile(0x7C00);
|
|
loop {}
|
|
}
|
|
}
|
|
</code></pre></pre>
|
|
<p>Throw that into your project skeleton, build the program, 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 <em>already</em> went wrong. Double check things, phone a friend,
|
|
write your senators, try asking <code>Lokathor</code> or <code>Ketsuban</code> on the <a href="https://discordapp.com/invite/aVESxV8">Rust Community
|
|
Discord</a>, until you're eventually able to
|
|
get your three dots going.</p>
|
|
<p>Of course, I'm sure you want to know why those numbers are the numbers to use.
|
|
Well that's what the whole rest of the book is about!</p>
|
|
<a class="header" href="#help-and-resources" id="help-and-resources"><h1>Help and Resources</h1></a>
|
|
<a class="header" href="#help" id="help"><h2>Help</h2></a>
|
|
<p>So you're stuck on a problem and the book doesn't say what to do. Where can you
|
|
find out more?</p>
|
|
<p>The first place I would suggest is the <a href="https://discordapp.com/invite/aVESxV8">Rust Community
|
|
Discord</a>. If it's a general Rust question
|
|
then you can ask anyone in any channel you feel is appropriate. If it's GBA
|
|
specific then you can try asking me (<code>Lokathor</code>) or <code>Ketsuban</code> in the <code>#gamedev</code>
|
|
channel.</p>
|
|
<a class="header" href="#emulators" id="emulators"><h2>Emulators</h2></a>
|
|
<p>You certainly might want to eventually write a game that you can put on a flash
|
|
cart and play on real hardware, but for most of your development you'll probably
|
|
want to be using an emulator for testing, because you don't have to fiddle with
|
|
cables and all that.</p>
|
|
<p>In terms of emulators, you want to be using
|
|
<a href="https://github.com/mgba-emu/mgba">mGBA</a>, and you want to be using the <a href="https://github.com/mgba-emu/mgba/releases/tag/0.7-b1">0.7 Beta
|
|
1</a> or later. This update
|
|
lets you run raw ELF files, which means that you can have full debug symbols
|
|
available while you're debugging problems.</p>
|
|
<a class="header" href="#information-resources" id="information-resources"><h2>Information Resources</h2></a>
|
|
<p>First, if I fail to describe something related to Rust, you can always try
|
|
checking in <a href="https://doc.rust-lang.org/nightly/reference/introduction.html">The Rust
|
|
Reference</a> to see
|
|
if they cover it. You can mostly ignore that big scary red banner at the top,
|
|
things are a lot better documented than they make it sound.</p>
|
|
<p>If you need help trying to fiddle your math down as hard as you can, there are
|
|
resources such as the <a href="https://graphics.stanford.edu/%7Eseander/bithacks.html">Bit Twiddling
|
|
Hacks</a> page.</p>
|
|
<p>As to GBA related lore, Ketsuban and I didn't magically learn this all from
|
|
nowhere, we read various technical manuals and guides ourselves and then
|
|
distilled those works oriented around C and C++ into a book for Rust.</p>
|
|
<p>We have personally used some or all of the following:</p>
|
|
<ul>
|
|
<li><a href="http://problemkaputt.de/gbatek.htm">GBATEK</a>: This is <em>the</em> resource. It
|
|
covers not only the GBA, but also the DS and DSi, and also a run down of ARM
|
|
assembly (32-bit and 16-bit opcodes). The link there is to the 2.9b version on
|
|
<code>problemkaputt.de</code> (the official home of the document), but if you just google
|
|
for gbatek the top result is for the 2.5 version on <code>akkit.org</code>, so make sure
|
|
you're looking at the newest version. Sometimes <code>problemkaputt.de</code> is a little
|
|
sluggish so I've also <a href="https://lokathor.com/gbatek.html">mirrored</a> the 2.9b
|
|
version on my own site as well. GBATEK is rather large, over 2mb of text, so
|
|
if you're on a phone or similar you might want to save an offline copy to go
|
|
easy on your data usage.</li>
|
|
<li><a href="https://www.coranac.com/tonc/text/">TONC</a>: While GBATEK is basically just a
|
|
huge tech specification, TONC is an actual <em>guide</em> on how to make sense of the
|
|
GBA's abilities and organize it into a game. It's written for C of course, but
|
|
as a Rust programmer you should always be practicing your ability to read C
|
|
code anyway. It's the programming equivalent of learning Latin because all the
|
|
old academic books are written in Latin.</li>
|
|
<li><a href="https://www.cs.rit.edu/%7Etjh8300/CowBite/CowBiteSpec.htm">CowBite</a>: This is
|
|
more like GBATEK, and it's less complete, but it mixes in a little more
|
|
friendly explanation of things in between the hardware spec parts.</li>
|
|
</ul>
|
|
<p>And I haven't had time to look at it myself, <a href="http://belogic.com/gba/">The Audio
|
|
Advance</a> seems to be very good. It explains in depth
|
|
how you can get audio working on the GBA. Note that the table of contents for
|
|
each page goes along the top instead of down the side.</p>
|
|
<a class="header" href="#non-rust-gba-community" id="non-rust-gba-community"><h2>Non-Rust GBA Community</h2></a>
|
|
<p>There's also the <a href="http://www.gbadev.org/">GBADev.org</a> site, which has a forum
|
|
and everything. They're coding in C and C++, but you can probably overcome that
|
|
difference with a little work on your part.</p>
|
|
<p>I also found a place called
|
|
<a href="https://gbatemp.net/categories/nintendo-gba-discussions.32/">GBATemp</a>, which
|
|
seems to have a more active forum but less of a focus on actual coding.</p>
|
|
<a class="header" href="#quirks" id="quirks"><h1>Quirks</h1></a>
|
|
<p>The GBA supports a lot of totally normal Rust code exactly like you'd think.</p>
|
|
<p>However, it also is missing a lot of what you might expect, and sometimes we
|
|
have to do things in slightly weird ways.</p>
|
|
<p>We start the book by covering the quirks our code will have, just to avoid too
|
|
many surprises later.</p>
|
|
<a class="header" href="#no-std" id="no-std"><h1>No Std</h1></a>
|
|
<p>First up, as you already saw in the <code>hello_magic</code> code, we have to use the
|
|
<code>#![no_std]</code> outer attribute on our program when we target the GBA. You can find
|
|
some info about <code>no_std</code> in two official sources:</p>
|
|
<ul>
|
|
<li><a href="https://doc.rust-lang.org/unstable-book/language-features/lang-items.html#writing-an-executable-without-stdlib">unstable
|
|
book section</a></li>
|
|
<li><a href="https://rust-embedded.github.io/book/intro/no-std.html?highlight=no_std#a--no_std--rust-environment">embedded
|
|
book section</a></li>
|
|
</ul>
|
|
<p>The unstable book is borderline useless here because it's describing too many
|
|
things in too many words. The embedded book is much better, but still fairly
|
|
terse.</p>
|
|
<a class="header" href="#bare-metal" id="bare-metal"><h2>Bare Metal</h2></a>
|
|
<p>The GBA falls under what the Embedded Book calls "Bare Metal Environments".
|
|
Basically, the machine powers on and immediately begins executing some ASM code.
|
|
Our ASM startup was provided by <code>Ketsuban</code> (check the <code>crt0.s</code> file). We'll go
|
|
over <em>how</em> it works much later on, for now it's enough to know that it does
|
|
work, and eventually control passes into Rust code.</p>
|
|
<p>On the rust code side of things, we determine our starting point with the
|
|
<code>#[start]</code> attribute on our <code>main</code> function. The <code>main</code> function also has a
|
|
specific type signature that's different from the usual <code>main</code> that you'd see in
|
|
Rust. I'd tell you to read the unstable-book entry on <code>#[start]</code> but they
|
|
<a href="https://doc.rust-lang.org/unstable-book/language-features/start.html">literally</a>
|
|
just tell you to look at the <a href="https://github.com/rust-lang/rust/issues/29633">tracking issue for
|
|
it</a> instead, and that's not very
|
|
helpful either. Basically it just <em>has</em> to be declared the way it is, even
|
|
though there's nothing passing in the arguments and there's no place that the
|
|
return value will go. The compiler won't accept it any other way.</p>
|
|
<a class="header" href="#no-standard-library" id="no-standard-library"><h2>No Standard Library</h2></a>
|
|
<p>The Embedded Book tells us that we can't use the standard library, but we get
|
|
access to something called "libcore", which sounds kinda funny. What they're
|
|
talking about is just <a href="https://doc.rust-lang.org/core/index.html">the core
|
|
crate</a>, which is called <code>libcore</code>
|
|
within the rust repository for historical reasons.</p>
|
|
<p>The <code>core</code> crate is actually still a really big portion of Rust. The standard
|
|
library doesn't actually hold too much code (relatively speaking), instead it
|
|
just takes code form other crates and then re-exports it in an organized way. So
|
|
with just <code>core</code> instead of <code>std</code>, what are we missing?</p>
|
|
<p>In no particular order:</p>
|
|
<ul>
|
|
<li>Allocation</li>
|
|
<li>Clock</li>
|
|
<li>Network</li>
|
|
<li>File System</li>
|
|
</ul>
|
|
<p>The allocation system and all the types that you can use if you have a global
|
|
allocator are neatly packaged up in the
|
|
<a href="https://doc.rust-lang.org/alloc/index.html">alloc</a> crate. The rest isn't as
|
|
nicely organized.</p>
|
|
<p>It's <em>possible</em> to implement a fair portion of the entire standard library
|
|
within a GBA context and make the rest just panic if you try to use it. However,
|
|
do you really need all that? Eh... probably not?</p>
|
|
<ul>
|
|
<li>We don't need a file system, because all of our data is just sitting there in
|
|
the ROM for us to use. When programming we can organize our <code>const</code> data into
|
|
modules and such to keep it organized, but once the game is compiled it's just
|
|
one huge flat address space. TODO: Parasyte says that a FS can be handy even
|
|
if it's all just ReadOnly, so we'll eventually talk about how you might set up
|
|
such a thing I guess, since we'll already be talking about replacements for
|
|
three of the other four things we "lost". Maybe we'll make Parasyte write that
|
|
section.</li>
|
|
<li>Networking, well, the GBA has a Link Cable you can use to communicate with
|
|
another GBA, but it's not really like a unix socket with TCP, so the standard
|
|
Rust networking isn't a very good match.</li>
|
|
<li>Clock is actually two different things at once. One is the ability to store
|
|
the time long term, which is a bit of hardware that some gamepaks have in them
|
|
(eg: pokemon ruby/sapphire/emerald). The GBA itself can't keep time while
|
|
power is off. However, the second part is just tracking time moment to moment,
|
|
which the GBA can totally do. We'll see how to access the timers soon enough.</li>
|
|
</ul>
|
|
<p>Which just leaves us with allocation. Do we need an allocator? Depends on your
|
|
game. For demos and small games you probably don't need one. For bigger games
|
|
you'll maybe want to get an allocator going eventually. It's in some sense a
|
|
crutch, but it's a very useful one.</p>
|
|
<p>So I promise that at some point we'll cover how to get an allocator going.
|
|
Either a Rust Global Allocator (if practical), which would allow for a lot of
|
|
the standard library types to be used "for free" once it was set up, or just a
|
|
custom allocator that's GBA specific if Rust's global allocator style isn't a
|
|
good fit for the GBA (I honestly haven't looked into it).</p>
|
|
<a class="header" href="#bare-metal-panic" id="bare-metal-panic"><h2>Bare Metal Panic</h2></a>
|
|
<p>TODO: expand this</p>
|
|
<ul>
|
|
<li>Write <code>0xC0DE</code> to <code>0x4fff780</code> (<code>u16</code>) to enable mGBA logging. Write any other
|
|
value to disable it.</li>
|
|
<li>Read <code>0x4fff780</code> (<code>u16</code>) to check mGBA logging status.
|
|
<ul>
|
|
<li>You get <code>0x1DEA</code> if debugging is active.</li>
|
|
<li>Otherwise you get standard open bus nonsense values.</li>
|
|
</ul>
|
|
</li>
|
|
<li>Write your message into the virtual <code>[u8; 255]</code> array starting at <code>0x4fff600</code>.
|
|
mGBA will interpret these bytes as a CString value.</li>
|
|
<li>Write <code>0x100</code> PLUS the message level to <code>0x4fff700</code> (<code>u16</code>) when you're ready
|
|
to send a message line:
|
|
<ul>
|
|
<li>0: Fatal (halts execution with a popup)</li>
|
|
<li>1: Error</li>
|
|
<li>2: Warning</li>
|
|
<li>3: Info</li>
|
|
<li>4: Debug</li>
|
|
</ul>
|
|
</li>
|
|
<li>Sending the message also automatically zeroes the output buffer.</li>
|
|
<li>View the output within the "Tools" menu, "View Logs...". Note that the Fatal
|
|
message, if any doesn't get logged.</li>
|
|
</ul>
|
|
<p>TODO: this will probably fail without a <code>__clzsi2</code> implementation, which is a
|
|
good seg for the next section</p>
|
|
<a class="header" href="#llvm-intrinsics" id="llvm-intrinsics"><h2>LLVM Intrinsics</h2></a>
|
|
<p>TODO: explain that we'll occasionally have to provide some intrinsics.</p>
|
|
<a class="header" href="#fixed-only" id="fixed-only"><h1>Fixed Only</h1></a>
|
|
<p>In addition to not having much of the standard library available, we don't even
|
|
have a floating point unit available! We can't do floating point math in
|
|
hardware! We <em>could</em> still do floating point math as pure software computations
|
|
if we wanted, but that's a slow, slow thing to do.</p>
|
|
<p>Are there faster ways? It's the same answer as always: "Yes, but not without a
|
|
tradeoff."</p>
|
|
<p>The faster way is to represent fractional values using a system called a <a href="https://en.wikipedia.org/wiki/Fixed-point_arithmetic">Fixed
|
|
Point Representation</a>.
|
|
What do we trade away? Numeric range.</p>
|
|
<ul>
|
|
<li>Floating point math stores bits for base value and for exponent all according
|
|
to a single <a href="https://en.wikipedia.org/wiki/IEEE_754">well defined</a> standard
|
|
for how such a complicated thing works.</li>
|
|
<li>Fixed point math takes a normal integer (either signed or unsigned) and then
|
|
just "mentally associates" it (so to speak) with a fractional value for its
|
|
"units". If you have 3 and it's in units of 1/2, then you have 3/2, or 1.5
|
|
using decimal notation. If your number is 256 and it's in units of 1/256th
|
|
then the value is 1.0 in decimal notation.</li>
|
|
</ul>
|
|
<p>Floating point math requires dedicated hardware to perform quickly, but it can
|
|
"trade" precision when it needs to represent extremely large or small values.</p>
|
|
<p>Fixed point math is just integral math, which our GBA is reasonably good at, but
|
|
because your number is associated with a fixed fraction your results can get out
|
|
of range very easily.</p>
|
|
<a class="header" href="#representing-a-fixed-point-value" id="representing-a-fixed-point-value"><h2>Representing A Fixed Point Value</h2></a>
|
|
<p>So we want to associate our numbers with a mental note of what units they're in:</p>
|
|
<ul>
|
|
<li><a href="https://doc.rust-lang.org/core/marker/struct.PhantomData.html">PhantomData</a>
|
|
is a type that tells the compiler "please remember this extra type info" when
|
|
you add it as a field to a struct. It goes away at compile time, so it's
|
|
perfect for us to use as space for a note to ourselves without causing runtime
|
|
overhead.</li>
|
|
<li>The <a href="https://crates.io/crates/typenum">typenum</a> crate is the best way to
|
|
represent a number within a type in Rust. Since our values on the GBA are
|
|
always specified as a number of fractional bits to count the number as, we can
|
|
put <code>typenum</code> types such as <code>U8</code> or <code>U14</code> into our <code>PhantomData</code> to keep track
|
|
of what's going on.</li>
|
|
</ul>
|
|
<p>Now, those of you who know me, or perhaps just know my reputation, will of
|
|
course <em>immediately</em> question what happened to the real Lokathor. I do not care
|
|
for most crates, and I particularly don't care for using a crate in teaching
|
|
situations. However, <code>typenum</code> has a number of factors on its side that let me
|
|
suggest it in this situation:</p>
|
|
<ul>
|
|
<li>It's version 1.10 with a total of 21 versions and nearly 700k downloads, so we
|
|
can expect that the major troubles have been shaken out and that it will remain
|
|
fairly stable for quite some time to come.</li>
|
|
<li>It has no further dependencies that it's going to drag into the compilation.</li>
|
|
<li>It happens all at compile time, so it's not clogging up our actual game with
|
|
any nonsense.</li>
|
|
<li>The (interesting) subject of "how do you do math inside Rust's trait system?" is
|
|
totally separate from the concern that we're trying to focus on here.</li>
|
|
</ul>
|
|
<p>Therefore, we will consider it acceptable to use this crate.</p>
|
|
<p>Now the <code>typenum</code> crate defines a whole lot, but we'll focus down to just a
|
|
single type at the moment:
|
|
<a href="https://docs.rs/typenum/1.10.0/typenum/uint/struct.UInt.html">UInt</a> is a
|
|
type-level unsigned value. It's like <code>u8</code> or <code>u16</code>, but while they're types that
|
|
then have values, each <code>UInt</code> construction statically equates to a specific
|
|
value. Like how the <code>()</code> type only has one value, which is also called <code>()</code>. In
|
|
this case, you wrap up <code>UInt</code> around smaller <code>UInt</code> values and a <code>B1</code> or <code>B0</code>
|
|
value to build up the binary number that you want at the type level.</p>
|
|
<p>In other words, instead of writing</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
let six = 0b110;
|
|
#}</code></pre></pre>
|
|
<p>We write</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
type U6 = UInt<UInt<UInt<UTerm, B1>, B1>, B0>;
|
|
#}</code></pre></pre>
|
|
<p>Wild, I know. If you look into the <code>typenum</code> crate you can do math and stuff
|
|
with these type level numbers, and we will a little bit below, but to start off
|
|
we <em>just</em> need to store one in some <code>PhantomData</code>.</p>
|
|
<a class="header" href="#a-struct-for-fixed-point" id="a-struct-for-fixed-point"><h3>A struct For Fixed Point</h3></a>
|
|
<p>Our actual type for a fixed point value looks like this:</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
use core::marker::PhantomData;
|
|
use typenum::marker_traits::Unsigned;
|
|
|
|
/// Fixed point `T` value with `F` fractional bits.
|
|
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
|
|
#[repr(transparent)]
|
|
pub struct Fx<T, F: Unsigned> {
|
|
bits: T,
|
|
_phantom: PhantomData<F>,
|
|
}
|
|
#}</code></pre></pre>
|
|
<p>This says that <code>Fx<T,F></code> is a generic type that holds some base number type <code>T</code>
|
|
and a <code>F</code> type that's marking off how many fractional bits we're using. We only
|
|
want people giving unsigned type-level values for the <code>PhantomData</code> type, so we
|
|
use the trait bound <code>F: Unsigned</code>.</p>
|
|
<p>We use
|
|
<a href="https://github.com/rust-lang/rfcs/blob/master/text/1758-repr-transparent.md">repr(transparent)</a>
|
|
here to ensure that <code>Fx</code> will always be treated just like the base type in the
|
|
final program (in terms of bit pattern and ABI).</p>
|
|
<p>If you go and check, this is <em>basically</em> how the existing general purpose crates
|
|
for fixed point math represent their numbers. They're a little fancier about it
|
|
because they have to cover every case, and we only have to cover our GBA case.</p>
|
|
<p>That's quite a bit to type though. We probably want to make a few type aliases
|
|
for things to be easier to look at. Unfortunately there's <a href="https://en.wikipedia.org/wiki/Fixed-point_arithmetic#Notation">no standard
|
|
notation</a> for how
|
|
you write a fixed point type. We also have to limit ourselves to what's valid
|
|
for use in a Rust type too. I like the <code>fx</code> thing, so we'll use that for signed
|
|
and then <code>fxu</code> if we need an unsigned value.</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
/// Alias for an `i16` fixed point value with 8 fractional bits.
|
|
pub type fx8_8 = Fx<i16,U8>;
|
|
#}</code></pre></pre>
|
|
<p>Rust will complain about having <code>non_camel_case_types</code>, and you can shut that
|
|
warning up by putting an <code>#[allow(non_camel_case_types)]</code> attribute on the type
|
|
alias directly, or you can use <code>#![allow(non_camel_case_types)]</code> at the very top
|
|
of the module to shut up that warning for the whole module (which is what I
|
|
did).</p>
|
|
<a class="header" href="#constructing-a-fixed-point-value" id="constructing-a-fixed-point-value"><h2>Constructing A Fixed Point Value</h2></a>
|
|
<p>So how do we actually <em>make</em> one of these values? Well, we can always just wrap or unwrap any value in our <code>Fx</code> type:</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
impl<T, F: Unsigned> Fx<T, F> {
|
|
/// Uses the provided value directly.
|
|
pub fn from_raw(r: T) -> Self {
|
|
Fx {
|
|
num: r,
|
|
phantom: PhantomData,
|
|
}
|
|
}
|
|
/// Unwraps the inner value.
|
|
pub fn into_raw(self) -> T {
|
|
self.num
|
|
}
|
|
}
|
|
#}</code></pre></pre>
|
|
<p>I'd like to use the <code>From</code> trait of course, but it was giving me some trouble, i
|
|
think because of the orphan rule. Oh well.</p>
|
|
<p>If we want to be particular to the fact that these are supposed to be
|
|
<em>numbers</em>... that gets tricky. Rust is actually quite bad at being generic about
|
|
number types. You can use the <a href="https://crates.io/crates/num">num</a> crate, or you
|
|
can just use a macro and invoke it once per type. Guess what we're gonna do.</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
macro_rules! fixed_point_methods {
|
|
($t:ident) => {
|
|
impl<F: Unsigned> Fx<$t, F> {
|
|
/// Gives the smallest positive non-zero value.
|
|
pub fn precision() -> Self {
|
|
Fx {
|
|
num: 1,
|
|
phantom: PhantomData,
|
|
}
|
|
}
|
|
|
|
/// Makes a value with the integer part shifted into place.
|
|
pub fn from_int_part(i: $t) -> Self {
|
|
Fx {
|
|
num: i << F::U8,
|
|
phantom: PhantomData,
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
fixed_point_methods! {u8}
|
|
fixed_point_methods! {i8}
|
|
fixed_point_methods! {i16}
|
|
fixed_point_methods! {u16}
|
|
fixed_point_methods! {i32}
|
|
fixed_point_methods! {u32}
|
|
#}</code></pre></pre>
|
|
<p>Now <em>you'd think</em> that those can be <code>const</code>, but at the moment you can't have a
|
|
<code>const</code> function with a bound on any trait other than <code>Sized</code>, so they have to
|
|
be normal functions.</p>
|
|
<p>Also, we're doing something a little interesting there with <code>from_int_part</code>. We
|
|
can take our <code>F</code> type and get its constant value. There's other associated
|
|
constants if we want it in other types, and also non-const methods if you wanted
|
|
that for some reason (maybe passing it as a closure function? dunno).</p>
|
|
<a class="header" href="#casting-base-values" id="casting-base-values"><h2>Casting Base Values</h2></a>
|
|
<p>Next, once we have a value in one base type we will need to be able to move it
|
|
into another base type. Unfortunately this means we gotta use the <code>as</code> operator,
|
|
which requires a concrete source type and a concrete destination type. There's
|
|
no easy way for us to make it generic here.</p>
|
|
<p>We could let the user use <code>into_raw</code>, cast, and then do <code>from_raw</code>, but that's
|
|
error prone because they might change the fractional bit count accidentally.
|
|
This means that we have to write a function that does the casting while
|
|
perfectly preserving the fractional bit quantity. If we wrote one function for
|
|
each conversion it'd be like 30 different possible casts (6 base types that we
|
|
support, and then 5 possible target types). Instead, we'll write it just once in
|
|
a way that takes a closure, and let the user pass a closure that does the cast.
|
|
The compiler should merge it all together quite nicely for us once optimizations
|
|
kick in.</p>
|
|
<p>This code goes outside the macro. I want to avoid too much code in the macro if
|
|
we can, it's a little easier to cope with I think.</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
/// Casts the base type, keeping the fractional bit quantity the same.
|
|
pub fn cast_inner<Z, C: Fn(T) -> Z>(self, op: C) -> Fx<Z, F> {
|
|
Fx {
|
|
num: op(self.num),
|
|
phantom: PhantomData,
|
|
}
|
|
}
|
|
#}</code></pre></pre>
|
|
<p>It's horrible and ugly, but Rust is just bad at numbers sometimes.</p>
|
|
<a class="header" href="#adjusting-fractional-part" id="adjusting-fractional-part"><h2>Adjusting Fractional Part</h2></a>
|
|
<p>In addition to the base value we might want to change our fractional bit
|
|
quantity. This is actually easier that it sounds, but it also requires us to be
|
|
tricky with the generics. We can actually use some typenum type level operators
|
|
here.</p>
|
|
<p>This code goes inside the macro: we need to be able to use the left shift and
|
|
right shift, which is easiest when we just use the macro's <code>$t</code> as our type. We
|
|
could alternately put a similar function outside the macro and be generic on <code>T</code>
|
|
having the left and right shift operators by using a <code>where</code> clause. As much as
|
|
I'd like to avoid too much code being generated by macro, I'd <em>even more</em> like
|
|
to avoid generic code with huge and complicated trait bounds. It comes down to
|
|
style, and you gotta decide for yourself.</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
/// Changes the fractional bit quantity, keeping the base type the same.
|
|
pub fn adjust_fractional_bits<Y: Unsigned + IsEqual<F, Output = False>>(self) -> Fx<$t, Y> {
|
|
let leftward_movement: i32 = Y::to_i32() - F::to_i32();
|
|
Fx {
|
|
num: if leftward_movement > 0 {
|
|
self.num << leftward_movement
|
|
} else {
|
|
self.num >> (-leftward_movement)
|
|
},
|
|
phantom: PhantomData,
|
|
}
|
|
}
|
|
#}</code></pre></pre>
|
|
<p>There's a few things at work. First, we introduce <code>Y</code> as the target number of
|
|
fractional bits, and we <em>also</em> limit it that the target bits quantity can't be
|
|
the same as we already have using a type-level operator. If it's the same as we
|
|
started with, why are you doing the cast at all?</p>
|
|
<p>Now, once we're sure that the current bits and target bits aren't the same, we
|
|
compute <code>target - start</code>, and call this our "leftward movement". Example: if
|
|
we're targeting 8 bits and we're at 4 bits, we do 8-4 and get +4 as our leftward
|
|
movement. If the leftward_movement is positive we naturally shift our current
|
|
value to the left. If it's not positive then it <em>must</em> be negative because we
|
|
eliminated 0 as a possibility using the type-level operator, so we shift to the
|
|
right by the negative value.</p>
|
|
<a class="header" href="#addition-subtraction-shifting-negative-comparisons" id="addition-subtraction-shifting-negative-comparisons"><h2>Addition, Subtraction, Shifting, Negative, Comparisons</h2></a>
|
|
<p>From here on we're getting help from <a href="https://spin.atomicobject.com/2012/03/15/simple-fixed-point-math/">this blog
|
|
post</a> by <a href="https://spin.atomicobject.com/author/vranish/">Job
|
|
Vranish</a>, so thank them if you
|
|
learn something.</p>
|
|
<p>I might have given away the game a bit with those <code>derive</code> traits on our fixed
|
|
point type. For a fair number of operations you can use the normal form of the
|
|
op on the inner bits as long as the fractional parts have the same quantity.
|
|
This includes equality and ordering (which we derived) as well as addition,
|
|
subtraction, and bit shifting (which we need to do ourselves).</p>
|
|
<p>This code can go outside the macro, with sufficient trait bounds.</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
impl<T: Add<Output = T>, F: Unsigned> Add for Fx<T, F> {
|
|
type Output = Self;
|
|
fn add(self, rhs: Fx<T, F>) -> Self::Output {
|
|
Fx {
|
|
num: self.num + rhs.num,
|
|
phantom: PhantomData,
|
|
}
|
|
}
|
|
}
|
|
#}</code></pre></pre>
|
|
<p>The bound on <code>T</code> makes it so that <code>Fx<T, F></code> can be added any time that <code>T</code> can
|
|
be added to its own type with itself as the output. We can use the exact same
|
|
pattern for <code>Sub</code>, <code>Shl</code>, <code>Shr</code>, and <code>Neg</code>. With enough trait bounds, we can do
|
|
anything!</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
impl<T: Sub<Output = T>, F: Unsigned> Sub for Fx<T, F> {
|
|
type Output = Self;
|
|
fn sub(self, rhs: Fx<T, F>) -> Self::Output {
|
|
Fx {
|
|
num: self.num - rhs.num,
|
|
phantom: PhantomData,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T: Shl<u32, Output = T>, F: Unsigned> Shl<u32> for Fx<T, F> {
|
|
type Output = Self;
|
|
fn shl(self, rhs: u32) -> Self::Output {
|
|
Fx {
|
|
num: self.num << rhs,
|
|
phantom: PhantomData,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T: Shr<u32, Output = T>, F: Unsigned> Shr<u32> for Fx<T, F> {
|
|
type Output = Self;
|
|
fn shr(self, rhs: u32) -> Self::Output {
|
|
Fx {
|
|
num: self.num >> rhs,
|
|
phantom: PhantomData,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T: Neg<Output = T>, F: Unsigned> Neg for Fx<T, F> {
|
|
type Output = Self;
|
|
fn neg(self) -> Self::Output {
|
|
Fx {
|
|
num: -self.num,
|
|
phantom: PhantomData,
|
|
}
|
|
}
|
|
}
|
|
#}</code></pre></pre>
|
|
<p>Unfortunately, for <code>Shl</code> and <code>Shr</code> to have as much coverage on our type as it
|
|
does on the base type (allowing just about any right hand side) we'd have to do
|
|
another macro, but I think just <code>u32</code> is fine. We can always add more later if
|
|
we need.</p>
|
|
<p>We could also implement <code>BitAnd</code>, <code>BitOr</code>, <code>BitXor</code>, and <code>Not</code>, but they don't
|
|
seem relevent to our fixed point math use, and this section is getting long
|
|
already. Just use the same general patterns if you want to add it in your own
|
|
programs. Shockingly, <code>Rem</code> also works directly if you want it, though I don't
|
|
forsee us needing floating point remainder. Also, the GBA can't do hardware
|
|
division or remainder, and we'll have to work around that below when we
|
|
implement <code>Div</code> (which maybe we don't need, but it's complex enough I should
|
|
show it instead of letting people guess).</p>
|
|
<p><strong>Note:</strong> In addition to the various <code>Op</code> traits, there's also <code>OpAssign</code>
|
|
variants. Each <code>OpAssign</code> is the same as <code>Op</code>, but takes <code>&mut self</code> instead of
|
|
<code>self</code> and then modifies in place instead of producing a fresh value. In other
|
|
words, if you want both <code>+</code> and <code>+=</code> you'll need to do the <code>AddAssign</code> trait
|
|
too. It's not the worst thing to just write <code>a = a+b</code>, so I won't bother with
|
|
showing all that here. It's pretty easy to figure out for yourself if you want.</p>
|
|
<a class="header" href="#multiplication" id="multiplication"><h2>Multiplication</h2></a>
|
|
<p>This is where things get more interesting. When we have two numbers <code>A</code> and <code>B</code>
|
|
they really stand for <code>(a*f)</code> and <code>(b*f)</code>. If we write <code>A*B</code> then we're really
|
|
writing <code>(a*f)*(b*f)</code>, which can be rewritten as <code>(a*b)*2f</code>, and now it's
|
|
obvious that we have one more <code>f</code> than we wanted to have. We have to do the
|
|
multiply of the inner value and then divide out the <code>f</code>. We divide by <code>1 << bit_count</code>, so if we have 8 fractional bits we'll divide by 256.</p>
|
|
<p>The catch is that, when we do the multiply we're <em>extremely</em> likely to overflow
|
|
our base type with that multiplication step. Then we do that divide, and now our
|
|
result is basically nonsense. We can avoid this to some extent by casting up to
|
|
a higher bit type, doing the multiplication and division at higher precision,
|
|
and then casting back down. We want as much precision as possible without being
|
|
too inefficient, so we'll always cast up to 32-bit (on a 64-bit machine you'd
|
|
cast up to 64-bit instead).</p>
|
|
<p>Naturally, any signed value has to be cast up to <code>i32</code> and any unsigned value
|
|
has to be cast up to <code>u32</code>, so we'll have to handle those separately.</p>
|
|
<p>Also, instead of doing an <em>actual</em> divide we can right-shift by the correct
|
|
number of bits to achieve the same effect. <em>Except</em> when we have a signed value
|
|
that's negative, because actual division truncates towards zero and
|
|
right-shifting truncates towards negative infinity. We can get around <em>this</em> by
|
|
flipping the sign, doing the shift, and flipping the sign again (which sounds
|
|
silly but it's so much faster than doing an actual division).</p>
|
|
<p>Also, again signed values can be annoying, because if the value <em>just happens</em>
|
|
to be <code>i32::MIN</code> then when you negate it you'll have... <em>still</em> a negative
|
|
value. I'm not 100% on this, but I think the correct thing to do at that point
|
|
is to give <code>$t::MIN</code> as the output num value.</p>
|
|
<p>Did you get all that? Good, because this involves casting, so we will need to
|
|
implement it three times, which calls for another macro.</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
macro_rules! fixed_point_signed_multiply {
|
|
($t:ident) => {
|
|
impl<F: Unsigned> Mul for Fx<$t, F> {
|
|
type Output = Self;
|
|
fn mul(self, rhs: Fx<$t, F>) -> Self::Output {
|
|
let pre_shift = (self.num as i32).wrapping_mul(rhs.num as i32);
|
|
if pre_shift < 0 {
|
|
if pre_shift == core::i32::MIN {
|
|
Fx {
|
|
num: core::$t::MIN,
|
|
phantom: PhantomData,
|
|
}
|
|
} else {
|
|
Fx {
|
|
num: (-((-pre_shift) >> F::U8)) as $t,
|
|
phantom: PhantomData,
|
|
}
|
|
}
|
|
} else {
|
|
Fx {
|
|
num: (pre_shift >> F::U8) as $t,
|
|
phantom: PhantomData,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
fixed_point_signed_multiply! {i8}
|
|
fixed_point_signed_multiply! {i16}
|
|
fixed_point_signed_multiply! {i32}
|
|
|
|
macro_rules! fixed_point_unsigned_multiply {
|
|
($t:ident) => {
|
|
impl<F: Unsigned> Mul for Fx<$t, F> {
|
|
type Output = Self;
|
|
fn mul(self, rhs: Fx<$t, F>) -> Self::Output {
|
|
Fx {
|
|
num: ((self.num as u32).wrapping_mul(rhs.num as u32) >> F::U8) as $t,
|
|
phantom: PhantomData,
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
fixed_point_unsigned_multiply! {u8}
|
|
fixed_point_unsigned_multiply! {u16}
|
|
fixed_point_unsigned_multiply! {u32}
|
|
#}</code></pre></pre>
|
|
<a class="header" href="#division" id="division"><h2>Division</h2></a>
|
|
<p>Division is similar to multiplication, but reversed. Which makes sense. This
|
|
time <code>A/B</code> gives <code>(a*f)/(b*f)</code> which is <code>a/b</code>, one <em>less</em> <code>f</code> than we were
|
|
after.</p>
|
|
<p>As with the multiplication version of things, we have to up-cast our inner value
|
|
as much a we can before doing the math, to allow for the most precision
|
|
possible.</p>
|
|
<p>The snag here is that the GBA has no division or remainder. Instead, the GBA has
|
|
a BIOS function you can call to do <code>i32/i32</code> division.</p>
|
|
<p>This is a potential problem for us though. If we have some unsigned value, we
|
|
need it to fit within the positive space of an <code>i32</code> <em>after the multiply</em> so
|
|
that we can cast it to <code>i32</code>, call the BIOS function that only works on <code>i32</code>
|
|
values, and cast it back to its actual type.</p>
|
|
<ul>
|
|
<li>If you have a u8 you're always okay, even with 8 floating bits.</li>
|
|
<li>If you have a u16 you're okay even with a maximum value up to 15 floating
|
|
bits, but having a maximum value and 16 floating bits makes it break.</li>
|
|
<li>If you have a u32 you're probably going to be in trouble all the time.</li>
|
|
</ul>
|
|
<p>So... ugh, there's not much we can do about this. For now we'll just have to
|
|
suffer some.</p>
|
|
<p>// TODO: find a numerics book that tells us how to do <code>u32/u32</code> divisions.</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
macro_rules! fixed_point_signed_division {
|
|
($t:ident) => {
|
|
impl<F: Unsigned> Div for Fx<$t, F> {
|
|
type Output = Self;
|
|
fn div(self, rhs: Fx<$t, F>) -> Self::Output {
|
|
let mul_output: i32 = (self.num as i32).wrapping_mul(1 << F::U8);
|
|
let divide_result: i32 = crate::bios::div(mul_output, rhs.num as i32);
|
|
Fx {
|
|
num: divide_result as $t,
|
|
phantom: PhantomData,
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
fixed_point_signed_division! {i8}
|
|
fixed_point_signed_division! {i16}
|
|
fixed_point_signed_division! {i32}
|
|
|
|
macro_rules! fixed_point_unsigned_division {
|
|
($t:ident) => {
|
|
impl<F: Unsigned> Div for Fx<$t, F> {
|
|
type Output = Self;
|
|
fn div(self, rhs: Fx<$t, F>) -> Self::Output {
|
|
let mul_output: i32 = (self.num as i32).wrapping_mul(1 << F::U8);
|
|
let divide_result: i32 = crate::bios::div(mul_output, rhs.num as i32);
|
|
Fx {
|
|
num: divide_result as $t,
|
|
phantom: PhantomData,
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
fixed_point_unsigned_division! {u8}
|
|
fixed_point_unsigned_division! {u16}
|
|
fixed_point_unsigned_division! {u32}
|
|
#}</code></pre></pre>
|
|
<a class="header" href="#trigonometry" id="trigonometry"><h2>Trigonometry</h2></a>
|
|
<p>TODO: look up tables! arcbits!</p>
|
|
<a class="header" href="#just-using-a-crate" id="just-using-a-crate"><h2>Just Using A Crate</h2></a>
|
|
<p>If, after seeing all that, and seeing that I still didn't even cover every
|
|
possible trait impl that you might want for all the possible types... if after
|
|
all that you feel too intimidated, then I'll cave a bit on your behalf and
|
|
suggest to you that the <a href="https://crates.io/crates/fixed">fixed</a> crate seems to
|
|
be the best crate available for fixed point math.</p>
|
|
<p><em>I have not tested its use on the GBA myself</em>.</p>
|
|
<p>It's just my recommendation from looking at the docs of the various options
|
|
available, if you really wanted to just have a crate for it.</p>
|
|
<a class="header" href="#volatile-destination" id="volatile-destination"><h1>Volatile Destination</h1></a>
|
|
<p>TODO: update this when we can make more stuff <code>const</code></p>
|
|
<a class="header" href="#volatile-memory" id="volatile-memory"><h2>Volatile Memory</h2></a>
|
|
<p>The compiler is an eager friend, so when it sees a read or a write that won't
|
|
have an effect, it eliminates that read or write. For example, if we write</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
let mut x = 5;
|
|
x = 7;
|
|
#}</code></pre></pre>
|
|
<p>The compiler won't actually ever put 5 into <code>x</code>. It'll skip straight to putting
|
|
7 in <code>x</code>, because we never read from <code>x</code> when it's 5, so that's a safe change to
|
|
make. Normally, values are stored in RAM, which has no side effects when you
|
|
read and write from it. RAM is purely for keeping notes about values you'll need
|
|
later on.</p>
|
|
<p>However, what if we had a bit of hardware where we wanted to do a write and that
|
|
did something <em>other than</em> keeping the value for us to look at later? As you saw
|
|
in the <code>hello_magic</code> example, we have to use a <code>write_volatile</code> operation.
|
|
Volatile means "just do it anyway". The compiler thinks that it's pointless, but
|
|
we know better, so we can force it to really do exactly what we say by using
|
|
<code>write_volatile</code> instead of <code>write</code>.</p>
|
|
<p>This is kinda error prone though, right? Because it's just a raw pointer, so we
|
|
might forget to use <code>write_volatile</code> at some point.</p>
|
|
<p>Instead, we want a type that's always going to use volatile reads and writes.
|
|
Also, we want a pointer type that lets our reads and writes to be as safe as
|
|
possible once we've unsafely constructed the initial value.</p>
|
|
<a class="header" href="#constructing-the-voladdress-type" id="constructing-the-voladdress-type"><h3>Constructing The VolAddress Type</h3></a>
|
|
<p>First, we want a type that stores a location within the address space. This can
|
|
be a pointer, or a <code>usize</code>, and we'll use a <code>usize</code> because that's easier to
|
|
work with in a <code>const</code> context (and we want to have <code>const</code> when we can get it).
|
|
We'll also have our type use <code>NonZeroUsize</code> instead of just <code>usize</code> so that
|
|
<code>Option<VolAddress<T>></code> stays as a single machine word. This helps quite a bit
|
|
when we want to iterate over the addresses of a block of memory (such as
|
|
locations within the palette memory). Hardware is never at the null address
|
|
anyway. Also, if we had <em>just</em> an address number then we wouldn't be able to
|
|
track what type the address is for. We need some
|
|
<a href="https://doc.rust-lang.org/core/marker/struct.PhantomData.html">PhantomData</a>,
|
|
and specifically we need the phantom data to be for <code>*mut T</code>:</p>
|
|
<ul>
|
|
<li>If we used <code>*const T</code> that'd have the wrong
|
|
<a href="https://doc.rust-lang.org/nomicon/subtyping.html">variance</a>.</li>
|
|
<li>If we used <code>&mut T</code> then that's fusing in the ideas of <em>lifetime</em> and
|
|
<em>exclusive access</em> to our type. That's potentially important, but that's also
|
|
an abstraction we'll build <em>on top of</em> this <code>VolAddress</code> type if we need it.</li>
|
|
</ul>
|
|
<p>One abstraction layer at a time, so we start with just a phantom pointer. This gives us a type that looks like this:</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
#[derive(Debug)]
|
|
#[repr(transparent)]
|
|
pub struct VolAddress<T> {
|
|
address: NonZeroUsize,
|
|
marker: PhantomData<*mut T>,
|
|
}
|
|
#}</code></pre></pre>
|
|
<p>Now, because of how <code>derive</code> is specified, it derives traits <em>if the generic
|
|
parameter</em> supports those traits. Since our type is like a pointer, the traits
|
|
it supports are distinct from whatever traits the target type supports. So we'll
|
|
provide those implementations manually.</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
impl<T> Clone for VolAddress<T> {
|
|
fn clone(&self) -> Self {
|
|
*self
|
|
}
|
|
}
|
|
impl<T> Copy for VolAddress<T> {}
|
|
impl<T> PartialEq for VolAddress<T> {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
self.address == other.address
|
|
}
|
|
}
|
|
impl<T> Eq for VolAddress<T> {}
|
|
impl<T> PartialOrd for VolAddress<T> {
|
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
Some(self.address.cmp(&other.address))
|
|
}
|
|
}
|
|
impl<T> Ord for VolAddress<T> {
|
|
fn cmp(&self, other: &Self) -> Ordering {
|
|
self.address.cmp(&other.address)
|
|
}
|
|
}
|
|
#}</code></pre></pre>
|
|
<p>Boilerplate junk, not interesting. There's a reason that you derive those traits
|
|
99% of the time in Rust.</p>
|
|
<a class="header" href="#constructing-a-voladdress-value" id="constructing-a-voladdress-value"><h3>Constructing A VolAddress Value</h3></a>
|
|
<p>Okay so here's the next core concept: If we unsafely <em>construct</em> a
|
|
<code>VolAddress<T></code>, then we can safely <em>use</em> the value once it's been properly
|
|
created.</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
// you'll need these features enabled and a recent nightly
|
|
#![feature(const_int_wrapping)]
|
|
#![feature(min_const_unsafe_fn)]
|
|
|
|
impl<T> VolAddress<T> {
|
|
pub const unsafe fn new_unchecked(address: usize) -> Self {
|
|
VolAddress {
|
|
address: NonZeroUsize::new_unchecked(address),
|
|
marker: PhantomData,
|
|
}
|
|
}
|
|
pub const unsafe fn cast<Z>(self) -> VolAddress<Z> {
|
|
VolAddress {
|
|
address: self.address,
|
|
marker: PhantomData,
|
|
}
|
|
}
|
|
pub unsafe fn offset(self, offset: isize) -> Self {
|
|
VolAddress {
|
|
address: NonZeroUsize::new_unchecked(self.address.get().wrapping_add(offset as usize * core::mem::size_of::<T>())),
|
|
marker: PhantomData,
|
|
}
|
|
}
|
|
}
|
|
#}</code></pre></pre>
|
|
<p>So what are the unsafety rules here?</p>
|
|
<ul>
|
|
<li>Non-null, obviously.</li>
|
|
<li>Must be aligned for <code>T</code></li>
|
|
<li>Must always produce valid bit patterns for <code>T</code></li>
|
|
<li>Must not be part of the address space that Rust's stack or allocator will ever
|
|
uses.</li>
|
|
</ul>
|
|
<p>So, again using the <code>hello_magic</code> example, we had</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
(0x400_0000 as *mut u16).write_volatile(0x0403);
|
|
#}</code></pre></pre>
|
|
<p>And instead we could declare</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
const MAGIC_LOCATION: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0000) };
|
|
#}</code></pre></pre>
|
|
<a class="header" href="#using-a-voladdress-value" id="using-a-voladdress-value"><h3>Using A VolAddress Value</h3></a>
|
|
<p>Now that we've named the magic location, we want to write to it.</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
impl<T> VolAddress<T> {
|
|
pub fn read(self) -> T
|
|
where
|
|
T: Copy,
|
|
{
|
|
unsafe { (self.address.get() as *mut T).read_volatile() }
|
|
}
|
|
pub unsafe fn read_non_copy(self) -> T {
|
|
(self.address.get() as *mut T).read_volatile()
|
|
}
|
|
pub fn write(self, val: T) {
|
|
unsafe { (self.address.get() as *mut T).write_volatile(val) }
|
|
}
|
|
}
|
|
#}</code></pre></pre>
|
|
<p>So if the type is <code>Copy</code> we can <code>read</code> it as much as we want. If, somehow, the
|
|
type isn't <code>Copy</code>, then it might be <code>Drop</code>, and that means if we read out a
|
|
value over and over we could cause the <code>drop</code> method to trigger UB. Since the
|
|
end user might really know what they're doing, we provide an unsafe backup
|
|
<code>read_non_copy</code>.</p>
|
|
<p>On the other hand, we can <code>write</code> to the location as much as we want. Even if
|
|
the type isn't <code>Copy</code>, <em>not running <code>Drop</code> is safe</em>, so a <code>write</code> is always
|
|
safe.</p>
|
|
<p>Now we can write to our magical location.</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
MAGIC_LOCATION.write(0x0403);
|
|
#}</code></pre></pre>
|
|
<a class="header" href="#voladdress-iteration" id="voladdress-iteration"><h3>VolAddress Iteration</h3></a>
|
|
<p>We've already seen that sometimes we want to have a base address of some sort
|
|
and then offset from that location to another. What if we wanted to iterate over
|
|
<em>all the locations</em>. That's not particularly hard.</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
impl<T> VolAddress<T> {
|
|
pub const unsafe fn iter_slots(self, slots: usize) -> VolAddressIter<T> {
|
|
VolAddressIter { vol_address: self, slots }
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct VolAddressIter<T> {
|
|
vol_address: VolAddress<T>,
|
|
slots: usize,
|
|
}
|
|
impl<T> Clone for VolAddressIter<T> {
|
|
fn clone(&self) -> Self {
|
|
VolAddressIter {
|
|
vol_address: self.vol_address,
|
|
slots: self.slots,
|
|
}
|
|
}
|
|
}
|
|
impl<T> PartialEq for VolAddressIter<T> {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
self.vol_address == other.vol_address && self.slots == other.slots
|
|
}
|
|
}
|
|
impl<T> Eq for VolAddressIter<T> {}
|
|
impl<T> Iterator for VolAddressIter<T> {
|
|
type Item = VolAddress<T>;
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
if self.slots > 0 {
|
|
let out = self.vol_address;
|
|
unsafe {
|
|
self.slots -= 1;
|
|
self.vol_address = self.vol_address.offset(1);
|
|
}
|
|
Some(out)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
impl<T> FusedIterator for VolAddressIter<T> {}
|
|
#}</code></pre></pre>
|
|
<a class="header" href="#voladdressblock" id="voladdressblock"><h3>VolAddressBlock</h3></a>
|
|
<p>Obviously, having a base address and a length exist separately is error prone.
|
|
There's a good reason for slices to keep their pointer and their length
|
|
together. We want something like that, which we'll call a "block" because
|
|
"array" and "slice" are already things in Rust.</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
#[derive(Debug)]
|
|
pub struct VolAddressBlock<T> {
|
|
vol_address: VolAddress<T>,
|
|
slots: usize,
|
|
}
|
|
impl<T> Clone for VolAddressBlock<T> {
|
|
fn clone(&self) -> Self {
|
|
VolAddressBlock {
|
|
vol_address: self.vol_address,
|
|
slots: self.slots,
|
|
}
|
|
}
|
|
}
|
|
impl<T> PartialEq for VolAddressBlock<T> {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
self.vol_address == other.vol_address && self.slots == other.slots
|
|
}
|
|
}
|
|
impl<T> Eq for VolAddressBlock<T> {}
|
|
|
|
impl<T> VolAddressBlock<T> {
|
|
pub const unsafe fn new_unchecked(vol_address: VolAddress<T>, slots: usize) -> Self {
|
|
VolAddressBlock { vol_address, slots }
|
|
}
|
|
pub const fn iter(self) -> VolAddressIter<T> {
|
|
VolAddressIter {
|
|
vol_address: self.vol_address,
|
|
slots: self.slots,
|
|
}
|
|
}
|
|
pub unsafe fn index_unchecked(self, slot: usize) -> VolAddress<T> {
|
|
self.vol_address.offset(slot as isize)
|
|
}
|
|
pub fn index(self, slot: usize) -> VolAddress<T> {
|
|
if slot < self.slots {
|
|
unsafe { self.vol_address.offset(slot as isize) }
|
|
} else {
|
|
panic!("Index Requested: {} >= Bound: {}", slot, self.slots)
|
|
}
|
|
}
|
|
pub fn get(self, slot: usize) -> Option<VolAddress<T>> {
|
|
if slot < self.slots {
|
|
unsafe { Some(self.vol_address.offset(slot as isize)) }
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
#}</code></pre></pre>
|
|
<p>Now we can have something like:</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
const OTHER_MAGIC: VolAddressBlock<u16> = unsafe {
|
|
VolAddressBlock::new_unchecked(
|
|
VolAddress::new_unchecked(0x600_0000),
|
|
240 * 160
|
|
)
|
|
};
|
|
|
|
OTHER_MAGIC.index(120 + 80 * 240).write_volatile(0x001F);
|
|
OTHER_MAGIC.index(136 + 80 * 240).write_volatile(0x03E0);
|
|
OTHER_MAGIC.index(120 + 96 * 240).write_volatile(0x7C00);
|
|
#}</code></pre></pre>
|
|
<a class="header" href="#docs" id="docs"><h3>Docs?</h3></a>
|
|
<p>If you wanna see these types and methods with a full docs write up you should
|
|
check the GBA crate's source.</p>
|
|
<a class="header" href="#volatile-asm" id="volatile-asm"><h2>Volatile ASM</h2></a>
|
|
<p>In addition to some memory locations being volatile, it's also possible for
|
|
inline assembly to be declared volatile. This is basically the same idea, "hey
|
|
just do what I'm telling you, don't get smart about it".</p>
|
|
<p>Normally when you have some <code>asm!</code> it's basically treated like a function,
|
|
there's inputs and outputs and the compiler will try to optimize it so that if
|
|
you don't actually use the outputs it won't bother with doing those
|
|
instructions. However, <code>asm!</code> is basically a pure black box, so the compiler
|
|
doesn't know what's happening inside at all, and it can't see if there's any
|
|
important side effects going on.</p>
|
|
<p>An example of an important side effect that doesn't have output values would be
|
|
putting the CPU into a low power state while we want for the next VBlank. This
|
|
lets us save quite a bit of battery power. It requires some setup to be done
|
|
safely (otherwise the GBA won't ever actually wake back up from the low power
|
|
state), but the <code>asm!</code> you use once you're ready is just a single instruction
|
|
with no return value. The compiler can't tell what's going on, so you just have
|
|
to say "do it anyway".</p>
|
|
<p>Note that if you use a linker script to include any ASM with your Rust program
|
|
(eg: the <code>crt0.s</code> file that we setup in the "Development Setup" section), all of
|
|
that ASM is "volatile" for these purposes. Volatile isn't actually a <em>hardware</em>
|
|
concept, it's just an LLVM concept, and the linker script runs after LLVM has
|
|
done its work.</p>
|
|
<a class="header" href="#newtype" id="newtype"><h1>Newtype</h1></a>
|
|
<p>TODO: we've already used newtype twice by now (fixed point values and volatile
|
|
addresses), so we need to adjust how we start this section.</p>
|
|
<p>There's a great Zero Cost abstraction that we'll be using a lot that you might
|
|
not already be familiar with: we're talking about the "Newtype Pattern"!</p>
|
|
<p>Now, I told you to read the Rust Book before you read this book, and I'm sure
|
|
you're all good students who wouldn't sneak into this book without doing the
|
|
required reading, so I'm sure you all remember exactly what I'm talking about,
|
|
because they touch on the newtype concept in the book twice, in two <em>very</em> long
|
|
named sections:</p>
|
|
<ul>
|
|
<li><a href="https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#using-the-newtype-pattern-to-implement-external-traits-on-external-types">Using the Newtype Pattern to Implement External Traits on External
|
|
Types</a></li>
|
|
<li><a href="https://doc.rust-lang.org/book/ch19-04-advanced-types.html#using-the-newtype-pattern-for-type-safety-and-abstraction">Using the Newtype Pattern for Type Safety and
|
|
Abstraction</a></li>
|
|
</ul>
|
|
<p>...Yeah... The Rust Book doesn't know how to make a short sub-section name to
|
|
save its life. Shame.</p>
|
|
<a class="header" href="#newtype-basics" id="newtype-basics"><h2>Newtype Basics</h2></a>
|
|
<p>So, we have all these pieces of data, and we want to keep them separated, and we
|
|
don't wanna pay the cost for it at runtime. Well, we're in luck, we can pay the
|
|
cost at compile time.</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
pub struct PixelColor(u16);
|
|
#}</code></pre></pre>
|
|
<p>TODO: we've already talked about repr(transparent) by now</p>
|
|
<p>Ah, except that, as I'm sure you remember from <a href="https://doc.rust-lang.org/nomicon/other-reprs.html#reprtransparent">The
|
|
Rustonomicon</a>
|
|
(and from the RFC too, of course), if we have a single field struct that's
|
|
sometimes different from having just the bare value, so we should be using
|
|
<code>#[repr(transparent)]</code> with our newtypes.</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
#[repr(transparent)]
|
|
pub struct PixelColor(u16);
|
|
#}</code></pre></pre>
|
|
<p>And then we'll need to do that same thing for <em>every other newtype we want</em>.</p>
|
|
<p>Except there's only two tiny parts that actually differ between newtype
|
|
declarations: the new name and the base type. All the rest is just the same rote
|
|
code over and over. Generating piles and piles of boilerplate code? Sounds like
|
|
a job for a macro to me!</p>
|
|
<a class="header" href="#making-it-a-macro" id="making-it-a-macro"><h2>Making It A Macro</h2></a>
|
|
<p>If you're going to do much with macros you should definitely read through <a href="https://danielkeep.github.io/tlborm/book/index.html">The
|
|
Little Book of Rust
|
|
Macros</a>, but we won't be
|
|
doing too much so you can just follow along here a bit if you like.</p>
|
|
<p>The most basic version of a newtype macro starts like this:</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
#[macro_export]
|
|
macro_rules! newtype {
|
|
($new_name:ident, $old_name:ident) => {
|
|
#[repr(transparent)]
|
|
pub struct $new_name($old_name);
|
|
};
|
|
}
|
|
#}</code></pre></pre>
|
|
<p>The <code>#[macro_export]</code> makes it exported by the current module (like <code>pub</code>
|
|
kinda), and then we have one expansion option that takes an identifier, a <code>,</code>,
|
|
and then a second identifier. The new name is the outer type we'll be using, and
|
|
the old name is the inner type that's being wrapped. You'd use our new macro
|
|
something like this:</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
newtype! {PixelColorCurly, u16}
|
|
|
|
newtype!(PixelColorParens, u16);
|
|
|
|
newtype![PixelColorBrackets, u16];
|
|
#}</code></pre></pre>
|
|
<p>Note that you can invoke the macro with the outermost grouping as any of <code>()</code>,
|
|
<code>[]</code>, or <code>{}</code>. It makes no particular difference to the macro. Also, that space
|
|
in the first version is kinda to show off that you can put white space in
|
|
between the macro name and the grouping if you want. The difference is mostly
|
|
style, but there are some rules and considerations here:</p>
|
|
<ul>
|
|
<li>If you use curly braces then you <em>must not</em> put a <code>;</code> after the invocation.</li>
|
|
<li>If you use parentheses or brackets then you <em>must</em> put the <code>;</code> at the end.</li>
|
|
<li>Rustfmt cares which you use and formats accordingly:
|
|
<ul>
|
|
<li>Curly brace macro use mostly gets treated like a code block.</li>
|
|
<li>Parentheses macro use mostly gets treated like a function call.</li>
|
|
<li>Bracket macro use mostly gets treated like an array declaration.</li>
|
|
</ul>
|
|
</li>
|
|
</ul>
|
|
<a class="header" href="#upgrade-that-macro" id="upgrade-that-macro"><h2>Upgrade That Macro!</h2></a>
|
|
<p>We also want to be able to add <code>derive</code> stuff and doc comments to our newtype.
|
|
Within the context of <code>macro_rules!</code> definitions these are called "meta". Since
|
|
we can have any number of them we wrap it all up in a "zero or more" matcher.
|
|
Then our macro looks like this:</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
#[macro_export]
|
|
macro_rules! newtype {
|
|
($(#[$attr:meta])* $new_name:ident, $old_name:ident) => {
|
|
$(#[$attr])*
|
|
#[repr(transparent)]
|
|
pub struct $new_name($old_name);
|
|
};
|
|
}
|
|
#}</code></pre></pre>
|
|
<p>So now we can write</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
newtype! {
|
|
/// Color on the GBA gives 5 bits for each channel, the highest bit is ignored.
|
|
#[derive(Debug, Clone, Copy)]
|
|
PixelColor, u16
|
|
}
|
|
#}</code></pre></pre>
|
|
<p>And that's about all we'll need for the examples.</p>
|
|
<p><strong>As a reminder:</strong> remember that <code>macro_rules</code> macros have to appear <em>before</em>
|
|
they're invoked in your source, so the <code>newtype</code> macro will always have to be at
|
|
the very top of your file, or if you put it in a module within your project
|
|
you'll need to declare the module before anything that uses it.</p>
|
|
<a class="header" href="#potential-homework" id="potential-homework"><h2>Potential Homework</h2></a>
|
|
<p>If you wanted to keep going and get really fancy with it, you could potentially
|
|
add a lot more:</p>
|
|
<ul>
|
|
<li>Make a <code>pub const fn new() -> Self</code> method that outputs the base value in a
|
|
const way. Combine this with builder style "setter" methods that are also
|
|
const and you can get the compiler to do quite a bit of the value building
|
|
work at compile time.</li>
|
|
<li>Making the macro optionally emit a <code>From</code> impl to unwrap it back into the base
|
|
type.</li>
|
|
<li>Allow for visibility modifiers to be applied to the inner field and the newly
|
|
generated type.</li>
|
|
<li>Allowing for generic newtypes. You already saw the need for this once in the
|
|
volatile section. Unfortunately, this particular part gets really tricky if
|
|
you're using <code>macro_rules!</code>, so you might need to move up to a full
|
|
<code>proc_macro</code>. Having a <code>proc_macro</code> isn't bad except that they have to be
|
|
defined in a crate of their own and they're compiled before use. You can't
|
|
ever use them in the crate that defines them, so we won't be using them in any
|
|
of our single file examples.</li>
|
|
<li>Allowing for optional <code>Deref</code> and <code>DerefMut</code> of the inner value. This takes
|
|
away most all the safety aspect of doing the newtype, but there may be times
|
|
for it. As an example, you could make a newtype with a different form of
|
|
Display impl that you want to otherwise treat as the base type in all places.</li>
|
|
</ul>
|
|
<a class="header" href="#constant-assertions" id="constant-assertions"><h1>Constant Assertions</h1></a>
|
|
<p>Have you ever wanted to assert things <em>even before runtime</em>? We all have, of
|
|
course. Particularly when the runtime machine is a poor little GBA, we'd like to
|
|
have the machine doing the compile handle as much checking as possible.</p>
|
|
<p>Enter the <a href="https://docs.rs/static_assertions/">static assertions</a> crate, which
|
|
provides a way to let you assert on a <code>const</code> expression.</p>
|
|
<p>This is an amazing crate that you should definitely use when you can.</p>
|
|
<p>It's written by <a href="https://github.com/nvzqz">Nikolai Vazquez</a>, and they kindly
|
|
wrote up a <a href="https://nikolaivazquez.com/posts/programming/rust-static-assertions/">blog
|
|
post</a> that
|
|
explains the thinking behind it.</p>
|
|
<p>However, I promised that each example would be single file, and I also promised
|
|
to explain what's going on as we go, so we'll briefly touch upon giving an
|
|
explanation here.</p>
|
|
<a class="header" href="#how-we-const-assert" id="how-we-const-assert"><h2>How We Const Assert</h2></a>
|
|
<p>Alright, as it stands (2018-12-15), we can't use <code>if</code> in a <code>const</code> context.</p>
|
|
<p>Since we can't use <code>if</code>, we can't use a normal <code>assert!</code>. Some day it will be
|
|
possible, and a failed assert at compile time will be a compile error and a
|
|
failed assert at run time will be a panic and we'll have a nice unified
|
|
programming experience. We can add runtime-only assertions by being a little
|
|
tricky with the compiler.</p>
|
|
<p>If we write</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
const ASSERT: usize = 0 - 1;
|
|
#}</code></pre></pre>
|
|
<p>that gives a warning, since the math would underflow. We can upgrade that
|
|
warning to a hard error:</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
#[deny(const_err)]
|
|
const ASSERT: usize = 0 - 1;
|
|
#}</code></pre></pre>
|
|
<p>And to make our construction reusable we can enable the
|
|
<a href="https://github.com/rust-lang/rust/issues/54912">underscore_const_names</a> feature
|
|
in our program (or library) and then give each such const an underscore for a
|
|
name.</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#![feature(underscore_const_names)]
|
|
|
|
#fn main() {
|
|
#[deny(const_err)]
|
|
const _: usize = 0 - 1;
|
|
#}</code></pre></pre>
|
|
<p>Now we wrap this in a macro where we give a <code>bool</code> expression as input. We
|
|
negate the bool then cast it to a <code>usize</code>, meaning that <code>true</code> negates into
|
|
<code>false</code>, which becomes <code>0usize</code>, and then there's no underflow error. Or if the
|
|
input was <code>false</code>, it negates into <code>true</code>, then becomes <code>1usize</code>, and then the
|
|
underflow error fires.</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
macro_rules! const_assert {
|
|
($condition:expr) => {
|
|
#[deny(const_err)]
|
|
#[allow(dead_code)]
|
|
const ASSERT: usize = 0 - !$condition as usize;
|
|
}
|
|
}
|
|
#}</code></pre></pre>
|
|
<p>Technically, written like this, the expression can be anything with a
|
|
<code>core::ops::Not</code> implementation that can also be <code>as</code> cast into <code>usize</code>. That's
|
|
<code>bool</code>, but also basically all the other number types. Since we want to ensure
|
|
that we get proper looking type errors when things go wrong, we can use
|
|
<code>($condition && true)</code> to enforce that we get a <code>bool</code> (thanks to <code>Talchas</code> for
|
|
that particular suggestion).</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
macro_rules! const_assert {
|
|
($condition:expr) => {
|
|
#[deny(const_err)]
|
|
#[allow(dead_code)]
|
|
const _: usize = 0 - !($condition && true) as usize;
|
|
}
|
|
}
|
|
#}</code></pre></pre>
|
|
<a class="header" href="#asserting-something" id="asserting-something"><h2>Asserting Something</h2></a>
|
|
<p>As an example of how we might use a <code>const_assert</code>, we'll do a demo with colors.
|
|
There's a red, blue, and green channel. We store colors in a <code>u16</code> with 5 bits
|
|
for each channel.</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
newtype! {
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
Color, u16
|
|
}
|
|
#}</code></pre></pre>
|
|
<p>And when we're building a color, we're passing in <code>u16</code> values, but they could
|
|
be using more than just 5 bits of space. We want to make sure that each channel
|
|
is 31 or less, so we can make a color builder that does a <code>const_assert!</code> on the
|
|
value of each channel.</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
macro_rules! rgb {
|
|
($r:expr, $g:expr, $b:expr) => {
|
|
{
|
|
const_assert!($r <= 31);
|
|
const_assert!($g <= 31);
|
|
const_assert!($b <= 31);
|
|
Color($b << 10 | $g << 5 | $r)
|
|
}
|
|
}
|
|
}
|
|
#}</code></pre></pre>
|
|
<p>And then we can declare some colors</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
const RED: Color = rgb!(31, 0, 0);
|
|
|
|
const BLUE: Color = rgb!(31, 500, 0);
|
|
#}</code></pre></pre>
|
|
<p>The second one is clearly out of bounds and it fires an error just like we
|
|
wanted.</p>
|
|
<a class="header" href="#broad-concepts" id="broad-concepts"><h1>Broad Concepts</h1></a>
|
|
<p>The GameBoy Advance sits in a middle place between the chthonic game consoles of
|
|
the ancient past and the "small PC in a funny case" consoles of the modern age.</p>
|
|
<p>On the one hand, yeah, you're gonna find a few strange conventions as you learn
|
|
all the ropes.</p>
|
|
<p>On the other, at least we're writing in Rust at all, and not having to do all
|
|
the assembly by hand.</p>
|
|
<p>This chapter for "concepts" has a section for each part of the GBA's hardware
|
|
memory map, going by increasing order of base address value. The sections try to
|
|
explain as much as possible while sticking to just the concerns you might have
|
|
regarding that part of the memory map.</p>
|
|
<p>For an assessment of how to wrangle all three parts of the video system (PALRAM,
|
|
VRAM, and OAM), along with the correct IO registers, into something that shows a
|
|
picture, you'll want the Video chapter.</p>
|
|
<p>Similarly, the "IO Registers" part of the GBA actually controls how you interact
|
|
with every single bit of hardware connected to the GBA. A full description of
|
|
everything is obviously too much for just one section of the book. Instead you
|
|
get an overview of general IO register rules and advice. Each particular
|
|
register is described in the appropriate sections of either the Video or
|
|
Non-Video chapters.</p>
|
|
<a class="header" href="#bus-size" id="bus-size"><h2>Bus Size</h2></a>
|
|
<p>TODO: describe this</p>
|
|
<a class="header" href="#minimum-write-size" id="minimum-write-size"><h2>Minimum Write Size</h2></a>
|
|
<p>TODO: talk about parts where you can't write one byte at a time</p>
|
|
<a class="header" href="#volatile-or-not" id="volatile-or-not"><h2>Volatile or Not?</h2></a>
|
|
<p>TODO: discuss what memory should be used volatile style and what can be used normal style.</p>
|
|
<a class="header" href="#cpu" id="cpu"><h1>CPU</h1></a>
|
|
<a class="header" href="#bios" id="bios"><h1>BIOS</h1></a>
|
|
<ul>
|
|
<li><strong>Address Span:</strong> <code>0x0</code> to <code>0x3FFF</code> (16k)</li>
|
|
</ul>
|
|
<p>The <a href="https://en.wikipedia.org/wiki/BIOS">BIOS</a> of the GBA is a small read-only
|
|
portion of memory at the very base of the address space. However, it is also
|
|
hardware protected against reading, so if you try to read from BIOS memory when
|
|
the program counter isn't pointed into the BIOS (eg: any time code <em>you</em> write
|
|
is executing) then you get <a href="https://problemkaputt.de/gbatek.htm#gbaunpredictablethings">basically garbage
|
|
data</a> back.</p>
|
|
<p>So we're not going to spend time here talking about what bits to read or write
|
|
within BIOS memory like we do with the other sections. Instead we're going to
|
|
spend time talking about <a href="https://doc.rust-lang.org/unstable-book/language-features/asm.html">inline
|
|
assembly</a>
|
|
(<a href="https://github.com/rust-lang/rust/issues/29722">tracking issue</a>) and then use
|
|
it to call the <a href="https://problemkaputt.de/gbatek.htm#biosfunctions">GBA BIOS
|
|
Functions</a>.</p>
|
|
<p>Note that BIOS calls have <em>more overhead than normal function calls</em>, so don't
|
|
go using them all over the place if you don't have to. They're also usually
|
|
written more to be compact in terms of code than for raw speed, so you actually
|
|
can out speed them in some cases. Between the increased overhead and not being
|
|
as speed optimized, you can sometimes do a faster job without calling the BIOS
|
|
at all. (TODO: investigate more about what parts of the BIOS we could
|
|
potentially offer faster alternatives for.)</p>
|
|
<p>I'd like to take a moment to thank <a href="https://github.com/mbr">Marc Brinkmann</a>
|
|
(with contributions from <a href="https://github.com/oli-obk">Oliver Schneider</a> and
|
|
<a href="https://github.com/phil-opp">Philipp Oppermann</a>) for writing <a href="http://embed.rs/articles/2016/arm-inline-assembly-rust/">this blog
|
|
post</a>. It's at least
|
|
ten times the tutorial quality as the <code>asm</code> entry in the Unstable Book has. In
|
|
fairness to the Unstable Book, the actual spec of how inline ASM works in rust
|
|
is "basically what clang does", and that's specified as "basically what GCC
|
|
does", and that's basically/shockingly not specified much at all despite GCC
|
|
being like 30 years old.</p>
|
|
<p>So let's be slow and pedantic about this process.</p>
|
|
<a class="header" href="#inline-asm" id="inline-asm"><h2>Inline ASM</h2></a>
|
|
<p><strong>Fair Warning:</strong> Inline asm is one of the least stable parts of Rust overall,
|
|
and if you write bad things you can trigger internal compiler errors and panics
|
|
and crashes and make LLVM choke and die without explanation. If you write some
|
|
inline asm and then suddenly your program suddenly stops compiling without
|
|
explanation, try commenting out that whole inline asm use and see if it's
|
|
causing the problem. Double check that you've written every single part of the
|
|
asm call absolutely correctly, etc, etc.</p>
|
|
<p><strong>Bonus Warning:</strong> The general information that follows regarding the asm macro
|
|
is consistent from system to system, but specific information about register
|
|
names, register quantities, asm instruction argument ordering, and so on is
|
|
specific to ARM on the GBA. If you're programming for any other device you'll
|
|
need to carefully investigate that before you begin.</p>
|
|
<p>Now then, with those out of the way, the inline asm docs describe an asm call as
|
|
looking like this:</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
asm!(assembly template
|
|
: output operands
|
|
: input operands
|
|
: clobbers
|
|
: options
|
|
);
|
|
#}</code></pre></pre>
|
|
<p>And once you stick a lot of stuff in there it can <em>absolutely</em> be hard to
|
|
remember the ordering of the elements. So we'll start with a code block that
|
|
has some comments thrown in on each line:</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
asm!(/* ASM */ TODO
|
|
:/* OUT */ TODO
|
|
:/* INP */ TODO
|
|
:/* CLO */ TODO
|
|
:/* OPT */
|
|
);
|
|
#}</code></pre></pre>
|
|
<p>Now we have to decide what we're gonna write. Obviously we're going to do some
|
|
instructions, but those instructions use registers, and how are we gonna talk
|
|
about them? We've got two choices.</p>
|
|
<ol>
|
|
<li>
|
|
<p>We can pick each and every register used by specifying exact register names.
|
|
In THUMB mode we have 8 registers available, named <code>r0</code> through <code>r7</code>. If you
|
|
switch into 32-bit mode there's additional registers that are also available.</p>
|
|
</li>
|
|
<li>
|
|
<p>We can specify slots for registers we need and let LLVM decide. In this style
|
|
you name your slots <code>$0</code>, <code>$1</code> and so on. Slot numbers are assigned first to
|
|
all specified outputs, then to all specified inputs, in the order that you
|
|
list them.</p>
|
|
</li>
|
|
</ol>
|
|
<p>In the case of the GBA BIOS, each BIOS function has pre-designated input and
|
|
output registers, so we will use the first style. If you use inline ASM in other
|
|
parts of your code you're free to use the second style.</p>
|
|
<a class="header" href="#assembly" id="assembly"><h3>Assembly</h3></a>
|
|
<p>This is just one big string literal. You write out one instruction per line, and
|
|
excess whitespace is ignored. You can also do comments within your assembly
|
|
using <code>;</code> to start a comment that goes until the end of the line.</p>
|
|
<p>Assembly convention doesn't consider it unreasonable to comment potentially as
|
|
much as <em>every single line</em> of asm that you write when you're getting used to
|
|
things. Or even if you are used to things. This is cryptic stuff, there's a
|
|
reason we avoid writing in it as much as possible.</p>
|
|
<p>Remember that our Rust code is in 16-bit mode. You <em>can</em> switch to 32-bit mode
|
|
within your asm as long as you switch back by the time the block ends. Otherwise
|
|
you'll have a bad time.</p>
|
|
<a class="header" href="#outputs" id="outputs"><h3>Outputs</h3></a>
|
|
<p>A comma separated list. Each entry looks like</p>
|
|
<ul>
|
|
<li><code>"constraint" (binding)</code></li>
|
|
</ul>
|
|
<p>An output constraint starts with a symbol:</p>
|
|
<ul>
|
|
<li><code>=</code> for write only</li>
|
|
<li><code>+</code> for reads and writes</li>
|
|
<li><code>&</code> for for "early clobber", meaning that you'll write to this at some point
|
|
before all input values have been read. It prevents this register from being
|
|
assigned to an input register.</li>
|
|
</ul>
|
|
<p>Followed by <em>either</em> the letter <code>r</code> (if you want LLVM to pick the register to
|
|
use) or curly braces around a specific register (if you want to pick).</p>
|
|
<ul>
|
|
<li>The binding can be any single 32-bit or smaller value.</li>
|
|
<li>If your binding has bit pattern requirements ("must be non-zero", etc) you are
|
|
responsible for upholding that.</li>
|
|
<li>If your binding type will try to <code>Drop</code> later then you are responsible for it
|
|
being in a fit state to do that.</li>
|
|
<li>The binding must be either a mutable binding or a binding that was
|
|
pre-declared but not yet assigned.</li>
|
|
</ul>
|
|
<p>Anything else is UB.</p>
|
|
<a class="header" href="#inputs" id="inputs"><h3>Inputs</h3></a>
|
|
<p>This is a similar comma separated list.</p>
|
|
<ul>
|
|
<li><code>"constraint" (binding)</code></li>
|
|
</ul>
|
|
<p>An input constraint doesn't have the symbol prefix, you just pick either <code>r</code> or
|
|
a named register with curly braces around it.</p>
|
|
<ul>
|
|
<li>An input binding must be a single 32-bit or smaller value.</li>
|
|
<li>An input binding <em>should</em> be a type that is <code>Copy</code> but this is not an absolute
|
|
requirement. Having the input be read is semantically similar to using
|
|
<code>core::ptr::read(&binding)</code> and forgetting the value when you're done.</li>
|
|
</ul>
|
|
<a class="header" href="#clobbers" id="clobbers"><h3>Clobbers</h3></a>
|
|
<p>Sometimes your asm will touch registers other than the ones declared for input
|
|
and output.</p>
|
|
<p>Clobbers are declared as a comma separated list of string literals naming
|
|
specific registers. You don't use curly braces with clobbers.</p>
|
|
<p>LLVM <em>needs</em> to know this information. It can move things around to keep your
|
|
data safe, but only if you tell it what's about to happen.</p>
|
|
<p>Failure to define all of your clobbers can cause UB.</p>
|
|
<a class="header" href="#options" id="options"><h3>Options</h3></a>
|
|
<p>There's only one option we'd care to specify. That option is "volatile".</p>
|
|
<p>Just like with a function call, LLVM will skip a block of asm if it doesn't see
|
|
that any outputs from the asm were used later on. Nearly every single BIOS call
|
|
(other than the math operations) will need to be marked as "volatile".</p>
|
|
<a class="header" href="#bios-asm" id="bios-asm"><h3>BIOS ASM</h3></a>
|
|
<ul>
|
|
<li>Inputs are always <code>r0</code>, <code>r1</code>, <code>r2</code>, and/or <code>r3</code>, depending on function.</li>
|
|
<li>Outputs are always zero or more of <code>r0</code>, <code>r1</code>, and <code>r3</code>.</li>
|
|
<li>Any of the output registers that aren't actually used should be marked as
|
|
clobbered.</li>
|
|
<li>All other registers are unaffected.</li>
|
|
</ul>
|
|
<p>All of the GBA BIOS calls are performed using the
|
|
<a href="http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0068b/BABFCEEG.html">swi</a>
|
|
instruction, combined with a value depending on what BIOS function you're trying
|
|
to invoke. If you're in 16-bit code you use the value directly, and if you're in
|
|
32-bit mode you shift the value up by 16 bits first.</p>
|
|
<a class="header" href="#example-bios-function-division" id="example-bios-function-division"><h3>Example BIOS Function: Division</h3></a>
|
|
<p>For our example we'll use the division function, because GBATEK gives very clear
|
|
instructions on how each register is used with that one:</p>
|
|
<pre><code class="language-txt">Signed Division, r0/r1.
|
|
r0 signed 32bit Number
|
|
r1 signed 32bit Denom
|
|
Return:
|
|
r0 Number DIV Denom ;signed
|
|
r1 Number MOD Denom ;signed
|
|
r3 ABS (Number DIV Denom) ;unsigned
|
|
For example, incoming -1234, 10 should return -123, -4, +123.
|
|
The function usually gets caught in an endless loop upon division by zero.
|
|
</code></pre>
|
|
<p>The math folks tell me that the <code>r1</code> value should be properly called the
|
|
"remainder" not the "modulus". We'll go with that for our function, doesn't hurt
|
|
to use the correct names. Our Rust function has an assert against dividing by
|
|
<code>0</code>, then we name some bindings <em>without</em> giving them a value, we make the asm
|
|
call, and then return what we got.</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
pub fn div_rem(numerator: i32, denominator: i32) -> (i32, i32) {
|
|
assert!(denominator != 0);
|
|
let div_out: i32;
|
|
let rem_out: i32;
|
|
unsafe {
|
|
asm!(/* ASM */ "swi 0x06"
|
|
:/* OUT */ "={r0}"(div_out), "={r1}"(rem_out)
|
|
:/* INP */ "{r0}"(numerator), "{r1}"(denominator)
|
|
:/* CLO */ "r3"
|
|
:/* OPT */
|
|
);
|
|
}
|
|
(div_out, rem_out)
|
|
}
|
|
#}</code></pre></pre>
|
|
<p>I <em>hope</em> this all makes sense by now.</p>
|
|
<a class="header" href="#specific-bios-functions" id="specific-bios-functions"><h2>Specific BIOS Functions</h2></a>
|
|
<p>For a full list of all the specific BIOS functions and their use you should
|
|
check the <code>gba::bios</code> module within the <code>gba</code> crate. There's just so many of
|
|
them that enumerating them all here wouldn't serve much purpose.</p>
|
|
<p>Which is not to say that we'll never cover any BIOS functions in this book!
|
|
Instead, we'll simply mention them when whenever they're relevent to the task at
|
|
hand (such as controlling sound or waiting for vblank).</p>
|
|
<p>//TODO: list/name all BIOS functions as well as what they relate to elsewhere.</p>
|
|
<a class="header" href="#work-ram" id="work-ram"><h1>Work RAM</h1></a>
|
|
<a class="header" href="#external-work-ram-ewram" id="external-work-ram-ewram"><h2>External Work RAM (EWRAM)</h2></a>
|
|
<ul>
|
|
<li><strong>Address Span:</strong> <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>It's most helpful to think of EWRAM as slower, distant memory, similar to the
|
|
"heap" in a normal application. You can take the time to go store something
|
|
within EWRAM, or to load it out of EWRAM, but if you've got several operations
|
|
to do in a row and you're worried about time you should pull that value into
|
|
local memory, work on your local copy, and then push it back out to EWRAM.</p>
|
|
<a class="header" href="#internal-work-ram-iwram" id="internal-work-ram-iwram"><h2>Internal Work RAM (IWRAM)</h2></a>
|
|
<ul>
|
|
<li><strong>Address Span:</strong> <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 mostly up to you. The user's stack space starts at <code>0x3007F00</code>
|
|
and proceeds <em>down</em> from there. For best results you should probably start at
|
|
<code>0x3000000</code> and then go upwards. Under normal use it's unlikely that the two
|
|
memory regions will crash into each other.</p>
|
|
<a class="header" href="#io-registers" id="io-registers"><h1>IO Registers</h1></a>
|
|
<ul>
|
|
<li><strong>Address Span:</strong> <code>0x400_0000</code> to <code>0x400_03FE</code></li>
|
|
</ul>
|
|
<a class="header" href="#palette-ram-palram" id="palette-ram-palram"><h1>Palette RAM (PALRAM)</h1></a>
|
|
<ul>
|
|
<li><strong>Address Span:</strong> <code>0x500_0000</code> to <code>0x500_03FF</code> (1k)</li>
|
|
</ul>
|
|
<p>Palette RAM has a 16-bit bus, which isn't really a problem because it
|
|
conceptually just holds <code>u16</code> values. There's no automatic wait state, but if
|
|
you try to access the same location that the display controller is accessing you
|
|
get bumped by 1 cycle. Since the display controller can use the palette ram any
|
|
number of times per scanline it's basically impossible to predict if you'll have
|
|
to do a wait or not during VDraw. During VBlank you won't have any wait of
|
|
course.</p>
|
|
<p>PALRAM is among the memory where there's weirdness if you try to write just one
|
|
byte: if you try to write just 1 byte, it writes that byte into <em>both</em> parts of
|
|
the larger 16-bit location. This doesn't really affect us much with PALRAM,
|
|
because palette values are all supposed to be <code>u16</code> anyway.</p>
|
|
<p>The palette memory actually contains not one, but <em>two</em> sets of palettes. First
|
|
there's 256 entries for the background palette data (starting at <code>0x5000000</code>),
|
|
and then there's 256 entries for object palette data (starting at <code>0x5000200</code>).</p>
|
|
<p>The GBA also has two modes for palette access: 8-bits-per-pixel (8bpp) and
|
|
4-bits-per-pixel (4bpp).</p>
|
|
<ul>
|
|
<li>In 8bpp mode an 8-bit palette index value within a background or sprite
|
|
simply indexes directly into the 256 slots for that type of thing.</li>
|
|
<li>In 4bpp mode a 4-bit palette index value within a background or sprite
|
|
specifies an index within a particular "palbank" (16 palette entries each),
|
|
and then a <em>separate</em> setting outside of the graphical data determines which
|
|
palbank is to be used for that background or object (the screen entry data for
|
|
backgrounds, and the object attributes for objects).</li>
|
|
</ul>
|
|
<a class="header" href="#transparency" id="transparency"><h3>Transparency</h3></a>
|
|
<p>When a pixel within a background or object specifies index 0 as its palette
|
|
entry it is treated as a transparent pixel. This means that in 8bpp mode there's
|
|
only 255 actual color options (0 being transparent), and in 4bpp mode there's
|
|
only 15 actual color options available within each palbank (the 0th entry of
|
|
<em>each</em> palbank is transparent).</p>
|
|
<p>Individual backgrounds, and individual objects, each determine if they're 4bpp
|
|
or 8bpp separately, so a given overall palette slot might map to a used color in
|
|
8bpp and an unused/transparent color in 4bpp. If you're a palette wizard.</p>
|
|
<p>Palette slot 0 of the overall background palette is used to determine the
|
|
"backdrop" color. That's the color you see if no background or object ends up
|
|
being rendered within a given pixel.</p>
|
|
<p>Since display mode 3 and display mode 5 don't use the palette, they cannot
|
|
benefit from transparency.</p>
|
|
<a class="header" href="#video-ram-vram" id="video-ram-vram"><h1>Video RAM (VRAM)</h1></a>
|
|
<ul>
|
|
<li><strong>Address Span:</strong> <code>0x600_0000</code> to <code>0x601_7FFF</code> (96k)</li>
|
|
</ul>
|
|
<p>We've used this before! VRAM has a 16-bit bus and no wait. However, the same as
|
|
with PALRAM, the "you might have to wait if the display controller is looking at
|
|
it" rule applies here.</p>
|
|
<p>Unfortunately there's not much more exact detail that can be given about VRAM.
|
|
The use of the memory depends on the video mode that you're using.</p>
|
|
<p>One general detail of note is that you can't write individual bytes to any part
|
|
of VRAM. Depending on mode and location, you'll either get your bytes doubled
|
|
into both the upper and lower parts of the 16-bit location targeted, or you
|
|
won't even affect the memory. This usually isn't a big deal, except in two
|
|
situations:</p>
|
|
<ul>
|
|
<li>In Mode 4, if you want to change just 1 pixel, you'll have to be very careful
|
|
to read the old <code>u16</code>, overwrite just the byte you wanted to change, and then
|
|
write that back.</li>
|
|
<li>In any display mode, avoid using <code>memcopy</code> to place things into VRAM.
|
|
It's written to be byte oriented, and only does 32-bit transfers under select
|
|
conditions. The rest of the time it'll copy one byte at a time and you'll get
|
|
either garbage or nothing at all.</li>
|
|
</ul>
|
|
<a class="header" href="#object-attribute-memory-oam" id="object-attribute-memory-oam"><h1>Object Attribute Memory (OAM)</h1></a>
|
|
<ul>
|
|
<li><strong>Address Span:</strong> <code>0x700_0000</code> to <code>0x700_03FF</code> (1k)</li>
|
|
</ul>
|
|
<p>The Object Attribute Memory has a 32-bit bus and no default wait, but suffers
|
|
from the "you might have to wait if the display controller is looking at it"
|
|
rule. You cannot write individual bytes to OAM at all, but that's not really a
|
|
problem because all the fields of the data types within OAM are either <code>i16</code> or
|
|
<code>u16</code> anyway.</p>
|
|
<p>Object attribute memory is the wildest yet: it conceptually contains two types
|
|
of things, but they're <em>interlaced</em> with each other all the way through.</p>
|
|
<p>Now, <a href="http://problemkaputt.de/gbatek.htm#lcdobjoamattributes">GBATEK</a> and
|
|
<a href="https://www.cs.rit.edu/%7Etjh8300/CowBite/CowBiteSpec.htm#OAM%20(sprites)">CowByte</a>
|
|
doesn't quite give names to the two data types here.
|
|
<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>, but we'll be giving them names fitting with the
|
|
Rust naming convention. Just know that if you try to talk about it with others
|
|
they might not be using the same names. 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 ObjectAttributes {
|
|
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>ObjectAttributes</code> value is 8 bytes, so
|
|
naturally we can support up to 128 objects.</p>
|
|
<p><em>At the same time</em>, we've got 1024 bytes in OAM and each <code>AffineMatrix</code> is 32
|
|
bytes, so we can have 32 of them.</p>
|
|
<p>But, as I said, these things are all <em>interlaced</em> with each other. See how
|
|
there's "filler" fields in each struct? If we imagine the OAM as being just an
|
|
array of one type or the other, indexes 0/1/2/3 of the <code>ObjectAttributes</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-rom" id="game-pak-rom--flash-rom-rom"><h1>Game Pak ROM / Flash ROM (ROM)</h1></a>
|
|
<ul>
|
|
<li><strong>Address Span (Wait State 0):</strong> <code>0x800_0000</code> to <code>0x9FF_FFFF</code></li>
|
|
<li><strong>Address Span (Wait State 1):</strong> <code>0xA00_0000</code> to <code>0xBFF_FFFF</code></li>
|
|
<li><strong>Address Span (Wait State 2):</strong> <code>0xC00_0000</code> to <code>0xDFF_FFFF</code></li>
|
|
</ul>
|
|
<p>The game's ROM data is a single set of data that's up to 32 megabytes in size.
|
|
However, that data is mirrored to three different locations in the address
|
|
space. Depending on which part of the address space you use, it can affect the
|
|
memory timings involved.</p>
|
|
<p>TODO: describe <code>WAITCNT</code> here, we won't get a better chance at it.</p>
|
|
<p>TODO: discuss THUMB vs ARM code and why THUMB is so much faster (because ROM is a 16-bit bus)</p>
|
|
<a class="header" href="#save-ram-sram" id="save-ram-sram"><h1>Save RAM (SRAM)</h1></a>
|
|
<ul>
|
|
<li><strong>Address Span:</strong> <code>0xE00_0000</code> to <code>0xE00FFFF</code> (64k)</li>
|
|
</ul>
|
|
<p>The actual amount of SRAM available depends on your game pak, and the 64k figure
|
|
is simply the maximum possible. A particular game pak might have less, and an
|
|
emulator will likely let you have all 64k if you want.</p>
|
|
<p>As with other portions of the address space, SRAM has some number of wait cycles
|
|
per use. As with ROM, you can change the wait cycle settings via the <code>WAITCNT</code>
|
|
register if the defaults don't work well for your game pak. See the ROM section
|
|
for full details of how the <code>WAITCNT</code> register works.</p>
|
|
<p>The game pak SRAM also has only an 8-bit bus, so have fun with that.</p>
|
|
<p>The GBA Direct Memory Access (DMA) unit cannot access SRAM.</p>
|
|
<p>Also, you <a href="https://problemkaputt.de/gbatek.htm#gbacartbackupsramfram">should not write to SRAM with code executing from
|
|
ROM</a>. Instead, you
|
|
should move the code to WRAM and execute the save code from there. We'll cover
|
|
how to handle that eventually.</p>
|
|
<a class="header" href="#video" id="video"><h1>Video</h1></a>
|
|
<p>GBA Video starts with an IO register called the "Display Control Register", and
|
|
then spirals out from there. You generally have to use Palette RAM (PALRAM),
|
|
Video RAM (VRAM), Object Attribute Memory (OAM), as well as any number of other
|
|
IO registers.</p>
|
|
<p>They all have to work together just right, and there's a lot going on when you
|
|
first try doing it, so try to take it very slowly as you're learning each step.</p>
|
|
<a class="header" href="#rbg15-color" id="rbg15-color"><h1>RBG15 Color</h1></a>
|
|
<a class="header" href="#todo" id="todo"><h1>TODO</h1></a>
|
|
<a class="header" href="#non-video" id="non-video"><h1>Non-Video</h1></a>
|
|
<p>Besides video effects the GBA still has an okay amount of stuff going on.</p>
|
|
<p>Obviously you'll want to know how to read the user's button inputs. That can
|
|
almost go without saying, except that I said it.</p>
|
|
<p>Each other part can be handled in about any order you like.</p>
|
|
<p>Using interrupts is perhaps one of the hardest things for us as Rust programmers
|
|
due to quirks in our compilation process. Our code all gets compiled to 16-bit
|
|
THUMB instructions, and we don't have a way to mark a function to be compiled
|
|
using 32-bit ASM instructions instead. However, an interrupt handler <em>must</em> be
|
|
written in 32-bit ASM instructions for it to work. That means that we have to
|
|
write our interrupt handler in 32-bit ASM by hand. We'll do it, but I don't
|
|
think we'll be too happy about it.</p>
|
|
<p>The Link Cable related stuff is also probably a little harder to test than
|
|
anything else. Just because link cable emulation isn't always the best, and or
|
|
you need two GBAs with two flash carts and the cable for hardware testing.
|
|
Still, we'll try to go over it eventually.</p>
|
|
<a class="header" href="#buttons" id="buttons"><h1>Buttons</h1></a>
|
|
<p>It's all well and good to just show a picture, even to show an animation, but if
|
|
we want a game we have to let the user interact with something.</p>
|
|
<a class="header" href="#key-input-register" id="key-input-register"><h2>Key Input Register</h2></a>
|
|
<ul>
|
|
<li>KEYINPUT, <code>0x400_0130</code>, <code>u16</code>, read only</li>
|
|
</ul>
|
|
<p>This little <code>u16</code> stores the status of <em>all</em> the buttons on the GBA, all at
|
|
once. There's only 10 of them, and we have 16 bits to work with, so that sounds
|
|
easy. However, there's a bit of a catch. The register follows a "low-active"
|
|
convention, where pressing a button <em>clears</em> that bit until it's released.</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
const NO_BUTTONS_PRESSED: u16 = 0b0000_0011_1111_1111;
|
|
#}</code></pre></pre>
|
|
<p>The buttons are, going up in order from the 0th bit:</p>
|
|
<ul>
|
|
<li>A</li>
|
|
<li>B</li>
|
|
<li>Select</li>
|
|
<li>Start</li>
|
|
<li>Right</li>
|
|
<li>Left</li>
|
|
<li>Up</li>
|
|
<li>Down</li>
|
|
<li>R</li>
|
|
<li>L</li>
|
|
</ul>
|
|
<p>Bits above that are not used. However, since the left and right directions, as
|
|
well as the up and down directions, can never be pressed at the same time, the
|
|
<code>KEYINPUT</code> register should never read as zero. Of course, the register <em>might</em>
|
|
read as zero if someone is using an emulator that allows for such inputs, so I
|
|
wouldn't go so far as to make it be <code>NonZeroU16</code> or anything like that.</p>
|
|
<p>When programming, we usually are thinking of what buttons we want to have <em>be
|
|
pressed</em> instead of buttons we want to have <em>not be pressed</em>. This means that we
|
|
need an inversion to happen somewhere along the line. The easiest moment of
|
|
inversion is immediately as you read in from the register and wrap the value up
|
|
in a newtype.</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
pub fn read_key_input() -> KeyInput {
|
|
KeyInput(KEYINPUT.read() ^ 0b0000_0011_1111_1111)
|
|
}
|
|
#}</code></pre></pre>
|
|
<p>Now the KeyInput you get can be checked for what buttons are pressed by checking
|
|
for a set bit like you'd do anywhere else.</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
impl KeyInput {
|
|
pub fn a_pressed(self) -> bool {
|
|
(self.0 & A_BIT) > 0
|
|
}
|
|
}
|
|
#}</code></pre></pre>
|
|
<p>Note that the current <code>KEYINPUT</code> value changes in real time as the user presses
|
|
or releases the buttons. To account for this, it's best to read the value just
|
|
once per game frame and then use that single value as if it was the input across
|
|
the whole frame. If you've worked with polling input before that should sound
|
|
totally normal. If not, just remember to call <code>read_key_input</code> once per frame
|
|
and then use that <code>KeyInput</code> value across the whole frame.</p>
|
|
<a class="header" href="#detecting-new-presses" id="detecting-new-presses"><h3>Detecting New Presses</h3></a>
|
|
<p>The keypad only tells you what's <em>currently</em> pressed, but if you want to check
|
|
what's <em>newly</em> pressed it's not too much harder.</p>
|
|
<p>All that you do is store the last frame's keys and compare them to the current
|
|
keys with an <code>XOR</code>. In the <code>gba</code> crate it's called <code>KeyInput::difference</code>. Once
|
|
you've got the difference between last frame and this frame, you know what
|
|
changes happened.</p>
|
|
<ul>
|
|
<li>If something is in the difference and <em>not pressed</em> in the last frame, that
|
|
means it was newly pressed.</li>
|
|
<li>If something is in the difference and <em>pressed</em> in the last frame that means
|
|
it was newly released.</li>
|
|
<li>If something is not in the difference then there's no change between last
|
|
frame and this frame.</li>
|
|
</ul>
|
|
<a class="header" href="#key-interrupt-control" id="key-interrupt-control"><h2>Key Interrupt Control</h2></a>
|
|
<ul>
|
|
<li>KEYCNT, <code>0x400_0132</code>, <code>u16</code>, read/write</li>
|
|
</ul>
|
|
<p>This lets you control what keys will trigger a keypad interrupt. Of course, for
|
|
the actual interrupt to fire you also need to set the <code>IME</code> and <code>IE</code> registers
|
|
properly. See the <a href="05-interrupts.html">Interrupts</a> section for details there.</p>
|
|
<p>The main thing to know about this register is that the keys are in <em>the exact
|
|
same order</em> as the key input order. However, with this register they use a
|
|
high-active convention instead (eg: the bit is active when the button should be
|
|
pressed as part of the interrupt).</p>
|
|
<p>In addition to simply having the bits for the buttons, bit 14 is a flag for
|
|
enabling keypad interrupts (in addition to the flag in the <code>IE</code> register), and
|
|
bit 15 decides how having more than one button works. If bit 15 is disabled,
|
|
it's an OR combination (eg: "press any key to continue"). If bit 15 is enabled
|
|
it's an AND combination (eg: "press A+B+Start+Select to reset").</p>
|
|
<a class="header" href="#timers" id="timers"><h1>Timers</h1></a>
|
|
<a class="header" href="#direct-memory-access" id="direct-memory-access"><h1>Direct Memory Access</h1></a>
|
|
<a class="header" href="#sound" id="sound"><h1>Sound</h1></a>
|
|
<a class="header" href="#interrupts" id="interrupts"><h1>Interrupts</h1></a>
|
|
<a class="header" href="#link-cable" id="link-cable"><h1>Link Cable</h1></a>
|
|
<a class="header" href="#game-pak" id="game-pak"><h1>Game Pak</h1></a>
|
|
<a class="header" href="#examples" id="examples"><h1>Examples</h1></a>
|
|
|
|
</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>
|