gba/docs/01-quirks/03-volatile_destination.html

522 lines
30 KiB
HTML
Raw Normal View History

<!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></ol></li><li><a href="../02-concepts/00-index.html"><strong aria-hidden="true">3.</strong> Concepts</a></li><li><ol class="section"><li><a href="../02-concepts/01-cpu.html"><strong aria-hidden="true">3.1.</strong> CPU</a></li><li><a href="../02-concepts/02-bios.html"><strong aria-hidden="true">3.2.</strong> BIOS</a></li><li><a href="../02-concepts/03-wram.html"><strong aria-hidden="true">3.3.</strong> Work RAM</a></li><li><a href="../02-concepts/04-io-registers.html"><strong aria-hidden="true">3.4.</strong> IO Registers</a></li><li><a href="../02-concepts/05-palram.html"><strong aria-hidden="true">3.5.</strong> Palette RAM</a></li><li><a href="../02-concepts/06-vram.html"><strong aria-hidden="true">3.6.</strong> Video RAM</a></li><li><a href="../02-concepts/07-oam.html"><strong aria-hidden="true">3.7.</strong> Object Attribute Memory</a></li><li><a href="../02-concepts/08-rom.html"><strong aria-hidden="true">3.8.</strong> Game Pak ROM / Flash ROM</a></li><li><a href="../02-concepts/09-sram.html"><strong aria-hidden="true">3.9.</strong> Save RAM</a></li></ol></li><li><a href="../03-video/00-index.html"><strong aria-hidden="true">4.</strong> Video</a></li><li><ol class="section"><li><a href="../03-video/01-rgb15.html"><strong aria-hidden="true">4.1.</strong> RBG15 Color</a></li><li><a href="../03-video/TODO.html"><strong aria-hidden="true">4.2.</strong> TODO</a></li></ol></li><li><a href="../04-non-video/00-index.html"><strong aria-hidden="true">5.</strong> Non-Video</a></li><li><ol class="section"><li><a href="../04-non-video/01-buttons.html"><strong aria-hidden="true">5.1.</strong> Buttons</a></li><li><a href="../04-non-video/02-timers.html"><strong aria-hidden="true">5.2.</strong> Timers</a></li><li><a href="../04-non-video/03-dma.html"><strong aria-hidden="true">5.3.</strong> Direct Memory Access</a></li><li><a href="../04-non-video/04-sound.html"><strong aria-hidden="true">5.4.</strong> Sound</a></li><li><a href="../04-non-video/05-interrupts.html"><strong aria-hidden="true">5.5.</strong> Interrupts</a></li><li><a href="../04-non-video/06-network.html"><strong aria-hidden="true">5.6.</strong> Network</a></li><li><a href="../04-non-video/07-game_pak.html"><strong aria-hidden="true">5.7.</strong> Game Pak</a></li></ol></li><li><a href="../05-examples/00-index.html"><strong aria-hidden="true">6.</strong> Examples</a></li><li><ol class="section"><li><a href="../05-examples/01-hello_magic.html"><strong aria-hidden="true">6.1.</strong> hello_magic</a></li><li><a href="../05-examples/02-hello_world.html"><strong aria-hidden="true">6.2.</strong> hello_world</a></li><li><a href="../05-examples/03-light_cycle.html"><strong aria-hidden="true">6.3.</strong> light_cycle</a></li><li><a href="../05-ex
</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: replace all this one &quot;the rant&quot; is finalized</p>
<p>There's a reasonable chance that you've never heard of <code>volatile</code> before, so
what's that? Well, it's a term that can be used in more than one context, but
basically it means &quot;get your grubby mitts off my stuff you over-eager compiler&quot;.</p>
<a class="header" href="#volatile-memory" id="volatile-memory"><h2>Volatile Memory</h2></a>
<p>The first, and most common, form of volatile thing is volatile memory. Volatile
memory can change without your program changing it, usually because it's not a
location in RAM, but instead some special location that represents an actual
hardware device, or part of a hardware device perhaps. The compiler doesn't know
what's going on in this situation, but when the program is actually run and the
CPU gets an instruction to read or write from that location, instead of just
accessing some place in RAM like with normal memory, it accesses whatever bit of
hardware and does <em>something</em>. The details of that something depend on the
hardware, but what's important is that we need to actually, definitely execute
that read or write instruction.</p>
<p>This is not how normal memory works. Normally when the compiler
sees us write values into variables and read values from variables, it's free to
optimize those expressions and eliminate some of the reads and writes if it can,
and generally try to save us time. Maybe it even knows some stuff about the data
dependencies in our expressions and so it does some of the reads or writes out
of order from what the source says, because the compiler knows that it won't
actually make a difference to the operation of the program. A good and helpful
friend, that compiler.</p>
<p>Volatile memory works almost the opposite way. With volatile memory we
need the compiler to <em>definitely</em> emit an instruction to do a read or write and
they need to happen <em>exactly</em> in the order that we say to do it. Each volatile
read or write might have any sort of side effect that the compiler
doesn't know about, and it shouldn't try to be clever about the optimization. Just do what we
say, please.</p>
<p>In Rust, we don't mark volatile things as being a separate type of thing,
instead we use normal raw pointers and then call the
<a href="https://doc.rust-lang.org/core/ptr/fn.read_volatile.html">read_volatile</a> and
<a href="https://doc.rust-lang.org/core/ptr/fn.write_volatile.html">write_volatile</a>
functions (also available as methods, if you like), which then delegate to the
LLVM
<a href="https://doc.rust-lang.org/core/intrinsics/fn.volatile_load.html">volatile_load</a>
and
<a href="https://doc.rust-lang.org/core/intrinsics/fn.volatile_store.html">volatile_store</a>
intrinsics. In C and C++ you can tag a pointer as being volatile and then any
normal read and write with it becomes the volatile version, but in Rust we have
to remember to use the correct alternate function instead.</p>
<p>I'm told by the experts that this makes for a cleaner and saner design from a
<em>language design</em> perspective, but it really kinda screws us when doing low
level code. References, both mutable and shared, aren't volatile, so they
compile into normal reads and writes. This means we can't do anything we'd
normally do in Rust that utilizes references of any kind. Volatile blocks of
memory can't use normal <code>.iter()</code> or <code>.iter_mut()</code> based iteration (which give
<code>&amp;T</code> or <code>&amp;mut T</code>), and they also can't use normal <code>Index</code> and <code>IndexMut</code> sugar
like <code>a + x[i]</code> or <code>x[i] = 7</code>.</p>
<p>Unlike with normal raw pointers, this pain point never goes away. There's no way
to abstract over the difference with Rust as it exists now, you'd need to
actually adjust the core language by adding an additional pointer type (<code>*vol T</code>) and possibly a reference type to go with it (<code>&amp;vol T</code>) to get the right
semantics. And then you'd need an <code>IndexVol</code> trait, and you'd need
<code>.iter_vol()</code>, and so on for every other little thing. It would be a lot of
work, and the Rust developers just aren't interested in doing all that for such
a limited portion of their user population. We'll just have to deal with not
having any syntax sugar.</p>
<a class="header" href="#volatileptr" id="volatileptr"><h3>VolatilePtr</h3></a>
<p>No syntax sugar doesn't mean we can't at least make things a little easier for
ourselves. Enter the <code>VolatilePtr&lt;T&gt;</code> type, which is a newtype over a <code>*mut T</code>.
One of those &quot;manual&quot; newtypes I mentioned where we can't use our nice macro.</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct VolatilePtr&lt;T&gt;(pub *mut T);
#}</code></pre></pre>
<p>Obviously we want to be able to read and write:</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
impl&lt;T&gt; VolatilePtr&lt;T&gt; {
/// Performs a `read_volatile`.
pub unsafe fn read(self) -&gt; T {
self.0.read_volatile()
}
/// Performs a `write_volatile`.
pub unsafe fn write(self, data: T) {
self.0.write_volatile(data);
}
#}</code></pre></pre>
<p>And we want a way to jump around when we do have volatile memory that's in
blocks. This is where we can get ourselves into some trouble if we're not
careful. We have to decide between
<a href="https://doc.rust-lang.org/std/primitive.pointer.html#method.offset">offset</a> and
<a href="https://doc.rust-lang.org/std/primitive.pointer.html#method.wrapping_offset">wrapping_offset</a>.
The difference is that <code>offset</code> optimizes better, but also it can be Undefined
Behavior if the result is not &quot;in bounds or one byte past the end of the same
allocated object&quot;. I asked <a href="https://github.com/ubsan">ubsan</a> (who is the expert
that you should always listen to on matters like this) what that means exactly
when memory mapped hardware is involved (since we never allocated anything), and
the answer was that you <em>can</em> use an <code>offset</code> in statically memory mapped
situations like this as long as you don't use it to jump to the address of
something that Rust itself allocated at some point. Cool, we all like being able
to use the one that optimizes better. Unfortunately, the downside to using
<code>offset</code> instead of <code>wrapping_offset</code> is that with <code>offset</code>, it's Undefined
Behavior <em>simply to calculate the out of bounds result</em> (with <code>wrapping_offset</code>
it's not Undefined Behavior until you <em>use</em> the out of bounds result). We'll
have to be quite careful when we're using <code>offset</code>.</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
/// Performs a normal `offset`.
pub unsafe fn offset(self, count: isize) -&gt; Self {
VolatilePtr(self.0.offset(count))
}
#}</code></pre></pre>
<p>Now, one thing of note is that doing the <code>offset</code> isn't <code>const</code>. The math for it
is something that's possible to do in a <code>const</code> way of course, but Rust
basically doesn't allow you to fiddle raw pointers much during <code>const</code> right
now. Maybe in the future that will improve.</p>
<p>If we did want to have a <code>const</code> function for finding the correct address within
a volatile block of memory we'd have to do all the math using <code>usize</code> values,
and then cast that value into being a pointer once we were done. It'd look
something like this:</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
const fn address_index&lt;T&gt;(address: usize, index: usize) -&gt; usize {
address + (index * std::mem::size_of::&lt;T&gt;())
}
#}</code></pre></pre>
<p>But, back to methods for <code>VolatilePtr</code>, well we sometimes want to be able to
cast a <code>VolatilePtr</code> between pointer types. Since we won't be able to do that
with <code>as</code>, we'll have to write a method for it:</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
/// Performs a cast into some new pointer type.
pub fn cast&lt;Z&gt;(self) -&gt; VolatilePtr&lt;Z&gt; {
VolatilePtr(self.0 as *mut Z)
}
#}</code></pre></pre>
<a class="header" href="#volatile-iterating" id="volatile-iterating"><h3>Volatile Iterating</h3></a>
<p>How about that <code>Iterator</code> stuff I said we'd be missing? We can actually make
<em>an</em> Iterator available, it's just not the normal &quot;iterate by shared reference
or unique reference&quot; Iterator. Instead, it's more like a &quot;throw out a series of
<code>VolatilePtr</code> values&quot; style Iterator. Other than that small difference it's
totally normal, and we'll be able to use map and skip and take and all those
neat methods.</p>
<p>So how do we make this thing we need? First we check out the <a href="https://doc.rust-lang.org/core/iter/index.html#implementing-iterator">Implementing
Iterator</a>
section in the core documentation. It says we need a struct for holding the
iterator state. Right-o, probably something like this:</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct VolatilePtrIter&lt;T&gt; {
vol_ptr: VolatilePtr&lt;T&gt;,
slots: usize,
}
#}</code></pre></pre>
<p>And then we just implement
<a href="https://doc.rust-lang.org/core/iter/trait.Iterator.html">core::iter::Iterator</a>
on that struct. Wow, that's quite the trait though! Don't worry, we only need to
implement two small things and then the rest of it comes free as a bunch of
default methods.</p>
<p>So, the code that we <em>want</em> to write looks like this:</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
impl&lt;T&gt; Iterator for VolatilePtrIter&lt;T&gt; {
type Item = VolatilePtr&lt;T&gt;;
fn next(&amp;mut self) -&gt; Option&lt;VolatilePtr&lt;T&gt;&gt; {
if self.slots &gt; 0 {
let out = Some(self.vol_ptr);
self.slots -= 1;
self.vol_ptr = unsafe { self.vol_ptr.offset(1) };
out
} else {
None
}
}
}
#}</code></pre></pre>
<p>Except we <em>can't</em> write that code. What? The problem is that we used
<code>derive(Clone, Copy</code> on <code>VolatilePtr</code>. Because of a quirk in how <code>derive</code> works,
this means <code>VolatilePtr&lt;T&gt;</code> will only be <code>Copy</code> if the <code>T</code> is <code>Copy</code>, <em>even
though the pointer itself is always <code>Copy</code> regardless of what it points to</em>.
Ugh, terrible. We've got three basic ways to handle this:</p>
<ul>
<li>Make the <code>Iterator</code> implementation be for <code>&lt;T:Clone&gt;</code>, and then hope that we
always have types that are <code>Clone</code>.</li>
<li>Hand implement every trait we want <code>VolatilePtr</code> (and <code>VolatilePtrIter</code>) to
have so that we can override the fact that <code>derive</code> is basically broken in
this case.</li>
<li>Make <code>VolatilePtr</code> store a <code>usize</code> value instead of a pointer, and then cast
it to <code>*mut T</code> when we actually need to read and write. This would require us
to also store a <code>PhantomData&lt;T&gt;</code> so that the type of the address is tracked
properly, which would make it a lot more verbose to construct a <code>VolatilePtr</code>
value.</li>
</ul>
<p>None of those options are particularly appealing. I guess we'll do the first one
because it's the least amount of up front trouble, and I don't <em>think</em> we'll
need to be iterating non-Clone values. All we do to pick that option is add the
bound to the very start of the <code>impl</code> block, where we introduce the <code>T</code>:</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
impl&lt;T: Clone&gt; Iterator for VolatilePtrIter&lt;T&gt; {
type Item = VolatilePtr&lt;T&gt;;
fn next(&amp;mut self) -&gt; Option&lt;VolatilePtr&lt;T&gt;&gt; {
if self.slots &gt; 0 {
let out = Some(self.vol_ptr.clone());
self.slots -= 1;
self.vol_ptr = unsafe { self.vol_ptr.clone().offset(1) };
out
} else {
None
}
}
}
#}</code></pre></pre>
<p>What's going on here? Okay so our iterator has a number of slots that it'll go
over, and then when it's out of slots it starts producing <code>None</code> forever. That's
actually pretty simple. We're also masking some unsafety too. In this case,
we'll rely on the person who made the <code>VolatilePtrIter</code> to have selected the
correct number of slots. This gives us a new method for <code>VolatilePtr</code>:</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
pub unsafe fn iter_slots(self, slots: usize) -&gt; VolatilePtrIter&lt;T&gt; {
VolatilePtrIter {
vol_ptr: self,
slots,
}
}
#}</code></pre></pre>
<p>With this design, making the <code>VolatilePtrIter</code> at the start is <code>unsafe</code> (we have
to trust the caller that the right number of slots exists), and then using it
after that is totally safe (if the right number of slots was given we'll never
screw up our end of it).</p>
<a class="header" href="#volatileptr-formatting" id="volatileptr-formatting"><h3>VolatilePtr Formatting</h3></a>
<p>Also, just as a little bonus that we probably won't use, we could enable our new
pointer type to be formatted as a pointer value.</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
impl&lt;T&gt; core::fmt::Pointer for VolatilePtr&lt;T&gt; {
/// Formats exactly like the inner `*mut T`.
fn fmt(&amp;self, f: &amp;mut core::fmt::Formatter) -&gt; core::fmt::Result {
write!(f, &quot;{:p}&quot;, self.0)
}
}
#}</code></pre></pre>
<p>Neat!</p>
<a class="header" href="#volatileptr-complete" id="volatileptr-complete"><h3>VolatilePtr Complete</h3></a>
<p>That was a lot of small code blocks, let's look at it all put together:</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct VolatilePtr&lt;T&gt;(pub *mut T);
impl&lt;T&gt; VolatilePtr&lt;T&gt; {
pub unsafe fn read(self) -&gt; T {
self.0.read_volatile()
}
pub unsafe fn write(self, data: T) {
self.0.write_volatile(data);
}
pub unsafe fn offset(self, count: isize) -&gt; Self {
VolatilePtr(self.0.offset(count))
}
pub fn cast&lt;Z&gt;(self) -&gt; VolatilePtr&lt;Z&gt; {
VolatilePtr(self.0 as *mut Z)
}
pub unsafe fn iter_slots(self, slots: usize) -&gt; VolatilePtrIter&lt;T&gt; {
VolatilePtrIter {
vol_ptr: self,
slots,
}
}
}
impl&lt;T&gt; core::fmt::Pointer for VolatilePtr&lt;T&gt; {
fn fmt(&amp;self, f: &amp;mut core::fmt::Formatter) -&gt; core::fmt::Result {
write!(f, &quot;{:p}&quot;, self.0)
}
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct VolatilePtrIter&lt;T&gt; {
vol_ptr: VolatilePtr&lt;T&gt;,
slots: usize,
}
impl&lt;T: Clone&gt; Iterator for VolatilePtrIter&lt;T&gt; {
type Item = VolatilePtr&lt;T&gt;;
fn next(&amp;mut self) -&gt; Option&lt;VolatilePtr&lt;T&gt;&gt; {
if self.slots &gt; 0 {
let out = Some(self.vol_ptr.clone());
self.slots -= 1;
self.vol_ptr = unsafe { self.vol_ptr.clone().offset(1) };
out
} else {
None
}
}
}
#}</code></pre></pre>
<a class="header" href="#volatile-asm" id="volatile-asm"><h2>Volatile ASM</h2></a>
<p>In addition to some memory locations being volatile, it's also possible for
inline assembly to be declared volatile. This is basically the same idea, &quot;hey
just do what I'm telling you, don't get smart about it&quot;.</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 &quot;do it anyway&quot;.</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>