diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 70e34ec..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "C_Cpp.errorSquiggles": "disabled" -} \ No newline at end of file diff --git a/Makefile b/Makefile index 4cc0d82..bff0bf1 100644 --- a/Makefile +++ b/Makefile @@ -1,37 +1,105 @@ -FILES = ./build/kernel.asm.o ./build/isr.o ./build/kernel.o ./build/vga.o ./build/panic.o ./build/idt.o ./build/pic.o ./build/keyboard.o ./build/printk.o ./build/shell.o -FLAGS = -g -ffreestanding -nostdlib -nostartfiles -nodefaultlibs -Wall -O0 -Iinc - -all: - nasm -f bin ./src/boot.asm -o ./bin/boot.bin - nasm -f elf -g ./src/kernel.asm -o ./build/kernel.asm.o - nasm -f elf -g ./src/isr.asm -o ./build/isr.o - i686-elf-gcc -I./src $(FLAGS) -std=gnu99 -c ./src/kernel.c -o ./build/kernel.o - i686-elf-gcc -I./src $(FLAGS) -std=gnu99 -c ./src/vga.c -o ./build/vga.o - i686-elf-gcc -I./src $(FLAGS) -std=gnu99 -c ./src/panic.c -o ./build/panic.o - i686-elf-gcc -I./src $(FLAGS) -std=gnu99 -c ./src/idt.c -o ./build/idt.o - i686-elf-gcc -I./src $(FLAGS) -std=gnu99 -c ./src/pic.c -o ./build/pic.o - i686-elf-gcc -I./src $(FLAGS) -std=gnu99 -c ./src/keyboard.c -o ./build/keyboard.o - i686-elf-gcc -I./src $(FLAGS) -std=gnu99 -c ./src/printk.c -o ./build/printk.o - i686-elf-gcc -I./src $(FLAGS) -std=gnu99 -c ./src/shell.c -o ./build/shell.o - i686-elf-ld -g -relocatable $(FILES) -o ./build/completeKernel.o - i686-elf-gcc $(FLAGS) -T ./linkerScript.ld -o ./bin/kernel.bin -ffreestanding -O0 -nostdlib ./build/completeKernel.o - - dd if=./bin/boot.bin >> ./bin/os.bin - dd if=./bin/kernel.bin >> ./bin/os.bin - dd if=/dev/zero bs=512 count=8 >> ./bin/os.bin +CROSS ?= i686-elf- +AS := nasm +CC := $(CROSS)gcc +LD := $(CROSS)ld +OBJCOPY := $(CROSS)objcopy +QEMU := qemu-system-i386 + +BUILD_DIR := build + +STAGE1_SRC := src/bootloader/stage1/boot.asm +STAGE2_SRC := src/bootloader/stage2/main.asm +STAGE2_C_SRC := src/bootloader/stage2/main.c +STAGE2_STDIO_SRC := src/bootloader/stage2/stdio.c +STAGE2_X86_ASM_SRC := src/bootloader/stage2/x86.asm + +STAGE1_BIN := $(BUILD_DIR)/boot/stage1.bin +STAGE2_OBJ := $(BUILD_DIR)/boot/stage2.o +STAGE2_C_OBJ := $(BUILD_DIR)/boot/stage2_main.o +STAGE2_STDIO_OBJ := $(BUILD_DIR)/boot/stage2_stdio.o +STAGE2_X86_ASM_OBJ := $(BUILD_DIR)/boot/stage2_x86_asm.o +STAGE2_ELF := $(BUILD_DIR)/boot/stage2.elf +STAGE2_BIN := $(BUILD_DIR)/boot/stage2.bin +STAGE2_PAD := $(BUILD_DIR)/boot/stage2.padded.bin + +KERNEL_MAIN_SRC := src/kernel/main.asm +KERNEL_C_SRC := src/kernel/kernel.c +KERNEL_MAIN_OBJ := $(BUILD_DIR)/kernel/main.o +KERNEL_C_OBJ := $(BUILD_DIR)/kernel/kernel.o +KERNEL_ELF := $(BUILD_DIR)/kernel.elf +KERNEL_BIN := $(BUILD_DIR)/kernel.bin +KERNEL_PAD := $(BUILD_DIR)/kernel.padded.bin + +DISK_IMAGE := $(BUILD_DIR)/ginnos.img + +STAGE2_SECTORS := 4 +KERNEL_SECTORS := 12 +SECTOR_SIZE := 512 +STAGE2_MAX_BYTES := $(shell echo $$(( $(STAGE2_SECTORS) * $(SECTOR_SIZE) ))) +KERNEL_MAX_BYTES := $(shell echo $$(( $(KERNEL_SECTORS) * $(SECTOR_SIZE) ))) + +CFLAGS := -std=gnu11 -ffreestanding -O2 -Wall -Wextra -m32 +STAGE2_CFLAGS := -std=gnu11 -ffreestanding -O2 -Wall -Wextra -m16 -fno-pic -fno-stack-protector +LDFLAGS := -T linker/kernel.ld -nostdlib +STAGE2_LDFLAGS := -T linker/stage2.ld -nostdlib + +.PHONY: all run clean check-tools + +all: check-tools $(DISK_IMAGE) + +check-tools: + @command -v $(CC) >/dev/null 2>&1 || { echo "Missing tool: $(CC)"; exit 1; } + @command -v $(LD) >/dev/null 2>&1 || { echo "Missing tool: $(LD)"; exit 1; } + @command -v $(OBJCOPY) >/dev/null 2>&1 || { echo "Missing tool: $(OBJCOPY)"; exit 1; } + @command -v $(AS) >/dev/null 2>&1 || { echo "Missing tool: $(AS)"; exit 1; } + @command -v $(QEMU) >/dev/null 2>&1 || { echo "Missing tool: $(QEMU)"; exit 1; } + +$(STAGE1_BIN): $(STAGE1_SRC) + mkdir -p $(dir $@) + $(AS) -f bin $< -o $@ + @test $$(wc -c < $@) -eq 512 || { echo "stage1 must be exactly 512 bytes"; exit 1; } + +$(STAGE2_BIN): $(STAGE2_SRC) $(STAGE2_C_SRC) $(STAGE2_STDIO_SRC) $(STAGE2_X86_ASM_SRC) linker/stage2.ld + mkdir -p $(dir $@) + $(AS) -f elf32 $(STAGE2_SRC) -o $(STAGE2_OBJ) + $(CC) $(STAGE2_CFLAGS) -c $(STAGE2_C_SRC) -o $(STAGE2_C_OBJ) + $(CC) $(STAGE2_CFLAGS) -c $(STAGE2_STDIO_SRC) -o $(STAGE2_STDIO_OBJ) + $(AS) -f elf32 $(STAGE2_X86_ASM_SRC) -o $(STAGE2_X86_ASM_OBJ) + $(LD) $(STAGE2_LDFLAGS) -o $(STAGE2_ELF) $(STAGE2_OBJ) $(STAGE2_C_OBJ) $(STAGE2_STDIO_OBJ) $(STAGE2_X86_ASM_OBJ) + $(OBJCOPY) -O binary $(STAGE2_ELF) $(STAGE2_BIN) + @test $$(wc -c < $(STAGE2_BIN)) -le $(STAGE2_MAX_BYTES) || { echo "stage2 exceeds $(STAGE2_MAX_BYTES) bytes"; exit 1; } + +$(STAGE2_PAD): $(STAGE2_BIN) + cp $(STAGE2_BIN) $@ + truncate -s $(STAGE2_MAX_BYTES) $@ + +$(KERNEL_MAIN_OBJ): $(KERNEL_MAIN_SRC) + mkdir -p $(dir $@) + $(AS) -f elf32 $< -o $@ + +$(KERNEL_C_OBJ): $(KERNEL_C_SRC) + mkdir -p $(dir $@) + $(CC) $(CFLAGS) -c $< -o $@ + +$(KERNEL_ELF): $(KERNEL_MAIN_OBJ) $(KERNEL_C_OBJ) linker/kernel.ld + $(LD) $(LDFLAGS) -o $@ $(KERNEL_MAIN_OBJ) $(KERNEL_C_OBJ) + +$(KERNEL_BIN): $(KERNEL_ELF) + $(OBJCOPY) -O binary $(KERNEL_ELF) $@ + @test $$(wc -c < $@) -le $(KERNEL_MAX_BYTES) || { echo "kernel exceeds $(KERNEL_MAX_BYTES) bytes"; exit 1; } + +$(KERNEL_PAD): $(KERNEL_BIN) + cp $(KERNEL_BIN) $@ + truncate -s $(KERNEL_MAX_BYTES) $@ + +$(DISK_IMAGE): $(STAGE1_BIN) $(STAGE2_PAD) $(KERNEL_PAD) + truncate -s 1474560 $@ + dd if=$(STAGE1_BIN) of=$@ bs=512 seek=0 conv=notrunc status=none + dd if=$(STAGE2_PAD) of=$@ bs=512 seek=1 conv=notrunc status=none + dd if=$(KERNEL_PAD) of=$@ bs=512 seek=5 conv=notrunc status=none + +run: check-tools $(DISK_IMAGE) + $(QEMU) -drive if=floppy,format=raw,file=$(DISK_IMAGE) clean: - rm -f ./bin/boot.bin - rm -f ./bin/kernel.bin - rm -f ./bin/os.bin - rm -f ./build/kernel.asm.o - rm -f ./build/isr.o - rm -f ./build/kernel.o - rm -f ./build/vga.o - rm -f ./build/panic.o - rm -f ./build/idt.o - rm -f ./build/pic.o - rm -f ./build/keyboard.o - rm -f ./build/printk.o - rm -f ./build/shell.o - rm -f ./build/completeKernel.o \ No newline at end of file + rm -rf $(BUILD_DIR) diff --git a/README.md b/README.md index 1fe030a..02fc126 100644 --- a/README.md +++ b/README.md @@ -1,45 +1,103 @@ -# Ginnung OS +# GinnOS +GinnOS (Ginn short for Ginnungagap, and OS short for Operating System) is a personal hobby operating system, which i'm developing alongside a course on operating systems i'm taking at DTU. -This is a small personal project I am building alongside my operating systems course at DTU. -I am using it to learn more about operating systems development and therefore i don't really know what it will turn into. +I started this project because i wanted to understand how an operating system is really coded instead of just theory. I plan to keep building and improving it as i learn more. +The name GinnOS is taken from Ginnungagap, the primordial void in Norse mythology from which the world was created. -## Prerequisites +> "That was the age when nothing was; / There was no sand, nor sea, nor cool waves, / No earth nor sky nor grass there, / Only Ginnungagap." +> — Völuspá, Poetic Edda -To build this project on macOS, install: +## macOS Toolchain Setup (Manual) -- Xcode Command Line Tools: `xcode-select --install` -- Homebrew -- Build dependencies for the OSDev cross-compiler guide: - - `gmp` - - `mpfr` - - `libmpc` - - `texinfo` -- Tools used by this repository: - - `nasm` - - `qemu` or `qemu-system-i386` +### Install host dependencies -The cross-compiler is expected at `~/opt/cross/bin`, with `i686-elf-gcc` and `i686-elf-ld` available on your `PATH`. +Only install what this two-stage build needs: -## Build +```bash +brew install gmp mpfr libmpc texinfo nasm qemu +``` + +### Build i686 cross compiler manually + +Create build directories: + +```bash +mkdir -p "$HOME/src/cross" +mkdir -p "$HOME/opt/cross" +cd "$HOME/src/cross" +``` + +Build and install binutils: + +```bash +curl -LO https://ftp.gnu.org/gnu/binutils/binutils-2.42.tar.xz +tar -xf binutils-2.42.tar.xz +mkdir -p build-binutils +cd build-binutils + +../binutils-2.42/configure \ + --target=i686-elf \ + --prefix="$HOME/opt/cross" \ + --with-sysroot \ + --disable-nls \ + --disable-werror + +make -j"$(sysctl -n hw.ncpu)" +make install +cd .. +``` + +Build and install GCC (C only): + +```bash +curl -LO https://ftp.gnu.org/gnu/gcc/gcc-14.2.0/gcc-14.2.0.tar.xz +tar -xf gcc-14.2.0.tar.xz +mkdir -p build-gcc +cd build-gcc -Run the build script from the repository root: +../gcc-14.2.0/configure \ + --target=i686-elf \ + --prefix="$HOME/opt/cross" \ + --disable-nls \ + --enable-languages=c \ + --without-headers -```sh -./build.sh +make -j"$(sysctl -n hw.ncpu)" all-gcc all-target-libgcc +make install-gcc install-target-libgcc +cd .. ``` -This creates `bin/os.bin`. +### Add cross toolchain to PATH -## Boot +```bash +echo 'export PATH="$HOME/opt/cross/bin:$PATH"' >> ~/.zshrc +source ~/.zshrc +``` -After building, start the OS with: +### Verify required tools -```sh -qemu-system-i386 -m 64M -no-reboot -drive format=raw,file=bin/os.bin +```bash +i686-elf-gcc --version +i686-elf-ld --version +i686-elf-objcopy --version +nasm -v +qemu-system-i386 --version ``` -## Notes +## Build and Run + +From project root: + +```bash +make +make run +``` + +## Credits and Learning Resources + +- Nanobyte: https://www.youtube.com/@nanobyte-dev + Operating system tutorials that have been a big help. -- If `./build.sh` cannot find `i686-elf-gcc`, make sure your cross-compiler is installed under `~/opt/cross` and that `~/opt/cross/bin` is on `PATH`. -- If QEMU is installed under a different binary name on your system, use that executable instead. +- OSDev Wiki: https://wiki.osdev.org/ + The main reference used for low level concepts and implementation. \ No newline at end of file diff --git a/build.sh b/build.sh deleted file mode 100755 index c8d4148..0000000 --- a/build.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -export PREFIX="$HOME/opt/cross" -export TARGET=i686-elf -export PATH="$PREFIX/bin:$PATH" -make all \ No newline at end of file diff --git a/linker/kernel.ld b/linker/kernel.ld new file mode 100644 index 0000000..f4b2151 --- /dev/null +++ b/linker/kernel.ld @@ -0,0 +1,27 @@ +ENTRY(_start) + +SECTIONS +{ + . = 0x10000; + + .text : + { + *(.text*) + } + + .rodata : + { + *(.rodata*) + } + + .data : + { + *(.data*) + } + + .bss : + { + *(.bss*) + *(COMMON) + } +} diff --git a/linker/stage2.ld b/linker/stage2.ld new file mode 100644 index 0000000..cee89cf --- /dev/null +++ b/linker/stage2.ld @@ -0,0 +1,27 @@ +ENTRY(_start) + +SECTIONS +{ + . = 0x8000; + + .text : + { + *(.text*) + } + + .rodata : + { + *(.rodata*) + } + + .data : + { + *(.data*) + } + + .bss : + { + *(.bss*) + *(COMMON) + } +} diff --git a/linkerScript.ld b/linkerScript.ld deleted file mode 100644 index 675435e..0000000 --- a/linkerScript.ld +++ /dev/null @@ -1,26 +0,0 @@ -ENTRY(_start) -OUTPUT_FORMAT(binary) -SECTIONS -{ - . = 0x00010000; - .text : ALIGN(4096) - { - *(.text) - } - - .rodata : ALIGN(4096) - { - *(.rodata) - } - - .data : ALIGN(4096) - { - *(.data) - } - - .bss : ALIGN(4096) - { - *(COMMON) - *(.bss) - } -} \ No newline at end of file diff --git a/src/boot.asm b/src/boot.asm deleted file mode 100644 index 8554b56..0000000 --- a/src/boot.asm +++ /dev/null @@ -1,90 +0,0 @@ -[BITS 16] ; 16-bit real mode -[ORG 0x7c00] ; boot sector is loaded at memory address 0 - -CODE_OFFSET equ 0x8 -DATA_OFFSET equ 0x10 - -KERNEL_LOAD_SEG equ 0x1000 ; segment where the kernel will be loaded -KERNEL_START_ADDR equ 0x10000 ; physical address where the kernel will be loaded (64KB) - -; bootloader entry point -start: - ; set up the stack and segment registers - cli ; disable interrupts - mov ax, 0x00 ; zero the segment register - mov ds, ax ; zero the data segment register - mov es, ax ; zero the extra segment register - mov ss, ax ; zero the stack segment register - mov sp, 0x7c00 ; point stack to the boot sector - sti ; enable interrupts again - - ; load kernel from disk into memory - mov ax, KERNEL_LOAD_SEG ; load segment where kernel will be loaded - mov es, ax ; set the kernel load segment - xor bx, bx ; load at offset 0 within that segment - mov dh, 0x00 ; head number (0 for first head) - mov dl, 0x80 ; drive number (0x80 for first hard disk) - mov cl, 0x02 ; sector number (2 for the first sector of the kernel) - mov ch, 0x00 ; cylinder number (0 for the first cylinder) - mov ah, 0x02 ; BIOS function to read sectors from disk - mov al, 18 ; number of sectors to read (18 sectors = 9KB) - int 0x13 ; call BIOS interrupt to read sectors - - jc disk_read_error ; jump to error handler if disk read fails - -; switch to protected mode -load_pm: - cli ; disable interrupts - lgdt [gdt_descriptor] ; load the GDT descriptor into GDTR - mov eax, cr0 ; load the control register - or eax, 1 ; set the PE (Protection Enable) bit - mov cr0, eax ; write back to control register - jmp CODE_OFFSET:PModeMain ; jump to protected mode code - -disk_read_error: - hlt ; halt the CPU if disk read fails - -; gdt implementation -gdt_start: - dd 0x00000000 ; null descriptor - dd 0x00000000 - - ; code segment descriptor - dw 0xFFFF ; limit low - dw 0x0000 ; base low - db 0x00 ; base middle - db 10011010b ; access byte (present, ring 0, code segment, executable, readable) - db 11001111b ; flags (granularity, 32-bit) - db 0x00 ; base high - - ; data segment descriptor - dw 0xFFFF ; limit low - dw 0x0000 ; base low - db 0x00 ; base middle - db 10010010b ; access byte (present, ring 0, data segment, writable) - db 11001111b ; flags (granularity, 32-bit) - db 0x00 ; base high - -gdt_end: - -gdt_descriptor: - dw gdt_end - gdt_start - 1 ; size of gdt (limit) - dd gdt_start ; base address of gdt - -[BITS 32] ; 32-bit protected mode -PModeMain: - mov ax, DATA_OFFSET ; load data segment selector - mov ds, ax ; set data segment register - mov es, ax ; set extra segment register - mov fs, ax ; set fs segment register - mov ss, ax ; set stack segment register - mov gs, ax ; set gs segment register - mov esp, 0x9c00 ; set stack pointer to a safe location - - jmp CODE_OFFSET:KERNEL_START_ADDR ; jump to the kernel entry point in protected mode - - -; fill the rest of the boot sector with zeros so the total size is 510 bytes -times 510 - ($ - $$) db 0 - -dw 0xAA55 ; boot signature (0xAA55) to indicate a valid boot sector \ No newline at end of file diff --git a/src/bootloader/stage1/boot.asm b/src/bootloader/stage1/boot.asm new file mode 100644 index 0000000..b94546b --- /dev/null +++ b/src/bootloader/stage1/boot.asm @@ -0,0 +1,44 @@ +; src/bootloader/stage1/boot.asm +; 16-bit boot sector loaded by BIOS at 0x7C00. +; Loads stage2 from disk and jumps to it. + +BITS 16 +ORG 0x7C00 + +STAGE2_LOAD_SEGMENT equ 0x0000 +STAGE2_LOAD_OFFSET equ 0x8000 +STAGE2_SECTORS equ 4 + +start: + cli + xor ax, ax + mov ds, ax + mov es, ax + mov ss, ax + mov sp, 0x7C00 + sti + + mov [boot_drive], dl + + mov ah, 0x02 ; BIOS read sectors + mov al, STAGE2_SECTORS ; sector count + mov ch, 0x00 ; cylinder 0 + mov cl, 0x02 ; start at sector 2 (sector 1 is this boot sector) + mov dh, 0x00 ; head 0 + mov dl, [boot_drive] + mov bx, STAGE2_LOAD_OFFSET ; ES:BX destination (ES is 0) + int 0x13 + jc disk_error + + jmp STAGE2_LOAD_SEGMENT:STAGE2_LOAD_OFFSET + +disk_error: + cli +.hang: + hlt + jmp .hang + +boot_drive: db 0 + +times 510 - ($ - $$) db 0 +dw 0xAA55 diff --git a/src/bootloader/stage2/main.asm b/src/bootloader/stage2/main.asm new file mode 100644 index 0000000..dcfb881 --- /dev/null +++ b/src/bootloader/stage2/main.asm @@ -0,0 +1,64 @@ +; src/bootloader/stage2/main.asm +; Stage2 loader: +; 1) real mode disk load for kernel +; 2) call cstart_() implemented in C while still in real mode +; 3) remain in real mode + +BITS 16 + +section .text +global _start +extern cstart_ + +KERNEL_LOAD_SEGMENT equ 0x1000 ; physical 0x10000 +KERNEL_LOAD_OFFSET equ 0x0000 +KERNEL_SECTORS equ 12 +KERNEL_START_SECTOR equ 6 ; sector 1: stage1, sectors 2-5: stage2, kernel starts at 6 + +_start: + cli + xor ax, ax + mov ds, ax + mov ss, ax + mov sp, 0x7A00 + + xor eax, eax + mov ax, 0x7A00 + mov esp, eax + mov ebp, eax + + mov [boot_drive], dl + + mov ax, KERNEL_LOAD_SEGMENT + mov es, ax + mov bx, KERNEL_LOAD_OFFSET + + mov ah, 0x02 ; BIOS read sectors + mov al, KERNEL_SECTORS + mov ch, 0x00 ; cylinder 0 + mov cl, KERNEL_START_SECTOR ; sector index on track + mov dh, 0x00 ; head 0 + mov dl, [boot_drive] + int 0x13 + jc disk_error + + ; Call stage2 C entry in real mode. + ; The boot drive (DL) is forwarded as an argument. + xor eax, eax + mov al, [boot_drive] + push eax + call cstart_ + add sp, 4 + +realmode_hang: + cli + hlt + jmp realmode_hang + +disk_error: + cli +.hang: + hlt + jmp .hang + +boot_drive: db 0 diff --git a/src/bootloader/stage2/main.c b/src/bootloader/stage2/main.c new file mode 100644 index 0000000..8fcbb4c --- /dev/null +++ b/src/bootloader/stage2/main.c @@ -0,0 +1,16 @@ +#include "stdint.h" +#include "stdio.h" + +void cstart_(uint16_t bootDrive) +{ + (void)bootDrive; + const char *str = "another string"; + + puts("Hello world from C!\r\n"); + printf("Formatted %% %c %s %ls\r\n", 'a', "string", str); + printf("Formatted %d %i %x %p %o %hd %hi %hhu %hhd\r\n", 1234, -5678, 0xdead, 0xbeef, 012345, (short)27, (short)-42, (unsigned char)20, (signed char)-10); + printf("Formatted %ld %lx %lld %llx\r\n", -100000000l, 0xdeadbeeful, 10200300400ll, 0xdeadbeeffeebdaedull); + + for (;;) + ; +} \ No newline at end of file diff --git a/src/bootloader/stage2/stdint.h b/src/bootloader/stage2/stdint.h new file mode 100644 index 0000000..9251a51 --- /dev/null +++ b/src/bootloader/stage2/stdint.h @@ -0,0 +1,15 @@ +#pragma once + +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef signed short int16_t; +typedef unsigned short uint16_t; +typedef signed long int int32_t; +typedef unsigned long int uint32_t; +typedef signed long long int int64_t; +typedef unsigned long long int uint64_t; + +typedef uint8_t bool; + +#define false 0 +#define true 1 diff --git a/src/bootloader/stage2/stdio.c b/src/bootloader/stage2/stdio.c new file mode 100644 index 0000000..9522aba --- /dev/null +++ b/src/bootloader/stage2/stdio.c @@ -0,0 +1,293 @@ +#include "stdio.h" +#include "x86.h" + +void putc(char c) +{ + x86_Video_WriteCharTeletype(c, 0); +} + +void puts(const char *str) +{ + while (*str) + { + putc(*str); + str++; + } +} + +void puts_f(const char *str) +{ + while (*str) + { + putc(*str); + str++; + } +} + +enum +{ + PRINTF_STATE_NORMAL = 0, + PRINTF_STATE_LENGTH, + PRINTF_STATE_LENGTH_SHORT, + PRINTF_STATE_LENGTH_LONG, + PRINTF_STATE_SPEC, +}; + +enum +{ + PRINTF_LENGTH_DEFAULT = 0, + PRINTF_LENGTH_SHORT_SHORT, + PRINTF_LENGTH_SHORT, + PRINTF_LENGTH_LONG, + PRINTF_LENGTH_LONG_LONG, +}; + +typedef struct +{ + int state; + int length; + int radix; + bool sign; +} PrintfContext; + +static const char g_HexChars[] = "0123456789abcdef"; + +static void printf_context_reset(PrintfContext *ctx) +{ + ctx->state = PRINTF_STATE_NORMAL; + ctx->length = PRINTF_LENGTH_DEFAULT; + ctx->radix = 10; + ctx->sign = false; +} + +static int *printf_number(int *argp, int length, bool sign, int radix); + +static int *printf_handle_spec(int *argp, PrintfContext *ctx, char spec) +{ + switch (spec) + { + case 'c': + putc((char)*argp); + argp++; + break; + + case 's': + if (ctx->length == PRINTF_LENGTH_LONG || ctx->length == PRINTF_LENGTH_LONG_LONG) + { + puts_f(*(const char **)argp); + argp += 2; + } + else + { + puts(*(const char **)argp); + argp++; + } + break; + + case '%': + putc('%'); + break; + + case 'd': + case 'i': + ctx->radix = 10; + ctx->sign = true; + argp = printf_number(argp, ctx->length, ctx->sign, ctx->radix); + break; + + case 'u': + ctx->radix = 10; + ctx->sign = false; + argp = printf_number(argp, ctx->length, ctx->sign, ctx->radix); + break; + + case 'X': + case 'x': + case 'p': + ctx->radix = 16; + ctx->sign = false; + argp = printf_number(argp, ctx->length, ctx->sign, ctx->radix); + break; + + case 'o': + ctx->radix = 8; + ctx->sign = false; + argp = printf_number(argp, ctx->length, ctx->sign, ctx->radix); + break; + + default: + break; + } + + printf_context_reset(ctx); + return argp; +} + +void printf(const char *fmt, ...) +{ + int *argp = (int *)&fmt; + PrintfContext ctx; + + printf_context_reset(&ctx); + + argp++; + + while (*fmt) + { + char ch = *fmt; + bool advance = true; + + switch (ctx.state) + { + case PRINTF_STATE_NORMAL: + if (ch == '%') + { + ctx.state = PRINTF_STATE_LENGTH; + } + else + { + putc(ch); + } + break; + + case PRINTF_STATE_LENGTH: + if (ch == 'h') + { + ctx.length = PRINTF_LENGTH_SHORT; + ctx.state = PRINTF_STATE_LENGTH_SHORT; + } + else if (ch == 'l') + { + ctx.length = PRINTF_LENGTH_LONG; + ctx.state = PRINTF_STATE_LENGTH_LONG; + } + else + { + ctx.state = PRINTF_STATE_SPEC; + advance = false; + } + break; + + case PRINTF_STATE_LENGTH_SHORT: + if (ch == 'h') + { + ctx.length = PRINTF_LENGTH_SHORT_SHORT; + ctx.state = PRINTF_STATE_SPEC; + } + else + { + ctx.state = PRINTF_STATE_SPEC; + advance = false; + } + break; + + case PRINTF_STATE_LENGTH_LONG: + if (ch == 'l') + { + ctx.length = PRINTF_LENGTH_LONG_LONG; + ctx.state = PRINTF_STATE_SPEC; + } + else + { + ctx.state = PRINTF_STATE_SPEC; + advance = false; + } + break; + + case PRINTF_STATE_SPEC: + argp = printf_handle_spec(argp, &ctx, ch); + break; + } + + if (advance) + { + fmt++; + } + } +} + +static int *printf_number(int *argp, int length, bool sign, int radix) +{ + char buffer[32]; + unsigned long long number; + int number_sign = 1; + int pos = 0; + + // process length + switch (length) + { + case PRINTF_LENGTH_SHORT_SHORT: + case PRINTF_LENGTH_SHORT: + case PRINTF_LENGTH_DEFAULT: + if (sign) + { + int n = *argp; + if (n < 0) + { + n = -n; + number_sign = -1; + } + number = (unsigned long long)n; + } + else + { + number = *(unsigned int *)argp; + } + argp++; + break; + + case PRINTF_LENGTH_LONG: + if (sign) + { + long int n = *(long int *)argp; + if (n < 0) + { + n = -n; + number_sign = -1; + } + number = (unsigned long long)n; + } + else + { + number = *(unsigned long int *)argp; + } + argp += 2; + break; + + case PRINTF_LENGTH_LONG_LONG: + if (sign) + { + long long int n = *(long long int *)argp; + if (n < 0) + { + n = -n; + number_sign = -1; + } + number = (unsigned long long)n; + } + else + { + number = *(unsigned long long int *)argp; + } + argp += 4; + break; + } + + // convert number to ASCII + do + { + uint32_t rem; + x86_div64_32(number, radix, &number, &rem); + buffer[pos++] = g_HexChars[rem]; + } while (number > 0); + + // add sign + if (sign && number_sign < 0) + buffer[pos++] = '-'; + + // print number in reverse order + while (--pos >= 0) + putc(buffer[pos]); + + return argp; +} \ No newline at end of file diff --git a/src/bootloader/stage2/stdio.h b/src/bootloader/stage2/stdio.h new file mode 100644 index 0000000..0cd2929 --- /dev/null +++ b/src/bootloader/stage2/stdio.h @@ -0,0 +1,6 @@ +#pragma once + +void putc(char c); +void puts(const char *str); +void puts_f(const char *str); +void printf(const char *fmt, ...); \ No newline at end of file diff --git a/src/bootloader/stage2/x86.asm b/src/bootloader/stage2/x86.asm new file mode 100644 index 0000000..eeb4e10 --- /dev/null +++ b/src/bootloader/stage2/x86.asm @@ -0,0 +1,79 @@ +bits 16 + +section .text + +; +; void x86_div64_32(uint64_t dividend, uint32_t divisor, uint64_t* quotientOut, uint32_t* remainderOut); +; +global x86_div64_32 +x86_div64_32: + + ; make new call frame + push ebp ; save old call frame + mov ebp, esp ; initialize new call frame + + push ebx + + ; i386-style stack slots: + ; [ebp + 8] = dividend low 32 + ; [ebp + 12] = dividend high 32 + ; [ebp + 16] = divisor + ; [ebp + 20] = quotientOut + ; [ebp + 24] = remainderOut + + ; divide upper 32 bits + mov eax, [ebp + 12] ; eax <- upper 32 bits of dividend + mov ecx, [ebp + 16] ; ecx <- divisor + xor edx, edx + div ecx ; eax - quot, edx - remainder + + ; store upper 32 bits of quotient + mov ebx, [ebp + 20] + mov [ebx + 4], eax + + ; divide lower 32 bits + mov eax, [ebp + 8] ; eax <- lower 32 bits of dividend + ; edx <- old remainder + div ecx + + ; store results + mov [ebx], eax + mov ebx, [ebp + 24] + mov [ebx], edx + + pop ebx + + ; restore old call frame + mov esp, ebp + pop ebp + ret + +; +; int 10h ah=0Eh +; args: character, page +; +global x86_Video_WriteCharTeletype +x86_Video_WriteCharTeletype: + + ; make new call frame + push ebp ; save old call frame + mov ebp, esp ; initialize new call frame + + ; save bx + push ebx + + ; [ebp + 8] - first argument (character) + ; [ebp + 12] - second argument (page) + mov ah, 0Eh + mov al, [ebp + 8] + mov bh, [ebp + 12] + + int 10h + + ; restore bx + pop ebx + + ; restore old call frame + mov esp, ebp + pop ebp + ret \ No newline at end of file diff --git a/src/bootloader/stage2/x86.h b/src/bootloader/stage2/x86.h new file mode 100644 index 0000000..fb9a237 --- /dev/null +++ b/src/bootloader/stage2/x86.h @@ -0,0 +1,6 @@ +#pragma once +#include "stdint.h" + +void x86_div64_32(uint64_t dividend, uint32_t divisor, uint64_t *quotientOut, uint32_t *remainderOut); + +void x86_Video_WriteCharTeletype(char c, uint8_t page); \ No newline at end of file diff --git a/src/idt.c b/src/idt.c deleted file mode 100644 index ebaddfe..0000000 --- a/src/idt.c +++ /dev/null @@ -1,180 +0,0 @@ -#include "idt.h" - -#include "keyboard.h" -#include "pic.h" -#include "panic.h" - -struct idt_entry -{ - uint16_t offset_low; - uint16_t selector; - uint8_t zero; - uint8_t type_attr; - uint16_t offset_high; -} __attribute__((packed)); - -struct idt_ptr -{ - uint16_t limit; - uint32_t base; -} __attribute__((packed)); - -extern void isr0(void); -extern void isr1(void); -extern void isr2(void); -extern void isr3(void); -extern void isr4(void); -extern void isr5(void); -extern void isr6(void); -extern void isr7(void); -extern void isr8(void); -extern void isr9(void); -extern void isr10(void); -extern void isr11(void); -extern void isr12(void); -extern void isr13(void); -extern void isr14(void); -extern void isr15(void); -extern void isr16(void); -extern void isr17(void); -extern void isr18(void); -extern void isr19(void); -extern void isr20(void); -extern void isr21(void); -extern void isr22(void); -extern void isr23(void); -extern void isr24(void); -extern void isr25(void); -extern void isr26(void); -extern void isr27(void); -extern void isr28(void); -extern void isr29(void); -extern void isr30(void); -extern void isr31(void); -extern void isr32(void); -extern void isr33(void); -extern void isr34(void); -extern void isr35(void); -extern void isr36(void); -extern void isr37(void); -extern void isr38(void); -extern void isr39(void); -extern void isr40(void); -extern void isr41(void); -extern void isr42(void); -extern void isr43(void); -extern void isr44(void); -extern void isr45(void); -extern void isr46(void); -extern void isr47(void); - -static struct idt_entry idt_entries[256]; -static struct idt_ptr idt_descriptor; - -static void idt_set_gate(uint8_t vector, void (*handler)(void)) -{ - uint32_t handler_address = (uint32_t)handler; - - idt_entries[vector].offset_low = handler_address & 0xFFFF; - idt_entries[vector].selector = 0x08; - idt_entries[vector].zero = 0; - idt_entries[vector].type_attr = 0x8E; - idt_entries[vector].offset_high = (handler_address >> 16) & 0xFFFF; -} - -void idt_init(void) -{ - idt_set_gate(0, isr0); - idt_set_gate(1, isr1); - idt_set_gate(2, isr2); - idt_set_gate(3, isr3); - idt_set_gate(4, isr4); - idt_set_gate(5, isr5); - idt_set_gate(6, isr6); - idt_set_gate(7, isr7); - idt_set_gate(8, isr8); - idt_set_gate(9, isr9); - idt_set_gate(10, isr10); - idt_set_gate(11, isr11); - idt_set_gate(12, isr12); - idt_set_gate(13, isr13); - idt_set_gate(14, isr14); - idt_set_gate(15, isr15); - idt_set_gate(16, isr16); - idt_set_gate(17, isr17); - idt_set_gate(18, isr18); - idt_set_gate(19, isr19); - idt_set_gate(20, isr20); - idt_set_gate(21, isr21); - idt_set_gate(22, isr22); - idt_set_gate(23, isr23); - idt_set_gate(24, isr24); - idt_set_gate(25, isr25); - idt_set_gate(26, isr26); - idt_set_gate(27, isr27); - idt_set_gate(28, isr28); - idt_set_gate(29, isr29); - idt_set_gate(30, isr30); - idt_set_gate(31, isr31); - idt_set_gate(32, isr32); - idt_set_gate(33, isr33); - idt_set_gate(34, isr34); - idt_set_gate(35, isr35); - idt_set_gate(36, isr36); - idt_set_gate(37, isr37); - idt_set_gate(38, isr38); - idt_set_gate(39, isr39); - idt_set_gate(40, isr40); - idt_set_gate(41, isr41); - idt_set_gate(42, isr42); - idt_set_gate(43, isr43); - idt_set_gate(44, isr44); - idt_set_gate(45, isr45); - idt_set_gate(46, isr46); - idt_set_gate(47, isr47); - - idt_descriptor.limit = sizeof(idt_entries) - 1; - idt_descriptor.base = (uint32_t)idt_entries; - - __asm__ volatile("lidt %0" : : "m"(idt_descriptor)); -} - -void interrupt_handler(uint32_t vector, uint32_t error_code) -{ - (void)error_code; - - switch (vector) - { - case 0: - panic("Divide by zero"); - break; - case 6: - panic("Invalid opcode"); - break; - case 8: - panic("Double fault"); - break; - case 13: - panic("General protection fault"); - break; - case 14: - panic("Page fault"); - break; - default: - if (vector == 33) - { - keyboard_handle_irq(); - pic_send_eoi(1); - return; - } - - if (vector >= 32 && vector < 48) - { - pic_send_eoi((unsigned char)(vector - 32)); - return; - } - - panic("Unhandled CPU exception"); - break; - } -} \ No newline at end of file diff --git a/src/idt.h b/src/idt.h deleted file mode 100644 index ff15c3f..0000000 --- a/src/idt.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef IDT_H -#define IDT_H - -#include - -void idt_init(void); -void interrupt_handler(uint32_t vector, uint32_t error_code); - -#endif // IDT_H \ No newline at end of file diff --git a/src/isr.asm b/src/isr.asm deleted file mode 100644 index 0b21d1a..0000000 --- a/src/isr.asm +++ /dev/null @@ -1,95 +0,0 @@ -[BITS 32] - -extern interrupt_handler - -%macro ISR_NOERR 1 -global isr%1 -isr%1: - push dword 0 - push dword %1 - jmp isr_common -%endmacro - -%macro ISR_ERR 1 -global isr%1 -isr%1: - push dword %1 - jmp isr_common -%endmacro - -isr_common: - pusha - push ds - push es - push fs - push gs - - mov ax, 0x10 - mov ds, ax - mov es, ax - mov fs, ax - mov gs, ax - - mov eax, [esp + 48] - mov ebx, [esp + 52] - push ebx - push eax - call interrupt_handler - add esp, 8 - - pop gs - pop fs - pop es - pop ds - popa - add esp, 8 - iretd - -ISR_NOERR 0 -ISR_NOERR 1 -ISR_NOERR 2 -ISR_NOERR 3 -ISR_NOERR 4 -ISR_NOERR 5 -ISR_NOERR 6 -ISR_NOERR 7 -ISR_ERR 8 -ISR_NOERR 9 -ISR_ERR 10 -ISR_ERR 11 -ISR_ERR 12 -ISR_ERR 13 -ISR_ERR 14 -ISR_NOERR 15 -ISR_NOERR 16 -ISR_ERR 17 -ISR_NOERR 18 -ISR_NOERR 19 -ISR_NOERR 20 -ISR_NOERR 21 -ISR_NOERR 22 -ISR_NOERR 23 -ISR_NOERR 24 -ISR_NOERR 25 -ISR_NOERR 26 -ISR_NOERR 27 -ISR_NOERR 28 -ISR_NOERR 29 -ISR_ERR 30 -ISR_NOERR 31 -ISR_NOERR 32 -ISR_NOERR 33 -ISR_NOERR 34 -ISR_NOERR 35 -ISR_NOERR 36 -ISR_NOERR 37 -ISR_NOERR 38 -ISR_NOERR 39 -ISR_NOERR 40 -ISR_NOERR 41 -ISR_NOERR 42 -ISR_NOERR 43 -ISR_NOERR 44 -ISR_NOERR 45 -ISR_NOERR 46 -ISR_NOERR 47 \ No newline at end of file diff --git a/src/kernel.asm b/src/kernel.asm deleted file mode 100644 index ef15419..0000000 --- a/src/kernel.asm +++ /dev/null @@ -1,11 +0,0 @@ -[BITS 32] ; 32-bit protected mode - -global _start -extern kernel_main - -_start: - call kernel_main - - jmp $ ; infinite loop to halt the CPU after kernel_main returns - -times 512-($ - $$) db 0 ; fill the rest of the sector with zeros \ No newline at end of file diff --git a/src/kernel.c b/src/kernel.c deleted file mode 100644 index e045bc4..0000000 --- a/src/kernel.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "kernel.h" -#include "idt.h" -#include "pic.h" -#include "printk.h" -#include "shell.h" -#include "vga.h" - -void kernel_main() -{ - idt_init(); - pic_init(); - vga_clear(); - printk("That was the age when nothing was; / There was no sand, nor sea, nor cool waves, / No earth nor sky nor grass there, / Only Ginnungagap.\n"); - shell_init(); - - __asm__ volatile("sti"); - - for (;;) - { - __asm__ volatile("hlt"); - } -} \ No newline at end of file diff --git a/src/kernel.h b/src/kernel.h deleted file mode 100644 index 60d8e56..0000000 --- a/src/kernel.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef KERNEL_H -#define KERNEL_H - -void kernel_main(); - -#endif // KERNEL_H \ No newline at end of file diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c new file mode 100644 index 0000000..ff2b7e5 --- /dev/null +++ b/src/kernel/kernel.c @@ -0,0 +1,3 @@ +void kernel_main(void) +{ +} diff --git a/src/kernel/main.asm b/src/kernel/main.asm new file mode 100644 index 0000000..bae1b4c --- /dev/null +++ b/src/kernel/main.asm @@ -0,0 +1,23 @@ +; src/kernel/main.asm +; 32-bit kernel entry called by stage2. + +BITS 32 + +section .text +global _start +extern kernel_main + +_start: + mov esp, stack_top + call kernel_main + +.hang: + cli + hlt + jmp .hang + +section .bss +align 16 +stack_bottom: + resb 4096 +stack_top: diff --git a/src/keyboard.c b/src/keyboard.c deleted file mode 100644 index 79adf8d..0000000 --- a/src/keyboard.c +++ /dev/null @@ -1,45 +0,0 @@ -#include "keyboard.h" - -#include "shell.h" -#include "vga.h" - -static unsigned char keyboard_read_scancode(void) -{ - unsigned char scancode; - - __asm__ volatile("inb %1, %0" : "=a"(scancode) : "Nd"((unsigned short)0x60)); - return scancode; -} - -static char keyboard_translate_scancode(unsigned char scancode) -{ - static const char lookup_table[128] = { - 0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', - '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', 0, - 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, '\\', 'z', 'x', - 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, 0, 0, ' '}; - - if (scancode >= sizeof(lookup_table)) - { - return 0; - } - - return lookup_table[scancode]; -} - -void keyboard_handle_irq(void) -{ - unsigned char scancode = keyboard_read_scancode(); - - if (scancode & 0x80) - { - return; - } - - char character = keyboard_translate_scancode(scancode); - - if (character != 0) - { - shell_handle_char(character); - } -} \ No newline at end of file diff --git a/src/keyboard.h b/src/keyboard.h deleted file mode 100644 index a36bb74..0000000 --- a/src/keyboard.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef KEYBOARD_H -#define KEYBOARD_H - -void keyboard_handle_irq(void); - -#endif // KEYBOARD_H \ No newline at end of file diff --git a/src/panic.c b/src/panic.c deleted file mode 100644 index 88fc0d9..0000000 --- a/src/panic.c +++ /dev/null @@ -1,20 +0,0 @@ -#include "panic.h" -#include "vga.h" - -static void halt_forever(void) -{ - __asm__ volatile("cli"); - - for (;;) - { - __asm__ volatile("hlt"); - } -} - -void panic(const char *message) -{ - vga_clear(); - vga_write_line("KERNEL PANIC"); - vga_write_line(message); - halt_forever(); -} \ No newline at end of file diff --git a/src/panic.h b/src/panic.h deleted file mode 100644 index f59b5bf..0000000 --- a/src/panic.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef PANIC_H -#define PANIC_H - -void panic(const char *message); - -#endif // PANIC_H \ No newline at end of file diff --git a/src/pic.c b/src/pic.c deleted file mode 100644 index f90c569..0000000 --- a/src/pic.c +++ /dev/null @@ -1,42 +0,0 @@ -#include "pic.h" - -static unsigned char pic_read_data(unsigned short port) -{ - unsigned char value; - - __asm__ volatile("inb %1, %0" : "=a"(value) : "Nd"(port)); - return value; -} - -static void pic_write_data(unsigned short port, unsigned char value) -{ - __asm__ volatile("outb %0, %1" : : "a"(value), "Nd"(port)); -} - -void pic_send_eoi(unsigned char irq) -{ - if (irq >= 8) - { - pic_write_data(0xA0, 0x20); - } - - pic_write_data(0x20, 0x20); -} - -void pic_init(void) -{ - unsigned char master_mask = pic_read_data(0x21); - unsigned char slave_mask = pic_read_data(0xA1); - - pic_write_data(0x20, 0x11); - pic_write_data(0xA0, 0x11); - pic_write_data(0x21, 0x20); - pic_write_data(0xA1, 0x28); - pic_write_data(0x21, 0x04); - pic_write_data(0xA1, 0x02); - pic_write_data(0x21, 0x01); - pic_write_data(0xA1, 0x01); - - pic_write_data(0x21, (unsigned char)(master_mask & 0xFD)); - pic_write_data(0xA1, slave_mask); -} \ No newline at end of file diff --git a/src/pic.h b/src/pic.h deleted file mode 100644 index 6e14a3d..0000000 --- a/src/pic.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef PIC_H -#define PIC_H - -void pic_init(void); -void pic_send_eoi(unsigned char irq); - -#endif // PIC_H \ No newline at end of file diff --git a/src/printk.c b/src/printk.c deleted file mode 100644 index 4376585..0000000 --- a/src/printk.c +++ /dev/null @@ -1,122 +0,0 @@ -#include "printk.h" - -#include -#include - -#include "vga.h" - -static void printk_write_string(const char *text) -{ - if (text == NULL) - { - vga_write_string("(null)"); - return; - } - - vga_write_string(text); -} - -static void printk_write_char(char character) -{ - vga_write_char(character); -} - -static void printk_write_number(unsigned int value, int base) -{ - static const char digits[] = "0123456789abcdef"; - char buffer[33]; - int index = 0; - - if (value == 0) - { - printk_write_char('0'); - return; - } - - while (value > 0) - { - buffer[index++] = digits[value % base]; - value /= base; - } - - while (index-- > 0) - { - printk_write_char(buffer[index]); - } -} - -static void printk_write_signed(int value) -{ - if (value < 0) - { - printk_write_char('-'); - value = -value; - } - - printk_write_number((unsigned int)value, 10); -} - -static void vprintk_internal(const char *format, va_list args) -{ - if (format == NULL) - { - return; - } - - for (const char *cursor = format; *cursor != '\0'; cursor++) - { - if (*cursor != '%') - { - printk_write_char(*cursor); - continue; - } - - cursor++; - if (*cursor == '\0') - { - break; - } - - switch (*cursor) - { - case 's': - printk_write_string(va_arg(args, const char *)); - break; - case 'd': - printk_write_signed(va_arg(args, int)); - break; - case 'u': - printk_write_number(va_arg(args, unsigned int), 10); - break; - case 'x': - printk_write_number(va_arg(args, unsigned int), 16); - break; - case 'c': - printk_write_char((char)va_arg(args, int)); - break; - case '%': - printk_write_char('%'); - break; - default: - printk_write_char('%'); - printk_write_char(*cursor); - break; - } - } -} - -void printk(const char *format, ...) -{ - va_list args; - va_start(args, format); - vprintk_internal(format, args); - va_end(args); -} - -void printkernel(const char *format, ...) -{ - va_list args; - va_start(args, format); - vprintk_internal(format, args); - va_end(args); -} diff --git a/src/printk.h b/src/printk.h deleted file mode 100644 index ffb41a7..0000000 --- a/src/printk.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef PRINTK_H -#define PRINTK_H - -void printk(const char *format, ...); -void printkernel(const char *format, ...); - -#endif // PRINTK_H diff --git a/src/shell.c b/src/shell.c deleted file mode 100644 index 04b0ed8..0000000 --- a/src/shell.c +++ /dev/null @@ -1,222 +0,0 @@ -#include "shell.h" - -#include - -#include "printk.h" -#include "vga.h" - -#define SHELL_BUFFER_SIZE 80 -#define SHELL_PROMPT "> " - -typedef void (*shell_command_handler)(const char *arguments); - -static void shell_print_help(const char *arguments); -static void shell_handle_clear(const char *arguments); -static void shell_handle_echo(const char *arguments); -static void shell_handle_gfetch(const char *arguments); - -typedef struct -{ - const char *name; - const char *help; - shell_command_handler handler; -} shell_command; - -static const shell_command shell_commands[] = { - {"help", "Show this help message", shell_print_help}, - {"clear", "Clear the screen", shell_handle_clear}, - {"echo", "Echo the provided arguments", shell_handle_echo}, - {"gfetch", "Fetch system information", shell_handle_gfetch}, -}; - -static char shell_buffer[SHELL_BUFFER_SIZE]; -static unsigned int shell_buffer_length = 0; -static int shell_initialized = 0; - -static void shell_print_prompt(void) -{ - vga_write_string(SHELL_PROMPT); -} - -static void shell_clear_buffer(void) -{ - for (unsigned int index = 0; index < shell_buffer_length; index++) - { - shell_buffer[index] = '\0'; - } - - shell_buffer_length = 0; -} - -static void shell_print_help(const char *arguments) -{ - (void)arguments; - - printk("Available commands:\n"); - - for (unsigned int index = 0; index < sizeof(shell_commands) / sizeof(shell_commands[0]); index++) - { - printk(" %s - %s\n", shell_commands[index].name, shell_commands[index].help); - } -} - -static void shell_handle_clear(const char *arguments) -{ - (void)arguments; - vga_clear(); -} - -static void shell_handle_echo(const char *arguments) -{ - printk("%s\n", arguments); -} - -static void shell_handle_gfetch(const char *arguments) -{ - const char *gfetch_lines[] = { - " ______ user@ginnungOS", - " / ____| --------------", - " | | ___ OS: ginnungOS x86", - " | | |_ | Kernel: 0.1.0-dev", - " | |___| | Uptime: 9m", - " \\______/ Shell: gsh", - " Display: VGA 80x25 Text", - " Memory: 1240KB / 16MB", - NULL}; - - (void)arguments; - for (unsigned int index = 0; gfetch_lines[index] != NULL; index++) - { - printk("%s\n", gfetch_lines[index]); - } -} - -static const char *shell_extract_arguments(const char *input) -{ - unsigned int index = 0; - - while (input[index] != '\0' && input[index] != ' ') - { - index++; - } - - if (input[index] == ' ') - { - while (input[index] == ' ') - { - index++; - } - - return &input[index]; - } - - return ""; -} - -static int shell_match_command(const char *input, const char *name) -{ - unsigned int index = 0; - - while (input[index] != '\0' && input[index] != ' ' && name[index] != '\0') - { - if (input[index] != name[index]) - { - return 0; - } - - index++; - } - - return (name[index] == '\0') && (input[index] == '\0' || input[index] == ' '); -} - -static void shell_execute_command(void) -{ - if (shell_buffer_length == 0) - { - printk("\n"); - shell_print_prompt(); - return; - } - - shell_buffer[shell_buffer_length] = '\0'; - - printk("\n"); - - for (unsigned int index = 0; index < sizeof(shell_commands) / sizeof(shell_commands[0]); index++) - { - if (shell_match_command(shell_buffer, shell_commands[index].name)) - { - const char *arguments = shell_extract_arguments(shell_buffer); - - if (shell_commands[index].handler != NULL) - { - shell_commands[index].handler(arguments); - } - - shell_clear_buffer(); - shell_print_prompt(); - return; - } - } - - printk("Unknown command: %s\n", shell_buffer); - shell_clear_buffer(); - shell_print_prompt(); -} - -static void shell_handle_backspace(void) -{ - if (shell_buffer_length == 0) - { - return; - } - - shell_buffer[--shell_buffer_length] = '\0'; - vga_backspace(); -} - -void shell_init(void) -{ - if (shell_initialized) - { - return; - } - - shell_clear_buffer(); - shell_initialized = 1; - shell_print_prompt(); -} - -void shell_handle_char(char character) -{ - if (!shell_initialized) - { - shell_init(); - } - - if (character == '\b') - { - shell_handle_backspace(); - return; - } - - if (character == '\n' || character == '\r') - { - shell_execute_command(); - return; - } - - if (character == 0) - { - return; - } - - if (shell_buffer_length >= SHELL_BUFFER_SIZE - 1) - { - return; - } - - shell_buffer[shell_buffer_length++] = character; - vga_write_char(character); -} diff --git a/src/shell.h b/src/shell.h deleted file mode 100644 index 1b1d184..0000000 --- a/src/shell.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef SHELL_H -#define SHELL_H - -void shell_init(void); -void shell_handle_char(char character); - -#endif // SHELL_H diff --git a/src/vga.c b/src/vga.c deleted file mode 100644 index 370e051..0000000 --- a/src/vga.c +++ /dev/null @@ -1,122 +0,0 @@ -#include "vga.h" - -#define VGA_WIDTH 80 -#define VGA_HEIGHT 25 -#define VGA_COLOR 0x07 - -static volatile unsigned short *const video_memory = (volatile unsigned short *)0xB8000; -static unsigned int cursor_x = 0; -static unsigned int cursor_y = 0; - -static void vga_update_cursor(void) -{ - unsigned short cursor_position = (unsigned short)(cursor_y * VGA_WIDTH + cursor_x); - - __asm__ volatile("outb %0, %1" : : "a"((unsigned char)0x0F), "Nd"((unsigned short)0x3D4)); - __asm__ volatile("outb %0, %1" : : "a"((unsigned char)(cursor_position & 0xFF)), "Nd"((unsigned short)0x3D5)); - __asm__ volatile("outb %0, %1" : : "a"((unsigned char)0x0E), "Nd"((unsigned short)0x3D4)); - __asm__ volatile("outb %0, %1" : : "a"((unsigned char)((cursor_position >> 8) & 0xFF)), "Nd"((unsigned short)0x3D5)); -} - -static void vga_scroll(void) -{ - unsigned short blank_cell = (unsigned short)' ' | ((unsigned short)VGA_COLOR << 8); - - for (unsigned int row = 1; row < VGA_HEIGHT; row++) - { - for (unsigned int column = 0; column < VGA_WIDTH; column++) - { - video_memory[(row - 1) * VGA_WIDTH + column] = video_memory[row * VGA_WIDTH + column]; - } - } - - for (unsigned int column = 0; column < VGA_WIDTH; column++) - { - video_memory[(VGA_HEIGHT - 1) * VGA_WIDTH + column] = blank_cell; - } - - if (cursor_y > 0) - { - cursor_y--; - } -} - -static void vga_new_line(void) -{ - cursor_x = 0; - cursor_y++; - - if (cursor_y >= VGA_HEIGHT) - { - vga_scroll(); - } -} - -void vga_clear(void) -{ - unsigned short blank_cell = (unsigned short)' ' | ((unsigned short)VGA_COLOR << 8); - - for (unsigned int index = 0; index < VGA_WIDTH * VGA_HEIGHT; index++) - { - video_memory[index] = blank_cell; - } - - cursor_x = 0; - cursor_y = 0; - vga_update_cursor(); -} - -void vga_backspace(void) -{ - if (cursor_x == 0 && cursor_y == 0) - { - return; - } - - if (cursor_x == 0) - { - cursor_y--; - cursor_x = VGA_WIDTH - 1; - } - else - { - cursor_x--; - } - - video_memory[cursor_y * VGA_WIDTH + cursor_x] = (unsigned short)' ' | ((unsigned short)VGA_COLOR << 8); - vga_update_cursor(); -} - -void vga_write_char(char character) -{ - if (character == '\n') - { - vga_new_line(); - vga_update_cursor(); - return; - } - - video_memory[cursor_y * VGA_WIDTH + cursor_x] = (unsigned short)character | ((unsigned short)VGA_COLOR << 8); - cursor_x++; - - if (cursor_x >= VGA_WIDTH) - { - vga_new_line(); - } - - vga_update_cursor(); -} - -void vga_write_line(const char *text) -{ - vga_write_string(text); - vga_write_char('\n'); -} - -void vga_write_string(const char *text) -{ - for (unsigned int index = 0; text[index] != '\0'; index++) - { - vga_write_char(text[index]); - } -} \ No newline at end of file diff --git a/src/vga.h b/src/vga.h deleted file mode 100644 index ac4b251..0000000 --- a/src/vga.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef VGA_H -#define VGA_H - -void vga_clear(void); -void vga_backspace(void); -void vga_write_char(char character); -void vga_write_line(const char *text); -void vga_write_string(const char *text); - -#endif // VGA_H \ No newline at end of file