mirror of
https://github.com/italicsjenga/gba.git
synced 2024-12-24 11:11:31 +11:00
294 lines
18 KiB
HTML
294 lines
18 KiB
HTML
<!DOCTYPE HTML>
|
|
<html lang="en" class="sidebar-visible no-js">
|
|
<head>
|
|
<!-- Book generated using mdBook -->
|
|
<meta charset="UTF-8">
|
|
<title>Buttons - 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"><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>
|
|
</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="#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 "low-active"
|
|
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() -> 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) -> bool {
|
|
(self.0 & A_BIT) > 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: "press any key to continue"). If bit 15 is enabled
|
|
it's an AND combination (eg: "press A+B+Start+Select to reset").</p>
|
|
|
|
</main>
|
|
|
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
|
<!-- Mobile navigation buttons -->
|
|
|
|
<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">
|
|
<i class="fa fa-angle-left"></i>
|
|
</a>
|
|
|
|
|
|
|
|
<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">
|
|
<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="../04-non-video/00-index.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
|
<i class="fa fa-angle-left"></i>
|
|
</a>
|
|
|
|
|
|
|
|
<a href="../04-non-video/02-timers.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>
|