diff options
author | Johannes Stoelp <johannes.stoelp@gmail.com> | 2024-12-25 02:55:41 +0100 |
---|---|---|
committer | Johannes Stoelp <johannes.stoelp@gmail.com> | 2024-12-25 21:25:16 +0100 |
commit | a2ce17753fa4a727f082bfb9bd515dc3fde56f02 (patch) | |
tree | 9b5d3c309a32c483e4d9cec34d1fe57a476da578 | |
parent | 14845124cf5d4fb42a04cb1262b32bd3d00f45f8 (diff) | |
download | zig-playground-a2ce17753fa4a727f082bfb9bd515dc3fde56f02.tar.gz zig-playground-a2ce17753fa4a727f082bfb9bd515dc3fde56f02.zip |
mbr: zig only real mode 16 bit
-rw-r--r-- | x86-bare-metal/mbr-pure-zig/.gitignore | 1 | ||||
-rw-r--r-- | x86-bare-metal/mbr-pure-zig/Makefile | 29 | ||||
-rw-r--r-- | x86-bare-metal/mbr-pure-zig/bios.zig | 65 | ||||
-rw-r--r-- | x86-bare-metal/mbr-pure-zig/mbr.ld | 24 | ||||
-rw-r--r-- | x86-bare-metal/mbr-pure-zig/mbr.zig | 106 | ||||
-rw-r--r-- | x86-bare-metal/mbr-pure-zig/x220.png | bin | 0 -> 438430 bytes |
6 files changed, 225 insertions, 0 deletions
diff --git a/x86-bare-metal/mbr-pure-zig/.gitignore b/x86-bare-metal/mbr-pure-zig/.gitignore new file mode 100644 index 0000000..a92a67f --- /dev/null +++ b/x86-bare-metal/mbr-pure-zig/.gitignore @@ -0,0 +1 @@ +BUILD/
\ No newline at end of file diff --git a/x86-bare-metal/mbr-pure-zig/Makefile b/x86-bare-metal/mbr-pure-zig/Makefile new file mode 100644 index 0000000..41d2653 --- /dev/null +++ b/x86-bare-metal/mbr-pure-zig/Makefile @@ -0,0 +1,29 @@ +O := BUILD + +COMMON_FLAGS := -fno-unwind-tables -fno-strip -target x86-freestanding-code16 -O ReleaseSmall + +$(O)/boot: $(O)/boot.elf | check_ep dump_info + objcopy -O binary $< $@ + +$(O)/boot.elf: mbr.ld mbr.zig | $(O) + zig build-exe -femit-bin=$@ $(COMMON_FLAGS) -fno-entry --script $^ + +clean: + $(RM) -r $(O) + +$(O): + mkdir -p $(O) + +# Check entry point is exactly at 0x7c00. +check_ep: $(O)/boot.elf + readelf -h $< | awk '/Entry point address:/ { print $$4 }' | grep 0x7c00 >& /dev/null + +# Dump some debug info (disasm, elf load segments, code size). +dump_info: $(O)/boot.elf + objdump -Mintel -m i8086 --disassemble --visualize-jumps=extended-color $< + readelf -W -l $< + size $< + size $< | awk '/$(notdir $<)/ { print "MBR utilization " $$1/512 "%" }' + +run: $(O)/boot + qemu-system-i386 -hda $< $(QEMU_ARGS) diff --git a/x86-bare-metal/mbr-pure-zig/bios.zig b/x86-bare-metal/mbr-pure-zig/bios.zig new file mode 100644 index 0000000..b96b312 --- /dev/null +++ b/x86-bare-metal/mbr-pure-zig/bios.zig @@ -0,0 +1,65 @@ +/// BIOS starts with video mode 0h. +/// * 80x25 text mode +/// * 16 colors (4bit) +/// http://www.ctyme.com/intr/rb-0069.htm +pub const COLS = 80; +pub const ROWS = 25; + +/// BIOS CALL - video teletype output. +/// http://www.ctyme.com/intr/rb-0106.htm +pub fn putc(c: u8) void { + // ah=0eh (call nr) + // al=c (ascii char to write) + const ax = (@as(u16, 0xe) << 8) | c; + + _ = asm volatile ( + \\int $0x10 + : // No outputs. + : [a] "{ax}" (ax), // BIOS call number + char to write. + : "cc" + ); + + // On linefeed also write a carriage return to move to column 0. + if (c == '\n') { + putc('\r'); + } +} + +/// BIOS CALL - video set cursor position. +/// http://www.ctyme.com/intr/rb-0087.htm +pub fn set_pos(r: u8) void { + // dh=r (cursor row) + // dl=0 (cursor col) + const dx = @as(u16, r) << 8 | 0; + + _ = asm volatile ( + \\int $0x10 + : // No outputs. + : [a] "{ax}" (0x200), // BIOS call number. + [b] "{bx}" (0x0), // Page number. + [d] "{dx}" (dx), // Position. + : "cc" + ); +} + +/// BIOS CALL - video scroll up +/// http://www.ctyme.com/intr/rb-0096.htm +pub fn scroll_up() void { + // ah=06h (call nr) + // al=1 (rows to scroll) + const ax = (@as(u16, 0x6) << 8) | 1; + + // dh=ROWS-1 (row lower right corner) + // dl=COLS-1 (col lower right corner) + const dx = (@as(u16, ROWS - 1) << 8) | (COLS - 1); + + _ = asm volatile ( + \\int $0x10 + : // No outputs. + : [a] "{ax}" (ax), // BIOS call number + rows to scroll. + [p] "{dx}" (dx), // Row + col lower right corner. + [b] "{bx}" (0x0700), // Attribute, dark bg, white fg. + [c] "{cx}" (0), // Row + col upper left corner. + : "cc" + ); +} diff --git a/x86-bare-metal/mbr-pure-zig/mbr.ld b/x86-bare-metal/mbr-pure-zig/mbr.ld new file mode 100644 index 0000000..9ada8c4 --- /dev/null +++ b/x86-bare-metal/mbr-pure-zig/mbr.ld @@ -0,0 +1,24 @@ +/*OUTPUT_FORMAT(binary)*/ +OUTPUT_FORMAT(elf32-i386) +OUTPUT_ARCH(i386) +ENTRY(_entry) + +SECTIONS { + . = 0x7c00; + .boot : { *(.boot) } + .text : { *(.text) } + .data : { *(.data) } + .rodata : { *(.rodata) *(.rodata.str*) } + _boot_end = .; + + . = 0x7c00 + 510; + .mbr.magic : { + BYTE(0x55); + BYTE(0xaa); + } + + /*/DISCARD/ : { *(.*) }*/ + /*rest : { *(.*) }*/ + + ASSERT(_boot_end - 0x7c00 < 510, "boot sector must fit in 510 bytes") +} diff --git a/x86-bare-metal/mbr-pure-zig/mbr.zig b/x86-bare-metal/mbr-pure-zig/mbr.zig new file mode 100644 index 0000000..666aa6b --- /dev/null +++ b/x86-bare-metal/mbr-pure-zig/mbr.zig @@ -0,0 +1,106 @@ +const bios = @import("bios.zig"); + +// -- ENTRY POINT --------------------------------------------------------------- + +export fn _entry() linksection(".boot") callconv(.naked) noreturn { + asm volatile ( + // Disable interrupts. + \\cli + // Clear segment selectors. + \\xor %%ax, %%ax + \\mov %%ax, %%ds + \\mov %%ax, %%es + \\mov %%ax, %%ss + \\mov %%ax, %%fs + \\mov %%ax, %%gs + \\mov $0x7c00, %sp + // Long jump to set cs to 0x0000, as some BIOSes load the MBR + // to either 07c0:0000 or 0000:7c000. + \\ljmp $0x0, $main + ); +} + +// -- MAIN ---------------------------------------------------------------------- + +// main should be "callconv(.naked)", once issue is fixed. +// https://github.com/ziglang/zig/issues/18183 +export fn main() noreturn { + for (0..255) |i| { + if (i < bios.ROWS) { + bios.set_pos(@truncate(i)); + } else { + // Once we reach the bottom of the screen start scrolling. + bios.scroll_up(); + bios.set_pos(bios.ROWS - 1); + } + puts_dec(i + 1); + bios.putc(' '); + puts_hex(i + 1); + bios.putc(' '); + bios.putc(@truncate('a' + (i % 26))); + bios.putc(@truncate('a' + (i % 26))); + waste_time(); + } + puts("\ndone"); + + while (true) { + asm volatile ("hlt"); + } +} + +// -- UTILS --------------------------------------------------------------------- + +fn puts(str: []const u8) void { + for (str) |c| { + bios.putc(c); + } +} + +fn puts_dec(init_val: u32) void { + var val = init_val; + var buf: [32]u8 = undefined; + var idx = buf.len; + + while (val > 0 and idx != 0) : (val /= 10) { + idx -= 1; + buf[idx] = @as(u8, @truncate(val % 10)) + '0'; + } + puts(buf[idx..]); +} + +fn puts_hex(init_val: u32) void { + var val = init_val; + var buf: [32]u8 = undefined; + var idx = buf.len; + + while (val > 0 and idx != 0) : (val /= 16) { + idx -= 1; + const digit = switch (@as(u8, @truncate(val % 16))) { + 0...9 => |d| '0' + d, + 10...15 => |d| 'a' + d - 10, + else => '?', + }; + buf[idx] = digit; + } + puts(buf[idx..]); +} + +fn rdtsc() u32 { + return asm volatile ("rdtsc" + : [eax] "={eax}" (-> u32), + : + : "edx" + ); +} + +fn waste_time() void { + const start = rdtsc(); + var now = start; + var idx: usize = 0; + while (now - start < (1 << 28)) : (idx += 1) { + if (idx == 10_000) { + now = rdtsc(); + idx = 0; + } + } +} diff --git a/x86-bare-metal/mbr-pure-zig/x220.png b/x86-bare-metal/mbr-pure-zig/x220.png Binary files differnew file mode 100644 index 0000000..ac3a4e8 --- /dev/null +++ b/x86-bare-metal/mbr-pure-zig/x220.png |