Skip to content

Instantly share code, notes, and snippets.

@peterhellberg
Last active March 16, 2026 22:12
Show Gist options
  • Select an option

  • Save peterhellberg/b331a0330325084ffcb3ab1e48302061 to your computer and use it in GitHub Desktop.

Select an option

Save peterhellberg/b331a0330325084ffcb3ab1e48302061 to your computer and use it in GitHub Desktop.
WASMCarts in Zig

WASMCarts in Zig

I just found out about the in development fantasy console

https://github.com/pgattic/wasm-experiment

Since I'm on Pop_OS! which is currently based on 24.04 I need to install a newer version of cmake than what is in the default repos:

I installed SDL3 from source, such as what is described in this article:

wasmcarts-buildtool

This tool is written in Rust, so I just had to cargo build --release and then symlink the resulting binary to somewhere in my $PATH

wasmcarts (Linux engine)

$ export WASM3_SOURCE=/home/peter/Code/GitHub/wasm3/wasm3
$ CC="zig cc -fno-sanitize=undefined" cmake -S . -B build/linux -DCMAKE_C_FLAGS="-std=c23" -DCMAKE_C_FLAGS="-D_GNU_SOURCE"

Example cart in Zig

WASMCarts.toml

[package]
name = "wc-zig"
author = "Peter Hellberg"
version = "0.0.1"

[build.code]
command = ["zig", "build"]
output = "zig-out/bin/cart.wasm"

[build.assets]
dir = "assets"
sprite_tiles = "spr.4bpp"
background_tiles = "bg.4bpp"
background_map = "bg.map"

build.zig

const std = @import("std");

pub fn build(b: *std.Build) !void {
    const mod = b.createModule(.{
        .root_source_file = b.path("src/cart.zig"),
        .target = b.resolveTargetQuery(.{
            .cpu_arch = .wasm32,
            .os_tag = .wasi,
        }),
        .optimize = .ReleaseSmall,
    });

    mod.export_symbol_names = &[_][]const u8{
        "setup",
        "update",
    };

    const exe = b.addExecutable(.{
        .name = "cart",
        .root_module = mod,
    });

    exe.entry = .disabled;
    exe.import_memory = false;

    b.installArtifact(exe);
}

src/wc.zig

pub extern "env" fn _rand() u32;
pub extern "env" fn _clearScreen(c: u8) void;
pub extern "env" fn _pSet(x: i32, y: i32, c: u8) void;
pub extern "env" fn _rect(x: i32, y: i32, w: u32, h: u32, c: u8) void;
pub extern "env" fn _rectFill(x: i32, y: i32, w: u32, h: u32, c: u8) void;
pub extern "env" fn _sprite(x: i32, y: i32, id: u8) void;
pub extern "env" fn _btn(btn: u8) bool;
pub extern "env" fn _btnP(btn: u8) bool;
pub extern "env" fn _printLnDbg(ptr: usize) void;

pub fn rand() u32 {
    return _rand();
}

pub fn clearScreen(c: u8) void {
    _clearScreen(c);
}

pub fn pSet(x: i32, y: i32, c: u8) void {
    _pSet(x, y, c);
}

pub fn rect(x: i32, y: i32, w: u32, h: u32, c: u8) void {
    _rect(x, y, w, h, c);
}

pub fn rectFill(x: i32, y: i32, w: u32, h: u32, c: u8) void {
    _rectFill(x, y, w, h, c);
}

pub fn sprite(x: i32, y: i32, id: u8) void {
    _sprite(x, y, id);
}

pub const Button = enum(u8) {
    Left = 0,
    Right = 1,
    Up = 2,
    Down = 3,
    A = 4,
    B = 5,
    X = 6,
    Y = 7,
    L = 8,
    R = 9,
    Start = 10,
    Select = 11,
};

pub fn btn(button: Button) bool {
    return _btn(@intFromEnum(button));
}

pub fn btnP(button: Button) bool {
    return _btnP(@intFromEnum(button));
}

pub fn print(str: []const u8) void {
    _printLnDbg(@intFromPtr(str.ptr));
}

src/cart.zig

const wc = @import("wc.zig");

export fn setup() void {
    wc.print("Hello from Zig!");
}

export fn update() void {
    const w = 24;
    const h = 24;

    wc.clearScreen(0);

    wc.rectFill(12, 12, w, h, 1);
    wc.rectFill(24, 24, w, h, 2);
    wc.rectFill(36, 36, w, h, 3);
    wc.rectFill(48, 48, w, h, 4);
    wc.rectFill(60, 60, w, h, 5);
}
@peterhellberg
Copy link
Copy Markdown
Author

peterhellberg commented Mar 16, 2026

Basic conversion of 4bpp➤png and png➤4bpp

https://gist.github.com/peterhellberg/5f881735c6e91eadeefd8e0a7e6b3bab

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment