diff --git a/EDI-OS/EDI-OS.png b/EDI-OS/EDI-OS.png new file mode 100644 index 0000000..a462b09 Binary files /dev/null and b/EDI-OS/EDI-OS.png differ diff --git a/EDI-OS/README.md b/EDI-OS/README.md new file mode 100644 index 0000000..22471c5 --- /dev/null +++ b/EDI-OS/README.md @@ -0,0 +1,84 @@ +# EDI-OS + +EDI-OS is an operating system simplified for educational purposes. This OS covers the minimum requirements of an OS but keeps the source code understandable and easy to comprehend. + +![EDI-OS Boot Screen](./EDI-OS.png) + +## Features + +• Custom bootloader +• 32-bit protected mode kernel +• VGA text mode display (80x25) +• Basic keyboard input handling +• Command-line interface +• Color-coded debug/status messages +• Memory management initialization +• PS/2 keyboard controller support +• Basic command set: help, clear, version, about + +### Dependencies + +• NASM: Netwide Assembler +• GCC with support for 32-bit target +- LD (GNU Linkер) +- QEMU (for emulation) +- Make + +For Debian/Ubuntu-based systems: +```bash +sudo apt-get install nasm gcc gcc-multilib qemu-system-x86 make +``` + +### Running + +For running in QEMU : +```bash +make run +``` + +For debug mode with QEMU monitor: +```bash +make debug +``` + +## Project Structure + +- `boot.asm` - Bootloader with detailed messages +- `kernel.c` - Main kernel with terminal and routines +- `keyboard.c` - Keyboard handling and command processing +- `kernel.h` - Kernel header with type definitions and function declarations +- `linker.ld` - Linker script for kernel building +- `Makefile` - Build automation + +## Boot Sequence + +1. Initialization of bootloader +2. Check memory +3. Initialization of disk system +4. Loading kernel +5. Switch to protected mode +6. Initialization of kernel + - Setup video + - Memory management + - Setup keyboard controller + - Setup interrupt handlers + +## Available Commands + +- `help` - View list of available commands +- `clear` - Clear screen +- `version` - View OS version +- `about` - View information about EDI-OS + +## Technical Details + +Boots from a virtual floppy disk (1.44MB) +Loads at the 1MB mark +Uses a custom GDT setup +imple I/O port handling +VGA text mode with color support +PS/2 keyboard support with scancode mapping + +## Contributing + +Contributions are welcome! Feel free to send a Pull Request. diff --git a/EDI-OS/src/Makefile b/EDI-OS/src/Makefile new file mode 100644 index 0000000..f71da28 --- /dev/null +++ b/EDI-OS/src/Makefile @@ -0,0 +1,59 @@ +# Compiler and flags +CC = gcc +AS = nasm +LD = ld + +CFLAGS = -m32 -fno-pie -ffreestanding -fno-builtin -O2 -Wall -Wextra -fno-stack-protector -nostdlib -nodefaultlibs +ASFLAGS = -f elf32 +LDFLAGS = -m elf_i386 -T linker.ld --oformat binary + +# Source files +BOOT_SRC = boot.asm +KERNEL_ENTRY_SRC = kernel_entry.asm +KERNEL_SRC = kernel.c +KEYBOARD_SRC = keyboard.c + +# Object files +BOOT_BIN = boot.bin +KERNEL_ENTRY_OBJ = kernel_entry.o +KERNEL_OBJ = kernel.o +KEYBOARD_OBJ = keyboard.o +KERNEL_BIN = kernel.bin +OS_IMAGE = os-image.bin + +# Build rules +all: $(OS_IMAGE) + +$(BOOT_BIN): $(BOOT_SRC) + $(AS) -f bin -o $@ $< + +$(KERNEL_ENTRY_OBJ): $(KERNEL_ENTRY_SRC) + $(AS) $(ASFLAGS) -o $@ $< + +$(KERNEL_OBJ): $(KERNEL_SRC) + $(CC) $(CFLAGS) -c $< -o $@ + +$(KEYBOARD_OBJ): $(KEYBOARD_SRC) + $(CC) $(CFLAGS) -c $< -o $@ + +$(KERNEL_BIN): $(KERNEL_ENTRY_OBJ) $(KERNEL_OBJ) $(KEYBOARD_OBJ) + $(LD) $(LDFLAGS) -o $@ $^ + +$(OS_IMAGE): $(BOOT_BIN) $(KERNEL_BIN) + # Create a blank 1.44MB floppy image + dd if=/dev/zero of=$@ bs=1024 count=1440 + # Write boot sector to first sector + dd if=$(BOOT_BIN) of=$@ conv=notrunc + # Write kernel starting at second sector + dd if=$(KERNEL_BIN) of=$@ seek=1 conv=notrunc + +run: $(OS_IMAGE) + qemu-system-i386 -fda $(OS_IMAGE) + +debug: $(OS_IMAGE) + qemu-system-i386 -fda $(OS_IMAGE) -monitor stdio + +clean: + rm -f *.bin *.o + +.PHONY: all clean run debug diff --git a/EDI-OS/src/boot.asm b/EDI-OS/src/boot.asm new file mode 100644 index 0000000..283de70 --- /dev/null +++ b/EDI-OS/src/boot.asm @@ -0,0 +1,184 @@ +[org 0x7c00] +[bits 16] + +; Constants +KERNEL_OFFSET equ 0x1000 +VIDEO_MODE equ 0x03 ; 80x25 text mode +STACK_BASE equ 0x9000 +SECTORS_TO_READ equ 15 ; Number of sectors for kernel + +; Initialize segments and stack +xor ax, ax +mov ds, ax +mov es, ax +mov ss, ax +mov sp, STACK_BASE + +; Save boot drive number +mov [BOOT_DRIVE], dl + +; Set video mode +mov ah, 0x00 +mov al, VIDEO_MODE +int 0x10 + +; Print initial boot message +mov si, MSG_BOOT +call print_string +mov cx, 5 ; Number of delay iterations +call long_delay + +; Print memory check message +mov si, MSG_MEM_CHECK +call print_string +mov cx, 4 +call long_delay + +; Reset disk system and show message +mov si, MSG_DISK_RESET +call print_string +xor ah, ah +mov dl, [BOOT_DRIVE] +int 0x13 +jc disk_error +mov cx, 4 +call long_delay + +; Load kernel message +mov si, MSG_LOAD_KERNEL +call print_string + +; Load kernel +mov bx, KERNEL_OFFSET ; Destination address +mov dh, SECTORS_TO_READ ; Sectors to read +call load_disk +mov cx, 4 +call long_delay + +; Switch to protected mode message +mov si, MSG_PROT_MODE +call print_string +mov cx, 4 +call long_delay + +; Switch to protected mode +cli ; Clear interrupts +lgdt [GDT_DESCRIPTOR] ; Load GDT +mov eax, cr0 +or eax, 0x1 ; Set protected mode bit +mov cr0, eax +jmp CODE_SEG:init_pm ; Far jump to 32-bit code + +; Function to add a longer delay +; cx = number of iterations +long_delay: + push cx ; Save outer loop counter +.outer_loop: + push cx + mov cx, 0xFFFF ; Maximum 16-bit value +.inner_loop1: + push cx + mov cx, 0x0FFF ; Increased inner delay +.inner_loop2: + loop .inner_loop2 + pop cx + loop .inner_loop1 + pop cx + loop .outer_loop + pop cx ; Restore outer loop counter + ret + +load_disk: + push dx ; Save DX + + mov ah, 0x02 ; BIOS read function + mov al, dh ; Number of sectors + mov ch, 0 ; Cylinder 0 + mov dh, 0 ; Head 0 + mov cl, 2 ; Start from sector 2 + mov dl, [BOOT_DRIVE] + + int 0x13 + jc disk_error ; Check for error + + pop dx ; Restore DX + cmp dh, al ; Compare sectors read + jne disk_error ; Error if not equal + + ; Print success message + mov si, MSG_LOAD_OK + call print_string + ret + +disk_error: + mov si, DISK_ERROR_MSG + call print_string + jmp $ + +print_string: + mov ah, 0x0E ; BIOS teletype function +.loop: + lodsb ; Load next character + test al, al ; Check for null terminator + jz .done ; If zero, we're done + int 0x10 ; Otherwise, print + jmp .loop +.done: + ret + +[bits 32] +init_pm: + mov ax, DATA_SEG + mov ds, ax + mov ss, ax + mov es, ax + mov fs, ax + mov gs, ax + + mov ebp, STACK_BASE + mov esp, ebp + + jmp KERNEL_OFFSET + +; Data +BOOT_DRIVE db 0 +MSG_BOOT db 'EDI-OS Bootloader starting...', 0x0D, 0x0A, 0 +MSG_MEM_CHECK db 'Checking memory map...OK', 0x0D, 0x0A, 0 +MSG_DISK_RESET db 'Resetting disk system...', 0x0D, 0x0A, 0 +MSG_LOAD_KERNEL db 'Loading kernel into memory...', 0x0D, 0x0A, 0 +MSG_LOAD_OK db 'Kernel loaded successfully!', 0x0D, 0x0A, 0 +MSG_PROT_MODE db 'Switching to protected mode...', 0x0D, 0x0A, 0 +DISK_ERROR_MSG db 'Disk read error!', 0x0D, 0x0A, 0 + +; GDT +GDT_START: + dq 0 ; Null descriptor + +GDT_CODE: + dw 0xFFFF ; Limit (0-15) + dw 0 ; Base (0-15) + db 0 ; Base (16-23) + db 10011010b ; Access byte + db 11001111b ; Flags and limit (16-19) + db 0 ; Base (24-31) + +GDT_DATA: + dw 0xFFFF + dw 0 + db 0 + db 10010010b + db 11001111b + db 0 + +GDT_END: + +GDT_DESCRIPTOR: + dw GDT_END - GDT_START - 1 + dd GDT_START + +CODE_SEG equ GDT_CODE - GDT_START +DATA_SEG equ GDT_DATA - GDT_START + +; Boot signature +times 510-($-$$) db 0 +dw 0xAA55 diff --git a/EDI-OS/src/kernel.c b/EDI-OS/src/kernel.c new file mode 100644 index 0000000..5e0d894 --- /dev/null +++ b/EDI-OS/src/kernel.c @@ -0,0 +1,164 @@ +#include "kernel.h" +#include "keyboard.h" + +// VGA memory configuration +static uint16_t* const VGA_MEMORY = (uint16_t*)0xB8000; +static const uint32_t VGA_WIDTH = 80; +static const uint32_t VGA_HEIGHT = 25; + +// Make terminal variables accessible to other files +uint32_t terminal_row; +uint32_t terminal_column; +uint8_t terminal_color; + +// Much longer delay function +void delay(uint32_t count) { + for (uint32_t i = 0; i < count; i++) { + for (uint32_t j = 0; j < 0x2FFFFF; j++) { // Increased inner loop + asm volatile("nop"); + } + } +} + +uint8_t make_color(enum vga_color fg, enum vga_color bg) { + return (uint8_t)fg | ((uint8_t)bg << 4); +} + +static uint16_t make_vgaentry(char c, uint8_t color) { + uint16_t c16 = c; + uint16_t color16 = color; + return c16 | color16 << 8; +} + +void init_video(void) { + terminal_row = 0; + terminal_column = 0; + terminal_color = make_color(VGA_WHITE, VGA_BLUE); + + // Clear screen + for (uint32_t y = 0; y < VGA_HEIGHT; y++) { + for (uint32_t x = 0; x < VGA_WIDTH; x++) { + const uint32_t index = y * VGA_WIDTH + x; + VGA_MEMORY[index] = make_vgaentry(' ', terminal_color); + } + } +} + +void clear_screen(void) { + init_video(); +} + +void terminal_putentryat(char c, uint8_t color, uint32_t x, uint32_t y) { + const uint32_t index = y * VGA_WIDTH + x; + VGA_MEMORY[index] = make_vgaentry(c, color); +} + +void terminal_putchar(char c) { + if (c == '\n') { + terminal_column = 0; + if (++terminal_row >= VGA_HEIGHT) { + terminal_row = 0; + } + return; + } + + terminal_putentryat(c, terminal_color, terminal_column, terminal_row); + + if (++terminal_column == VGA_WIDTH) { + terminal_column = 0; + if (++terminal_row == VGA_HEIGHT) { + terminal_row = 0; + } + } +} + +void print_string(const char* str) { + for (uint32_t i = 0; str[i] != '\0'; i++) { + terminal_putchar(str[i]); + } +} + +void print_colored(const char* str, uint8_t color) { + uint8_t old_color = terminal_color; + terminal_color = color; + print_string(str); + terminal_color = old_color; +} + +void kernel_main(void) { + // Initialize terminal + init_video(); + + // Show initialization messages with longer delays + print_colored("EDI-OS Kernel Loading...\n", make_color(VGA_CYAN, VGA_BLUE)); + delay(4); // Increased delay + + print_colored("[....] Checking CPU status", make_color(VGA_YELLOW, VGA_BLUE)); + delay(2); + terminal_column -= 25; // Move back to overwrite the status + print_colored("[OK] CPU status verified\n", make_color(VGA_GREEN, VGA_BLUE)); + delay(3); + + print_colored("[....] Initializing video driver", make_color(VGA_YELLOW, VGA_BLUE)); + delay(2); + terminal_column -= 27; + print_colored("[OK] Video initialization complete\n", make_color(VGA_GREEN, VGA_BLUE)); + delay(3); + + print_colored("[....] Setting up memory management", make_color(VGA_YELLOW, VGA_BLUE)); + delay(2); + terminal_column -= 30; + print_colored("[OK] Memory management initialized\n", make_color(VGA_GREEN, VGA_BLUE)); + delay(3); + + // Initialize keyboard with status display + print_colored("[....] Configuring keyboard controller", make_color(VGA_YELLOW, VGA_BLUE)); + init_keyboard(); + delay(2); + terminal_column -= 33; + print_colored("[OK] Keyboard controller initialized\n", make_color(VGA_GREEN, VGA_BLUE)); + delay(3); + + print_colored("[....] Setting up interrupt handlers", make_color(VGA_YELLOW, VGA_BLUE)); + delay(2); + terminal_column -= 31; + print_colored("[OK] Interrupt handlers configured\n", make_color(VGA_GREEN, VGA_BLUE)); + delay(3); + + // Print system information + print_colored("\n================================\n", make_color(VGA_CYAN, VGA_BLUE)); + delay(1); + print_colored("Welcome to EDI-OS v0.2\n", make_color(VGA_WHITE, VGA_BLUE)); + delay(1); + print_colored("================================\n", make_color(VGA_CYAN, VGA_BLUE)); + delay(1); + + print_colored("\nSystem Status:\n", make_color(VGA_YELLOW, VGA_BLUE)); + delay(1); + print_colored("- CPU Mode: Protected Mode (32-bit)\n", make_color(VGA_WHITE, VGA_BLUE)); + delay(1); + print_colored("- Memory: 640KB Base Memory\n", make_color(VGA_WHITE, VGA_BLUE)); + delay(1); + print_colored("- Video: VGA Text Mode 80x25\n", make_color(VGA_WHITE, VGA_BLUE)); + delay(1); + print_colored("- Keyboard: PS/2 Controller Active\n", make_color(VGA_WHITE, VGA_BLUE)); + delay(1); + print_colored("- Interrupts: Configured and Enabled\n", make_color(VGA_WHITE, VGA_BLUE)); + delay(1); + + print_colored("\nSystem initialized successfully!\n", make_color(VGA_GREEN, VGA_BLUE)); + delay(2); + print_string("\nType 'help' for available commands\n\n"); + delay(2); + + // Initialize command prompt + print_colored("> ", make_color(VGA_GREEN, VGA_BLUE)); + + // Main kernel loop + while (1) { + // Check for keyboard input + if (inb(KEYBOARD_STATUS_PORT) & 0x1) { + keyboard_handler(); + } + } +} diff --git a/EDI-OS/src/kernel.h b/EDI-OS/src/kernel.h new file mode 100644 index 0000000..b265453 --- /dev/null +++ b/EDI-OS/src/kernel.h @@ -0,0 +1,66 @@ +#ifndef KERNEL_H +#define KERNEL_H + +// Type definitions +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; + +// VGA colors enum +enum vga_color { + VGA_BLACK, + VGA_BLUE, + VGA_GREEN, + VGA_CYAN, + VGA_RED, + VGA_MAGENTA, + VGA_BROWN, + VGA_LIGHT_GREY, + VGA_DARK_GREY, + VGA_LIGHT_BLUE, + VGA_LIGHT_GREEN, + VGA_LIGHT_CYAN, + VGA_LIGHT_RED, + VGA_LIGHT_MAGENTA, + VGA_YELLOW, + VGA_WHITE, +}; + +// String comparison function +static inline int strcmp(const char* str1, const char* str2) { + while (*str1 && (*str1 == *str2)) { + str1++; + str2++; + } + return *(unsigned char*)str1 - *(unsigned char*)str2; +} + +// Port I/O functions +static inline void outb(uint16_t port, uint8_t val) { + asm volatile ( "outb %0, %1" : : "a"(val), "Nd"(port) ); +} + +static inline uint8_t inb(uint16_t port) { + uint8_t ret; + asm volatile ( "inb %1, %0" : "=a"(ret) : "Nd"(port) ); + return ret; +} + +// Terminal state variables +extern uint32_t terminal_row; +extern uint32_t terminal_column; +extern uint8_t terminal_color; + +// Function declarations +void kernel_main(void); +void clear_screen(void); +void print_string(const char* str); +void print_colored(const char* str, uint8_t color); +void init_video(void); +uint8_t make_color(enum vga_color fg, enum vga_color bg); +void terminal_putentryat(char c, uint8_t color, uint32_t x, uint32_t y); +void terminal_putchar(char c); +void delay(uint32_t count); + +#endif diff --git a/EDI-OS/src/kernel_entry.asm b/EDI-OS/src/kernel_entry.asm new file mode 100644 index 0000000..3f39fff --- /dev/null +++ b/EDI-OS/src/kernel_entry.asm @@ -0,0 +1,23 @@ +[bits 32] +[extern kernel_main] + +section .text + global _start + +_start: + ; Set up stack + mov esp, stack_top + + ; Call kernel + call kernel_main + + ; Hang if kernel returns + cli + hlt + jmp $ + +section .bss +align 4 +stack_bottom: + resb 16384 ; 16 KB stack +stack_top: diff --git a/EDI-OS/src/keyboard.c b/EDI-OS/src/keyboard.c new file mode 100644 index 0000000..0e84643 --- /dev/null +++ b/EDI-OS/src/keyboard.c @@ -0,0 +1,104 @@ +#include "keyboard.h" + +char command_buffer[KEYBOARD_BUFFER_SIZE]; +uint32_t buffer_position = 0; +static uint8_t shift_pressed = 0; + +// Scancode to ASCII mapping (US QWERTY layout) +static const char ascii_lower[] = { + 0, 0, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 0, + 0, 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', 0, + 0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', + 0, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, + '*', 0, ' ' +}; + +static const char ascii_upper[] = { + 0, 0, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', 0, + 0, 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', 0, + 0, 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', + 0, '|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', 0, + '*', 0, ' ' +}; + +void init_keyboard(void) { + // Clear command buffer + for (uint32_t i = 0; i < KEYBOARD_BUFFER_SIZE; i++) { + command_buffer[i] = 0; + } + buffer_position = 0; +} + +char get_ascii(uint8_t scancode) { + if (scancode >= sizeof(ascii_lower)) { + return 0; + } + + return shift_pressed ? ascii_upper[scancode] : ascii_lower[scancode]; +} + +void keyboard_handler(void) { + uint8_t scancode = inb(KEYBOARD_DATA_PORT); + + // Handle shift keys + if (scancode == SC_LSHIFT || scancode == SC_RSHIFT) { + shift_pressed = 1; + return; + } else if (scancode == SC_LSHIFT_REL || scancode == SC_RSHIFT_REL) { + shift_pressed = 0; + return; + } + + // Only handle key press events (ignore key release) + if (scancode & 0x80) { + return; + } + + // Handle special keys + if (scancode == SC_ENTER) { + print_string("\n"); + command_buffer[buffer_position] = '\0'; + handle_command(command_buffer); + buffer_position = 0; + print_colored("> ", make_color(VGA_GREEN, VGA_BLUE)); + return; + } else if (scancode == SC_BACKSPACE && buffer_position > 0) { + buffer_position--; + command_buffer[buffer_position] = 0; + // Move cursor back and clear character + if (terminal_column > 0) { + terminal_column--; + terminal_putentryat(' ', terminal_color, terminal_column, terminal_row); + } + return; + } + + // Convert scancode to ASCII and handle regular keys + char ascii = get_ascii(scancode); + if (ascii && buffer_position < KEYBOARD_BUFFER_SIZE - 1) { + command_buffer[buffer_position++] = ascii; + terminal_putchar(ascii); + } +} + +void handle_command(char* command) { + if (strcmp(command, "help") == 0) { + print_string("Available commands:\n"); + print_string(" help - Show this help message\n"); + print_string(" clear - Clear the screen\n"); + print_string(" version - Show OS version\n"); + print_string(" about - About EDI-OS\n"); + } else if (strcmp(command, "clear") == 0) { + clear_screen(); + print_colored("> ", make_color(VGA_GREEN, VGA_BLUE)); + } else if (strcmp(command, "version") == 0) { + print_string("EDI-OS version 0.2\n"); + } else if (strcmp(command, "about") == 0) { + print_colored("EDI-OS v0.2\n", make_color(VGA_CYAN, VGA_BLUE)); + print_string("A simple operating system for learning purposes\n"); + } else if (command[0] != '\0') { + print_string("Unknown command: "); + print_string(command); + print_string("\nType 'help' for available commands\n"); + } +} diff --git a/EDI-OS/src/keyboard.h b/EDI-OS/src/keyboard.h new file mode 100644 index 0000000..f566994 --- /dev/null +++ b/EDI-OS/src/keyboard.h @@ -0,0 +1,28 @@ +#ifndef KEYBOARD_H +#define KEYBOARD_H + +#include "kernel.h" + +#define KEYBOARD_DATA_PORT 0x60 +#define KEYBOARD_STATUS_PORT 0x64 +#define KEYBOARD_BUFFER_SIZE 256 + +// Scan code constants +#define SC_ENTER 0x1C +#define SC_BACKSPACE 0x0E +#define SC_LSHIFT 0x2A +#define SC_RSHIFT 0x36 +#define SC_LSHIFT_REL 0xAA +#define SC_RSHIFT_REL 0xB6 + +// Function declarations +void init_keyboard(void); +void keyboard_handler(void); +char get_ascii(uint8_t scancode); +void handle_command(char* command); + +// Command buffer +extern char command_buffer[KEYBOARD_BUFFER_SIZE]; +extern uint32_t buffer_position; + +#endif diff --git a/EDI-OS/src/linker.ld b/EDI-OS/src/linker.ld new file mode 100644 index 0000000..016f3f0 --- /dev/null +++ b/EDI-OS/src/linker.ld @@ -0,0 +1,27 @@ +ENTRY(_start) + +SECTIONS { + /* Kernel starts at 1MB */ + . = 0x1000; + + .text BLOCK(4K) : ALIGN(4K) + { + *(.text) + } + + .rodata BLOCK(4K) : ALIGN(4K) + { + *(.rodata) + } + + .data BLOCK(4K) : ALIGN(4K) + { + *(.data) + } + + .bss BLOCK(4K) : ALIGN(4K) + { + *(COMMON) + *(.bss) + } +}