const std = @import("std"); // Although this function looks imperative, note that its job is to // declaratively construct a build graph that will be executed by an external // runner. pub fn build(b: *std.Build) void { const initramfs = addNasmFiles(b, AddNasmFilesOptions{ .filename = "src/initramfs.asm", .outputname = "initramfs.bin", .flags = &.{"-f bin"}, }); const kernel = addNasmFiles(b, AddNasmFilesOptions{ .filename = "src/kernel.asm", .outputname = "kernel.bin", .flags = &.{"-f bin"}, }); var floppy = createFloppy(b, "main_floppy.img"); floppy.run = formatFloppyFat12(b, floppy); const img_install = b.addInstallBinFile(floppy.obj, "main_floppy.img"); img_install.step.dependOn(&installImgToFloppy(b, floppy, initramfs, kernel).step); b.getInstallStep().dependOn(&img_install.step); const run = b.addSystemCommand(&.{ "qemu-system-i386", "-fda" }); run.addFileArg(b.path("zig-out/bin/initramfs.img")); const run_step = b.step("run", "run the os in qemu"); run_step.dependOn(&run.step); } const AddNasmFilesOptions = struct { filename: []const u8, outputname: ?[]const u8 = null, flags: []const []const u8 = &.{}, }; const NasmFile = struct { run: *std.Build.Step.Run, obj: std.Build.LazyPath, }; const Floppydisk = NasmFile; fn createFloppy(b: *std.Build, name: []const u8) Floppydisk { const dd = b.addSystemCommand(&.{ "dd", "if=/dev/zero" }); const Floppy = dd.addPrefixedOutputFileArg("of=", name); dd.addArgs(&.{ "bs=512", "count=2880" }); return Floppydisk{ .run = dd, .obj = Floppy, }; } fn formatFloppyFat12(b: *std.Build, floppy: Floppydisk) *std.Build.Step.Run { const mkfs = b.addSystemCommand(&.{ "mkfs.fat", "-F12", "-nNBOS" }); mkfs.addFileArg(floppy.obj); mkfs.step.dependOn(&floppy.run.step); return mkfs; } fn installImgToFloppy(b: *std.Build, floppy: Floppydisk, bootloader: NasmFile, kernel: NasmFile) *std.Build.Step.Run { const dd = b.addSystemCommand(&.{"dd"}); dd.addPrefixedFileArg("if=", bootloader.obj); dd.addPrefixedFileArg("of=", floppy.obj); dd.addArg("conv=notrunc"); dd.step.dependOn(&floppy.run.step); dd.step.dependOn(&bootloader.run.step); const mcopy = b.addSystemCommand(&.{"mcopy"}); mcopy.addPrefixedFileArg("-i", floppy.obj); mcopy.addFileArg(kernel.obj); mcopy.addArg("::kernel.bin"); mcopy.step.dependOn(&dd.step); mcopy.step.dependOn(&kernel.run.step); return mcopy; } // adapted from https://codeberg.org/raddari/zig-nasm-lib.git fn addNasmFiles(b: *std.Build, options: AddNasmFilesOptions) NasmFile { std.debug.assert(!std.fs.path.isAbsolute(options.filename)); const src_file = b.path(options.filename); const output = options.outputname orelse b.fmt("{s}.o", .{std.mem.sliceTo(options.filename, '.')}); const nasm = b.addSystemCommand(&.{"nasm"}); nasm.addArgs(options.flags); nasm.addPrefixedDirectoryArg("-i", b.path("src")); const obj = nasm.addPrefixedOutputFileArg("-o", output); nasm.addFileArg(src_file); return .{ .run = nasm, .obj = obj, }; }