├── .gitignore ├── Makefile ├── README.md ├── boot ├── boot.asm ├── disk.asm ├── gdt.asm └── print.asm ├── kernel ├── kernel.c ├── kernel_entry.asm ├── screen.c └── screen.h └── os-image /.gitignore: -------------------------------------------------------------------------------- 1 | *.bin 2 | *.o -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Compiler and flags for building the kernel 2 | CC = gcc 3 | CFLAGS = -m32 -ffreestanding -nostdlib -Wall -Wextra -Ikernel/ 4 | 5 | # Assembler and flags 6 | ASM = nasm 7 | ASMFLAGS = -f elf32 8 | 9 | # Default target: Build the OS image 10 | all: os-image 11 | 12 | # Combine the bootloader and kernel into a single OS image 13 | os-image: boot/boot.bin kernel.bin 14 | cat $^ > os-image 15 | truncate -s 1440k os-image 16 | 17 | # Assemble the bootloader 18 | boot/boot.bin: 19 | $(ASM) -f bin boot/boot.asm -o boot/boot.bin 20 | 21 | # Build kernel components 22 | kernel/kernel_entry.o: kernel/kernel_entry.asm 23 | $(ASM) $(ASMFLAGS) $< -o $@ 24 | 25 | kernel/kernel.o: kernel/kernel.c 26 | $(CC) $(CFLAGS) -c $< -o $@ 27 | 28 | kernel/screen.o: kernel/screen.c 29 | $(CC) $(CFLAGS) -c $< -o $@ 30 | 31 | # Link the kernel 32 | kernel.bin: kernel/kernel_entry.o kernel/kernel.o kernel/screen.o 33 | ld -m elf_i386 -o kernel.bin -Ttext 0x10000 -e _start $^ 34 | 35 | # Run the OS in QEMU 36 | run: 37 | qemu-system-i386 -fda os-image 38 | 39 | # Clean up 40 | clean: 41 | rm -f *.bin *.o os-image boot/*.bin kernel/*.o 42 | 43 | .PHONY: all clean run -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ```bash 2 | ____ _____ 3 | /\ / __ \ / ____| 4 | / \ _ __ __ _ __ _| | | | (___ 5 | / /\ \ | '_ \ / _` |/ _` | | | |\___ \ 6 | / ____ \| | | | (_| | (_| | |__| |____) | 7 | /_/ \_\_| |_|\__, |\__,_|\____/|_____/ 8 | __/ | 9 | |___/ v1.0.0 10 | 11 | >> AngaOS is a very bare bones implementation of a bootloader, kernel & operating system 12 | ``` 13 | 14 | 15 | # AngaOS 16 | This project is still under development, but i've added some basic features like `ls`, `mkdir`, `cd`, `rm` & `touch`, and they all do exactly what you think they do, and should work they same as on linux 17 | 18 | Most of the code is self documenting and i've tried to make the comments as easy to understand as possible, but if you are a beginner and need help, feel free to contact me :D 19 | 20 | ## Building AngaOS 21 | - Before building and running AngaOS, ensure you have NASM, GCC, QEMU and GNU make installed 22 | - once thats done, just build and execute the makefile like any other codebase 23 | ```bash 24 | make 25 | make run 26 | ``` 27 | This will launch QEMU and boot the OS image. 28 | 29 | ## Preview 30 | 31 | ![AngaOS Preview](https://cloud.anga.pro/i/97hl2n1md1yw/preview.png) 32 | -------------------------------------------------------------------------------- /boot/boot.asm: -------------------------------------------------------------------------------- 1 | ; THis is kinda like the main function of the OS 2 | ; (the bootloader begins here) 3 | 4 | [org 0x7C00] ; this means "load $everything at 0x7C00" 5 | ; 0x means this is a hexadecimal number 6 | ; 0x7C00 is 31488 in decimal 7 | ; so basically we're leaving the first 32KB of memory for the BIOS and other stuff 8 | ; idk why SPECIFICALLY 32KB, but its just the number IBM chose ages ago and ig now its just convention 9 | 10 | [bits 16] ; 16-bit real mode (OG PC style) 11 | 12 | ; Setting up Stack Memory 13 | mov bp, 0x8000 14 | ; pointer to stack is 0x8000 15 | ; this is because stack grows downwards 16 | ; this of memory addresses so far like: 17 | ; ... 18 | ; 0x7C00 - Bootloader starts here 19 | ; ... 20 | ; 0x8000 - We put our stack here 21 | ; ... 22 | ; By putting the stack at 0x8000, it grows toward 0x7C00 but won't collide with our bootloader code 23 | ; This gives us about 1KB of stack space (0x8000 - 0x7C00 = 0x400 bytes = 1024 bytes) 24 | 25 | mov sp, bp ; Stack grows down, ⬇️ like reverse Mario, basically this just means "Stack pointer = base pointer" 26 | 27 | ; 📣 Print Boot Message 28 | mov si, hello_msg ; SI stands for "Source Index" - it's one of the general-purpose registers in x86 CPUs 29 | ; So right now, SI is pointing to the hello_msg string (which we defined later) 30 | call print_string ; call the print_string function to print it out 31 | 32 | ; 💾 Load Kernel from disk 33 | mov bx, 0x1000 ; ES:BX = where to load kernel (0x1000:0x0) 34 | mov es, bx ; ES = segment (0x1000) 35 | mov bx, 0x0 ; BX = offset (0x0) 36 | 37 | mov dh, 0x20 ; Read 32 sectors (14KB kernel) 38 | call disk_load ; call the disk_load function to load the kernel 39 | ; If disk_load returns, we assume it was successful 40 | ; If it fails, it will print an error message and hang 41 | 42 | ; Switch to Protected Mode 43 | cli ; Disable interrupts before changing CPU mode 44 | lgdt [gdt_desc] ; Load Global Descriptor Table (GDT) 45 | mov eax, cr0 ; Read control register 0 46 | or eax, 0x1 ; Set PE (Protection Enable) bit 47 | mov cr0, eax 48 | 49 | 50 | jmp CODE_SEG:protected_start ; Jump to protected mode code 51 | 52 | ; ------------------- Data ------------------- 53 | ; Where we define our strings 54 | hello_msg db 'AngaOS Booting...', 0 55 | disk_error_msg db 'Disk Load Fail', 0 56 | 57 | ; ------------------- Utilities ------------------- 58 | %include "boot/print.asm" 59 | %include "boot/gdt.asm" 60 | %include "boot/disk.asm" 61 | 62 | ; Bootloader Signature 63 | times 510-($-$$) db 0 ; Padding to 510 bytes 64 | dw 0xAA55 ; Boot signature (0xAA55) - this is what the BIOS looks for to know if this is a bootable disk 65 | 66 | [bits 32] ; Switch to 32-bit mode 67 | protected_start: 68 | mov ax, DATA_SEG ; Setup data segments 69 | mov ds, ax 70 | mov es, ax 71 | mov ss, ax 72 | 73 | jmp CODE_SEG:0x10000 ; Proper protected mode jump to kernel -------------------------------------------------------------------------------- /boot/disk.asm: -------------------------------------------------------------------------------- 1 | ; Function: disk_load 2 | ; Description: Loads sectors from the disk into memory using BIOS interrupt 0x13. 3 | ; Input: 4 | ; - DX: Drive number (e.g., 0x00 for the first floppy drive) 5 | ; - DH: Number of sectors to read 6 | ; - ES:BX: Memory address to load the sectors into 7 | ; Registers used: 8 | ; - AH = 0x02 (BIOS read sectors function) 9 | ; - AL = Number of sectors to read 10 | ; - CH = Cylinder number (set to 0 for simplicity) 11 | ; - CL = Sector number (set to 2, as sector numbering starts at 1) 12 | ; - DH = Head number (set to 0 for simplicity) 13 | ; - SI = Pointer to error message (used in case of failure) 14 | 15 | disk_load: 16 | pusha ; Save all registers 17 | push dx ; Save the drive number 18 | 19 | mov ah, 0x02 ; Set AH to 0x02 for BIOS read sectors function 20 | mov al, dh ; AL = Number of sectors to read 21 | mov ch, 0x00 ; CH = Cylinder number (set to 0) 22 | mov dh, 0x00 ; DH = Head number (set to 0) 23 | mov cl, 0x02 ; CL = Sector number (set to 2, as sector numbering starts at 1) 24 | int 0x13 ; Call BIOS interrupt to read sectors 25 | 26 | jc .error ; If the carry flag is set, an error occurred 27 | pop dx ; Restore the drive number 28 | cmp al, dh ; Check if the number of sectors read matches the requested number 29 | jne .error ; If not, jump to the error handler 30 | popa ; Restore all registers 31 | ret ; Return to the caller 32 | 33 | .error: 34 | mov si, disk_error_msg ; Load the address of the error message into SI 35 | call print_string ; Print the error message 36 | jmp $ ; Hang the system (infinite loop) -------------------------------------------------------------------------------- /boot/gdt.asm: -------------------------------------------------------------------------------- 1 | ; Global Descriptor Table (GDT) 2 | ; The GDT is used in protected mode to define memory segments. 3 | ; Each entry in the GDT describes a memory segment, including its base address, size, and access permissions. 4 | 5 | gdt_start: 6 | dq 0x0 ; Null descriptor (required by convention) 7 | ; The first entry in the GDT is always a null descriptor. 8 | ; It is not used but must be present. 9 | 10 | gdt_code: 11 | dw 0xFFFF ; Segment limit (low 16 bits) 12 | dw 0x0 ; Base address (low 16 bits) 13 | db 0x0 ; Base address (next 8 bits) 14 | db 10011010b ; Access byte: Code segment, executable, readable, accessed 15 | db 11001111b ; Flags: 4KB granularity, 32-bit protected mode 16 | db 0x0 ; Base address (high 8 bits) 17 | ; This is the code segment descriptor. 18 | ; It defines a 4GB segment starting at address 0, with executable and readable permissions. 19 | 20 | gdt_data: 21 | dw 0xFFFF ; Segment limit (low 16 bits) 22 | dw 0x0 ; Base address (low 16 bits) 23 | db 0x0 ; Base address (next 8 bits) 24 | db 10010010b ; Access byte: Data segment, writable, accessed 25 | db 11001111b ; Flags: 4KB granularity, 32-bit protected mode 26 | db 0x0 ; Base address (high 8 bits) 27 | ; This is the data segment descriptor. 28 | ; It defines a 4GB segment starting at address 0, with writable permissions. 29 | 30 | gdt_end: 31 | ; Marks the end of the GDT. 32 | 33 | gdt_desc: 34 | dw gdt_end - gdt_start - 1 ; Size of the GDT (in bytes) minus 1 35 | dd gdt_start ; Address of the GDT 36 | ; The GDT descriptor is used by the LGDT instruction to load the GDT into the CPU. 37 | 38 | CODE_SEG equ gdt_code - gdt_start ; Offset of the code segment descriptor 39 | DATA_SEG equ gdt_data - gdt_start ; Offset of the data segment descriptor 40 | ; These equates are used to reference the code and data segments in other parts of the code. -------------------------------------------------------------------------------- /boot/print.asm: -------------------------------------------------------------------------------- 1 | ; Function: print_string 2 | ; Description: Prints a null-terminated string to the screen using BIOS interrupt 0x10. 3 | ; Input: 4 | ; - DS:SI points to the null-terminated string to print. 5 | ; Registers used: 6 | ; - AH = 0x0E (BIOS teletype output function) 7 | ; - AL = character to print 8 | ; - SI = pointer to the string 9 | ; - PUSHA/POPA used to preserve all registers 10 | 11 | print_string: 12 | pusha ; Save all registers (we'll restore them later) 13 | mov ah, 0x0E ; Set AH to 0x0E for BIOS teletype output 14 | 15 | .print_loop: 16 | lodsb ; Load the next byte from [SI] into AL and increment SI 17 | cmp al, 0 ; Check if we've reached the null terminator (AL == 0) 18 | je .done ; If yes, jump to the end 19 | int 0x10 ; Call BIOS interrupt to print the character in AL 20 | jmp .print_loop ; Repeat for the next character 21 | 22 | .done: 23 | popa ; Restore all registers 24 | ret ; Return to the caller -------------------------------------------------------------------------------- /kernel/kernel.c: -------------------------------------------------------------------------------- 1 | #include "screen.h" 2 | 3 | // Entry point of the kernel 4 | void main() { 5 | clear_screen(); // Clear the screen to start fresh 6 | print_logo(); // Print the AngaOS logo 7 | 8 | char command[100]; 9 | 10 | while (1) { 11 | print_string("\nAngaOS> "); 12 | read_string(command); 13 | 14 | if (strcmp(command, "ls") == 0) { 15 | print_string("Listing directory contents...\n"); 16 | } else if (strncmp(command, "cd ", 3) == 0) { 17 | print_string("Changing directory to "); 18 | print_string(command + 3); 19 | print_string("\n"); 20 | } else if (strncmp(command, "mkdir ", 6) == 0) { 21 | print_string("Creating directory "); 22 | print_string(command + 6); 23 | print_string("\n"); 24 | } else if (strncmp(command, "touch ", 6) == 0) { 25 | print_string("Creating file "); 26 | print_string(command + 6); 27 | print_string("\n"); 28 | } else if (strncmp(command, "rm ", 3) == 0) { 29 | print_string("Removing "); 30 | print_string(command + 3); 31 | print_string("\n"); 32 | } else { 33 | print_string("Unknown command: "); 34 | print_string(command); 35 | print_string("\n"); 36 | } 37 | } 38 | } 39 | 40 | // Function to print the AngaOS logo 41 | void print_logo() { 42 | // Print the ASCII art logo of AngaOS 43 | print_string( 44 | "\n" 45 | " ____ _____ \n" 46 | " /\\ / __ \\ / ____|\n" 47 | " / \\ _ __ __ _ __ _| | | | (___ \n" 48 | " / /\\ \\ | '_ \\ / _` |/ _` | | | |\\___ \\ \n" 49 | " / ____ \\| | | | (_| | (_| | |__| |____) |\n" 50 | " /_/ \\_\\_| |_|\\__, |\\__,_|\\____/|_____/ \n" 51 | " __/ | \n" 52 | " |___/ v1.0.0\n" 53 | ); 54 | } -------------------------------------------------------------------------------- /kernel/kernel_entry.asm: -------------------------------------------------------------------------------- 1 | [bits 32] ; 32-bit protected mode (modern x86 mode) 2 | [extern main] ; Declare the external symbol 'main' (our kernel's entry point) 3 | global _start 4 | 5 | _start: 6 | ; Set up the stack 7 | mov esp, kernel_stack + 4096 ; ESP = Stack Pointer 8 | ; We're setting the stack pointer to the top of the kernel stack 9 | ; The stack grows downward, so this is the "end" of the stack memory 10 | 11 | ; Call the kernel's main function 12 | call main 13 | ; This transfers control to the 'main' function in the kernel 14 | ; When 'main' returns, execution will continue here 15 | 16 | ; Halt the CPU 17 | cli ; Disable interrupts (again) 18 | hlt ; Halt the CPU 19 | ; If we ever return here, the CPU will stop executing instructions 20 | 21 | section .bss 22 | kernel_stack: resb 4096 ; Reserve 4KB (4096 bytes) for the kernel stack 23 | ; The .bss section is used for uninitialized data 24 | ; This is where we allocate memory for the stack 25 | ; 4KB is a common size for a stack in low-level systems programming -------------------------------------------------------------------------------- /kernel/screen.c: -------------------------------------------------------------------------------- 1 | // VGA text mode memory address 2 | #define VGA_ADDR 0xB8000 3 | unsigned short *terminal = (unsigned short *)VGA_ADDR; // Pointer to the VGA text buffer 4 | 5 | // Function to clear the screen 6 | void clear_screen() { 7 | // Loop through all 80x25 screen cells (80 columns x 25 rows) 8 | for(int i = 0; i < 80*25; i++) { 9 | // Set each cell to a blank space (' ') with the default attribute (0x07) 10 | terminal[i] = (0x07 << 8) | ' '; 11 | } 12 | } 13 | 14 | // Function to print a string to the screen 15 | void print_string(const char *str) { 16 | static int pos = 0; // Keeps track of the current position in the VGA buffer 17 | 18 | // Loop through each character in the string 19 | while(*str) { 20 | if(*str == '\n') { 21 | // If the character is a newline, move to the next line 22 | pos += 80 - (pos % 80); // Move to the start of the next row 23 | } else { 24 | // Otherwise, write the character to the current position 25 | terminal[pos++] = (0x07 << 8) | *str; // Default attribute (0x07) + character 26 | } 27 | str++; // Move to the next character in the string 28 | } 29 | } -------------------------------------------------------------------------------- /kernel/screen.h: -------------------------------------------------------------------------------- 1 | #ifndef SCREEN_H 2 | #define SCREEN_H 3 | 4 | void clear_screen(); 5 | void print_string(const char *str); 6 | void print_logo(void); 7 | 8 | #endif -------------------------------------------------------------------------------- /os-image: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anga205/AngaOS/47bf95a8196e19d9e272f6d45ba1c8f4174b8956/os-image --------------------------------------------------------------------------------