summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohannes Stoelp <johannes.stoelp@gmail.com>2024-12-23 22:51:52 +0100
committerJohannes Stoelp <johannes.stoelp@gmail.com>2024-12-25 21:25:16 +0100
commit14845124cf5d4fb42a04cb1262b32bd3d00f45f8 (patch)
tree13162d26afeb49a9c9b8d9d31049066c0f4b2998
parent42b6f9d23ab0f94744a90edb6fd74450f589ca14 (diff)
downloadzig-playground-14845124cf5d4fb42a04cb1262b32bd3d00f45f8.tar.gz
zig-playground-14845124cf5d4fb42a04cb1262b32bd3d00f45f8.zip
mbr: rm->pm, then jump into zig
-rw-r--r--x86-bare-metal/mbr-palette/.gitignore1
-rw-r--r--x86-bare-metal/mbr-palette/Makefile28
-rw-r--r--x86-bare-metal/mbr-palette/mbr.S89
-rw-r--r--x86-bare-metal/mbr-palette/mbr.ld25
-rw-r--r--x86-bare-metal/mbr-palette/x220.pngbin0 -> 376638 bytes
-rw-r--r--x86-bare-metal/mbr-palette/zmbr.zig29
6 files changed, 172 insertions, 0 deletions
diff --git a/x86-bare-metal/mbr-palette/.gitignore b/x86-bare-metal/mbr-palette/.gitignore
new file mode 100644
index 0000000..a92a67f
--- /dev/null
+++ b/x86-bare-metal/mbr-palette/.gitignore
@@ -0,0 +1 @@
+BUILD/ \ No newline at end of file
diff --git a/x86-bare-metal/mbr-palette/Makefile b/x86-bare-metal/mbr-palette/Makefile
new file mode 100644
index 0000000..7642791
--- /dev/null
+++ b/x86-bare-metal/mbr-palette/Makefile
@@ -0,0 +1,28 @@
+O := BUILD
+
+$(O)/boot: $(O)/boot.elf | dump_info
+ objcopy -O binary $< $@
+
+$(O)/boot.elf: mbr.ld $(O)/mbr.o $(O)/zmbr.o
+ ld -o $@ -nostdlib -T $^
+
+$(O)/mbr.o: mbr.S | $(O)
+ gcc -m32 -c -o $@ -ffreestanding mbr.S
+
+$(O)/zmbr.o: zmbr.zig | $(O)
+ zig build-obj -femit-bin=$@ -target x86-freestanding-none -O ReleaseSmall $<
+
+clean:
+ $(RM) -r $(O)
+
+$(O):
+ mkdir -p $(O)
+
+dump_info: $(O)/boot.elf
+ objdump -Mintel --disassemble=kmain --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-palette/mbr.S b/x86-bare-metal/mbr-palette/mbr.S
new file mode 100644
index 0000000..858f3f8
--- /dev/null
+++ b/x86-bare-metal/mbr-palette/mbr.S
@@ -0,0 +1,89 @@
+// -- BOOT TEXT SECTION ---------------------------------------------------------
+
+.code16
+.intel_syntax noprefix
+
+.section .boot, "ax", @progbits
+ // Disable interrupts.
+ cli
+
+ // Clear segment selectors.
+ xor ax, ax
+ mov ds, ax
+ mov es, ax
+ mov ss, ax
+ mov fs, ax
+ mov gs, ax
+
+ // Set cs to 0x0000, as some BIOSes load the MBR to either 07c0:0000 or 0000:7c000.
+ jmp 0x0000:entry_rm16
+
+entry_rm16:
+ // Set video mode 13h, see [1].
+ // * 320x200 graphic mode
+ // * 256 colors (8bit per pixel)
+ // * 0xA0000 screen address
+ //
+ // [1] http://www.ctyme.com/intr/rb-0069.htm
+ // [2] https://en.wikipedia.org/wiki/Mode_13h (shows color palette)
+ mov ax, 0x13
+ int 0x10
+
+ // Set blue screen and halt if setting video mode failed.
+ jnc 2f
+ mov ah, 0xb
+ mov bx, 1
+ int 0x10
+1:
+ hlt
+ jmp 1b
+2:
+
+ // Enable A20 address line.
+ in al, 0x92
+ or al, 2
+ out 0x92, al
+
+ // Load GDT descriptor.
+ lgdt [gdt_desc]
+
+ // Enable protected mode (set CR0.PE bit).
+ mov eax, cr0
+ or eax, (1 << 0)
+ mov cr0, eax
+
+ // Far jump which loads segment selector (0x0008) into cs.
+ // 0x0008 -> RPL=0, TI=0(GDT), I=1
+ jmp 0x0008:entry_pm32
+
+.code32
+entry_pm32:
+ // Select data segment selector (0x0010) for ds.
+ mov ax, gdt_data - gdt
+ mov ds, ax
+
+ // Initialize stack pointer.
+ // Real Mode memory (https://wiki.osdev.org/Memory_Map_(x86)
+ // 0x00000500 - 0x00007BFF | 29.75 KiB | conventional memory
+ mov esp, 0x7c00
+
+ // Enter zmbr.zig:kmain.
+ // Should not return, but for safety we emit a call rather than a jmp.
+ call kmain
+
+1:
+ hlt
+ jmp 1b
+
+// -- RODATA SECTION ------------------------------------------------------------
+
+.section .rodata, "a", @progbits
+.balign 8
+gdt:
+ .8byte 0x0000000000000000 // 0x00 | null descriptor
+ .8byte 0x00cf9a000000ffff // 0x08 | 32 bit, code (rx), present, dpl=0, g=4K, base=0, limit=fffff
+gdt_data:
+ .8byte 0x00cf92000000ffff // 0x10 | 32 bit, data (rw), present, dpl=0, g=4K, base=0, limit=fffff
+gdt_desc:
+ .2byte (. - gdt - 1) // size
+ .4byte gdt // address
diff --git a/x86-bare-metal/mbr-palette/mbr.ld b/x86-bare-metal/mbr-palette/mbr.ld
new file mode 100644
index 0000000..372ea42
--- /dev/null
+++ b/x86-bare-metal/mbr-palette/mbr.ld
@@ -0,0 +1,25 @@
+/*OUTPUT_FORMAT(binary)*/
+OUTPUT_FORMAT(elf32-i386)
+OUTPUT_ARCH(i386)
+
+SECTIONS {
+ . = 0x7c00;
+ .boot : {
+ *(.boot)
+ }
+ .text : { *(.text) }
+ .data : { *(.data) }
+ .rodata : { *(.rodata) }
+ _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-palette/x220.png b/x86-bare-metal/mbr-palette/x220.png
new file mode 100644
index 0000000..d3b6cc7
--- /dev/null
+++ b/x86-bare-metal/mbr-palette/x220.png
Binary files differ
diff --git a/x86-bare-metal/mbr-palette/zmbr.zig b/x86-bare-metal/mbr-palette/zmbr.zig
new file mode 100644
index 0000000..947cd06
--- /dev/null
+++ b/x86-bare-metal/mbr-palette/zmbr.zig
@@ -0,0 +1,29 @@
+const COLS = 320;
+const ROWS = 200;
+
+/// Draw the color palette.
+/// One color per column, remaining columns are drawn black.
+fn draw_palette(video: []u8) void {
+ for (0..ROWS) |r| {
+ for (0..COLS) |c| {
+ if (c < 256) {
+ video[COLS * r + c] = @truncate(c);
+ } else {
+ video[COLS * r + c] = 0;
+ }
+ }
+ }
+}
+
+// kmain should be "callconv(.naked)", once issue is fixed.
+// https://github.com/ziglang/zig/issues/18183
+export fn kmain() noreturn {
+ // Take a slice to 256-color VGA video memory (mode 13h graphic mode).
+ const video: []u8 = @as([*]u8, @ptrFromInt(0xA0000))[0 .. COLS * ROWS];
+
+ draw_palette(video);
+
+ while (true) {
+ asm volatile ("hlt");
+ }
+}