From e1926aec8ac32a811f0942b122aeaf04342ac0a1 Mon Sep 17 00:00:00 2001 From: Dario48 Date: Wed, 23 Jul 2025 17:24:23 +0200 Subject: [PATCH] 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); }