Initial commit

master
Louis Pearson 2022-01-14 19:33:23 -07:00
commit 1d430e81f7
4 changed files with 229 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/zig-cache
/zig-out

69
build.zig Normal file
View File

@ -0,0 +1,69 @@
const std = @import("std");
// Returns true if the version includes https://github.com/ziglang/zig/pull/10572/commits.
// When this is false, trying to place the stack first will result in data corruption.
fn version_supports_stack_first(zig_version: std.SemanticVersion) !bool {
if (zig_version.order(try std.SemanticVersion.parse("0.10.0")).compare(.gte)) {
// Merged here: https://github.com/ziglang/zig/pull/10572
return true;
}
if (zig_version.major == 0 and zig_version.minor == 10) {
// Check for 0.10.0-dev.258+. Conservatively check the prefix of the tag
// in case zig uses other prefixes that don't respect semver ordering.
if (zig_version.pre) |pre| {
// Merged here: https://github.com/ziglang/zig/pull/10572
return std.mem.startsWith(u8, pre, "dev.") and zig_version.order(try std.SemanticVersion.parse("0.10.0-dev.258")).compare(.gte);
}
}
// Backported here: https://github.com/ziglang/zig/commit/6f49233ac6a6569b909b689f22fc260dc8c19234
return zig_version.order(try std.SemanticVersion.parse("0.9.1")).compare(.gte);
}
test "stack version check" {
const expect = std.testing.expect;
const parse = std.SemanticVersion.parse;
try expect(!try version_supports_stack_first(try parse("0.8.0")));
try expect(!try version_supports_stack_first(try parse("0.9.0")));
try expect(!try version_supports_stack_first(try parse("0.9.1-dev.259")));
try expect(try version_supports_stack_first(try parse("0.9.1")));
// Conservatively don't recognize tags other than 'dev'.
try expect(!try version_supports_stack_first(try parse("0.10.0-aev.259")));
try expect(!try version_supports_stack_first(try parse("0.10.0-zev.259")));
try expect(!try version_supports_stack_first(try parse("0.10.0-dev.257")));
try expect(try version_supports_stack_first(try parse("0.10.0-dev.258")));
try expect(try version_supports_stack_first(try parse("0.10.0-dev.259")));
try expect(try version_supports_stack_first(try parse("0.10.0")));
try expect(try version_supports_stack_first(try parse("0.10.1-dev.100")));
try expect(try version_supports_stack_first(try parse("0.10.1-dev.300")));
try expect(try version_supports_stack_first(try parse("0.10.1")));
try expect(try version_supports_stack_first(try parse("1.0.0")));
}
pub fn build(b: *std.build.Builder) !void {
const zig_version = @import("builtin").zig_version;
const mode = b.standardReleaseOptions();
const lib = b.addSharedLibrary("cart", "src/main.zig", .unversioned);
lib.setBuildMode(mode);
lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
lib.import_memory = true;
lib.initial_memory = 65536;
lib.max_memory = 65536;
if (try version_supports_stack_first(zig_version)) {
lib.stack_size = 14752;
} else {
// `--stack-first` option have been reenabled on wasm targets with https://github.com/ziglang/zig/pull/10572
std.log.warn("Update to Zig >=0.9.1 (or >=0.10.0-dev.258 for nightly) to detect stack overflows at runtime.", .{});
lib.global_base = 6560;
lib.stack_size = 8192;
}
// Workaround https://github.com/ziglang/zig/issues/2910, preventing
// functions from compiler_rt getting incorrectly marked as exported, which
// prevents them from being removed even if unused.
lib.export_symbol_names = &[_][]const u8{ "start", "update" };
lib.install();
}

25
src/main.zig Normal file
View File

@ -0,0 +1,25 @@
const w4 = @import("wasm4.zig");
const smiley = [8]u8{
0b11000011,
0b10000001,
0b00100100,
0b00100100,
0b00000000,
0b00100100,
0b10011001,
0b11000011,
};
export fn update() void {
w4.DRAW_COLORS.* = 2;
w4.text("Hello from Zig!", 10, 10);
const gamepad = w4.GAMEPAD1.*;
if (gamepad & w4.BUTTON_1 != 0) {
w4.DRAW_COLORS.* = 4;
}
w4.blit(&smiley, 76, 76, 8, 8, w4.BLIT_1BPP);
w4.text("Press X to blink", 16, 90);
}

133
src/wasm4.zig Normal file
View File

@ -0,0 +1,133 @@
//
// WASM-4: https://wasm4.org/docs
//
//
// Platform Constants
//
//
pub const CANVAS_SIZE: u32 = 160;
//
//
// Memory Addresses
//
//
pub const PALETTE: *[4]u32 = @intToPtr(*[4]u32, 0x04);
pub const DRAW_COLORS: *u16 = @intToPtr(*u16, 0x14);
pub const GAMEPAD1: *const u8 = @intToPtr(*const u8, 0x16);
pub const GAMEPAD2: *const u8 = @intToPtr(*const u8, 0x17);
pub const GAMEPAD3: *const u8 = @intToPtr(*const u8, 0x18);
pub const GAMEPAD4: *const u8 = @intToPtr(*const u8, 0x19);
pub const MOUSE_X: *const i16 = @intToPtr(*const i16, 0x1a);
pub const MOUSE_Y: *const i16 = @intToPtr(*const i16, 0x1c);
pub const MOUSE_BUTTONS: *const u8 = @intToPtr(*const u8, 0x1e);
pub const SYSTEM_FLAGS: *u8 = @intToPtr(*u8, 0x1f);
pub const FRAMEBUFFER: *[6400]u8 = @intToPtr(*[6400]u8, 0xA0);
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 extern fn blit(sprite: [*]const u8, x: i32, y: i32, width: i32, height: i32, flags: u32) void;
/// Copies a subregion within a larger sprite atlas to the framebuffer.
pub extern fn blitSub(sprite: [*]const u8, x: i32, y: i32, width: i32, height: i32, src_x: u32, src_y: u32, stride: i32, flags: u32) void;
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 extern fn line(x1: i32, y1: i32, x2: i32, y2: i32) void;
/// Draws an oval (or circle).
pub extern fn oval(x: i32, y: i32, width: i32, height: i32) void;
/// Draws a rectangle.
pub extern fn rect(x: i32, y: i32, width: u32, height: u32) void;
/// Draws text using the built-in system font.
pub fn text(str: []const u8, x: i32, y: i32) void {
textUtf8(str.ptr, str.len, x, y);
}
extern fn textUtf8(strPtr: [*]const u8, strLen: usize, x: i32, y: i32) void;
/// Draws a vertical line
pub extern fn vline(x: i32, y: i32, len: u32) void;
/// Draws a horizontal line
pub extern fn hline(x: i32, y: i32, len: u32) void;
//
//
// Sound Functions
//
//
/// Plays a sound tone.
pub extern fn tone(frequency: u32, duration: u32, volume: u32, flags: u32) void;
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
//
//
/// Reads up to `size` bytes from persistent storage into the pointer `dest`.
pub extern fn diskr(dest: [*]u8, size: u32) u32;
/// Writes up to `size` bytes from the pointer `src` into persistent storage.
pub extern fn diskw(src: [*]const u8, size: u32) u32;
//
//
// Other Functions
//
//
/// Prints a message to the debug console.
pub fn trace(x: []const u8) void {
traceUtf8(x.ptr, x.len);
}
extern fn traceUtf8(strPtr: [*]const u8, strLen: usize) void;
/// Use with caution, as there's no compile-time type checking.
///
/// * %c, %d, and %x expect 32-bit integers.
/// * %f expects 64-bit floats.
/// * %s expects a *zero-terminated* string pointer.
///
/// See https://github.com/aduros/wasm4/issues/244 for discussion and type-safe
/// alternatives.
pub extern fn tracef(x: [*:0]const u8, ...) void;