initial commit

This commit is contained in:
Jessie Williams 2022-03-06 21:10:49 -05:00
commit 9c44fdd4f6
15 changed files with 605 additions and 0 deletions

15
.cargo/config.toml Normal file
View File

@ -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",
]

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
target/*
dist/*
.DS_Store

41
Cargo.lock generated Normal file
View File

@ -0,0 +1,41 @@
# 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",
]
[[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",
]

22
Cargo.toml Normal file
View File

@ -0,0 +1,22 @@
[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"
[profile.release]
opt-level = "z"
lto = true
panic = 'abort'
debug = true
[features]
# use `--no-default-features` or comment out next line to disable allocator
default = ["buddy-alloc"]

10
build.sh Executable file
View File

@ -0,0 +1,10 @@
#!/usr/bin/env bash
set -e
mkdir -p dist
rm -rf dist/*
cargo build --release
wasm-snip --snip-rust-fmt-code --snip-rust-panicking-code ./target/wasm32-unknown-unknown/release/cart.wasm -o ./dist/mara2_snip.wasm
wasm-opt -Oz --zero-filled-memory --strip-producers --dce ./dist/mara2_snip.wasm -o ./dist/mara2.wasm
wasm-strip ./dist/mara2.wasm

BIN
sprites/Mai.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 B

BIN
sprites/Mara.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 391 B

BIN
sprites/gate.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 B

BIN
sprites/palette.ase Normal file

Binary file not shown.

16
src/alloc.rs Normal file
View File

@ -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)
};

89
src/lib.rs Normal file
View File

@ -0,0 +1,89 @@
#[cfg(feature = "buddy-alloc")]
mod alloc;
mod sh0rk;
mod sprites;
use fastrand::Rng;
use sh0rk::{palette, sys::*, Point, Direction::{self, *}};
static mut GAME: Game = Game::new();
struct Game {
frame_count: u32,
prev_gamepad: u8,
rng: Option<Rng>,
mai_position: Point,
mara_position: Point,
mara_frame: u32,
mara_dir: Direction,
}
impl Game {
const fn new() -> Self {
Self {
frame_count: 0,
prev_gamepad: 0,
rng: None,
mai_position: Point { x: 76, y: 48 },
mara_position: Point { x: 0, y: 64 },
mara_frame: 0,
mara_dir: Right,
}
}
fn update(&mut self) {
self.frame_count += 1;
palette::set_draw_color(2);
text("Mara: Sh0rk of\nJustice 2\npowered by:\nsh0rk engine v0", 10, 10);
palette::set_draw_color(3);
text("From Within, 2022", 10, 144);
sprites::MAI.draw(self.mai_position, 0);
if self.frame_count % 15 == 0 {
self.mara_frame = if self.mara_frame == 1 { 0 } else { 1 };
}
if self.frame_count % 4 == 0 {
if self.mara_dir == Right && self.mara_position.x >= 144 {
self.mara_dir = Down;
}
if self.mara_dir == Down && self.mara_position.y >= 120 {
self.mara_dir = Left;
}
if self.mara_dir == Left && self.mara_position.x <= 0 {
self.mara_dir = Up;
}
if self.mara_dir == Up && self.mara_position.y <= 64 {
self.mara_dir = Right;
}
const SPEED: i32 = 2;
match self.mara_dir {
Left => self.mara_position.x -= SPEED,
Right => self.mara_position.x += SPEED,
Up => self.mara_position.y -= SPEED,
Down => self.mara_position.y += SPEED,
};
}
sprites::MARA.draw(
self.mara_dir,
self.mara_frame,
self.mara_position,
0,
);
}
}
#[no_mangle]
unsafe fn start() {
palette::en4();
GAME.rng = Some(Rng::with_seed(420));
}
#[no_mangle]
unsafe fn update() {
GAME.update();
}

91
src/sh0rk/mod.rs Normal file
View File

@ -0,0 +1,91 @@
pub mod palette;
pub mod sys;
#[derive(Clone, Copy, PartialEq, Eq, Default)]
pub struct Point {
pub x: i32,
pub y: i32,
}
impl From<(i32, i32)> for Point {
fn from((x, y): (i32, i32)) -> Self {
Point { x, y }
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum Direction {
Left,
Right,
Up,
Down,
}
use Direction::*;
pub struct SpriteAtlas<const N: usize> {
pub atlas: Sprite<N>,
pub width: u32,
pub height: u32,
pub animated: bool,
}
impl<const N: usize> SpriteAtlas<N> {
pub fn draw(&self, dir: Direction, step: u32, p: Point, flags: u32) {
let flags = flags
| if dir == Left {
sys::BLIT_FLIP_X
} else {
0
};
let frame: u32 = if self.animated {
match dir {
Left => 4,
Right => 4,
Up => 2,
Down => 0,
}
} else {
match dir {
Up => 1,
Down => 0,
_ => 2,
}
} + step;
palette::set_draw_color(self.atlas.palette);
sys::blit_sub(
&self.atlas.sprite,
p.x,
p.y,
self.width,
self.height,
self.width * frame,
0,
self.atlas.width,
self.atlas.flags | flags,
);
}
}
pub struct Sprite<const N: usize> {
pub palette: u16,
pub width: u32,
pub height: u32,
pub flags: u32,
pub sprite: [u8; N],
}
impl<const N: usize> Sprite<N> {
pub fn draw(&self, p: Point, flags: u32) {
palette::set_draw_color(self.palette);
sys::blit(
&self.sprite,
p.x,
p.y,
self.width,
self.height,
self.flags | flags,
);
}
}

31
src/sh0rk/palette.rs Normal file
View File

@ -0,0 +1,31 @@
use super::sys;
pub fn set_draw_color(idx: u16) {
unsafe { *sys::DRAW_COLORS = idx.into() }
}
pub fn set_palette(palette: [u32; 4]) {
unsafe {
*sys::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]);
}*/

222
src/sh0rk/sys.rs Normal file
View File

@ -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);
}

65
src/sprites.rs Normal file
View File

@ -0,0 +1,65 @@
use crate::sh0rk::{Sprite, SpriteAtlas};
pub const GATE: Sprite<12> = Sprite {
palette: 0x1320,
width: 12,
height: 14,
flags: 1,
sprite: [ 0x3d,0x5a,0xa7,0xf5,0xaa,0x57,0xd6,0xa5,0x6b,0x1f,0xff,0xfc ],
};
pub const MAI: Sprite<64> = Sprite {
palette: 0x1320,
width: 16,
height: 16,
flags: 1,
sprite: [
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xdf, 0xf5, 0xff, 0xff,
0x5f, 0xf6, 0x75, 0x9d, 0x9f, 0xf6, 0x59, 0x55, 0x9f, 0xf6, 0x95, 0x96, 0x9f, 0xfd, 0x41,
0x41, 0x7f, 0xfe, 0x49, 0x49, 0x7f, 0xfd, 0x49, 0x49, 0xbf, 0xfe, 0x55, 0x55, 0x7f, 0xff,
0x59, 0x66, 0xff, 0xff, 0xd6, 0x97, 0xff, 0xff, 0xf5, 0x5f, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff,
],
};
pub const MARA: SpriteAtlas<384> = SpriteAtlas {
atlas: Sprite {
palette: 0x3210,
width: 96,
height: 16,
flags: 1,
sprite: [
0x3f, 0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0xfc, 0x3f, 0x00,
0x00, 0xfc, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x36, 0xff, 0xc3, 0x9c,
0x36, 0xff, 0xc3, 0x9c, 0x3a, 0xc3, 0xff, 0xac, 0x3a, 0xc3, 0xff, 0xac, 0x00, 0x00,
0xec, 0x00, 0x00, 0x00, 0xec, 0x00, 0x35, 0xd6, 0xfe, 0x5c, 0x35, 0xd6, 0xfe, 0x5c,
0x3a, 0xbf, 0xab, 0xac, 0x3a, 0xbf, 0xab, 0xac, 0x30, 0x00, 0xeb, 0x00, 0x30, 0x00,
0xeb, 0x00, 0x3a, 0xdb, 0x56, 0xac, 0x3a, 0xdb, 0x56, 0xac, 0x3a, 0xe6, 0x66, 0xec,
0x3a, 0xe6, 0x66, 0xec, 0xdc, 0x03, 0xe9, 0xf0, 0xdc, 0x03, 0xe9, 0xf0, 0x0f, 0x6d,
0x5e, 0xf0, 0x0f, 0x6d, 0x5e, 0xf0, 0x0f, 0xb9, 0x9d, 0xf0, 0x0f, 0xb9, 0x9d, 0xf0,
0xe7, 0x0e, 0x65, 0x5c, 0xe7, 0x0e, 0x65, 0x5c, 0x03, 0xbd, 0x5e, 0xc0, 0x03, 0xbd,
0x5e, 0xc0, 0x03, 0xde, 0x77, 0xc0, 0x03, 0xde, 0x77, 0xc0, 0xe7, 0x0d, 0xa7, 0x5c,
0xe7, 0x0d, 0xa7, 0x5c, 0x03, 0xf7, 0xd6, 0xc0, 0x03, 0xf7, 0xd6, 0xc0, 0x03, 0xe7,
0xdb, 0xc0, 0x03, 0xe7, 0xdb, 0xc0, 0xe9, 0xcd, 0xa7, 0x5c, 0xe9, 0xcd, 0xa7, 0x5c,
0x00, 0xe5, 0x5b, 0x00, 0x00, 0xe5, 0x5b, 0x00, 0x00, 0xe9, 0x6b, 0x00, 0x00, 0xe9,
0x6b, 0x00, 0x3a, 0x7d, 0xa5, 0x7c, 0x3a, 0x7d, 0xa5, 0x7c, 0x03, 0xff, 0xff, 0xc0,
0x03, 0xff, 0xff, 0xc0, 0x03, 0xfa, 0xaf, 0xc0, 0x03, 0xfa, 0xaf, 0xc0, 0x3a, 0xc3,
0xe9, 0xf0, 0x3a, 0xc3, 0xe9, 0xf0, 0x0d, 0xaa, 0xaa, 0x70, 0x0d, 0xaa, 0xaa, 0x70,
0x0d, 0xae, 0xba, 0x70, 0x0d, 0xae, 0xba, 0x70, 0x3b, 0x00, 0xff, 0xc0, 0x3b, 0x00,
0xff, 0xc0, 0x0d, 0xea, 0xab, 0x70, 0x0d, 0xea, 0xab, 0x70, 0x0d, 0xee, 0xbb, 0x70,
0x0d, 0xee, 0xbb, 0x70, 0xeb, 0x03, 0xb7, 0xb0, 0xeb, 0x03, 0xb7, 0xb0, 0x0d, 0xea,
0xab, 0x70, 0x0d, 0xea, 0xab, 0x70, 0x0d, 0xee, 0xbb, 0x70, 0x0d, 0xee, 0xbb, 0x70,
0xea, 0xff, 0xde, 0xb0, 0xea, 0xff, 0xad, 0xf0, 0x00, 0x37, 0xdc, 0xc0, 0x03, 0x37,
0xdc, 0xc0, 0x03, 0x3e, 0xbc, 0xc0, 0x03, 0x3e, 0xbc, 0xc0, 0x3a, 0xaa, 0xfa, 0xb0,
0x3a, 0xaa, 0xab, 0xb0, 0x00, 0x37, 0xdc, 0x00, 0x00, 0x37, 0xdc, 0x00, 0x00, 0x3f,
0xfc, 0x00, 0x00, 0x3f, 0xfc, 0x00, 0x3a, 0xaf, 0xd5, 0xf0, 0x3a, 0xaf, 0xd5, 0xf0,
0x00, 0x37, 0x30, 0x00, 0x00, 0x0c, 0xdc, 0x00, 0x00, 0x37, 0x30, 0x00, 0x00, 0x0c,
0xdc, 0x00, 0x0f, 0xf0, 0x37, 0x00, 0x0f, 0xf0, 0x37, 0x00, 0x00, 0x0c, 0x00, 0x00,
0x00, 0x00, 0x30, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x03,
0x8c, 0xb0, 0x00, 0x00, 0xee, 0xc0,
],
},
height: 16,
width: 16,
animated: true,
};