D Bare Bones II
From OSDev Wiki
This tutorial needs to explain what the code does as tutorials are not just copy paste. You can help out by editing this page to include more context to what the code does. |
WAIT! Have you read Getting Started, Beginner Mistakes, and some of the related OS theory? |
Difficulty level |
---|
Beginner |
Kernel Designs |
---|
Models |
Other Concepts |
In this Tutorial, we will continue to write the kernel in D, making a basic output to the console.
Contents |
Overview
In this tutorial, we will continue to create our kernel on D, namely, we will make a minimal output to the console. Our file structure will be as follows:
- start.asm
- kernel.main.d
- linker.ld
start.asm
global start extern kmain ; Allow kmain() to be called from the assembly code extern start_ctors, end_ctors, start_dtors, end_dtors MODULEALIGN equ 1<<0 MEMINFO equ 1<<1 FLAGS equ MODULEALIGN | MEMINFO MAGIC equ 0x1BADB002 CHECKSUM equ -(MAGIC + FLAGS) section .text ; Next is the Grub Multiboot Header align 4 MultiBootHeader: dd MAGIC dd FLAGS dd CHECKSUM STACKSIZE equ 0x4000 ; 16 KiB if you're wondering static_ctors_loop: mov ebx, start_ctors jmp .test .body: call [ebx] add ebx,4 .test: cmp ebx, end_ctors jb .body start: mov esp, STACKSIZE+stack push eax push ebx call main static_dtors_loop: mov ebx, start_dtors jmp .test .body: call [ebx] add ebx,4 .test: cmp ebx, end_dtors jb .body cpuhalt: hlt jmp cpuhalt section .bss align 32 stack: resb STACKSIZE
Assemble that with:
nasm -f elf -o start.o start.asm
kernel.main.d
module kernel.main; import core.volatile; extern(C): // We denote that all functions in our file will have the extern(C) flag // Video memory address const ubyte* vidmem = cast(ubyte*)0xFFFF_8000_000B_8000; // Creating variables to indicate the cursor position. // All global dynamic variables need to be marked with the shared flag, because there is no TLS in our kernel shared int xpos = 0; shared int ypos = 0; // Creating a function to clear the console. void clear() { for (int i = 0; i < 80*25*2; i++) { volatileStore(vidmem + i, 0); } } // Here we output the symbol passed to the function with the color as 0x07(light gray) void putc_at(char symbol, int x, int y) { volatileStore(vidmem + x*2+y*160,symbol&0xFF); volatileStore(vidmem + x*2+y*160 + 1,0x07); } // Here we output a character with a check to see if it is a newline character. void putc(char symbol) { if(symbol == '\n') { xpos=0; ypos+=1; } else { putc_at(symbol,xpos,ypos); xpos+=1; if(xpos==80) {xpos=0; ypos+=1;} } } // Here we output a string by looping through it void puts(immutable(char*) str) { int counter = 0; while(str[counter] != 0) { putc(str[counter]); counter+=1; } } void kmain(uint magic, uint addr) { puts("Hello, world".ptr); // Output "Hello, world!" to the console for (;;) { //Loop forever. You can add your kernel logic here } }
You then compile that with:
gdc -fno-druntime -m32 -c kernel.main.d -o kernel.main.o -g
linker.ld
OUTPUT_FORMAT(elf32-i386) ENTRY (start) SECTIONS{ . = 0x00100000; .text :{ code = .; _code = .; __code = .; *(.text) *(.rodata) } .rodata ALIGN (0x1000) : { *(.rodata) } .data ALIGN (0x1000) : { data = .; _data = .; __data = .; *(.data) start_ctors = .; *(.ctors) end_ctors = .; start_dtors = .; *(.dtors) end_dtors = .; } .bss : { sbss = .; bss = .; _bss = .; __bss = .; *(COMMON) *(.bss) ebss = .; } end = .; _end = .; __end = .; }
Now finally you can link all of that with:
ld -melf_i386 -T linker.ld -o kernel.bin start.o kernel.main.o
Your kernel is now kernel.bin, and can now be booted by grub, or run in qemu:
qemu-system-i386 -kernel kernel.bin