From 2c44bea9b1332cfedf77a9ea1f04a157f2c301cb Mon Sep 17 00:00:00 2001 From: Dario48 Date: Tue, 15 Jul 2025 21:15:11 +0200 Subject: [PATCH 1/6] added readme --- README.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..7a6beca --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +an operating system made in nasm assembly and (in the future) zig, using the zig build system. +this project is based on [this video series](https://www.youtube.com/playlist?list=PLFjM7v6KGMpiH2G-kT781ByCNC_0pKpPN) by [nanobyte](https://www.youtube.com/@nanobyte-dev) + +# HOWTO + +## dependencies + +this project requires the nasm assembler, mtools for floppy disk generation and zig to build. +this project uses qemu as the default emulator, but this is optional + +## building + +```bash +git clone https://dario48.site/git/Dario48/zos.git +cd zos +zig build +``` + +## running + +- with the default emulator + +```bash +cd zos +zig build run +``` + +- with a different emulator + +```bash +cd zos +emulator_cmd zig-out/bin/main_floppy.img +``` From da1a2345a95d5c3cce22caead2cd4bcea6c3877d Mon Sep 17 00:00:00 2001 From: Dario48 Date: Thu, 17 Jul 2025 04:37:30 +0200 Subject: [PATCH 2/6] adding reference impementation for reading files from fat --- build.zig | 57 +++++++++--- src/reference_implementations/fat.zig | 117 +++++++++++++++++++++++++ src/reference_implementations/test.txt | 3 + 3 files changed, 167 insertions(+), 10 deletions(-) create mode 100644 src/reference_implementations/fat.zig create mode 100644 src/reference_implementations/test.txt diff --git a/build.zig b/build.zig index 712b389..e4051ee 100644 --- a/build.zig +++ b/build.zig @@ -18,16 +18,40 @@ pub fn build(b: *std.Build) void { var floppy = createFloppy(b, "main_floppy.img"); floppy.run = formatFloppyFat12(b, floppy); + var installToFloppy = installImgToFloppy(b, floppy, initramfs, &.{ + NamedFile{ .file = .{ .runFile = kernel }, .name = "::kernel.bin" }, + NamedFile{ .file = .{ .simpleFile = b.path("src/reference_implementations/test.txt") }, .name = "::test.txt" }, + }); + const img_install = b.addInstallBinFile(floppy.obj, "main_floppy.img"); - img_install.step.dependOn(&installImgToFloppy(b, floppy, initramfs, kernel).step); + img_install.step.dependOn(&installToFloppy); b.getInstallStep().dependOn(&img_install.step); + const target = b.standardTargetOptions(.{}); + const fat_reference_mod = b.createModule(.{ + .root_source_file = b.path("src/reference_implementations/fat.zig"), + .target = target, + .link_libc = true, + }); + const run = b.addSystemCommand(&.{ "qemu-system-i386", "-fda" }); run.addFileArg(floppy.obj); const run_step = b.step("run", "run the os in qemu"); run_step.dependOn(&run.step); + + const fat_reference = b.addExecutable(.{ + .root_module = fat_reference_mod, + .name = "fat_reference_implementation", + .use_lld = false, + .use_llvm = false, + }); + + const fat_install = b.addInstallArtifact(fat_reference, .{}); + + const build_references = b.step("references", "build the reference implementations"); + build_references.dependOn(&fat_install.step); } const AddNasmFilesOptions = struct { @@ -43,6 +67,11 @@ const NasmFile = struct { const Floppydisk = NasmFile; +const NamedFile = struct { + file: union(enum) { runFile: NasmFile, simpleFile: std.Build.LazyPath }, + name: []const u8, +}; + fn createFloppy(b: *std.Build, name: []const u8) Floppydisk { const dd = b.addSystemCommand(&.{ "dd", "if=/dev/zero" }); const Floppy = dd.addPrefixedOutputFileArg("of=", name); @@ -63,7 +92,7 @@ fn formatFloppyFat12(b: *std.Build, floppy: Floppydisk) *std.Build.Step.Run { return mkfs; } -fn installImgToFloppy(b: *std.Build, floppy: Floppydisk, bootloader: NasmFile, kernel: NasmFile) *std.Build.Step.Run { +fn installImgToFloppy(b: *std.Build, floppy: Floppydisk, bootloader: NasmFile, additional_files: []const NamedFile) std.Build.Step { const dd = b.addSystemCommand(&.{"dd"}); dd.addPrefixedFileArg("if=", bootloader.obj); dd.addPrefixedFileArg("of=", floppy.obj); @@ -71,14 +100,22 @@ fn installImgToFloppy(b: *std.Build, floppy: Floppydisk, bootloader: NasmFile, k 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; + var mcopy_step = std.Build.Step.init(.{ .name = "mcopy", .owner = b, .id = .custom }); + mcopy_step.dependOn(&dd.step); + for (additional_files) |f| { + const mcopy = b.addSystemCommand(&.{"mcopy"}); + mcopy.addPrefixedFileArg("-i", floppy.obj); + switch (f.file) { + .runFile => |file| { + mcopy.addFileArg(file.obj); + mcopy_step.dependOn(&file.run.step); + }, + .simpleFile => |file| mcopy.addFileArg(file), + } + mcopy.addArg(f.name); + mcopy_step.dependOn(&mcopy.step); + } + return mcopy_step; } // adapted from https://codeberg.org/raddari/zig-nasm-lib.git diff --git a/src/reference_implementations/fat.zig b/src/reference_implementations/fat.zig new file mode 100644 index 0000000..61d7a0f --- /dev/null +++ b/src/reference_implementations/fat.zig @@ -0,0 +1,117 @@ +const std = @import("std"); + +const Errors = error{ InvalidSyntax, ReadSectors, FileNotFound }; + +var buffer: [1024]u8 = undefined; + +var g_Fat: []u8 = undefined; + +const BootSector = extern struct { + BootJumpInstruction: [3]u8 align(1), + OemIdentifier: [8]u8 align(1), + BytesPerSector: u16 align(1), + SectorsPerCluster: u8 align(1), + ReservedSectors: u16 align(1), + FatCount: u8 align(1), + DirEntryCount: u16 align(1), + TotalSectors: u16 align(1), + MediaDescriptorType: u8 align(1), + SectorsPerFat: u16 align(1), + SectorsPerTrack: u16 align(1), + Heads: u16 align(1), + HiddenSectors: u32 align(1), + LargeSectorCount: u32 align(1), + + // ebr + DriveNumber: u8 align(1), + _Reserved: u8 align(1), + Signature: u8 align(1), + VolumeId: u32 align(1), + VolumeLaber: [11]u8 align(1), + SystemId: [8]u8 align(1), +}; + +var g_BootSector: BootSector = undefined; + +const DirectoryEntry = extern struct { + Name: [11]u8 align(1), + Attributes: u8 align(1), + _Reserved: u8 align(1), + CreatedTimeTenths: u8 align(1), + CreatedTime: u16 align(1), + CreatedDate: u16 align(1), + AccessedDate: u16 align(1), + FirstClusterHigh: u16 align(1), + ModifiedTime: u16 align(1), + ModifiedDate: u16 align(1), + FirstClusterLow: u16 align(1), + Size: u32 align(1), +}; + +var g_RootDirectory: []DirectoryEntry = undefined; + +fn readStruct(reader: *std.fs.File.Reader, T: type) !T { + var ret: [@sizeOf(T)]u8 = undefined; + _ = try reader.*.read(&ret); + return @bitCast(ret); +} + +fn readBootSector(disk: *std.fs.File) !void { + var reader = disk.*.reader(&buffer); + g_BootSector = try readStruct(&reader, BootSector); +} + +fn readSectors(disk: *std.fs.File, lba: u32, count: u32, bufferOut: *[]anyopaque) !void { + const dest: []u8 = try std.heap.smp_allocator.alloc(u8, g_BootSector.BytesPerSector * count); + defer std.heap.smp_allocator.free(dest); + var stream = disk.readerStreaming(&buffer); + try stream.seekTo(lba * g_BootSector.BytesPerSector); + if (try stream.readStreaming(dest) != g_BootSector.BytesPerSector * count) return Errors.ReadSectors; + @memcpy(@as(*[]u8, @ptrCast(bufferOut)).*, dest); +} + +fn readFat(disk: *std.fs.File) !void { + g_Fat = try std.heap.smp_allocator.alloc(u8, g_BootSector.SectorsPerFat * g_BootSector.BytesPerSector); + errdefer std.heap.smp_allocator.free(g_Fat); + try readSectors(disk, g_BootSector.ReservedSectors, g_BootSector.SectorsPerFat, &g_Fat); +} + +fn readRootDirectory(disk: std.fs.File) !void { + const lba: u32 = g_BootSector.ReservedSectors + g_BootSector.SectorsPerFat + g_BootSector.FatCount; + const size: u32 = @sizeOf(DirectoryEntry) * g_BootSector.DirEntryCount; + const sectors: u32 = (size / g_BootSector.BytesPerSector); + if (@rem(size, g_BootSector.BytesPerSector > 0)) + sectors += 1; + + g_RootDirectory = try std.heap.smp_allocator.alloc(DirectoryEntry, sectors * g_BootSector.BytesPerSector); + errdefer std.heap.smp_allocator.free(g_RootDirectory); + try readSectors(disk, lba, sectors, &g_RootDirectory); +} + +fn findFile(name: []const u8) !*DirectoryEntry { + for (g_RootDirectory, 0..) |Entry, i| + if (std.mem.eql(u8, Entry.Name, name)) + return &g_RootDirectory[i]; + return Errors.FileNotFound; +} + +pub fn main() !void { + if (std.os.argv.len < 3) { + std.log.err("syntax: {s} ", .{std.os.argv[0]}); + return Errors.InvalidSyntax; + } + + var disk = try std.fs.cwd().openFileZ(std.os.argv[1], .{ .mode = .read_only }); + + _ = &disk; + try readBootSector(&disk); + + try readFat(&disk); + defer std.heap.smp_allocator.free(g_Fat); + + try readRootDirectory(disk); + defer std.heap.smp_allocator.free(g_RootDirectory); + + const fileEntry = try findFile(std.os.argv[2]); + _ = fileEntry; +} diff --git a/src/reference_implementations/test.txt b/src/reference_implementations/test.txt new file mode 100644 index 0000000..20e13f9 --- /dev/null +++ b/src/reference_implementations/test.txt @@ -0,0 +1,3 @@ +testing file +aabaacaadaaeaafaagaahaaiaajaakaalaamaanaaoaapaaqaaraasaataauaavaawaaxaayaaz +tstngfl From c7ec508dbabe1a11c180e37b0cfce6a1d9eab6ea Mon Sep 17 00:00:00 2001 From: Dario48 Date: Thu, 17 Jul 2025 04:40:04 +0200 Subject: [PATCH 3/6] updated zig version --- build.zig.zon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.zig.zon b/build.zig.zon index 365621b..f29dbdf 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -28,7 +28,7 @@ // Tracks the earliest Zig version that the package considers to be a // supported use case. - .minimum_zig_version = "0.14.1", + .minimum_zig_version = "0.15.0", // This field is optional. // Each dependency must either provide a `url` and `hash`, or a `path`. From e1926aec8ac32a811f0942b122aeaf04342ac0a1 Mon Sep 17 00:00:00 2001 From: Dario48 Date: Wed, 23 Jul 2025 17:24:23 +0200 Subject: [PATCH 4/6] initramfs -> bootloader, kernel can now be loaded, FAT can now be looked at throug reference implementation --- build.zig | 83 ++---- src/bootloader.asm | 381 ++++++++++++++++++++++++++ src/initramfs.asm | 217 --------------- src/kernel.asm | 34 +-- src/reference_implementations/fat.zig | 96 +++++-- 5 files changed, 495 insertions(+), 316 deletions(-) create mode 100644 src/bootloader.asm delete mode 100644 src/initramfs.asm diff --git a/build.zig b/build.zig index e4051ee..78a2e05 100644 --- a/build.zig +++ b/build.zig @@ -1,12 +1,13 @@ const std = @import("std"); +const LazyPath = std.Build.LazyPath; // 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", + const bootloader = addNasmFiles(b, AddNasmFilesOptions{ + .filename = "src/bootloader.asm", + .outputname = "bootloader.bin", .flags = &.{"-f bin"}, }); const kernel = addNasmFiles(b, AddNasmFilesOptions{ @@ -15,16 +16,16 @@ pub fn build(b: *std.Build) void { .flags = &.{"-f bin"}, }); - var floppy = createFloppy(b, "main_floppy.img"); - floppy.run = formatFloppyFat12(b, floppy); + const floppy = createFloppy(b, "main_floppy.img"); + formatFloppyFat12(b, floppy); - var installToFloppy = installImgToFloppy(b, floppy, initramfs, &.{ - NamedFile{ .file = .{ .runFile = kernel }, .name = "::kernel.bin" }, - NamedFile{ .file = .{ .simpleFile = b.path("src/reference_implementations/test.txt") }, .name = "::test.txt" }, + var installToFloppy = installImgToFloppy(b, floppy, bootloader, &.{ + NamedFile{ .file = kernel, .name = "::kernel.bin" }, + NamedFile{ .file = b.path("src/reference_implementations/test.txt"), .name = "::test.txt" }, }); - const img_install = b.addInstallBinFile(floppy.obj, "main_floppy.img"); - img_install.step.dependOn(&installToFloppy); + const img_install = b.addInstallBinFile(floppy, "main_floppy.img"); + img_install.step.dependOn(&installToFloppy.step); b.getInstallStep().dependOn(&img_install.step); @@ -35,8 +36,7 @@ pub fn build(b: *std.Build) void { .link_libc = true, }); - const run = b.addSystemCommand(&.{ "qemu-system-i386", "-fda" }); - run.addFileArg(floppy.obj); + const run = b.addSystemCommand(&.{ "qemu-system-i386", "-fda", "zig-out/bin/main_floppy.img" }); const run_step = b.step("run", "run the os in qemu"); run_step.dependOn(&run.step); @@ -60,66 +60,45 @@ const AddNasmFilesOptions = struct { flags: []const []const u8 = &.{}, }; -const NasmFile = struct { - run: *std.Build.Step.Run, - obj: std.Build.LazyPath, -}; - -const Floppydisk = NasmFile; - const NamedFile = struct { - file: union(enum) { runFile: NasmFile, simpleFile: std.Build.LazyPath }, + file: std.Build.LazyPath, name: []const u8, }; -fn createFloppy(b: *std.Build, name: []const u8) Floppydisk { +fn createFloppy(b: *std.Build, name: []const u8) LazyPath { const dd = b.addSystemCommand(&.{ "dd", "if=/dev/zero" }); const Floppy = dd.addPrefixedOutputFileArg("of=", name); + dd.has_side_effects = true; dd.addArgs(&.{ "bs=512", "count=2880" }); - return Floppydisk{ - .run = dd, - .obj = Floppy, - }; + return Floppy; } -fn formatFloppyFat12(b: *std.Build, floppy: Floppydisk) *std.Build.Step.Run { +fn formatFloppyFat12(b: *std.Build, floppy: LazyPath) void { const mkfs = b.addSystemCommand(&.{ "mkfs.fat", "-F12", "-nNBOS" }); - mkfs.addFileArg(floppy.obj); - - mkfs.step.dependOn(&floppy.run.step); - - return mkfs; + mkfs.addFileArg(floppy); } -fn installImgToFloppy(b: *std.Build, floppy: Floppydisk, bootloader: NasmFile, additional_files: []const NamedFile) std.Build.Step { +fn installImgToFloppy(b: *std.Build, floppy: LazyPath, bootloader: LazyPath, additional_files: []const NamedFile) *std.Build.Step.Run { const dd = b.addSystemCommand(&.{"dd"}); - dd.addPrefixedFileArg("if=", bootloader.obj); - dd.addPrefixedFileArg("of=", floppy.obj); + dd.addPrefixedFileArg("if=", bootloader); + dd.addPrefixedFileArg("of=", floppy); dd.addArg("conv=notrunc"); - dd.step.dependOn(&floppy.run.step); - dd.step.dependOn(&bootloader.run.step); - var mcopy_step = std.Build.Step.init(.{ .name = "mcopy", .owner = b, .id = .custom }); - mcopy_step.dependOn(&dd.step); + var last_step = dd; for (additional_files) |f| { const mcopy = b.addSystemCommand(&.{"mcopy"}); - mcopy.addPrefixedFileArg("-i", floppy.obj); - switch (f.file) { - .runFile => |file| { - mcopy.addFileArg(file.obj); - mcopy_step.dependOn(&file.run.step); - }, - .simpleFile => |file| mcopy.addFileArg(file), - } + mcopy.addPrefixedFileArg("-i", floppy); + mcopy.addFileArg(f.file); mcopy.addArg(f.name); - mcopy_step.dependOn(&mcopy.step); + mcopy.step.dependOn(&last_step.step); + last_step = mcopy; } - return mcopy_step; + return last_step; } // adapted from https://codeberg.org/raddari/zig-nasm-lib.git -fn addNasmFiles(b: *std.Build, options: AddNasmFilesOptions) NasmFile { +fn addNasmFiles(b: *std.Build, options: AddNasmFilesOptions) LazyPath { 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, '.')}); @@ -129,9 +108,7 @@ fn addNasmFiles(b: *std.Build, options: AddNasmFilesOptions) NasmFile { nasm.addPrefixedDirectoryArg("-i", b.path("src")); const obj = nasm.addPrefixedOutputFileArg("-o", output); nasm.addFileArg(src_file); + nasm.has_side_effects = true; - return .{ - .run = nasm, - .obj = obj, - }; + return obj; } diff --git a/src/bootloader.asm b/src/bootloader.asm new file mode 100644 index 0000000..3ac094e --- /dev/null +++ b/src/bootloader.asm @@ -0,0 +1,381 @@ +; vim: ft=nasm +org 0x7C00 +bits 16 + + +%define ENDL 0x0D, 0x0A + + +; +; FAT12 Header +; +jmp short start +nop + +bdb_oem: db "mkfs.fat" ; 8 bytes +bdb_bytes_per_sector: dw 512 +bdb_sectors_per_cluster: db 1 +bdb_reserved_sectors: dw 1 +bdb_fat_count: db 2 +bdb_dir_entries_count: dw 0E0h +bdb_total_sectors: dw 2880 ; 2880 * 512 = 1.44MB +bdb_media_descriptor_type: db 0F0h ; F0 = 3.5" floppy disk +bdb_sectors_per_fat: dw 9 ; 9 sectors/fat +bdb_sectors_per_track: dw 18 +bdb_heads: dw 2 +bdb_hidden_sectors: dd 0 +bdb_large_sector_count: dd 0 + +; Extended boot record +ebr_drive_number: db 0 ; 0x00 floppy, 0x80 hdd, useless + db 0 ; reserved +ebr_signature: db 29h +ebr_volume_id: db 68h, 6Fh, 6Dh, 6Fh ; serial number, value doesn't matter +ebr_volume_label: db 'zos disk' ; 11 bytes, padded with spaces +ebr_system_id: db 'FAT12 ' ; 8 bytes + + + + + +start: + ; setup data segments + mov ax, 0 ; use ax as and intermediary as we can't write to es/ds directly + mov ds, ax + mov es, ax + + ; setup stack + mov ss, ax + mov sp, 0x7C00 ; stack grows downward from where we are loaded in memory + + ; some BIOS start us at 07C00:0000 instead of 0000:07C0, make sure we are in the right place + + push es + push word .after + retf + +.after: + + + + mov [ebr_drive_number], dl + + + mov si, msg_loading + call echo + + + ; read drive parameters (you never know) + push es + mov ah, 08h + int 13h + jc floppy_error + pop es + + and cl, 0x3F ; remove top 2 bites + xor ch, ch + mov [bdb_sectors_per_track], cx ; sectors count + + inc dh + mov [bdb_heads], dh ; head count + + ; read FAT root directory + ; compute LBA of root directory = reserved + fats * sectors_per_fat + mov ax, [bdb_sectors_per_fat] + mov bl, [bdb_fat_count] + xor bh, bh + mul bx ; ax = fats * sectors_per_fat + add ax, [bdb_reserved_sectors] ; ax = LBA of root dir + push ax + + ; compute size of root directory = (32 * number_of_entries) / bytes_per_sector + mov ax, [bdb_dir_entries_count] + shl ax, 5 ; ax *= 32 + xor dx, dx + div word [bdb_bytes_per_sector] ; number of sectors we need to read + + test dx, dx ; if != 0 add 1 + jz .root_dir_after + inc ax ; divistion reminder != 0, add 1 + +.root_dir_after: + + ; read root dir + mov cl, al ; cl = number of sectors to read = size of root directory + pop ax ; ax = LBA of root directory + mov dl, [ebr_drive_number] ; dl = drive number (as saved previously) + mov bx, buffer ; es:bx = buffer + call disk_read + + ; search for kernel.bin + xor bx, bx + mov di, buffer + +.search_kernel: + mov si, file_kernel_bin + mov cx, 11 ; compare up to 11 characters + push di + repe cmpsb ; repe: repeat until cx = 0 + ; cmpsb: compare ds:si to es:di, they are also incremented or decremented based on a flag + pop di + je .found_kernel + + add di, 32 + inc bx + cmp bx, [bdb_dir_entries_count] + jl .search_kernel + + ; kernel not found + jmp kernel_not_found_error + +.found_kernel: + + ; di should point to the adress of the entry + mov ax, [di + 26] ; first logical cluster (offset 26) + mov [kernel_cluster], ax + + ; load FAT from disk into memory + mov ax, [bdb_reserved_sectors] + mov bx, buffer + mov cl, [bdb_sectors_per_fat] + mov dl, [ebr_drive_number] + call disk_read + + ; read kernel and process FAT chain + mov bx, KERNEL_LOAD_SEGMENT + mov es, bx + mov bx, KERNEL_LOAD_OFFSET + +.load_kernel_loop: + + ; read next cluster + mov ax, [kernel_cluster] + + + add ax, 31 ; tmp hardcoding ; first cluster = (kernel_cluster - 2) * sectors_per_cluster + start_sector + ; start_sector = reserved + fats + root_directory_size = 1 + 18 + 134 = 33 + mov cl, 1 + mov dl, [ebr_drive_number] + call disk_read + + ; can overflow, risky!!!! + add bx, [bdb_bytes_per_sector] + + ; cumpute location of next cluster + mov ax, [kernel_cluster] + mov cx, 3 + mul cx + mov cx, 2 + div cx ; ax = index of entry in FAT, dx = cluster mod 2 + + mov si, buffer + add si, ax + mov ax, [ds:si] ; read entry from FAT table at index ax + + or dx, dx + jz .even + +.odd: + shr ax, 4 + jmp .next_cluster_after + +.even: + and ax, 0x0FFF + +.next_cluster_after: + cmp ax, 0x0FF8 ; end of chain + jae .read_finish + + mov [kernel_cluster], ax + jmp .load_kernel_loop + +.read_finish: + + ; jump to kernel + mov dl, [ebr_drive_number] ; boot device in dl + + mov ax, KERNEL_LOAD_SEGMENT ; set segment registers + mov ds, ax + mov es, ax + + jmp KERNEL_LOAD_SEGMENT:KERNEL_LOAD_OFFSET + + jmp wait_key_and_reboot ; should never happen + + cli + hlt + +; errors + + + + +floppy_error: + mov si, msg_read_failed + call echo + jmp wait_key_and_reboot + +kernel_not_found_error: + mov si, msg_kernel_not_found + call echo + jmp wait_key_and_reboot + +wait_key_and_reboot: + mov ah, 0 + int 16h ; wait for keypress + jmp 0FFFFh:0 ; jump to start of bios, should reboot + +.halt: + cli ; disable interrupts, this way we shouldn't be able to get out of halt + hlt + + +; +; echo: +; print something to the screen +; - ds:si points to string +; +echo: + ; save the registers we want to modify + push si + push ax + push bx + +.loop: + lodsb ; load byte from ds:si to al + or al, al ; check if next char is null + jz .done + + mov ah, 0x0E + mov bh, 0 + int 0x10 + + jmp .loop + +.done: + pop bx + pop ax + pop si + ret + + + + +; +; args: +; ax = lba adress +; return: +; cx[0..5] = sector number +; cx[6-15] = cilinder number +; dh: head +; + + + +lba_to_chs: + + push ax + push dx + + xor dx, dx ; clear dw + div word [bdb_sectors_per_track] ; ax = LBA / SectorsPerTrack + ; dx = LBA % SectorsPerTrack + + inc dx + mov cx, dx ; cx = sector + + xor dx, dx ; clear dx + div word [bdb_heads] ; ax = (LBA / SectorsPerTrack) / Heads = cylinder + ; dx = (LBA / SectorsPerTrack) % Heads = head + mov dh, dl + mov ch, al + shl ah, 6 + or cl, ah ; put upper 2 bits of cylinder in CL + + pop ax + mov dl, al + pop ax + ret + + +; +; args: +; ax = LBA adress +; cl = number of sectors to read +; dl = drive number +; es:bx = pointer to where to store the data +; + +disk_read: + + push ax + push bx + push cx + push dx + push di + + push cx ; cx will be overridden + call lba_to_chs + pop ax ; al = number of sectors to read + + mov ah, 02h + mov di, 3 ; hoe many times to try + +.retry: + pusha + stc ; set carry flag + int 13h ; if no carry flag => it work + jnc .done + + ; read fail + popa + call disk_reset + + dec di + test di, di + jnz .retry + +.fail: + ; all attempts exausted + jmp floppy_error + +.done: + popa + + pop di + pop dx + pop cx + pop bx + pop ax + ret + + + +; arg: dl = drive number + + + +disk_reset: + pusha + mov ah, 0 + stc ; set carry + int 13h + jc floppy_error + popa + ret + + +msg_loading: db 'starting zos', ENDL, 0 +msg_read_failed: db 'error on floppy read', ENDL, 0 +msg_kernel_not_found: db 'kernel.bin not found', ENDL, 0 +file_kernel_bin: db 'KERNEL BIN' +kernel_cluster: dw 0 + +KERNEL_LOAD_SEGMENT equ 0x2000 +KERNEL_LOAD_OFFSET equ 0 + + +times 510-($-$$) db 0 +dw 0AA55h + +buffer: diff --git a/src/initramfs.asm b/src/initramfs.asm deleted file mode 100644 index b2e02b3..0000000 --- a/src/initramfs.asm +++ /dev/null @@ -1,217 +0,0 @@ -; vim: ft=nasm -org 0x7C00 -bits 16 - - -%define ENDL 0x0D, 0x0A - -; -; FAT12 Header -; -jmp short start -nop - -bdb_oem: db "mkfs.fat" -bdb_bytes_per_sector: dw 512 -bdb_sectors_per_cluster: db 1 -bdb_reserved_sectors: dw 1 -bdb_fat_count: db 2 -bdb_dir_entries_count: dw 0E0h -bdb_total_sectors: dw 2880 -bdb_media_descriptor_type: db 0F0h -bdb_sectors_per_fat: dw 9 -bdb_sectors_per_track: dw 18 -bdb_heads: dw 2 -bdb_hidden_sectors: dd 0 -bdb_large_sector_count: dd 0 - -; Extended boot record -ebr_drive_number: db 0 - db 0 -ebr_signature: db 29h -ebr_volume_id: db 68h, 6Fh, 6Dh, 6Fh -ebr_volume_laber: db 'zos disk ' -ebr_system_id: db 'FAT12 ' - - - - -start: - jmp main - -; -; echo: -; print something to the screen -; - ds:si points to string -; -echo: - ; save the registers we want to modify - push si - push ax - push bx - - mov ah, 0xe - mov bh, 0 - -.loop: - lodsb ; load byte from ds:si to al - or al, al ; check if next char is null - jz .done - - int 0x10 - jmp .loop - -.done: - pop bx - pop ax - pop si - ret - -main: - - ; setup data segments - ; use ax as and intermediary as we can't write to es/ds directly - mov ax, 0 - mov ds, ax - mov es, ax - - ; setup stack - mov ss, ax - mov sp, 0x7C00 ; stack grows downward from where we are loaded in memory - - mov si, msg_startup - call echo - - mov si, msg_reading_from_disk - call echo - mov [ebr_drive_number], dl - - mov ax, 1 ; second sector from disk - mov cl, 1 ; 1 sector to read - mov bx, 0x7E00 ; data should be after the bootloader - call disk_read - - cli - hlt - -; errors - -floppy_error: - mov si, msg_read_failed - call echo - jmp .wait_and_reboot - -.wait_and_reboot: - mov ah, 0 - int 16h - - jmp 0FFFFh:0 ; jump to start of bios, should reboot - - hlt - -.halt: - cli ; disable interrupts, this way we shouldn't be able to get out of halt - hlt - jmp .halt - - -; -; args: -; ax = lba adress -; return: -; cx[0..5] = sector number -; cx[6-15] = cilinder number -; dh: head -; -lba_to_chs: - push ax - push dx - - xor dx, dx ; clear dw - div word [bdb_sectors_per_track] ; ax = LBA / SectorsPerTrack - ; dx = LBA % SectorsPerTrack - - inc dx - mov cx, dx ; cx = sector - - xor dx, dx ; clear dx - div word [bdb_heads] ; ax = (LBA / SectorsPerTrack) / Heads = cylinder - ; dx = (LBA / SectorsPerTrack) % Heads = head - mov dh, dl - mov ch, al - shl ah, 6 - or cl, ah ; put upper 2 bits of cylinder in CL - - pop ax - mov dl, al - pop ax - - ret - - -; -; args: -; ax = LBA adress -; cl = number of sectors to read -; dl = drive number -; es:bx = pointer to where to store the data -; - -disk_read: - push ax - push bx - push cx - push dx - push di - - push cx ; cx will be overridden - call lba_to_chs - pop ax ; al = number of sectors to read - mov ah, 02h - mov di, 3 ; hoe many times to try - -.retry: - pusha - stc ; set carry flag - int 13h ; if no carry flag => it work - - jnc .done - - ; read fail - popa - call disk_reset - dec di - - test di, di - jnz .retry - -.fail: - jmp floppy_error - -.done: - popa - - pop di - pop dx - pop cx - pop bx - pop ax - ret - -; arg: dl = drive number - -disk_reset: - pusha - mov ah, 0 - stc ; set carry - int 13h - jc floppy_error - popa - ret - -msg_startup: db 'starting zos, please hold while we check for any problems', ENDL, 0 -msg_reading_from_disk: db 'testing reading from disk', ENDL, 0 -msg_read_failed: db 'error when trying to read from floppy', ENDL, 0 - -times 510-($-$$) db 0 -dw 0AA55h diff --git a/src/kernel.asm b/src/kernel.asm index 6da1603..d52ab82 100644 --- a/src/kernel.asm +++ b/src/kernel.asm @@ -1,13 +1,18 @@ ; vim: ft=nasm -org 0x7C00 +org 0x0 bits 16 %define ENDL 0x0D, 0x0A start: - jmp main +; ; print hello world message + mov si, msg_hello + call echo + +.halt: + cli + hlt -; ; echo: ; print something to the screen ; - ds:si points to string @@ -35,28 +40,5 @@ echo: pop si ret -main: - - ; setup data segments - ; use ax as and intermediary as we can't write to es/ds directly - mov ax, 0 - mov ds, ax - mov es, ax - - ; setup stack - mov ss, ax - mov sp, 0x7C00 ; stack grows downward from where we are loaded in memory - - ; print the hello world - mov si, msg_hello - call echo - - hlt - -.halt: - jmp .halt msg_hello: db 'Hello, world!', ENDL, 0 - -times 510-($-$$) db 0 -dw 0AA55h diff --git a/src/reference_implementations/fat.zig b/src/reference_implementations/fat.zig index 61d7a0f..8473ae0 100644 --- a/src/reference_implementations/fat.zig +++ b/src/reference_implementations/fat.zig @@ -6,6 +6,10 @@ var buffer: [1024]u8 = undefined; var g_Fat: []u8 = undefined; +pub const std_options: std.Options = .{ + .log_level = .info, +}; + const BootSector = extern struct { BootJumpInstruction: [3]u8 align(1), OemIdentifier: [8]u8 align(1), @@ -49,10 +53,11 @@ const DirectoryEntry = extern struct { }; var g_RootDirectory: []DirectoryEntry = undefined; +var g_RootDirectoryEnd: u32 = undefined; fn readStruct(reader: *std.fs.File.Reader, T: type) !T { var ret: [@sizeOf(T)]u8 = undefined; - _ = try reader.*.read(&ret); + _ = try reader.*.readStreaming(&ret); return @bitCast(ret); } @@ -61,13 +66,13 @@ fn readBootSector(disk: *std.fs.File) !void { g_BootSector = try readStruct(&reader, BootSector); } -fn readSectors(disk: *std.fs.File, lba: u32, count: u32, bufferOut: *[]anyopaque) !void { +fn readSectors(disk: *std.fs.File, lba: u32, count: u32, bufferOut: *[]u8) !void { const dest: []u8 = try std.heap.smp_allocator.alloc(u8, g_BootSector.BytesPerSector * count); defer std.heap.smp_allocator.free(dest); - var stream = disk.readerStreaming(&buffer); - try stream.seekTo(lba * g_BootSector.BytesPerSector); - if (try stream.readStreaming(dest) != g_BootSector.BytesPerSector * count) return Errors.ReadSectors; - @memcpy(@as(*[]u8, @ptrCast(bufferOut)).*, dest); + try disk.seekTo(lba * g_BootSector.BytesPerSector); + var reader = disk.readerStreaming(&buffer); + if (try reader.readStreaming(dest) != g_BootSector.BytesPerSector * count) return Errors.ReadSectors; + @memcpy(bufferOut.*, dest); } fn readFat(disk: *std.fs.File) !void { @@ -76,42 +81,93 @@ fn readFat(disk: *std.fs.File) !void { try readSectors(disk, g_BootSector.ReservedSectors, g_BootSector.SectorsPerFat, &g_Fat); } -fn readRootDirectory(disk: std.fs.File) !void { - const lba: u32 = g_BootSector.ReservedSectors + g_BootSector.SectorsPerFat + g_BootSector.FatCount; +fn readRootDirectory(disk: *std.fs.File) !void { + const lba: u32 = g_BootSector.ReservedSectors + g_BootSector.SectorsPerFat * g_BootSector.FatCount; const size: u32 = @sizeOf(DirectoryEntry) * g_BootSector.DirEntryCount; - const sectors: u32 = (size / g_BootSector.BytesPerSector); - if (@rem(size, g_BootSector.BytesPerSector > 0)) + var sectors: u32 = (size / g_BootSector.BytesPerSector); + if (@rem(size, g_BootSector.BytesPerSector) > 0) sectors += 1; g_RootDirectory = try std.heap.smp_allocator.alloc(DirectoryEntry, sectors * g_BootSector.BytesPerSector); - errdefer std.heap.smp_allocator.free(g_RootDirectory); - try readSectors(disk, lba, sectors, &g_RootDirectory); + g_RootDirectoryEnd = lba + sectors; + try readSectors(disk, lba, sectors, @ptrCast(&g_RootDirectory)); + for (g_RootDirectory) |Entry| { + std.log.debug("Entry: {any}, Entry.Name: {s}", .{ + Entry, Entry.Name, + }); + } } fn findFile(name: []const u8) !*DirectoryEntry { - for (g_RootDirectory, 0..) |Entry, i| - if (std.mem.eql(u8, Entry.Name, name)) + for (g_RootDirectory, 0..) |Entry, i| { + std.log.debug("looking at {s}", .{Entry.Name}); + if (std.mem.eql(u8, &Entry.Name, name)) return &g_RootDirectory[i]; + } return Errors.FileNotFound; } +fn readFile(file: *DirectoryEntry, disk: *std.fs.File, outputBuffer: *[]u8) !void { + var currentCluster: u16 = file.FirstClusterLow; + const offset = g_BootSector.SectorsPerCluster * g_BootSector.BytesPerSector; + var i: usize = 0; + + while (currentCluster < 0x0FF8) { + std.log.debug("currentCluster: {}", .{currentCluster}); + const lba: u32 = g_RootDirectoryEnd + (currentCluster -% 2) * g_BootSector.SectorsPerCluster; + var sizedOutput = outputBuffer.*[i .. i + offset]; + try readSectors(disk, lba, g_BootSector.SectorsPerCluster, &sizedOutput); + std.log.debug("sizedOutput: {s}", .{sizedOutput}); + i += offset; + + const fatindex = currentCluster * 3 / 2; + + std.log.debug("@as(u16, g_Fat[fatindex]): {}, g_Fat[fatindex]: {}", .{ @as(*align(1) u16, @ptrCast(&g_Fat[fatindex])).*, g_Fat[fatindex] }); + if (@rem(currentCluster, 2) == 0) { + currentCluster = @as(*align(1) u16, @ptrCast(&g_Fat[fatindex])).* & 0x0FFF; + } else { + currentCluster = @as(*align(1) u16, @ptrCast(&g_Fat[fatindex])).* >> 4; + } + } +} + pub fn main() !void { - if (std.os.argv.len < 3) { + var args = try std.process.argsWithAllocator(std.heap.smp_allocator); + defer args.deinit(); + if (args.inner.count < 3) { std.log.err("syntax: {s} ", .{std.os.argv[0]}); return Errors.InvalidSyntax; } - var disk = try std.fs.cwd().openFileZ(std.os.argv[1], .{ .mode = .read_only }); + _ = args.skip(); + + var disk = try std.fs.cwd().openFileZ(args.next().?, .{ .mode = .read_only }); + std.log.debug("opened disk {any}", .{disk}); - _ = &disk; try readBootSector(&disk); + std.log.debug("read BootSector from disk, {any}", .{g_BootSector}); try readFat(&disk); defer std.heap.smp_allocator.free(g_Fat); + std.log.debug("read file allocation table from disk, {any}", .{g_Fat}); - try readRootDirectory(disk); + try readRootDirectory(&disk); defer std.heap.smp_allocator.free(g_RootDirectory); + std.log.debug("read root directory from disk, {any}", .{g_RootDirectory}); - const fileEntry = try findFile(std.os.argv[2]); - _ = fileEntry; + const fileEntry = try findFile(try std.fmt.allocPrint(std.heap.smp_allocator, "{s}", .{args.next().?})); + + var fileBuffer = try std.heap.smp_allocator.alloc(u8, fileEntry.Size + g_BootSector.BytesPerSector); + @memset(fileBuffer, 0); + defer std.heap.smp_allocator.free(fileBuffer); + + try readFile(fileEntry, &disk, &fileBuffer); + const stdout = std.fs.File.stdout(); + defer stdout.close(); + var writer = stdout.writer(&buffer); + try writer.interface.print("{s}\n", .{@as(*[:0]u8, @ptrCast(&fileBuffer)).*}); + + const newline = "\n"; + _ = try std.posix.write(0, fileBuffer); + _ = try std.posix.write(0, newline); } From 2dd27016f4b6a73cc7c31d9349bab03c1f6a57a4 Mon Sep 17 00:00:00 2001 From: Dario48 Date: Thu, 24 Jul 2025 16:30:40 +0200 Subject: [PATCH 5/6] wip zig kernel --- as386.sed | 196 ++++++++++++++++++++++++++++ build.zig | 29 +++- src/{kernel.asm => definitions.asm} | 17 +-- src/kernel.zig | 25 ++++ 4 files changed, 246 insertions(+), 21 deletions(-) create mode 100644 as386.sed rename src/{kernel.asm => definitions.asm} (67%) create mode 100644 src/kernel.zig diff --git a/as386.sed b/as386.sed new file mode 100644 index 0000000..0c416f0 --- /dev/null +++ b/as386.sed @@ -0,0 +1,196 @@ +#\ +# @(#)as386.sed 1.1 - 86/11/17\ +#\ +# This is a sed script which converts Intel 386 assembly code to Unix\ +# 386 assembly code\ +#\ +# This script does not attempt to convert 100% of the ASM386 source code,\ +# it cannot handle the following constructs:\ +#\ +# - strange segmentation schemes\ +# - data declarations beyond the simple db/dw/dd with simple init list\ +# - ascii strings\ +# - structure template addressing (i.e., [ebp].foo)\ +# - complex expressions (parenthesis and operators other than +-*/)\ +# - immediate operands that are not simple constants\ +# - immediate operands with automatically typed memory operands\ +# - source files in upper case\ +# - source files with continued lines\ +#\ +# A typical way to use this sed script is:\ +# tr "[A-Z]" "[a-z]" outfile\ +#\ +#\ +# Meaning of labels:\ +#\ +# cmpress preserve comments, insert tabs, change '?' to '_'\ +# control convert '$' control lines\ +# segment convert segmentation directives\ +# equate convert 'equ' directives\ +# data convert db/dw/dd data declarations\ +# modifer delete address/type modifiers\ +# registr convert register names\ +# bincons convert binary/octal/hex conatants\ +# address regularize addess expressions, delete unneeded tabs\ +# normliz normalize instruction formats (name-tab-opcode-tab-operands)\ +# operand swap operands, convert scale/index/base address format\ +# opcode append b/w to opcode for byte/word register operands\ +# comment restore preserved comments\ + +:cmpress +h +s/;.*$// +s/[ ]*$/ / +s/[ ][ ]*/ /g +s/\([][)(.,:*/+-]\)/ \1 /g +s/ ? / 0 /g +s/?/_/g +s/[ ][ ]*/ /g + +:control +s/^\$[ ]*include ( \([^ .)]*\)[^)]*)/#include "\1.h"/ +s/^\$[ ]*eject /; / +/^\$/ s/^/;/ + +:segment +/ segment e[or] / s/^/ .text ;/ +/ segment ro / s/^/ .text ;/ +/ segment rw / s/^/ .data ;/ +/ ends / s/^/;/ +/ stackseg / s/^/;/ +s/^[ ]*name \([^ ]*\)/ .file "\1"/ +/^[ ]*end / s/^/;/ +/^[ ]*assume / s/^/;/ +/^[ ]*extrn / s/^/;/ +/^[ ]*public [^ ,]* , / { +s/^[ ]*public / .globl / +s/ , .*$// +G +s/\n\([ ]*\)public\([ ]\)[^,]*,/\ +\1public\2/ +P +D +} +s/^[ ]*public / .globl / +s/^[ ]*comm / .comm / +s/^\([^ ][^ ]*\) proc /\1 : ;proc / +/ endp / s/^/;/ +s/^\([^ ][^ ]*\) label /\1 : ;label / +s/^[ ]*even / .even / + +:equate +s/^\([^ ]*\) equ \(.*\) $/#define : \1 \2 / + +:data +s/^[ ]*db / .byte / +s/^\([^ ][^ ]*\) db /\1 : .byte / +s/^[ ]*dw / .value / +s/^\([^ ][^ ]*\) dw /\1 : .value / +s/^[ ]*dd / .long / +s/^\([^ ][^ ]*\) dd /\1 : .long / +/^[ ]*dp / { +s/^[ ]*dp / .value / +s/$/\/selector of dp pointer/ +G +s/\n\([ ]*\)dp\([ ]\)/\ +\1dd\2/ +P +D +} +/^\([^ ][^ ]*\) dp / { +s/^\([^ ][^ ]*\) dp /\1: .value / +s/$/\/selector of dp pointer/ +G +s/\n[^ ]*\([ ]*\)dp\([ ]\)/\ +\1dd\2/ +P +D +} +s/^\([^ ][^ ]*\) struc /\1 : ;struc / +s/^\([^ ][^ ]*\) record /\1 : ;record / + +:modifer +s/ : near / /g +s/ : far / /g +s/ : byte / /g +s/ : word / /g +s/ : dword / /g +s/ short / /g +s/ offset / $/g +/ byte ptr / s/$/?%al?/ +s/ byte ptr / /g +/ word ptr / s/$/?%ax?/ +s/ word ptr / /g +s/ dword ptr / /g +s/ pword ptr / /g + +:registr +s/ e\([abcd]\)x / %e\1x /g +s/ \([abcd]\)\([hlx]\) / %\1\2 /g +s/ e\([ds]\)i / %e\1i /g +s/ \([ds]\)i / %\1i /g +s/ e\([bs]\)p / %e\1p /g +s/ \([bs]\)p / %\1p /g +s/ \([cdefgs]\)s / %\1s /g + +:bincons +s/ \([01][01]*\)b / 0b\1 /g +s/ \([0-7][0-7]*\)[oq] / 0\1 /g +s/ \([0-9][0-9a-f]*\)h / 0x\1 /g + +t address +:address +s/\[/+/g +/;/ !s/ ] \. / + /g +s/ ]//g +s/ %\([^ ]*\) \* \([248]\) + \([^ %][^ ]*\)/ \3 + %\1 * \2/g +s/ %\([^ ]*\) + \([^ %][^ ]*\)/ \2 + %\1/g +s/ %\([^ ]*\) \* \([248]\) - \([^ %][^ ]*\)/ - \3 + %\1 * \2/g +s/ %\([^ ]*\) - \([^ %][^ ]*\)/ - \2 + %\1/g +s/ + - / - /g +t address +s/ \([)(,*/+-]\)/\1/g +s/\([)(,*/+-]\) /\1/g +s/ :/:/g +s/%\([cdefgs]\)s: /%\1s:/g + +:normliz +/: / !s/^\([^ ;#][^ ]*\)/ \1/ +/: / !s/^ \([^ +,-]*\)\([+-]\)/ \1 \2/g +/: / !s/^ \([^ +,]*\)+%/ \1 +%/g +/: / s/^\([^ ]*\) \([^ +,-]*\)\([+-]\)/\1 \2 \3/g +/: / s/^\([^ ]*\) \([^ +,]*\)+%/\1 \2 +%/g +s/+%\([^ ,]*\)/(%\1)/g +s/ +/ /g +s/\([:,]\)+/\1/g + +:operand +/[.;#]/ !s/^ \([^ ]*\) \([^,]*\),\([^ ]*\)/ \1 \3,\2/ +/[.;#]/ !s/^\([^ ][^ ]*\) \([^ ]*\) \([^,]*\),\([^ ]*\)/\1 \2 \4,\3/ +/[.;#]/ !s/^ \([^ ]*\) \([0-9][0-9a-fx]*\)\([ ,]\)/ \1 $\2\3/ +/[.;#]/ !s/^\([^ ][^ ]*\) \([^ ]*\) \([0-9][0-9a-fx]*\)\([ ,]\)/\1 \2 $\3\4/ +s/(%\([^)+*]*\)+%\([^)*]*\)\*\([248]\))/(%\1,%\2,\3)/g +s/(%\([^)+*]*\)\*\([248]\)+%\([^)]*\))/(%\3,%\1,\2)/g +s/(%\([^)+*]*\)+%\([^)]*\))/(%\1,%\2)/g +s/(%\([^)+*]*\)\*\([248]\))/(,%\1,\2)/g + +:opcode +/[ ,]%[abcd][hl]/ s/$/?%al?/ +/[ ,]%[abcd]x/ s/$/?%ax?/ +/[ ,]%[ds]i/ s/$/?%ax?/ +/[ ,]%[bs]p/ s/$/?%ax?/ +/?%al?/ s/^\([^ ]*\) \([^ ]*\) /\1 \2b / +/?%ax?/ s/^\([^ ]*\) \([^ ]*\) /\1 \2w / +s/^#define:/#define/ +s/?%a[xl]?//g + +:comment +s/ *$// +x +/;/ !s/^.*$// +s/\([ ]*\);/;\1;/ +s/^[^;]*;// +x +G +s/\n// +s/;/\// diff --git a/build.zig b/build.zig index 78a2e05..ee95b69 100644 --- a/build.zig +++ b/build.zig @@ -10,17 +10,35 @@ pub fn build(b: *std.Build) void { .outputname = "bootloader.bin", .flags = &.{"-f bin"}, }); - const kernel = addNasmFiles(b, AddNasmFilesOptions{ - .filename = "src/kernel.asm", - .outputname = "kernel.bin", - .flags = &.{"-f bin"}, + //const kernel = addNasmFiles(b, AddNasmFilesOptions{ + // .filename = "src/kernel.asm", + // .outputname = "kernel.bin", + // .flags = &.{"-f bin"}, + //}); + + const target = b.standardTargetOptions(.{ + .default_target = .{ + .os_tag = .uefi, + .cpu_arch = .x86, + }, + }); + + const kernel_mod = b.addModule("kernel", .{ + .root_source_file = b.path("src/kernel.zig"), + .target = target, + }); + + const kernel = b.addExecutable(.{ + .root_module = kernel_mod, + .linkage = .static, + .name = "kernel", }); const floppy = createFloppy(b, "main_floppy.img"); formatFloppyFat12(b, floppy); var installToFloppy = installImgToFloppy(b, floppy, bootloader, &.{ - NamedFile{ .file = kernel, .name = "::kernel.bin" }, + NamedFile{ .file = kernel.getEmittedBin(), .name = "::kernel.bin" }, NamedFile{ .file = b.path("src/reference_implementations/test.txt"), .name = "::test.txt" }, }); @@ -29,7 +47,6 @@ pub fn build(b: *std.Build) void { b.getInstallStep().dependOn(&img_install.step); - const target = b.standardTargetOptions(.{}); const fat_reference_mod = b.createModule(.{ .root_source_file = b.path("src/reference_implementations/fat.zig"), .target = target, diff --git a/src/kernel.asm b/src/definitions.asm similarity index 67% rename from src/kernel.asm rename to src/definitions.asm index d52ab82..87b2f9b 100644 --- a/src/kernel.asm +++ b/src/definitions.asm @@ -2,22 +2,12 @@ org 0x0 bits 16 -%define ENDL 0x0D, 0x0A - -start: -; ; print hello world message - mov si, msg_hello - call echo - -.halt: - cli - hlt - ; echo: ; print something to the screen ; - ds:si points to string ; -echo: +global _echo:function +_echo: ; save the registers we want to modify push si push ax @@ -39,6 +29,3 @@ echo: pop ax pop si ret - - -msg_hello: db 'Hello, world!', ENDL, 0 diff --git a/src/kernel.zig b/src/kernel.zig new file mode 100644 index 0000000..690655e --- /dev/null +++ b/src/kernel.zig @@ -0,0 +1,25 @@ +fn BIOSprint(string: []const u8) void { + asm volatile ( + \\pushw %%ax + \\pushw %%bx + ); + for (string) |char| { + asm volatile ( + \\movb $0x0E, %%ah + \\movb $0, %%bh + \\int $0x10 + : + : [arg1] "{al}" (char), + ); + } + asm volatile ( + \\popw %%bx + \\popw %%ax + ); +} + +const std = @import("std"); + +pub fn main() void { + BIOSprint("kernel loaded"); +} From c8ce196828aab17442a34ccec353b895b6ceda39 Mon Sep 17 00:00:00 2001 From: Dario48 Date: Fri, 25 Jul 2025 22:59:11 +0200 Subject: [PATCH 6/6] actually maybe let's not just yet --- build.zig | 30 +++++++++-------------------- src/bootloader.asm | 2 -- src/{definitions.asm => kernel.asm} | 24 ++++++++++++++++++----- src/kernel.zig | 25 ------------------------ 4 files changed, 28 insertions(+), 53 deletions(-) rename src/{definitions.asm => kernel.asm} (68%) delete mode 100644 src/kernel.zig diff --git a/build.zig b/build.zig index ee95b69..bb6acf2 100644 --- a/build.zig +++ b/build.zig @@ -10,11 +10,11 @@ pub fn build(b: *std.Build) void { .outputname = "bootloader.bin", .flags = &.{"-f bin"}, }); - //const kernel = addNasmFiles(b, AddNasmFilesOptions{ - // .filename = "src/kernel.asm", - // .outputname = "kernel.bin", - // .flags = &.{"-f bin"}, - //}); + const kernel = addNasmFiles(b, AddNasmFilesOptions{ + .filename = "src/kernel.asm", + .outputname = "kernel.bin", + .flags = &.{"-f bin"}, + }); const target = b.standardTargetOptions(.{ .default_target = .{ @@ -23,22 +23,10 @@ pub fn build(b: *std.Build) void { }, }); - const kernel_mod = b.addModule("kernel", .{ - .root_source_file = b.path("src/kernel.zig"), - .target = target, - }); - - const kernel = b.addExecutable(.{ - .root_module = kernel_mod, - .linkage = .static, - .name = "kernel", - }); - const floppy = createFloppy(b, "main_floppy.img"); - formatFloppyFat12(b, floppy); var installToFloppy = installImgToFloppy(b, floppy, bootloader, &.{ - NamedFile{ .file = kernel.getEmittedBin(), .name = "::kernel.bin" }, + NamedFile{ .file = kernel, .name = "::kernel.bin" }, NamedFile{ .file = b.path("src/reference_implementations/test.txt"), .name = "::test.txt" }, }); @@ -91,16 +79,16 @@ fn createFloppy(b: *std.Build, name: []const u8) LazyPath { return Floppy; } -fn formatFloppyFat12(b: *std.Build, floppy: LazyPath) void { +fn installImgToFloppy(b: *std.Build, floppy: LazyPath, bootloader: LazyPath, additional_files: []const NamedFile) *std.Build.Step.Run { const mkfs = b.addSystemCommand(&.{ "mkfs.fat", "-F12", "-nNBOS" }); mkfs.addFileArg(floppy); -} + mkfs.has_side_effects = true; -fn installImgToFloppy(b: *std.Build, floppy: LazyPath, bootloader: LazyPath, additional_files: []const NamedFile) *std.Build.Step.Run { const dd = b.addSystemCommand(&.{"dd"}); dd.addPrefixedFileArg("if=", bootloader); dd.addPrefixedFileArg("of=", floppy); dd.addArg("conv=notrunc"); + dd.step.dependOn(&mkfs.step); var last_step = dd; for (additional_files) |f| { diff --git a/src/bootloader.asm b/src/bootloader.asm index 3ac094e..c3a7e6a 100644 --- a/src/bootloader.asm +++ b/src/bootloader.asm @@ -37,7 +37,6 @@ ebr_system_id: db 'FAT12 ' ; 8 bytes - start: ; setup data segments mov ax, 0 ; use ax as and intermediary as we can't write to es/ds directly @@ -364,7 +363,6 @@ disk_reset: popa ret - msg_loading: db 'starting zos', ENDL, 0 msg_read_failed: db 'error on floppy read', ENDL, 0 msg_kernel_not_found: db 'kernel.bin not found', ENDL, 0 diff --git a/src/definitions.asm b/src/kernel.asm similarity index 68% rename from src/definitions.asm rename to src/kernel.asm index 87b2f9b..fa4647e 100644 --- a/src/definitions.asm +++ b/src/kernel.asm @@ -2,26 +2,38 @@ org 0x0 bits 16 + +%define ENDL 0x0D, 0x0A + + +start: + mov si, msg_init + call echo + +.halt: + cli + hlt + +; ; echo: ; print something to the screen ; - ds:si points to string ; -global _echo:function -_echo: +echo: ; save the registers we want to modify push si push ax push bx - mov ah, 0xe - mov bh, 0 - .loop: lodsb ; load byte from ds:si to al or al, al ; check if next char is null jz .done + mov ah, 0x0E + mov bh, 0 int 0x10 + jmp .loop .done: @@ -29,3 +41,5 @@ _echo: pop ax pop si ret + +msg_init: db 'started kernel', ENDL, 0 diff --git a/src/kernel.zig b/src/kernel.zig deleted file mode 100644 index 690655e..0000000 --- a/src/kernel.zig +++ /dev/null @@ -1,25 +0,0 @@ -fn BIOSprint(string: []const u8) void { - asm volatile ( - \\pushw %%ax - \\pushw %%bx - ); - for (string) |char| { - asm volatile ( - \\movb $0x0E, %%ah - \\movb $0, %%bh - \\int $0x10 - : - : [arg1] "{al}" (char), - ); - } - asm volatile ( - \\popw %%bx - \\popw %%ax - ); -} - -const std = @import("std"); - -pub fn main() void { - BIOSprint("kernel loaded"); -}