mirror of
https://github.com/italicsjenga/gba.git
synced 2024-12-24 03:11:29 +11:00
make docs be hosted in a separate branch
This commit is contained in:
parent
e0c76417b3
commit
70b752f82b
|
@ -44,10 +44,10 @@ script:
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
provider: pages
|
provider: pages
|
||||||
|
local-dir: target/book-output
|
||||||
skip-cleanup: true
|
skip-cleanup: true
|
||||||
github-token: $GITHUB_TOKEN
|
github-token: $GITHUB_TOKEN
|
||||||
target-branch: master
|
keep-history: false
|
||||||
keep-history: true
|
|
||||||
name: DocsBot
|
name: DocsBot
|
||||||
verbose: true
|
verbose: true
|
||||||
on:
|
on:
|
||||||
|
|
|
@ -9,7 +9,7 @@ keywords = ["gba"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
|
|
||||||
#publish = false
|
publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
typenum = "1.10"
|
typenum = "1.10"
|
||||||
|
|
|
@ -3,5 +3,5 @@ title = "Rust GBA Guide"
|
||||||
authors = ["Lokathor"]
|
authors = ["Lokathor"]
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
build-dir = "../docs"
|
build-dir = "../target/book-output"
|
||||||
create-missing = true
|
create-missing = true
|
||||||
|
|
|
@ -1,339 +0,0 @@
|
||||||
<!DOCTYPE HTML>
|
|
||||||
<html lang="en" class="sidebar-visible no-js">
|
|
||||||
<head>
|
|
||||||
<!-- Book generated using mdBook -->
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>No Std - 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" class="active"><strong aria-hidden="true">2.1.</strong> No Std</a></li><li><a href="../01-quirks/02-fixed_only.html"><strong aria-hidden="true">2.2.</strong> Fixed Only</a></li><li><a href="../01-quirks/03-volatile_destination.html"><strong aria-hidden="true">2.3.</strong> Volatile Destination</a></li><li><a href="../01-quirks/04-newtype.html"><strong aria-hidden="true">2.4.</strong> Newtype</a></li><li><a href="../01-quirks/05-const_asserts.html"><strong aria-hidden="true">2.5.</strong> Const Asserts</a></li></ol></li><li><a href="../02-concepts/00-index.html"><strong aria-hidden="true">3.</strong> Concepts</a></li><li><ol class="section"><li><a href="../02-concepts/01-cpu.html"><strong aria-hidden="true">3.1.</strong> CPU</a></li><li><a href="../02-concepts/02-bios.html"><strong aria-hidden="true">3.2.</strong> BIOS</a></li><li><a href="../02-concepts/03-wram.html"><strong aria-hidden="true">3.3.</strong> Work RAM</a></li><li><a href="../02-concepts/04-io-registers.html"><strong aria-hidden="true">3.4.</strong> IO Registers</a></li><li><a href="../02-concepts/05-palram.html"><strong aria-hidden="true">3.5.</strong> Palette RAM</a></li><li><a href="../02-concepts/06-vram.html"><strong aria-hidden="true">3.6.</strong> Video RAM</a></li><li><a href="../02-concepts/07-oam.html"><strong aria-hidden="true">3.7.</strong> Object Attribute Memory</a></li><li><a href="../02-concepts/08-rom.html"><strong aria-hidden="true">3.8.</strong> Game Pak ROM / Flash ROM</a></li><li><a href="../02-concepts/09-sram.html"><strong aria-hidden="true">3.9.</strong> Save RAM</a></li></ol></li><li><a href="../03-video/00-index.html"><strong aria-hidden="true">4.</strong> Video</a></li><li><ol class="section"><li><a href="../03-video/01-rgb15.html"><strong aria-hidden="true">4.1.</strong> RBG15 Color</a></li><li><a href="../03-video/TODO.html"><strong aria-hidden="true">4.2.</strong> TODO</a></li></ol></li><li><a href="../04-non-video/00-index.html"><strong aria-hidden="true">5.</strong> Non-Video</a></li><li><ol class="section"><li><a href="../04-non-video/01-buttons.html"><strong aria-hidden="true">5.1.</strong> Buttons</a></li><li><a href="../04-non-video/02-timers.html"><strong aria-hidden="true">5.2.</strong> Timers</a></li><li><a href="../04-non-video/03-dma.html"><strong aria-hidden="true">5.3.</strong> Direct Memory Access</a></li><li><a href="../04-non-video/04-sound.html"><strong aria-hidden="true">5.4.</strong> Sound</a></li><li><a href="../04-non-video/05-interrupts.html"><strong aria-hidden="true">5.5.</strong> Interrupts</a></li><li><a href="../04-non-video/06-link_cable.html"><strong aria-hidden="true">5.6.</strong> Link Cable</a></li><li><a href="../04-non-video/07-game_pak.html"><strong aria-hidden="true">5.7.</strong> Game Pak</a></li></ol></li><li><a href="../05-examples/00-index.html"><strong aria-hidden="true">6.</strong> Examples</a></li></ol>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<div id="page-wrapper" class="page-wrapper">
|
|
||||||
|
|
||||||
<div class="page">
|
|
||||||
|
|
||||||
<div id="menu-bar" class="menu-bar">
|
|
||||||
<div id="menu-bar-sticky-container">
|
|
||||||
<div class="left-buttons">
|
|
||||||
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
|
||||||
<i class="fa fa-bars"></i>
|
|
||||||
</button>
|
|
||||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
|
||||||
<i class="fa fa-paint-brush"></i>
|
|
||||||
</button>
|
|
||||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
|
||||||
<li role="none"><button role="menuitem" class="theme" id="light">Light <span class="default">(default)</span></button></li>
|
|
||||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
|
||||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
|
||||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
|
||||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
|
||||||
<i class="fa fa-search"></i>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h1 class="menu-title">Rust GBA Guide</h1>
|
|
||||||
|
|
||||||
<div class="right-buttons">
|
|
||||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
|
||||||
<i id="print-button" class="fa fa-print"></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div id="search-wrapper" class="hidden">
|
|
||||||
<form id="searchbar-outer" class="searchbar-outer">
|
|
||||||
<input type="search" name="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
|
||||||
</form>
|
|
||||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
|
||||||
<div id="searchresults-header" class="searchresults-header"></div>
|
|
||||||
<ul id="searchresults">
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
|
||||||
<script type="text/javascript">
|
|
||||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
|
||||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
|
||||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
|
||||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div id="content" class="content">
|
|
||||||
<main>
|
|
||||||
<a class="header" href="#no-std" id="no-std"><h1>No Std</h1></a>
|
|
||||||
<p>First up, as you already saw in the <code>hello_magic</code> code, we have to use the
|
|
||||||
<code>#![no_std]</code> outer attribute on our program when we target the GBA. You can find
|
|
||||||
some info about <code>no_std</code> in two official sources:</p>
|
|
||||||
<ul>
|
|
||||||
<li><a href="https://doc.rust-lang.org/unstable-book/language-features/lang-items.html#writing-an-executable-without-stdlib">unstable
|
|
||||||
book section</a></li>
|
|
||||||
<li><a href="https://rust-embedded.github.io/book/intro/no-std.html?highlight=no_std#a--no_std--rust-environment">embedded
|
|
||||||
book section</a></li>
|
|
||||||
</ul>
|
|
||||||
<p>The unstable book is borderline useless here because it's describing too many
|
|
||||||
things in too many words. The embedded book is much better, but still fairly
|
|
||||||
terse.</p>
|
|
||||||
<a class="header" href="#bare-metal" id="bare-metal"><h2>Bare Metal</h2></a>
|
|
||||||
<p>The GBA falls under what the Embedded Book calls "Bare Metal Environments".
|
|
||||||
Basically, the machine powers on and immediately begins executing some ASM code.
|
|
||||||
Our ASM startup was provided by <code>Ketsuban</code> (check the <code>crt0.s</code> file). We'll go
|
|
||||||
over <em>how</em> it works much later on, for now it's enough to know that it does
|
|
||||||
work, and eventually control passes into Rust code.</p>
|
|
||||||
<p>On the rust code side of things, we determine our starting point with the
|
|
||||||
<code>#[start]</code> attribute on our <code>main</code> function. The <code>main</code> function also has a
|
|
||||||
specific type signature that's different from the usual <code>main</code> that you'd see in
|
|
||||||
Rust. I'd tell you to read the unstable-book entry on <code>#[start]</code> but they
|
|
||||||
<a href="https://doc.rust-lang.org/unstable-book/language-features/start.html">literally</a>
|
|
||||||
just tell you to look at the <a href="https://github.com/rust-lang/rust/issues/29633">tracking issue for
|
|
||||||
it</a> instead, and that's not very
|
|
||||||
helpful either. Basically it just <em>has</em> to be declared the way it is, even
|
|
||||||
though there's nothing passing in the arguments and there's no place that the
|
|
||||||
return value will go. The compiler won't accept it any other way.</p>
|
|
||||||
<a class="header" href="#no-standard-library" id="no-standard-library"><h2>No Standard Library</h2></a>
|
|
||||||
<p>The Embedded Book tells us that we can't use the standard library, but we get
|
|
||||||
access to something called "libcore", which sounds kinda funny. What they're
|
|
||||||
talking about is just <a href="https://doc.rust-lang.org/core/index.html">the core
|
|
||||||
crate</a>, which is called <code>libcore</code>
|
|
||||||
within the rust repository for historical reasons.</p>
|
|
||||||
<p>The <code>core</code> crate is actually still a really big portion of Rust. The standard
|
|
||||||
library doesn't actually hold too much code (relatively speaking), instead it
|
|
||||||
just takes code form other crates and then re-exports it in an organized way. So
|
|
||||||
with just <code>core</code> instead of <code>std</code>, what are we missing?</p>
|
|
||||||
<p>In no particular order:</p>
|
|
||||||
<ul>
|
|
||||||
<li>Allocation</li>
|
|
||||||
<li>Clock</li>
|
|
||||||
<li>Network</li>
|
|
||||||
<li>File System</li>
|
|
||||||
</ul>
|
|
||||||
<p>The allocation system and all the types that you can use if you have a global
|
|
||||||
allocator are neatly packaged up in the
|
|
||||||
<a href="https://doc.rust-lang.org/alloc/index.html">alloc</a> crate. The rest isn't as
|
|
||||||
nicely organized.</p>
|
|
||||||
<p>It's <em>possible</em> to implement a fair portion of the entire standard library
|
|
||||||
within a GBA context and make the rest just panic if you try to use it. However,
|
|
||||||
do you really need all that? Eh... probably not?</p>
|
|
||||||
<ul>
|
|
||||||
<li>We don't need a file system, because all of our data is just sitting there in
|
|
||||||
the ROM for us to use. When programming we can organize our <code>const</code> data into
|
|
||||||
modules and such to keep it organized, but once the game is compiled it's just
|
|
||||||
one huge flat address space. TODO: Parasyte says that a FS can be handy even
|
|
||||||
if it's all just ReadOnly, so we'll eventually talk about how you might set up
|
|
||||||
such a thing I guess, since we'll already be talking about replacements for
|
|
||||||
three of the other four things we "lost". Maybe we'll make Parasyte write that
|
|
||||||
section.</li>
|
|
||||||
<li>Networking, well, the GBA has a Link Cable you can use to communicate with
|
|
||||||
another GBA, but it's not really like a unix socket with TCP, so the standard
|
|
||||||
Rust networking isn't a very good match.</li>
|
|
||||||
<li>Clock is actually two different things at once. One is the ability to store
|
|
||||||
the time long term, which is a bit of hardware that some gamepaks have in them
|
|
||||||
(eg: pokemon ruby/sapphire/emerald). The GBA itself can't keep time while
|
|
||||||
power is off. However, the second part is just tracking time moment to moment,
|
|
||||||
which the GBA can totally do. We'll see how to access the timers soon enough.</li>
|
|
||||||
</ul>
|
|
||||||
<p>Which just leaves us with allocation. Do we need an allocator? Depends on your
|
|
||||||
game. For demos and small games you probably don't need one. For bigger games
|
|
||||||
you'll maybe want to get an allocator going eventually. It's in some sense a
|
|
||||||
crutch, but it's a very useful one.</p>
|
|
||||||
<p>So I promise that at some point we'll cover how to get an allocator going.
|
|
||||||
Either a Rust Global Allocator (if practical), which would allow for a lot of
|
|
||||||
the standard library types to be used "for free" once it was set up, or just a
|
|
||||||
custom allocator that's GBA specific if Rust's global allocator style isn't a
|
|
||||||
good fit for the GBA (I honestly haven't looked into it).</p>
|
|
||||||
<a class="header" href="#bare-metal-panic" id="bare-metal-panic"><h2>Bare Metal Panic</h2></a>
|
|
||||||
<p>If our code panics, we usually want to see that panic message. Unfortunately,
|
|
||||||
without a way to access something like <code>stdout</code> or <code>stderr</code> we've gotta do
|
|
||||||
something a little weirder.</p>
|
|
||||||
<p>If our program is running within the <code>mGBA</code> emulator, version 0.7 or later, we
|
|
||||||
can access a special set of addresses that allow us to send out <code>CString</code>
|
|
||||||
values, which then appear within a message log that you can check.</p>
|
|
||||||
<p>We can capture this behavior by making an <code>MGBADebug</code> type, and then implement
|
|
||||||
<code>core::fmt::Write</code> for that type. Once done, the <code>write!</code> macro will let us
|
|
||||||
target the mGBA debug output channel.</p>
|
|
||||||
<p>When used, it looks like this:</p>
|
|
||||||
<pre><pre class="playpen"><code class="language-rust">
|
|
||||||
# #![allow(unused_variables)]
|
|
||||||
#fn main() {
|
|
||||||
#[panic_handler]
|
|
||||||
fn panic(info: &core::panic::PanicInfo) -> ! {
|
|
||||||
use core::fmt::Write;
|
|
||||||
use gba::mgba::{MGBADebug, MGBADebugLevel};
|
|
||||||
|
|
||||||
if let Some(mut mgba) = MGBADebug::new() {
|
|
||||||
let _ = write!(mgba, "{}", info);
|
|
||||||
mgba.send(MGBADebugLevel::Fatal);
|
|
||||||
}
|
|
||||||
loop {}
|
|
||||||
}
|
|
||||||
#}</code></pre></pre>
|
|
||||||
<p>If you want to follow the particulars you can check the <code>MGBADebug</code> source in
|
|
||||||
the <code>gba</code> crate. Basically, there's one address you can use to try and activate
|
|
||||||
the debug output, and if it works you write your message into the "array" at
|
|
||||||
another address, and then finally write a send value to a third address. You'll
|
|
||||||
need to have read the <a href="03-volatile_destination.html">volatile</a> section for the
|
|
||||||
details to make sense.</p>
|
|
||||||
<a class="header" href="#llvm-intrinsics" id="llvm-intrinsics"><h2>LLVM Intrinsics</h2></a>
|
|
||||||
<p>The above code will make your program fail to build in debug mode, saying that
|
|
||||||
<code>__clzsi2</code> can't be found. This is a special builtin function that LLVM attempts
|
|
||||||
to use when there's no hardware version of an operation it wants to do (in this
|
|
||||||
case, counting the leading zeros). It's not <em>actually</em> necessary in this case,
|
|
||||||
which is why you only need it in debug mode. The higher optimization level of
|
|
||||||
release mode makes LLVM pre-compute more and fold more constants or whatever and
|
|
||||||
then it stops trying to call <code>__clzsi2</code>.</p>
|
|
||||||
<p>Unfortunately, sometimes a build will fail with a missing intrinsic even in
|
|
||||||
release mode.</p>
|
|
||||||
<p>If LLVM wants <em>core</em> to have that intrinsic then you're in
|
|
||||||
trouble, you'll have to send a PR to the
|
|
||||||
<a href="https://github.com/rust-lang-nursery/compiler-builtins">compiler-builtins</a>
|
|
||||||
repository and hope to get it into rust itself.</p>
|
|
||||||
<p>If LLVM wants <em>your code</em> to have the intrinsic then you're in less trouble. You
|
|
||||||
can look up the details and then implement it yourself. It can go anywhere in
|
|
||||||
your program, as long as it has the right ABI and name. In the case of
|
|
||||||
<code>__clzsi2</code> it takes a <code>usize</code> and returns a <code>usize</code>, so you'd write something
|
|
||||||
like:</p>
|
|
||||||
<pre><pre class="playpen"><code class="language-rust">
|
|
||||||
# #![allow(unused_variables)]
|
|
||||||
#fn main() {
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn __clzsi2(mut x: usize) -> usize {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
#}</code></pre></pre>
|
|
||||||
<p>And so on for whatever other missing intrinsic.</p>
|
|
||||||
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
|
||||||
<!-- Mobile navigation buttons -->
|
|
||||||
|
|
||||||
<a rel="prev" href="../01-quirks/00-index.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
|
||||||
<i class="fa fa-angle-left"></i>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a rel="next" href="../01-quirks/02-fixed_only.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
|
||||||
<i class="fa fa-angle-right"></i>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
|
|
||||||
<div style="clear: both"></div>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
|
||||||
|
|
||||||
<a href="../01-quirks/00-index.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
|
||||||
<i class="fa fa-angle-left"></i>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a href="../01-quirks/02-fixed_only.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
|
||||||
<i class="fa fa-angle-right"></i>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script src="../elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
|
|
||||||
<script src="../mark.min.js" type="text/javascript" charset="utf-8"></script>
|
|
||||||
<script src="../searcher.js" type="text/javascript" charset="utf-8"></script>
|
|
||||||
|
|
||||||
|
|
||||||
<script src="../clipboard.min.js" type="text/javascript" charset="utf-8"></script>
|
|
||||||
<script src="../highlight.js" type="text/javascript" charset="utf-8"></script>
|
|
||||||
<script src="../book.js" type="text/javascript" charset="utf-8"></script>
|
|
||||||
|
|
||||||
<!-- Custom JS scripts -->
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,698 +0,0 @@
|
||||||
<!DOCTYPE HTML>
|
|
||||||
<html lang="en" class="sidebar-visible no-js">
|
|
||||||
<head>
|
|
||||||
<!-- Book generated using mdBook -->
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Fixed Only - 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" class="active"><strong aria-hidden="true">2.2.</strong> Fixed Only</a></li><li><a href="../01-quirks/03-volatile_destination.html"><strong aria-hidden="true">2.3.</strong> Volatile Destination</a></li><li><a href="../01-quirks/04-newtype.html"><strong aria-hidden="true">2.4.</strong> Newtype</a></li><li><a href="../01-quirks/05-const_asserts.html"><strong aria-hidden="true">2.5.</strong> Const Asserts</a></li></ol></li><li><a href="../02-concepts/00-index.html"><strong aria-hidden="true">3.</strong> Concepts</a></li><li><ol class="section"><li><a href="../02-concepts/01-cpu.html"><strong aria-hidden="true">3.1.</strong> CPU</a></li><li><a href="../02-concepts/02-bios.html"><strong aria-hidden="true">3.2.</strong> BIOS</a></li><li><a href="../02-concepts/03-wram.html"><strong aria-hidden="true">3.3.</strong> Work RAM</a></li><li><a href="../02-concepts/04-io-registers.html"><strong aria-hidden="true">3.4.</strong> IO Registers</a></li><li><a href="../02-concepts/05-palram.html"><strong aria-hidden="true">3.5.</strong> Palette RAM</a></li><li><a href="../02-concepts/06-vram.html"><strong aria-hidden="true">3.6.</strong> Video RAM</a></li><li><a href="../02-concepts/07-oam.html"><strong aria-hidden="true">3.7.</strong> Object Attribute Memory</a></li><li><a href="../02-concepts/08-rom.html"><strong aria-hidden="true">3.8.</strong> Game Pak ROM / Flash ROM</a></li><li><a href="../02-concepts/09-sram.html"><strong aria-hidden="true">3.9.</strong> Save RAM</a></li></ol></li><li><a href="../03-video/00-index.html"><strong aria-hidden="true">4.</strong> Video</a></li><li><ol class="section"><li><a href="../03-video/01-rgb15.html"><strong aria-hidden="true">4.1.</strong> RBG15 Color</a></li><li><a href="../03-video/TODO.html"><strong aria-hidden="true">4.2.</strong> TODO</a></li></ol></li><li><a href="../04-non-video/00-index.html"><strong aria-hidden="true">5.</strong> Non-Video</a></li><li><ol class="section"><li><a href="../04-non-video/01-buttons.html"><strong aria-hidden="true">5.1.</strong> Buttons</a></li><li><a href="../04-non-video/02-timers.html"><strong aria-hidden="true">5.2.</strong> Timers</a></li><li><a href="../04-non-video/03-dma.html"><strong aria-hidden="true">5.3.</strong> Direct Memory Access</a></li><li><a href="../04-non-video/04-sound.html"><strong aria-hidden="true">5.4.</strong> Sound</a></li><li><a href="../04-non-video/05-interrupts.html"><strong aria-hidden="true">5.5.</strong> Interrupts</a></li><li><a href="../04-non-video/06-link_cable.html"><strong aria-hidden="true">5.6.</strong> Link Cable</a></li><li><a href="../04-non-video/07-game_pak.html"><strong aria-hidden="true">5.7.</strong> Game Pak</a></li></ol></li><li><a href="../05-examples/00-index.html"><strong aria-hidden="true">6.</strong> Examples</a></li></ol>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<div id="page-wrapper" class="page-wrapper">
|
|
||||||
|
|
||||||
<div class="page">
|
|
||||||
|
|
||||||
<div id="menu-bar" class="menu-bar">
|
|
||||||
<div id="menu-bar-sticky-container">
|
|
||||||
<div class="left-buttons">
|
|
||||||
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
|
||||||
<i class="fa fa-bars"></i>
|
|
||||||
</button>
|
|
||||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
|
||||||
<i class="fa fa-paint-brush"></i>
|
|
||||||
</button>
|
|
||||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
|
||||||
<li role="none"><button role="menuitem" class="theme" id="light">Light <span class="default">(default)</span></button></li>
|
|
||||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
|
||||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
|
||||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
|
||||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
|
||||||
<i class="fa fa-search"></i>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h1 class="menu-title">Rust GBA Guide</h1>
|
|
||||||
|
|
||||||
<div class="right-buttons">
|
|
||||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
|
||||||
<i id="print-button" class="fa fa-print"></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div id="search-wrapper" class="hidden">
|
|
||||||
<form id="searchbar-outer" class="searchbar-outer">
|
|
||||||
<input type="search" name="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
|
||||||
</form>
|
|
||||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
|
||||||
<div id="searchresults-header" class="searchresults-header"></div>
|
|
||||||
<ul id="searchresults">
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
|
||||||
<script type="text/javascript">
|
|
||||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
|
||||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
|
||||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
|
||||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div id="content" class="content">
|
|
||||||
<main>
|
|
||||||
<a class="header" href="#fixed-only" id="fixed-only"><h1>Fixed Only</h1></a>
|
|
||||||
<p>In addition to not having much of the standard library available, we don't even
|
|
||||||
have a floating point unit available! We can't do floating point math in
|
|
||||||
hardware! We <em>could</em> still do floating point math as pure software computations
|
|
||||||
if we wanted, but that's a slow, slow thing to do.</p>
|
|
||||||
<p>Are there faster ways? It's the same answer as always: "Yes, but not without a
|
|
||||||
tradeoff."</p>
|
|
||||||
<p>The faster way is to represent fractional values using a system called a <a href="https://en.wikipedia.org/wiki/Fixed-point_arithmetic">Fixed
|
|
||||||
Point Representation</a>.
|
|
||||||
What do we trade away? Numeric range.</p>
|
|
||||||
<ul>
|
|
||||||
<li>Floating point math stores bits for base value and for exponent all according
|
|
||||||
to a single <a href="https://en.wikipedia.org/wiki/IEEE_754">well defined</a> standard
|
|
||||||
for how such a complicated thing works.</li>
|
|
||||||
<li>Fixed point math takes a normal integer (either signed or unsigned) and then
|
|
||||||
just "mentally associates" it (so to speak) with a fractional value for its
|
|
||||||
"units". If you have 3 and it's in units of 1/2, then you have 3/2, or 1.5
|
|
||||||
using decimal notation. If your number is 256 and it's in units of 1/256th
|
|
||||||
then the value is 1.0 in decimal notation.</li>
|
|
||||||
</ul>
|
|
||||||
<p>Floating point math requires dedicated hardware to perform quickly, but it can
|
|
||||||
"trade" precision when it needs to represent extremely large or small values.</p>
|
|
||||||
<p>Fixed point math is just integral math, which our GBA is reasonably good at, but
|
|
||||||
because your number is associated with a fixed fraction your results can get out
|
|
||||||
of range very easily.</p>
|
|
||||||
<a class="header" href="#representing-a-fixed-point-value" id="representing-a-fixed-point-value"><h2>Representing A Fixed Point Value</h2></a>
|
|
||||||
<p>So we want to associate our numbers with a mental note of what units they're in:</p>
|
|
||||||
<ul>
|
|
||||||
<li><a href="https://doc.rust-lang.org/core/marker/struct.PhantomData.html">PhantomData</a>
|
|
||||||
is a type that tells the compiler "please remember this extra type info" when
|
|
||||||
you add it as a field to a struct. It goes away at compile time, so it's
|
|
||||||
perfect for us to use as space for a note to ourselves without causing runtime
|
|
||||||
overhead.</li>
|
|
||||||
<li>The <a href="https://crates.io/crates/typenum">typenum</a> crate is the best way to
|
|
||||||
represent a number within a type in Rust. Since our values on the GBA are
|
|
||||||
always specified as a number of fractional bits to count the number as, we can
|
|
||||||
put <code>typenum</code> types such as <code>U8</code> or <code>U14</code> into our <code>PhantomData</code> to keep track
|
|
||||||
of what's going on.</li>
|
|
||||||
</ul>
|
|
||||||
<p>Now, those of you who know me, or perhaps just know my reputation, will of
|
|
||||||
course <em>immediately</em> question what happened to the real Lokathor. I do not care
|
|
||||||
for most crates, and I particularly don't care for using a crate in teaching
|
|
||||||
situations. However, <code>typenum</code> has a number of factors on its side that let me
|
|
||||||
suggest it in this situation:</p>
|
|
||||||
<ul>
|
|
||||||
<li>It's version 1.10 with a total of 21 versions and nearly 700k downloads, so we
|
|
||||||
can expect that the major troubles have been shaken out and that it will remain
|
|
||||||
fairly stable for quite some time to come.</li>
|
|
||||||
<li>It has no further dependencies that it's going to drag into the compilation.</li>
|
|
||||||
<li>It happens all at compile time, so it's not clogging up our actual game with
|
|
||||||
any nonsense.</li>
|
|
||||||
<li>The (interesting) subject of "how do you do math inside Rust's trait system?" is
|
|
||||||
totally separate from the concern that we're trying to focus on here.</li>
|
|
||||||
</ul>
|
|
||||||
<p>Therefore, we will consider it acceptable to use this crate.</p>
|
|
||||||
<p>Now the <code>typenum</code> crate defines a whole lot, but we'll focus down to just a
|
|
||||||
single type at the moment:
|
|
||||||
<a href="https://docs.rs/typenum/1.10.0/typenum/uint/struct.UInt.html">UInt</a> is a
|
|
||||||
type-level unsigned value. It's like <code>u8</code> or <code>u16</code>, but while they're types that
|
|
||||||
then have values, each <code>UInt</code> construction statically equates to a specific
|
|
||||||
value. Like how the <code>()</code> type only has one value, which is also called <code>()</code>. In
|
|
||||||
this case, you wrap up <code>UInt</code> around smaller <code>UInt</code> values and a <code>B1</code> or <code>B0</code>
|
|
||||||
value to build up the binary number that you want at the type level.</p>
|
|
||||||
<p>In other words, instead of writing</p>
|
|
||||||
<pre><pre class="playpen"><code class="language-rust">
|
|
||||||
# #![allow(unused_variables)]
|
|
||||||
#fn main() {
|
|
||||||
let six = 0b110;
|
|
||||||
#}</code></pre></pre>
|
|
||||||
<p>We write</p>
|
|
||||||
<pre><pre class="playpen"><code class="language-rust">
|
|
||||||
# #![allow(unused_variables)]
|
|
||||||
#fn main() {
|
|
||||||
type U6 = UInt<UInt<UInt<UTerm, B1>, B1>, B0>;
|
|
||||||
#}</code></pre></pre>
|
|
||||||
<p>Wild, I know. If you look into the <code>typenum</code> crate you can do math and stuff
|
|
||||||
with these type level numbers, and we will a little bit below, but to start off
|
|
||||||
we <em>just</em> need to store one in some <code>PhantomData</code>.</p>
|
|
||||||
<a class="header" href="#a-struct-for-fixed-point" id="a-struct-for-fixed-point"><h3>A struct For Fixed Point</h3></a>
|
|
||||||
<p>Our actual type for a fixed point value looks like this:</p>
|
|
||||||
<pre><pre class="playpen"><code class="language-rust">
|
|
||||||
# #![allow(unused_variables)]
|
|
||||||
#fn main() {
|
|
||||||
use core::marker::PhantomData;
|
|
||||||
use typenum::marker_traits::Unsigned;
|
|
||||||
|
|
||||||
/// Fixed point `T` value with `F` fractional bits.
|
|
||||||
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
|
|
||||||
#[repr(transparent)]
|
|
||||||
pub struct Fx<T, F: Unsigned> {
|
|
||||||
bits: T,
|
|
||||||
_phantom: PhantomData<F>,
|
|
||||||
}
|
|
||||||
#}</code></pre></pre>
|
|
||||||
<p>This says that <code>Fx<T,F></code> is a generic type that holds some base number type <code>T</code>
|
|
||||||
and a <code>F</code> type that's marking off how many fractional bits we're using. We only
|
|
||||||
want people giving unsigned type-level values for the <code>PhantomData</code> type, so we
|
|
||||||
use the trait bound <code>F: Unsigned</code>.</p>
|
|
||||||
<p>We use
|
|
||||||
<a href="https://github.com/rust-lang/rfcs/blob/master/text/1758-repr-transparent.md">repr(transparent)</a>
|
|
||||||
here to ensure that <code>Fx</code> will always be treated just like the base type in the
|
|
||||||
final program (in terms of bit pattern and ABI).</p>
|
|
||||||
<p>If you go and check, this is <em>basically</em> how the existing general purpose crates
|
|
||||||
for fixed point math represent their numbers. They're a little fancier about it
|
|
||||||
because they have to cover every case, and we only have to cover our GBA case.</p>
|
|
||||||
<p>That's quite a bit to type though. We probably want to make a few type aliases
|
|
||||||
for things to be easier to look at. Unfortunately there's <a href="https://en.wikipedia.org/wiki/Fixed-point_arithmetic#Notation">no standard
|
|
||||||
notation</a> for how
|
|
||||||
you write a fixed point type. We also have to limit ourselves to what's valid
|
|
||||||
for use in a Rust type too. I like the <code>fx</code> thing, so we'll use that for signed
|
|
||||||
and then <code>fxu</code> if we need an unsigned value.</p>
|
|
||||||
<pre><pre class="playpen"><code class="language-rust">
|
|
||||||
# #![allow(unused_variables)]
|
|
||||||
#fn main() {
|
|
||||||
/// Alias for an `i16` fixed point value with 8 fractional bits.
|
|
||||||
pub type fx8_8 = Fx<i16,U8>;
|
|
||||||
#}</code></pre></pre>
|
|
||||||
<p>Rust will complain about having <code>non_camel_case_types</code>, and you can shut that
|
|
||||||
warning up by putting an <code>#[allow(non_camel_case_types)]</code> attribute on the type
|
|
||||||
alias directly, or you can use <code>#![allow(non_camel_case_types)]</code> at the very top
|
|
||||||
of the module to shut up that warning for the whole module (which is what I
|
|
||||||
did).</p>
|
|
||||||
<a class="header" href="#constructing-a-fixed-point-value" id="constructing-a-fixed-point-value"><h2>Constructing A Fixed Point Value</h2></a>
|
|
||||||
<p>So how do we actually <em>make</em> one of these values? Well, we can always just wrap or unwrap any value in our <code>Fx</code> type:</p>
|
|
||||||
<pre><pre class="playpen"><code class="language-rust">
|
|
||||||
# #![allow(unused_variables)]
|
|
||||||
#fn main() {
|
|
||||||
impl<T, F: Unsigned> Fx<T, F> {
|
|
||||||
/// Uses the provided value directly.
|
|
||||||
pub fn from_raw(r: T) -> Self {
|
|
||||||
Fx {
|
|
||||||
num: r,
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Unwraps the inner value.
|
|
||||||
pub fn into_raw(self) -> T {
|
|
||||||
self.num
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#}</code></pre></pre>
|
|
||||||
<p>I'd like to use the <code>From</code> trait of course, but it was giving me some trouble, i
|
|
||||||
think because of the orphan rule. Oh well.</p>
|
|
||||||
<p>If we want to be particular to the fact that these are supposed to be
|
|
||||||
<em>numbers</em>... that gets tricky. Rust is actually quite bad at being generic about
|
|
||||||
number types. You can use the <a href="https://crates.io/crates/num">num</a> crate, or you
|
|
||||||
can just use a macro and invoke it once per type. Guess what we're gonna do.</p>
|
|
||||||
<pre><pre class="playpen"><code class="language-rust">
|
|
||||||
# #![allow(unused_variables)]
|
|
||||||
#fn main() {
|
|
||||||
macro_rules! fixed_point_methods {
|
|
||||||
($t:ident) => {
|
|
||||||
impl<F: Unsigned> Fx<$t, F> {
|
|
||||||
/// Gives the smallest positive non-zero value.
|
|
||||||
pub fn precision() -> Self {
|
|
||||||
Fx {
|
|
||||||
num: 1,
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Makes a value with the integer part shifted into place.
|
|
||||||
pub fn from_int_part(i: $t) -> Self {
|
|
||||||
Fx {
|
|
||||||
num: i << F::U8,
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fixed_point_methods! {u8}
|
|
||||||
fixed_point_methods! {i8}
|
|
||||||
fixed_point_methods! {i16}
|
|
||||||
fixed_point_methods! {u16}
|
|
||||||
fixed_point_methods! {i32}
|
|
||||||
fixed_point_methods! {u32}
|
|
||||||
#}</code></pre></pre>
|
|
||||||
<p>Now <em>you'd think</em> that those can be <code>const</code>, but at the moment you can't have a
|
|
||||||
<code>const</code> function with a bound on any trait other than <code>Sized</code>, so they have to
|
|
||||||
be normal functions.</p>
|
|
||||||
<p>Also, we're doing something a little interesting there with <code>from_int_part</code>. We
|
|
||||||
can take our <code>F</code> type and get its constant value. There's other associated
|
|
||||||
constants if we want it in other types, and also non-const methods if you wanted
|
|
||||||
that for some reason (maybe passing it as a closure function? dunno).</p>
|
|
||||||
<a class="header" href="#casting-base-values" id="casting-base-values"><h2>Casting Base Values</h2></a>
|
|
||||||
<p>Next, once we have a value in one base type we will need to be able to move it
|
|
||||||
into another base type. Unfortunately this means we gotta use the <code>as</code> operator,
|
|
||||||
which requires a concrete source type and a concrete destination type. There's
|
|
||||||
no easy way for us to make it generic here.</p>
|
|
||||||
<p>We could let the user use <code>into_raw</code>, cast, and then do <code>from_raw</code>, but that's
|
|
||||||
error prone because they might change the fractional bit count accidentally.
|
|
||||||
This means that we have to write a function that does the casting while
|
|
||||||
perfectly preserving the fractional bit quantity. If we wrote one function for
|
|
||||||
each conversion it'd be like 30 different possible casts (6 base types that we
|
|
||||||
support, and then 5 possible target types). Instead, we'll write it just once in
|
|
||||||
a way that takes a closure, and let the user pass a closure that does the cast.
|
|
||||||
The compiler should merge it all together quite nicely for us once optimizations
|
|
||||||
kick in.</p>
|
|
||||||
<p>This code goes outside the macro. I want to avoid too much code in the macro if
|
|
||||||
we can, it's a little easier to cope with I think.</p>
|
|
||||||
<pre><pre class="playpen"><code class="language-rust">
|
|
||||||
# #![allow(unused_variables)]
|
|
||||||
#fn main() {
|
|
||||||
/// Casts the base type, keeping the fractional bit quantity the same.
|
|
||||||
pub fn cast_inner<Z, C: Fn(T) -> Z>(self, op: C) -> Fx<Z, F> {
|
|
||||||
Fx {
|
|
||||||
num: op(self.num),
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#}</code></pre></pre>
|
|
||||||
<p>It's horrible and ugly, but Rust is just bad at numbers sometimes.</p>
|
|
||||||
<a class="header" href="#adjusting-fractional-part" id="adjusting-fractional-part"><h2>Adjusting Fractional Part</h2></a>
|
|
||||||
<p>In addition to the base value we might want to change our fractional bit
|
|
||||||
quantity. This is actually easier that it sounds, but it also requires us to be
|
|
||||||
tricky with the generics. We can actually use some typenum type level operators
|
|
||||||
here.</p>
|
|
||||||
<p>This code goes inside the macro: we need to be able to use the left shift and
|
|
||||||
right shift, which is easiest when we just use the macro's <code>$t</code> as our type. We
|
|
||||||
could alternately put a similar function outside the macro and be generic on <code>T</code>
|
|
||||||
having the left and right shift operators by using a <code>where</code> clause. As much as
|
|
||||||
I'd like to avoid too much code being generated by macro, I'd <em>even more</em> like
|
|
||||||
to avoid generic code with huge and complicated trait bounds. It comes down to
|
|
||||||
style, and you gotta decide for yourself.</p>
|
|
||||||
<pre><pre class="playpen"><code class="language-rust">
|
|
||||||
# #![allow(unused_variables)]
|
|
||||||
#fn main() {
|
|
||||||
/// Changes the fractional bit quantity, keeping the base type the same.
|
|
||||||
pub fn adjust_fractional_bits<Y: Unsigned + IsEqual<F, Output = False>>(self) -> Fx<$t, Y> {
|
|
||||||
let leftward_movement: i32 = Y::to_i32() - F::to_i32();
|
|
||||||
Fx {
|
|
||||||
num: if leftward_movement > 0 {
|
|
||||||
self.num << leftward_movement
|
|
||||||
} else {
|
|
||||||
self.num >> (-leftward_movement)
|
|
||||||
},
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#}</code></pre></pre>
|
|
||||||
<p>There's a few things at work. First, we introduce <code>Y</code> as the target number of
|
|
||||||
fractional bits, and we <em>also</em> limit it that the target bits quantity can't be
|
|
||||||
the same as we already have using a type-level operator. If it's the same as we
|
|
||||||
started with, why are you doing the cast at all?</p>
|
|
||||||
<p>Now, once we're sure that the current bits and target bits aren't the same, we
|
|
||||||
compute <code>target - start</code>, and call this our "leftward movement". Example: if
|
|
||||||
we're targeting 8 bits and we're at 4 bits, we do 8-4 and get +4 as our leftward
|
|
||||||
movement. If the leftward_movement is positive we naturally shift our current
|
|
||||||
value to the left. If it's not positive then it <em>must</em> be negative because we
|
|
||||||
eliminated 0 as a possibility using the type-level operator, so we shift to the
|
|
||||||
right by the negative value.</p>
|
|
||||||
<a class="header" href="#addition-subtraction-shifting-negative-comparisons" id="addition-subtraction-shifting-negative-comparisons"><h2>Addition, Subtraction, Shifting, Negative, Comparisons</h2></a>
|
|
||||||
<p>From here on we're getting help from <a href="https://spin.atomicobject.com/2012/03/15/simple-fixed-point-math/">this blog
|
|
||||||
post</a> by <a href="https://spin.atomicobject.com/author/vranish/">Job
|
|
||||||
Vranish</a>, so thank them if you
|
|
||||||
learn something.</p>
|
|
||||||
<p>I might have given away the game a bit with those <code>derive</code> traits on our fixed
|
|
||||||
point type. For a fair number of operations you can use the normal form of the
|
|
||||||
op on the inner bits as long as the fractional parts have the same quantity.
|
|
||||||
This includes equality and ordering (which we derived) as well as addition,
|
|
||||||
subtraction, and bit shifting (which we need to do ourselves).</p>
|
|
||||||
<p>This code can go outside the macro, with sufficient trait bounds.</p>
|
|
||||||
<pre><pre class="playpen"><code class="language-rust">
|
|
||||||
# #![allow(unused_variables)]
|
|
||||||
#fn main() {
|
|
||||||
impl<T: Add<Output = T>, F: Unsigned> Add for Fx<T, F> {
|
|
||||||
type Output = Self;
|
|
||||||
fn add(self, rhs: Fx<T, F>) -> Self::Output {
|
|
||||||
Fx {
|
|
||||||
num: self.num + rhs.num,
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#}</code></pre></pre>
|
|
||||||
<p>The bound on <code>T</code> makes it so that <code>Fx<T, F></code> can be added any time that <code>T</code> can
|
|
||||||
be added to its own type with itself as the output. We can use the exact same
|
|
||||||
pattern for <code>Sub</code>, <code>Shl</code>, <code>Shr</code>, and <code>Neg</code>. With enough trait bounds, we can do
|
|
||||||
anything!</p>
|
|
||||||
<pre><pre class="playpen"><code class="language-rust">
|
|
||||||
# #![allow(unused_variables)]
|
|
||||||
#fn main() {
|
|
||||||
impl<T: Sub<Output = T>, F: Unsigned> Sub for Fx<T, F> {
|
|
||||||
type Output = Self;
|
|
||||||
fn sub(self, rhs: Fx<T, F>) -> Self::Output {
|
|
||||||
Fx {
|
|
||||||
num: self.num - rhs.num,
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Shl<u32, Output = T>, F: Unsigned> Shl<u32> for Fx<T, F> {
|
|
||||||
type Output = Self;
|
|
||||||
fn shl(self, rhs: u32) -> Self::Output {
|
|
||||||
Fx {
|
|
||||||
num: self.num << rhs,
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Shr<u32, Output = T>, F: Unsigned> Shr<u32> for Fx<T, F> {
|
|
||||||
type Output = Self;
|
|
||||||
fn shr(self, rhs: u32) -> Self::Output {
|
|
||||||
Fx {
|
|
||||||
num: self.num >> rhs,
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Neg<Output = T>, F: Unsigned> Neg for Fx<T, F> {
|
|
||||||
type Output = Self;
|
|
||||||
fn neg(self) -> Self::Output {
|
|
||||||
Fx {
|
|
||||||
num: -self.num,
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#}</code></pre></pre>
|
|
||||||
<p>Unfortunately, for <code>Shl</code> and <code>Shr</code> to have as much coverage on our type as it
|
|
||||||
does on the base type (allowing just about any right hand side) we'd have to do
|
|
||||||
another macro, but I think just <code>u32</code> is fine. We can always add more later if
|
|
||||||
we need.</p>
|
|
||||||
<p>We could also implement <code>BitAnd</code>, <code>BitOr</code>, <code>BitXor</code>, and <code>Not</code>, but they don't
|
|
||||||
seem relevent to our fixed point math use, and this section is getting long
|
|
||||||
already. Just use the same general patterns if you want to add it in your own
|
|
||||||
programs. Shockingly, <code>Rem</code> also works directly if you want it, though I don't
|
|
||||||
forsee us needing floating point remainder. Also, the GBA can't do hardware
|
|
||||||
division or remainder, and we'll have to work around that below when we
|
|
||||||
implement <code>Div</code> (which maybe we don't need, but it's complex enough I should
|
|
||||||
show it instead of letting people guess).</p>
|
|
||||||
<p><strong>Note:</strong> In addition to the various <code>Op</code> traits, there's also <code>OpAssign</code>
|
|
||||||
variants. Each <code>OpAssign</code> is the same as <code>Op</code>, but takes <code>&mut self</code> instead of
|
|
||||||
<code>self</code> and then modifies in place instead of producing a fresh value. In other
|
|
||||||
words, if you want both <code>+</code> and <code>+=</code> you'll need to do the <code>AddAssign</code> trait
|
|
||||||
too. It's not the worst thing to just write <code>a = a+b</code>, so I won't bother with
|
|
||||||
showing all that here. It's pretty easy to figure out for yourself if you want.</p>
|
|
||||||
<a class="header" href="#multiplication" id="multiplication"><h2>Multiplication</h2></a>
|
|
||||||
<p>This is where things get more interesting. When we have two numbers <code>A</code> and <code>B</code>
|
|
||||||
they really stand for <code>(a*f)</code> and <code>(b*f)</code>. If we write <code>A*B</code> then we're really
|
|
||||||
writing <code>(a*f)*(b*f)</code>, which can be rewritten as <code>(a*b)*2f</code>, and now it's
|
|
||||||
obvious that we have one more <code>f</code> than we wanted to have. We have to do the
|
|
||||||
multiply of the inner value and then divide out the <code>f</code>. We divide by <code>1 << bit_count</code>, so if we have 8 fractional bits we'll divide by 256.</p>
|
|
||||||
<p>The catch is that, when we do the multiply we're <em>extremely</em> likely to overflow
|
|
||||||
our base type with that multiplication step. Then we do that divide, and now our
|
|
||||||
result is basically nonsense. We can avoid this to some extent by casting up to
|
|
||||||
a higher bit type, doing the multiplication and division at higher precision,
|
|
||||||
and then casting back down. We want as much precision as possible without being
|
|
||||||
too inefficient, so we'll always cast up to 32-bit (on a 64-bit machine you'd
|
|
||||||
cast up to 64-bit instead).</p>
|
|
||||||
<p>Naturally, any signed value has to be cast up to <code>i32</code> and any unsigned value
|
|
||||||
has to be cast up to <code>u32</code>, so we'll have to handle those separately.</p>
|
|
||||||
<p>Also, instead of doing an <em>actual</em> divide we can right-shift by the correct
|
|
||||||
number of bits to achieve the same effect. <em>Except</em> when we have a signed value
|
|
||||||
that's negative, because actual division truncates towards zero and
|
|
||||||
right-shifting truncates towards negative infinity. We can get around <em>this</em> by
|
|
||||||
flipping the sign, doing the shift, and flipping the sign again (which sounds
|
|
||||||
silly but it's so much faster than doing an actual division).</p>
|
|
||||||
<p>Also, again signed values can be annoying, because if the value <em>just happens</em>
|
|
||||||
to be <code>i32::MIN</code> then when you negate it you'll have... <em>still</em> a negative
|
|
||||||
value. I'm not 100% on this, but I think the correct thing to do at that point
|
|
||||||
is to give <code>$t::MIN</code> as the output num value.</p>
|
|
||||||
<p>Did you get all that? Good, because this involves casting, so we will need to
|
|
||||||
implement it three times, which calls for another macro.</p>
|
|
||||||
<pre><pre class="playpen"><code class="language-rust">
|
|
||||||
# #![allow(unused_variables)]
|
|
||||||
#fn main() {
|
|
||||||
macro_rules! fixed_point_signed_multiply {
|
|
||||||
($t:ident) => {
|
|
||||||
impl<F: Unsigned> Mul for Fx<$t, F> {
|
|
||||||
type Output = Self;
|
|
||||||
fn mul(self, rhs: Fx<$t, F>) -> Self::Output {
|
|
||||||
let pre_shift = (self.num as i32).wrapping_mul(rhs.num as i32);
|
|
||||||
if pre_shift < 0 {
|
|
||||||
if pre_shift == core::i32::MIN {
|
|
||||||
Fx {
|
|
||||||
num: core::$t::MIN,
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Fx {
|
|
||||||
num: (-((-pre_shift) >> F::U8)) as $t,
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Fx {
|
|
||||||
num: (pre_shift >> F::U8) as $t,
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fixed_point_signed_multiply! {i8}
|
|
||||||
fixed_point_signed_multiply! {i16}
|
|
||||||
fixed_point_signed_multiply! {i32}
|
|
||||||
|
|
||||||
macro_rules! fixed_point_unsigned_multiply {
|
|
||||||
($t:ident) => {
|
|
||||||
impl<F: Unsigned> Mul for Fx<$t, F> {
|
|
||||||
type Output = Self;
|
|
||||||
fn mul(self, rhs: Fx<$t, F>) -> Self::Output {
|
|
||||||
Fx {
|
|
||||||
num: ((self.num as u32).wrapping_mul(rhs.num as u32) >> F::U8) as $t,
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fixed_point_unsigned_multiply! {u8}
|
|
||||||
fixed_point_unsigned_multiply! {u16}
|
|
||||||
fixed_point_unsigned_multiply! {u32}
|
|
||||||
#}</code></pre></pre>
|
|
||||||
<a class="header" href="#division" id="division"><h2>Division</h2></a>
|
|
||||||
<p>Division is similar to multiplication, but reversed. Which makes sense. This
|
|
||||||
time <code>A/B</code> gives <code>(a*f)/(b*f)</code> which is <code>a/b</code>, one <em>less</em> <code>f</code> than we were
|
|
||||||
after.</p>
|
|
||||||
<p>As with the multiplication version of things, we have to up-cast our inner value
|
|
||||||
as much a we can before doing the math, to allow for the most precision
|
|
||||||
possible.</p>
|
|
||||||
<p>The snag here is that the GBA has no division or remainder. Instead, the GBA has
|
|
||||||
a BIOS function you can call to do <code>i32/i32</code> division.</p>
|
|
||||||
<p>This is a potential problem for us though. If we have some unsigned value, we
|
|
||||||
need it to fit within the positive space of an <code>i32</code> <em>after the multiply</em> so
|
|
||||||
that we can cast it to <code>i32</code>, call the BIOS function that only works on <code>i32</code>
|
|
||||||
values, and cast it back to its actual type.</p>
|
|
||||||
<ul>
|
|
||||||
<li>If you have a u8 you're always okay, even with 8 floating bits.</li>
|
|
||||||
<li>If you have a u16 you're okay even with a maximum value up to 15 floating
|
|
||||||
bits, but having a maximum value and 16 floating bits makes it break.</li>
|
|
||||||
<li>If you have a u32 you're probably going to be in trouble all the time.</li>
|
|
||||||
</ul>
|
|
||||||
<p>So... ugh, there's not much we can do about this. For now we'll just have to
|
|
||||||
suffer some.</p>
|
|
||||||
<p>// TODO: find a numerics book that tells us how to do <code>u32/u32</code> divisions.</p>
|
|
||||||
<pre><pre class="playpen"><code class="language-rust">
|
|
||||||
# #![allow(unused_variables)]
|
|
||||||
#fn main() {
|
|
||||||
macro_rules! fixed_point_signed_division {
|
|
||||||
($t:ident) => {
|
|
||||||
impl<F: Unsigned> Div for Fx<$t, F> {
|
|
||||||
type Output = Self;
|
|
||||||
fn div(self, rhs: Fx<$t, F>) -> Self::Output {
|
|
||||||
let mul_output: i32 = (self.num as i32).wrapping_mul(1 << F::U8);
|
|
||||||
let divide_result: i32 = crate::bios::div(mul_output, rhs.num as i32);
|
|
||||||
Fx {
|
|
||||||
num: divide_result as $t,
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fixed_point_signed_division! {i8}
|
|
||||||
fixed_point_signed_division! {i16}
|
|
||||||
fixed_point_signed_division! {i32}
|
|
||||||
|
|
||||||
macro_rules! fixed_point_unsigned_division {
|
|
||||||
($t:ident) => {
|
|
||||||
impl<F: Unsigned> Div for Fx<$t, F> {
|
|
||||||
type Output = Self;
|
|
||||||
fn div(self, rhs: Fx<$t, F>) -> Self::Output {
|
|
||||||
let mul_output: i32 = (self.num as i32).wrapping_mul(1 << F::U8);
|
|
||||||
let divide_result: i32 = crate::bios::div(mul_output, rhs.num as i32);
|
|
||||||
Fx {
|
|
||||||
num: divide_result as $t,
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fixed_point_unsigned_division! {u8}
|
|
||||||
fixed_point_unsigned_division! {u16}
|
|
||||||
fixed_point_unsigned_division! {u32}
|
|
||||||
#}</code></pre></pre>
|
|
||||||
<a class="header" href="#trigonometry" id="trigonometry"><h2>Trigonometry</h2></a>
|
|
||||||
<p>TODO: look up tables! arcbits!</p>
|
|
||||||
<a class="header" href="#just-using-a-crate" id="just-using-a-crate"><h2>Just Using A Crate</h2></a>
|
|
||||||
<p>If, after seeing all that, and seeing that I still didn't even cover every
|
|
||||||
possible trait impl that you might want for all the possible types... if after
|
|
||||||
all that you feel too intimidated, then I'll cave a bit on your behalf and
|
|
||||||
suggest to you that the <a href="https://crates.io/crates/fixed">fixed</a> crate seems to
|
|
||||||
be the best crate available for fixed point math.</p>
|
|
||||||
<p><em>I have not tested its use on the GBA myself</em>.</p>
|
|
||||||
<p>It's just my recommendation from looking at the docs of the various options
|
|
||||||
available, if you really wanted to just have a crate for it.</p>
|
|
||||||
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
|
||||||
<!-- Mobile navigation buttons -->
|
|
||||||
|
|
||||||
<a rel="prev" href="../01-quirks/01-no_std.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
|
||||||
<i class="fa fa-angle-left"></i>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a rel="next" href="../01-quirks/03-volatile_destination.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
|
||||||
<i class="fa fa-angle-right"></i>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
|
|
||||||
<div style="clear: both"></div>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
|
||||||
|
|
||||||
<a href="../01-quirks/01-no_std.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
|
||||||
<i class="fa fa-angle-left"></i>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a href="../01-quirks/03-volatile_destination.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
|
||||||
<i class="fa fa-angle-right"></i>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script src="../elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
|
|
||||||
<script src="../mark.min.js" type="text/javascript" charset="utf-8"></script>
|
|
||||||
<script src="../searcher.js" type="text/javascript" charset="utf-8"></script>
|
|
||||||
|
|
||||||
|
|
||||||
<script src="../clipboard.min.js" type="text/javascript" charset="utf-8"></script>
|
|
||||||
<script src="../highlight.js" type="text/javascript" charset="utf-8"></script>
|
|
||||||
<script src="../book.js" type="text/javascript" charset="utf-8"></script>
|
|
||||||
|
|
||||||
<!-- Custom JS scripts -->
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,521 +0,0 @@
|
||||||
<!DOCTYPE HTML>
|
|
||||||
<html lang="en" class="sidebar-visible no-js">
|
|
||||||
<head>
|
|
||||||
<!-- Book generated using mdBook -->
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Volatile Destination - 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" class="active"><strong aria-hidden="true">2.3.</strong> Volatile Destination</a></li><li><a href="../01-quirks/04-newtype.html"><strong aria-hidden="true">2.4.</strong> Newtype</a></li><li><a href="../01-quirks/05-const_asserts.html"><strong aria-hidden="true">2.5.</strong> Const Asserts</a></li></ol></li><li><a href="../02-concepts/00-index.html"><strong aria-hidden="true">3.</strong> Concepts</a></li><li><ol class="section"><li><a href="../02-concepts/01-cpu.html"><strong aria-hidden="true">3.1.</strong> CPU</a></li><li><a href="../02-concepts/02-bios.html"><strong aria-hidden="true">3.2.</strong> BIOS</a></li><li><a href="../02-concepts/03-wram.html"><strong aria-hidden="true">3.3.</strong> Work RAM</a></li><li><a href="../02-concepts/04-io-registers.html"><strong aria-hidden="true">3.4.</strong> IO Registers</a></li><li><a href="../02-concepts/05-palram.html"><strong aria-hidden="true">3.5.</strong> Palette RAM</a></li><li><a href="../02-concepts/06-vram.html"><strong aria-hidden="true">3.6.</strong> Video RAM</a></li><li><a href="../02-concepts/07-oam.html"><strong aria-hidden="true">3.7.</strong> Object Attribute Memory</a></li><li><a href="../02-concepts/08-rom.html"><strong aria-hidden="true">3.8.</strong> Game Pak ROM / Flash ROM</a></li><li><a href="../02-concepts/09-sram.html"><strong aria-hidden="true">3.9.</strong> Save RAM</a></li></ol></li><li><a href="../03-video/00-index.html"><strong aria-hidden="true">4.</strong> Video</a></li><li><ol class="section"><li><a href="../03-video/01-rgb15.html"><strong aria-hidden="true">4.1.</strong> RBG15 Color</a></li><li><a href="../03-video/TODO.html"><strong aria-hidden="true">4.2.</strong> TODO</a></li></ol></li><li><a href="../04-non-video/00-index.html"><strong aria-hidden="true">5.</strong> Non-Video</a></li><li><ol class="section"><li><a href="../04-non-video/01-buttons.html"><strong aria-hidden="true">5.1.</strong> Buttons</a></li><li><a href="../04-non-video/02-timers.html"><strong aria-hidden="true">5.2.</strong> Timers</a></li><li><a href="../04-non-video/03-dma.html"><strong aria-hidden="true">5.3.</strong> Direct Memory Access</a></li><li><a href="../04-non-video/04-sound.html"><strong aria-hidden="true">5.4.</strong> Sound</a></li><li><a href="../04-non-video/05-interrupts.html"><strong aria-hidden="true">5.5.</strong> Interrupts</a></li><li><a href="../04-non-video/06-link_cable.html"><strong aria-hidden="true">5.6.</strong> Link Cable</a></li><li><a href="../04-non-video/07-game_pak.html"><strong aria-hidden="true">5.7.</strong> Game Pak</a></li></ol></li><li><a href="../05-examples/00-index.html"><strong aria-hidden="true">6.</strong> Examples</a></li></ol>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<div id="page-wrapper" class="page-wrapper">
|
|
||||||
|
|
||||||
<div class="page">
|
|
||||||
|
|
||||||
<div id="menu-bar" class="menu-bar">
|
|
||||||
<div id="menu-bar-sticky-container">
|
|
||||||
<div class="left-buttons">
|
|
||||||
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
|
||||||
<i class="fa fa-bars"></i>
|
|
||||||
</button>
|
|
||||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
|
||||||
<i class="fa fa-paint-brush"></i>
|
|
||||||
</button>
|
|
||||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
|
||||||
<li role="none"><button role="menuitem" class="theme" id="light">Light <span class="default">(default)</span></button></li>
|
|
||||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
|
||||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
|
||||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
|
||||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
|
||||||
<i class="fa fa-search"></i>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h1 class="menu-title">Rust GBA Guide</h1>
|
|
||||||
|
|
||||||
<div class="right-buttons">
|
|
||||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
|
||||||
<i id="print-button" class="fa fa-print"></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div id="search-wrapper" class="hidden">
|
|
||||||
<form id="searchbar-outer" class="searchbar-outer">
|
|
||||||
<input type="search" name="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
|
||||||
</form>
|
|
||||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
|
||||||
<div id="searchresults-header" class="searchresults-header"></div>
|
|
||||||
<ul id="searchresults">
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
|
||||||
<script type="text/javascript">
|
|
||||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
|
||||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
|
||||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
|
||||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div id="content" class="content">
|
|
||||||
<main>
|
|
||||||
<a class="header" href="#volatile-destination" id="volatile-destination"><h1>Volatile Destination</h1></a>
|
|
||||||
<p>TODO: update this when we can make more stuff <code>const</code></p>
|
|
||||||
<a class="header" href="#volatile-memory" id="volatile-memory"><h2>Volatile Memory</h2></a>
|
|
||||||
<p>The compiler is an eager friend, so when it sees a read or a write that won't
|
|
||||||
have an effect, it eliminates that read or write. For example, if we write</p>
|
|
||||||
<pre><pre class="playpen"><code class="language-rust">
|
|
||||||
# #![allow(unused_variables)]
|
|
||||||
#fn main() {
|
|
||||||
let mut x = 5;
|
|
||||||
x = 7;
|
|
||||||
#}</code></pre></pre>
|
|
||||||
<p>The compiler won't actually ever put 5 into <code>x</code>. It'll skip straight to putting
|
|
||||||
7 in <code>x</code>, because we never read from <code>x</code> when it's 5, so that's a safe change to
|
|
||||||
make. Normally, values are stored in RAM, which has no side effects when you
|
|
||||||
read and write from it. RAM is purely for keeping notes about values you'll need
|
|
||||||
later on.</p>
|
|
||||||
<p>However, what if we had a bit of hardware where we wanted to do a write and that
|
|
||||||
did something <em>other than</em> keeping the value for us to look at later? As you saw
|
|
||||||
in the <code>hello_magic</code> example, we have to use a <code>write_volatile</code> operation.
|
|
||||||
Volatile means "just do it anyway". The compiler thinks that it's pointless, but
|
|
||||||
we know better, so we can force it to really do exactly what we say by using
|
|
||||||
<code>write_volatile</code> instead of <code>write</code>.</p>
|
|
||||||
<p>This is kinda error prone though, right? Because it's just a raw pointer, so we
|
|
||||||
might forget to use <code>write_volatile</code> at some point.</p>
|
|
||||||
<p>Instead, we want a type that's always going to use volatile reads and writes.
|
|
||||||
Also, we want a pointer type that lets our reads and writes to be as safe as
|
|
||||||
possible once we've unsafely constructed the initial value.</p>
|
|
||||||
<a class="header" href="#constructing-the-voladdress-type" id="constructing-the-voladdress-type"><h3>Constructing The VolAddress Type</h3></a>
|
|
||||||
<p>First, we want a type that stores a location within the address space. This can
|
|
||||||
be a pointer, or a <code>usize</code>, and we'll use a <code>usize</code> because that's easier to
|
|
||||||
work with in a <code>const</code> context (and we want to have <code>const</code> when we can get it).
|
|
||||||
We'll also have our type use <code>NonZeroUsize</code> instead of just <code>usize</code> so that
|
|
||||||
<code>Option<VolAddress<T>></code> stays as a single machine word. This helps quite a bit
|
|
||||||
when we want to iterate over the addresses of a block of memory (such as
|
|
||||||
locations within the palette memory). Hardware is never at the null address
|
|
||||||
anyway. Also, if we had <em>just</em> an address number then we wouldn't be able to
|
|
||||||
track what type the address is for. We need some
|
|
||||||
<a href="https://doc.rust-lang.org/core/marker/struct.PhantomData.html">PhantomData</a>,
|
|
||||||
and specifically we need the phantom data to be for <code>*mut T</code>:</p>
|
|
||||||
<ul>
|
|
||||||
<li>If we used <code>*const T</code> that'd have the wrong
|
|
||||||
<a href="https://doc.rust-lang.org/nomicon/subtyping.html">variance</a>.</li>
|
|
||||||
<li>If we used <code>&mut T</code> then that's fusing in the ideas of <em>lifetime</em> and
|
|
||||||
<em>exclusive access</em> to our type. That's potentially important, but that's also
|
|
||||||
an abstraction we'll build <em>on top of</em> this <code>VolAddress</code> type if we need it.</li>
|
|
||||||
</ul>
|
|
||||||
<p>One abstraction layer at a time, so we start with just a phantom pointer. This gives us a type that looks like this:</p>
|
|
||||||
<pre><pre class="playpen"><code class="language-rust">
|
|
||||||
# #![allow(unused_variables)]
|
|
||||||
#fn main() {
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[repr(transparent)]
|
|
||||||
pub struct VolAddress<T> {
|
|
||||||
address: NonZeroUsize,
|
|
||||||
marker: PhantomData<*mut T>,
|
|
||||||
}
|
|
||||||
#}</code></pre></pre>
|
|
||||||
<p>Now, because of how <code>derive</code> is specified, it derives traits <em>if the generic
|
|
||||||
parameter</em> supports those traits. Since our type is like a pointer, the traits
|
|
||||||
it supports are distinct from whatever traits the target type supports. So we'll
|
|
||||||
provide those implementations manually.</p>
|
|
||||||
<pre><pre class="playpen"><code class="language-rust">
|
|
||||||
# #![allow(unused_variables)]
|
|
||||||
#fn main() {
|
|
||||||
impl<T> Clone for VolAddress<T> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
*self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T> Copy for VolAddress<T> {}
|
|
||||||
impl<T> PartialEq for VolAddress<T> {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.address == other.address
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T> Eq for VolAddress<T> {}
|
|
||||||
impl<T> PartialOrd for VolAddress<T> {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
||||||
Some(self.address.cmp(&other.address))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T> Ord for VolAddress<T> {
|
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
|
||||||
self.address.cmp(&other.address)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#}</code></pre></pre>
|
|
||||||
<p>Boilerplate junk, not interesting. There's a reason that you derive those traits
|
|
||||||
99% of the time in Rust.</p>
|
|
||||||
<a class="header" href="#constructing-a-voladdress-value" id="constructing-a-voladdress-value"><h3>Constructing A VolAddress Value</h3></a>
|
|
||||||
<p>Okay so here's the next core concept: If we unsafely <em>construct</em> a
|
|
||||||
<code>VolAddress<T></code>, then we can safely <em>use</em> the value once it's been properly
|
|
||||||
created.</p>
|
|
||||||
<pre><pre class="playpen"><code class="language-rust">
|
|
||||||
# #![allow(unused_variables)]
|
|
||||||
#fn main() {
|
|
||||||
// you'll need these features enabled and a recent nightly
|
|
||||||
#![feature(const_int_wrapping)]
|
|
||||||
#![feature(min_const_unsafe_fn)]
|
|
||||||
|
|
||||||
impl<T> VolAddress<T> {
|
|
||||||
pub const unsafe fn new_unchecked(address: usize) -> Self {
|
|
||||||
VolAddress {
|
|
||||||
address: NonZeroUsize::new_unchecked(address),
|
|
||||||
marker: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub const unsafe fn cast<Z>(self) -> VolAddress<Z> {
|
|
||||||
VolAddress {
|
|
||||||
address: self.address,
|
|
||||||
marker: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub unsafe fn offset(self, offset: isize) -> Self {
|
|
||||||
VolAddress {
|
|
||||||
address: NonZeroUsize::new_unchecked(self.address.get().wrapping_add(offset as usize * core::mem::size_of::<T>())),
|
|
||||||
marker: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#}</code></pre></pre>
|
|
||||||
<p>So what are the unsafety rules here?</p>
|
|
||||||
<ul>
|
|
||||||
<li>Non-null, obviously.</li>
|
|
||||||
<li>Must be aligned for <code>T</code></li>
|
|
||||||
<li>Must always produce valid bit patterns for <code>T</code></li>
|
|
||||||
<li>Must not be part of the address space that Rust's stack or allocator will ever
|
|
||||||
uses.</li>
|
|
||||||
</ul>
|
|
||||||
<p>So, again using the <code>hello_magic</code> example, we had</p>
|
|
||||||
<pre><pre class="playpen"><code class="language-rust">
|
|
||||||
# #![allow(unused_variables)]
|
|
||||||
#fn main() {
|
|
||||||
(0x400_0000 as *mut u16).write_volatile(0x0403);
|
|
||||||
#}</code></pre></pre>
|
|
||||||
<p>And instead we could declare</p>
|
|
||||||
<pre><pre class="playpen"><code class="language-rust">
|
|
||||||
# #![allow(unused_variables)]
|
|
||||||
#fn main() {
|
|
||||||
const MAGIC_LOCATION: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0000) };
|
|
||||||
#}</code></pre></pre>
|
|
||||||
<a class="header" href="#using-a-voladdress-value" id="using-a-voladdress-value"><h3>Using A VolAddress Value</h3></a>
|
|
||||||
<p>Now that we've named the magic location, we want to write to it.</p>
|
|
||||||
<pre><pre class="playpen"><code class="language-rust">
|
|
||||||
# #![allow(unused_variables)]
|
|
||||||
#fn main() {
|
|
||||||
impl<T> VolAddress<T> {
|
|
||||||
pub fn read(self) -> T
|
|
||||||
where
|
|
||||||
T: Copy,
|
|
||||||
{
|
|
||||||
unsafe { (self.address.get() as *mut T).read_volatile() }
|
|
||||||
}
|
|
||||||
pub unsafe fn read_non_copy(self) -> T {
|
|
||||||
(self.address.get() as *mut T).read_volatile()
|
|
||||||
}
|
|
||||||
pub fn write(self, val: T) {
|
|
||||||
unsafe { (self.address.get() as *mut T).write_volatile(val) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#}</code></pre></pre>
|
|
||||||
<p>So if the type is <code>Copy</code> we can <code>read</code> it as much as we want. If, somehow, the
|
|
||||||
type isn't <code>Copy</code>, then it might be <code>Drop</code>, and that means if we read out a
|
|
||||||
value over and over we could cause the <code>drop</code> method to trigger UB. Since the
|
|
||||||
end user might really know what they're doing, we provide an unsafe backup
|
|
||||||
<code>read_non_copy</code>.</p>
|
|
||||||
<p>On the other hand, we can <code>write</code> to the location as much as we want. Even if
|
|
||||||
the type isn't <code>Copy</code>, <em>not running <code>Drop</code> is safe</em>, so a <code>write</code> is always
|
|
||||||
safe.</p>
|
|
||||||
<p>Now we can write to our magical location.</p>
|
|
||||||
<pre><pre class="playpen"><code class="language-rust">
|
|
||||||
# #![allow(unused_variables)]
|
|
||||||
#fn main() {
|
|
||||||
MAGIC_LOCATION.write(0x0403);
|
|
||||||
#}</code></pre></pre>
|
|
||||||
<a class="header" href="#voladdress-iteration" id="voladdress-iteration"><h3>VolAddress Iteration</h3></a>
|
|
||||||
<p>We've already seen that sometimes we want to have a base address of some sort
|
|
||||||
and then offset from that location to another. What if we wanted to iterate over
|
|
||||||
<em>all the locations</em>. That's not particularly hard.</p>
|
|
||||||
<pre><pre class="playpen"><code class="language-rust">
|
|
||||||
# #![allow(unused_variables)]
|
|
||||||
#fn main() {
|
|
||||||
impl<T> VolAddress<T> {
|
|
||||||
pub const unsafe fn iter_slots(self, slots: usize) -> VolAddressIter<T> {
|
|
||||||
VolAddressIter { vol_address: self, slots }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct VolAddressIter<T> {
|
|
||||||
vol_address: VolAddress<T>,
|
|
||||||
slots: usize,
|
|
||||||
}
|
|
||||||
impl<T> Clone for VolAddressIter<T> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
VolAddressIter {
|
|
||||||
vol_address: self.vol_address,
|
|
||||||
slots: self.slots,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T> PartialEq for VolAddressIter<T> {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.vol_address == other.vol_address && self.slots == other.slots
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T> Eq for VolAddressIter<T> {}
|
|
||||||
impl<T> Iterator for VolAddressIter<T> {
|
|
||||||
type Item = VolAddress<T>;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
if self.slots > 0 {
|
|
||||||
let out = self.vol_address;
|
|
||||||
unsafe {
|
|
||||||
self.slots -= 1;
|
|
||||||
self.vol_address = self.vol_address.offset(1);
|
|
||||||
}
|
|
||||||
Some(out)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T> FusedIterator for VolAddressIter<T> {}
|
|
||||||
#}</code></pre></pre>
|
|
||||||
<a class="header" href="#voladdressblock" id="voladdressblock"><h3>VolAddressBlock</h3></a>
|
|
||||||
<p>Obviously, having a base address and a length exist separately is error prone.
|
|
||||||
There's a good reason for slices to keep their pointer and their length
|
|
||||||
together. We want something like that, which we'll call a "block" because
|
|
||||||
"array" and "slice" are already things in Rust.</p>
|
|
||||||
<pre><pre class="playpen"><code class="language-rust">
|
|
||||||
# #![allow(unused_variables)]
|
|
||||||
#fn main() {
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct VolAddressBlock<T> {
|
|
||||||
vol_address: VolAddress<T>,
|
|
||||||
slots: usize,
|
|
||||||
}
|
|
||||||
impl<T> Clone for VolAddressBlock<T> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
VolAddressBlock {
|
|
||||||
vol_address: self.vol_address,
|
|
||||||
slots: self.slots,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T> PartialEq for VolAddressBlock<T> {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.vol_address == other.vol_address && self.slots == other.slots
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T> Eq for VolAddressBlock<T> {}
|
|
||||||
|
|
||||||
impl<T> VolAddressBlock<T> {
|
|
||||||
pub const unsafe fn new_unchecked(vol_address: VolAddress<T>, slots: usize) -> Self {
|
|
||||||
VolAddressBlock { vol_address, slots }
|
|
||||||
}
|
|
||||||
pub const fn iter(self) -> VolAddressIter<T> {
|
|
||||||
VolAddressIter {
|
|
||||||
vol_address: self.vol_address,
|
|
||||||
slots: self.slots,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub unsafe fn index_unchecked(self, slot: usize) -> VolAddress<T> {
|
|
||||||
self.vol_address.offset(slot as isize)
|
|
||||||
}
|
|
||||||
pub fn index(self, slot: usize) -> VolAddress<T> {
|
|
||||||
if slot < self.slots {
|
|
||||||
unsafe { self.vol_address.offset(slot as isize) }
|
|
||||||
} else {
|
|
||||||
panic!("Index Requested: {} >= Bound: {}", slot, self.slots)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn get(self, slot: usize) -> Option<VolAddress<T>> {
|
|
||||||
if slot < self.slots {
|
|
||||||
unsafe { Some(self.vol_address.offset(slot as isize)) }
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#}</code></pre></pre>
|
|
||||||
<p>Now we can have something like:</p>
|
|
||||||
<pre><pre class="playpen"><code class="language-rust">
|
|
||||||
# #![allow(unused_variables)]
|
|
||||||
#fn main() {
|
|
||||||
const OTHER_MAGIC: VolAddressBlock<u16> = unsafe {
|
|
||||||
VolAddressBlock::new_unchecked(
|
|
||||||
VolAddress::new_unchecked(0x600_0000),
|
|
||||||
240 * 160
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
OTHER_MAGIC.index(120 + 80 * 240).write_volatile(0x001F);
|
|
||||||
OTHER_MAGIC.index(136 + 80 * 240).write_volatile(0x03E0);
|
|
||||||
OTHER_MAGIC.index(120 + 96 * 240).write_volatile(0x7C00);
|
|
||||||
#}</code></pre></pre>
|
|
||||||
<a class="header" href="#docs" id="docs"><h3>Docs?</h3></a>
|
|
||||||
<p>If you wanna see these types and methods with a full docs write up you should
|
|
||||||
check the GBA crate's source.</p>
|
|
||||||
<a class="header" href="#volatile-asm" id="volatile-asm"><h2>Volatile ASM</h2></a>
|
|
||||||
<p>In addition to some memory locations being volatile, it's also possible for
|
|
||||||
inline assembly to be declared volatile. This is basically the same idea, "hey
|
|
||||||
just do what I'm telling you, don't get smart about it".</p>
|
|
||||||
<p>Normally when you have some <code>asm!</code> it's basically treated like a function,
|
|
||||||
there's inputs and outputs and the compiler will try to optimize it so that if
|
|
||||||
you don't actually use the outputs it won't bother with doing those
|
|
||||||
instructions. However, <code>asm!</code> is basically a pure black box, so the compiler
|
|
||||||
doesn't know what's happening inside at all, and it can't see if there's any
|
|
||||||
important side effects going on.</p>
|
|
||||||
<p>An example of an important side effect that doesn't have output values would be
|
|
||||||
putting the CPU into a low power state while we want for the next VBlank. This
|
|
||||||
lets us save quite a bit of battery power. It requires some setup to be done
|
|
||||||
safely (otherwise the GBA won't ever actually wake back up from the low power
|
|
||||||
state), but the <code>asm!</code> you use once you're ready is just a single instruction
|
|
||||||
with no return value. The compiler can't tell what's going on, so you just have
|
|
||||||
to say "do it anyway".</p>
|
|
||||||
<p>Note that if you use a linker script to include any ASM with your Rust program
|
|
||||||
(eg: the <code>crt0.s</code> file that we setup in the "Development Setup" section), all of
|
|
||||||
that ASM is "volatile" for these purposes. Volatile isn't actually a <em>hardware</em>
|
|
||||||
concept, it's just an LLVM concept, and the linker script runs after LLVM has
|
|
||||||
done its work.</p>
|
|
||||||
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
|
||||||
<!-- Mobile navigation buttons -->
|
|
||||||
|
|
||||||
<a rel="prev" href="../01-quirks/02-fixed_only.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
|
||||||
<i class="fa fa-angle-left"></i>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a rel="next" href="../01-quirks/04-newtype.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
|
||||||
<i class="fa fa-angle-right"></i>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
|
|
||||||
<div style="clear: both"></div>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
|
||||||
|
|
||||||
<a href="../01-quirks/02-fixed_only.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
|
||||||
<i class="fa fa-angle-left"></i>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a href="../01-quirks/04-newtype.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
|
||||||
<i class="fa fa-angle-right"></i>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script src="../elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
|
|
||||||
<script src="../mark.min.js" type="text/javascript" charset="utf-8"></script>
|
|
||||||
<script src="../searcher.js" type="text/javascript" charset="utf-8"></script>
|
|
||||||
|
|
||||||
|
|
||||||
<script src="../clipboard.min.js" type="text/javascript" charset="utf-8"></script>
|
|
||||||
<script src="../highlight.js" type="text/javascript" charset="utf-8"></script>
|
|
||||||
<script src="../book.js" type="text/javascript" charset="utf-8"></script>
|
|
||||||
|
|
||||||
<!-- Custom JS scripts -->
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
Loading…
Reference in a new issue