├── README.md ├── boot.asm ├── kernel.c ├── link.ld └── run.sh /README.md: -------------------------------------------------------------------------------- 1 | # beginOS 2 | A hello world OS from scratch for absolute beginners. 3 | 4 | # 5 | 6 | Run it by executing the run.sh script in Linux terminal. 7 | 8 | You need to have the programs seen inside the script file. 9 | -------------------------------------------------------------------------------- /boot.asm: -------------------------------------------------------------------------------- 1 | bits 32 2 | section .text 3 | align 4 4 | dd 0x1BADB002 5 | dd 0x00 6 | dd - (0x1BADB002+0x00) 7 | 8 | global start 9 | extern kernel_main 10 | start: 11 | cli ; clears the interrupts 12 | call kernel_main 13 | hlt -------------------------------------------------------------------------------- /kernel.c: -------------------------------------------------------------------------------- 1 | char* vgaBuffer = (char*) 0xb8000; // screen buffer (VGA Text Mode) 2 | 3 | #define VGA_HEIGHT 80 // screen height 4 | #define VGA_WIDTH 25 // screen width 5 | 6 | // We will come to this later 7 | void terminal_clear_screen(void); 8 | 9 | kernel_main() 10 | { 11 | terminal_clear_screen(); 12 | 13 | /* 14 | Each VGA character is 16 bits. First 8 bits determine the ASCII character 15 | and last 8 bits determine the color of that character. We used an 16 | 8 bit char pointer for avoiding complexity when writing to each element 17 | in VGA buffer. Therefore each 2 indexes here make up a single VGA character. 18 | 19 | (See Wikipedia "VGA Text Mode") 20 | */ 21 | 22 | vgaBuffer[0] = 'H'; // First 8 bits: character 23 | vgaBuffer[1] = 79; // Last 8 bits: color 24 | 25 | vgaBuffer[2] = 'e'; // First 8 bits: character 26 | vgaBuffer[3] = 15; // Last 8 bits: color 27 | 28 | vgaBuffer[4] = 'l'; // and so on... 29 | vgaBuffer[5] = 10; 30 | 31 | vgaBuffer[6] = 'l'; 32 | vgaBuffer[7] = 4; 33 | 34 | vgaBuffer[8] = 'o'; 35 | vgaBuffer[9] = 160; 36 | 37 | vgaBuffer[10] = '!'; 38 | vgaBuffer[11] = 64; 39 | 40 | int vgaBufferPos = 12; 41 | const char *message = " world!"; 42 | // Prints the message 43 | int i = 0; 44 | while (message[i] != '\0') { 45 | vgaBuffer[vgaBufferPos] = message[i]; 46 | vgaBuffer[vgaBufferPos+1] = 15; 47 | 48 | ++i; 49 | vgaBufferPos = vgaBufferPos + 2; 50 | } 51 | 52 | // Printing text in a practical way complicates the code a fair bit 53 | // so I have deleted those parts 54 | } 55 | 56 | // We have to clear the leftover text from boot screen 57 | void terminal_clear_screen(void) 58 | { 59 | /* 60 | We multiply width by 2 because we have an 8 bit pointer instead of a 61 | 16 bit. Therefore we have to increment twice on horizontal axis in 62 | order to reach to the next VGA character 63 | */ 64 | int limit = (VGA_WIDTH * 2) * (VGA_HEIGHT); 65 | for (int i = 0; i < limit; ++i) { 66 | vgaBuffer[i] = 0; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /link.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT(elf32-i386) 2 | ENTRY(start) 3 | SECTIONS 4 | { 5 | . = 0x100000; 6 | .text : { *(.text) } 7 | .data :{ *(.data) } 8 | .bss : { *(.bss) } 9 | } 10 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | nasm -f elf32 boot.asm -o boot.o 2 | gcc -m32 -c kernel.c -o kernel.o 3 | ld -m elf_i386 -T link.ld -o kernel.bin boot.o kernel.o 4 | qemu-system-i386 -kernel kernel.bin 5 | --------------------------------------------------------------------------------