Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix build, improve reloader, API #32

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,7 @@

## Getting Started

Temporarily this repo targets the `zig-master` branch of
[MicroZig](https://github.com/ZigEmbeddedGroup/microzig). In order to build this
repo, you must have it checked out, and `microzig` next to `sycl-badge-2024` in
your filesystem. Once this branch is merged it won't be as janky to build this
firmware.

Check out the [Introduction](docs/introduction/README.md)!

## Uploading firmware using a debugger

Expand Down
33 changes: 22 additions & 11 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,17 @@ pub fn build(b: *Build) void {
watch.linkFramework("CoreServices");
}

b.getInstallStep().dependOn(&b.addInstallArtifact(watch, .{
.dest_dir = .disabled,
}).step);

var dep: std.Build.Dependency = .{ .builder = b };
const feature_test_cart = add_cart(&dep, b, .{
.name = "feature_test",
.optimize = .ReleaseSmall,
.root_source_file = .{ .path = "samples/feature_test.zig" },
});
feature_test_cart.install(b);
const watch_run_step = feature_test_cart.install_with_watcher(&dep, b);

const zeroman_cart = add_cart(&dep, b, .{
.name = "zeroman",
Expand All @@ -61,15 +65,9 @@ pub fn build(b: *Build) void {
});
add_zeroman_assets_step(b, zeroman_cart);
zeroman_cart.install(b);
//
// TODO: parameterize:
const watch_run = b.addRunArtifact(watch);
watch_run.addArg("serve");
watch_run.addArtifactArg(feature_test_cart.wasm);
watch_run.addArgs(&.{ "--zig-out-bin-dir", "zig-out/bin" });

const watch_step = b.step("watch", "");
watch_step.dependOn(&watch_run.step);
watch_step.dependOn(&watch_run_step.step);

const badge = mz.add_firmware(b, .{
.name = "badge",
Expand Down Expand Up @@ -130,12 +128,25 @@ pub const Cart = struct {
cart_lib: *Build.Step.Compile,

options: CartOptions,
//watch_run_cmd: *std.Build.Step.Run,

pub fn install(c: *const Cart, b: *Build) void {
c.mz.install_firmware(b, c.fw, .{ .format = .{ .uf2 = .SAMD51 } });
b.installArtifact(c.wasm);
}

pub fn install_with_watcher(c: *const Cart, d: *Build.Dependency, b: *Build) *Build.Step.Run {
c.mz.install_firmware(b, c.fw, .{ .format = .{ .uf2 = .SAMD51 } });
const install_artifact_step = b.addInstallArtifact(c.wasm, .{});
b.getInstallStep().dependOn(&install_artifact_step.step);

const watch_run = b.addRunArtifact(d.artifact("watch"));
// watch_run.addArgs(&.{ "serve", b.graph.zig_exe, "--input-dir", b.pathFromRoot(std.fs.path.dirname(options.root_source_file) orelse ""), "--cart", b.pathFromRoot("zig-out/bin/feature_test.wasm") });
watch_run.addArgs(&.{ "serve", b.graph.zig_exe, "--input-dir" });
watch_run.addFileArg(c.options.root_source_file.dirname());
watch_run.addArgs(&.{ "--cart", b.getInstallPath(install_artifact_step.dest_dir.?, install_artifact_step.dest_sub_path) });

return watch_run;
}
};

pub const CartOptions = struct {
Expand Down Expand Up @@ -163,8 +174,8 @@ pub fn add_cart(

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

Expand Down
17 changes: 17 additions & 0 deletions docs/introduction/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Introduction

This guide will help you get up and running with the badge and fittingly help introduce you to other attendees.

## Running

## On the simulator

The simulator is ideal for fast iteration as it supports live reloading.

Run `zig build watch` and head to https://badgesim.microzig.tech/.

## On hardware

Once you're happy with what you've made, you'll need to flash it onto your badge!

TODO
15 changes: 15 additions & 0 deletions docs/introduction/build.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const std = @import("std");
const badge = @import("sycl-badge");

pub fn build(b: *std.Build) void {
const dep = b.dependency("sycl-badge", .{});
const feature_test_cart = badge.add_cart(dep, b, .{
.name = "hello",
.optimize = .ReleaseSmall,
.root_source_file = .{ .path = "hello.zig" },
});
const watch_run_step = feature_test_cart.install_with_watcher(dep, b);

const watch_step = b.step("watch", "");
watch_step.dependOn(&watch_run_step.step);
}
14 changes: 14 additions & 0 deletions docs/introduction/build.zig.zon
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.{
.name = "sycl-badge-introduction",
.version = "0.0.0",
.paths = .{
"hello.zig",
"build.zig",
},

.dependencies = .{
.@"sycl-badge" = .{
.path = "../..",
},
},
}
10 changes: 10 additions & 0 deletions docs/introduction/hello.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const cart = @import("cart-api");

export fn update() void {
// Set background to a nice gray
@memset(cart.framebuffer, cart.DisplayColor{
.r = 10,
.g = 20,
.b = 10,
});
}
107 changes: 84 additions & 23 deletions samples/feature_test.zig
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,15 @@ fn write_stored_number(number: u64) void {

export fn update() void {
if (offset % (60 * 2) == 0) {
cart.tone(440, 20, 10, .{
.channel = .pulse1,
.duty_cycle = .@"1/8",
.panning = .left,
cart.tone(.{
.frequency = 440,
.duration = 20,
.volume = 10,
.flags = .{
.channel = .pulse1,
.duty_cycle = .@"1/8",
.panning = .left,
},
});
}

Expand Down Expand Up @@ -62,32 +67,88 @@ export fn update() void {
for (0..cart.screen_height) |y| {
for (0..cart.screen_width) |x| {
cart.framebuffer[y * cart.screen_width + x] = .{
.red = @intFromFloat(@as(f32, @floatFromInt(x)) / cart.screen_width * 31),
.green = green_565,
.blue = @intFromFloat(@as(f32, @floatFromInt(y)) / cart.screen_height * 31),
.r = @intFromFloat(@as(f32, @floatFromInt(x)) / cart.screen_width * 31),
.g = green_565,
.b = @intFromFloat(@as(f32, @floatFromInt(y)) / cart.screen_height * 31),
};
}
}

for (cart.neopixels, 0..) |*np, i| {
np.* = .{
.red = @intFromFloat(@as(f32, @floatFromInt(i)) / 5 * 255),
.green = @intFromFloat(@as(f32, @floatFromInt(cart.light_level.*)) / std.math.maxInt(u12) * 255),
.blue = @intFromFloat(@as(f32, @floatFromInt(i)) / 5 * 255),
.r = @intFromFloat(@as(f32, @floatFromInt(i)) / 5 * 255),
.g = @intFromFloat(@as(f32, @floatFromInt(cart.light_level.*)) / std.math.maxInt(u12) * 255),
.b = @intFromFloat(@as(f32, @floatFromInt(i)) / 5 * 255),
};
}

// TODO: blit, blitSub

cart.line(.{ .red = 0, .green = 63, .blue = 0 }, 50, 50, 70, 70);

cart.hline(.{ .red = 31, .green = 0, .blue = 0 }, 30, 30, 20);
cart.vline(.{ .red = 31, .green = 0, .blue = 0 }, 30, 30, 20);

cart.oval(.{ .red = 0, .green = 0, .blue = 31 }, .{ .red = 31, .green = 0, .blue = 31 }, 80, 80, 10, 10);
cart.rect(.{ .red = 31, .green = 31, .blue = 31 }, .{ .red = 0, .green = 63, .blue = 31 }, 100, 100, 10, 10);

cart.text(.{ .red = 0, .green = 0, .blue = 0 }, .{ .red = 31, .green = 63, .blue = 31 }, fbs.getWritten(), 0, 0);

cart.text(.{ .red = 0, .green = 0, .blue = 0 }, .{ .red = 31, .green = 63, .blue = 31 }, "\x80\x81\x82\x83\x84\x85\x86\x87\x88", 0, 120);
cart.blit(.{
.sprite = &.{
.{ .r = 31, .g = 0, .b = 0 },
.{ .r = 0, .g = 0, .b = 31 },
.{ .r = 31, .g = 0, .b = 0 },
.{ .r = 0, .g = 0, .b = 31 },
},
.x = 40,
.y = 40,
.width = 2,
.height = 2,
.flags = .{},
});

cart.line(.{
.x1 = 50,
.y1 = 50,
.x2 = 70,
.y2 = 70,
.color = .{ .r = 0, .g = 63, .b = 0 },
});

cart.hline(.{
.x = 30,
.y = 30,
.len = 20,
.color = .{ .r = 31, .g = 0, .b = 0 },
});

cart.vline(.{
.x = 30,
.y = 30,
.len = 20,
.color = .{ .r = 31, .g = 0, .b = 0 },
});

cart.oval(.{
.x = 80,
.y = 80,
.width = 10,
.height = 10,
.stroke_color = .{ .r = 0, .g = 0, .b = 31 },
.fill_color = .{ .r = 31, .g = 0, .b = 31 },
});

cart.rect(.{
.x = 100,
.y = 100,
.width = 10,
.height = 10,
.stroke_color = .{ .r = 31, .g = 31, .b = 31 },
.fill_color = .{ .r = 0, .g = 63, .b = 31 },
});

cart.text(.{
.str = fbs.getWritten(),
.x = 0,
.y = 0,
.text_color = .{ .r = 0, .g = 0, .b = 0 },
.background_color = .{ .r = 31, .g = 63, .b = 31 },
});

cart.text(.{
.str = "\x80\x81\x82\x83\x84\x85\x86\x87\x88",
.x = 0,
.y = 120,
.text_color = .{ .r = 0, .g = 0, .b = 0 },
.background_color = .{ .r = 31, .g = 63, .b = 31 },
});
}
2 changes: 1 addition & 1 deletion samples/zeroman/build/convert_gfx.zig
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ fn convert(args: ConvertFile, writer: std.fs.File.Writer) !void {

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.print(" .{{ .r = {}, .g = {}, .b = {} }},\n", .{ c.r, c.g, c.b });
}
try writer.writeAll(" };\n");

Expand Down
47 changes: 45 additions & 2 deletions simulator/src/framebuffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,15 +238,15 @@ export class Framebuffer {
y += 8;
currentX = x;
} else if (charCode >= 32 && charCode <= 255) {
this.blit([textColor, backgroundColor], FONT, currentX, y, 8, 8, 0, (charCode - 32) << 3, 8);
this.blitPalette([textColor, backgroundColor], FONT, currentX, y, 8, 8, 0, (charCode - 32) << 3, 8);
currentX += 8;
} else {
currentX += 8;
}
}
}

blit (
blitPalette (
colors: [number, number] | [number, number, number, number],
sprite: Uint8Array,
dstX: number, dstY: number,
Expand Down Expand Up @@ -302,4 +302,47 @@ export class Framebuffer {
}
}
}

blit(
sprite: Uint16Array,
dstX: number, dstY: number,
width: number, height: number,
srcX: number, srcY: number,
srcStride: number,
flipX: number | boolean = false,
flipY: number | boolean = false,
rotate: number | boolean = false
) {
// Clip rectangle to screen
let clipXMin, clipYMin, clipXMax, clipYMax;
if (rotate) {
flipX = !flipX;
clipXMin = Math.max(0, dstY) - dstY;
clipYMin = Math.max(0, dstX) - dstX;
clipXMax = Math.min(width, HEIGHT - dstY);
clipYMax = Math.min(height, WIDTH - dstX);
} else {
clipXMin = Math.max(0, dstX) - dstX;
clipYMin = Math.max(0, dstY) - dstY;
clipXMax = Math.min(width, WIDTH - dstX);
clipYMax = Math.min(height, HEIGHT - dstY);
}

// Iterate pixels in rectangle
for (let y = clipYMin; y < clipYMax; y++) {
for (let x = clipXMin; x < clipXMax; x++) {
// Calculate sprite target coords
const tx = dstX + (rotate ? y : x);
const ty = dstY + (rotate ? x : y);

// Calculate sprite source coords
const sx = srcX + (flipX ? width - x - 1 : x);
const sy = srcY + (flipY ? height - y - 1 : y);

const index = sy * srcStride + sx;

this.drawPoint(sprite[index], tx, ty);
}
}
}
}
Loading
Loading