Skip to content

Commit

Permalink
fixes for zig 0.12 (except AVR)
Browse files Browse the repository at this point in the history
  • Loading branch information
190n committed May 10, 2024
1 parent 5a00f0c commit d22556b
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 56 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
zig-cache
zig-out
.vscode
49 changes: 26 additions & 23 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ pub fn build(b: *std.Build) void {
// Standard optimization options allow the person running `zig build` to select
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
// set a preferred release mode, allowing the user to decide how to optimize.
const optimize = b.standardOptimizeOption(.{});
const optimize = b.standardOptimizeOption(.{
.preferred_optimize_mode = .ReleaseSmall,
});

const use_avr_gcc = b.option(
bool,
Expand Down Expand Up @@ -44,23 +46,27 @@ pub fn build(b: *std.Build) void {
.target = target,
.optimize = optimize,
});
native_library.addModule("build_options", options_module);
const native_library_install = b.addInstallLibFile(native_library.getEmittedBin(), "libzip8.a");
native_library.root_module.addImport("build_options", options_module);
const native_library_install = b.addInstallLibFile(
native_library.getEmittedBin(),
b.fmt("libzip8{s}", .{target.result.dynamicLibSuffix()}),
);

const wasm_library = b.addSharedLibrary(.{
const wasm_library = b.addExecutable(.{
.name = "zip8",
// In this case the main source file is merely a path, however, in more
// complicated build scripts, this could be a generated file.
.root_source_file = .{ .path = "src/main.zig" },
.target = .{
.target = b.resolveTargetQuery(.{
.cpu_arch = .wasm32,
.os_tag = .freestanding,
.cpu_features_add = std.Target.wasm.featureSet(&.{.bulk_memory}),
},
}),
.optimize = optimize,
});
wasm_library.entry = .disabled;
wasm_library.root_module.addImport("build_options", options_module);
wasm_library.rdynamic = true;
wasm_library.addModule("build_options", options_module);

const wasm_step = b.step("wasm", "Build WebAssembly library");

Expand Down Expand Up @@ -91,49 +97,46 @@ pub fn build(b: *std.Build) void {
// In this case the main source file is merely a path, however, in more
// complicated build scripts, this could be a generated file.
.root_source_file = .{ .path = "src/main.zig" },
.target = .{
.target = b.resolveTargetQuery(.{
.cpu_arch = .thumb,
.os_tag = .freestanding,
.abi = .eabi,
.cpu_model = .{ .explicit = &std.Target.arm.cpu.cortex_m0plus },
},
}),
.optimize = optimize,
});
m0plus_library.addModule("build_options", options_module);
m0plus_library.root_module.addImport("build_options", options_module);
const m0plus_library_install = b.addInstallLibFile(m0plus_library.getEmittedBin(), "cortex-m0plus/libzip8.a");

// build a static library for AVR
const atmega4809_library = b.addStaticLibrary(.{
.name = "zip8",
.root_source_file = .{ .path = "src/main.zig" },
.target = .{
.target = b.resolveTargetQuery(.{
.cpu_arch = .avr,
.os_tag = .freestanding,
.cpu_model = .{ .explicit = &std.Target.avr.cpu.atmega4809 },
.ofmt = if (use_avr_gcc) .c else null,
},
}),
.optimize = optimize,
});
atmega4809_library.addModule("build_options", options_module);
atmega4809_library.root_module.addImport("build_options", options_module);

const atmega4809_library_file = if (use_avr_gcc) bin: {
// we use this to get the lib_dir from our zig installation as that directory contains zig.h
const zig_lib_dir_cmd = b.addSystemCommand(&.{ "sh", "-c", "$0 env | jq -r .lib_dir", b.zig_exe });
const zig_lib_dir_file = zig_lib_dir_cmd.captureStdOut();

const gcc_cmd = b.addSystemCommand(&.{
"sh",
"-c",
"avr-gcc -c -Wno-incompatible-pointer-types -Wno-builtin-declaration-mismatch -mmcu=atmega4809 $0 -I $(cat $1) $2 -o $3",
"avr-gcc -c -Wno-incompatible-pointer-types -Wno-builtin-declaration-mismatch -mmcu=atmega4809 $0 -I $1 $2 -o $3",
switch (optimize) {
.Debug => "-g",
.ReleaseSafe => "-O3",
.ReleaseFast => "-O3",
.ReleaseSmall => "-Oz",
},
});
gcc_cmd.addFileArg(zig_lib_dir_file);
gcc_cmd.addArg(b.lib_dir);
gcc_cmd.addFileArg(atmega4809_library.getEmittedBin());

const avr_object_path = gcc_cmd.addOutputFileArg("zip8.o");

const ar_cmd = b.addSystemCommand(&.{ "ar", "-rcs" });
Expand All @@ -150,8 +153,8 @@ pub fn build(b: *std.Build) void {
const write_files_step = b.addWriteFiles();
_ = write_files_step.addCopyFile(m0plus_library.getEmittedBin(), "zip8/src/cortex-m0plus/libzip8.a");
_ = write_files_step.addCopyFile(atmega4809_library_file, "zip8/src/atmega4809/libzip8.a");
_ = write_files_step.addCopyFile(std.build.LazyPath.relative("src/zip8.h"), "zip8/src/zip8.h");
_ = write_files_step.addCopyFile(std.build.LazyPath.relative("library.properties"), "zip8/library.properties");
_ = write_files_step.addCopyFile(b.path("src/zip8.h"), "zip8/src/zip8.h");
_ = write_files_step.addCopyFile(b.path("library.properties"), "zip8/library.properties");
const zip_step = b.addSystemCommand(&.{ "sh", "-c", "cd $0; zip -r $1 zip8" });
zip_step.addDirectoryArg(write_files_step.getDirectory());
const zip_output = zip_step.addOutputFileArg("zip8.zip");
Expand All @@ -174,12 +177,12 @@ pub fn build(b: *std.Build) void {
.target = target,
.optimize = optimize,
});
exe_tests.addModule("build_options", options_module);
exe_tests.root_module.addImport("build_options", options_module);

// Similar to creating the run step earlier, this exposes a `test` step to
// the `zig build --help` menu, providing a way for the user to request
// running the unit tests.
const test_step = b.step("test", "Run unit tests");
const test_run_cmd = b.addRunArtifact(exe_tests.step.cast(std.build.Step.Compile).?);
const test_run_cmd = b.addRunArtifact(exe_tests.step.cast(std.Build.Step.Compile).?);
test_step.dependOn(&test_run_cmd.step);
}
22 changes: 16 additions & 6 deletions src/bindings.zig
Original file line number Diff line number Diff line change
Expand Up @@ -107,18 +107,28 @@ export fn zip8CpuSetFlagsNotDirty(cpu: ?*anyopaque) callconv(.C) void {
cpuPtrCast(cpu).flags_dirty = false;
}

fn zip8CpuAlloc() callconv(.C) ?[*]u8 {
return (std.heap.wasm_allocator.alignedAlloc(u8, @alignOf(Cpu), @sizeOf(Cpu)) catch return null).ptr;
export fn zip8CpuGetDrawBytes(cpu: ?*const anyopaque) callconv(.C) usize {
return cpuPtrCast(cpu).draw_bytes_this_frame;
}

fn wasmAlloc(n: usize) callconv(.C) ?[*]u8 {
return (std.heap.wasm_allocator.alignedAlloc(u8, @import("builtin").target.maxIntAlignment(), n) catch return null).ptr;
export fn zip8CpuResetDrawBytes(cpu: ?*anyopaque) callconv(.C) void {
cpuPtrCast(cpu).draw_bytes_this_frame = 0;
}

comptime {
if (@import("builtin").target.isWasm()) {
@export(zip8CpuAlloc, .{ .name = "zip8CpuAlloc" });
@export(wasmAlloc, .{ .name = "wasmAlloc" });
const wasm_only_functions = struct {
fn zip8CpuAlloc() callconv(.C) ?[*]u8 {
return (std.heap.wasm_allocator.alignedAlloc(u8, @alignOf(Cpu), @sizeOf(Cpu)) catch return null).ptr;
}

fn wasmAlloc(n: usize) callconv(.C) ?[*]u8 {
return (std.heap.wasm_allocator.alignedAlloc(u8, @import("builtin").target.maxIntAlignment(), n) catch return null).ptr;
}
};

@export(wasm_only_functions.zip8CpuAlloc, .{ .name = "zip8CpuAlloc" });
@export(wasm_only_functions.wasmAlloc, .{ .name = "wasmAlloc" });
}
}

Expand Down
9 changes: 7 additions & 2 deletions src/cpu.zig
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,20 @@ const log = if (@import("builtin").is_test) struct {
V: [16]u8 = .{0} ** 16,
/// 12-bit register I for indexing memory
I: u12 = 0,
/// 4 KiB of memory
mem: [memory_size]u8 = [_]u8{0} ** memory_size,
/// program counter
pc: u12 = initial_pc,
/// call stack
stack: std.BoundedArray(u12, 16),
/// random number generator to use
rand: std.rand.DefaultPrng,

/// 4 KiB of memory
mem: [memory_size]u8 = .{0x00} ** memory_size,
/// track which memory has not been synchronized to clients
mem_dirty: [memory_size / 8]u8 = .{0xff} ** (memory_size / 8),

draw_bytes_this_frame: usize = 0,

/// display is 64x32 stored row-major
display: [display_width * display_height / 8]u8 = .{0} ** (display_width * display_height / 8),
/// whether the contents of the screen have changed since the last time this flag was set to false
Expand Down
30 changes: 22 additions & 8 deletions src/instruction.zig
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ opcode: u16,

const testing_seed = 1337;

const draw_log = std.log.scoped(.draw);

/// split up an opcode into useful parts
pub fn decode(opcode: u16) Instruction {
const nibbles = [4]u4{
Expand Down Expand Up @@ -113,6 +115,8 @@ fn op00EX(self: Instruction, cpu: *Cpu) !?u12 {
0x00E0 => {
@memset(&cpu.display, 0);
cpu.display_dirty = true;
draw_log.info("clear", .{});
cpu.draw_bytes_this_frame += 4;
return null;
},
0x00EE => {
Expand Down Expand Up @@ -639,20 +643,16 @@ test "CXNN random" {
try std.testing.expectEqual(@as(u8, 0x00), cpu.V[0x0]);
}

export fn draw(ptr: ?*anyopaque, xReg: u8, yReg: u8, rows: u8) callconv(.C) void {
const cpu: *Cpu = @alignCast(@ptrCast(ptr.?));
const inst = Instruction.decode(0xd000 | (@as(u16, xReg) << 8) | (yReg << 4) | rows);
_ = inst.exec(cpu) catch unreachable;
cpu.pc +%= 2;
}

/// DXYN: draw an 8xN sprite from memory starting at I at (VX, VY); set VF to 1 if any pixel was
/// turned off, 0 otherwise
fn opDraw(self: Instruction, cpu: *Cpu) !?u12 {
cpu.V[0xF] = 0;
const sprite: []const u8 = cpu.mem[(cpu.I)..(cpu.I + self.low4)];
const sprite: []const u8 = cpu.mem[cpu.I..][0..self.low4];
const x_start = cpu.V[self.regX] % Cpu.display_width;
const y_start = cpu.V[self.regY] % Cpu.display_height;

var any_bytes_dirty = false;

for (sprite, 0..) |row, y_sprite| {
for (0..8) |x_sprite| {
const pixel: u1 = @truncate(row >> @intCast(7 - x_sprite));
Expand All @@ -668,8 +668,21 @@ fn opDraw(self: Instruction, cpu: *Cpu) !?u12 {
cpu.invertPixel(x, y);
cpu.display_dirty = true;
}

const mem_index = y_sprite + cpu.I;
if ((cpu.mem_dirty[mem_index / 8] >> @truncate(mem_index)) & 0x01 != 0) {
any_bytes_dirty = true;
cpu.mem_dirty[mem_index / 8] ^= (@as(u8, 1) << @truncate(mem_index));
}
}
}

if (any_bytes_dirty) {
draw_log.info("untaint: [{}, {})", .{ cpu.I, cpu.I + self.low4 });
cpu.draw_bytes_this_frame += self.low4;
}
draw_log.info("sprite: {} rows at ({}, {})", .{ sprite.len, x_start, y_start });
cpu.draw_bytes_this_frame += 4;
return null;
}

Expand Down Expand Up @@ -934,6 +947,7 @@ fn opStore(self: Instruction, cpu: *Cpu) !?u12 {
for (0..(@as(u8, self.regX) + 1)) |offset| {
cpu.mem[cpu.I] = cpu.V[offset];
cpu.I +%= 1;
cpu.mem_dirty[cpu.I / 8] |= (@as(u8, 1) << @truncate(cpu.I));
}
return null;
}
Expand Down
35 changes: 18 additions & 17 deletions src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,26 @@ pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace, ret_
while (true) {}
}

pub const std_options = struct {
pub const log_level = .info;
pub fn logFn(
comptime level: std.log.Level,
comptime scope: @Type(.EnumLiteral),
comptime format: []const u8,
args: anytype,
) void {
const level_prefix = comptime level.asText();
const prefix = comptime level_prefix ++ switch (scope) {
.default => ": ",
else => " (" ++ @tagName(scope) ++ "): ",
};

pub fn logFn(
comptime level: std.log.Level,
comptime scope: @TypeOf(.EnumLiteral),
comptime format: []const u8,
args: anytype,
) void {
const level_prefix = comptime level.asText();
const prefix = comptime level_prefix ++ switch (scope) {
.default => ": ",
else => " (" ++ @tagName(scope) ++ "): ",
};
var buf: [1024:0]u8 = undefined;
const string = std.fmt.bufPrintZ(&buf, prefix ++ format, args) catch &buf;
zip8Log(string.ptr, string.len);
}

var buf: [1024:0]u8 = undefined;
const string = std.fmt.bufPrintZ(&buf, prefix ++ format, args) catch &buf;
zip8Log(string.ptr, string.len);
}
pub const std_options = std.Options{
.log_level = .info,
.logFn = logFn,
};

comptime {
Expand Down
10 changes: 10 additions & 0 deletions web-host/src/cpu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ interface Zip8Exports {
zip8CpuFlagsAreDirty(cpuPtr: number): boolean;
zip8CpuSetFlagsNotDirty(cpuPtr: number): void;
zip8CpuAlloc(): number;
zip8CpuGetDrawBytes(cpuPtr: number): number;
zip8CpuResetDrawBytes(cpuPtr: number): void;
wasmAlloc(size: number): number;
}

Expand Down Expand Up @@ -202,4 +204,12 @@ export default class CPU {
}
return flags;
}

getDrawBytes(): number {
return this.exports.zip8CpuGetDrawBytes(this.cpuPtr);
}

resetDrawBytes() {
this.exports.zip8CpuResetDrawBytes(this.cpuPtr);
}
}
4 changes: 4 additions & 0 deletions web-host/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ async function run(rom: ArrayBuffer) {
cpu.setKeys(keys);
}

cpu.resetDrawBytes();

if (!cpu.isWaitingForKey()) {
for (let i = 0; i < instructionsPerTick && !halt; i++) {
try {
Expand All @@ -116,6 +118,8 @@ async function run(rom: ArrayBuffer) {
localStorage.setItem(key, JSON.stringify(flags));
}
}

console.log(cpu.getDrawBytes());
}

tick();
Expand Down

0 comments on commit d22556b

Please sign in to comment.