Skip to content

Commit

Permalink
Blit Zero Man assets (#29)
Browse files Browse the repository at this point in the history
  • Loading branch information
fabioarnold authored Apr 25, 2024
1 parent a5fa6ae commit e138226
Show file tree
Hide file tree
Showing 17 changed files with 258 additions and 12 deletions.
86 changes: 75 additions & 11 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,31 @@ pub const sycl_badge_2024 = MicroZig.Target{
pub fn build(b: *Build) void {
const mz = MicroZig.init(b, .{});

const wasm_target = b.resolveTargetQuery(.{
.cpu_arch = .wasm32,
.os_tag = .freestanding,
});
const optimize = b.standardOptimizeOption(.{});

_ = b.addModule("wasm4", .{ .root_source_file = .{ .path = "src/wasm4.zig" } });

var dep: std.Build.Dependency = .{ .builder = b };
const cart = add_cart(&dep, b, .{
.name = "sample",
.optimize = optimize,
.root_source_file = .{ .path = "samples/feature_test.zig" },
// const sample_cart = add_cart(&dep, b, .{
// .name = "sample",
// .target = wasm_target,
// .optimize = .ReleaseSmall,
// .root_source_file = .{ .path = "samples/feature_test.zig" },
// });
const zeroman_cart = add_cart(&dep, b, .{
.name = "zeroman",
.target = wasm_target,
.optimize = .ReleaseSmall,
.root_source_file = .{ .path = "samples/zeroman/main.zig" },
});
add_zeroman_assets_step(b, zeroman_cart);

const watch_step = b.step("watch", "");
watch_step.dependOn(&cart.watch_run_cmd.step);
watch_step.dependOn(&zeroman_cart.watch_run_cmd.step);

//const modified_memory_regions = b.allocator.dupe(MicroZig.MemoryRegion, py_badge.chip.memory_regions) catch @panic("out of memory");
//for (modified_memory_regions) |*memory_region| {
Expand Down Expand Up @@ -112,11 +124,14 @@ pub const Cart = struct {
// mz: *MicroZig,
// fw: *MicroZig.Firmware,

options: CartOptions,
lib: *std.Build.Step.Compile,
watch_run_cmd: *std.Build.Step.Run,
};

pub const CartOptions = struct {
name: []const u8,
target: std.Build.ResolvedTarget,
optimize: std.builtin.OptimizeMode,
root_source_file: Build.LazyPath,
};
Expand All @@ -129,18 +144,15 @@ pub fn add_cart(
const lib = b.addExecutable(.{
.name = "cart",
.root_source_file = options.root_source_file,
.target = b.resolveTargetQuery(.{
.cpu_arch = .wasm32,
.os_tag = .freestanding,
}),
.target = options.target,
.optimize = options.optimize,
});
b.installArtifact(lib);

lib.entry = .disabled;
lib.import_memory = true;
lib.initial_memory = 65536;
lib.max_memory = 65536;
lib.initial_memory = 2 * 65536;
lib.max_memory = 2 * 65536;
lib.stack_size = 14752;
lib.global_base = 160 * 128 * 2 + 0x1e;

Expand Down Expand Up @@ -177,6 +189,8 @@ pub fn add_cart(

const cart: *Cart = b.allocator.create(Cart) catch @panic("out of memory");
cart.* = .{
.options = options,
.lib = lib,
.watch_run_cmd = watch_run_cmd,
};
return cart;
Expand Down Expand Up @@ -218,3 +232,53 @@ pub fn install_cart(b: *Build, cart: *Cart) void {
cart.mz.install_firmware(b, cart.fw, .{});
cart.mz.install_firmware(b, cart.fw, .{ .format = .{ .uf2 = .SAMD51 } });
}

fn add_zeroman_assets_step(b: *Build, cart: *Cart) void {
const host_target = b.resolveTargetQuery(.{});
const convert = b.addExecutable(.{
.name = "convert_gfx",
.root_source_file = b.path("samples/zeroman/build/convert_gfx.zig"),
.target = host_target,
.optimize = cart.options.optimize,
});
convert.root_module.addImport("zigimg", b.dependency("zigimg", .{}).module("zigimg"));

const base_path = "samples/zeroman/assets/";
const gen_gfx = b.addRunArtifact(convert);
inline for (zeroman_assets) |file| {
gen_gfx.addArg("-i");
gen_gfx.addFileArg(b.path(base_path ++ file.path));
gen_gfx.addArg(std.fmt.comptimePrint("{}", .{file.bits}));
gen_gfx.addArg(std.fmt.comptimePrint("{}", .{file.transparency}));
}
gen_gfx.addArg("-o");
const gfx_zig = gen_gfx.addOutputFileArg("gfx.zig");

const gfx_mod = b.addModule("gfx", .{
.root_source_file = gfx_zig,
.target = cart.options.target,
.optimize = cart.options.optimize,
});
var dep: std.Build.Dependency = .{ .builder = b };
gfx_mod.addImport("wasm4", dep.module("wasm4"));

cart.lib.step.dependOn(&gen_gfx.step);
cart.lib.root_module.addImport("gfx", gfx_mod);
}

const GfxAsset = struct { path: []const u8, bits: u4, transparency: bool };

const zeroman_assets = [_]GfxAsset{
.{ .path = "door.png", .bits = 2, .transparency = false },
.{ .path = "effects.png", .bits = 2, .transparency = true },
.{ .path = "font.png", .bits = 2, .transparency = true },
.{ .path = "gopher.png", .bits = 4, .transparency = true },
.{ .path = "healthbar.png", .bits = 4, .transparency = true },
.{ .path = "hurt.png", .bits = 1, .transparency = true },
.{ .path = "needleman.png", .bits = 4, .transparency = false },
.{ .path = "shot.png", .bits = 2, .transparency = true },
.{ .path = "spike.png", .bits = 2, .transparency = true },
.{ .path = "teleport.png", .bits = 2, .transparency = true },
.{ .path = "title.png", .bits = 4, .transparency = false },
.{ .path = "zero.png", .bits = 4, .transparency = true },
};
4 changes: 4 additions & 0 deletions build.zig.zon
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,9 @@
.url = "git+https://github.com/karlseguin/websocket.zig.git#c77f87d0e6548865636eb9781106a8be72e5755a",
.hash = "12208720b772330f309cfb48957f4152ee0930b716837d0c1d07fee2dea2f4dc712e",
},
.zigimg = .{
.url = "https://github.com/zigimg/zigimg/archive/8873f29fc449e1b63400e9f4ad86d3c76204f962.tar.gz",
.hash = "122019f6439545235af116d0d8eb81fde1ff05fdb070da57c723772c557f84c5bf39",
},
},
}
Binary file added samples/zeroman/assets/door.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/zeroman/assets/effects.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/zeroman/assets/font.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/zeroman/assets/gopher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/zeroman/assets/healthbar.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/zeroman/assets/hurt.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/zeroman/assets/needleman.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/zeroman/assets/shot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/zeroman/assets/spike.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/zeroman/assets/teleport.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/zeroman/assets/title.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/zeroman/assets/zero.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
114 changes: 114 additions & 0 deletions samples/zeroman/build/convert_gfx.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
const std = @import("std");
const allocator = std.heap.c_allocator;
const Image = @import("zigimg").Image;

const ConvertFile = struct {
path: []const u8,
bits: u4,
transparency: bool,
};

pub fn main() !void {
var args = std.process.args();
_ = args.next();
var in_files = std.ArrayList(ConvertFile).init(allocator);
var out_path: []const u8 = undefined;
while (args.next()) |arg| {
if (std.mem.eql(u8, arg, "-i")) {
const path = args.next() orelse return error.MissingArg;
const bits = args.next() orelse return error.MissingArg;
const transparency = args.next() orelse return error.MissingArg;
try in_files.append(.{ .path = path, .bits = @intCast(bits[0] - '0'), .transparency = transparency[0] == 't' });
} else if (std.mem.eql(u8, arg, "-o")) {
out_path = args.next() orelse return error.MissingArg;
}
}
std.debug.print("{s}\n", .{out_path});

const out_file = try std.fs.cwd().createFile(out_path, .{});
defer out_file.close();

const writer = out_file.writer();
try writer.writeAll("const PackedIntSlice = @import(\"std\").packed_int_array.PackedIntSlice;\n");
try writer.writeAll("const DisplayColor = @import(\"wasm4\").DisplayColor;\n\n");

for (in_files.items) |in_file| {
try convert(in_file, writer);
}
}

fn convert(args: ConvertFile, writer: std.fs.File.Writer) !void {
const N = 8 / args.bits;

var image = try Image.fromFilePath(allocator, args.path);
defer image.deinit();

var colors = std.ArrayList(Color).init(allocator);
defer colors.deinit();
if (args.transparency) try colors.append(.{ .r = 31, .g = 0, .b = 31 });
var indices = try std.ArrayList(usize).initCapacity(allocator, image.width * image.height);
defer indices.deinit();
var it = image.iterator();
while (it.next()) |pixel| {
const color = Color{
.r = @intFromFloat(31.0 * pixel.r),
.g = @intFromFloat(63.0 * pixel.g),
.b = @intFromFloat(31.0 * pixel.b),
};
const index = try getIndex(&colors, color);
indices.appendAssumeCapacity(index);
}
std.debug.print("{} colors: {any}\n", .{ colors.items.len, colors.items });
var packed_data = try allocator.alloc(u8, indices.items.len / N);
defer allocator.free(packed_data);
for (packed_data, 0..) |_, i| {
packed_data[i] = 0;
for (0..N) |n| {
const shift: u3 = @intCast(n * args.bits);
packed_data[i] |= @intCast(indices.items[N * i + n] << shift);
}
}

{
const name = std.fs.path.stem(args.path);
try writer.print("pub const {s} = struct {{\n", .{name});

try writer.print(" pub const w = {};\n", .{image.width});
try writer.print(" pub const h = {};\n", .{image.height});

try writer.writeAll(" pub const colors = [_]DisplayColor{\n");
for (colors.items) |c| {
try writer.print(" .{{ .red = {}, .green = {}, .blue = {} }},\n", .{ c.r, c.g, c.b });
}
try writer.writeAll(" };\n");

try writer.print(" pub const indices = PackedIntSlice(u{}).init(@constCast(data[0..]), data.len * {});\n", .{ args.bits, N });
try writer.writeAll(" const data = [_]u8{\n");
for (packed_data, 0..) |index, i| {
if (i % 32 == 0) try writer.writeAll(" ");
try writer.print("{}, ", .{index});
if ((i + 1) % 32 == 0) try writer.writeAll("\n");
}
try writer.writeAll(" };\n");

try writer.writeAll("};\n\n");
}
}

pub const Color = packed struct(u16) {
b: u5,
g: u6,
r: u5,

fn eql(self: Color, other: Color) bool {
return @as(u16, @bitCast(self)) == @as(u16, @bitCast(other));
}
};

fn getIndex(colors: *std.ArrayList(Color), color: Color) !usize {
for (colors.items, 0..) |c, i| {
if (c.eql(color)) return i;
}
try colors.append(color);
return colors.items.len - 1;
}
64 changes: 64 additions & 0 deletions samples/zeroman/main.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
const wasm4 = @import("wasm4");
const gfx = @import("gfx");

fn blitZero() void {
var y: usize = 0;
while (y < 32) : (y += 1) {
var x: usize = 0;
while (x < 24) : (x += 1) {
const index = gfx.zero.indices.get(y * gfx.zero.w + x);
if (index == 0) continue;
wasm4.framebuffer[(y + 96) * wasm4.screen_width + x + 80 - 12] = gfx.zero.colors[index];
}
}
}

fn blitGopher() void {
var y: usize = 0;
while (y < 24) : (y += 1) {
var x: usize = 0;
while (x < 24) : (x += 1) {
const index = gfx.gopher.indices.get(y * gfx.gopher.w + x);
if (index == 0) continue;
wasm4.framebuffer[(y + 104) * wasm4.screen_width + x + 92] = gfx.gopher.colors[index];
}
}
}

pub fn blitSpriteOpaque(sheet: anytype, dx: u32, dy: u32) void {
var y: usize = 0;
while (y < sheet.h) : (y += 1) {
var x: usize = 0;
while (x < sheet.w) : (x += 1) {
const index = sheet.indices.get(y * sheet.w + x);
wasm4.framebuffer[(y + dy) * wasm4.screen_width + x + dx] = sheet.colors[index];
}
}
}

pub fn blitSprite(sheet: anytype, dx: u32, dy: u32) void {
var y: usize = 0;
while (y < sheet.h) : (y += 1) {
var x: usize = 0;
while (x < sheet.w) : (x += 1) {
const index = sheet.indices.get(y * sheet.w + x);
if (index == 0) continue;
wasm4.framebuffer[(y + dy) * wasm4.screen_width + x + dx] = sheet.colors[index];
}
}
}

pub export fn update() void {
blitSpriteOpaque(gfx.needleman, 0, 0);
blitSpriteOpaque(gfx.title, 0, 40);
blitSprite(gfx.font, 16, 0);
blitSpriteOpaque(gfx.door, 0, 0);
blitSprite(gfx.healthbar, 0, 0);
blitSprite(gfx.hurt, 92, 104);
blitSprite(gfx.spike, 0, 104);
blitSprite(gfx.teleport, 68 - 24, 96);
blitSprite(gfx.effects, 0, 72);
blitZero();
blitGopher();
blitSprite(gfx.shot, 100, 112);
}
2 changes: 1 addition & 1 deletion simulator/src/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export class Runtime {

this.flashBuffer = new ArrayBuffer(constants.FLASH_PAGE_SIZE);

this.memory = new WebAssembly.Memory({initial: 1, maximum: 1});
this.memory = new WebAssembly.Memory({initial: 2, maximum: 2});
this.data = new DataView(this.memory.buffer);

this.framebuffer = new Framebuffer(this.memory.buffer);
Expand Down

0 comments on commit e138226

Please sign in to comment.