From 8d3892b6dea072f692805c9d9450f0d6f559fcf5 Mon Sep 17 00:00:00 2001 From: Jeeves Date: Sat, 5 Apr 2025 16:12:50 -0600 Subject: [PATCH] init --- .gitattributes | 1 + .gitignore | 19 +++++++ build.zig | 74 +++++++++++++++++++++++++++ build.zig.zon | 12 +++++ cert.pem | 21 ++++++++ flake.lock | 78 ++++++++++++++++++++++++++++ flake.nix | 78 ++++++++++++++++++++++++++++ index.html | 55 ++++++++++++++++++++ key.pem | 28 +++++++++++ src/main.zig | 3 ++ src/root.zig | 13 +++++ src/wasm.zig | 134 +++++++++++++++++++++++++++++++++++++++++++++++++ 12 files changed, 516 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 build.zig create mode 100644 build.zig.zon create mode 100644 cert.pem create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 index.html create mode 100644 key.pem create mode 100644 src/main.zig create mode 100644 src/root.zig create mode 100644 src/wasm.zig diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6313b56 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f27e682 --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +# This file is for zig-specific build artifacts. +# If you have OS-specific or editor-specific files to ignore, +# such as *.swp or .DS_Store, put those in your global +# ~/.gitignore and put this in your ~/.gitconfig: +# +# [core] +# excludesfile = ~/.gitignore +# +# Cheers! +# -andrewrk + +.zig-cache/ +zig-cache/ +zig-out/ +/release/ +/debug/ +/build/ +/build-*/ +/docgen_tmp/ diff --git a/build.zig b/build.zig new file mode 100644 index 0000000..bdc469f --- /dev/null +++ b/build.zig @@ -0,0 +1,74 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + // const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const wasm_mod = b.createModule(.{ + .root_source_file = b.path("src/wasm.zig"), + .target = b.resolveTargetQuery(.{ + .cpu_arch = .wasm32, + .os_tag = .freestanding, + }), + // .optimize = .ReleaseSafe, + .optimize = optimize, + }); + + const wasm = b.addExecutable(.{ + .name = "ledbox", + .root_module = wasm_mod, + }); + wasm.entry = .disabled; + wasm.rdynamic = true; + wasm.import_memory = true; + wasm.initial_memory = std.wasm.page_size * 2; + wasm.max_memory = std.wasm.page_size * 2; + wasm.stack_size = std.wasm.page_size; + wasm.global_base = 6560; + b.installArtifact(wasm); + + // const lib_mod = b.createModule(.{ + // .root_source_file = b.path("src/root.zig"), + // .target = target, + // .optimize = optimize, + // }); + + // const exe_mod = b.createModule(.{ + // .root_source_file = b.path("src/main.zig"), + // .target = target, + // .optimize = optimize, + // }); + + // exe_mod.addImport("ledbox_lib", lib_mod); + + // const lib = b.addLibrary(.{ + // .linkage = .static, + // .name = "ledbox", + // .root_module = lib_mod, + // }); + + // b.installArtifact(lib); + + // const exe = b.addExecutable(.{ + // .name = "ledbox", + // .root_module = exe_mod, + // }); + + // b.installArtifact(exe); + + // const lib_unit_tests = b.addTest(.{ + // .root_module = lib_mod, + // }); + + // const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests); + + // const exe_unit_tests = b.addTest(.{ + // .root_module = exe_mod, + // }); + + // const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests); + + // const test_step = b.step("test", "Run unit tests"); + // test_step.dependOn(&run_lib_unit_tests.step); + // test_step.dependOn(&run_exe_unit_tests.step); +} diff --git a/build.zig.zon b/build.zig.zon new file mode 100644 index 0000000..05c41d1 --- /dev/null +++ b/build.zig.zon @@ -0,0 +1,12 @@ +.{ + .name = .ledbox, + .version = "0.0.0", + .fingerprint = 0xdc8a67c307abf09e, + .minimum_zig_version = "0.15.0-dev.129+b84db311d", + + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + }, +} diff --git a/cert.pem b/cert.pem new file mode 100644 index 0000000..34e538b --- /dev/null +++ b/cert.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDazCCAlOgAwIBAgIUJ4me6Svr4LN0POTKXD8iMsjFDeUwDQYJKoZIhvcNAQEL +BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM +GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNTA0MDUxMzE2MzFaFw0zNTA0 +MDMxMzE2MzFaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw +HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQCTD1fUqbGo7NeCsxjm2l1BJy1cuR706bwxyLEcTi3O +mV0dopVpBACol7jQ9W8NTGTxdDVe93sYdEL3A2VBmo7QK4fysUbMnQ3j/nvl+Fgj +Z0eGKFuC4ReKmOwdx+kmIrbitYG0EntY7JArV50n1GkF8C3gN5RQOzmFJ7uhu0JR +6B8z5PJH41SB07JjxBfntNaoET88RjekSM/Y+IvMWwcWOjGHtHgzfkajkKOmYmIU +KRuJmXNF6iJS6SSIbkDcMH2d8kdG62Qdv2LcGjkqSkmx5xnT1UmllKRtWHDAoWyN +gUmhflE3UEdr5MEf9JMzE8wmy9Edwq8Gwd3ulUT80MeVAgMBAAGjUzBRMB0GA1Ud +DgQWBBRUcVjjuIm3d2UNzoFVQMqqSyksLTAfBgNVHSMEGDAWgBRUcVjjuIm3d2UN +zoFVQMqqSyksLTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBY +KQhTPqp7m6sypagfDsbS3roZZx6FVbFC4z7WDCGmjzBbXDD3sHxswPpbsrpPJedW +5ZgihOhwNXtKKz+Ey12+YxN4htVLrxmRnogqs3bz421LI1R4wJ3aCHBrS1vjvrgh +vxykm/hckEmxkQjuEASEt/rwgBmEnV+JGEpifWiid7A5WIfJONEzxo/BciS/+z63 ++UO7IYe2ZX3u4F0dBzpgESAqEeH35lnxmDDkL1DTltihuQgursiIT810d0rnx1nP +C6NJ3B+1D8aDfrHxsTUgOXQI0pxOm1dNQ9MRwvvgFhLHJX5xyv8TC1bwK31Ntq/x +M0H4tXXOtLanPND7O3Md +-----END CERTIFICATE----- diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..68aab57 --- /dev/null +++ b/flake.lock @@ -0,0 +1,78 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1743125403, + "narHash": "sha256-ax5yY7IA9XaO+qHiu2XNs7oivWlD4+YG+G8+VaAl8bE=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "b25d37292b5b6a56a6a508d4632feceb52266333", + "type": "github" + }, + "original": { + "owner": "nixos", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "zig2nix": "zig2nix" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "zig2nix": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1743144458, + "narHash": "sha256-0vS/7AWx4O7vI7kJ156tk+nahxQb+HXd/CiFx6ZPBQE=", + "owner": "Cloudef", + "repo": "zig2nix", + "rev": "de3fa1e6475471ca6c76ba9a88dfa4df2befe13c", + "type": "github" + }, + "original": { + "owner": "Cloudef", + "repo": "zig2nix", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..c127471 --- /dev/null +++ b/flake.nix @@ -0,0 +1,78 @@ +{ + description = "Zig project flake"; + + inputs = { + zig2nix.url = "github:Cloudef/zig2nix"; + }; + + outputs = { zig2nix, ... }: let + flake-utils = zig2nix.inputs.flake-utils; + in (flake-utils.lib.eachDefaultSystem (system: let + # Zig flake helper + # Check the flake.nix in zig2nix project for more options: + # + env = zig2nix.outputs.zig-env.${system} { zig = zig2nix.outputs.packages.${system}.zig-master; }; + in with builtins; with env.pkgs.lib; rec { + # Produces clean binaries meant to be ship'd outside of nix + # nix build .#foreign + packages.foreign = env.package { + src = cleanSource ./.; + + # Packages required for compiling + nativeBuildInputs = with env.pkgs; []; + + # Packages required for linking + buildInputs = with env.pkgs; []; + + # Smaller binaries and avoids shipping glibc. + zigPreferMusl = true; + }; + + # nix build . + packages.default = packages.foreign.override (attrs: { + # Prefer nix friendly settings. + zigPreferMusl = false; + + # Executables required for runtime + # These packages will be added to the PATH + zigWrapperBins = with env.pkgs; []; + + # Libraries required for runtime + # These packages will be added to the LD_LIBRARY_PATH + zigWrapperLibs = attrs.buildInputs or []; + }); + + # For bundling with nix bundle for running outside of nix + # example: https://github.com/ralismark/nix-appimage + apps.bundle = { + type = "app"; + program = "${packages.foreign}/bin/master"; + }; + + # nix run . + apps.default = env.app [] "zig build run -- \"$@\""; + + # nix run .#build + apps.build = env.app [] "zig build \"$@\""; + + # nix run .#test + apps.test = env.app [] "zig build test -- \"$@\""; + + # nix run .#docs + apps.docs = env.app [] "zig build docs -- \"$@\""; + + # nix run .#zig2nix + apps.zig2nix = env.app [] "zig2nix \"$@\""; + + # nix develop + devShells.default = env.mkShell { + # Packages required for compiling, linking and running + # Libraries added here will be automatically added to the LD_LIBRARY_PATH and PKG_CONFIG_PATH + nativeBuildInputs = [] + ++ packages.default.nativeBuildInputs + ++ packages.default.buildInputs + ++ packages.default.zigWrapperBins + ++ packages.default.zigWrapperLibs; + }; + })); +} diff --git a/index.html b/index.html new file mode 100644 index 0000000..a4dea70 --- /dev/null +++ b/index.html @@ -0,0 +1,55 @@ + + + + vLEDMatrix + + + + + + + + diff --git a/key.pem b/key.pem new file mode 100644 index 0000000..b3d32c5 --- /dev/null +++ b/key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCTD1fUqbGo7NeC +sxjm2l1BJy1cuR706bwxyLEcTi3OmV0dopVpBACol7jQ9W8NTGTxdDVe93sYdEL3 +A2VBmo7QK4fysUbMnQ3j/nvl+FgjZ0eGKFuC4ReKmOwdx+kmIrbitYG0EntY7JAr +V50n1GkF8C3gN5RQOzmFJ7uhu0JR6B8z5PJH41SB07JjxBfntNaoET88RjekSM/Y ++IvMWwcWOjGHtHgzfkajkKOmYmIUKRuJmXNF6iJS6SSIbkDcMH2d8kdG62Qdv2Lc +GjkqSkmx5xnT1UmllKRtWHDAoWyNgUmhflE3UEdr5MEf9JMzE8wmy9Edwq8Gwd3u +lUT80MeVAgMBAAECggEACbwhR02jGhibkjuthqf74QfpOSh0A9MZMdPO4vMgldA8 +hBgIJfcth/uLMMyeeQCOn2ve60DHYEYJ93S9FjZXWoZsgxn4eCu0CLIG8kvvTGCr +hmuo1dAgESewXFmodcLPMntrcPKBlyW0ds9fnYxdFhc2d4+WUif+1cax5VySYP46 +qJPHS7RBSk2d7ceJijM8E3tYgzk8ecMpsJ5kNDb0fd61P/5BKsKCbgrSqd34b6F9 +6GsYdvzicbdNGpaks+M68yEMTQw+kenGs5t6i1Sft0brsnDwl0ee+AgxtpT7qe8L +FeSM+51ocUlhIR3IDp90wnkl30Xuu1Vu9SVl/47HHQKBgQDGfLKBWCIe662G58rg +99opgI2IF5j9yCA9LvNDrPNPxLkU4M4ewuLSCtpD70T/uknU7EudRIc2w2KX2aX8 +99LTd0rvM8DZWdwv2Oxzn2FzgQEOeef4+I4QUvFYJJL2CNRubasoNP4XkUjjWEqn +bqki4hFz0MIczTRZShz3/WL0kwKBgQC9q+aLKsAv1GitxaXlG2+KFJtWxJX0dzc4 +QX3T2/LbSOWd1wVwBQ2gWTlA0B3lum8+ypBJa/mfdntFEnuYdbBdhELx05nlBQ1a +s+ZNkbmWaIaq/PheGOao5QySasNAZHQ572BbIxtdRPkTY8Rdya1vx0LP1O5HNXiw +xDwpiTpUNwKBgBmktJ83Vrkt865RNgnYm6vu+toRSmfHOjsNXMpqhsjQfqTu4lJh +FVLTqh1KxVxQogdLO/2mg92UYqGBsgs7rVY6vEipJAowrNzVpr7NYZa9nPhU0Z59 +5kS/ooSjvQTiYSefLZS1O/qpf4Q3vPViu6FRGbYfy2RTMqcyhwmXB/jXAoGBAKix +6JXkixS/Ve8geKFiGn10QIkWxcyt77YgQbqZyNGSb29IXDS6udjhIpPrxnuZqvHt +FhHHcDiNF6xieP0rx5YVWbleG2VfbfY7RV2+e5M0GnqgDoMaoKSdO+ZKAKkX72vL +5SaJ/f+hperB9Ff6VUCyuFDDML6y50pI7r1+qRtXAoGBAKnEgB62mNZsJMcj0Sma +OEsLDDlZifubKnz6eGTyRnTCvQQLB3ZrGtrCuB/491THbwRGHoWfH433jgybkB+t +curB5sOv7Grn6gyc91RPWh/a4RwUWz1ZhW/bCz77idOfukVG4yCvaEdHCrNc7C4U +5pgpK7wpQ+Egl2Vr+1M6Zs0K +-----END PRIVATE KEY----- diff --git a/src/main.zig b/src/main.zig new file mode 100644 index 0000000..13ab026 --- /dev/null +++ b/src/main.zig @@ -0,0 +1,3 @@ +const std = @import("std"); + +pub fn main() !void {} diff --git a/src/root.zig b/src/root.zig new file mode 100644 index 0000000..27d2be8 --- /dev/null +++ b/src/root.zig @@ -0,0 +1,13 @@ +//! By convention, root.zig is the root source file when making a library. If +//! you are making an executable, the convention is to delete this file and +//! start with main.zig instead. +const std = @import("std"); +const testing = std.testing; + +pub export fn add(a: i32, b: i32) i32 { + return a + b; +} + +test "basic add functionality" { + try testing.expect(add(3, 7) == 10); +} diff --git a/src/wasm.zig b/src/wasm.zig new file mode 100644 index 0000000..f869897 --- /dev/null +++ b/src/wasm.zig @@ -0,0 +1,134 @@ +const std = @import("std"); + +const width = 64; +const height = 32; + +var pixel_data = std.mem.zeroes([height * width * 3]u8); + +export fn getPixelDataPointer() [*]u8 { + return @ptrCast(&pixel_data); +} + +const marker_color = Color{ .r = 0xAA, .g = 0x61, .b = 0xB7 }; +const seconds_color = Color{ .r = 0xFF, .g = 0x98, .b = 0x00 }; +const minutes_color = Color{ .r = 0x29, .g = 0xB6, .b = 0xF6 }; +const hours_color = Color{ .r = 0x38, .g = 0x8E, .b = 0x3C }; + +const indicator_from = 6; +const indicator_to = 6; + +const seconds_from = 5; +const seconds_to = 5; + +const minutes_from = 0; +const minutes_to = 4; + +const hours_from = 0; +const hours_to = 3; + +const position = Vector2{ .x = 6, .y = 6 }; + +export fn draw() void { + @memset(&pixel_data, 0); + + for (0..12) |a| { + clockHand(position, indicator_from, indicator_to, (@as(f32, @floatFromInt(a)) / 12.0) * 360.0, marker_color); + } + + const epoch_seconds = std.time.epoch.EpochSeconds{ .secs = timestamp() - std.time.s_per_hour * 6 }; + const day_seconds = epoch_seconds.getDaySeconds(); + + const seconds: f32 = @floatFromInt(day_seconds.getSecondsIntoMinute()); + const minutes: f32 = @floatFromInt(day_seconds.getMinutesIntoHour()); + const hours: f32 = @floatFromInt(day_seconds.getHoursIntoDay()); + + clockHand(position, seconds_from, seconds_to, 270 + (seconds / 60.0) * 360.0, seconds_color); + clockHand(position, minutes_from, minutes_to, 270 + (minutes / 60.0) * 360.0, minutes_color); + clockHand(position, hours_from, hours_to, 270 + (hours / 12.0) * 360.0, hours_color); +} + +fn clockHand(pos: Vector2, from: u8, to: u8, angle: f32, color: Color) void { + const radians = angle * std.math.pi / 180.0; + const x: f32 = @floatFromInt(pos.x); + const y: f32 = @floatFromInt(pos.y); + const from_float: f32 = @floatFromInt(from); + const to_float: f32 = @floatFromInt(to); + line( + .{ + .x = @intFromFloat(@round(x + @cos(radians) * from_float)), + .y = @intFromFloat(@round(y + @sin(radians) * from_float)), + }, + .{ + .x = @intFromFloat(@round(x + @cos(radians) * to_float)), + .y = @intFromFloat(@round(y + @sin(radians) * to_float)), + }, + color, + ); +} + +fn line(from: Vector2, to: Vector2, color: Color) void { + const dx = @abs(@as(i16, @intCast(to.x)) - @as(i16, @intCast(from.x))); + const dy = @abs(@as(i16, @intCast(to.y)) - @as(i16, @intCast(from.y))); + const sx: i2 = if (from.x < to.x) 1 else -1; + const sy: i2 = if (from.y < to.y) 1 else -1; + var v = from; + + if (dx > dy) { + var err = @as(f32, @floatFromInt(dx)) / 2.0; + while (v.x != to.x) { + setPixel(v, color); + err -= @floatFromInt(dy); + if (err < 0) { + v.y = @intCast(@as(i16, @intCast(v.y)) + sy); + err += @floatFromInt(dx); + } + v.x = @intCast(@as(i16, @intCast(v.x)) + sx); + } + } else { + var err = @as(f32, @floatFromInt(dy)) / 2.0; + while (v.y != to.y) { + setPixel(v, color); + err -= @floatFromInt(dx); + if (err < 0) { + v.x = @intCast(@as(i16, @intCast(v.x)) + sx); + err += @floatFromInt(dy); + } + v.y = @intCast(@as(i16, @intCast(v.y)) + sy); + } + } + setPixel(v, color); +} + +inline fn setPixel(pos: Vector2, color: Color) void { + const ptr: *[height][width][3]u8 = @ptrCast(&pixel_data); + ptr[pos.y][pos.x][0] = color.r; + ptr[pos.y][pos.x][1] = color.g; + ptr[pos.y][pos.x][2] = color.b; +} + +inline fn getPixel(pos: Vector2) Color { + const ptr: *[height][width][3]u8 = @ptrCast(&pixel_data); + return .{ + .r = ptr[pos.x][pos.x][0], + .g = ptr[pos.x][pos.x][1], + .b = ptr[pos.x][pos.x][2], + }; +} + +const Color = struct { + r: u8, + g: u8, + b: u8, + + pub const WHITE = Color{ .r = 0xFF, .g = 0xFF, .b = 0xFF }; + pub const BLACK = Color{ .r = 0x00, .g = 0x00, .b = 0x00 }; +}; + +const Vector2 = struct { + x: u8, + y: u8, +}; + +extern fn timestamp() u64; +extern fn printi64(arg: i64) void; +extern fn printf32(arg: f32) void;