gametank 0.17.0

Hardware abstraction layer for the GameTank console
Documentation

GameTank SDK

The Rust SDK for developing games on the GameTank console.

Quick Start

Every GameTank program starts with a main function that receives a Console:

use rom::sdk::{scr::Console, via::Via};
use rom::boot::wait;

#[unsafe(no_mangle)]
fn main(mut console: Console) {
    let via = unsafe { Via::new() };
    
    loop {
        unsafe { wait(); }  // Wait for vblank (60 Hz)
        
        // Flip buffers so we draw to the hidden one
        if let Some(fb) = console.dma.framebuffers(&mut console.sc) {
            fb.flip(&mut console.sc);
        }
        
        // Draw a red rectangle
        let mut blitter = console.dma.blitter(&mut console.sc).unwrap();
        blitter.draw_square(&mut console.sc, 10, 10, 32, 32, !0b010_11_100);
        blitter.wait_blit();
    }
}

The Game Loop

A typical frame looks like this:

loop {
    // 1. Wait for vblank (TV finished drawing previous frame)
    unsafe { wait(); }
    
    // 2. Flip framebuffers (swap which buffer is displayed vs drawn to)
    console.dma.framebuffers(&mut console.sc).unwrap().flip(&mut console.sc);
    
    // 3. Start drawing background (blitter runs in parallel!)
    let mut blitter = console.dma.blitter(&mut console.sc).unwrap();
    blitter.draw_sprite(&mut console.sc, 0, 0, 0, 0, 128, 128);
    
    // 4. Do CPU work WHILE blitter draws (this is free parallelism!)
    update_game_logic();
    read_input();
    
    // 5. Wait for background to finish, then draw sprites on top
    blitter.wait_blit();
    for sprite in &sprites {
        blitter.draw_sprite(&mut console.sc, ...);
        blitter.wait_blit();
    }
}

Drawing

All drawing goes through the BlitterGuard:

let mut blitter = console.dma.blitter(&mut console.sc).unwrap();

// Fill a rectangle with a solid color
blitter.draw_square(&mut console.sc, x, y, width, height, !color);

// Copy a sprite from sprite RAM to the screen
blitter.draw_sprite(&mut console.sc, src_x, src_y, dst_x, dst_y, width, height);

// IMPORTANT: Wait before starting another blit or accessing video memory
blitter.wait_blit();

Colors

Colors are 8-bit HSL: 0bHHH_SS_LLL (Hue, Saturation, Luminosity)

// Common colors
const BLACK: u8  = 0b000_00_000;
const WHITE: u8  = 0b000_00_111;
const RED: u8    = 0b010_11_100;
const GREEN: u8  = 0b111_11_100;
const BLUE: u8   = 0b101_11_100;
const YELLOW: u8 = 0b000_11_100;

// Hues: 0=Yellow, 1=Orange, 2=Red, 3=Magenta, 4=Violet, 5=Blue, 6=Cyan, 7=Green
// Saturation 0 = grayscale

// IMPORTANT: Invert colors when drawing!
blitter.draw_square(&mut console.sc, x, y, w, h, !RED);

Loading Sprites

Before you can draw sprites, load them into sprite RAM:

// Your sprite data (typically from an asset macro)
static SPRITES: &[u8] = include_bytes!("sprites.bin");

// Get sprite RAM access
let mut sprite_mem = console.dma.sprite_mem(&mut console.sc).unwrap();

// Copy sprite data
sprite_mem.bytes()[..SPRITES.len()].copy_from_slice(SPRITES);

Audio

The GameTank has a dedicated audio coprocessor. Initialize it with firmware:

use rom::sdk::audio::{FIRMWARE, voices, MidiNote, WAVETABLE};

// Load audio firmware (do this once at startup)
console.sc.set_audio(0);  // Disable while loading
console.audio.copy_from_slice(FIRMWARE);
console.sc.set_audio(0xFF);  // Enable at ~14kHz

// Play notes using the wavetable synth
let v = voices();
v[0].set_note(MidiNote::C4);
v[0].set_volume(63);
v[0].set_wavetable(WAVETABLE[0]);

ROM Banking

For large games, store assets in ROM banks and switch as needed:

// Place data in a specific bank
#[unsafe(link_section = ".rodata.bank10")]
static LEVEL_DATA: [u8; 8192] = [...];

// Switch to that bank before accessing
let via = unsafe { Via::new() };
via.change_rom_bank(10);
// Now LEVEL_DATA is accessible at its address

Hardware Overview

Feature Spec
CPU W65C02S @ ~3.58 MHz
Display 128×128 pixels, ~200 colors
Blitter ~60,000 pixels/frame (3.6× screen)
RAM 32KB (4 × 8KB banks)
Sprite RAM 512KB (8 pages × 256×256)
ROM 2MB (128 × 16KB banks)
Audio 6502 coprocessor, 8-bit DAC, ~14kHz