All SDK methods are accessed through the Console struct passed to your update and render callbacks. Import the prelude at the top of game.rs:
use console32_sdk::prelude::*;
Display
Screen: 320×240 pixels, RGB565. Origin (0, 0) is top-left. Coordinates are i16. The framebuffer is DMA-flushed to the panel after render returns — never flush manually.
Clearing and background
c.display.clear(colour); // fill entire screen
Primitives
c.display.draw_pixel(x, y, colour);
c.display.draw_line(x0, y0, x1, y1, colour);
c.display.draw_rect(x, y, w, h, colour); // outline
c.display.fill_rect(x, y, w, h, colour); // filled
c.display.draw_circle(cx, cy, r, colour); // outline
c.display.fill_circle(cx, cy, r, colour); // filled
Text
c.display.draw_text(text: &str, x, y, colour);
// Returns the pixel width of the string in the current font
let w: u16 = c.display.text_width(text: &str);
// Switch to a custom font (see Font in the ABI types)
c.display.set_font(font: &'static Font);
// Restore the built-in default font
c.display.reset_font();
Camera and clipping
// Offset all draw calls by (cam_x, cam_y) — world-space scrolling
c.display.set_camera(cam_x: i16, cam_y: i16);
c.display.reset_camera();
// Restrict all drawing to a rectangle; pixels outside are discarded
c.display.set_clip_rect(x, y, w, h);
c.display.reset_clip_rect();
Sprites
// Blit a sprite at (x, y). Transparent colour is skipped.
c.display.blit(sprite: &Sprite, x, y);
// Blit with horizontal flip
c.display.blit_flipped(sprite: &Sprite, x, y);
// Blit a sub-rectangle of a sprite sheet
c.display.blit_frame(sheet: &SpriteSheet, frame: u16, x, y);
Colours
Colours are u16 RGB565. Named constants in the prelude:
| Constant | Value |
|---|---|
BLACK | 0x0000 |
WHITE | 0xFFFF |
RED | 0xF800 |
GREEN | 0x07E0 |
BLUE | 0x001F |
YELLOW | 0xFFE0 |
CYAN | 0x07FF |
MAGENTA | 0xF81F |
Build arbitrary colours with:
let orange = rgb(255, 140, 0); // rgb(r: u8, g: u8, b: u8) -> Colour
Input
Button state is sampled once per frame by the firmware before update is called.
Button constants
| Group | Constants |
|---|---|
| Face | buttons::A, buttons::B, buttons::X, buttons::Y |
| D-pad | buttons::UP, buttons::DOWN, buttons::LEFT, buttons::RIGHT |
| Shoulder | buttons::L, buttons::R |
Constants can be OR'd together to test multiple buttons at once.
Methods
// True every frame the button is physically held
c.input.is_held(mask: u32) -> bool
// True only on the first frame the button goes down (rising edge)
c.input.is_pressed(mask: u32) -> bool
// True only on the frame the button comes up (falling edge)
c.input.is_released(mask: u32) -> bool
// Number of consecutive frames the button has been held
c.input.hold_duration(mask: u32) -> u32
// Raw bitmask of all currently held buttons
c.input.held() -> u32
Example
use buttons::*;
if c.input.is_held(LEFT) { self.x -= 2; }
if c.input.is_held(RIGHT) { self.x += 2; }
if c.input.is_held(UP) { self.y -= 2; }
if c.input.is_held(DOWN) { self.y += 2; }
if c.input.is_pressed(A) { self.jump(); }
if c.input.is_pressed(L) { self.toggle_pause(); }
// Chord — only fires when both are held simultaneously
if c.input.is_held(L | R) { self.open_map(); }
Audio
Four independent channels. Each plays a mono AudioSample (signed 8-bit PCM, 22 050 Hz).
// Play a sample on a channel (0–3)
// volume: 0 (silent) – 255 (full)
c.audio.play_sfx(
sample: &'static AudioSample,
channel: u8,
volume: u8,
looping: bool,
);
// Stop a channel
c.audio.stop_sfx(channel: u8);
// Set channel volume without restarting playback
c.audio.set_channel_vol(channel: u8, volume: u8);
// Master volume (multiplied with channel volumes)
c.audio.set_master_vol(volume: u8);
c.audio.get_master_vol() -> u8;
Embedding audio
Use include_bytes! with the asset converter (cargo console32 convert) to turn WAV files into embeddable PCM data:
static JUMP_PCM: &[i8] = include_bytes_as_i8!("assets/jump.pcm");
static JUMP: AudioSample = AudioSample::from_pcm(JUMP_PCM);
Storage
Persistent key-value slots. Each slot is a fixed-size [u8; N] buffer.
// Returns true if a save exists at the given slot index
c.storage.save_exists(slot: u8) -> bool
// Read saved bytes into a buffer; returns number of bytes read
c.storage.save_read(slot: u8, buf: &mut [u8]) -> usize
// Write bytes to a slot
c.storage.save_write(slot: u8, data: &[u8])
// Erase a slot
c.storage.save_erase(slot: u8)
System
// Frames elapsed since game_init was called (wraps at u32::MAX)
c.system.frame_count() -> u32
// Microseconds elapsed since game_init (wraps at u64::MAX)
c.system.time_us() -> u64
// Seeded pseudo-random number in 0..=u32::MAX
c.system.random() -> u32
// Emit a debug message visible in `cargo console32 monitor`
c.system.log(msg: &str)
// Request the firmware return to the launcher after this frame
c.system.quit()
Maths
All trig uses u16 full-circle angles where 0 = 0°, 16384 = 90°, 32768 = 180°, 65535 ≈ 360°. Results are signed 1.15 fixed-point — shift right by 15 to get a ratio.
c.math.sin(angle: u16) -> i16 // 1.15 fixed-point
c.math.cos(angle: u16) -> i16 // 1.15 fixed-point
c.math.atan2(y: i16, x: i16) -> u16 // full-circle angle
c.math.sqrt(x: u32) -> u32
c.math.distance(x0: i16, y0: i16, x1: i16, y1: i16) -> u32
c.math.lerp(a: i32, b: i32, t_256: u8) -> i32 // t in 0–255 (256 = 1.0)
c.math.clamp(v: i32, min: i32, max: i32) -> i32
Fixed-point types
Fixed is a 16.16 fixed-point scalar (i32 × 65536). Vec2i is a pair of Fixed values.
let speed = Fixed::from(3); // integer → Fixed
let half = Fixed::from_raw(32768); // raw 16.16 value
let a = Fixed::from(10);
let b = Fixed::from(3);
let c = a * b; // Fixed × Fixed → Fixed
let i = a.to_i32(); // Fixed → i32 (truncated)
// Vec2i
let pos = Vec2i::new(Fixed::from(160), Fixed::from(120));
let vel = Vec2i::new(Fixed::from(2), Fixed::from(-1));
let new_pos = pos + vel;
let dist_sq = pos.length_sq_px(); // distance² in pixel² (no sqrt)
Pool
Pool<T, N> is a fixed-capacity object collection with no heap allocation.
let mut enemies: Pool<Enemy, 32> = Pool::new();
// Spawn a new object (returns None if the pool is full)
enemies.spawn_with(Enemy { x: 100, y: 50, hp: 3 });
// Iterate
for e in enemies.iter() { /* ... */ }
for e in enemies.iter_mut() { e.x += e.vx; }
// Remove items matching a predicate
enemies.retain(|e| e.hp > 0);
// Current count and capacity
let count = enemies.len(); // items alive
let cap = enemies.capacity(); // always N