mirror of
https://github.com/italicsjenga/gba.git
synced 2024-12-24 11:11:31 +11:00
522 lines
27 KiB
HTML
522 lines
27 KiB
HTML
<!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>
|