2018-11-08 15:20:40 +11:00
<!DOCTYPE HTML>
< html lang = "en" class = "sidebar-visible no-js" >
< head >
<!-- Book generated using mdBook -->
< meta charset = "UTF-8" >
2018-11-18 11:14:42 +11:00
< title > Rust GBA Guide< / title >
2018-11-08 15:20:40 +11:00
< 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" >
2018-12-16 11:35:43 +11:00
< 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 >
2018-11-08 15:20:40 +11:00
< / 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 >
2018-11-18 11:14:42 +11:00
< h1 class = "menu-title" > Rust GBA Guide< / h1 >
2018-11-08 15:20:40 +11:00
< 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 >
2018-11-10 20:03:37 +11:00
< a class = "header" href = "#introduction" id = "introduction" > < h1 > Introduction< / h1 > < / a >
2018-12-16 11:35:43 +11:00
< p > This is the book for learning how to write GameBoy Advance (GBA) games in Rust.< / p >
2018-12-10 19:48:06 +11:00
< 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 >
2018-12-16 11:35:43 +11:00
< 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 >
2018-12-09 04:57:38 +11:00
< a class = "header" href = "#reader-requirements" id = "reader-requirements" > < h1 > Reader Requirements< / h1 > < / a >
2018-12-10 19:48:06 +11:00
< 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 >
2018-12-09 04:57:38 +11:00
< a class = "header" href = "#book-goals-and-style" id = "book-goals-and-style" > < h1 > Book Goals and Style< / h1 > < / a >
2018-12-10 19:48:06 +11:00
< 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 >
2018-12-09 04:57:38 +11:00
< a class = "header" href = "#development-setup" id = "development-setup" > < h1 > Development Setup< / h1 > < / a >
2018-12-10 19:48:06 +11:00
< 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
2018-12-11 04:32:04 +11:00
build. Well, crates that work in the GBA's limited environment, but you get
2018-12-10 19:48:06 +11:00
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 >
2018-12-16 11:35:43 +11:00
< pre > < pre class = "playpen" > < code class = "language-rust" > #![no_std]
#![feature(start)]
2018-12-10 19:48:06 +11:00
#[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 >
2018-12-16 11:35:43 +11:00
< 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 >
2018-12-10 19:48:06 +11:00
< a class = "header" href = "#newtype" id = "newtype" > < h1 > Newtype< / h1 > < / a >
2018-12-16 11:35:43 +11:00
< 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 >
2018-12-10 19:48:06 +11:00
< 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
2018-12-16 11:35:43 +11:00
add more things:< / p >
2018-12-10 19:48:06 +11:00
< ul >
2018-12-16 11:35:43 +11:00
< 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 >
2018-12-10 19:48:06 +11:00
< / ul >
2018-12-16 11:35:43 +11:00
< 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 >
2018-12-09 04:57:38 +11:00
< 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 >
2018-12-10 19:48:06 +11:00
< a class = "header" href = "#work-ram" id = "work-ram" > < h1 > Work RAM< / h1 > < / a >
2018-11-10 20:03:37 +11:00
< a class = "header" href = "#io-registers" id = "io-registers" > < h1 > IO Registers< / h1 > < / a >
2018-12-09 04:57:38 +11:00
< 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 >
2018-12-10 19:48:06 +11:00
< a class = "header" href = "#todo" id = "todo" > < h1 > TODO< / h1 > < / a >
2018-12-09 04:57:38 +11:00
< 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 >
2018-11-13 10:29:17 +11:00
< a class = "header" href = "#light_cycle" id = "light_cycle" > < h1 > light_cycle< / h1 > < / a >
2018-12-09 04:57:38 +11:00
< a class = "header" href = "#bg_demo" id = "bg_demo" > < h1 > bg_demo< / h1 > < / a >
2018-11-08 15:20:40 +11:00
< / 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 >