basic os stuff

This commit is contained in:
CScatgirl
2026-03-02 17:37:33 -05:00
commit e6231d46f2
7 changed files with 342 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
CLAUDE.md
*.err

95
Makefile Normal file
View File

@@ -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

58
bios.asm Normal file
View File

@@ -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

70
boot.asm Normal file
View File

@@ -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

28
entry.asm Normal file
View File

@@ -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

10
kernel.lnk Normal file
View File

@@ -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

79
kmain.c Normal file
View File

@@ -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> ");
}
}