mirror of
https://github.com/italicsjenga/gba.git
synced 2024-12-24 03:11:29 +11:00
1111 lines
65 KiB
HTML
1111 lines
65 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></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-network.html"><strong aria-hidden="true">5.6.</strong> Network</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><li><ol class="section"><li><a href="05-examples/01-hello_magic.html"><strong aria-hidden="true">6.1.</strong> hello_magic</a></li><li><a href="05-examples/02-hello_world.html"><strong aria-hidden="true">6.2.</strong> hello_world</a></li><li><a href="05-examples/03-light_cycle.html"><strong aria-hidden="true">6.3.</strong> light_cycle</a></li><li><a href="05-examples/04-bg_demo.html"><strong aria-hidden="true">6.4.</strong> bg_demo</a></li></ol></li></ol>
|
|
</nav>
|
|
|
|
<div id="page-wrapper" class="page-wrapper">
|
|
|
|
<div class="page">
|
|
|
|
<div id="menu-bar" class="menu-bar">
|
|
<div id="menu-bar-sticky-container">
|
|
<div class="left-buttons">
|
|
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
|
<i class="fa fa-bars"></i>
|
|
</button>
|
|
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
|
<i class="fa fa-paint-brush"></i>
|
|
</button>
|
|
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
|
<li role="none"><button role="menuitem" class="theme" id="light">Light <span class="default">(default)</span></button></li>
|
|
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
|
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
|
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
|
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
|
</ul>
|
|
|
|
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
|
<i class="fa fa-search"></i>
|
|
</button>
|
|
|
|
</div>
|
|
|
|
<h1 class="menu-title">Rust GBA Guide</h1>
|
|
|
|
<div class="right-buttons">
|
|
<a href="print.html" title="Print this book" aria-label="Print this book">
|
|
<i id="print-button" class="fa fa-print"></i>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div id="search-wrapper" class="hidden">
|
|
<form id="searchbar-outer" class="searchbar-outer">
|
|
<input type="search" name="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
|
</form>
|
|
<div id="searchresults-outer" class="searchresults-outer hidden">
|
|
<div id="searchresults-header" class="searchresults-header"></div>
|
|
<ul id="searchresults">
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
|
<script type="text/javascript">
|
|
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
|
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
|
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
|
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
|
});
|
|
</script>
|
|
|
|
<div id="content" class="content">
|
|
<main>
|
|
<a class="header" href="#introduction" id="introduction"><h1>Introduction</h1></a>
|
|
<p>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>I'm <em>not</em> gonna tell you how to use a crate that already exists.</p>
|
|
<p>Don't get me wrong, there <em>is</em> a <a href="https://crates.io/crates/gba">gba</a> crate, and
|
|
it's on crates.io and all that jazz.</p>
|
|
<p>However, unlike most crates that come with a tutorial book, I don't want to just
|
|
teach you how to use the crate. What I want is to teach you what you need to
|
|
know so that you could build the crate yourself, from scratch, if it didn't
|
|
already exist for you. Let's call it the <a href="https://handmadehero.org/">Handmade
|
|
Hero</a> school of design. Much more than you might find
|
|
in other Rust crate books, I'll be attempting to show a lot of the <em>why</em> in
|
|
addition to just the <em>how</em>. Once you know how to do it all on your own, you can
|
|
decide for yourself if the <code>gba</code> crate does it well, or if you think you can
|
|
come up with something that suits your needs better.</p>
|
|
<p>Overall the book is sorted for easy review once you're trying to program
|
|
something, and the GBA has a few 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.</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>Ketsuban and I didn't magically learn this all from nowhere, we read various
|
|
technical manuals and guides ourselves and then distilled the knowledge (usually
|
|
oriented towards C and C++) into this 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="#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="#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>
|
|
<a class="header" href="#fixed-only" id="fixed-only"><h1>Fixed Only</h1></a>
|
|
<p>In addition to not having the standard library available, we don't even have a
|
|
floating point unit available! We can't do floating point math in hardware! We
|
|
could still do floating point math as software computations if we wanted, but
|
|
that's a slow, slow thing to do.</p>
|
|
<p>Instead let's learn about another way to have fractional values called "Fixed
|
|
Point"</p>
|
|
<a class="header" href="#fixed-point" id="fixed-point"><h2>Fixed Point</h2></a>
|
|
<p>TODO: describe fixed point, make some types, do the impls, all that.</p>
|
|
<a class="header" href="#volatile-destination" id="volatile-destination"><h1>Volatile Destination</h1></a>
|
|
<p>TODO: replace all this one "the rant" is finalized</p>
|
|
<p>There's a reasonable chance that you've never heard of <code>volatile</code> before, so
|
|
what's that? Well, it's a term that can be used in more than one context, but
|
|
basically it means "get your grubby mitts off my stuff you over-eager compiler".</p>
|
|
<a class="header" href="#volatile-memory" id="volatile-memory"><h2>Volatile Memory</h2></a>
|
|
<p>The first, and most common, form of volatile thing is volatile memory. Volatile
|
|
memory can change without your program changing it, usually because it's not a
|
|
location in RAM, but instead some special location that represents an actual
|
|
hardware device, or part of a hardware device perhaps. The compiler doesn't know
|
|
what's going on in this situation, but when the program is actually run and the
|
|
CPU gets an instruction to read or write from that location, instead of just
|
|
accessing some place in RAM like with normal memory, it accesses whatever bit of
|
|
hardware and does <em>something</em>. The details of that something depend on the
|
|
hardware, but what's important is that we need to actually, definitely execute
|
|
that read or write instruction.</p>
|
|
<p>This is not how normal memory works. Normally when the compiler
|
|
sees us write values into variables and read values from variables, it's free to
|
|
optimize those expressions and eliminate some of the reads and writes if it can,
|
|
and generally try to save us time. Maybe it even knows some stuff about the data
|
|
dependencies in our expressions and so it does some of the reads or writes out
|
|
of order from what the source says, because the compiler knows that it won't
|
|
actually make a difference to the operation of the program. A good and helpful
|
|
friend, that compiler.</p>
|
|
<p>Volatile memory works almost the opposite way. With volatile memory we
|
|
need the compiler to <em>definitely</em> emit an instruction to do a read or write and
|
|
they need to happen <em>exactly</em> in the order that we say to do it. Each volatile
|
|
read or write might have any sort of side effect that the compiler
|
|
doesn't know about, and it shouldn't try to be clever about the optimization. Just do what we
|
|
say, please.</p>
|
|
<p>In Rust, we don't mark volatile things as being a separate type of thing,
|
|
instead we use normal raw pointers and then call the
|
|
<a href="https://doc.rust-lang.org/core/ptr/fn.read_volatile.html">read_volatile</a> and
|
|
<a href="https://doc.rust-lang.org/core/ptr/fn.write_volatile.html">write_volatile</a>
|
|
functions (also available as methods, if you like), which then delegate to the
|
|
LLVM
|
|
<a href="https://doc.rust-lang.org/core/intrinsics/fn.volatile_load.html">volatile_load</a>
|
|
and
|
|
<a href="https://doc.rust-lang.org/core/intrinsics/fn.volatile_store.html">volatile_store</a>
|
|
intrinsics. In C and C++ you can tag a pointer as being volatile and then any
|
|
normal read and write with it becomes the volatile version, but in Rust we have
|
|
to remember to use the correct alternate function instead.</p>
|
|
<p>I'm told by the experts that this makes for a cleaner and saner design from a
|
|
<em>language design</em> perspective, but it really kinda screws us when doing low
|
|
level code. References, both mutable and shared, aren't volatile, so they
|
|
compile into normal reads and writes. This means we can't do anything we'd
|
|
normally do in Rust that utilizes references of any kind. Volatile blocks of
|
|
memory can't use normal <code>.iter()</code> or <code>.iter_mut()</code> based iteration (which give
|
|
<code>&T</code> or <code>&mut T</code>), and they also can't use normal <code>Index</code> and <code>IndexMut</code> sugar
|
|
like <code>a + x[i]</code> or <code>x[i] = 7</code>.</p>
|
|
<p>Unlike with normal raw pointers, this pain point never goes away. There's no way
|
|
to abstract over the difference with Rust as it exists now, you'd need to
|
|
actually adjust the core language by adding an additional pointer type (<code>*vol T</code>) and possibly a reference type to go with it (<code>&vol T</code>) to get the right
|
|
semantics. And then you'd need an <code>IndexVol</code> trait, and you'd need
|
|
<code>.iter_vol()</code>, and so on for every other little thing. It would be a lot of
|
|
work, and the Rust developers just aren't interested in doing all that for such
|
|
a limited portion of their user population. We'll just have to deal with not
|
|
having any syntax sugar.</p>
|
|
<a class="header" href="#volatileptr" id="volatileptr"><h3>VolatilePtr</h3></a>
|
|
<p>No syntax sugar doesn't mean we can't at least make things a little easier for
|
|
ourselves. Enter the <code>VolatilePtr<T></code> type, which is a newtype over a <code>*mut T</code>.
|
|
One of those "manual" newtypes I mentioned where we can't use our nice macro.</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
|
#[repr(transparent)]
|
|
pub struct VolatilePtr<T>(pub *mut T);
|
|
#}</code></pre></pre>
|
|
<p>Obviously we want to be able to read and write:</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
impl<T> VolatilePtr<T> {
|
|
/// Performs a `read_volatile`.
|
|
pub unsafe fn read(self) -> T {
|
|
self.0.read_volatile()
|
|
}
|
|
|
|
/// Performs a `write_volatile`.
|
|
pub unsafe fn write(self, data: T) {
|
|
self.0.write_volatile(data);
|
|
}
|
|
#}</code></pre></pre>
|
|
<p>And we want a way to jump around when we do have volatile memory that's in
|
|
blocks. This is where we can get ourselves into some trouble if we're not
|
|
careful. We have to decide between
|
|
<a href="https://doc.rust-lang.org/std/primitive.pointer.html#method.offset">offset</a> and
|
|
<a href="https://doc.rust-lang.org/std/primitive.pointer.html#method.wrapping_offset">wrapping_offset</a>.
|
|
The difference is that <code>offset</code> optimizes better, but also it can be Undefined
|
|
Behavior if the result is not "in bounds or one byte past the end of the same
|
|
allocated object". I asked <a href="https://github.com/ubsan">ubsan</a> (who is the expert
|
|
that you should always listen to on matters like this) what that means exactly
|
|
when memory mapped hardware is involved (since we never allocated anything), and
|
|
the answer was that you <em>can</em> use an <code>offset</code> in statically memory mapped
|
|
situations like this as long as you don't use it to jump to the address of
|
|
something that Rust itself allocated at some point. Cool, we all like being able
|
|
to use the one that optimizes better. Unfortunately, the downside to using
|
|
<code>offset</code> instead of <code>wrapping_offset</code> is that with <code>offset</code>, it's Undefined
|
|
Behavior <em>simply to calculate the out of bounds result</em> (with <code>wrapping_offset</code>
|
|
it's not Undefined Behavior until you <em>use</em> the out of bounds result). We'll
|
|
have to be quite careful when we're using <code>offset</code>.</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
/// Performs a normal `offset`.
|
|
pub unsafe fn offset(self, count: isize) -> Self {
|
|
VolatilePtr(self.0.offset(count))
|
|
}
|
|
#}</code></pre></pre>
|
|
<p>Now, one thing of note is that doing the <code>offset</code> isn't <code>const</code>. The math for it
|
|
is something that's possible to do in a <code>const</code> way of course, but Rust
|
|
basically doesn't allow you to fiddle raw pointers much during <code>const</code> right
|
|
now. Maybe in the future that will improve.</p>
|
|
<p>If we did want to have a <code>const</code> function for finding the correct address within
|
|
a volatile block of memory we'd have to do all the math using <code>usize</code> values,
|
|
and then cast that value into being a pointer once we were done. It'd look
|
|
something like this:</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
const fn address_index<T>(address: usize, index: usize) -> usize {
|
|
address + (index * std::mem::size_of::<T>())
|
|
}
|
|
#}</code></pre></pre>
|
|
<p>But, back to methods for <code>VolatilePtr</code>, well we sometimes want to be able to
|
|
cast a <code>VolatilePtr</code> between pointer types. Since we won't be able to do that
|
|
with <code>as</code>, we'll have to write a method for it:</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
/// Performs a cast into some new pointer type.
|
|
pub fn cast<Z>(self) -> VolatilePtr<Z> {
|
|
VolatilePtr(self.0 as *mut Z)
|
|
}
|
|
#}</code></pre></pre>
|
|
<a class="header" href="#volatile-iterating" id="volatile-iterating"><h3>Volatile Iterating</h3></a>
|
|
<p>How about that <code>Iterator</code> stuff I said we'd be missing? We can actually make
|
|
<em>an</em> Iterator available, it's just not the normal "iterate by shared reference
|
|
or unique reference" Iterator. Instead, it's more like a "throw out a series of
|
|
<code>VolatilePtr</code> values" style Iterator. Other than that small difference it's
|
|
totally normal, and we'll be able to use map and skip and take and all those
|
|
neat methods.</p>
|
|
<p>So how do we make this thing we need? First we check out the <a href="https://doc.rust-lang.org/core/iter/index.html#implementing-iterator">Implementing
|
|
Iterator</a>
|
|
section in the core documentation. It says we need a struct for holding the
|
|
iterator state. Right-o, probably something like this:</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
|
pub struct VolatilePtrIter<T> {
|
|
vol_ptr: VolatilePtr<T>,
|
|
slots: usize,
|
|
}
|
|
#}</code></pre></pre>
|
|
<p>And then we just implement
|
|
<a href="https://doc.rust-lang.org/core/iter/trait.Iterator.html">core::iter::Iterator</a>
|
|
on that struct. Wow, that's quite the trait though! Don't worry, we only need to
|
|
implement two small things and then the rest of it comes free as a bunch of
|
|
default methods.</p>
|
|
<p>So, the code that we <em>want</em> to write looks like this:</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
impl<T> Iterator for VolatilePtrIter<T> {
|
|
type Item = VolatilePtr<T>;
|
|
|
|
fn next(&mut self) -> Option<VolatilePtr<T>> {
|
|
if self.slots > 0 {
|
|
let out = Some(self.vol_ptr);
|
|
self.slots -= 1;
|
|
self.vol_ptr = unsafe { self.vol_ptr.offset(1) };
|
|
out
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
#}</code></pre></pre>
|
|
<p>Except we <em>can't</em> write that code. What? The problem is that we used
|
|
<code>derive(Clone, Copy</code> on <code>VolatilePtr</code>. Because of a quirk in how <code>derive</code> works,
|
|
this means <code>VolatilePtr<T></code> will only be <code>Copy</code> if the <code>T</code> is <code>Copy</code>, <em>even
|
|
though the pointer itself is always <code>Copy</code> regardless of what it points to</em>.
|
|
Ugh, terrible. We've got three basic ways to handle this:</p>
|
|
<ul>
|
|
<li>Make the <code>Iterator</code> implementation be for <code><T:Clone></code>, and then hope that we
|
|
always have types that are <code>Clone</code>.</li>
|
|
<li>Hand implement every trait we want <code>VolatilePtr</code> (and <code>VolatilePtrIter</code>) to
|
|
have so that we can override the fact that <code>derive</code> is basically broken in
|
|
this case.</li>
|
|
<li>Make <code>VolatilePtr</code> store a <code>usize</code> value instead of a pointer, and then cast
|
|
it to <code>*mut T</code> when we actually need to read and write. This would require us
|
|
to also store a <code>PhantomData<T></code> so that the type of the address is tracked
|
|
properly, which would make it a lot more verbose to construct a <code>VolatilePtr</code>
|
|
value.</li>
|
|
</ul>
|
|
<p>None of those options are particularly appealing. I guess we'll do the first one
|
|
because it's the least amount of up front trouble, and I don't <em>think</em> we'll
|
|
need to be iterating non-Clone values. All we do to pick that option is add the
|
|
bound to the very start of the <code>impl</code> block, where we introduce the <code>T</code>:</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
impl<T: Clone> Iterator for VolatilePtrIter<T> {
|
|
type Item = VolatilePtr<T>;
|
|
|
|
fn next(&mut self) -> Option<VolatilePtr<T>> {
|
|
if self.slots > 0 {
|
|
let out = Some(self.vol_ptr.clone());
|
|
self.slots -= 1;
|
|
self.vol_ptr = unsafe { self.vol_ptr.clone().offset(1) };
|
|
out
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
#}</code></pre></pre>
|
|
<p>What's going on here? Okay so our iterator has a number of slots that it'll go
|
|
over, and then when it's out of slots it starts producing <code>None</code> forever. That's
|
|
actually pretty simple. We're also masking some unsafety too. In this case,
|
|
we'll rely on the person who made the <code>VolatilePtrIter</code> to have selected the
|
|
correct number of slots. This gives us a new method for <code>VolatilePtr</code>:</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
pub unsafe fn iter_slots(self, slots: usize) -> VolatilePtrIter<T> {
|
|
VolatilePtrIter {
|
|
vol_ptr: self,
|
|
slots,
|
|
}
|
|
}
|
|
#}</code></pre></pre>
|
|
<p>With this design, making the <code>VolatilePtrIter</code> at the start is <code>unsafe</code> (we have
|
|
to trust the caller that the right number of slots exists), and then using it
|
|
after that is totally safe (if the right number of slots was given we'll never
|
|
screw up our end of it).</p>
|
|
<a class="header" href="#volatileptr-formatting" id="volatileptr-formatting"><h3>VolatilePtr Formatting</h3></a>
|
|
<p>Also, just as a little bonus that we probably won't use, we could enable our new
|
|
pointer type to be formatted as a pointer value.</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
impl<T> core::fmt::Pointer for VolatilePtr<T> {
|
|
/// Formats exactly like the inner `*mut T`.
|
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
|
write!(f, "{:p}", self.0)
|
|
}
|
|
}
|
|
#}</code></pre></pre>
|
|
<p>Neat!</p>
|
|
<a class="header" href="#volatileptr-complete" id="volatileptr-complete"><h3>VolatilePtr Complete</h3></a>
|
|
<p>That was a lot of small code blocks, let's look at it all put together:</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
#[repr(transparent)]
|
|
pub struct VolatilePtr<T>(pub *mut T);
|
|
impl<T> VolatilePtr<T> {
|
|
pub unsafe fn read(self) -> T {
|
|
self.0.read_volatile()
|
|
}
|
|
pub unsafe fn write(self, data: T) {
|
|
self.0.write_volatile(data);
|
|
}
|
|
pub unsafe fn offset(self, count: isize) -> Self {
|
|
VolatilePtr(self.0.offset(count))
|
|
}
|
|
pub fn cast<Z>(self) -> VolatilePtr<Z> {
|
|
VolatilePtr(self.0 as *mut Z)
|
|
}
|
|
pub unsafe fn iter_slots(self, slots: usize) -> VolatilePtrIter<T> {
|
|
VolatilePtrIter {
|
|
vol_ptr: self,
|
|
slots,
|
|
}
|
|
}
|
|
}
|
|
impl<T> core::fmt::Pointer for VolatilePtr<T> {
|
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
|
write!(f, "{:p}", self.0)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
|
pub struct VolatilePtrIter<T> {
|
|
vol_ptr: VolatilePtr<T>,
|
|
slots: usize,
|
|
}
|
|
impl<T: Clone> Iterator for VolatilePtrIter<T> {
|
|
type Item = VolatilePtr<T>;
|
|
fn next(&mut self) -> Option<VolatilePtr<T>> {
|
|
if self.slots > 0 {
|
|
let out = Some(self.vol_ptr.clone());
|
|
self.slots -= 1;
|
|
self.vol_ptr = unsafe { self.vol_ptr.clone().offset(1) };
|
|
out
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
#}</code></pre></pre>
|
|
<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>
|
|
<a class="header" href="#newtype" id="newtype"><h1>Newtype</h1></a>
|
|
<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>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 <a href="https://github.com/rust-lang/rfcs/blob/master/text/1758-repr-transparent.md">the
|
|
RFC</a>
|
|
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>Ah, and of course we'll need to make it so you can unwrap the value:</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
#[repr(transparent)]
|
|
pub struct PixelColor(u16);
|
|
|
|
impl From<PixelColor> for u16 {
|
|
fn from(color: PixelColor) -> u16 {
|
|
color.0
|
|
}
|
|
}
|
|
#}</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>The most basic version of the macro we want goes 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>Except we also want to be able to add attributes (which includes doc comments),
|
|
so we upgrade our macro a bit:</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>And we want to automatically add the ability to turn the wrapper type back into
|
|
the wrapped type.</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);
|
|
|
|
impl From<$new_name> for $old_name {
|
|
fn from(x: $new_name) -> $old_name {
|
|
x.0
|
|
}
|
|
}
|
|
};
|
|
}
|
|
#}</code></pre></pre>
|
|
<p>That seems like enough for all of our examples, so we'll stop there. We could
|
|
add more things:</p>
|
|
<ul>
|
|
<li>Making the <code>From</code> impl being optional. We'd have to make the newtype
|
|
invocation be more complicated somehow, the user puts ", no-unwrap" after the
|
|
inner type declaration or something, or something like that.</li>
|
|
<li>Allowing for more precise visibility controls on the wrapping type and on the
|
|
inner field. This would add a lot of line noise, so we'll just always have our
|
|
newtypes be <code>pub</code>.</li>
|
|
<li>Allowing for generic newtypes, which might sound silly but that we'll actually
|
|
see an example of soon enough. To do this you might <em>think</em> that we can change
|
|
the <code>:ident</code> declarations to <code>:ty</code>, but since we're declaring a fresh type not
|
|
using an existing type we have to accept it as an <code>:ident</code>. The way you get
|
|
around this is with a proc-macro, which is a lot more powerful but which also
|
|
requires that you write the proc-macro in an entirely other crate that gets
|
|
compiled first. We don't need that much power, so for our examples we'll go
|
|
with the macro_rules version and just do it by hand in the few cases where we
|
|
need a generic newtype.</li>
|
|
<li>Allowing for <code>Deref</code> and <code>DerefMut</code>, which usually defeats the point of doing
|
|
the newtype, but maybe sometimes it's the right thing, so if you were going
|
|
for the full industrial strength version with a proc-macro and all you might
|
|
want to make that part of your optional add-ons as well the same way you might
|
|
want optional <code>From</code>. You'd probably want <code>From</code> to be "on by default" and
|
|
<code>Deref</code>/<code>DerefMut</code> to be "off by default", but whatever.</li>
|
|
</ul>
|
|
<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="#broad-concepts" id="broad-concepts"><h1>Broad Concepts</h1></a>
|
|
<a class="header" href="#cpu" id="cpu"><h1>CPU</h1></a>
|
|
<a class="header" href="#bios" id="bios"><h1>BIOS</h1></a>
|
|
<a class="header" href="#work-ram" id="work-ram"><h1>Work RAM</h1></a>
|
|
<a class="header" href="#io-registers" id="io-registers"><h1>IO Registers</h1></a>
|
|
<a class="header" href="#palette-ram" id="palette-ram"><h1>Palette RAM</h1></a>
|
|
<a class="header" href="#video-ram" id="video-ram"><h1>Video RAM</h1></a>
|
|
<a class="header" href="#object-attribute-memory" id="object-attribute-memory"><h1>Object Attribute Memory</h1></a>
|
|
<a class="header" href="#game-pak-rom--flash-rom" id="game-pak-rom--flash-rom"><h1>Game Pak ROM / Flash ROM</h1></a>
|
|
<a class="header" href="#save-ram" id="save-ram"><h1>Save RAM</h1></a>
|
|
<a class="header" href="#video" id="video"><h1>Video</h1></a>
|
|
<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>
|
|
<a class="header" href="#buttons" id="buttons"><h1>Buttons</h1></a>
|
|
<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="#network" id="network"><h1>Network</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>
|
|
<a class="header" href="#hello_magic" id="hello_magic"><h1>hello_magic</h1></a>
|
|
<a class="header" href="#hello_world" id="hello_world"><h1>hello_world</h1></a>
|
|
<a class="header" href="#light_cycle" id="light_cycle"><h1>light_cycle</h1></a>
|
|
<a class="header" href="#bg_demo" id="bg_demo"><h1>bg_demo</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>
|