gametank/lib.rs
1#![no_std]
2
3//! # GameTank SDK
4//!
5//! The Rust SDK for developing games on the [GameTank](https://gametank.zone/) console.
6//!
7//! ## Quick Start
8//!
9//! Every GameTank program starts with a `main` function that receives a [`Console`](scr::Console):
10//!
11//! ```ignore
12//! use rom::sdk::{scr::Console, via::Via};
13//! use rom::boot::wait;
14//!
15//! #[unsafe(no_mangle)]
16//! fn main(mut console: Console) {
17//! let via = unsafe { Via::new() };
18//!
19//! loop {
20//! unsafe { wait(); } // Wait for vblank (60 Hz)
21//!
22//! // Flip buffers so we draw to the hidden one
23//! if let Some(fb) = console.dma.framebuffers(&mut console.sc) {
24//! fb.flip(&mut console.sc);
25//! }
26//!
27//! // Draw a red rectangle
28//! let mut blitter = console.dma.blitter(&mut console.sc).unwrap();
29//! blitter.draw_square(&mut console.sc, 10, 10, 32, 32, !0b010_11_100);
30//! blitter.wait_blit();
31//! }
32//! }
33//! ```
34//!
35//! ## The Game Loop
36//!
37//! A typical frame looks like this:
38//!
39//! ```ignore
40//! loop {
41//! // 1. Wait for vblank (TV finished drawing previous frame)
42//! unsafe { wait(); }
43//!
44//! // 2. Flip framebuffers (swap which buffer is displayed vs drawn to)
45//! console.dma.framebuffers(&mut console.sc).unwrap().flip(&mut console.sc);
46//!
47//! // 3. Start drawing background (blitter runs in parallel!)
48//! let mut blitter = console.dma.blitter(&mut console.sc).unwrap();
49//! blitter.draw_sprite(&mut console.sc, 0, 0, 0, 0, 128, 128);
50//!
51//! // 4. Do CPU work WHILE blitter draws (this is free parallelism!)
52//! update_game_logic();
53//! read_input();
54//!
55//! // 5. Wait for background to finish, then draw sprites on top
56//! blitter.wait_blit();
57//! for sprite in &sprites {
58//! blitter.draw_sprite(&mut console.sc, ...);
59//! blitter.wait_blit();
60//! }
61//! }
62//! ```
63//!
64//! ## Drawing
65//!
66//! All drawing goes through the [`BlitterGuard`](video_dma::blitter::BlitterGuard):
67//!
68//! ```ignore
69//! let mut blitter = console.dma.blitter(&mut console.sc).unwrap();
70//!
71//! // Fill a rectangle with a solid color
72//! blitter.draw_square(&mut console.sc, x, y, width, height, !color);
73//!
74//! // Copy a sprite from sprite RAM to the screen
75//! blitter.draw_sprite(&mut console.sc, src_x, src_y, dst_x, dst_y, width, height);
76//!
77//! // IMPORTANT: Wait before starting another blit or accessing video memory
78//! blitter.wait_blit();
79//! ```
80//!
81//! ## Colors
82//!
83//! Colors are 8-bit HSL: `0bHHH_SS_LLL` (Hue, Saturation, Luminosity)
84//!
85//! ```ignore
86//! // Common colors
87//! const BLACK: u8 = 0b000_00_000;
88//! const WHITE: u8 = 0b000_00_111;
89//! const RED: u8 = 0b010_11_100;
90//! const GREEN: u8 = 0b111_11_100;
91//! const BLUE: u8 = 0b101_11_100;
92//! const YELLOW: u8 = 0b000_11_100;
93//!
94//! // Hues: 0=Yellow, 1=Orange, 2=Red, 3=Magenta, 4=Violet, 5=Blue, 6=Cyan, 7=Green
95//! // Saturation 0 = grayscale
96//!
97//! // IMPORTANT: Invert colors when drawing!
98//! blitter.draw_square(&mut console.sc, x, y, w, h, !RED);
99//! ```
100//!
101//! ## Loading Sprites
102//!
103//! Before you can draw sprites, load them into sprite RAM:
104//!
105//! ```ignore
106//! // Your sprite data (typically from an asset macro)
107//! static SPRITES: &[u8] = include_bytes!("sprites.bin");
108//!
109//! // Get sprite RAM access
110//! let mut sprite_mem = console.dma.sprite_mem(&mut console.sc).unwrap();
111//!
112//! // Copy sprite data
113//! sprite_mem.bytes()[..SPRITES.len()].copy_from_slice(SPRITES);
114//! ```
115//!
116//! ## Audio
117//!
118//! The GameTank has a dedicated audio coprocessor. Initialize it with firmware:
119//!
120//! ```ignore
121//! use rom::sdk::audio::{FIRMWARE, voices, MidiNote, WAVETABLE};
122//!
123//! // Load audio firmware (do this once at startup)
124//! console.sc.set_audio(0); // Disable while loading
125//! console.audio.copy_from_slice(FIRMWARE);
126//! console.sc.set_audio(0xFF); // Enable at ~14kHz
127//!
128//! // Play notes using the wavetable synth
129//! let v = voices();
130//! v[0].set_note(MidiNote::C4);
131//! v[0].set_volume(63);
132//! v[0].set_wavetable(WAVETABLE[0]);
133//! ```
134//!
135//! ## ROM Banking
136//!
137//! For large games, store assets in ROM banks and switch as needed:
138//!
139//! ```ignore
140//! // Place data in a specific bank
141//! #[unsafe(link_section = ".rodata.bank10")]
142//! static LEVEL_DATA: [u8; 8192] = [...];
143//!
144//! // Switch to that bank before accessing
145//! let via = unsafe { Via::new() };
146//! via.change_rom_bank(10);
147//! // Now LEVEL_DATA is accessible at its address
148//! ```
149//!
150//! ## Hardware Overview
151//!
152//! | Feature | Spec |
153//! |---------|------|
154//! | CPU | W65C02S @ ~3.58 MHz |
155//! | Display | 128×128 pixels, ~200 colors |
156//! | Blitter | ~60,000 pixels/frame (3.6× screen) |
157//! | RAM | 32KB (4 × 8KB banks) |
158//! | Sprite RAM | 512KB (8 pages × 256×256) |
159//! | ROM | 2MB (128 × 16KB banks) |
160//! | Audio | 6502 coprocessor, 8-bit DAC, ~14kHz |
161
162pub mod blitter;
163pub mod scr;
164pub mod via;
165pub mod video_dma;
166pub mod audio;
167pub mod boot;