mirror of
https://github.com/italicsjenga/gba.git
synced 2024-12-25 19:51:30 +11:00
411 lines
25 KiB
HTML
411 lines
25 KiB
HTML
<!DOCTYPE HTML>
|
|
<html lang="en" class="sidebar-visible no-js">
|
|
<head>
|
|
<!-- Book generated using mdBook -->
|
|
<meta charset="UTF-8">
|
|
<title>BIOS - Rust GBA Guide</title>
|
|
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
|
<meta name="description" content="">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<meta name="theme-color" content="#ffffff" />
|
|
|
|
<link rel="shortcut icon" href="../favicon.png">
|
|
<link rel="stylesheet" href="../css/variables.css">
|
|
<link rel="stylesheet" href="../css/general.css">
|
|
<link rel="stylesheet" href="../css/chrome.css">
|
|
<link rel="stylesheet" href="../css/print.css" media="print">
|
|
|
|
<!-- Fonts -->
|
|
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
|
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800" rel="stylesheet" type="text/css">
|
|
<link href="https://fonts.googleapis.com/css?family=Source+Code+Pro:500" rel="stylesheet" type="text/css">
|
|
|
|
<!-- Highlight.js Stylesheets -->
|
|
<link rel="stylesheet" href="../highlight.css">
|
|
<link rel="stylesheet" href="../tomorrow-night.css">
|
|
<link rel="stylesheet" href="../ayu-highlight.css">
|
|
|
|
<!-- Custom theme stylesheets -->
|
|
|
|
|
|
|
|
</head>
|
|
<body class="light">
|
|
<!-- Provide site root to javascript -->
|
|
<script type="text/javascript">var path_to_root = "../";</script>
|
|
|
|
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
|
<script type="text/javascript">
|
|
try {
|
|
var theme = localStorage.getItem('mdbook-theme');
|
|
var sidebar = localStorage.getItem('mdbook-sidebar');
|
|
|
|
if (theme.startsWith('"') && theme.endsWith('"')) {
|
|
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
|
}
|
|
|
|
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
|
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
|
}
|
|
} catch (e) { }
|
|
</script>
|
|
|
|
<!-- Set the theme before any content is loaded, prevents flash -->
|
|
<script type="text/javascript">
|
|
var theme;
|
|
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
|
if (theme === null || theme === undefined) { theme = 'light'; }
|
|
document.body.className = theme;
|
|
document.querySelector('html').className = theme + ' js';
|
|
</script>
|
|
|
|
<!-- Hide / unhide sidebar before it is displayed -->
|
|
<script type="text/javascript">
|
|
var html = document.querySelector('html');
|
|
var sidebar = 'hidden';
|
|
if (document.body.clientWidth >= 1080) {
|
|
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
|
sidebar = sidebar || 'visible';
|
|
}
|
|
html.classList.remove('sidebar-visible');
|
|
html.classList.add("sidebar-" + sidebar);
|
|
</script>
|
|
|
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
|
<ol class="chapter"><li><a href="../00-introduction/00-index.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><ol class="section"><li><a href="../00-introduction/01-requirements.html"><strong aria-hidden="true">1.1.</strong> Reader Requirements</a></li><li><a href="../00-introduction/02-goals_and_style.html"><strong aria-hidden="true">1.2.</strong> Book Goals and Style</a></li><li><a href="../00-introduction/03-development-setup.html"><strong aria-hidden="true">1.3.</strong> Development Setup</a></li><li><a href="../00-introduction/04-hello-magic.html"><strong aria-hidden="true">1.4.</strong> Hello, Magic</a></li><li><a href="../00-introduction/05-help_and_resources.html"><strong aria-hidden="true">1.5.</strong> Help and Resources</a></li></ol></li><li><a href="../01-quirks/00-index.html"><strong aria-hidden="true">2.</strong> Quirks</a></li><li><ol class="section"><li><a href="../01-quirks/01-no_std.html"><strong aria-hidden="true">2.1.</strong> No Std</a></li><li><a href="../01-quirks/02-fixed_only.html"><strong aria-hidden="true">2.2.</strong> Fixed Only</a></li><li><a href="../01-quirks/03-volatile_destination.html"><strong aria-hidden="true">2.3.</strong> Volatile Destination</a></li><li><a href="../01-quirks/04-newtype.html"><strong aria-hidden="true">2.4.</strong> Newtype</a></li><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" class="active"><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><li><ol class="section"><li><a href="../05-examples/01-hello_magic.html"><strong aria-hidden="true">6.1.</strong> hello_magic</a></li><li><a href="../05-examples/02-hello_world.html"><strong aria-hidden="true">6.2.</strong> hello_world</a></li><li><a href="../05-examples/03-light_cycle.html"><strong aria-hidden="true">6.3.</strong> light_cycle</a></li><li><a href="../05-examples/04-bg_demo.html"><strong aria-hidden="true">6.4.</strong> bg_demo</a></li></ol></li></ol>
|
|
</nav>
|
|
|
|
<div id="page-wrapper" class="page-wrapper">
|
|
|
|
<div class="page">
|
|
|
|
<div id="menu-bar" class="menu-bar">
|
|
<div id="menu-bar-sticky-container">
|
|
<div class="left-buttons">
|
|
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
|
<i class="fa fa-bars"></i>
|
|
</button>
|
|
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
|
<i class="fa fa-paint-brush"></i>
|
|
</button>
|
|
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
|
<li role="none"><button role="menuitem" class="theme" id="light">Light <span class="default">(default)</span></button></li>
|
|
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
|
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
|
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
|
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
|
</ul>
|
|
|
|
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
|
<i class="fa fa-search"></i>
|
|
</button>
|
|
|
|
</div>
|
|
|
|
<h1 class="menu-title">Rust GBA Guide</h1>
|
|
|
|
<div class="right-buttons">
|
|
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
|
<i id="print-button" class="fa fa-print"></i>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div id="search-wrapper" class="hidden">
|
|
<form id="searchbar-outer" class="searchbar-outer">
|
|
<input type="search" name="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
|
</form>
|
|
<div id="searchresults-outer" class="searchresults-outer hidden">
|
|
<div id="searchresults-header" class="searchresults-header"></div>
|
|
<ul id="searchresults">
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
|
<script type="text/javascript">
|
|
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
|
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
|
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
|
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
|
});
|
|
</script>
|
|
|
|
<div id="content" class="content">
|
|
<main>
|
|
<a class="header" href="#bios" id="bios"><h1>BIOS</h1></a>
|
|
<ul>
|
|
<li><strong>Address Span:</strong> <code>0x0</code> to <code>0x3FFF</code> (16k)</li>
|
|
</ul>
|
|
<p>The <a href="https://en.wikipedia.org/wiki/BIOS">BIOS</a> of the GBA is a small read-only
|
|
portion of memory at the very base of the address space. However, it is also
|
|
hardware protected against reading, so if you try to read from BIOS memory when
|
|
the program counter isn't pointed into the BIOS (eg: any time code <em>you</em> write
|
|
is executing) then you get <a href="https://problemkaputt.de/gbatek.htm#gbaunpredictablethings">basically garbage
|
|
data</a> back.</p>
|
|
<p>So we're not going to spend time here talking about what bits to read or write
|
|
within BIOS memory like we do with the other sections. Instead we're going to
|
|
spend time talking about <a href="https://doc.rust-lang.org/unstable-book/language-features/asm.html">inline
|
|
assembly</a>
|
|
(<a href="https://github.com/rust-lang/rust/issues/29722">tracking issue</a>) and then use
|
|
it to call the <a href="https://problemkaputt.de/gbatek.htm#biosfunctions">GBA BIOS
|
|
Functions</a>.</p>
|
|
<p>Note that BIOS calls have <em>more overhead than normal function calls</em>, so don't
|
|
go using them all over the place if you don't have to. They're also usually
|
|
written more to be compact in terms of code than for raw speed, so you actually
|
|
can out speed them in some cases. Between the increased overhead and not being
|
|
as speed optimized, you can sometimes do a faster job without calling the BIOS
|
|
at all. (TODO: investigate more about what parts of the BIOS we could
|
|
potentially offer faster alternatives for.)</p>
|
|
<p>I'd like to take a moment to thank <a href="https://github.com/mbr">Marc Brinkmann</a>
|
|
(with contributions from <a href="https://github.com/oli-obk">Oliver Schneider</a> and
|
|
<a href="https://github.com/phil-opp">Philipp Oppermann</a>) for writing <a href="http://embed.rs/articles/2016/arm-inline-assembly-rust/">this blog
|
|
post</a>. It's at least
|
|
ten times the tutorial quality as the <code>asm</code> entry in the Unstable Book has. In
|
|
fairness to the Unstable Book, the actual spec of how inline ASM works in rust
|
|
is "basically what clang does", and that's specified as "basically what GCC
|
|
does", and that's basically/shockingly not specified much at all despite GCC
|
|
being like 30 years old.</p>
|
|
<p>So let's be slow and pedantic about this process.</p>
|
|
<a class="header" href="#inline-asm" id="inline-asm"><h2>Inline ASM</h2></a>
|
|
<p><strong>Fair Warning:</strong> Inline asm is one of the least stable parts of Rust overall,
|
|
and if you write bad things you can trigger internal compiler errors and panics
|
|
and crashes and make LLVM choke and die without explanation. If you write some
|
|
inline asm and then suddenly your program suddenly stops compiling without
|
|
explanation, try commenting out that whole inline asm use and see if it's
|
|
causing the problem. Double check that you've written every single part of the
|
|
asm call absolutely correctly, etc, etc.</p>
|
|
<p><strong>Bonus Warning:</strong> The general information that follows regarding the asm macro
|
|
is consistent from system to system, but specific information about register
|
|
names, register quantities, asm instruction argument ordering, and so on is
|
|
specific to ARM on the GBA. If you're programming for any other device you'll
|
|
need to carefully investigate that before you begin.</p>
|
|
<p>Now then, with those out of the way, the inline asm docs describe an asm call as
|
|
looking like this:</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
asm!(assembly template
|
|
: output operands
|
|
: input operands
|
|
: clobbers
|
|
: options
|
|
);
|
|
#}</code></pre></pre>
|
|
<p>And once you stick a lot of stuff in there it can <em>absolutely</em> be hard to
|
|
remember the ordering of the elements. So we'll start with a code block that
|
|
has some comments thrown in on each line:</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
asm!(/* ASM */ TODO
|
|
:/* OUT */ TODO
|
|
:/* INP */ TODO
|
|
:/* CLO */ TODO
|
|
:/* OPT */
|
|
);
|
|
#}</code></pre></pre>
|
|
<p>Now we have to decide what we're gonna write. Obviously we're going to do some
|
|
instructions, but those instructions use registers, and how are we gonna talk
|
|
about them? We've got two choices.</p>
|
|
<ol>
|
|
<li>
|
|
<p>We can pick each and every register used by specifying exact register names.
|
|
In THUMB mode we have 8 registers available, named <code>r0</code> through <code>r7</code>. If you
|
|
switch into 32-bit mode there's additional registers that are also available.</p>
|
|
</li>
|
|
<li>
|
|
<p>We can specify slots for registers we need and let LLVM decide. In this style
|
|
you name your slots <code>$0</code>, <code>$1</code> and so on. Slot numbers are assigned first to
|
|
all specified outputs, then to all specified inputs, in the order that you
|
|
list them.</p>
|
|
</li>
|
|
</ol>
|
|
<p>In the case of the GBA BIOS, each BIOS function has pre-designated input and
|
|
output registers, so we will use the first style. If you use inline ASM in other
|
|
parts of your code you're free to use the second style.</p>
|
|
<a class="header" href="#assembly" id="assembly"><h3>Assembly</h3></a>
|
|
<p>This is just one big string literal. You write out one instruction per line, and
|
|
excess whitespace is ignored. You can also do comments within your assembly
|
|
using <code>;</code> to start a comment that goes until the end of the line.</p>
|
|
<p>Assembly convention doesn't consider it unreasonable to comment potentially as
|
|
much as <em>every single line</em> of asm that you write when you're getting used to
|
|
things. Or even if you are used to things. This is cryptic stuff, there's a
|
|
reason we avoid writing in it as much as possible.</p>
|
|
<p>Remember that our Rust code is in 16-bit mode. You <em>can</em> switch to 32-bit mode
|
|
within your asm as long as you switch back by the time the block ends. Otherwise
|
|
you'll have a bad time.</p>
|
|
<a class="header" href="#outputs" id="outputs"><h3>Outputs</h3></a>
|
|
<p>A comma separated list. Each entry looks like</p>
|
|
<ul>
|
|
<li><code>"constraint" (binding)</code></li>
|
|
</ul>
|
|
<p>An output constraint starts with a symbol:</p>
|
|
<ul>
|
|
<li><code>=</code> for write only</li>
|
|
<li><code>+</code> for reads and writes</li>
|
|
<li><code>&</code> for for "early clobber", meaning that you'll write to this at some point
|
|
before all input values have been read. It prevents this register from being
|
|
assigned to an input register.</li>
|
|
</ul>
|
|
<p>Followed by <em>either</em> the letter <code>r</code> (if you want LLVM to pick the register to
|
|
use) or curly braces around a specific register (if you want to pick).</p>
|
|
<ul>
|
|
<li>The binding can be any single 32-bit or smaller value.</li>
|
|
<li>If your binding has bit pattern requirements ("must be non-zero", etc) you are
|
|
responsible for upholding that.</li>
|
|
<li>If your binding type will try to <code>Drop</code> later then you are responsible for it
|
|
being in a fit state to do that.</li>
|
|
<li>The binding must be either a mutable binding or a binding that was
|
|
pre-declared but not yet assigned.</li>
|
|
</ul>
|
|
<p>Anything else is UB.</p>
|
|
<a class="header" href="#inputs" id="inputs"><h3>Inputs</h3></a>
|
|
<p>This is a similar comma separated list.</p>
|
|
<ul>
|
|
<li><code>"constraint" (binding)</code></li>
|
|
</ul>
|
|
<p>An input constraint doesn't have the symbol prefix, you just pick either <code>r</code> or
|
|
a named register with curly braces around it.</p>
|
|
<ul>
|
|
<li>An input binding must be a single 32-bit or smaller value.</li>
|
|
<li>An input binding <em>should</em> be a type that is <code>Copy</code> but this is not an absolute
|
|
requirement. Having the input be read is semantically similar to using
|
|
<code>core::ptr::read(&binding)</code> and forgetting the value when you're done.</li>
|
|
</ul>
|
|
<a class="header" href="#clobbers" id="clobbers"><h3>Clobbers</h3></a>
|
|
<p>Sometimes your asm will touch registers other than the ones declared for input
|
|
and output.</p>
|
|
<p>Clobbers are declared as a comma separated list of string literals naming
|
|
specific registers. You don't use curly braces with clobbers.</p>
|
|
<p>LLVM <em>needs</em> to know this information. It can move things around to keep your
|
|
data safe, but only if you tell it what's about to happen.</p>
|
|
<p>Failure to define all of your clobbers can cause UB.</p>
|
|
<a class="header" href="#options" id="options"><h3>Options</h3></a>
|
|
<p>There's only one option we'd care to specify. That option is "volatile".</p>
|
|
<p>Just like with a function call, LLVM will skip a block of asm if it doesn't see
|
|
that any outputs from the asm were used later on. Nearly every single BIOS call
|
|
(other than the math operations) will need to be marked as "volatile".</p>
|
|
<a class="header" href="#bios-asm" id="bios-asm"><h3>BIOS ASM</h3></a>
|
|
<ul>
|
|
<li>Inputs are always <code>r0</code>, <code>r1</code>, <code>r2</code>, and/or <code>r3</code>, depending on function.</li>
|
|
<li>Outputs are always zero or more of <code>r0</code>, <code>r1</code>, and <code>r3</code>.</li>
|
|
<li>Any of the output registers that aren't actually used should be marked as
|
|
clobbered.</li>
|
|
<li>All other registers are unaffected.</li>
|
|
</ul>
|
|
<p>All of the GBA BIOS calls are performed using the
|
|
<a href="http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0068b/BABFCEEG.html">swi</a>
|
|
instruction, combined with a value depending on what BIOS function you're trying
|
|
to invoke. If you're in 16-bit code you use the value directly, and if you're in
|
|
32-bit mode you shift the value up by 16 bits first.</p>
|
|
<a class="header" href="#example-bios-function-division" id="example-bios-function-division"><h3>Example BIOS Function: Division</h3></a>
|
|
<p>For our example we'll use the division function, because GBATEK gives very clear
|
|
instructions on how each register is used with that one:</p>
|
|
<pre><code class="language-txt">Signed Division, r0/r1.
|
|
r0 signed 32bit Number
|
|
r1 signed 32bit Denom
|
|
Return:
|
|
r0 Number DIV Denom ;signed
|
|
r1 Number MOD Denom ;signed
|
|
r3 ABS (Number DIV Denom) ;unsigned
|
|
For example, incoming -1234, 10 should return -123, -4, +123.
|
|
The function usually gets caught in an endless loop upon division by zero.
|
|
</code></pre>
|
|
<p>The math folks tell me that the <code>r1</code> value should be properly called the
|
|
"remainder" not the "modulus". We'll go with that for our function, doesn't hurt
|
|
to use the correct names. Our Rust function has an assert against dividing by
|
|
<code>0</code>, then we name some bindings <em>without</em> giving them a value, we make the asm
|
|
call, and then return what we got.</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
pub fn div_rem(numerator: i32, denominator: i32) -> (i32, i32) {
|
|
assert!(denominator != 0);
|
|
let div_out: i32;
|
|
let rem_out: i32;
|
|
unsafe {
|
|
asm!(/* ASM */ "swi 0x06"
|
|
:/* OUT */ "={r0}"(div_out), "={r1}"(rem_out)
|
|
:/* INP */ "{r0}"(numerator), "{r1}"(denominator)
|
|
:/* CLO */ "r3"
|
|
:/* OPT */
|
|
);
|
|
}
|
|
(div_out, rem_out)
|
|
}
|
|
#}</code></pre></pre>
|
|
<p>I <em>hope</em> this all makes sense by now.</p>
|
|
<a class="header" href="#specific-bios-functions" id="specific-bios-functions"><h2>Specific BIOS Functions</h2></a>
|
|
<p>For a full list of all the specific BIOS functions and their use you should
|
|
check the <code>gba::bios</code> module within the <code>gba</code> crate. There's just so many of
|
|
them that enumerating them all here wouldn't serve much purpose.</p>
|
|
<p>Which is not to say that we'll never cover any BIOS functions in this book!
|
|
Instead, we'll simply mention them when whenever they're relevent to the task at
|
|
hand (such as controlling sound or waiting for vblank).</p>
|
|
<p>//TODO: list/name all BIOS functions as well as what they relate to elsewhere.</p>
|
|
|
|
</main>
|
|
|
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
|
<!-- Mobile navigation buttons -->
|
|
|
|
<a rel="prev" href="../02-concepts/01-cpu.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="../02-concepts/03-wram.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="../02-concepts/01-cpu.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="../02-concepts/03-wram.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>
|