From e6231d46f2476b23250bbd0b82a0bea6b56fab5c Mon Sep 17 00:00:00 2001 From: CScatgirl Date: Mon, 2 Mar 2026 17:37:33 -0500 Subject: [PATCH] basic os stuff --- .gitignore | 2 ++ Makefile | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ bios.asm | 58 +++++++++++++++++++++++++++++++++ boot.asm | 70 ++++++++++++++++++++++++++++++++++++++++ entry.asm | 28 ++++++++++++++++ kernel.lnk | 10 ++++++ kmain.c | 79 +++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 342 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 bios.asm create mode 100644 boot.asm create mode 100644 entry.asm create mode 100644 kernel.lnk create mode 100644 kmain.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..64ebf0f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +CLAUDE.md +*.err diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5fe1e89 --- /dev/null +++ b/Makefile @@ -0,0 +1,95 @@ +# ------------------------------------------------------- +# Real Mode OS Makefile +# Tools: NASM, OpenWatcom (wcc/wlink), QEMU +# ------------------------------------------------------- + +# Tools +NASM := nasm +WCC := wcc +WLINK := wlink +QEMU := qemu-system-i386 +DD := dd + +# Flags +NASM_BIN := -f bin +NASM_OBJ := -f obj +WCC_FLAGS := -ms -0 -wx -s -ecc # small model, 8086, warnings, no stack check, cdecl +QEMU_FLAGS = -fda $(IMG) -monitor stdio + +# Output names +BOOT_SRC := boot.asm +ENTRY_SRC := entry.asm +KMAIN_SRC := kmain.c +LINK_FILE := kernel.lnk + +BOOT_BIN := bootloader.bin +ENTRY_OBJ := entry.obj +KMAIN_OBJ := kmain.obj +KERNEL_BIN := kernel.bin +IMG := floppy.img + +BIOS_SRC := bios.asm +BIOS_OBJ := bios.obj + + + +# ------------------------------------------------------- +# Default target +# ------------------------------------------------------- +.PHONY: all clean run debug + +all: $(IMG) + +# ------------------------------------------------------- +# Bootloader — flat binary +# ------------------------------------------------------- +$(BOOT_BIN): $(BOOT_SRC) + $(NASM) $(NASM_BIN) $< -o $@ + +# ------------------------------------------------------- +# Kernel entry point — OMF object +# ------------------------------------------------------- +$(ENTRY_OBJ): $(ENTRY_SRC) + $(NASM) $(NASM_OBJ) $< -o $@ + +# ------------------------------------------------------- +# C kernel — OMF object via OpenWatcom +# ------------------------------------------------------- +$(KMAIN_OBJ): $(KMAIN_SRC) + $(WCC) $< $(WCC_FLAGS) -fo=$@ + +$(BIOS_OBJ): $(BIOS_SRC) + $(NASM) $(NASM_OBJ) $< -o $@ + +$(KERNEL_BIN): $(ENTRY_OBJ) $(BIOS_OBJ) $(KMAIN_OBJ) $(LINK_FILE) + $(WLINK) @$(LINK_FILE) + +# ------------------------------------------------------- +# Floppy image +# ------------------------------------------------------- +$(IMG): $(BOOT_BIN) $(KERNEL_BIN) + $(DD) if=/dev/zero of=$@ bs=512 count=2880 + $(DD) if=$(BOOT_BIN) of=$@ conv=notrunc + $(DD) if=$(KERNEL_BIN) of=$@ seek=1 conv=notrunc bs=512 + +# ------------------------------------------------------- +# Run in QEMU +# ------------------------------------------------------- +run: $(IMG) + $(QEMU) $(QEMU_FLAGS) + +# ------------------------------------------------------- +# Debug — QEMU with GDB stub and interrupt logging +# ------------------------------------------------------- +debug: $(IMG) + $(QEMU) $(QEMU_FLAGS) \ + -s -S \ + -d int \ + -D qemu.log + +# ------------------------------------------------------- +# Clean +# ------------------------------------------------------- +clean: + rm -f $(BOOT_BIN) $(ENTRY_OBJ) $(KMAIN_OBJ) $(KERNEL_BIN) $(IMG) $(BIOS_OBJ)\ + kernel.map qemu.log diff --git a/bios.asm b/bios.asm new file mode 100644 index 0000000..d3d25b9 --- /dev/null +++ b/bios.asm @@ -0,0 +1,58 @@ +[BITS 16] + + global _print_char + global _get_char + global _check_key + global _set_cursor + global _get_cursor_pos + global _set_cursor_pos + segment _TEXT class=CODE + + _print_char: + push bp + mov bp, sp + mov ah, 0x0E ; BIOS TTY write + mov al, [bp+4] ; first arg: char c + xor bh, bh ; page 0 + int 0x10 + pop bp + ret +_get_char: + ; Returns: AL = ASCII char, AH = scancode + ; Blocks until a key is pressed + xor ah, ah ; Function 0: wait for keypress + int 0x16 + xor ah, ah + ret + +_check_key: + ; Non-blocking: returns ZF=1 if no key waiting + mov ah, 0x01 ; Function 1: check buffer + int 0x16 + jz .empty + mov ax, 1 + ret ; ZF=0 means key available, AL/AH = key + .empty: + xor ax, ax + ret +_set_cursor: + mov ah, 0x01 + mov cx, 0x0607 + ret +_get_cursor_pos: + mov ah, 0x03 + xor bh, bh + int 0x10 ; DH = row, DL = col + mov ax, dx ; DH->AH, DL->AL in one shot + ret + +_set_cursor_pos: + push bp + mov bp, sp + mov ah, 0x02 ; AH=02h: set cursor position + xor bh, bh ; page 0 + mov dh, [bp+4] ; row + mov dl, [bp+6] ; col + int 0x10 + pop bp + ret diff --git a/boot.asm b/boot.asm new file mode 100644 index 0000000..90dfe92 --- /dev/null +++ b/boot.asm @@ -0,0 +1,70 @@ +[BITS 16] +[ORG 0x7C00] + +KERNEL_SEGMENT equ 0x1000 +KERNEL_SECTORS equ 32 +boot: +jmp 0x0000: .init +.init: +cli +xor ax,ax +mov ds,ax +mov es, ax +mov ss, ax +mov sp, 0x7C00 +sti + +mov [boot_drive], dl +xor ax,ax +int 0x13 + +jc .disk_error + +call load_kernel + +mov ax, KERNEL_SEGMENT +mov ds, ax +mov es, ax + +jmp KERNEL_SEGMENT:0x0000 + +.disk_error: +mov si, disk_err +call print_str +cli +hlt +load_kernel: +mov ax, KERNEL_SEGMENT +mov es,ax +xor bx, bx + +mov ah, 0x02 +mov al, KERNEL_SECTORS +mov ch, 0 +mov cl, 2 +mov dh, 0 +mov dl, [boot_drive] +int 0x13 +jc .disk_error +ret +.disk_error: +mov si, disk_err +call print_str +cli +hlt + + +print_str: +lodsb +or al, al +jz .done +mov ah, 0x0E +int 0x10 +jmp print_str +.done: +ret + +boot_drive: db 0 +disk_err: db "Disk error", 0x0D, 0X0A, 0 +times 510 - ($-$$) db 0 +dw 0xAA55 diff --git a/entry.asm b/entry.asm new file mode 100644 index 0000000..4e56a60 --- /dev/null +++ b/entry.asm @@ -0,0 +1,28 @@ +[BITS 16] + + + +extern _kmain + +global kernel_entry +global _small_code_ ; Watcom small model expects this + +segment _TEXT class=CODE + +_small_code_: ; Must be at start of CODE segment +kernel_entry: + cli + mov ax, 0x1000 + mov ds, ax + mov es, ax + mov ss, ax + mov sp, 0x7000 ; Just use a fixed address for stack + xor bp, bp + sti + call _kmain +.halt: +cli +hlt +jmp .halt +segment _BSS class=BSS + resb 4096 diff --git a/kernel.lnk b/kernel.lnk new file mode 100644 index 0000000..bb24a6b --- /dev/null +++ b/kernel.lnk @@ -0,0 +1,10 @@ +FORMAT RAW BIN +ORDER CLNAME CODE CLNAME DATA CLNAME BSS +OPTION OFFSET=0x0000 +OPTION MAP=kernel.map +OPTION STACK=4096 +OPTION NODEFAULTLIBS +FILE entry.obj +FILE bios.obj +FILE kmain.obj +NAME kernel.bin diff --git a/kmain.c b/kmain.c new file mode 100644 index 0000000..b7c8273 --- /dev/null +++ b/kmain.c @@ -0,0 +1,79 @@ +#define BUF_SIZE 64 +#define HEAP_SIZE 8192 +static char heap[HEAP_SIZE]; +static int heap_top = 0; + +void* kmalloc(int size) { + void* ptr; + if (heap_top + size > HEAP_SIZE) return 0; + ptr = &heap[heap_top]; + heap_top += size; + return ptr; +} +void print_char(char c); +char get_char(void); +int check_key(void); +void set_cursor(void); +void set_cursor_pos(char row, char col); +int get_cursor_pos(); +int str_eq(char* a, char* b) { + int i = 0; + while (a[i] && b[i]) { + if (a[i] != b[i]) return 0; + i++; + } + return a[i] == b[i]; + } +void print_string(char* str){ + int i = 0; + while (str[i] != '\0'){ + print_char(str[i]); + i++; + } +} +void print_hex(unsigned int n) { + char hex[] = "0x0000"; + int i; + int nibble; + for (i = 5; i >= 2; i--) { + nibble = n & 0xF; + hex[i] = nibble < 10 ? '0' + nibble : 'A' + nibble - 10; + n >>= 4; + } + print_string(hex); + } + + + void read_line(char* buf) { + int i = 0; + char c; + while (i < BUF_SIZE - 1) { + c = get_char(); + if (c == '\r') break; /* Enter */ + if (c == '\b' && i > 0) { /* Backspace */ + i--; + print_char('\b'); + print_char(' '); + print_char('\b'); + } else { + buf[i++] = c; + print_char(c); /* echo */ + } + } + buf[i] = '\0'; + } + void kmain(void) { + char buf[BUF_SIZE]; + set_cursor(); + print_string("senkOS> "); + while (1) { + read_line(buf); + print_char('\n'); + if (str_eq(buf, "hello")) { + print_string("world\n"); + } else { + print_string("unknown command\n"); + } + print_string("senkOS> "); + } + }