gba/docs/04-non-video/01-buttons.html

294 lines
18 KiB
HTML
Raw Normal View History

2018-11-08 15:20:40 +11:00
<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
2018-12-10 19:48:06 +11:00
<title>Buttons - Rust GBA Guide</title>
2018-11-08 15:20:40 +11:00
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff" />
2018-11-10 20:03:37 +11:00
<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">
2018-11-08 15:20:40 +11:00
<!-- Fonts -->
2018-11-10 20:03:37 +11:00
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
2018-11-08 15:20:40 +11:00
<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 -->
2018-11-10 20:03:37 +11:00
<link rel="stylesheet" href="../highlight.css">
<link rel="stylesheet" href="../tomorrow-night.css">
<link rel="stylesheet" href="../ayu-highlight.css">
2018-11-08 15:20:40 +11:00
<!-- Custom theme stylesheets -->
</head>
<body class="light">
<!-- Provide site root to javascript -->
2018-11-10 20:03:37 +11:00
<script type="text/javascript">var path_to_root = "../";</script>
2018-11-08 15:20:40 +11:00
<!-- 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"><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" class="active"><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>
2018-11-08 15:20:40 +11:00
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar" class="menu-bar">
<div id="menu-bar-sticky-container">
<div class="left-buttons">
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</button>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="light">Light <span class="default">(default)</span></button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
2018-11-18 11:14:42 +11:00
<h1 class="menu-title">Rust GBA Guide</h1>
2018-11-08 15:20:40 +11:00
<div class="right-buttons">
2018-11-10 20:03:37 +11:00
<a href="../print.html" title="Print this book" aria-label="Print this book">
2018-11-08 15:20:40 +11:00
<i id="print-button" class="fa fa-print"></i>
</a>
</div>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" name="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script type="text/javascript">
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
2018-12-10 19:48:06 +11:00
<a class="header" href="#buttons" id="buttons"><h1>Buttons</h1></a>
<p>It's all well and good to just show a picture, even to show an animation, but if
we want a game we have to let the user interact with something.</p>
<a class="header" href="#key-input-register" id="key-input-register"><h2>Key Input Register</h2></a>
<ul>
<li>KEYINPUT, <code>0x400_0130</code>, <code>u16</code>, read only</li>
</ul>
<p>This little <code>u16</code> stores the status of <em>all</em> the buttons on the GBA, all at
once. There's only 10 of them, and we have 16 bits to work with, so that sounds
easy. However, there's a bit of a catch. The register follows a &quot;low-active&quot;
convention, where pressing a button <em>clears</em> that bit until it's released.</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
const NO_BUTTONS_PRESSED: u16 = 0b0000_0011_1111_1111;
#}</code></pre></pre>
<p>The buttons are, going up in order from the 0th bit:</p>
<ul>
<li>A</li>
<li>B</li>
<li>Select</li>
<li>Start</li>
<li>Right</li>
<li>Left</li>
<li>Up</li>
<li>Down</li>
<li>R</li>
<li>L</li>
</ul>
<p>Bits above that are not used. However, since the left and right directions, as
well as the up and down directions, can never be pressed at the same time, the
<code>KEYINPUT</code> register should never read as zero. Of course, the register <em>might</em>
read as zero if someone is using an emulator that allows for such inputs, so I
wouldn't go so far as to make it be <code>NonZeroU16</code> or anything like that.</p>
<p>When programming, we usually are thinking of what buttons we want to have <em>be
pressed</em> instead of buttons we want to have <em>not be pressed</em>. This means that we
need an inversion to happen somewhere along the line. The easiest moment of
inversion is immediately as you read in from the register and wrap the value up
in a newtype.</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
pub fn read_key_input() -&gt; KeyInput {
KeyInput(KEYINPUT.read() ^ 0b0000_0011_1111_1111)
}
#}</code></pre></pre>
<p>Now the KeyInput you get can be checked for what buttons are pressed by checking
for a set bit like you'd do anywhere else.</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
impl KeyInput {
pub fn a_pressed(self) -&gt; bool {
(self.0 &amp; A_BIT) &gt; 0
}
}
#}</code></pre></pre>
<p>Note that the current <code>KEYINPUT</code> value changes in real time as the user presses
or releases the buttons. To account for this, it's best to read the value just
once per game frame and then use that single value as if it was the input across
the whole frame. If you've worked with polling input before that should sound
totally normal. If not, just remember to call <code>read_key_input</code> once per frame
and then use that <code>KeyInput</code> value across the whole frame.</p>
<a class="header" href="#detecting-new-presses" id="detecting-new-presses"><h3>Detecting New Presses</h3></a>
<p>The keypad only tells you what's <em>currently</em> pressed, but if you want to check
what's <em>newly</em> pressed it's not too much harder.</p>
<p>All that you do is store the last frame's keys and compare them to the current
keys with an <code>XOR</code>. In the <code>gba</code> crate it's called <code>KeyInput::difference</code>. Once
you've got the difference between last frame and this frame, you know what
changes happened.</p>
<ul>
<li>If something is in the difference and <em>not pressed</em> in the last frame, that
means it was newly pressed.</li>
<li>If something is in the difference and <em>pressed</em> in the last frame that means
it was newly released.</li>
<li>If something is not in the difference then there's no change between last
frame and this frame.</li>
</ul>
<a class="header" href="#key-interrupt-control" id="key-interrupt-control"><h2>Key Interrupt Control</h2></a>
<ul>
<li>KEYCNT, <code>0x400_0132</code>, <code>u16</code>, read/write</li>
</ul>
<p>This lets you control what keys will trigger a keypad interrupt. Of course, for
the actual interrupt to fire you also need to set the <code>IME</code> and <code>IE</code> registers
properly. See the <a href="05-interrupts.html">Interrupts</a> section for details there.</p>
<p>The main thing to know about this register is that the keys are in <em>the exact
same order</em> as the key input order. However, with this register they use a
high-active convention instead (eg: the bit is active when the button should be
pressed as part of the interrupt).</p>
<p>In addition to simply having the bits for the buttons, bit 14 is a flag for
enabling keypad interrupts (in addition to the flag in the <code>IE</code> register), and
bit 15 decides how having more than one button works. If bit 15 is disabled,
it's an OR combination (eg: &quot;press any key to continue&quot;). If bit 15 is enabled
it's an AND combination (eg: &quot;press A+B+Start+Select to reset&quot;).</p>
2018-11-08 15:20:40 +11:00
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
2018-12-10 19:48:06 +11:00
<a rel="prev" href="../04-non-video/00-index.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
2018-11-10 20:03:37 +11:00
<i class="fa fa-angle-left"></i>
</a>
2018-11-08 15:20:40 +11:00
2018-12-10 19:48:06 +11:00
<a rel="next" href="../04-non-video/02-timers.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
2018-11-10 20:03:37 +11:00
<i class="fa fa-angle-right"></i>
</a>
2018-11-08 15:20:40 +11:00
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
2018-12-10 19:48:06 +11:00
<a href="../04-non-video/00-index.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
2018-11-10 20:03:37 +11:00
<i class="fa fa-angle-left"></i>
</a>
2018-11-08 15:20:40 +11:00
2018-12-10 19:48:06 +11:00
<a href="../04-non-video/02-timers.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
2018-11-10 20:03:37 +11:00
<i class="fa fa-angle-right"></i>
</a>
2018-11-08 15:20:40 +11:00
</nav>
</div>
2018-11-10 20:03:37 +11:00
<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>
2018-11-08 15:20:40 +11:00
2018-11-10 20:03:37 +11:00
<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>
2018-11-08 15:20:40 +11:00
<!-- Custom JS scripts -->
</body>
</html>