2018-11-12 08:56:11 +11:00
<!DOCTYPE HTML>
< html lang = "en" class = "sidebar-visible no-js" >
< head >
<!-- Book generated using mdBook -->
< meta charset = "UTF-8" >
2018-11-18 11:14:42 +11:00
< title > light_cycle - Rust GBA Guide< / title >
2018-11-12 08:56:11 +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" / >
< 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" >
2018-11-24 08:41:07 +11:00
< ol class = "chapter" > < li > < a href = "../introduction.html" > < strong aria-hidden = "true" > 1.< / strong > Introduction< / a > < / li > < li > < a href = "../ch00/index.html" > < strong aria-hidden = "true" > 2.< / strong > Ch 0: Development Setup< / a > < / li > < li > < a href = "../ch01/index.html" > < strong aria-hidden = "true" > 3.< / strong > Ch 1: Hello GBA< / a > < / li > < li > < ol class = "section" > < li > < a href = "../ch01/hello1.html" > < strong aria-hidden = "true" > 3.1.< / strong > hello1< / a > < / li > < li > < a href = "../ch01/volatile.html" > < strong aria-hidden = "true" > 3.2.< / strong > Volatile< / a > < / li > < li > < a href = "../ch01/io_registers.html" > < strong aria-hidden = "true" > 3.3.< / strong > IO Registers< / a > < / li > < li > < a href = "../ch01/the_display_control_register.html" > < strong aria-hidden = "true" > 3.4.< / strong > The Display Control Register< / a > < / li > < li > < a href = "../ch01/video_memory_intro.html" > < strong aria-hidden = "true" > 3.5.< / strong > Video Memory Intro< / a > < / li > < li > < a href = "../ch01/hello2.html" > < strong aria-hidden = "true" > 3.6.< / strong > hello2< / a > < / li > < / ol > < / li > < li > < a href = "../ch02/index.html" > < strong aria-hidden = "true" > 4.< / strong > Ch 2: User Input< / a > < / li > < li > < ol class = "section" > < li > < a href = "../ch02/the_key_input_register.html" > < strong aria-hidden = "true" > 4.1.< / strong > The Key Input Register< / a > < / li > < li > < a href = "../ch02/the_vcount_register.html" > < strong aria-hidden = "true" > 4.2.< / strong > The VCount Register< / a > < / li > < li > < a href = "../ch02/light_cycle.html" class = "active" > < strong aria-hidden = "true" > 4.3.< / strong > light_cycle< / a > < / li > < / ol > < / li > < li > < a href = "../ch03/index.html" > < strong aria-hidden = "true" > 5.< / strong > Ch 3: Memory and Objects< / a > < / li > < li > < ol class = "section" > < li > < a href = "../ch03/gba_memory_mapping.html" > < strong aria-hidden = "true" > 5.1.< / strong > GBA Memory Mapping< / a > < / li > < li > < a href = "../ch03/tile_data.html" > < strong aria-hidden = "true" > 5.2.< / strong > Tile Data< / a > < / li > < li > < a href = "../ch03/regular_backgrounds.html" > < strong aria-hidden = "true" > 5.3.< / strong > Regular Backgrounds< / a > < / li > < li > < a href = "../ch03/regular_objects.html" > < strong aria-hidden = "true" > 5.4.< / strong > Regular Objects< / a > < / li > < li > < a href = "../ch03/gba_rng.html" > < strong aria-hidden = "true" > 5.5.< / strong > GBA RNG< / a > < / li > < li > < a href = "../ch03/memory_game.html" > < strong aria-hidden = "true" > 5.6.< / strong > memory_game< / a > < / li > < / ol > < / li > < / ol >
2018-11-12 08:56:11 +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-12 08:56:11 +11:00
< 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 >
2018-11-13 10:29:17 +11:00
< a class = "header" href = "#light_cycle" id = "light_cycle" > < h1 > light_cycle< / h1 > < / a >
< p > Now let's make a game of " light_cycle" with our new knowledge.< / p >
< a class = "header" href = "#gameplay" id = "gameplay" > < h2 > Gameplay< / h2 > < / a >
< p > < code > light_cycle< / code > is pretty simple, and very obvious if you've ever seen Tron. The
player moves around the screen with a trail left behind them. They die if they
go off the screen or if they touch their own trail.< / p >
< a class = "header" href = "#operations" id = "operations" > < h2 > Operations< / h2 > < / a >
< p > We need some better drawing operations this time around.< / p >
< pre > < pre class = "playpen" > < code class = "language-rust" >
# #![allow(unused_variables)]
#fn main() {
pub unsafe fn mode3_clear_screen(color: u16) {
let color = color as u32;
let bulk_color = color < < 16 | color;
2018-11-19 16:19:13 +11:00
let mut ptr = VolatilePtr(VRAM as *mut u32);
2018-11-13 10:29:17 +11:00
for _ in 0..SCREEN_HEIGHT {
for _ in 0..(SCREEN_WIDTH / 2) {
2018-11-19 16:19:13 +11:00
ptr.write(bulk_color);
2018-11-13 10:29:17 +11:00
ptr = ptr.offset(1);
}
}
}
pub unsafe fn mode3_draw_pixel(col: isize, row: isize, color: u16) {
2018-11-19 16:19:13 +11:00
VolatilePtr(VRAM as *mut u16).offset(col + row * SCREEN_WIDTH).write(color);
2018-11-13 10:29:17 +11:00
}
pub unsafe fn mode3_read_pixel(col: isize, row: isize) -> u16 {
2018-11-19 16:19:13 +11:00
VolatilePtr(VRAM as *mut u16).offset(col + row * SCREEN_WIDTH).read()
2018-11-13 10:29:17 +11:00
}
#}< / code > < / pre > < / pre >
< p > The draw pixel and read pixel are both pretty obvious. What's new is the clear
screen operation. It changes the < code > u16< / code > color into a < code > u32< / code > and then packs the
value in twice. Then we write out < code > u32< / code > values the whole way through screen
memory. This means we have to do less write operations overall, and so the
screen clear is twice as fast.< / p >
< p > Now we just have to fill in the main function:< / p >
< pre > < pre class = "playpen" > < code class = "language-rust" > #[start]
fn main(_argc: isize, _argv: *const *const u8) -> isize {
unsafe {
2018-11-19 16:19:13 +11:00
DISPCNT.write(MODE3 | BG2);
2018-11-13 10:29:17 +11:00
}
let mut px = SCREEN_WIDTH / 2;
let mut py = SCREEN_HEIGHT / 2;
let mut color = rgb16(31, 0, 0);
loop {
// read the input for this frame
2018-11-19 16:19:13 +11:00
let this_frame_keys = key_input();
2018-11-13 10:29:17 +11:00
// adjust game state and wait for vblank
px += 2 * this_frame_keys.column_direction() as isize;
py += 2 * this_frame_keys.row_direction() as isize;
wait_until_vblank();
// draw the new game and wait until the next frame starts.
unsafe {
if px < 0 || py < 0 || px == SCREEN_WIDTH || py == SCREEN_HEIGHT {
// out of bounds, reset the screen and position.
mode3_clear_screen(0);
color = color.rotate_left(5);
px = SCREEN_WIDTH / 2;
py = SCREEN_HEIGHT / 2;
} else {
let color_here = mode3_read_pixel(px, py);
if color_here != 0 {
// crashed into our own line, reset the screen
mode3_clear_screen(0);
color = color.rotate_left(5);
} else {
// draw the new part of the line
mode3_draw_pixel(px, py, color);
mode3_draw_pixel(px, py + 1, color);
mode3_draw_pixel(px + 1, py, color);
mode3_draw_pixel(px + 1, py + 1, color);
}
}
}
wait_until_vdraw();
}
}
< / code > < / pre > < / pre >
< p > Oh that's a lot more than before!< / p >
< p > First we set Mode 3 and Background 2, we know about that.< / p >
< p > Then we're going to store the player's x and y, along with a color value for
their light cycle. Then we enter the core loop.< / p >
< p > We read the keys for input, and then do as much as we can without touching video
memory. Since we're using video memory as the place to store the player's light
2018-11-14 04:32:27 +11:00
trail, we can't do much, we just update their position and wait for VBlank to
2018-11-13 10:29:17 +11:00
start. The player will be a 2x2 square, so the arrows will move you 2 pixels per
frame.< / p >
2018-11-14 04:32:27 +11:00
< p > Once we're in VBlank we check to see what kind of drawing we're doing. If the
2018-11-13 10:29:17 +11:00
player has gone out of bounds, we clear the screen, rotate their color, and then
reset their position. Why rotate the color? Just because it's fun to have
different colors.< / p >
< p > Next, if the player is in bounds we read the video memory for their position. If
it's not black that means we've been here before and the player has crashed into
their own line. In this case, we reset the game without moving them to a new
location.< / p >
2018-11-15 13:53:21 +11:00
< p > Finally, if the player is in bounds and they haven't crashed, we write their
color into memory at this position.< / p >
2018-11-13 10:29:17 +11:00
< p > Regardless of how it worked out, we hold here until vdraw starts before going to
2018-11-15 13:53:21 +11:00
the next loop. That's all there is to it.< / p >
< a class = "header" href = "#the-gba-crate-doesnt-quite-work-like-this" id = "the-gba-crate-doesnt-quite-work-like-this" > < h2 > The gba crate doesn't quite work like this< / h2 > < / a >
< p > Once again, as with the < code > hello1< / code > and < code > hello2< / code > examples, the < code > gba< / code > crate covers
much of this same ground as our example here, but in slightly different ways.< / p >
< p > Better organization and abstractions are usually only realized once you've used
more of the whole thing you're trying to work with. If we want to have a crate
where the whole thing is well integrated with itself, then the examples would
also end up having to explain about things we haven't really touched on much
yet. It becomes a lot harder to teach.< / p >
< p > So, going forward, we will continue to teach concepts and build examples that
don't directly depend on the < code > gba< / code > crate. This allows the crate to freely grow
without all the past examples becoming a great inertia upon it.< / p >
2018-11-12 08:56:11 +11:00
< / main >
< nav class = "nav-wrapper" aria-label = "Page navigation" >
<!-- Mobile navigation buttons -->
2018-11-15 13:53:21 +11:00
< a rel = "prev" href = "../ch02/the_vcount_register.html" class = "mobile-nav-chapters previous" title = "Previous chapter" aria-label = "Previous chapter" aria-keyshortcuts = "Left" >
2018-11-12 08:56:11 +11:00
< i class = "fa fa-angle-left" > < / i >
< / a >
2018-11-15 13:53:21 +11:00
< a rel = "next" href = "../ch03/index.html" class = "mobile-nav-chapters next" title = "Next chapter" aria-label = "Next chapter" aria-keyshortcuts = "Right" >
< i class = "fa fa-angle-right" > < / i >
< / a >
2018-11-12 08:56:11 +11:00
< div style = "clear: both" > < / div >
< / nav >
< / div >
< / div >
< nav class = "nav-wide-wrapper" aria-label = "Page navigation" >
2018-11-15 13:53:21 +11:00
< a href = "../ch02/the_vcount_register.html" class = "nav-chapters previous" title = "Previous chapter" aria-label = "Previous chapter" aria-keyshortcuts = "Left" >
2018-11-12 08:56:11 +11:00
< i class = "fa fa-angle-left" > < / i >
< / a >
2018-11-15 13:53:21 +11:00
< a href = "../ch03/index.html" class = "nav-chapters next" title = "Next chapter" aria-label = "Next chapter" aria-keyshortcuts = "Right" >
< i class = "fa fa-angle-right" > < / i >
< / a >
2018-11-12 08:56:11 +11:00
< / 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 >