From 11f0a4a265f2a7670280148fe25fd832713b7846 Mon Sep 17 00:00:00 2001 From: Auguste Rame <19855629+SuperAuguste@users.noreply.github.com> Date: Mon, 13 May 2024 19:57:38 +0200 Subject: [PATCH] Intro and fix (#35) --- build.zig | 41 ++++---- docs/introduction/README.md | 6 +- docs/introduction/build.zig | 2 +- docs/introduction/hello.zig | 10 -- docs/introduction/src/hello.zig | 164 ++++++++++++++++++++++++++++++++ samples/feature_test.zig | 4 +- src/cart/api.zig | 4 + 7 files changed, 198 insertions(+), 33 deletions(-) delete mode 100644 docs/introduction/hello.zig create mode 100644 docs/introduction/src/hello.zig diff --git a/build.zig b/build.zig index fac0770..83d1ba6 100644 --- a/build.zig +++ b/build.zig @@ -10,18 +10,20 @@ pub const py_badge: MicroZig.Target = .{ .hal = null, }; -pub const sycl_badge = MicroZig.Target{ - .preferred_format = .elf, - .chip = atsam.chips.atsamd51j19.chip, - .hal = .{ - .root_source_file = .{ .cwd_relative = "src/hal.zig" }, - }, - .board = .{ - .name = "SYCL Badge Rev A", - .root_source_file = .{ .cwd_relative = "src/board.zig" }, - }, - .linker_script = .{ .cwd_relative = "src/badge/samd51j19a_self.ld" }, -}; +fn sycl_badge_microzig_target(d: *Build.Dependency) MicroZig.Target { + return .{ + .preferred_format = .elf, + .chip = atsam.chips.atsamd51j19.chip, + .hal = .{ + .root_source_file = d.path("src/hal.zig"), + }, + .board = .{ + .name = "SYCL Badge Rev A", + .root_source_file = d.path("src/board.zig"), + }, + .linker_script = d.path("src/badge/samd51j19a_self.ld"), + }; +} pub fn build(b: *Build) void { const mz = MicroZig.init(b, .{}); @@ -71,7 +73,7 @@ pub fn build(b: *Build) void { const badge = mz.add_firmware(b, .{ .name = "badge", - .target = sycl_badge, + .target = sycl_badge_microzig_target(&dep), .optimize = .ReleaseSmall, .root_source_file = .{ .path = "src/badge.zig" }, }); @@ -92,7 +94,7 @@ pub fn build(b: *Build) void { }) |name| { const mvp = mz.add_firmware(b, .{ .name = std.fmt.comptimePrint("badge.demo.{s}", .{name}), - .target = sycl_badge, + .target = sycl_badge_microzig_target(&dep), .optimize = optimize, .root_source_file = .{ .path = std.fmt.comptimePrint("src/badge/demos/{s}.zig", .{name}) }, }); @@ -152,6 +154,7 @@ pub const Cart = struct { // 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 }); if (opt.watch_dirs) |dirs| { + if (dirs.len == 0) @panic("watch input directories should either be empty or null"); for (dirs) |dir| { watch_run.addArgs(&.{ "--input-dir", dir }); } @@ -199,7 +202,7 @@ pub fn add_cart( wasm.root_module.addImport("cart-api", d.module("cart-api")); const sycl_badge_target = - b.resolveTargetQuery(sycl_badge.chip.cpu.target); + b.resolveTargetQuery(sycl_badge_microzig_target(d).chip.cpu.target); const cart_lib = b.addStaticLibrary(.{ .name = "cart", @@ -212,7 +215,7 @@ pub fn add_cart( .use_lld = true, }); cart_lib.root_module.addImport("cart-api", d.module("cart-api")); - cart_lib.linker_script = .{ .path = "src/cart.ld" }; + cart_lib.linker_script = d.path("src/cart.ld"); const fw_options = b.addOptions(); fw_options.addOption(bool, "have_cart", true); @@ -221,10 +224,10 @@ pub fn add_cart( const fw = mz.add_firmware(d.builder, .{ .name = options.name, - .target = sycl_badge, + .target = sycl_badge_microzig_target(d), .optimize = options.optimize, - .root_source_file = .{ .path = "src/main.zig" }, - .linker_script = .{ .path = "src/cart.ld" }, + .root_source_file = d.path("src/main.zig"), + .linker_script = d.path("src/cart.ld"), }); fw.artifact.linkLibrary(cart_lib); fw.artifact.step.dependOn(&fw_options.step); diff --git a/docs/introduction/README.md b/docs/introduction/README.md index 6440990..6b541b2 100644 --- a/docs/introduction/README.md +++ b/docs/introduction/README.md @@ -1,6 +1,10 @@ # Introduction -This guide will help you get up and running with the badge and fittingly help introduce you to other attendees. +This is a small example that shows how to use the badge as an actual badge with a little game as a bonus. + +## What's Inside + +See `src/hello.zig` and tweak the the values to your liking. ## Running diff --git a/docs/introduction/build.zig b/docs/introduction/build.zig index f3ba0de..ca6048d 100644 --- a/docs/introduction/build.zig +++ b/docs/introduction/build.zig @@ -6,7 +6,7 @@ pub fn build(b: *std.Build) void { const feature_test_cart = badge.add_cart(dep, b, .{ .name = "hello", .optimize = .ReleaseSmall, - .root_source_file = .{ .path = "hello.zig" }, + .root_source_file = .{ .path = "src/hello.zig" }, }); const watch_run_step = feature_test_cart.install_with_watcher(dep, b, .{}); diff --git a/docs/introduction/hello.zig b/docs/introduction/hello.zig deleted file mode 100644 index b87b3ea..0000000 --- a/docs/introduction/hello.zig +++ /dev/null @@ -1,10 +0,0 @@ -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, - }); -} diff --git a/docs/introduction/src/hello.zig b/docs/introduction/src/hello.zig new file mode 100644 index 0000000..3200ab3 --- /dev/null +++ b/docs/introduction/src/hello.zig @@ -0,0 +1,164 @@ +const std = @import("std"); +const cart = @import("cart-api"); + +export fn start() void {} + +var scene: enum { intro, game } = .intro; + +export fn update() void { + switch (scene) { + .intro => scene_intro(), + .game => scene_game(), + } +} + +const lines = &[_][]const u8{ + "Auguste Rame", + "~AOE4 Player", + + "", + + "aurame", + "SuperAuguste", + + "", + + "SYCL24", + "Press START", +}; +const spacing = (cart.font_height * 4 / 3); + +var ticks: u8 = 0; + +fn scene_intro() void { + set_background(); + + @memset(cart.neopixels, .{ + .r = 0, + .g = 0, + .b = 0, + }); + + if (ticks / 128 == 0) { + // Make the neopixel 24-bit color LEDs a nice Zig orange + @memset(cart.neopixels, .{ + .r = 247, + .g = 164, + .b = 29, + }); + } + + const y_start = (cart.screen_height - (cart.font_height + spacing * (lines.len - 1))) / 2; + + // Write it out! + for (lines, 0..) |line, i| { + cart.text(.{ + .text_color = .{ .r = 31, .g = 63, .b = 31 }, + .str = line, + .x = @intCast((cart.screen_width - cart.font_width * line.len) / 2), + .y = @intCast(y_start + spacing * i), + }); + } + + if (ticks == 0) cart.red_led.* = !cart.red_led.*; + if (cart.controls.start) scene = .game; + + ticks +%= 4; +} + +const Player = enum(u8) { x = 0, o = 1, none = std.math.maxInt(u8) }; + +var selected_x: u8 = 0; +var selected_y: u8 = 0; +var control_cooldown: bool = false; +var turn: Player = .x; +var state: [3][3]Player = @bitCast([1]Player{.none} ** 9); + +fn scene_game() void { + set_background(); + + const title = "TIC-TAC-TOE"; + cart.text(.{ + .text_color = .{ .r = 31, .g = 63, .b = 31 }, + .str = title, + .x = @intCast((cart.screen_width - cart.font_width * title.len) / 2), + .y = 10, + }); + + const instructions = "D-PAD + SELECT"; + cart.text(.{ + .text_color = .{ .r = 31, .g = 63, .b = 31 }, + .str = instructions, + .x = @intCast((cart.screen_width - cart.font_width * instructions.len) / 2), + .y = cart.screen_height - cart.font_height - 10, + }); + + for (0..3) |y| { + for (0..3) |x| { + cart.rect(.{ + .stroke_color = .{ .r = 31, .g = 63, .b = 31 }, + .fill_color = if (x == selected_x and y == selected_y) .{ .r = 31 / 2, .g = 63 / 2, .b = 31 / 2 } else null, + .x = @intCast(cart.screen_width / 2 + x * 19 - 10 * 3), + .y = @intCast(cart.screen_height / 2 + y * 19 - 10 * 3), + .width = 20, + .height = 20, + }); + + cart.text(.{ + .text_color = .{ .r = 31, .g = 63, .b = 31 }, + .str = switch (state[y][x]) { + .x => "X", + .o => "O", + .none => "", + }, + .x = @intCast(cart.screen_width / 2 + x * 19 - 10 * 3 + 7), + .y = @intCast(cart.screen_height / 2 + y * 19 - 10 * 3 + 7), + }); + } + } + + if (!control_cooldown) { + if (cart.controls.left) selected_x -|= 1; + if (cart.controls.right and selected_x != 2) selected_x += 1; + if (cart.controls.up) selected_y -|= 1; + if (cart.controls.down and selected_y != 2) selected_y += 1; + if (cart.controls.select and state[selected_y][selected_x] == .none) { + state[selected_y][selected_x] = turn; + turn = switch (turn) { + .x => .o, + .o => .x, + else => unreachable, + }; + + if (check_win()) { + turn = .x; + @memset(@as(*[9]Player, @ptrCast(&state)), .none); + scene = .intro; + selected_x = 0; + selected_y = 0; + } + } + } + + control_cooldown = false; + if (cart.controls.left or cart.controls.right or cart.controls.up or cart.controls.down or cart.controls.select) control_cooldown = true; +} + +fn set_background() void { + const ratio = (4095 - @as(f32, @floatFromInt(cart.light_level.*))) / 4095 * 0.2; + + @memset(cart.framebuffer, cart.DisplayColor{ + .r = @intFromFloat(ratio * 31), + .g = @intFromFloat(ratio * 63), + .b = @intFromFloat(ratio * 31), + }); +} + +fn check_win() bool { + for (0..3) |i| { + if (state[i][0] != .none and state[i][0] == state[i][1] and state[i][1] == state[i][2]) return true; + if (state[0][i] != .none and state[0][i] == state[1][i] and state[1][i] == state[2][i]) return true; + } + + return (state[0][0] != .none and state[0][0] == state[1][1] and state[1][1] == state[2][2]) or (state[0][2] != .none and state[0][2] == state[1][1] and state[1][1] == state[2][0]); +} diff --git a/samples/feature_test.zig b/samples/feature_test.zig index c389e3d..817a59d 100644 --- a/samples/feature_test.zig +++ b/samples/feature_test.zig @@ -15,7 +15,6 @@ fn read_stored_number() u64 { fn write_stored_number(number: u64) void { var page: [cart.flash_page_size]u8 = undefined; - // @as(*u64, @alignCast(@ptrCast(page[0..8]))).* = number; std.mem.bytesAsSlice(u64, &page)[0] = number; cart.write_flash_page(0, page); } @@ -39,7 +38,8 @@ export fn update() void { var inputs_buf: [128]u8 = undefined; var fbs = std.io.fixedBufferStream(&inputs_buf); - fbs.writer().print("{d}\n", .{read_stored_number()}) catch unreachable; + // ENABLE AT YOUR OWN RISK + // fbs.writer().print("{d}\n", .{read_stored_number()}) catch unreachable; inline for (std.meta.fields(cart.Controls)) |control| { if (comptime !std.mem.eql(u8, control.name, "padding")) { diff --git a/src/cart/api.zig b/src/cart/api.zig index 31ebacf..74ce282 100644 --- a/src/cart/api.zig +++ b/src/cart/api.zig @@ -10,6 +10,9 @@ const builtin = @import("builtin"); pub const screen_width: u32 = 160; pub const screen_height: u32 = 128; +pub const font_width: u32 = 8; +pub const font_height: u32 = 8; + // ┌───────────────────────────────────────────────────────────────────────────┐ // │ │ // │ Memory Addresses │ @@ -63,6 +66,7 @@ pub const Controls = packed struct(u9) { }; pub const controls: *const Controls = @ptrFromInt(base + 0x04); +/// 0-4095 pub const light_level: *const u12 = @ptrFromInt(base + 0x06); /// 5 24-bit color LEDs pub const neopixels: *[5]NeopixelColor = @ptrFromInt(base + 0x08);