diff --git a/.asm-lsp.toml b/.asm-lsp.toml deleted file mode 100644 index e34e92b..0000000 --- a/.asm-lsp.toml +++ /dev/null @@ -1,21 +0,0 @@ -[default_config] -version = "0.10.0" -assembler = "nasm" -instruction_set = "x86/x86-64" - -[default_config.opts] -compiler = "zig" -compile_flags_txt = [ - "cc", - "-x", - "assembler-with-cpp", - "-g", - "-Wall", - "-Wextra", - "-pedantic", - "-pedantic-errors", - "-std=c2y", - "-mllvm --x86-asm-syntax=intel", -] -diagnostics = true -default_diagnostics = false diff --git a/.gitignore b/.gitignore index 0ae2e6a..880cd5d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ zig-out .zig-cache -.kate-swp diff --git a/README.md b/README.md deleted file mode 100644 index 7a6beca..0000000 --- a/README.md +++ /dev/null @@ -1,33 +0,0 @@ -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 -``` diff --git a/as386.sed b/as386.sed deleted file mode 100644 index 0c416f0..0000000 --- a/as386.sed +++ /dev/null @@ -1,196 +0,0 @@ -#\ -# @(#)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 bb6acf2..191e029 100644 --- a/build.zig +++ b/build.zig @@ -1,13 +1,12 @@ 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 bootloader = addNasmFiles(b, AddNasmFilesOptions{ - .filename = "src/bootloader.asm", - .outputname = "bootloader.bin", + const initramfs = addNasmFiles(b, AddNasmFilesOptions{ + .filename = "src/initramfs.asm", + .outputname = "initramfs.bin", .flags = &.{"-f bin"}, }); const kernel = addNasmFiles(b, AddNasmFilesOptions{ @@ -16,47 +15,19 @@ pub fn build(b: *std.Build) void { .flags = &.{"-f bin"}, }); - const target = b.standardTargetOptions(.{ - .default_target = .{ - .os_tag = .uefi, - .cpu_arch = .x86, - }, - }); + var floppy = createFloppy(b, "main_floppy.img"); + floppy.run = formatFloppyFat12(b, floppy); - const floppy = createFloppy(b, "main_floppy.img"); - - 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, "main_floppy.img"); - img_install.step.dependOn(&installToFloppy.step); + 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 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", "zig-out/bin/main_floppy.img" }); + 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 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 { @@ -65,45 +36,53 @@ const AddNasmFilesOptions = struct { flags: []const []const u8 = &.{}, }; -const NamedFile = struct { - file: std.Build.LazyPath, - name: []const u8, +const NasmFile = struct { + run: *std.Build.Step.Run, + obj: std.Build.LazyPath, }; -fn createFloppy(b: *std.Build, name: []const u8) 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.has_side_effects = true; dd.addArgs(&.{ "bs=512", "count=2880" }); - return Floppy; + return Floppydisk{ + .run = dd, + .obj = Floppy, + }; } -fn installImgToFloppy(b: *std.Build, floppy: LazyPath, bootloader: LazyPath, additional_files: []const NamedFile) *std.Build.Step.Run { +fn formatFloppyFat12(b: *std.Build, floppy: Floppydisk) *std.Build.Step.Run { const mkfs = b.addSystemCommand(&.{ "mkfs.fat", "-F12", "-nNBOS" }); - mkfs.addFileArg(floppy); - mkfs.has_side_effects = true; + 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); - dd.addPrefixedFileArg("of=", floppy); + dd.addPrefixedFileArg("if=", bootloader.obj); + dd.addPrefixedFileArg("of=", floppy.obj); dd.addArg("conv=notrunc"); - dd.step.dependOn(&mkfs.step); + dd.step.dependOn(&floppy.run.step); + dd.step.dependOn(&bootloader.run.step); - var last_step = dd; - for (additional_files) |f| { - const mcopy = b.addSystemCommand(&.{"mcopy"}); - mcopy.addPrefixedFileArg("-i", floppy); - mcopy.addFileArg(f.file); - mcopy.addArg(f.name); - mcopy.step.dependOn(&last_step.step); - last_step = mcopy; - } - return last_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) LazyPath { +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, '.')}); @@ -113,7 +92,9 @@ fn addNasmFiles(b: *std.Build, options: AddNasmFilesOptions) LazyPath { nasm.addPrefixedDirectoryArg("-i", b.path("src")); const obj = nasm.addPrefixedOutputFileArg("-o", output); nasm.addFileArg(src_file); - nasm.has_side_effects = true; - return obj; + return .{ + .run = nasm, + .obj = obj, + }; } diff --git a/build.zig.zon b/build.zig.zon index f29dbdf..365621b 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.15.0", + .minimum_zig_version = "0.14.1", // This field is optional. // Each dependency must either provide a `url` and `hash`, or a `path`. diff --git a/src/bootloader.asm b/src/bootloader.asm deleted file mode 100644 index c3a7e6a..0000000 --- a/src/bootloader.asm +++ /dev/null @@ -1,379 +0,0 @@ -; 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 new file mode 100644 index 0000000..6da1603 --- /dev/null +++ b/src/initramfs.asm @@ -0,0 +1,62 @@ +; vim: ft=nasm +org 0x7C00 +bits 16 + +%define ENDL 0x0D, 0x0A + +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 + + ; 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/kernel.asm b/src/kernel.asm index fa4647e..6da1603 100644 --- a/src/kernel.asm +++ b/src/kernel.asm @@ -1,18 +1,11 @@ ; vim: ft=nasm -org 0x0 +org 0x7C00 bits 16 - %define ENDL 0x0D, 0x0A - start: - mov si, msg_init - call echo - -.halt: - cli - hlt + jmp main ; ; echo: @@ -25,15 +18,15 @@ echo: 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: @@ -42,4 +35,28 @@ echo: pop si ret -msg_init: db 'started kernel', ENDL, 0 +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 deleted file mode 100644 index 8473ae0..0000000 --- a/src/reference_implementations/fat.zig +++ /dev/null @@ -1,173 +0,0 @@ -const std = @import("std"); - -const Errors = error{ InvalidSyntax, ReadSectors, FileNotFound }; - -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), - 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; -var g_RootDirectoryEnd: u32 = undefined; - -fn readStruct(reader: *std.fs.File.Reader, T: type) !T { - var ret: [@sizeOf(T)]u8 = undefined; - _ = try reader.*.readStreaming(&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: *[]u8) !void { - const dest: []u8 = try std.heap.smp_allocator.alloc(u8, g_BootSector.BytesPerSector * count); - defer std.heap.smp_allocator.free(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 { - 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; - 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); - 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| { - 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 { - 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; - } - - _ = args.skip(); - - var disk = try std.fs.cwd().openFileZ(args.next().?, .{ .mode = .read_only }); - std.log.debug("opened disk {any}", .{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); - defer std.heap.smp_allocator.free(g_RootDirectory); - std.log.debug("read root directory from disk, {any}", .{g_RootDirectory}); - - 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); -} diff --git a/src/reference_implementations/test.txt b/src/reference_implementations/test.txt deleted file mode 100644 index 20e13f9..0000000 --- a/src/reference_implementations/test.txt +++ /dev/null @@ -1,3 +0,0 @@ -testing file -aabaacaadaaeaafaagaahaaiaajaakaalaamaanaaoaapaaqaaraasaataauaavaawaaxaayaaz -tstngfl