initial commit
Signed-off-by: Jessie Williams <quorawings@gmail.com>
This commit is contained in:
commit
884f0125bc
|
@ -0,0 +1,15 @@
|
|||
[build]
|
||||
target = "wasm32-unknown-unknown"
|
||||
|
||||
[target.wasm32-unknown-unknown]
|
||||
rustflags = [
|
||||
# Import memory from WASM-4
|
||||
"-C", "link-arg=--import-memory",
|
||||
"-C", "link-arg=--initial-memory=65536",
|
||||
"-C", "link-arg=--max-memory=65536",
|
||||
|
||||
# Temporary workaround for #255 issue.
|
||||
# Reserve 8192 bytes of Rust stack space, offset from 6560.
|
||||
# Bump this value, 16-byte aligned, if the framebuffer gets corrupted.
|
||||
"-C", "link-arg=-zstack-size=14752",
|
||||
]
|
|
@ -0,0 +1,3 @@
|
|||
target
|
||||
dist
|
||||
.DS_Store
|
|
@ -0,0 +1,48 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "buddy-alloc"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ff9f338986406db85e2b5deb40a9255b796ca03a194c7457403d215173f3fd5"
|
||||
|
||||
[[package]]
|
||||
name = "cart"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"buddy-alloc",
|
||||
"fastrand",
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf"
|
||||
dependencies = [
|
||||
"instant",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|
@ -0,0 +1,21 @@
|
|||
[package]
|
||||
name = "cart"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
buddy-alloc = { version = "0.4.1", optional = true }
|
||||
fastrand = "1.6.0"
|
||||
lazy_static = "1.4.0"
|
||||
|
||||
[profile.release]
|
||||
opt-level = "z"
|
||||
lto = true
|
||||
|
||||
[features]
|
||||
# use `--no-default-features` or comment out next line to disable allocator
|
||||
default = ["buddy-alloc"]
|
|
@ -0,0 +1,26 @@
|
|||
# akesi
|
||||
|
||||
A game written in Rust for the [WASM-4](https://wasm4.org) fantasy console.
|
||||
|
||||
## Building
|
||||
|
||||
Build the cart by running:
|
||||
|
||||
```shell
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
Then run it with:
|
||||
|
||||
```shell
|
||||
w4 run target/wasm32-unknown-unknown/release/cart.wasm
|
||||
```
|
||||
|
||||
For more info about setting up WASM-4, see the [quickstart guide](https://wasm4.org/docs/getting-started/setup?code-lang=rust#quickstart).
|
||||
|
||||
## Links
|
||||
|
||||
- [Documentation](https://wasm4.org/docs): Learn more about WASM-4.
|
||||
- [Snake Tutorial](https://wasm4.org/docs/tutorials/snake/goal): Learn how to build a complete game
|
||||
with a step-by-step tutorial.
|
||||
- [GitHub](https://github.com/aduros/wasm4): Submit an issue or PR. Contributions are welcome!
|
Binary file not shown.
After Width: | Height: | Size: 449 B |
|
@ -0,0 +1,16 @@
|
|||
use buddy_alloc::{BuddyAllocParam, FastAllocParam, NonThreadsafeAlloc};
|
||||
|
||||
// These values can be tuned
|
||||
const FAST_HEAP_SIZE: usize = 4 * 1024; // 4 KB
|
||||
const HEAP_SIZE: usize = 16 * 1024; // 16 KB
|
||||
const LEAF_SIZE: usize = 16;
|
||||
|
||||
static mut FAST_HEAP: [u8; FAST_HEAP_SIZE] = [0u8; FAST_HEAP_SIZE];
|
||||
static mut HEAP: [u8; HEAP_SIZE] = [0u8; HEAP_SIZE];
|
||||
|
||||
#[global_allocator]
|
||||
static ALLOC: NonThreadsafeAlloc = unsafe {
|
||||
let fast_param = FastAllocParam::new(FAST_HEAP.as_ptr(), FAST_HEAP_SIZE);
|
||||
let buddy_param = BuddyAllocParam::new(HEAP.as_ptr(), HEAP_SIZE, LEAF_SIZE);
|
||||
NonThreadsafeAlloc::new(fast_param, buddy_param)
|
||||
};
|
|
@ -0,0 +1,217 @@
|
|||
#[cfg(feature = "buddy-alloc")]
|
||||
mod alloc;
|
||||
mod palette;
|
||||
mod snake;
|
||||
mod sprites;
|
||||
mod wasm4;
|
||||
|
||||
use crate::snake::{Point, Snake};
|
||||
use fastrand::Rng;
|
||||
use lazy_static::lazy_static;
|
||||
use std::sync::Mutex;
|
||||
|
||||
lazy_static! {
|
||||
static ref SNAKE_GAME: Mutex<Game> = Mutex::new(Game::new());
|
||||
}
|
||||
|
||||
enum State {
|
||||
Title,
|
||||
Playing,
|
||||
Dead,
|
||||
}
|
||||
|
||||
pub struct Game {
|
||||
state: State,
|
||||
rng: Rng,
|
||||
snake: Snake,
|
||||
frame_count: u32,
|
||||
prev_gamepad: u8,
|
||||
fruit: Point,
|
||||
}
|
||||
|
||||
impl Game {
|
||||
pub fn new() -> Self {
|
||||
let rng = Rng::with_seed(235);
|
||||
Self {
|
||||
state: State::Title,
|
||||
snake: Snake::new(),
|
||||
frame_count: 0,
|
||||
prev_gamepad: 0,
|
||||
fruit: Point {
|
||||
x: rng.i32(0..20),
|
||||
y: rng.i32(0..20),
|
||||
},
|
||||
rng,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.snake = Snake::new();
|
||||
self.state = State::Playing;
|
||||
}
|
||||
|
||||
pub fn game_input(&mut self) {
|
||||
let gamepad = unsafe { *wasm4::GAMEPAD1 };
|
||||
let just_pressed = gamepad & (gamepad ^ self.prev_gamepad);
|
||||
|
||||
if just_pressed & wasm4::BUTTON_UP != 0 {
|
||||
self.snake.up();
|
||||
}
|
||||
|
||||
if just_pressed & wasm4::BUTTON_DOWN != 0 {
|
||||
self.snake.down();
|
||||
}
|
||||
|
||||
if just_pressed & wasm4::BUTTON_LEFT != 0 {
|
||||
self.snake.left();
|
||||
}
|
||||
|
||||
if just_pressed & wasm4::BUTTON_RIGHT != 0 {
|
||||
self.snake.right();
|
||||
}
|
||||
|
||||
self.prev_gamepad = gamepad;
|
||||
}
|
||||
|
||||
pub fn update(&mut self) {
|
||||
self.frame_count += 1;
|
||||
|
||||
match self.state {
|
||||
State::Title => self.show_title(),
|
||||
State::Playing => self.step_game(),
|
||||
State::Dead => self.show_score(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn show_title(&mut self) {
|
||||
wasm4::text("akesi", 48, 8);
|
||||
wasm4::text("Press x", 48, 128);
|
||||
wasm4::text("From Within", 32, 144);
|
||||
|
||||
palette::set_draw_color(0x30);
|
||||
|
||||
wasm4::blit(
|
||||
&sprites::AKESI,
|
||||
48,
|
||||
24,
|
||||
sprites::AKESI_WIDTH,
|
||||
sprites::AKESI_HEIGHT,
|
||||
wasm4::BLIT_1BPP,
|
||||
);
|
||||
|
||||
let gamepad = unsafe { *wasm4::GAMEPAD1 };
|
||||
let just_pressed = gamepad & (gamepad ^ self.prev_gamepad);
|
||||
|
||||
if just_pressed & wasm4::BUTTON_1 != 0 {
|
||||
wasm4::tone(0 | (1000 << 16), 4 | (20 << 8), 80, wasm4::TONE_TRIANGLE);
|
||||
self.reset();
|
||||
}
|
||||
|
||||
if just_pressed & wasm4::BUTTON_UP != 0 {
|
||||
palette::en4();
|
||||
}
|
||||
|
||||
if just_pressed & wasm4::BUTTON_DOWN != 0 {
|
||||
palette::moonlight();
|
||||
}
|
||||
|
||||
if just_pressed & wasm4::BUTTON_LEFT != 0 {
|
||||
palette::cafe_nouveau();
|
||||
}
|
||||
|
||||
if just_pressed & wasm4::BUTTON_RIGHT != 0 {
|
||||
palette::amanita();
|
||||
}
|
||||
|
||||
self.prev_gamepad = gamepad;
|
||||
}
|
||||
|
||||
pub fn show_score(&mut self) {
|
||||
wasm4::text("Score", 48, 48);
|
||||
wasm4::text("Press x to\nplay again", 36, 128);
|
||||
|
||||
let mut x = 16;
|
||||
let mut y = 64;
|
||||
for _ in 0..self.snake.body.len() - 3 {
|
||||
palette::set_draw_color(0x4320);
|
||||
wasm4::blit(&sprites::FRUIT, x, y, 8, 8, wasm4::BLIT_2BPP);
|
||||
x += 8;
|
||||
if x > 140 {
|
||||
x = 16;
|
||||
y += 8;
|
||||
}
|
||||
}
|
||||
|
||||
let gamepad = unsafe { *wasm4::GAMEPAD1 };
|
||||
let just_pressed = gamepad & (gamepad ^ self.prev_gamepad);
|
||||
|
||||
if just_pressed & wasm4::BUTTON_1 != 0 {
|
||||
wasm4::tone(0 | (1000 << 16), 4 | (20 << 8), 80, wasm4::TONE_TRIANGLE);
|
||||
self.reset();
|
||||
}
|
||||
self.prev_gamepad = gamepad;
|
||||
}
|
||||
|
||||
pub fn step_game(&mut self) {
|
||||
self.game_input();
|
||||
|
||||
if self.snake.is_dead() {
|
||||
wasm4::tone(
|
||||
460 | (140 << 16),
|
||||
94 | (48 << 8) | (62 << 16),
|
||||
80,
|
||||
wasm4::TONE_TRIANGLE,
|
||||
);
|
||||
self.state = State::Dead;
|
||||
}
|
||||
|
||||
let speed = match self.snake.body.len() {
|
||||
3..=8 => 18,
|
||||
9..=15 => 14,
|
||||
16..=24 => 12,
|
||||
25..=32 => 10,
|
||||
_ => 5,
|
||||
};
|
||||
|
||||
if self.frame_count % speed == 0 {
|
||||
let dropped_pos = self.snake.update();
|
||||
|
||||
if self.snake.body[0] == self.fruit {
|
||||
if let Some(last_pos) = dropped_pos {
|
||||
self.snake.body.push(last_pos);
|
||||
}
|
||||
self.fruit.x = self.rng.i32(0..20);
|
||||
self.fruit.y = self.rng.i32(0..20);
|
||||
|
||||
while self.snake.inside(&self.fruit) {
|
||||
self.fruit.x = self.rng.i32(0..20);
|
||||
self.fruit.y = self.rng.i32(0..20);
|
||||
}
|
||||
|
||||
wasm4::tone(140 | (250 << 16), 12 | (6 << 8), 80, wasm4::TONE_TRIANGLE);
|
||||
}
|
||||
}
|
||||
self.snake.draw();
|
||||
|
||||
palette::set_draw_color(0x4320);
|
||||
wasm4::blit(
|
||||
&sprites::FRUIT,
|
||||
self.fruit.x * 8,
|
||||
self.fruit.y * 8,
|
||||
8,
|
||||
8,
|
||||
wasm4::BLIT_2BPP,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
fn start() {
|
||||
palette::moonlight();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
fn update() {
|
||||
palette::set_draw_color(0x2);
|
||||
SNAKE_GAME.lock().expect("game_state").update();
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
use crate::wasm4;
|
||||
|
||||
pub fn set_draw_color(idx: u16) {
|
||||
unsafe { *wasm4::DRAW_COLORS = idx.into() }
|
||||
}
|
||||
|
||||
pub fn set_palette(palette: [u32; 4]) {
|
||||
unsafe {
|
||||
*wasm4::PALETTE = palette;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn moonlight() {
|
||||
set_palette([0xf3eaab, 0x86a0b7, 0x3d476a, 0x19152a]);
|
||||
}
|
||||
|
||||
pub fn en4() {
|
||||
set_palette([0xfbf7f3, 0xe5b083, 0x426e5d, 0x20283d]);
|
||||
}
|
||||
|
||||
pub fn amanita() {
|
||||
set_palette([0xf1eee3, 0xccc2b8, 0xb34750, 0x4d0f40]);
|
||||
}
|
||||
|
||||
pub fn cafe_nouveau() {
|
||||
set_palette([0xf8e6d0, 0xc08e70, 0x683a34, 0x200816]);
|
||||
}
|
||||
|
||||
/*pub fn coldfire() {
|
||||
set_palette([0x46425e, 0x5b768d, 0xd17c7c, 0xf6c6a8]);
|
||||
}*/
|
|
@ -0,0 +1,89 @@
|
|||
use crate::{palette::set_draw_color, wasm4};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Point {
|
||||
pub x: i32,
|
||||
pub y: i32,
|
||||
}
|
||||
|
||||
pub struct Snake {
|
||||
pub body: Vec<Point>,
|
||||
pub direction: Point,
|
||||
}
|
||||
|
||||
impl Snake {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
body: vec![
|
||||
Point { x: 2, y: 0 },
|
||||
Point { x: 1, y: 0 },
|
||||
Point { x: 0, y: 0 },
|
||||
],
|
||||
direction: Point { x: 1, y: 0 },
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_dead(&self) -> bool {
|
||||
self.inside(&self.body[0])
|
||||
}
|
||||
|
||||
pub fn inside(&self, pt: &Point) -> bool {
|
||||
self.body
|
||||
.iter()
|
||||
.skip(1)
|
||||
.any(|body_section| body_section == pt)
|
||||
}
|
||||
|
||||
pub fn down(&mut self) {
|
||||
if self.direction.y == 0 {
|
||||
self.direction = Point { x: 0, y: 1 };
|
||||
}
|
||||
}
|
||||
|
||||
pub fn up(&mut self) {
|
||||
if self.direction.y == 0 {
|
||||
self.direction = Point { x: 0, y: -1 };
|
||||
}
|
||||
}
|
||||
|
||||
pub fn left(&mut self) {
|
||||
if self.direction.x == 0 {
|
||||
self.direction = Point { x: -1, y: 0 };
|
||||
}
|
||||
}
|
||||
|
||||
pub fn right(&mut self) {
|
||||
if self.direction.x == 0 {
|
||||
self.direction = Point { x: 1, y: 0 };
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self) -> Option<Point> {
|
||||
self.body.insert(
|
||||
0,
|
||||
Point {
|
||||
x: (self.body[0].x + self.direction.x) % 20,
|
||||
y: (self.body[0].y + self.direction.y) % 20,
|
||||
},
|
||||
);
|
||||
|
||||
if self.body[0].x < 0 {
|
||||
self.body[0].x = 19;
|
||||
}
|
||||
|
||||
if self.body[0].y < 0 {
|
||||
self.body[0].y = 19;
|
||||
}
|
||||
self.body.pop()
|
||||
}
|
||||
|
||||
pub fn draw(&self) {
|
||||
set_draw_color(0x43);
|
||||
for &Point { x, y } in self.body.iter() {
|
||||
wasm4::rect(x * 8, y * 8, 8, 8);
|
||||
}
|
||||
|
||||
set_draw_color(0x4);
|
||||
wasm4::rect(self.body[0].x * 8, self.body[0].y * 8, 8, 8);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
pub const FRUIT: [u8; 16] = [
|
||||
0x00, 0xa0, 0x02, 0x00, 0x0e, 0xf0, 0x36, 0x5c, 0xd6, 0x57, 0xd5, 0x57, 0x35, 0x5c, 0x0f, 0xf0,
|
||||
];
|
||||
|
||||
// akesi
|
||||
pub const AKESI_WIDTH: u32 = 64;
|
||||
pub const AKESI_HEIGHT: u32 = 96;
|
||||
pub const AKESI: [u8; 768] = [
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xc0, 0x00, 0x00, 0x07, 0x80, 0x00,
|
||||
0x00, 0x1f, 0xe0, 0x00, 0x00, 0x0f, 0xc0, 0x00, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x1f, 0xe0, 0x00,
|
||||
0x00, 0x3f, 0xf0, 0x00, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x3f, 0xf0, 0x00,
|
||||
0x00, 0x3f, 0xf0, 0x00, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x1f, 0xe0, 0x00, 0x00, 0x3f, 0xf0, 0x00,
|
||||
0x00, 0x0f, 0xc0, 0x00, 0x00, 0x1f, 0xe0, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x0f, 0xc0, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xc0, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x07, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xfe, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x1f, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xc0, 0x00, 0x00,
|
||||
0x00, 0x00, 0x3f, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xf0, 0x00, 0x00,
|
||||
0x00, 0x00, 0x7f, 0xe3, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xc1, 0xff, 0xf8, 0x00, 0x00,
|
||||
0x00, 0x00, 0xff, 0xc0, 0xff, 0xfc, 0x00, 0x00, 0x3f, 0xff, 0xff, 0x80, 0x1f, 0xfc, 0x1f, 0x80,
|
||||
0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||
0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00,
|
||||
0x00, 0x07, 0xf8, 0x00, 0x01, 0xfe, 0x00, 0x00, 0x00, 0x07, 0xf8, 0x00, 0x01, 0xfe, 0x00, 0x00,
|
||||
0x00, 0x07, 0xf8, 0x00, 0x01, 0xfe, 0x00, 0x00, 0x00, 0x07, 0xf8, 0x00, 0x01, 0xfe, 0x00, 0x00,
|
||||
0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80,
|
||||
0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||
0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||
0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
|
||||
0x00, 0x07, 0xf8, 0x00, 0x01, 0xfe, 0x00, 0x00, 0x00, 0x07, 0xf8, 0x00, 0x01, 0xfe, 0x00, 0x00,
|
||||
0x00, 0x07, 0xf8, 0x00, 0x03, 0xfe, 0x00, 0x00, 0x00, 0x07, 0xf8, 0x00, 0x03, 0xfe, 0x00, 0x00,
|
||||
0x00, 0x07, 0xf8, 0x00, 0x03, 0xfe, 0x00, 0x00, 0x00, 0x07, 0xf8, 0x00, 0x03, 0xfc, 0x00, 0x00,
|
||||
0x00, 0x07, 0xf8, 0x00, 0x03, 0xfc, 0x00, 0x00, 0x00, 0x07, 0xf8, 0x00, 0x03, 0xfc, 0x00, 0x00,
|
||||
0x00, 0x07, 0xf8, 0x00, 0x07, 0xfc, 0x03, 0xc0, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0,
|
||||
0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0,
|
||||
0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0,
|
||||
0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0,
|
||||
0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x01, 0xff, 0x01, 0xff, 0xe0, 0x00, 0x00,
|
||||
0x00, 0x01, 0xff, 0x87, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xff, 0x8f, 0xff, 0x80, 0x00, 0x00,
|
||||
0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xfe, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x7f, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xf8, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x3f, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xc0, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x0f, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x03, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf0, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
];
|
|
@ -0,0 +1,222 @@
|
|||
//
|
||||
// WASM-4: https://wasm4.org/docs
|
||||
|
||||
#![allow(unused)]
|
||||
|
||||
// ┌───────────────────────────────────────────────────────────────────────────┐
|
||||
// │ │
|
||||
// │ Platform Constants │
|
||||
// │ │
|
||||
// └───────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
pub const SCREEN_SIZE: u32 = 160;
|
||||
|
||||
// ┌───────────────────────────────────────────────────────────────────────────┐
|
||||
// │ │
|
||||
// │ Memory Addresses │
|
||||
// │ │
|
||||
// └───────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
pub const PALETTE: *mut [u32; 4] = 0x04 as *mut [u32; 4];
|
||||
pub const DRAW_COLORS: *mut u16 = 0x14 as *mut u16;
|
||||
pub const GAMEPAD1: *const u8 = 0x16 as *const u8;
|
||||
pub const GAMEPAD2: *const u8 = 0x17 as *const u8;
|
||||
pub const GAMEPAD3: *const u8 = 0x18 as *const u8;
|
||||
pub const GAMEPAD4: *const u8 = 0x19 as *const u8;
|
||||
pub const MOUSE_X: *const i16 = 0x1a as *const i16;
|
||||
pub const MOUSE_Y: *const i16 = 0x1c as *const i16;
|
||||
pub const MOUSE_BUTTONS: *const u8 = 0x1e as *const u8;
|
||||
pub const SYSTEM_FLAGS: *mut u8 = 0x1f as *mut u8;
|
||||
pub const FRAMEBUFFER: *mut [u8; 6400] = 0xa0 as *mut [u8; 6400];
|
||||
|
||||
pub const BUTTON_1: u8 = 1;
|
||||
pub const BUTTON_2: u8 = 2;
|
||||
pub const BUTTON_LEFT: u8 = 16;
|
||||
pub const BUTTON_RIGHT: u8 = 32;
|
||||
pub const BUTTON_UP: u8 = 64;
|
||||
pub const BUTTON_DOWN: u8 = 128;
|
||||
|
||||
pub const MOUSE_LEFT: u8 = 1;
|
||||
pub const MOUSE_RIGHT: u8 = 2;
|
||||
pub const MOUSE_MIDDLE: u8 = 4;
|
||||
|
||||
pub const SYSTEM_PRESERVE_FRAMEBUFFER: u8 = 1;
|
||||
pub const SYSTEM_HIDE_GAMEPAD_OVERLAY: u8 = 2;
|
||||
|
||||
// ┌───────────────────────────────────────────────────────────────────────────┐
|
||||
// │ │
|
||||
// │ Drawing Functions │
|
||||
// │ │
|
||||
// └───────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
/// Copies pixels to the framebuffer.
|
||||
pub fn blit(sprite: &[u8], x: i32, y: i32, width: u32, height: u32, flags: u32) {
|
||||
unsafe { extern_blit(sprite.as_ptr(), x, y, width, height, flags) }
|
||||
}
|
||||
extern "C" {
|
||||
#[link_name = "blit"]
|
||||
fn extern_blit(sprite: *const u8, x: i32, y: i32, width: u32, height: u32, flags: u32);
|
||||
}
|
||||
|
||||
/// Copies a subregion within a larger sprite atlas to the framebuffer.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn blit_sub(
|
||||
sprite: &[u8],
|
||||
x: i32,
|
||||
y: i32,
|
||||
width: u32,
|
||||
height: u32,
|
||||
src_x: u32,
|
||||
src_y: u32,
|
||||
stride: u32,
|
||||
flags: u32,
|
||||
) {
|
||||
unsafe {
|
||||
extern_blit_sub(
|
||||
sprite.as_ptr(),
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
src_x,
|
||||
src_y,
|
||||
stride,
|
||||
flags,
|
||||
)
|
||||
}
|
||||
}
|
||||
extern "C" {
|
||||
#[link_name = "blitSub"]
|
||||
fn extern_blit_sub(
|
||||
sprite: *const u8,
|
||||
x: i32,
|
||||
y: i32,
|
||||
width: u32,
|
||||
height: u32,
|
||||
src_x: u32,
|
||||
src_y: u32,
|
||||
stride: u32,
|
||||
flags: u32,
|
||||
);
|
||||
}
|
||||
|
||||
pub const BLIT_2BPP: u32 = 1;
|
||||
pub const BLIT_1BPP: u32 = 0;
|
||||
pub const BLIT_FLIP_X: u32 = 2;
|
||||
pub const BLIT_FLIP_Y: u32 = 4;
|
||||
pub const BLIT_ROTATE: u32 = 8;
|
||||
|
||||
/// Draws a line between two points.
|
||||
pub fn line(x1: i32, y1: i32, x2: i32, y2: i32) {
|
||||
unsafe { extern_line(x1, y1, x2, y2) }
|
||||
}
|
||||
extern "C" {
|
||||
#[link_name = "line"]
|
||||
fn extern_line(x1: i32, y1: i32, x2: i32, y2: i32);
|
||||
}
|
||||
|
||||
/// Draws an oval (or circle).
|
||||
pub fn oval(x: i32, y: i32, width: u32, height: u32) {
|
||||
unsafe { extern_oval(x, y, width, height) }
|
||||
}
|
||||
extern "C" {
|
||||
#[link_name = "oval"]
|
||||
fn extern_oval(x: i32, y: i32, width: u32, height: u32);
|
||||
}
|
||||
|
||||
/// Draws a rectangle.
|
||||
pub fn rect(x: i32, y: i32, width: u32, height: u32) {
|
||||
unsafe { extern_rect(x, y, width, height) }
|
||||
}
|
||||
extern "C" {
|
||||
#[link_name = "rect"]
|
||||
fn extern_rect(x: i32, y: i32, width: u32, height: u32);
|
||||
}
|
||||
|
||||
/// Draws text using the built-in system font.
|
||||
pub fn text<T: AsRef<str>>(text: T, x: i32, y: i32) {
|
||||
let text_ref = text.as_ref();
|
||||
unsafe { extern_text(text_ref.as_ptr(), text_ref.len(), x, y) }
|
||||
}
|
||||
extern "C" {
|
||||
#[link_name = "textUtf8"]
|
||||
fn extern_text(text: *const u8, length: usize, x: i32, y: i32);
|
||||
}
|
||||
|
||||
/// Draws a vertical line
|
||||
pub fn vline(x: i32, y: i32, len: u32) {
|
||||
unsafe {
|
||||
extern_vline(x, y, len);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "vline"]
|
||||
fn extern_vline(x: i32, y: i32, len: u32);
|
||||
}
|
||||
|
||||
/// Draws a horizontal line
|
||||
pub fn hline(x: i32, y: i32, len: u32) {
|
||||
unsafe {
|
||||
extern_hline(x, y, len);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "hline"]
|
||||
fn extern_hline(x: i32, y: i32, len: u32);
|
||||
}
|
||||
|
||||
// ┌───────────────────────────────────────────────────────────────────────────┐
|
||||
// │ │
|
||||
// │ Sound Functions │
|
||||
// │ │
|
||||
// └───────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
/// Plays a sound tone.
|
||||
pub fn tone(frequency: u32, duration: u32, volume: u32, flags: u32) {
|
||||
unsafe { extern_tone(frequency, duration, volume, flags) }
|
||||
}
|
||||
extern "C" {
|
||||
#[link_name = "tone"]
|
||||
fn extern_tone(frequency: u32, duration: u32, volume: u32, flags: u32);
|
||||
}
|
||||
|
||||
pub const TONE_PULSE1: u32 = 0;
|
||||
pub const TONE_PULSE2: u32 = 1;
|
||||
pub const TONE_TRIANGLE: u32 = 2;
|
||||
pub const TONE_NOISE: u32 = 3;
|
||||
pub const TONE_MODE1: u32 = 0;
|
||||
pub const TONE_MODE2: u32 = 4;
|
||||
pub const TONE_MODE3: u32 = 8;
|
||||
pub const TONE_MODE4: u32 = 12;
|
||||
|
||||
// ┌───────────────────────────────────────────────────────────────────────────┐
|
||||
// │ │
|
||||
// │ Storage Functions │
|
||||
// │ │
|
||||
// └───────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
extern "C" {
|
||||
/// Reads up to `size` bytes from persistent storage into the pointer `dest`.
|
||||
pub fn diskr(dest: *mut u8, size: u32) -> u32;
|
||||
|
||||
/// Writes up to `size` bytes from the pointer `src` into persistent storage.
|
||||
pub fn diskw(src: *const u8, size: u32) -> u32;
|
||||
}
|
||||
|
||||
// ┌───────────────────────────────────────────────────────────────────────────┐
|
||||
// │ │
|
||||
// │ Other Functions │
|
||||
// │ │
|
||||
// └───────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
/// Prints a message to the debug console.
|
||||
pub fn trace<T: AsRef<str>>(text: T) {
|
||||
let text_ref = text.as_ref();
|
||||
unsafe { extern_trace(text_ref.as_ptr(), text_ref.len()) }
|
||||
}
|
||||
extern "C" {
|
||||
#[link_name = "traceUtf8"]
|
||||
fn extern_trace(trace: *const u8, length: usize);
|
||||
}
|
Loading…
Reference in New Issue